Surface integral/dolfinx >=0.4.0

Hi all,

Under dolfinx<0.4.0, I calculated the surface area by simply calling:

area = fem.assemble_scalar(1.0*(ds(0)))

Here ‘0’ is an arbitrary ID depending on specific definitions (btw, I use snippets found here Setting multiple Dirichlet, Neumann, and Robin conditions — FEniCSx tutorial to load surfaces as defined in gmsh).

Now, using dolfinx-0.4.1 and using the same call, I get:

  File "/usr/local/dolfinx-complex/lib/python3.8/dist-packages/dolfinx/fem/assemble.py", line 128, in assemble_scalar
    constants = constants or _pack_constants(M)
TypeError: pack_constants(): incompatible function arguments. The following argument types are supported:
    1. (arg0: dolfinx::fem::Form<double>) -> numpy.ndarray[numpy.float64]
    2. (arg0: dolfinx::fem::Expression<double>) -> numpy.ndarray[numpy.float64]
    3. (arg0: dolfinx::fem::Form<float>) -> numpy.ndarray[numpy.float32]
    4. (arg0: dolfinx::fem::Expression<float>) -> numpy.ndarray[numpy.float32]
    5. (arg0: dolfinx::fem::Form<std::complex<double> >) -> numpy.ndarray[numpy.complex128]
    6. (arg0: dolfinx::fem::Expression<std::complex<double> >) -> numpy.ndarray[numpy.complex128]

Invoked with: Form([Integral(FloatValue(1.0), 'exterior_facet', Mesh(VectorElement(FiniteElement('Lagrange', tetrahedron, 1), dim=3), 0), 1, {}, <dolfinx.cpp.mesh.MeshTags_int32 object at 0x7f4afeaaf3b0>)])

Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>,
<pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic
conversions are optional and require extra headers to be included
when compiling your pybind11 module.

Did I do something wrong, or did the API differs somewhat from the previous version? Before calling, I switched to the complex mode (source dolfinx-complex-mode).

Thanks,
Mario

As you can see in the tutorial that you are referring to, assemble_scalar has a fem.form wrapping up the UFL form:

error_L2 = np.sqrt(mesh.comm.allreduce(assemble_scalar(form((uh - u_exact)**2 * dx)), op=MPI.SUM))

This is because whenever you call form on an ufl object, it checks the cache, and if not found recompiles the form.
Thus, to minimize runtime for repeated assembly, one should do:

area_form = form(1.0*ds(0))
for i in range(10):
     area = fem.assemble_scalar(area_form)
2 Likes

Is the ‘10’ in the range command somehow arbitrary? Anyway, it works now. Thanks a lot!

The 10 was just to show that you do not need to recompile the form if you want to assemble the same form multiple times. For the work you are doing, if you are only assembling once, you do not need a loop.