Shape optimization

I would appreciate some guidance.

I have a FeniCSx (dolfinx 0.9) code that performs some computations, it works fine for a given mesh. The mesh consists of 2 regions, one is a three quarters-annulus that is fixed, and the other part closes the 3-quarters, and is a shape I wish to optimize (as to maximize/minimize some value). I wish to implement some constraint(s), such as the area of the mesh being optimized be worth a constant or target value within some tolerance.

I have read about CutFEMx, which seems to be a method I could use. If I understand well, and please correct me if I am wrong, this tool would be appropriate to reach my goal. The main idea would be to create a single mesh, such as a big square, in which the 3-quarters annulus would be defined by some functions (2 functions? One for the inner and for the outer radii?), and the shape to optimize by another 2 functions.

Then I would have to use an optimizer such as Optuna to vary the 2 curves defining the region whose shape is to be optimized. I could specify to the optimizer whether the constraint is being violated, in which case to prune the result before the FEM computations are even done, saving time.

In the end I would be left with the curve corresponding to the shape of the best solution found by the optimizer. But exactly how would the optimizer vary the function(s)? What exactly would be left to be done for me?

I realize my question could be asked in a CutFEMx forum rather than FEniCSx, if only it existed


Any idea, help, comment or guidance is appreciated.

There are multiple libraries and approaches in FEniCSx that you could use. The simplest approach would be the SIMP method detailed here SIMP or Fenitop. If you need a high accuracy at the boundary or if you have explicit parametric descriptions of your domain, CutFEMx in combination with OptiCut might be indeed the most interesting for you.

2 Likes

Hello Susanne,
Thank you for your reply, I did not expect a reply from you!
The thing is that I have 2 regions (two different materials) sharing 2 interfaces, but these 2 interfaces should be fixed (not submitted to any shape optimization). I don’t know if this complicates things


I do not have figured out how to deal with the mesh generation, other than using randomness in picking x_i and y_i s coordinates for a Bezier curves, yet. It works well in that gmsh does not crash nor core dumps nor hangs, and produces a mesh within a few seconds, but this way of creating meshes is not good enough because it isn’t contemplating all possible shapes. It only considers a limited subset possible, so even if I improved on this method, for example by figuring out a way to produce meshes of a given surface area, it would still be not that good at all, I suspect all meshes would look similar to each other.

All I would need is to link 2 pairs of points together, I do not particularly care about the details as long as I cover a large range of possible ways/shapes to link those points. Here is an example of what my code generates:


The 2 bottom points should always be linked by a straight line (this is the interface 1 with the other material), and the 2 top right points should also be linked to each other by a straight line, as they are in the picture. It is the other 2 curves that need to be adapted, changed, as to optimize a computed quantity.

Therefore, I do not have, yet at least, a parametric description of my domain. I could obtain one, I guess, at least a basis from which the optimizer would have to take over. For example I could start with a circular shape, and let the optimizer add a polynomial to the curves, adjusting the coefficients.

I would have a few questions regarding CutFEMx. In your Github example in the demo here, you use “jump” operations (I guess that’s the Nitsche’s method?), would have I to do the same? I currently do not do anything of the sort. I also use a residual F =0 instead of having a bilinear form with a and L. I really wonder if my code would be reasonably easy to modify to make use of CutFEMx.

For example, right now, I don’t really know how to reproduce this code:

a_cut = cut_form(a)
L_cut = cut_form(L)

b = assemble_vector(L_cut)
A = assemble_matrix(a_cut)
deactivate(A,"phi>0",level_set,[V])
A_py = A.to_scipy()

In my code the weak form looks like:

    # Weak form.
    J_vector = -sigma * grad(volt) - sigma * Seebeck_tensor * grad(temp)
    Fourier_term = dot(-Îș * grad(temp), grad(u)) * dx
    F_T = Fourier_term
    rest_terms = dot(temp * Seebeck_tensor* J_vector, grad(u)) * dx + dot(volt * J_vector, grad(u)) * dx
    F_T += rest_terms
    
    F_V = -dot(grad(v), sigma * grad(volt))*dx -dot(grad(v), sigma * Seebeck_tensor * grad(temp))*dx #- v * J_out * ds(out_current_curve) + v * J_inj * ds(inj_current_curve) 

    weak_form = F_T + F_V

