Examples and tutorials » Shape optimization examples » Shape optimization of a cantilever

Shows how to optimize the shape of a cantilever.

Image

Introduction

This example performs boundary-variation shape optimization of a 2D cantilever beam. The goal is to find a shape $ \Omega $ that minimizes the structural compliance (i.e. maximizes stiffness) subject to a volume constraint:

\[ \min_\Omega \; J(\Omega) = \int_\Omega A e(u) : e(u)\,dx + \ell\,|\Omega| \]

where $ u $ solves the linearized elasticity system on $ \Omega $ and $ \ell $ is a Lagrange multiplier penalizing the volume.

Problem Setup

Includes

We need the core Rodin modules plus the linear-elasticity helper and the MMG remesher:

#include <Rodin/Solver.h>
#include <Rodin/Geometry.h>
#include <Rodin/Assembly.h>
#include <Rodin/Variational.h>
#include <Rodin/LinearElasticity.h>
#include <Rodin/MMG.h>

using namespace Rodin;
using namespace Rodin::Geometry;
using namespace Rodin::Variational;

Parameters

The boundary is decomposed into three parts:

  • $ \Gamma_0 $ : traction-free (the shape boundary to be optimized)
  • $ \Gamma_D $ : clamped (homogeneous Dirichlet)
  • $ \Gamma_N $ : loaded (Neumann, downward force)
static constexpr Geometry::Attribute Gamma0 = 1; // Traction-free boundary
static constexpr Geometry::Attribute GammaD = 2; // Clamped (Dirichlet)
static constexpr Geometry::Attribute GammaN = 3; // Loaded (Neumann)

// Lamé coefficients for the material
static constexpr Real mu     = 0.3846;
static constexpr Real lambda = 0.5769;

// Optimization parameters
static constexpr size_t maxIt = 50;         // Max iterations
static constexpr Real hmax   = 0.1;         // Mesh size (max)
static constexpr Real hmin   = 0.1 * hmax;  // Mesh size (min)
static constexpr Real ell    = 5.0;         // Volume penalty
static constexpr Real alpha  = 4 * hmax;    // Regularization

Loading the Mesh

The initial cantilever mesh is loaded from an MFEM file using the MMG::Mesh class, which extends Mesh with remeshing capabilities:

MMG::Mesh Omega;
Omega.load(meshFile, IO::FileFormat::MFEM);

Optimization Loop

Each iteration consists of four steps:

  1. Solve the state equation (linearized elasticity)
  2. Compute the shape gradient via the Hilbert extension-regularization
  3. Move the mesh along the descent direction
  4. Remesh the deformed domain with MMG

Step 1 — State Equation

We solve the elasticity problem on the current domain. The LinearElasticityIntegral assembles the bilinear form for the Hooke tensor with Lamé parameters $ \lambda $ and $ \mu $ :

int d = Omega.getSpaceDimension();
P1 sh(Omega);           // Scalar P1 space
P1 vh(Omega, d);        // Vector P1 space (d components)

VectorFunction f{0, -1};  // Downward force

TrialFunction u(vh);
TestFunction  v(vh);
Problem elasticity(u, v);
elasticity = LinearElasticityIntegral(u, v)(lambda, mu)
           - BoundaryIntegral(f, v).over(GammaN)
           + DirichletBC(u, VectorFunction{0, 0}).on(GammaD);
Solver::SparseLU(elasticity).solve();

Step 2 — Shape Gradient

The shape derivative of the compliance is a boundary integral involving the Cauchy stress tensor $ \sigma = Ae $ . We compute a smooth descent direction $ g $ using the Hilbert extension-regularization method:

TrialFunction g(vh);
TestFunction  w(vh);

// Strain and stress from the state equation
auto e  = 0.5 * (Jacobian(u.getSolution()) + Jacobian(u.getSolution()).T());
auto Ae = 2.0 * mu * e + lambda * Trace(e) * IdentityMatrix(d);

Problem hilbert(g, w);
hilbert = Integral(alpha * Jacobian(g), Jacobian(w))
        + Integral(g, w)
        - BoundaryIntegral(Dot(Ae, e) - ell,
                           Dot(BoundaryNormal(Omega), w)).over(Gamma0)
        + DirichletBC(g, VectorFunction{0, 0}).on({GammaD, GammaN});
Solver::SparseLU(hilbert).solve();

Step 3 — Mesh Displacement

The descent direction is normalized and the mesh is displaced:

GridFunction norm(sh);
norm = Frobenius(dJ);
g.getSolution() *= hmin / norm.max();
Omega.displace(g.getSolution());

Step 4 — Remeshing with MMG

After displacing the mesh, we remesh with the MMG::Optimizer to maintain good element quality:

MMG::Optimizer().setHMax(hmax).setHMin(hmin).optimize(Omega);

Full Source Code

The complete source code is:

See Also