Compliance minimization
Minimizing the compliance of a structure is a classic problem that has been the subject of numerous studies.
Problem definition
We seek the optimal shape, \(\widetilde{\Omega}\subset D\), that minimizes the compliance of the structure for a linear elastic material subject to Dirichlet and Neumann boundary conditions. We impose a target area on the structure, which results in the addition of an equality constraint. The optimization problem is defined as
u is the displacement field solution to PDE equation and the constraint over the area is defined as:
with \(\overline{V}\) the target volume.
Shape derivative
Using the Céa method the shape derivative of the compliance minimization problem is defined as :
where \(n\) is the unit normal.
To account for the constraint in the optimization, the ALM (see Augmented lagrangian Method ) is used. First, we define the modified cost function as:
Then, we calculate the shape derivative of this new function. We obtain the following shape derivative:
Using the shape optimization algorithm, the descent direction is directly defined as:
Algorithm
Application
We study the case of an embedded steel beam of \(1\text{m}\times 2\text{m}\) subjected to a uniformly distributed tensile load at \(\pm 0.5\) m such that \(g=-0.1 e_{y}\) GPa. The numerical values used as parameters in the mechanical model are detailed in the Table 1. The domain \(\Omega\) included in \(D\) is initialized as shown in Figure 13, and it is discretized with a \(100\times200\) finite element mesh. The problem is solved with a target area of \(1.2 \text{ m}^{2}\) and an initial area of \(1.64m^{2}\). The optimal shape obtained is shown in CutFEM solution with CutFEM method and in Ersatz solution for Ersatz method.
Parameter |
Value |
Unity |
|---|---|---|
E (Young Modulus) |
210 |
GPa |
\(\nu\) |
0.33 |
|
Strength |
0.1 |
GPa |
Initialization of all parameters
The first step is to instantiate the Parameter object and then initialize all of its attributes using the provided data file named “param_compliance.txt”.
1parameters = Parameters("compliance")
2name = "param_compliance.txt"
3parameters.set__paramFolder(name)
Mesh Generation
Then to generate the rectangular mesh seen in Figure 14 the function create_mesh.create_mesh_2D() is called.
1from create_mesh import *
2
3msh = create_mesh_2D(parameters.lx, parameters.ly, int(parameters.lx/parameters.h),int(parameters.ly/parameters.h))
Initialization of the spaces
Next, we generate the spaces for all the functions that will be used.
1V = fem.functionspace(msh, ("Lagrange", 1, (msh.geometry.dim, )))
2V_vm = fem.functionspace(msh, ("Lagrange", 2, (msh.geometry.dim, )))
3V_ls = fem.functionspace(msh, ("Lagrange", 1))
4Q = fem.functionspace(msh, ("DG", 0))
5V_DG = fem.functionspace(msh, ("DG", 0, (msh.geometry.dim, )))
6
7# Initialization of the spacial coordinate
8x = ufl.SpatialCoordinate(msh)
Initialization of the level set
To initialize the level set function, we use the module geometry_initialization() and the function : geometry_initialization.level_set().
In this module, we define all the desired initializations for the level set function and adjust the call based on the desired configuration.
1import geometry_initialization
2
3ls_func_ufl = geometry_initialization.level_set(x,parameters)
4ls_func_expr = fem.Expression(ls_func_ufl, V_ls.element.interpolation_points())
5ls_func = Function(V_ls)
6ls_func.interpolate(ls_func_expr)
Initialization of the boundary conditions
We define the Dirichlet conditions for the linear elasticity problem, denoted \(\Gamma_{D}\) in Figure 13.
1import numpy as np
2from dolfinx import fem, mesh
3
4fdim = msh.topology.dim - 1 # facet dimension
5
6def clamped_boundary_cantilever(x):
7 return np.isclose(x[0], 0)
8
9boundary_facets = mesh.locate_entities_boundary(msh, fdim,clamped_boundary_cantilever)
10u_D = np.array([0,0], dtype=ScalarType)
11
12bc = fem.dirichletbc(u_D, fem.locate_dofs_topological(V, fdim, boundary_facets), V)
We define the Neumann conditions for the linear elasticity problem, denoted \(\Gamma_{N}\) in Figure 13. Then, we define Dirichlet boundary condition for the velocity field.
1import numpy as np
2from dolfinx.mesh import meshtags
3
4
5def load_marker(x):
6 return np.logical_and(np.isclose(x[0],parameters.lx),np.logical_and(x[1]<(0.55),x[1]>(0.45)))
7
8# Boundary condition for the velocity field.
9boundary_dofs = fem.locate_dofs_geometrical(V_ls, load_marker)
10bc_velocity = fem.dirichletbc(ScalarType(0.), boundary_dofs, V_ls)
11
12# Neumann boundary condition for the primal problem.
13facet_indices, facet_markers = [], []
14facets = mesh.locate_entities(msh, fdim, load_marker)
15facet_indices.append(facets)
16facet_markers.append(np.full_like(facets, 2))
17
18facet_indices = np.hstack(facet_indices).astype(np.int32)
19facet_markers = np.hstack(facet_markers).astype(np.int32)
20sorted_facets = np.argsort(facet_indices)
21facet_tag = meshtags(msh, fdim, facet_indices[sorted_facets], facet_markers[sorted_facets])
22ds = ufl.Measure("ds", domain=msh, subdomain_data=facet_tag)
Instantiation of the objects
- We instantiate the objects of the following classes:
Reinitialization (
levelSet_tool.Reinitialization()),ErsatzMethod (
ersatz_method.ErsatzMethod()),CutFemMethod (
cutfem_method.CutFemMethod()).
Then, we initialize the coefficients of Lamé with the function mechanics_tool.lame_compute().
1Reinitialization = Reinitialization(ls_func, V_ls=V_ls, l=parameters.l_reinit)
2
3ErsatzMethod = ErsatzMethod(ls_func,V_ls, V, ds = ds, bc = bc, parameters = parameters, shift = shift)
4
5CutFemMethod = CutFemMethod(ls_func,V_ls, V, ds = ds, bc = bc, parameters = parameters, shift = shift)
6
7lame_mu,lame_lambda = mechanics_tool.lame_compute(parameters.young_modulus,parameters.poisson)
Instantiation of Problem class
We instantiate the problem class.
Here, we aim to minimize the compliance, so the problem is of the Compliance_Problem class.
Defining the problem object then allows for automating the call of functions to calculate the cost, its integrand,
the integrand of the cost’s derivative, the constraint value, the integrand of the constraint, the integrand of the derivative of the constraint,
and the dual operator if necessary. See problem.Compliance_Problem() for clarification.
1problem_topo = problem.Compliance_Problem()
Resolution of linear elasticity
Our code offers two methods for the optimization problem: the ersatz material method and the CutFEM method. Here, we demonstrate the implementation of both methods for solving the linear elasticity problem.
1if parameters.cutFEM == 1:
2 CutFemMethod.set_measure_dxq(ls_func)
3 uh = CutFemMethod.primal_problem(ls_func_temp,parameters)
4
5 measure = CutFemMethod.dxq
6else:
7 xsi = ErsatzMethod.heaviside(ls_func)
8 uh = ErsatzMethod.primal_problem(ls_func_temp, parameters)
9
10 measure = ufl.dx
11
12cost = problem_topo.cost(uh,ph,lame_mu,lame_lambda,measure,parameters)
Figure 17 Displacement field of the first iteration with CutFEM.
Figure 18 Displacement field of the first iteration with Ersatz method.
Note
The heaviside function is needed for the ersatz method. The definition is given by (14) and we choose \(\varepsilon=0.001\).
Shape derivative computation
1shape_derivative = problem_topo.shape_derivative_integrand(uh,ph,lame_mu,lame_lambda,parameters,measure)
2
3rest_constraint = problem_topo.constraint(uh,lame_mu,lame_lambda,parameters,measure,(1-parameters.cutFEM)*xsi)
4
5shape_derivative_integrand_constraint = problem_topo.shape_derivative_integrand_constraint(uh,ph,lame_mu,lame_lambda,parameters,measure)
Note
For the compliance minimization the dual solution is automatically initialized as zero function.
Update ALM parameters
To update the Augmented Lagrangian parameters we call the almMethod.maj_param_constraint_optim():
1almMethod.maj_param_constraint_optim(parameters,rest_constraint)
Note
To simplify, the penalization method can also be used by simply initializing parameters.ALM to 0. The penalization value must be chosen very carefully, depending on the problem.
Compute the descent direction
To compute the advection velocity, we first call on of this two functions :
depending on the chosen method.
Next, we normalize the solution field using an adaptation of the H1 norm by calling the function cutfem_method.CutFemMethod.velocity_normalization() .
Finally, we compute the maximum of the velocity field to determine a time step for advection that satisfies the CFL condition.
1velocity_field = problem_topo.descent_direction(CutFemMethod.level_set,msh,parameters,bc_velocity,V_ls,\
2 V_DG,rest_constraint,shape_derivative_integrand_constraint,shape_derivative)
3velocity_field = CutFemMethod.velocity_normalization(velocity_field,parameters.alpha_reg_velocity)
4
5velocity_expr = fem.Expression(velocity_field, V_ls.element.interpolation_points())
6velocity = fem.Function(V_ls)
7velocity.interpolate(velocity_expr)
8
9max_velocity = comm.allreduce(np.max(np.abs(velocity.x.array[:])),op=MPI.MAX)
Figure 19 Velocity field of the first iteration.
Advection of the level set function
To advect the level set in the computed descent direction, we call the function cutfem_method.CutFemMethod.cut_fem_adv().
To optimize our code, we first call opti_tool.adapt_c_HJ(), which optimizes the choice of the parameter c when computing the advection time step.
This function uses the evolution of the convergence criterion to gradually decrease the value of c. Then, the function opti_tool.adapt_dt() is
called to adjust the advection time step.
1c_param_HJ = opti_tool.adapt_c_HJ(c_param_HJ,crit,parameters.tol_cost_func,lagrangian)
2
3parameters.dt = opti_tool.adapt_dt(lagrangian_cost,lagrangian_cost_previous,max_velocity,parameters,c_param_HJ)
4
5ls_func_temp = CutFemMethod.cut_fem_adv(ls_func_temp,(1/adv_bool)*parameters.dt, velocity_field)
Reinitialization of the level set
To reinitialize the level set using the P.C. method after setting the number of frequency for the
reinitialization to parameters.step_reinit we call the method levelSet_tool.Reinitialization.reinitializationPC()
1if ((j%parameters.freq_reinit)==0):
2 ls_func_temp, temp_func = Reinitialization.reinitializationPC(ls_func_temp,parameters.step_reinit)
Note
The frequency of the reinitialization is the number of iterations of optimization problem we want to do after reinitialized the level set function. For this exemple we set it to \(1\).
Update all the parameters
1parameters.dt, adv_bool = opti_tool.catch_NAN(cost,lagrangian_cost,rest_constraint,parameters.dt,adv_bool)
2
3if adv_bool<2:
4 parameters.j_max = opti_tool.vm_adapta_HJ(lagrangian_cost,lagrangian_cost_previous,parameters.j_max,parameters.dt,parameters)
5else:
6 parameters.j_max = 1
CutFEM solution
The results of the optimization with the CutFEM method, obtained after a fixed number of iterations set to 300, are provided below. The geometry of the domain obtained corresponds to the usual results found in the literature. It is observed that the constraint imposed on the volume is well satisfied.
Ersatz solution
The results of the optimization with the fictitious material method, obtained after a fixed number of iterations set to 300, are provided below. The geometry of the domain is very similar to the previous one, obtained with CutFEM. It is observed that the constraint imposed on the volume is also well satisfied.