I am not sure which part I should deactivate
 I guess not “weak_form”?

Hi raboynics,

I think I understand your problem a bit better now but a lot of the details are not clear. Do you have a minimal working example you could post?

I will try to explain in a bit more detail what the current CutFEMx library does (see also the documentation of OptiCut and the CutFEM paper).

I have one level set (typically a signed distance function to a boundary) whose zero contour line describes my geometry. I then cut along a piecewise linear approximation of that zero contour line and I adjust my integration rules for the elements that are cut as well as the unfitted cut interface.

As the zero level set cuts arbitrarly through background elements, very small integration regions can appear and these need a form of stabilisation. This is where the jump operators come in. They are continuous interior penalty terms in intersected cells in order to extend the solution in an accurate way in the interface region. Have a look at the CutFEM paper for a detailed description of those (called ghost-penalty).

The deactivation that you see is a trick to set all the unknowns that are fully outside of your domain to zero. If you skip this step you have zero rows in your matrices which will give you nan’s in a direct solver.

If you post a minimal example I can help you better.

1 Like

Hello Susanne,
I had glanced at your paper, very quickly (most of it is way above my head, I am not a specialist in the domain), and today I glanced at OptiCut’s documentation, which is similar in some ways to the information found in the paper. I think I have understood the gist of it, but not the details.
For example, in my case I use polynomials of degree 4 (2 is minimal for me not to get a vanishing quantity that depends on the 2nd order spatial derivatives of “temp” and “volt”, the 2 unknowns of the 2 coupled PDEs in my problem), and from what I understand, the solution is computed not only at the mesh vertices but also in some interior points of the triangles making up the mesh. So I don’t exactly know how CutFEMx is able to retrieve the position of those points, checks whether they are inside or outside the set_level that the user has defined, and so on. But of course, I am confident that it is able to deal with this case without problem.

Unfortunately I do not have a MWE to share. A MWE with all the features that my full code possesses, such as mixed element space, anisotropy in a physical property and a mesh with 2 regions. My code is “big”, spread over several files and uses an object oriented approach, i.e. I created some classes and helper functions to handle my problem. I don’t have much time and I took advantage of LLM AI to create a MWE. I reviewed the code, modified it a bit, executed it and it looks OK to me. It did modify the weak form (you can see this yourself, I gave my real weak form in my previous post), so the equations it solves might not make entirely physical sense. But the code does work and is very similar to my real code, so if we can take it from there, I think I could apply any suggestion to my real code.

The code includes some post processing that can safely be removed, but it’s just some extra lines making sure everything is correct.
Here’s the Python code:

import numpy as np
from mpi4py import MPI
from petsc4py import PETSc
from dolfinx import fem, log
from dolfinx.fem import (Constant, Function, functionspace, assemble_scalar,
                         dirichletbc, form, locate_dofs_topological, Expression)
from dolfinx.fem.petsc import NonlinearProblem
from dolfinx.nls.petsc import NewtonSolver
from dolfinx.io import gmshio
from dolfinx import default_scalar_type
from ufl import (Measure, SpatialCoordinate, TestFunction, TrialFunction,
                 split, derivative, grad, dot, dx, dS, div, FacetNormal)
from basix.ufl import element, mixed_element

# --- 1. Simulation and Material Parameters ---
# Physical constants and simulation settings
T_cold = 300.0
ΔT = 100.0
Îș = 1.8  # Thermal conductivity

# Finite element degree
deg = 4

# Seebeck tensor components for the anisotropic region
s_xx, s_yy = 1e-5, 1e-3

# --- 2. Mesh Loading and Definition of Measures ---
# Load the mesh and associated markers from the .msh file
# This replaces the TheMesh class
mesh, cell_markers, facet_markers = gmshio.read_from_msh("meshes/full_annulus.msh", MPI.COMM_WORLD, gdim=2)

# Define geometric and integration entities
x = SpatialCoordinate(mesh)
n = FacetNormal(mesh)
dx = Measure("dx", domain=mesh, subdomain_data=cell_markers)
ds = Measure("ds", domain=mesh, subdomain_data=facet_markers) # Redundant but kept for consistency
dS = Measure("dS", domain=mesh, subdomain_data=facet_markers)

