Skip to content

Commit 6881da3

Browse files
committed
ready, except for video
1 parent acd7add commit 6881da3

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed
3.79 MB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
Shape optimization
2+
==================
3+
4+
Shape optimization is about modifying the shape of a domain so that an
5+
objective function is minimized. In this demo, we consider an objective
6+
function constrained to a boundary value problem and implement a simple
7+
mesh-moving shape optimization strategy using Firedrake and pyadjoint. This
8+
tutorial was contributed by `Alberto Paganini <mailto:[email protected]>`__
9+
and was written during the `ccp-dcm hackaton
10+
<https://ccp-dcm.github.io/exeter_hackathon>`__ at Dartington Hall.
11+
12+
Let
13+
14+
.. math::
15+
16+
J(\Omega) = \int_\Omega \big(u(\mathbf{x}) - u_t(\mathbf{x})\big)^2 \,\mathrm{d}\mathbf{x}\,.
17+
18+
where :math:`u:\mathbb{R}^2\to\mathbb{R}` is the solution to the scalar
19+
boundary value problem
20+
21+
.. math::
22+
23+
-\Delta u = 4 \quad \text{in }\Omega\,, \qquad u = 0 \quad \text{on } \partial\Omega
24+
25+
26+
and :math:`u_t:\mathbb{R}^2\to\mathbb{R}` is a target function. In particular,
27+
we consider
28+
29+
.. math::
30+
31+
u_t(x,y) = 1.21 - (x - 0.5)^2 - (y - 0.5)^2\,.
32+
33+
Beside the empty set, the domain that minimizes :math:`J(\Omega)` is a disc of
34+
radius :math:`1.1` centered at :math:`(0.5,0.5)`.
35+
36+
We can now proceed to set up the problem. We import firedrake and pyadjoint and
37+
choose an initial guess (in this case, a unit disc centred at the origin)::
38+
39+
from firedrake import *
40+
from firedrake.adjoint import *
41+
mesh = UnitDiskMesh(refinement_level=3)
42+
43+
Then, we start annotating and turn the mesh coordinates into a control variable::
44+
45+
continue_annotation()
46+
Q = mesh.coordinates.function_space()
47+
dT = Function(Q)
48+
mesh.coordinates.assign(mesh.coordinates + dT)
49+
50+
We can now implement the target function::
51+
52+
x, y = SpatialCoordinate(mesh)
53+
u_t = Constant(1.21) - (x - Constant(0.5))**2 - (y - Constant(0.5))**2
54+
55+
solve the weak form of the boundary value problem::
56+
57+
V = FunctionSpace(mesh, "CG", 1)
58+
u = Function(V, name='state')
59+
v = TestFunction(V)
60+
F = (dot(grad(u), grad(v)) - 4 * v) * dx
61+
bcs = DirichletBC(V, Constant(0.), "on_boundary")
62+
solve(F == 0, u, bcs=bcs)
63+
64+
and evaluate the objective function::
65+
66+
J = assemble((u - u_t)**2*dx)
67+
68+
We now turn the objective function into a reduced function so that pyadjoint
69+
(and UFL shape differentiation capability) can automatically compute shape
70+
gradients, that is, directions of steepest ascent::
71+
72+
Jred = ReducedFunctional(J, Control(dT))
73+
stop_annotating()
74+
75+
We now have all the ingredients to implement a basic steepest descent shape
76+
optimization algorithm with fixed step size.::
77+
78+
File = VTKFile("shape_iterates.pvd")
79+
for ii in range(30):
80+
print("J(ii =", ii, ") =", Jred(dT))
81+
File.write(mesh.coordinates)
82+
83+
# compute the gradient (steepest ascent)
84+
opts = {"riesz_representation": "H1"}
85+
gradJ = Jred.derivative(options=opts)
86+
87+
# update domain
88+
dT -= 0.2*gradJ
89+
90+
File.write(mesh.coordinates)
91+
print("J(final) =", Jred(dT))
92+
93+
<video width="640" height="360" controls loop autoplay muted>
94+
<source src="shape_optimization.avi" type="video/avi">
95+
Your browser does not support the video tag.
96+
</video>
97+
98+
**Remark:** mesh-moving shape optimization can lead to mesh tangling, which
99+
invalidates finite element computations. For faster and more robust shape
100+
optimization, we recommend using Firedrake's shape optimization toolbox
101+
`Fireshape <https://github.com/fireshape/fireshape>`__.
102+
103+
A python script version of this demo can be found :demo:`here <shape_optimization.py>`.

0 commit comments

Comments
 (0)