In dolfin-old when defining a form including a constant, the form was not recompiled when changing its value (I suppose there was a specific signature associated to each constant).
This is not anymore the case in dolfinx. When I redefine a constant, the form is recompiled, which is annoying. Is there a way to avoid this?
I give below a MWE. Of course one can assign the value of the constant, but I cannot do it in my case, because the constant is a function that I call for a parametric analysis, and I do not want to have dolfinx.fem.Constant in the launcher of the parameter analysis.
from dolfinx.fem import (Constant, assemble_scalar, form)
from dolfinx.mesh import create_unit_square
from mpi4py import MPI
from ufl import dx
import numpy as np
from pathlib import Path
import shutil
mesh = create_unit_square(MPI.COMM_WORLD, 1, 1)
cache_dir = f"{str(Path.cwd())}/cache"
shutil.rmtree(cache_dir,ignore_errors=True)
jit_params = {"cache_dir": cache_dir, "cffi_verbose": True}
# this recompiles the form at each iterations (different behavior wrt dolfin-old)
for c in np.linspace(0,1,10):
cnst = Constant(mesh,c)
f = assemble_scalar(form(cnst * dx(domain=mesh),jit_params=jit_params))
print(f)
# this does not recompile (but I need the former)
cnst = Constant(mesh,1.)
for c in np.linspace(0,.2,10):
cnst.value = c
f = assemble_scalar(form(cnst * dx(domain=mesh),jit_params=jit_params))
print(f)
Note however the different behaviour between Constant(mesh.ufl_cell(),c) and Constant(mesh,c)
when using different meshes. Why Constant is defined on the whole mesh and not on the single cell in all the examples? What is the rationale for using Constant(mesh.ufl_cell(),c) instead that Constant(mesh,c)?
from dolfinx.fem import (Constant, assemble_scalar, form)
from dolfinx.mesh import create_unit_square
from mpi4py import MPI
from ufl import dx
import numpy as np
from pathlib import Path
import shutil
cache_dir = f"{str(Path.cwd())}/cache"
shutil.rmtree(cache_dir,ignore_errors=True)
jit_params = {"cache_dir": cache_dir, "cffi_verbose": True}
# Does not recompile
for c in np.linspace(0,1,10):
mesh = create_unit_square(MPI.COMM_WORLD, 1, 1)
cnst = Constant(mesh.ufl_cell(),c)
f = assemble_scalar(form(cnst * dx(domain=mesh),jit_params=jit_params))
print(f)
shutil.rmtree(cache_dir,ignore_errors=True)
# Recompile each time
for c in np.linspace(0,1,10):
mesh = create_unit_square(MPI.COMM_WORLD, 1, 1)
cnst = Constant(mesh,c)
f = assemble_scalar(form(cnst * dx(domain=mesh),jit_params=jit_params))
print(f)
I guess we haven’t thought about this in the examples. The rationale would be for problems where you use cnst*ufl.dx and not cnst*ufl.dx(domain=mesh), as the former will not compile:
for c in np.linspace(0, 1, 10):
mesh = create_unit_square(MPI.COMM_WORLD, 1, 1)
cnst = Constant(mesh.ufl_cell(), c)
f = assemble_scalar(form(cnst * dx), jit_params=jit_params) # (domain=mesh), jit_params=jit_params))
print(f)
returns
RuntimeError: Expecting to find a Mesh in the form.
I’ve now updated the PR in UFL to make sure that we now use the correct signature for both the cases above.
Thanks a lot. It works also for different meshes, as I was expecting.
# This does not recompile at each iteration
for c in np.linspace(1,10,10):
mesh = create_unit_square(MPI.COMM_WORLD, 1, int(c))
f = assemble_scalar(form(cnst * dx(domain=mesh),jit_params=jit_params))
print(f)