# --- 3. Definition of Region-Dependent Material Properties ---
# The mesh has two material regions marked with IDs 20 and 21.
# We define material properties (Seebeck tensor, resistivity, conductivity)
# that have different values in each region.

# Create a discontinuous function space (DG0) for region-dependent properties
# Resistivity (rho) and electrical conductivity (sigma)
rho_space = functionspace(mesh, ("DG", 0))
rho = Function(rho_space)
sigma = Function(rho_space)

three_quarters_cells = cell_markers.find(20)
quarter_cells = cell_markers.find(21)

# Assign values for the "three-quarter" material region (ID 20)
rho.x.array[three_quarters_cells] = np.full_like(three_quarters_cells, 1e0, dtype=default_scalar_type)
sigma.x.array[three_quarters_cells] = np.full_like(three_quarters_cells, 1.0 / 1e0, dtype=default_scalar_type)

# Assign values for the "quarter" material region (ID 21)
rho.x.array[quarter_cells] = np.full_like(quarter_cells, 1.0, dtype=default_scalar_type)
sigma.x.array[quarter_cells] = np.full_like(quarter_cells, 1.0 / 1.0, dtype=default_scalar_type)

# Define the anisotropic Seebeck tensor using a DG0 tensor function space
tensor_el = element(family="DG", cell=str(mesh.ufl_cell()), degree=0, shape=(2, 2))
S_space = functionspace(mesh, tensor_el)
Seebeck_tensor = Function(S_space)

# Define the tensor values for each region
def seebeck_three_quarters(x):
    # Isotropic with zero Seebeck effect in this region
    tensor = np.array([[0, 0], [0, 0]])
    num_points = x.shape[1]
    return np.tile(tensor.flatten(), (num_points, 1)).T.reshape(4, num_points)

def seebeck_quarter(x):
    # Anisotropic Seebeck tensor in this region
    tensor = np.array([[s_xx, 0], [0, s_yy]])
    num_points = x.shape[1]
    return np.tile(tensor.flatten(), (num_points, 1)).T.reshape(4, num_points)

# Interpolate the functions to define the Seebeck tensor across the domain
Seebeck_tensor.interpolate(seebeck_three_quarters, cells0=three_quarters_cells)
Seebeck_tensor.interpolate(seebeck_quarter, cells0=quarter_cells)


# --- 4. Weak Formulation using Mixed Elements ---
# Define a mixed element function space for temperature (temp) and voltage (volt)
el = element("CG", str(mesh.ufl_cell()), deg)
mel = mixed_element([el, el])
ME = functionspace(mesh, mel)

# Define test functions and the function to hold the solution
u, v = TestFunction(ME)
TempVolt = Function(ME)
temp, volt = split(TempVolt)
dTV = TrialFunction(ME)

# Define the electric current density J
J_vector = -sigma * grad(volt) - sigma * Seebeck_tensor * grad(temp)

# Weak form for the heat equation (F_T)
F_T = dot(Îș * grad(temp), grad(u)) * dx - dot(rho * J_vector, J_vector) * u * dx

F_V = dot(J_vector, grad(v)) * dx

# Combine into the final weak form
weak_form = F_T + F_V

# --- 5. Boundary Conditions ---
# Boundary IDs from the gmsh file
# Physical Curve("r_inner_quarter", 24)
# Physical Curve("r_outer_quarter", 25)
# Physical Curve("r_inner_three_quarter", 26)
# Physical Curve("r_outer_three_quarter", 27)
T_cold_boundary_curve = 24
T_hot_boundary_curve = 25

# Locate degrees of freedom for temperature on the specified boundaries
facets_cold = facet_markers.find(T_cold_boundary_curve)
facets_hot = facet_markers.find(T_hot_boundary_curve)

dofs_cold = locate_dofs_topological(ME.sub(0), mesh.topology.dim - 1, facets_cold)
dofs_hot = locate_dofs_topological(ME.sub(0), mesh.topology.dim - 1, facets_hot)

# Define and apply Dirichlet (fixed value) boundary conditions for temperature
bc_cold = dirichletbc(PETSc.ScalarType(T_cold), dofs_cold, ME.sub(0))
bc_hot = dirichletbc(PETSc.ScalarType(T_cold + ΔT), dofs_hot, ME.sub(0))

bcs = [bc_cold, bc_hot]

# --- 6. Solver Configuration and Solution ---
# The problem is nonlinear, so we define the Jacobian and use a Newton solver
Jac = derivative(weak_form, TempVolt, dTV)

# Set up the nonlinear problem
problem = NonlinearProblem(weak_form, TempVolt, bcs=bcs, J=Jac)

# Configure the Newton solver
solver = NewtonSolver(MPI.COMM_WORLD, problem)
solver.convergence_criterion = "residual"
solver.rtol = 1e-14
solver.atol = 1e-14
solver.report = True
solver.max_it = 100

# Configure the linear solver (KSP) within the Newton solver
# Use a direct LU solver (MUMPS) for robustness
ksp = solver.krylov_solver
opts = PETSc.Options()
option_prefix = ksp.getOptionsPrefix()
opts[f"{option_prefix}ksp_type"] = "preonly"
opts[f"{option_prefix}pc_type"] = "lu"
opts[f"{option_prefix}pc_factor_mat_solver_type"] = "mumps"
ksp.setFromOptions()

log.set_log_level(log.LogLevel.WARNING)

# Solve the system
print("--- Starting nonlinear solver ---")
n_iter, converged = solver.solve(TempVolt)
if converged:
    print(f"Solver converged in {n_iter} iterations.")
else:
    print("Solver did not converge.")

# The solution is now in TempVolt. We can split it to get temp and volt.
temp_sol, volt_sol = TempVolt.sub(0).collapse(), TempVolt.sub(1).collapse()


# --- 7. Post-Processing (Optional) ---
# Example calculation: Check for current conservation across internal boundaries
# Physical Curve("theta_0", 22)
# Physical Curve("theta_pi_over_2", 23)
# These are internal lines separating the two material regions
J_integrand = (-sigma('+') * grad(volt)('+') - sigma('+') * Seebeck_tensor('+') * grad(temp)('+'))

# Calculate the total current flowing through each interface
I_theta0 = assemble_scalar(form(dot(J_integrand, n('+')) * dS(22)))
I_thetapi_over_2 = assemble_scalar(form(dot(J_integrand, n('+')) * dS(23)))

print("\n--- Post-processing Results ---")
print(f"Current through interface at theta=0 (ID 22): {I_theta0:.6e} A")
print(f"Current through interface at theta=pi/2 (ID 23): {I_thetapi_over_2:.6e} A")
# For a closed system with no current sources/sinks, these should sum to zero.

# Check that the divergence of J is close to zero over the whole domain
div_J_integral = assemble_scalar(form(div(J_vector) * dx))
print(f"Volume integral of div(J): {div_J_integral:.6e}")

Its execution yields:

Info    : Reading 'meshes/full_annulus.msh'...
Info    : 13 entities
Info    : 6205 nodes
Info    : 12444 elements
Info    : Done reading 'meshes/full_annulus.msh'
--- Starting nonlinear solver ---
Solver converged in 2 iterations.

--- Post-processing Results ---
Current through interface at theta=0 (ID 22): 7.841666e-03 A
Current through interface at theta=pi/2 (ID 23): 8.043990e-03 A
Volume integral of div(J): -3.967311e-12

The mesh I am considering comes from the following file.geo:

// Gmsh project created on Sat Mar  8 22:37:56 2025
SetFactory("OpenCASCADE");
//+
Mesh.CharacteristicLengthMax = 0.0009;
Mesh.CharacteristicLengthMin = 0.0001;

Disk(1) = {0, 0, 0, 0.05, 0.05};
//+
Disk(2) = {0, 0, 0, 0.035, 0.035};

BooleanDifference{ Surface{1}; Delete;}{ Surface{2}; Delete;}
// Surface 3 is the full annulus. I need to split it into 2.

Point(4) = {0, 0.05, 0};
//+
Point(5) = {0, 0.035, 0};
//+
// Origin.
Point(6) = {0, 0, 0};

Line(4) = {2,3};
Line(5) = {4,5};

Circle(6) = {5, 6, 2};

Circle(7) = {4, 6, 3};
Curve Loop(8) = {5,6,4,7};
Plane Surface(9) = {8};

// 3-quarter surface
BooleanDifference{ Surface{1}; Delete;}{ Surface{9};}

BooleanFragments{ Surface{1}; Surface{9}; Delete; }{ }

Physical Surface("three_quarter_material", 20) = {1};
Physical Surface("quarter_material", 21) = {9};
Physical Curve("theta_0", 22) = {4};
Physical Curve("theta_pi_over_2", 23) = {5};
Physical Curve("r_inner_quarter", 24) = {6};
Physical Curve("r_outer_quarter", 25) = {7};
Physical Curve("r_inner_three_quarter", 26) = {8};
Physical Curve("r_outer_three_quarter", 27) = {9};

which can generate the file.msh either from Gmsh GUI or from the command gmsh -2 full_annulus.geo.

Let me know if that’s too much, too big, i.e. if you would prefer something trimmed. I just don’t know if I would be able to apply the suggestions to my real code if I trim it significantly from the above example.

Thank you very much for your time and your help. :slight_smile:

Hello Susanne,
I did not have time to work on this since my last message, but I might try tonight. I am excited and worried at the same time, this shape optimization is the very last part of my research on a problem, so I will make the efforts to make it work one way or another, and I am worried because I don’t know if I’ll have the capacity to pull it out reasonably easily.

In the meantime, can you please comment on my following questions?

  • I have to define a single mesh, for example a square of sides 0.1 that would contain my annulus region. Then level sets for both the 3 quarters and the quarter annulus regions, is that correct? To keep things simple, my current goal is to replicate the output of the code I posted above, without dealing with modifying the shape yet.

  • Since my problem is non linear, I am forced to define or use a custom Newton solver method, is that correct? In regular dolfinx this is not the case.

  • I would have to add ghost terms in my weak form to stabilize the problem, is this correct?

  • I would have to add other terms related to Nietsche’s method to ensure the Dirichlet boundary conditions are well applied, is this correct? This part is a bit over my head at the moment, but I guess LLM AI can help me for a first draft.

Feel free to let me know if my problem and/or goal is unclear.

Dear Raboynics,

Your questions summarize indeed the main points to address:

  1. Define a regular background grid (normally a rectangle or a box) that contains your full geometry . Normally this is a bounding box with a generous amount of padding.
  2. Newton solver: the Newton solver is not as convenient to use in CutFEMx as in FEniCSx. You need to write it a bit by hand as shown in the nice tutorials by JĂžrgen. Make sure to use the assembler from cutfemx in your Newton iterations.
  3. Reformulate your geometry description in terms of level sets. Note that you can form complex shapes with level sets by taking the min/max over several level sets (AI should be able to help with that). For example, two circles can be merged by taking the minimum over two level sets each defining a circle, e.g. \sqrt{(x-c_x)^2+(y-c_y)^2}-r.
  4. Add gradient jump penalty for each unknown in the interface region, e.g. \gamma h \int_{\mathcal{F}} [[\nabla u]]_n [[\nabla v]]_n \, dS. Scaling them properly is difficult but using them approximately is fairly straightforward (just add above term for each unknown).
  5. You need to derive a way to enforce boundary conditions weakly, e.g. Nitsche’s method. This means keeping the term from the integration by parts on your diffusion terms \Delta u, adding a symmetric term and a penalty term.

I hope this helps.

2 Likes

Thanks Susanne, your answer is very helpful and will allow me to write some code.

I am facing some CutFEMx installation issues though. I followed your guide on Github with a tiny bit of differences, I started with a dolfinx 0.9 conda installation, so I did not compile and install it the way described in your guide. I did follow the rest, though.

I also faced some ssh issues preventing me from git cloning all the repositories, so I downloaded them, unzipped them, took care to pick the correct branch and followed your commands. It worked fine for CutCells-main, ffcx-runtime-0.9, basix-0.9 and ufl-2024.2.0. But not for CutFEMx-main. I got stuck on the penultimate command to run: pip install --check-build-dependencies --no-build-isolation .
The traceback is long, so I skip the beginning:

      [15/19] Building CXX object CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/petsc.cpp.o
      FAILED: CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/petsc.cpp.o
      /usr/bin/g++  -pthread -B /home/isaac/.conda/envs/cutfemx_conda_env_isaac/share/python_compiler_compat -DADIOS2_USE_MPI -DBOOST_TIMER_DYN_LINK -DBOOST_TIMER_NO_LIB -DDOLFINX_VERSION=\"0.9.0\" -DFMT_SHARED -DHAS_ADIOS2 -DHAS_KAHIP -DHAS_PARMETIS -DHAS_PETSC -DHAS_PETSC4PY -DHAS_PTSCOTCH -DHAS_SLEPC -DSPDLOG_COMPILED_LIB -DSPDLOG_FMT_EXTERNAL -DSPDLOG_SHARED_LIB -Dcutfemx_cpp_EXPORTS -Dcxx_std_20 -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/petsc4py/include -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/mpi4py/include -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/dolfinx/wrappers -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/python3.13 -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/nanobind/include -isystem /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include -isystem /home/isaac/.local/include -O3 -DNDEBUG -std=gnu++20 -fPIC -fvisibility=hidden -fno-stack-protector -ffunction-sections -fdata-sections -MD -MT CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/petsc.cpp.o -MF CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/petsc.cpp.o.d -o CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/petsc.cpp.o -c /home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/CutFEMx-main/python/cutfemx/wrappers/petsc.cpp
      In file included from /home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/CutFEMx-main/python/cutfemx/wrappers/petsc.cpp:28:
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h: In function ‘void cutfemx::fem::pack_elements(ufcx_integral*, std::unordered_map<long unsigned int, std::shared_ptr<basix::FiniteElement<T> > >&, std::vector<std::pair<std::shared_ptr<basix::FiniteElement<U> >, int> >&)’:
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:527:32: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘num_fe’ [-Wtemplate-body]
        527 |   int num_elements = integral->num_fe;
            |                                ^~~~~~
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:538:35: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘finite_element_hashes’; did you mean ‘coordinate_element_hash’? [-Wtemplate-body]
        538 |     element_hashes[i] = integral->finite_element_hashes[i];
            |                                   ^~~~~~~~~~~~~~~~~~~~~
            |                                   coordinate_element_hash
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:539:40: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘finite_element_deriv_order’ [-Wtemplate-body]
        539 |     element_deriv_order[i] = integral->finite_element_deriv_order[i];
            |                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h: In function ‘cutfemx::fem::CutForm<T, U> cutfemx::fem::create_cut_form_factory(const ufcx_form&, std::shared_ptr<const dolfinx::fem::Form<T, U> >, const std::map<IntegralType, std::vector<std::pair<int, std::shared_ptr<cutfemx::quadrature::QuadratureRules<U> > > > >&)’:
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:621:53: error: ‘cutcell’ was not declared in this scope; did you mean ‘cutfemx::fem::IntegralType::cutcell’? [-Wtemplate-body]
        621 |                                  + integral_offsets[cutcell],
            |                                                     ^~~~~~~
            |                                                     cutfemx::fem::IntegralType::cutcell
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:44:3: note: ‘cutfemx::fem::IntegralType::cutcell’ declared here
         44 |   cutcell = 0,           ///< run time integration one parent cell -> dC
            |   ^~~~~~~
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:659:29: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘tabulate_tensor_runtime_float32’; did you mean ‘tabulate_tensor_float32’? [-Wtemplate-body]
        659 |               k = integral->tabulate_tensor_runtime_float32;
            |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            |                             tabulate_tensor_float32
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:661:29: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘tabulate_tensor_runtime_float64’; did you mean ‘tabulate_tensor_float64’? [-Wtemplate-body]
        661 |               k = integral->tabulate_tensor_runtime_float64;
            |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            |                             tabulate_tensor_float64
      [16/19] Building CXX object CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/level_set.cpp.o
      [17/19] Building CXX object CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/quadrature.cpp.o
      [18/19] Building CXX object CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/fem.cpp.o
      FAILED: CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/fem.cpp.o
      /usr/bin/g++  -pthread -B /home/isaac/.conda/envs/cutfemx_conda_env_isaac/share/python_compiler_compat -DADIOS2_USE_MPI -DBOOST_TIMER_DYN_LINK -DBOOST_TIMER_NO_LIB -DDOLFINX_VERSION=\"0.9.0\" -DFMT_SHARED -DHAS_ADIOS2 -DHAS_KAHIP -DHAS_PARMETIS -DHAS_PETSC -DHAS_PETSC4PY -DHAS_PTSCOTCH -DHAS_SLEPC -DSPDLOG_COMPILED_LIB -DSPDLOG_FMT_EXTERNAL -DSPDLOG_SHARED_LIB -Dcutfemx_cpp_EXPORTS -Dcxx_std_20 -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/petsc4py/include -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/mpi4py/include -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/dolfinx/wrappers -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/python3.13 -I/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/nanobind/include -isystem /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include -isystem /home/isaac/.local/include -O3 -DNDEBUG -std=gnu++20 -fPIC -fvisibility=hidden -fno-stack-protector -ffunction-sections -fdata-sections -MD -MT CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/fem.cpp.o -MF CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/fem.cpp.o.d -o CMakeFiles/cutfemx_cpp.dir/cutfemx/wrappers/fem.cpp.o -c /home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/CutFEMx-main/python/cutfemx/wrappers/fem.cpp
      In file included from /home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/CutFEMx-main/python/cutfemx/wrappers/fem.cpp:23:
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h: In function ‘void cutfemx::fem::pack_elements(ufcx_integral*, std::unordered_map<long unsigned int, std::shared_ptr<basix::FiniteElement<T> > >&, std::vector<std::pair<std::shared_ptr<basix::FiniteElement<U> >, int> >&)’:
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:527:32: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘num_fe’ [-Wtemplate-body]
        527 |   int num_elements = integral->num_fe;
            |                                ^~~~~~
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:538:35: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘finite_element_hashes’; did you mean ‘coordinate_element_hash’? [-Wtemplate-body]
        538 |     element_hashes[i] = integral->finite_element_hashes[i];
            |                                   ^~~~~~~~~~~~~~~~~~~~~
            |                                   coordinate_element_hash
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:539:40: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘finite_element_deriv_order’ [-Wtemplate-body]
        539 |     element_deriv_order[i] = integral->finite_element_deriv_order[i];
            |                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h: In function ‘cutfemx::fem::CutForm<T, U> cutfemx::fem::create_cut_form_factory(const ufcx_form&, std::shared_ptr<const dolfinx::fem::Form<T, U> >, const std::map<IntegralType, std::vector<std::pair<int, std::shared_ptr<cutfemx::quadrature::QuadratureRules<U> > > > >&)’:
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:621:53: error: ‘cutcell’ was not declared in this scope; did you mean ‘cutfemx::fem::IntegralType::cutcell’? [-Wtemplate-body]
        621 |                                  + integral_offsets[cutcell],
            |                                                     ^~~~~~~
            |                                                     cutfemx::fem::IntegralType::cutcell
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:44:3: note: ‘cutfemx::fem::IntegralType::cutcell’ declared here
         44 |   cutcell = 0,           ///< run time integration one parent cell -> dC
            |   ^~~~~~~
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:659:29: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘tabulate_tensor_runtime_float32’; did you mean ‘tabulate_tensor_float32’? [-Wtemplate-body]
        659 |               k = integral->tabulate_tensor_runtime_float32;
            |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            |                             tabulate_tensor_float32
      /home/isaac/.conda/envs/cutfemx_conda_env_isaac/include/cutfemx/fem/CutForm.h:661:29: error: ‘ufcx_integral’ {aka ‘struct ufcx_integral’} has no member named ‘tabulate_tensor_runtime_float64’; did you mean ‘tabulate_tensor_float64’? [-Wtemplate-body]
        661 |               k = integral->tabulate_tensor_runtime_float64;
            |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            |                             tabulate_tensor_float64
      ninja: build stopped: subcommand failed.
      
      *** CMake build failed
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for cutfemx
Failed to build cutfemx
ERROR: Failed to build installable wheels for some pyproject.toml based projects (cutfemx)

It’s as if there was a mismatching of versions error, the CutFEMx code is expecting some functions in fccx that are not present in the code, it looks like to me. Would you happen to have an idea on what I could have done wrong?

This looks like you still have the standard ffcx installed and not the modified version with runtime integral support. Try this

git clone git@github.com:sclaus2/ffcx-runtime-0.9.0.git
cd ffcx
cmake -DCMAKE_INSTALL_PREFIX="$CONDA_PREFIX" -B build-dir -S cmake/
cmake --build build-dir
cmake --install build-dir
pip install .

the cmake commands make sure that ufcx.h from the ffcx-runtime repository is installed.

1 Like

Thank you again, Susanne. It is a bit frustrating, now the installation process completed successfully, however running your latest example (demo_cut_poisson.py) yields:

Traceback (most recent call last):
  File "/home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/example.py", line 37, in <module>
    V = fem.functionspace(msh, ("Lagrange", 2))
  File "/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/dolfinx/fem/function.py", line 628, in functionspace
    cpp_element = _create_dolfinx_element(mesh.comm, mesh.topology.cell_type, ufl_e, dtype)
  File "/home/isaac/.conda/envs/cutfemx_conda_env_isaac/lib/python3.13/site-packages/dolfinx/fem/function.py", line 586, in _create_dolfinx_element
    return CppElement(basix_e, ufl_e.block_size, ufl_e.is_symmetric)
TypeError: __init__(): incompatible function arguments. The following argument types are supported:
    1. __init__(self, element: basix::FiniteElement<double>, block_size: int, symmetric: bool) -> None
    2. __init__(self, elements: collections.abc.Sequence[dolfinx.cpp.fem.FiniteElement_float64]) -> None
    3. __init__(self, cell_type: dolfinx.cpp.mesh.CellType, points: numpy.ndarray[dtype=float64, shape=(*, *), ], block_size: int, symmetry: bool) -> None

Invoked with types: dolfinx.cpp.fem.FiniteElement_float64, basix._basixcpp.FiniteElement_float64, int, bool

Hmm, could it be due to the fact that I installed dolfinx with conda as described in the official website, rather than compiling it on my own?

Edit: I will retry, following your installation process exactly this time, no shortcut.

No luck either
 this time I almost got out of RAM (300 Mb available, out of 16 GB), the step of conda dependencies took over 1 hour to complete. But then, I get stuck at installing dolfinx:

-- Configuring done (4.5s)
-- Generating done (0.0s)
-- Build files have been written to: /home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/dolfinx-0.9.0/cpp/build-dir
[  2%] Building CXX object dolfinx/CMakeFiles/dolfinx.dir/common/defines.cpp.o
[  4%] Building CXX object dolfinx/CMakeFiles/dolfinx.dir/common/IndexMap.cpp.o
In file included from /home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/dolfinx-0.9.0/cpp/dolfinx/common/MPI.h:10,
                 from /home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/dolfinx-0.9.0/cpp/dolfinx/common/IndexMap.h:11,
                 from /home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/dolfinx-0.9.0/cpp/dolfinx/common/IndexMap.cpp:8:
/home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/dolfinx-0.9.0/cpp/dolfinx/common/log.h:9:10: fatal error: spdlog/spdlog.h: No such file or directory
    9 | #include <spdlog/spdlog.h>
      |          ^~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [dolfinx/CMakeFiles/dolfinx.dir/build.make:93: dolfinx/CMakeFiles/dolfinx.dir/common/IndexMap.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:368: dolfinx/CMakeFiles/dolfinx.dir/all] Error 2
make: *** [Makefile:136: all] Error 2
-- Install configuration: "Release"
CMake Error at build-dir/dolfinx/cmake_install.cmake:102 (file):
  file INSTALL cannot find
  "/home/isaac/Documents/fenicsx/Bridgman/2d_bridgman_textbook_example/debug_CutFEMx_annulus/dolfinx-0.9.0/cpp/build-dir/dolfinx/libdolfinx.so.0.9.0":
  No such file or directory.
Call Stack (most recent call first):
  build-dir/cmake_install.cmake:47 (include)

Hmm, not sure why I face these issues


Edit: I fixed this problem by using conda to install spdlog, maybe it’s a missing dependency that should be added to the list(s).

Edit2: I am facing endless other issues. I wonder if the Python version matters, without specifying a specific version, it uses 3.13. Would using the 3.12 help in any way?

Edit 3: Apparently, this made it. Specifying python 3.12 at the beginning of the conda env, following all the instruction + using conda to install the spdlog package worked, I can now run your example! Nice
 all all the fun begins :smiley:

1 Like

I made notes of these changes in the README.md for CutFEMx . With the next FEniCSx release I am planning to do a conda install recipe directly that should simplify the installation of CutFEMx a lot. I am very happy to hear that the code is now running. If you have any other questions let me know.

1 Like

Thank you Susanne, yeah, good idea to simplify the installation process.

I need more time to study the Nitsche method, the ghost penalty, etc.

I will probably open a new thread in the next days with questions in it, either regarding where I’m stuck or to make sure what I do is correct. I plan to work from the above example but with my real, definitive weak form.