From 93f87ca94abc8b99ccba8e9f463d67bae5018be6 Mon Sep 17 00:00:00 2001 From: Sebastian Blauth Date: Wed, 11 Oct 2023 09:19:56 +0200 Subject: [PATCH 1/3] Start with documentation of topology optimization --- .../poisson_clover/config.ini | 59 ++++ .../poisson_clover/demo_poisson_clover.py | 285 ++++++++++++++++++ .../poisson_clover/img_poisson_clover.png | Bin 0 -> 61402 bytes .../poisson_clover/results/history.json | 1 + .../poisson_clover/results/history.txt | 131 ++++++++ .../demos/topology_optimization/index.rst | 11 + docs/source/user/index.rst | 5 +- 7 files changed, 490 insertions(+), 2 deletions(-) create mode 100755 demos/documented/topology_optimization/poisson_clover/config.ini create mode 100644 demos/documented/topology_optimization/poisson_clover/demo_poisson_clover.py create mode 100644 demos/documented/topology_optimization/poisson_clover/img_poisson_clover.png create mode 100644 demos/documented/topology_optimization/poisson_clover/results/history.json create mode 100644 demos/documented/topology_optimization/poisson_clover/results/history.txt create mode 100644 docs/source/user/demos/topology_optimization/index.rst diff --git a/demos/documented/topology_optimization/poisson_clover/config.ini b/demos/documented/topology_optimization/poisson_clover/config.ini new file mode 100755 index 00000000..8a87a267 --- /dev/null +++ b/demos/documented/topology_optimization/poisson_clover/config.ini @@ -0,0 +1,59 @@ +[StateSystem] +is_linear = True +newton_rtol = 1e-11 +newton_atol = 1e-13 +newton_iter = 50 +newton_damped = True +newton_inexact = False +newton_verbose = False +picard_iteration = False +picard_rtol = 1e-10 +picard_atol = 1e-12 +picard_iter = 10 +picard_verbose = False + +[OptimizationRoutine] +algorithm = bfgs +rtol = 1e-10 +atol = 0.0 +max_iter = 10 +gradient_method = direct +gradient_tol = 1e-9 +soft_exit = True + +[LineSearch] +initial_stepsize = 1.0 +#safeguard_stepsize = True +epsilon_armijo = 1e-4 +beta_armijo = 2 +;method = polynomial + +[AlgoLBFGS] +bfgs_memory_size = 5 +use_bfgs_scaling = True + +[AlgoCG] +cg_method = PR +cg_periodic_restart = False +cg_periodic_its = 5 +cg_relative_restart = False +cg_restart_tol = 0.5 + +[AlgoTNM] +inner_newton = cg +max_it_inner_newton = 100 +inner_newton_rtol = 1e-15 +inner_newton_atol = 0.0 + +[TopologyOptimization] +topological_derivative_is_identical = True + +[Output] +verbose = True +save_results = True +save_txt = True +save_state = False +save_adjoint = False +save_gradient = False +time_suffix = False + diff --git a/demos/documented/topology_optimization/poisson_clover/demo_poisson_clover.py b/demos/documented/topology_optimization/poisson_clover/demo_poisson_clover.py new file mode 100644 index 00000000..37381f62 --- /dev/null +++ b/demos/documented/topology_optimization/poisson_clover/demo_poisson_clover.py @@ -0,0 +1,285 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.14.4 +# --- + +# ```{eval-rst} +# .. include:: ../../../global.rst +# ``` +# +# (demo_poisson_clover)= +# # Topology Optimization with a Poisson Equation +# +# ## Some remarks about topology optimization +# +# As topology optimization problems are very involved from a theoretical point of view, +# it is, at the moment, not possible to automatically derive topological derivatives. +# Therefore, cashocs cannot be used as "black-box" solver for topology optimization +# problems in general. +# +# Moreover, our framework for topology optimization of using a level-set function is +# quite flexible, but requires a lot of theoretical understanding. In the following, we +# briefly go over some theoretical foundations required for using cashocs' topology +# optimization features. We refer the reader, e.g., to CITE for an exhaustive treatment +# of these topics. +# +# ## Solving topology optimization problems with a level-set method +# +# The general form of a topology optimization problem is given by +# +# $$ +# \min_\Omega J(\Omega), +# $$ +# +# i.e., we want to minimize some cost functional {math}`J` depending on a geometry or +# set {math}`\Omega` which is contained in some hold-all domain {math}`\mathrm{D}` by +# varying the topology and shape of {math}`\Omega`. In contrast to shape optimization +# problems we have discussed previously, topology optimization considers topological +# changes of {math}`\Omega`, i.e., the addition or removal of material. +# +# One can represent the domain {math}`\Omega` with a continuous level-set function {math}`\Psi +# \colon \mathrm{D} \to \mathbb{R}` as follows: +# +# $$ +# \begin{align} +# \Psi(x) < 0 \quad &\Leftrightarrow \quad x \in \Omega,\\ +# \Psi(x) > 0 \quad &\Leftrightarrow \quad x \in \mathcal{D} \setminus \bar{\Omega},\\ +# \Psi(x) = 0 \quad &\Leftrightarrow \quad x \in \partial \Omega \setminus \partial \mathrm{D}. +# \end{align} +# $$ +# +# Assuming that the topological derivative of the problem exists, which we denote by +# {math}`DJ(\Omega)`, then the generalized topological derivative is given by +# +# $$ +# \mathcal{D}J(\Omega)(x) = +# \begin{cases} +# -DJ(\Omega)(x) \quad &\text{ if } x \in \Omega,\\ +# DJ(\Omega)(x) \quad &\text{ if } x \in \mathcal{D} \setminus \bar{\Omega}. +# \end{cases} +# $$ +# +# This is motivated by the fact that if there exists some constant {math}`c > 0` such +# that +# +# $$ +# \mathcal{D}J(\Omega)(x) = c\Psi(x) \quad \text{ for all } x \in \mathrm{D}, +# $$ +# +# then we have that {math}`DJ(\Omega) \geq 0` which is a necessary condition for +# {math}`\Omega` being a local minimizer of the problem. +# According to this, several optimization algorithms have been developed in the +# literature to solve topology optimization problems with level-set methods. We refer +# the reader to [Blauth and Sturm - Quasi-Newton methods for topology optimization +# using a level-set method](https://doi.org/10.1007/s00158-023-03653-2) for a detailed +# discussion of the algorithms implemented in cashocs. +# +# +# ## Problem Formulation +# +# In this demo, we investigate the basics of using cashocs for solving topology +# optimization problems with a level-set approach. This approachs goes back to +# [Amstutz and Andrä - A new algorithm for topology optimization using a level-set +# method](https://doi.org/10.1016/j.jcp.2005.12.015) and in cashocs we have implemented +# novel gradient-type and quasi-Newton methods for solving such problems from [Blauth +# and Sturm - Quasi-Newton methods for topology optimization using a level-set +# method](https://doi.org/10.1007/s00158-023-03653-2). +# +# In this demo, we consider the following model problem +# +# $$ +# \begin{align} +# &\min_{\Omega,u} J(\Omega, u) = \frac{1}{2} \int_\mathrm{D} \left( u - u_\mathrm{des} \right)^2 \text{ d}x\\ +# &\text{subject to} \qquad +# \begin{alignedat}[t]{2} +# - \Delta u + \alpha_\Omega u &= f_\Omega \quad &&\text{ in } \mathrm{D},\\ +# u &= 0 \quad &&\text{ on } \partial \mathrm{D}. +# \end{alignedat} +# \end{align} +# $$ +# +# Here, {math}`\alpha_\Omega` and {math}`f_\Omega` have jumps between {math}`\Omega` +# and {math}`\mathrm{D} \setminus \bar{\Omega}` and are given by +# {math}`\alpha_\Omega(x) = \chi_\Omega(x)\alpha_\mathrm{in} + +# \chi_{\Omega^c}(x) \alpha_\mathrm{out}` and {math}`f_\Omega(x) = \chi_\Omega(x) +# f_\mathrm{in} + \chi_{\Omega^c}(x) f_\mathrm{out}` with {math}`\alpha_\mathrm{in}, +# \alpha_\mathrm{out} > 0` and {math}`f_\mathrm{in}, f_\mathrm{out} \in \mathbb{R}`, and +# {math}`\Omega^c = \mathrm{D} \setminus \bar{\Omega}` is the complement of +# {math}`\Omega` in {math}`\mathrm{D}`. Moreover, {math}`u_\mathrm{des}` is a desired +# state, which in our case comes from the solution of the state equation for a desired +# shape, which we want to recover by the above problem. The generalized topological +# derivative for this problem is given by +# +# $$ +# \mathcal{D}J(\Omega)(x) = (\alpha_\mathrm{out} - \alpha_\mathrm{in}) u(x)p(x) +# - (f_\mathrm{out} - f_\mathrm{in})p(x), +# $$ +# where {math}`u` solves the state equation and {math}`p` solves the adjoint equation. +# +# ## Implementation +# +# The complete python code can be found in the file {download}`demo_poisson_clover.py +# `, +# and the corresponding config can be found in {download}`config.ini +# `. +# +# ### Initialization and setup +# +# As usual, we start by using a wildcard import for FEniCS and by importing cashocs + +# + +from fenics import * + +import cashocs + +# - + +# Next, as before, we can specify the verbosity of cashocs with the line + +cashocs.set_log_level(cashocs.LogLevel.INFO) + +# As with the other problem types, the solution algorithms of cashocs can be adapted +# with the help of configuration files, which is loaded with the {py:func}`load_config < +# cashocs.load_config>` function + +cfg = cashocs.load_config("config.ini") + +# In the next step, we define the mesh used for discretization of the hold-all domain +# {math}`\mathrm{D}`. To do so, we use the in-built {py:func}`cashocs.regular_box_mesh` +# function, which gives us a discretization of {math}`\mathrm{D} = (-2, 2)^2`. + +mesh, subdomains, boundaries, dx, ds, dS = cashocs.regular_box_mesh( + n=32, start_x=-2, start_y=-2, end_x=2, end_y=2, diagonal="crossed" +) + +# Now, we define the function spaces required for solving the state system, i.e., a +# finite element space consisting of piecewise linear Lagrange elements, and we define +# a finite element space consisting of piecewise constant elements, which is used to +# discretize the jumping coefficients {math}`\alpha_\Omega` and {math}`f_\Omega`. + +V = FunctionSpace(mesh, "CG", 1) +DG0 = FunctionSpace(mesh, "DG", 0) + +# Now, we define the level-set function used to represent our domain {math}`\Omega` and +# initialize {math}`\Omega` to the empty set by setting {math}`\Psi = 1` with the lines + +psi = Function(V) +psi.vector()[:] = 1.0 + +# We define the constants for the jumping coefficients as follows + +# + +f_1 = 10.0 +f_2 = 1.0 +f_diff = f_1 - f_2 + +alpha_1 = 10.0 +alpha_2 = 1.0 +alpha_diff = alpha_1 - alpha_2 + +alpha = Function(DG0) +f = Function(DG0) +# - +# + + +def create_desired_state(): + a = 4.0 / 5.0 + b = 2 + + f_expr = Expression( + "0.1 * ( " + + "(sqrt(pow(x[0] - a, 2) + b * pow(x[1], 2)) - 1)" + + "* (sqrt(pow(x[0] + a, 2) + b * pow(x[1], 2)) - 1)" + + "* (sqrt(b * pow(x[0], 2) + pow(x[1] - a, 2)) - 1)" + + "* (sqrt(b * pow(x[0], 2) + pow(x[1] + a, 2)) - 1)" + + "- 0.001)", + degree=1, + a=a, + b=b, + ) + psi_des = interpolate(f_expr, V) + + cashocs.interpolate_levelset_function_to_cells(psi_des, alpha_1, alpha_2, alpha) + cashocs.interpolate_levelset_function_to_cells(psi_des, f_1, f_2, f) + + y_des = Function(V) + v = TestFunction(V) + + F = dot(grad(y_des), grad(v)) * dx + alpha * y_des * v * dx - f * v * dx + bcs = cashocs.create_dirichlet_bcs(V, Constant(0.0), boundaries, [1, 2, 3, 4]) + + cashocs.newton_solve(F, y_des, bcs, is_linear=True) + + return y_des, psi_des + + +y_des, psi_des = create_desired_state() + +y = Function(V) +p = Function(V) +F = dot(grad(y), grad(p)) * dx + alpha * y * p * dx - f * p * dx +bcs = cashocs.create_dirichlet_bcs(V, Constant(0.0), boundaries, [1, 2, 3, 4]) +J = cashocs.IntegralFunctional(Constant(0.5) * pow(y - y_des, 2) * dx) + +# Now, we have to define the topological derivative of the problem, which we do with + +dJ_in = Constant(alpha_diff) * y * p - Constant(f_diff) * p +dJ_out = Constant(alpha_diff) * y * p - Constant(f_diff) * p + +# ::::{note} +# We remark that the generalized topological derivative for this problem is identical +# in {math}`\Omega` and {math}`\Omega^c`, which is usually not the case. For this +# reason, the special structure of the problem can be exploited with the following +# lines in the configuration file +# ```{code-block} ini +# :caption: config.ini +# [TopologyOptimization] +# topological_derivative_is_identical = True +# ``` +# :::: + + +def update_level_set(): + cashocs.interpolate_levelset_function_to_cells(psi, alpha_1, alpha_2, alpha) + cashocs.interpolate_levelset_function_to_cells(psi, f_1, f_2, f) + + +top = cashocs.TopologyOptimizationProblem( + F, bcs, J, y, p, psi, dJ_in, dJ_out, update_level_set, config=cfg +) +top.solve(algorithm="bfgs", rtol=0.0, atol=0.0, angle_tol=1.0, max_iter=100) + +# We visualize the results with the following code + +# + +import matplotlib.pyplot as plt + +plt.figure(figsize=(10, 5)) + +ax_mesh = plt.subplot(1, 2, 1) +top.plot_shape() +plt.title("Obtained shape") + +ax_u = plt.subplot(1, 2, 2) +psi.vector()[:] = psi_des.vector()[:] +top.plot_shape() +plt.title("Desired shape") + +plt.tight_layout() +# plt.savefig("./img_poisson_clover.png", dpi=150, bbox_inches="tight") +# - + +# and the result looks like this +# ![](/../../demos/documented/topology_optimization/poisson_clover/img_poisson_clover.png) +# +# :::{note} +# The method {py:meth}`plot_shape ` can +# be used to visualize the current shape based on the level-set function `psi`. Note +# that the cells inside {math}`\Omega` are colored in yellow and the ones outside are +# colored blue. diff --git a/demos/documented/topology_optimization/poisson_clover/img_poisson_clover.png b/demos/documented/topology_optimization/poisson_clover/img_poisson_clover.png new file mode 100644 index 0000000000000000000000000000000000000000..c53a83832e7b8646f67a6eceda1d3c95077a35d1 GIT binary patch literal 61402 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 D2S)df literal 0 HcmV?d00001 diff --git a/demos/documented/topology_optimization/poisson_clover/results/history.json b/demos/documented/topology_optimization/poisson_clover/results/history.json new file mode 100644 index 00000000..bed6f5b8 --- /dev/null +++ b/demos/documented/topology_optimization/poisson_clover/results/history.json @@ -0,0 +1 @@ +{"cost_function_value": [0.5800616682006292, 0.3325676606666016, 0.02707149694927578, 0.0061372271898759816, 0.004323504265685982, 0.0025310661177898926, 0.0011373846144194745, 0.0005528302647165692, 0.0005018996071803278, 0.00029288630657896676, 0.000248261058371371, 0.00023807724535494332, 0.00021284818254684894, 0.0002081364675489232, 0.00019087438360595243, 0.0001865571095941604, 0.0001818349441218058, 0.0001787585058251572, 0.00017426649832585626, 0.00017285172609108557, 0.00016783184546712552, 0.0001649605380526463, 0.00016199329678191616, 0.0001595233445034876, 0.00015792284802262295, 0.00015425732858625983, 0.0001506339477757503, 0.00014910624251586048, 0.0001465123164315774, 0.00014456591573916064, 0.00014208996173679226, 0.00013925335270173058, 0.00013803388992773242, 0.00013342844218676103, 0.00013163008056849416, 0.0001267993667742858, 0.00012428241856956182, 0.00012165563025302349, 0.00011913110446445137, 0.00011621608348320211, 0.00011331922760637426, 0.00011019449155011358, 0.0001078826409576415, 0.00010349598340910188, 0.00010326858357449918, 9.072548046928046e-05, 8.73939639758474e-05, 8.6138552340671e-05, 8.297451820582854e-05, 8.06172866966374e-05, 7.955241774461983e-05, 7.75020389340465e-05, 7.584250838194973e-05, 7.451639565974259e-05, 7.312720681260359e-05, 7.146279247280698e-05, 7.030871907790785e-05, 6.772769206829198e-05, 6.382850605129322e-05, 6.278276875333249e-05, 6.176856724930628e-05, 6.108911714865468e-05, 6.0331336909636015e-05, 5.862907164284715e-05, 5.8097436148112936e-05, 5.7148918176835746e-05, 5.642012606978659e-05, 5.577522393158527e-05, 5.452720670651872e-05, 5.3974197578770065e-05, 5.3235999244289656e-05, 5.2429689829191113e-05, 5.185256435313535e-05, 5.122997381144141e-05, 5.0275061049338364e-05, 4.9864224515258e-05, 4.894655415339667e-05, 4.837234048657562e-05, 4.737542885417463e-05, 4.656648060568855e-05, 4.5849236893957204e-05, 4.5362305736445496e-05, 4.4824153874892256e-05, 4.34994709101804e-05, 4.289190683061302e-05, 4.215373981689771e-05, 4.1796420689199904e-05, 4.126584762725859e-05, 4.0098248339907105e-05, 3.9203854430128684e-05, 3.8691047007562264e-05, 3.7829436111721646e-05, 3.712106947196059e-05, 3.685744415693845e-05, 3.5138415922113483e-05, 3.409395125787242e-05, 3.351264595245779e-05, 3.3023839657888254e-05, 3.250595208843186e-05, 3.119120487990661e-05, 3.060205036832637e-05], "gradient_norm": [1.0, 0.11612026259585996, 0.0736695679496803, 0.023471225208265962, 0.013956871691454254, 0.014964105215826176, 0.0066680065747805085, 0.003998959948220842, 0.005680664031263855, 0.0013481064119710664, 0.002332528673297748, 0.0025525290455773704, 0.0015602576195056055, 0.0018224707351637882, 0.0007227702614402375, 0.0009039810690824934, 0.0006228733267016275, 0.0009633561295152585, 0.0006696547861262369, 0.0010881854975368977, 0.0007146932641201976, 0.0006383344648877808, 0.0007039877012931328, 0.0006760499387336251, 0.0008598590329900928, 0.0005858024435586715, 0.0006505704583152244, 0.0007881539786952937, 0.0006498805948982514, 0.0008316190169396586, 0.0007496355998230718, 0.000738352521506001, 0.000904366957828999, 0.0005802165048465591, 0.0009312851279228254, 0.0005663123308637409, 0.0005973538223597061, 0.0004986764975763527, 0.0006652135580318397, 0.0004427696849530114, 0.0007085442015327113, 0.0004135991471013626, 0.0009074351694361684, 0.00035116080720942473, 0.0016369870479602355, 0.0006393707465054658, 0.00038866434279824995, 0.000305867464591039, 0.00036303266421006987, 0.0003173426812874372, 0.0006902815430941769, 0.00037798484839961254, 0.0002925854638500425, 0.0005605677618955574, 0.0003281072711700456, 0.00026102354233245367, 0.0007260761822664446, 0.0002688040176383007, 0.0003506968837153382, 0.0003099723460033232, 0.00043861494359000386, 0.00030755901242149846, 0.00023814178217436083, 0.0004180427399225918, 0.00033724958981023486, 0.0004532525718959024, 0.00030568571285341707, 0.00022333396212047903, 0.00047178834228195593, 0.00033699391912765864, 0.00046826789542172897, 0.00030589990600797274, 0.00021074866031289054, 0.0005424673452564396, 0.0003237464250430684, 0.0004885074801422721, 0.0003160981539231133, 0.00020040364003670534, 0.00044095584516427763, 0.0002763920377152239, 0.00034257928444917366, 0.00023777884293137914, 0.0001863442103635731, 0.0002958557672703074, 0.00023903234523201707, 0.0002782986676127266, 0.00020847347362119826, 0.00017671628557563388, 0.00021744679245855032, 0.00018729778875216244, 0.0004166332962465147, 0.0002221079645741389, 0.0001707618603784822, 0.0005555514573030554, 0.0001991269688817341, 0.00022293612569490522, 0.0003226179639286749, 0.00020563230312354826, 0.00014718430355699014, 0.0003230791915900703, NaN], "stepsize": [1.0, 1.0, 0.5, 0.375, 0.5625, 0.10546875, 0.03955078125, 0.0296630859375, 0.022247314453125, 0.0333709716796875, 0.012514114379882812, 0.01877117156982422, 0.007039189338684082, 0.010558784008026123, 0.003959544003009796, 0.002969658002257347, 0.0022272435016930103, 0.0033408652525395155, 0.0025056489394046366, 0.003758473409106955, 0.002818855056830216, 0.002114141292622662, 0.0031712119389339932, 0.002378408954200495, 0.0035676134313007424, 0.0026757100734755568, 0.004013565110213335, 0.0030101738326600014, 0.002257630374495001, 0.0033864455617425016, 0.002539834171306876, 0.0038097512569603142, 0.0028573134427202357, 0.0042859701640803535, 0.003214477623060265, 0.004821716434590398, 0.001808143662971399, 0.0027122154944570986, 0.002034161620842824, 0.0030512424312642357, 0.002288431823448177, 0.0034326477351722656, 0.002574485801379199, 0.003861728702068799, 0.0057925930531031984, 0.008688889579654798, 0.0008145833980926373, 0.001221875097138956, 0.001832812645708434, 0.002749218968562651, 0.001030957113210994, 0.001546435669816491, 0.0023196535047247365, 0.0008698700642717762, 0.0013048050964076642, 0.0019572076446114964, 0.0014679057334586222, 0.0022018586001879333, 0.0033027879002819, 0.002477090925211425, 0.0004644545484771422, 0.0006966818227157134, 0.00104502273407357, 0.001567534101110355, 0.0023513011516655326, 0.00044086896593728737, 0.0006613034489059311, 0.0009919551733588968, 0.0014879327600383452, 0.0022318991400575175, 0.00041848108876078454, 0.0006277216331411768, 0.0009415824497117652, 0.0014123736745676478, 0.0021185605118514716, 0.00039723009597215095, 0.0005958451439582264, 0.0008937677159373396, 0.0013406515739060095, 0.0020109773608590143, 0.0003770582551610652, 0.0005655873827415977, 0.0008483810741123966, 0.001272571611168595, 0.0019088574167528925, 0.00035791076564116735, 0.000536866148461751, 0.0008052992226926265, 0.0012079488340389398, 0.0018119232510584097, 0.0006794712191469036, 0.0010192068287203553, 0.001528810243080533, 0.0011466076823103996, 0.0017199115234655994, 0.002579867285198399, 0.00048372511597469985, 0.0007255876739620498, 0.0010883815109430746, 0.0016325722664146118, 0.002448858399621918], "MeshQuality": [], "angle": [152.90568212114098, 117.52594687892473, 66.23252231550758, 54.546032756260416, 86.90645381751298, 150.3995933453613, 76.92408931684015, 151.5873267097817, 49.261112476241074, 149.43511340723035, 64.07306384188557, 149.66084356714325, 78.29471728120745, 151.76922429391928, 136.98757486110196, 156.77985077617404, 145.7395699710482, 155.45021442842008, 136.66674228203095, 153.5964194185855, 125.62362511700643, 155.00110378417256, 121.6931540809874, 153.75972832200242, 93.48293446352575, 151.0910856367846, 115.20869138005033, 150.65012148728601, 106.83470748108654, 149.49541621744964, 82.48506954271484, 148.6653766585283, 64.16658120594283, 146.6176576163968, 57.66606574959234, 145.11437379783172, 75.27567728744518, 142.5619396096702, 61.55305906097176, 138.85016819439846, 54.27291167905152, 136.11027888453398, 46.11280436047297, 119.34503321462509, 39.8195953909605, 140.0779254301748, 58.18277159893409, 87.58955707932621, 58.41013066372018, 126.18179802932285, 42.15276401120938, 52.30643679523279, 121.09351545792094, 42.920203363374625, 55.45106564525192, 103.5994852345549, 40.2012089708276, 66.89417688961885, 47.46158988183377, 128.95586731490693, 42.658606884827186, 50.13117581656329, 74.4425435893362, 42.410754018219706, 132.64652974168794, 41.238161568350485, 47.45858301154192, 75.5045636497889, 40.40965571589888, 133.208779961422, 40.23984267579459, 45.385412995532036, 76.81568991103727, 39.209710014924056, 132.9529827072238, 39.511956866293325, 43.40873487123275, 77.37199287851651, 39.38865153800499, 129.50000677252177, 40.61917729641425, 47.25096510707069, 72.45588249061119, 40.73391074762887, 124.52591843291134, 40.71285806622886, 47.3113138102391, 61.95802193525898, 43.92553517918081, 107.62800738953091, 37.200782969429376, 41.701561901204485, 96.71738798678328, 36.629354279453686, 42.27003657025293, 124.44488680996731, 36.92171472116081, 40.20894184620379, 67.81336136112684, 36.515761666876735, 36.515761666876735], "initial_gradient_norm": 0.9946178152040442, "state_solves": 167, "adjoint_solves": 100, "iterations": 100} \ No newline at end of file diff --git a/demos/documented/topology_optimization/poisson_clover/results/history.txt b/demos/documented/topology_optimization/poisson_clover/results/history.txt new file mode 100644 index 00000000..3d39cb8b --- /dev/null +++ b/demos/documented/topology_optimization/poisson_clover/results/history.txt @@ -0,0 +1,131 @@ + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 0, 5.801e-01, 1.000e+00, 9.946e-01, 152.906, + + 1, 9.714e-02, 2.761e-01, 2.746e-01, 123.964, 1.000e+00 + 2, 1.649e-02, 5.593e-02, 5.563e-02, 62.276, 1.000e+00 + 3, 1.103e-02, 4.471e-02, 4.447e-02, 60.985, 1.000e+00 + 4, 1.288e-03, 1.141e-02, 1.135e-02, 121.413, 1.000e+00 + 5, 4.884e-04, 3.750e-03, 3.730e-03, 93.332, 1.000e+00 + 6, 2.442e-04, 1.236e-03, 1.230e-03, 93.774, 1.000e+00 + 7, 2.006e-04, 2.517e-03, 2.503e-03, 132.759, 1.000e+00 + 8, 1.956e-04, 2.096e-03, 2.085e-03, 132.942, 1.000e+00 + 9, 1.810e-04, 1.429e-03, 1.422e-03, 131.191, 1.000e+00 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 10, 1.722e-04, 3.840e-03, 3.820e-03, 127.601, 5.000e-01 + 11, 1.598e-04, 3.228e-03, 3.211e-03, 127.581, 5.000e-01 + 12, 1.223e-04, 9.861e-04, 9.808e-04, 121.283, 1.000e+00 + 13, 9.798e-05, 7.730e-04, 7.689e-04, 118.013, 1.000e+00 + 14, 9.640e-05, 4.486e-03, 4.462e-03, 54.618, 5.000e-01 + 15, 6.022e-05, 5.404e-04, 5.375e-04, 66.581, 1.000e+00 + 16, 4.595e-05, 1.271e-03, 1.264e-03, 56.587, 1.000e+00 + 17, 3.086e-05, 2.008e-03, 1.997e-03, 55.071, 3.125e-02 + 18, 2.018e-05, 2.102e-03, 2.090e-03, 53.997, 6.250e-02 + 19, 2.017e-05, 2.102e-03, 2.091e-03, 53.888, 1.562e-02 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 20, 8.333e-06, 1.017e-03, 1.012e-03, 53.631, 1.000e+00 + 21, 3.758e-06, 3.760e-04, 3.740e-04, 59.377, 1.000e+00 + 22, 2.996e-06, 2.596e-04, 2.582e-04, 111.928, 1.000e+00 + 23, 2.330e-06, 1.300e-04, 1.293e-04, 74.581, 1.000e+00 + 24, 1.908e-06, 2.748e-04, 2.733e-04, 53.160, 5.000e-01 + 25, 1.879e-06, 3.292e-04, 3.274e-04, 54.288, 5.000e-01 + 26, 1.852e-06, 4.246e-04, 4.223e-04, 56.770, 1.000e+00 + 27, 1.404e-06, 2.434e-04, 2.421e-04, 60.859, 1.000e+00 + 28, 1.167e-06, 1.127e-04, 1.121e-04, 86.777, 1.000e+00 + 29, 1.010e-06, 1.106e-04, 1.100e-04, 114.831, 2.500e-01 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 30, 1.008e-06, 1.174e-04, 1.168e-04, 117.420, 6.250e-02 + 31, 9.642e-07, 9.175e-05, 9.126e-05, 109.540, 1.000e+00 + 32, 8.635e-07, 6.078e-05, 6.046e-05, 112.307, 1.000e+00 + 33, 8.193e-07, 5.357e-05, 5.328e-05, 90.282, 6.250e-02 + 34, 8.062e-07, 4.568e-05, 4.543e-05, 93.192, 1.000e+00 + 35, 7.328e-07, 1.272e-04, 1.265e-04, 123.783, 2.500e-01 + 36, 6.926e-07, 1.057e-04, 1.052e-04, 123.590, 2.500e-01 + 37, 5.983e-07, 2.840e-05, 2.825e-05, 92.617, 1.000e+00 + 38, 5.861e-07, 3.007e-05, 2.991e-05, 78.739, 1.000e+00 + 39, 4.880e-07, 5.021e-05, 4.994e-05, 60.253, 1.000e+00 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 40, 3.246e-07, 4.887e-05, 4.861e-05, 57.186, 1.000e+00 + 41, 3.100e-07, 2.294e-05, 2.281e-05, 63.973, 1.000e+00 + 42, 3.013e-07, 1.654e-05, 1.645e-05, 105.046, 1.000e+00 + 43, 2.937e-07, 2.208e-05, 2.196e-05, 116.908, 1.000e+00 + 44, 2.604e-07, 3.808e-05, 3.788e-05, 121.899, 1.000e+00 + 45, 1.912e-07, 2.194e-05, 2.182e-05, 119.732, 1.000e+00 + 46, 1.191e-07, 2.055e-05, 2.044e-05, 63.295, 1.000e+00 + 47, 1.086e-07, 1.851e-05, 1.841e-05, 66.786, 1.000e+00 + 48, 1.008e-07, 1.115e-05, 1.109e-05, 121.867, 2.500e-01 + 49, 9.792e-08, 1.405e-05, 1.398e-05, 128.871, 1.000e+00 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 50, 9.535e-08, 1.606e-05, 1.598e-05, 128.687, 2.000e+00 + 51, 9.252e-08, 8.358e-06, 8.313e-06, 120.722, 4.000e+00 + 52, 8.735e-08, 1.113e-05, 1.107e-05, 125.272, 1.000e+00 + 53, 8.666e-08, 7.606e-06, 7.565e-06, 122.146, 1.000e+00 + 54, 8.451e-08, 5.215e-06, 5.187e-06, 103.400, 1.000e+00 + 55, 7.902e-08, 4.687e-06, 4.662e-06, 100.363, 1.000e+00 + 56, 4.615e-08, 3.968e-05, 3.946e-05, 50.964, 1.000e+00 + 57, 4.547e-08, 3.956e-05, 3.935e-05, 51.670, 2.500e-01 + 58, 4.184e-08, 2.364e-05, 2.351e-05, 50.390, 1.000e+00 + 59, 3.896e-08, 1.812e-05, 1.802e-05, 50.537, 1.000e+00 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 60, 3.393e-08, 1.596e-05, 1.588e-05, 55.182, 1.000e+00 + 61, 3.122e-08, 4.962e-06, 4.935e-06, 113.815, 3.125e-02 + 62, 3.094e-08, 4.319e-06, 4.296e-06, 56.404, 1.000e+00 + 63, 3.093e-08, 7.118e-06, 7.080e-06, 54.503, 2.500e-01 + 64, 3.056e-08, 3.672e-06, 3.652e-06, 57.683, 1.000e+00 + 65, 3.015e-08, 2.653e-06, 2.638e-06, 76.438, 1.000e+00 + 66, 2.926e-08, 2.648e-06, 2.634e-06, 71.589, 1.000e+00 + 67, 2.059e-08, 2.329e-05, 2.317e-05, 50.823, 2.500e-01 + 68, 2.008e-08, 2.165e-05, 2.153e-05, 50.724, 2.500e-01 + 69, 1.602e-08, 6.023e-06, 5.990e-06, 50.576, 1.000e+00 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 70, 1.594e-08, 1.030e-05, 1.025e-05, 120.027, 1.000e+00 + 71, 1.540e-08, 1.942e-06, 1.931e-06, 53.049, 1.000e+00 + 72, 1.520e-08, 1.983e-06, 1.972e-06, 51.651, 1.000e+00 + 73, 1.511e-08, 2.026e-06, 2.015e-06, 50.896, 1.000e+00 + 74, 1.495e-08, 2.000e-06, 1.989e-06, 50.856, 2.000e+00 + 75, 1.470e-08, 1.349e-05, 1.342e-05, 119.369, 1.250e-01 + 76, 1.378e-08, 7.189e-06, 7.151e-06, 52.281, 1.000e+00 + 77, 1.309e-08, 7.923e-06, 7.880e-06, 53.116, 1.000e+00 + 78, 1.266e-08, 2.799e-06, 2.784e-06, 51.099, 1.000e+00 + 79, 1.251e-08, 1.583e-06, 1.575e-06, 70.094, 1.000e+00 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 80, 1.239e-08, 1.814e-06, 1.805e-06, 80.754, 1.000e+00 + 81, 1.151e-08, 3.363e-06, 3.345e-06, 61.982, 1.000e+00 + 82, 1.088e-08, 4.780e-06, 4.755e-06, 47.399, 1.000e+00 + 83, 1.075e-08, 1.044e-05, 1.038e-05, 51.171, 2.500e-01 + 84, 1.050e-08, 5.721e-06, 5.690e-06, 49.199, 1.000e+00 + 85, 1.033e-08, 3.351e-06, 3.333e-06, 48.368, 1.000e+00 + 86, 9.986e-09, 2.432e-06, 2.419e-06, 48.626, 1.000e+00 + 87, 8.772e-09, 9.585e-06, 9.534e-06, 51.448, 1.000e+00 + 88, 8.531e-09, 4.881e-06, 4.854e-06, 49.094, 1.000e+00 + 89, 8.411e-09, 2.606e-06, 2.592e-06, 47.590, 1.000e+00 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 90, 8.212e-09, 2.550e-06, 2.536e-06, 48.003, 1.000e+00 + 91, 8.209e-09, 3.063e-06, 3.047e-06, 98.480, 1.562e-02 + 92, 8.012e-09, 4.404e-06, 4.380e-06, 50.333, 5.000e-01 + 93, 7.916e-09, 2.329e-06, 2.317e-06, 48.532, 1.000e+00 + 94, 7.829e-09, 1.666e-06, 1.657e-06, 50.600, 1.000e+00 + 95, 7.552e-09, 3.511e-06, 3.492e-06, 48.171, 1.000e+00 + 96, 7.518e-09, 2.573e-06, 2.559e-06, 47.321, 1.000e+00 + 97, 7.380e-09, 2.901e-06, 2.885e-06, 47.979, 1.000e+00 + 98, 7.349e-09, 2.203e-06, 2.191e-06, 47.554, 1.000e+00 + 99, 7.198e-09, 2.770e-06, 2.755e-06, 47.725, 1.000e+00 diff --git a/docs/source/user/demos/topology_optimization/index.rst b/docs/source/user/demos/topology_optimization/index.rst new file mode 100644 index 00000000..a54c7dbb --- /dev/null +++ b/docs/source/user/demos/topology_optimization/index.rst @@ -0,0 +1,11 @@ +Topology Optimization Problems +============================== + +In this part of the tutorial, we investigate how topology optimization problems can +be treated with cashocs. + +.. toctree:: + :maxdepth: 1 + :caption: List of all topology optimization problems: + + demo_poisson_clover.md diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst index eb9b7bab..566b5891 100644 --- a/docs/source/user/index.rst +++ b/docs/source/user/index.rst @@ -4,8 +4,8 @@ cashocs Tutorial ================ Welcome to the cashocs tutorial. In the following, we present several example -programs that showcase how cashocs can be used to solve optimal control and -shape optimization problems. +programs that showcase how cashocs can be used to solve optimal control, +shape optimization, and topology optimization problems. .. include:: ../../../README.rst :start-after: readme_start_disclaimer @@ -25,6 +25,7 @@ Shape Optimization and Optimal Control Software Date: Thu, 12 Oct 2023 13:12:44 +0200 Subject: [PATCH 2/3] Add demos for topology optimization --- README.rst | 3 + .../cantilever/config.ini | 56 ++++ .../cantilever/demo_cantilever.py | 317 ++++++++++++++++++ .../cantilever/img_cantilever.png | Bin 0 -> 53961 bytes .../cantilever/results/history.json | 1 + .../cantilever/results/history.txt | 40 +++ .../pipe_bend/config.ini | 58 ++++ .../pipe_bend/demo_pipe_bend.py | 276 +++++++++++++++ .../pipe_bend/img_pipe_bend.png | Bin 0 -> 28385 bytes .../pipe_bend/results/history.json | 1 + .../pipe_bend/results/history.txt | 65 ++++ .../poisson_clover/demo_poisson_clover.py | 159 +++++++-- .../user/demos/shape_optimization/index.rst | 2 +- .../demos/topology_optimization/index.rst | 11 +- 14 files changed, 957 insertions(+), 32 deletions(-) create mode 100644 demos/documented/topology_optimization/cantilever/config.ini create mode 100644 demos/documented/topology_optimization/cantilever/demo_cantilever.py create mode 100644 demos/documented/topology_optimization/cantilever/img_cantilever.png create mode 100644 demos/documented/topology_optimization/cantilever/results/history.json create mode 100644 demos/documented/topology_optimization/cantilever/results/history.txt create mode 100644 demos/documented/topology_optimization/pipe_bend/config.ini create mode 100644 demos/documented/topology_optimization/pipe_bend/demo_pipe_bend.py create mode 100644 demos/documented/topology_optimization/pipe_bend/img_pipe_bend.png create mode 100644 demos/documented/topology_optimization/pipe_bend/results/history.json create mode 100644 demos/documented/topology_optimization/pipe_bend/results/history.txt diff --git a/README.rst b/README.rst index 864d7dcd..34e48356 100755 --- a/README.rst +++ b/README.rst @@ -65,6 +65,9 @@ we can recommend the textbooks - Shape Optimization - `Delfour and Zolesio - Shapes and Geometries `_ - `Sokolowski and Zolesio - Introduction to Shape Optimization `_ +- Topology Optimization + - `Sokolowski and Novotny - Topological Derivatives in Shape Optimization `_ + - `Amstutz - An Introduction to the Topological Derivative `_ - FEniCS - `Logg, Mardal, and Wells - Automated Solution of Differential Equations by the Finite Element Method `_ - `The FEniCS demos `_ diff --git a/demos/documented/topology_optimization/cantilever/config.ini b/demos/documented/topology_optimization/cantilever/config.ini new file mode 100644 index 00000000..0f529226 --- /dev/null +++ b/demos/documented/topology_optimization/cantilever/config.ini @@ -0,0 +1,56 @@ +[StateSystem] +is_linear = True +newton_rtol = 1e-11 +newton_atol = 1e-13 +newton_iter = 50 +newton_damped = True +newton_inexact = False +newton_verbose = False +picard_iteration = False +picard_rtol = 1e-10 +picard_atol = 1e-12 +picard_iter = 10 +picard_verbose = False + +[OptimizationRoutine] +algorithm = bfgs +rtol = 1e-10 +atol = 0.0 +max_iter = 1000 +gradient_method = direct +gradient_tol = 1e-9 +soft_exit = True + +[LineSearch] +;method = polynomial +initial_stepsize = 1e-3 +safeguard_stepsize = False +epsilon_armijo = 1e-20 +beta_armijo = 2.0 + +[AlgoLBFGS] +bfgs_memory_size = 5 +use_bfgs_scaling = True +bfgs_periodic_restart = 3 + +[AlgoCG] +cg_method = PR +cg_periodic_restart = False +cg_periodic_its = 5 +cg_relative_restart = False +cg_restart_tol = 0.5 + +[AlgoTNM] +inner_newton = cg +max_it_inner_newton = 100 +inner_newton_rtol = 1e-15 +inner_newton_atol = 0.0 + +[Output] +verbose = True +save_results = True +save_txt = True +save_state = False +save_adjoint = False +save_gradient = False +time_suffix = False diff --git a/demos/documented/topology_optimization/cantilever/demo_cantilever.py b/demos/documented/topology_optimization/cantilever/demo_cantilever.py new file mode 100644 index 00000000..475d1de8 --- /dev/null +++ b/demos/documented/topology_optimization/cantilever/demo_cantilever.py @@ -0,0 +1,317 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.14.4 +# --- + +# ```{eval-rst} +# .. include:: ../../../global.rst +# ``` +# +# (demo_cantilever)= +# # Topology Optimization with Linear Elasticity - Cantilever +# +# ## Problem Formulation +# +# In this demo, we consider the topology optimization of linear elastic structures, +# using the well-known cantilever example, which has been investigated, e.g., in +# [Blauth and Sturm - Quasi-Newton methods for topology optimization +# using a level-set method](https://doi.org/10.1007/s00158-023-03653-2) and +# [Amstutz and Andrä - A new algorithm for topology optimization using a level-set +# method](https://doi.org/10.1016/j.jcp.2005.12.015). Our goal is to minimize the +# compliance of a linear elastic material, which can be formulated via the topology +# optimization problem +# +# $$ +# \begin{align} +# &\min_{\Omega,u} J(\Omega,u) = \int_\mathrm{D} \alpha_\Omega \sigma(u):e(u) \text{d}x + \gamma \lvert \Omega \rvert \\ +# &\text{subject to} \qquad +# \begin{alignedat}{2} +# -\text{div}(\alpha_\Omega \sigma(u)) &= f \quad &&\text{ in } \mathrm{D},\\ +# u &= 0 \quad &&\text{ on } \Gamma_D,\\ +# \alpha_\Omega \sigma(u)n &= g \quad &&\text{ on } \Gamma_N. +# \end{alignedat} +# \end{align} +# $$ +# +# Here, {math}`u` is the deformation of a linear elastic material, {math}`\sigma(u) = +# 2 \mu e(u) + \lambda \text{tr}e(u)I` is Hooke's tensor, where {math}`e(u) = +# \frac{1}{2}\left( \nabla u + (\nabla u)^T \right)` is the symmetric gradient. The +# coefficients {math}`\mu` and {math}`\lambda` are the Lamé parameters which satisfy +# {math}`\mu \geq 0` and {math}`2\mu + d \lambda > 0`, where {math}`d` is the dimension +# of the problem. As in {ref}`demo_poisson_clover`, the coefficient +# {math}`\alpha_\Omega` is given by {math}`\alpha_\Omega(x) = +# \chi_\Omega(x)\alpha_\mathrm{in} + \chi_{\Omega^c}(x) \alpha_\mathrm{out}`, and it +# models the elasticity of the material. We note that the term +# {math}`\gamma \lvert \Omega\rvert` is used to penalize too large domains +# {math}`\Omega` so that not the trivial solution {math}`\Omega = \mathrm{D}` is found. +# On the Dirichlet boundary {math}`\Gamma_D`, the material is fixed, and at the +# Neumann boundary {math}`\Gamma_N` a load {math}`g` is applied. +# +# Note that the generalized topological derivative for this problem is given by +# +# $$ +# \mathcal{D}J(\Omega)(x) = +# \begin{cases} +# \alpha_\mathrm{in} \frac{r_\mathrm{in} - 1}{\kappa r_\mathrm{in} +1} +# \frac{\kappa + 1}{2}\left( 2 \sigma(u): e(u) + +# \frac{(r_\mathrm{in}-1)(\kappa - 2)}{\kappa + 2 r_\mathrm{in} - 1} +# \text{tr}\sigma(u)\text{tr}e(u) \right) + \gamma \quad &\text{ for } x \in \Omega,\\ +# -\alpha_\mathrm{out} \frac{r_\mathrm{out} - 1}{\kappa r_\mathrm{out} + 1} +# \frac{\kappa + 1}{2} \left( 2 \sigma(u):e(u) + +# \frac{(r_\mathrm{out} - 1)(\kappa - 2)}{\kappa + 2r_\mathrm{out} - 1} \text{tr} +# \sigma(u) \text{tr}e(u) \right) + \gamma \quad &\text{ for } x \in +# \Omega^c = \mathrm{D}\setminus \bar{\Omega}, +# \end{cases} +# $$ +# +# where {math}`r_\mathrm{in} = \frac{\alpha_\mathrm{out}}{\alpha_\mathrm{in}}`, +# {math}`r_\mathrm{out} = \frac{\alpha_\mathrm{in}}{\alpha_\mathrm{out}}`, and +# {math}`\kappa = \frac{\lambda + 3\mu}{\lambda + \mu}`. +# +# :::{note} +# In the following, we consider only two-dimensional problems. For this case of plane +# stress, the Lamé parameters are given by {math}`\mu = \frac{E}{2(1+\nu)}` and +# {math}`\lambda = \frac{2\mu \lambda^*}{\lambda^* + 2\mu}`, where {math}`\lambda^* = +# \frac{E\nu}{(1+\nu)(1-2\nu)}` and {math}`E` and {math}`\nu` denote the Young's +# modulus and Poisson's ratio of the material, respectively. +# ::: +# +# ## Implementation +# +# The complete python code can be found in the file {download}`demo_cantilever.py +# `, +# and the corresponding config can be found in {download}`config.ini +# `. +# +# ### Initialization and Setup +# +# As with all other demos, we start by importing FEniCS and cashocs. +# +# + + +from fenics import * + +import cashocs + +# - +# +# Next, we load the configuration file of the problem with the line + +cfg = cashocs.load_config("config.ini") + +# Following this, we define the computational domain, using the built-in +# {py:func}`cashocs.regular_mesh` function, so that our hold-all domain is given by +# {math}`\mathrm{D} = (0,2) \times (0,1)`. + +mesh, subdomains, boundaries, dx, ds, dS = cashocs.regular_mesh( + 32, length_x=2.0, diagonal="crossed" +) + +# Next up, we define the function spaces for the problems. The solution of the state +# (and adjoint) systems lives in a vector function space of piecewise linear Lagrange +# elements, which is defined with + +V = VectorFunctionSpace(mesh, "CG", 1) + +# We also define scalar piecewise linear Lagrange elements (the `CG1` space) and +# piecewise constant discontinuous Lagrange elements (the `DG0` space) in the following. +# The `CG1` space is needed to represent the level-set function and the `DG0` space +# is used to treat the jumping coefficients. + +CG1 = FunctionSpace(mesh, "CG", 1) +DG0 = FunctionSpace(mesh, "DG", 0) + +# Now, we define the level-set function which represents our geometry (see +# {ref}`the previous demo `), and we initialize it with +# {math}`\Psi = -1`, so that {math}`\Omega = \mathrm{D}` as initial guess. + +psi = Function(CG1) +psi.vector()[:] = -1.0 + +# ### Definition of the Material Parameters +# +# In the following lines, we define the physical and numerical parameters for the +# material and optimization problem. First, the penalty parameter {math}`\gamma` is +# defined as + +gamma = 100.0 + +# Next, we define the Lamé parameters for the material, using a Young's modulus of +# {math}`E = 1` and Poisson's ratio of {math}`\nu = 0.3` + +E = 1.0 +nu = 0.3 +mu = E / (2.0 * (1.0 + nu)) +lambd_star = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu)) +lambd = 2 * mu * lambd_star / (lambd_star + 2.0 * mu) + +# Finally, the parameters {math}`\alpha_\mathrm{in}` and {math}`\alpha_\mathrm{out}` are +# defined as + +alpha_in = 1.0 +alpha_out = 1e-3 +alpha = Function(DG0) + +# which models the presence of material in {math}`\Omega` as well as the absence +# thereof in {math}`\Omega^c`. As before, {math}`\alpha` is a jumping coefficient, +# so that we define a piecewise constant FEM function which will represent it later. +# +# As we also need to take care of the volume {math}`\lvert \Omega \rvert`, we define a +# indicator function for the domain {math}`\Omega`, which is, as the jumping coefficient +# represented by a piecewise constant function. + + +indicator_omega = Function(DG0) + +# :::{note} +# As cells cut by the level-set function +# will recieve averaged values in these piecewise constant functions, the integral +# of the indicator function even gives us the exact volume of {math}`\Omega`. +# ::: +# +# ### Definition of the State System +# +# In the following, we define two python functions which return Hooke's tensor and +# the symmetrized gradient + +# + + + +def eps(u): + return Constant(0.5) * (grad(u) + grad(u).T) + + +def sigma(u): + return Constant(2.0 * mu) * eps(u) + Constant(lambd) * tr(eps(u)) * Identity(2) + + +# - +# +# For the load applied to the system we use a unitary point load at (2, 0.5), i.e., in +# the middle of the outer rightmost boundary. To do so, a Dirac-Delta function can be +# defined via a FEniCS `UserExpression` as follows. + + +class Delta(UserExpression): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def eval(self, values, x): + if near(x[0], 2.0) and near(x[1], 0.5): + values[0] = 3.0 / mesh.hmax() + else: + values[0] = 0.0 + + def value_shape(self): + return () + + +# We use this definition to define the point load as follows + +delta = Delta(degree=2) +g = delta * Constant((0.0, -1.0)) + +# Next, we define the state and adjoint variables {math}`u` and {math}`v` + +u = Function(V) +v = Function(V) + +# as well as the state system, the linear elasticity equations + +F = alpha * inner(sigma(u), eps(v)) * dx - dot(g, v) * ds(2) + +# which is supplied with boundary conditions of the form + +bcs = cashocs.create_dirichlet_bcs(V, Constant((0.0, 0.0)), boundaries, 1) + +# ### Setup of the Cost Functional and Topological Derivative +# +# To define the topology optimization problem, we first define the cost functional of +# the problem, given by + +J = cashocs.IntegralFunctional( + alpha * inner(sigma(u), eps(u)) * dx + Constant(gamma) * indicator_omega * dx +) + +# :::{note} +# As stated before, the integration of the function `indiciator_omega` gives the exact +# volume of {math}`\Omega`, so that the regularization term can be written as above. +# ::: + +# Finally, as stated previously, we have to specify the generalized topological +# derivative of the problem, which has been derived above. This is done with the lines + +# + + +kappa = (lambd + 3.0 * mu) / (lambd + mu) +r_in = alpha_out / alpha_in +r_out = alpha_in / alpha_out + +dJ_in = ( + Constant(alpha_in * (r_in - 1.0) / (kappa * r_in + 1.0) * (kappa + 1.0) / 2.0) + * ( + Constant(2.0) * inner(sigma(u), eps(u)) + + Constant((r_in - 1.0) * (kappa - 2.0) / (kappa + 2 * r_in - 1.0)) + * tr(sigma(u)) + * tr(eps(u)) + ) +) + Constant(gamma) +dJ_out = ( + Constant(-alpha_out * (r_out - 1.0) / (kappa * r_out + 1.0) * (kappa + 1.0) / 2.0) + * ( + Constant(2.0) * inner(sigma(u), eps(u)) + + Constant((r_out - 1.0) * (kappa - 2.0) / (kappa + 2 * r_out - 1.0)) + * tr(sigma(u)) + * tr(eps(u)) + ) +) + Constant(gamma) + +# - +# +# As in {ref}`demo_poisson_clover`, we now only have to specify what needs to happen +# when the level-set function is updated, i.e., when the geometry changes. Of course, +# the jumping coefficient {math}`\alpha` needs to be updated with the +# {py:func}`cashocs.interpolate_levelset_function_to_cells` function, but so does the +# indicator function of the geometry. This is specified in the following function. + + +def update_level_set(): + cashocs.interpolate_levelset_function_to_cells(psi, alpha_in, alpha_out, alpha) + cashocs.interpolate_levelset_function_to_cells(psi, 1.0, 0.0, indicator_omega) + + +# :::{note} +# As we want `indicator_omega` to be the indicator function of {math}`\Omega`, the +# above call makes sure that it is {math}`1` inside {math}`\Omega` and {math}`0` outside +# of it, representing a proper indicator function. +# ::: +# +# ### Definition and Solution of the Topology Optimization Problem +# +# Now, all that is left to do is to define the +# {py:class}`TopologyOptimizationProblem ` and +# solve it with its {py:meth}`solve ` method. + +top = cashocs.TopologyOptimizationProblem( + F, bcs, J, u, v, psi, dJ_in, dJ_out, update_level_set, config=cfg +) +top.solve(algorithm="bfgs", rtol=0.0, atol=0.0, angle_tol=1.5, max_iter=250) + +# As before, we can visualize the result using the following code + +# + +import matplotlib.pyplot as plt + +top.plot_shape() +plt.title("Obtained Cantilever Design") +plt.tight_layout() +# plt.savefig("./img_cantilever.png", dpi=150, bbox_inches="tight") +# - +# +# and the result looks like this +# ![](/../../demos/documented/topology_optimization/cantilever/img_cantilever.png) diff --git a/demos/documented/topology_optimization/cantilever/img_cantilever.png b/demos/documented/topology_optimization/cantilever/img_cantilever.png new file mode 100644 index 0000000000000000000000000000000000000000..a2efd5c83a0af7441da50d0194db407b1fba41f3 GIT binary patch literal 53961 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


ta3K#{6LMwqoV@Y0s+65B4nj{nc(9 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

KWXt88YK3O`r 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 literal 0 HcmV?d00001 diff --git a/demos/documented/topology_optimization/cantilever/results/history.json b/demos/documented/topology_optimization/cantilever/results/history.json new file mode 100644 index 00000000..c2d2d1ac --- /dev/null +++ b/demos/documented/topology_optimization/cantilever/results/history.json @@ -0,0 +1 @@ +{"cost_function_value": [239.53424422622115, 239.53424422622115, 239.53424422622115, 239.53424422622115, 235.45309867653734, 217.37119337596053, 208.9262145441077, 204.25657568071964, 202.85916360255797, 192.34356691224943, 189.7424000081197, 173.89484058803257, 165.16928236538132, 160.33625106628602, 158.41919997820253, 155.22177138931278, 154.15349964695167, 153.82097846830186, 153.80255017368734, 153.8005988441504, 153.799518539605, 153.79290868826308], "gradient_norm": [1.0, 1.0504034110943057, 1.1086364591825344, 1.0334296036131743, 0.9756773557243431, 0.8969587795103783, 0.883465007537447, 0.8764339888075806, 1.1166582098283442, 0.8802108273956757, 1.3527710023399095, 0.8525642977354847, 0.8341363444218072, 0.6465491575498764, 0.4681788522393535, 0.3349867715843251, 0.19452904859918965, 0.12186497762760716, 0.09121079226504318, 0.06917660965920856, 0.06072170916692272, 0.04376877374272062], "stepsize": [1.0, 0.001, 0.002, 0.004, 0.001, 0.03125, 0.015625, 0.0078125, 0.000125, 0.00025, 0.5, 0.001, 1.0, 0.25, 0.001, 1.0, 1.0, 1.0, 0.001, 0.25, 0.125, 0.25], "MeshQuality": [], "angle": [116.1396902089962, 109.44338267687696, 95.58788664858685, 68.08416186350985, 61.16220743510479, 54.06642617028483, 51.83975160125166, 50.6645398689501, 51.41695973229307, 46.13259388791498, 49.424308346035666, 35.78489880614035, 28.936452343936867, 21.91286194790358, 16.271289689226855, 11.101104739392598, 6.441962000461975, 4.045708869251757, 3.0353523937632665, 2.306465793093987, 2.0267069400692987, 1.4644305967635298], "initial_gradient_norm": 117.40766682321009, "state_solves": 84, "adjoint_solves": 23, "iterations": 21} \ No newline at end of file diff --git a/demos/documented/topology_optimization/cantilever/results/history.txt b/demos/documented/topology_optimization/cantilever/results/history.txt new file mode 100644 index 00000000..2e9da1aa --- /dev/null +++ b/demos/documented/topology_optimization/cantilever/results/history.txt @@ -0,0 +1,40 @@ + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 0, 2.395e+02, 1.000e+00, 1.174e+02, 116.140, + + 1, 2.395e+02, 1.050e+00, 1.233e+02, 109.443, 1.000e-03 + 2, 2.395e+02, 1.109e+00, 1.302e+02, 95.588, 2.000e-03 + 3, 2.395e+02, 1.033e+00, 1.213e+02, 68.084, 4.000e-03 + 4, 2.355e+02, 9.757e-01, 1.146e+02, 61.162, 1.000e-03 + 5, 2.174e+02, 8.970e-01, 1.053e+02, 54.066, 3.125e-02 + 6, 2.089e+02, 8.835e-01, 1.037e+02, 51.840, 1.562e-02 + 7, 2.043e+02, 8.764e-01, 1.029e+02, 50.665, 7.812e-03 + 8, 2.029e+02, 1.117e+00, 1.311e+02, 51.417, 1.250e-04 + 9, 1.923e+02, 8.802e-01, 1.033e+02, 46.133, 2.500e-04 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 10, 1.897e+02, 1.353e+00, 1.588e+02, 49.424, 5.000e-01 + 11, 1.739e+02, 8.526e-01, 1.001e+02, 35.785, 1.000e-03 + 12, 1.652e+02, 8.341e-01, 9.793e+01, 28.936, 1.000e+00 + 13, 1.603e+02, 6.465e-01, 7.591e+01, 21.913, 2.500e-01 + 14, 1.584e+02, 4.682e-01, 5.497e+01, 16.271, 1.000e-03 + 15, 1.552e+02, 3.350e-01, 3.933e+01, 11.101, 1.000e+00 + 16, 1.542e+02, 1.945e-01, 2.284e+01, 6.442, 1.000e+00 + 17, 1.538e+02, 1.219e-01, 1.431e+01, 4.046, 1.000e+00 + 18, 1.538e+02, 9.121e-02, 1.071e+01, 3.035, 1.000e-03 + 19, 1.538e+02, 6.918e-02, 8.122e+00, 2.306, 2.500e-01 + +iter, cost function, rel. grad. norm, abs. grad. norm, angle, step size + + 20, 1.538e+02, 6.072e-02, 7.129e+00, 2.027, 1.250e-01 + 21, 1.538e+02, 4.377e-02, 5.139e+00, 1.464, 2.500e-01 + +Optimization was successful. +Statistics: + total iterations: 21 + final objective value: 1.538e+02 + final gradient norm: 4.377e-02 + total number of state systems solved: 84 + total number of adjoint systems solved: 23 diff --git a/demos/documented/topology_optimization/pipe_bend/config.ini b/demos/documented/topology_optimization/pipe_bend/config.ini new file mode 100644 index 00000000..e3be4b4b --- /dev/null +++ b/demos/documented/topology_optimization/pipe_bend/config.ini @@ -0,0 +1,58 @@ +[StateSystem] +is_linear = True +newton_rtol = 1e-8 +newton_atol = 1e-13 +newton_iter = 20 +newton_damped = True +newton_inexact = False +newton_verbose = False +picard_iteration = False +picard_rtol = 1e-10 +picard_atol = 1e-12 +picard_iter = 10 +picard_verbose = False + +[OptimizationRoutine] +algorithm = bfgs +rtol = 1e-10 +atol = 0.0 +max_iter = 1000 +gradient_method = direct +gradient_tol = 1e-9 +soft_exit = True + +[LineSearch] +initial_stepsize = 1.0 +safeguard_stepsize = False +epsilon_armijo = 1e-4 +beta_armijo = 2 +;method = polynomial + +[AlgoLBFGS] +bfgs_memory_size = 5 +use_bfgs_scaling = True + +[AlgoCG] +cg_method = PR +cg_periodic_restart = False +cg_periodic_its = 5 +cg_relative_restart = False +cg_restart_tol = 0.5 + +[AlgoTNM] +inner_newton = cg +max_it_inner_newton = 100 +inner_newton_rtol = 1e-15 +inner_newton_atol = 0.0 + +[TopologyOptimization] +topological_derivative_is_identical = True + +[Output] +verbose = True +save_results = True +save_txt = True +save_state = False +save_adjoint = False +save_gradient = False +time_suffix = False diff --git a/demos/documented/topology_optimization/pipe_bend/demo_pipe_bend.py b/demos/documented/topology_optimization/pipe_bend/demo_pipe_bend.py new file mode 100644 index 00000000..f65351de --- /dev/null +++ b/demos/documented/topology_optimization/pipe_bend/demo_pipe_bend.py @@ -0,0 +1,276 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.14.4 +# --- + +# ```{eval-rst} +# .. include:: ../../../global.rst +# ``` +# +# (demo_pipe_bend)= +# # Topology Optimization with Stokes Flow - Pipe Bend +# +# ## Problem Formulation +# +# In this demo, we consider the topology optimization with Stokes flow, where +# we aim at finding the optimal shape for a pipe bend. This problem has been +# investigated previously, e.g., in [Blauth and Sturm - Quasi-Newton methods for +# topology optimization using a level-set +# method](https://doi.org/10.1007/s00158-023-03653-2) and [Sa, Amigo, Novotny, Silva - +# Topological derivatives applied to fluid flow channel design optimization +# problems](https://doi.org/10.1007/s00158-016-1399-0). The problem can be written +# as follows +# +# $$ +# \begin{align} +# &\min_{\Omega,u} J(\Omega, u) = \int_\mathrm{D} \mu \nabla u : \nabla u + +# \alpha_\Omega u \cdot u \text{d}x + \frac{\lambda}{2}\left( \lvert \Omega \rvert - \text{vol}_\mathrm{des} \right)^2 \\ +# &\text{subject to} \qquad +# \begin{alignedat}{2} +# -\mu \Delta u + \nabla p + \alpha_\Omega u &= 0 \quad &&\text{ in } \mathrm{D},\\ +# \nabla \cdot u &= 0 \quad &&\text{ in } \mathrm{D},\\ +# u &= u_D \quad &&\text{ on } \partial \mathrm{D},\\ +# p &= 0 \quad &&\text{ at } x^*. +# \end{alignedat} +# \end{align} +# $$ +# +# Here, {math}`u` and {math}`p` denote the fluids velocity and pressure, respectively, +# {math}`\mu` is its viscosity. Moreover, +# {math}`\alpha` denotes the viscous resistance or inverse permeability of the material, +# which is used to distinguish between fluid (where {math}`\alpha` is low) and solid +# (where {math}`\alpha` is high). As in the previous demos, e.g. {ref}`demo_cantilever`, +# {math}`\alpha` represents a jumping coefficient between the considered materials, +# i.e., it is given by {math}`\alpha_\Omega(x) = +# \chi_\Omega(x)\alpha_\mathrm{in} + \chi_{\Omega^c}(x) \alpha_\mathrm{out}`, so that +# {math}`\Omega` models our fluid and {math}`\Omega^c` models the solid part. +# +# On the outer boundary of the hold-all domain, +# Dirichlet boundary conditions are specified, +# indicating, where the fluid enters and exits. Moreover, the goal of the optimization +# problem is to minimize the energy dissipation of the fluid while achieving a certain +# volume of the fluid region. For more details on this problem, we refer the reader +# to [Blauth and Sturm - Quasi-Newton methods for topology optimization using a +# level-set method](https://doi.org/10.1007/s00158-023-03653-2). +# +# The generalized topological derivative for this problem is given by +# +# $$ +# \mathcal{D}J(\Omega)(x) = \left(\alpha_\mathrm{in} - \alpha_\mathrm{out}\right) +# u(x)\cdot (u(x) + v(x)) +# + \lambda \left( \lvert \Omega \rvert - \text{vol}_\mathrm{des}\right). +# $$ +# +# :::{note} +# We do not specify the adjoint equation here, as this is derived automatically +# by cashocs. For more details, we refer to [Blauth and Sturm - Quasi-Newton methods +# for topology optimization using a level-set +# method](https://doi.org/10.1007/s00158-023-03653-2). +# ::: +# +# :::{attention} +# As in {ref}`demo_poisson_clover`, there is a minus sign missing in our paper +# [Blauth and Sturm - Quasi-Newton methods for topology optimization using a +# level-set method](https://doi.org/10.1007/s00158-023-03653-2). The topological +# derivative presented here is, in fact, correct. +# ::: +# +# ## Implementation +# +# The complete python code can be found in the file {download}`demo_pipe_bend.py +# `, +# and the corresponding config can be found in {download}`config.ini +# `. +# +# ### Initialization and Setup +# +# We start by importing cashocs and FEniCS into our script +# +# + + +from fenics import * +import numpy as np + +import cashocs + +# - +# +# Next, we load the configuration file for the problem and define the mesh with the +# lines + +cfg = cashocs.load_config("config.ini") +mesh, subdomains, boundaries, dx, ds, dS = cashocs.regular_mesh(25, diagonal="crossed") + +# Now, we define the finite element spaces for the functions. These are given by +# a Taylor-Hood space for velocity and pressure variables, a piecewise linear Lagrange +# space for the level-set function, and a piecewise constant discontinuous Lagrange +# space for the jumping coefficients. + +v_elem = VectorElement("CG", mesh.ufl_cell(), 2) +p_elem = FiniteElement("CG", mesh.ufl_cell(), 1) +V = FunctionSpace(mesh, v_elem * p_elem) +CG1 = FunctionSpace(mesh, "CG", 1) +DG0 = FunctionSpace(mesh, "DG", 0) + +# Additionally, we define a scalar real +# function space (`R`), which will be used to deal with the volume regularization +# of the problem, and a function `vol`, which represents the current volume of +# {math}`\Omega`, as follows. + +R = FunctionSpace(mesh, "R", 0) +vol = Function(R) + +# ### Definition of Physical Parameters and Jumping Coefficients +# +# Next, we define the physical parameters for the problem, including a viscosity of +# {math}`\mu = 0.01` as well as parameters for {math}`\alpha_\mathrm{in}` and +# {math}`\alpha_\mathrm{out}` + +mu = 1e-2 +alpha_in = 2.5 * mu / 1e2**2 +alpha_out = 2.5 * mu * 1e2**2 +alpha = Function(DG0) + +# As before, the jumping coefficient {math}`\alpha` is represented using a piecewise +# constant (on each element) function. +# +# Next, the desired volume and the regularization parameter {math}`\lambda` are defined, +# together with the indicator function of {math}`\Omega` (see {ref}`demo_cantilever`). + +vol_des = assemble(1 * dx) * 0.08 * np.pi +lambd = 1e4 +indicator_omega = Function(DG0) + +# Then, the level-set function is introduced and set to {math}`\Psi = -1`, so that we +# use {math}`\Omega = \mathrm{D}` as initial guess. + +psi = Function(CG1) +psi.vector()[:] = -1.0 + +# ### Definition of the State System +# +# In the following, we define the state and adjoint variables of our problem, +# in analogy to, e.g., {ref}`demo_shape_stokes`: + +up = Function(V) +u, p = split(up) +vq = Function(V) +v, q = split(vq) + +# and the weak form of the Stokes system is given by the lines + +F = ( + Constant(mu) * inner(grad(u), grad(v)) * dx + - p * div(v) * dx + - q * div(u) * dx + + alpha * dot(u, v) * dx +) + +# For the Dirichlet boundary conditions, we specify that we have an inflow at +# the upper left part of the boundary as well as an outflow on the lower right part, +# with the following expressions.Additionally, we specify the pressure at the point +# {math}`x^* = (0,0)` to obtain uniqueness of the pressure. +# Altogether, the boundary conditions are specified using the code +# +# + + +v_max = 1e0 +v_in = Expression( + ("(x[1] >= 0.7 && x[1] <= 0.9) ? v_max*(1 - 100*pow(x[1] - 0.8, 2)) : 0.0", "0.0"), + degree=2, + v_max=v_max, +) +v_out = Expression( + ("0.0", "(x[0] >= 0.7 && x[0] <= 0.9) ? -v_max*(1 - 100*pow(x[0] - 0.8, 2)) : 0.0"), + degree=2, + v_max=v_max, +) + + +def pressure_point(x, on_boundary): + return near(x[0], 0) and near(x[1], 0) + + +bcs = cashocs.create_dirichlet_bcs(V.sub(0), v_in, boundaries, 1) +bcs += cashocs.create_dirichlet_bcs(V.sub(0), v_out, boundaries, 3) +bcs += cashocs.create_dirichlet_bcs(V.sub(0), Constant((0.0, 0.0)), boundaries, [2, 4]) +bcs += [DirichletBC(V.sub(1), Constant(0), pressure_point, method="pointwise")] + +# - +# +# ### Cost Functional and Topological Derivative +# +# Now, we define the cost functional of our problem as well as its corresponding +# generalized topological derivative with the lines + +J = cashocs.IntegralFunctional( + Constant(mu) * inner(grad(u), grad(u)) * dx + + alpha * dot(u, u) * dx + + Constant(lambd / 2) * pow(vol - Constant(vol_des), 2) * dx +) +dJ_in = Constant(alpha_in - alpha_out) * (dot(u, v) + dot(u, u)) + Constant(lambd) * ( + vol - Constant(vol_des) +) +dJ_out = Constant(alpha_in - alpha_out) * (dot(u, v) + dot(u, u)) + Constant(lambd) * ( + vol - Constant(vol_des) +) + +# :::{note} +# Note, that the second term of the cost functional measures the discrepancy between +# the current volume `vol` of {math}`\Omega` and the desired volume. Due to the +# `update_level_set` function, which is defined below, the variable `vol` holds the +# correct value in each iteration. +# ::: + +# As in the previous demos, we have to specify the update routine of the level-set +# function `update_level_set`, which we do as follows: + + +def update_level_set(): + cashocs.interpolate_levelset_function_to_cells(psi, alpha_in, alpha_out, alpha) + cashocs.interpolate_levelset_function_to_cells(psi, 1.0, 0.0, indicator_omega) + vol.vector()[0] = assemble(indicator_omega * dx) + + +# That is, in the `update_level_set` function, first the jumping coefficient is updated +# with the {py:func}`cashocs.interpolate_levelset_function_to_cells` function. +# Then, the indicator function is updated and used to compute the current volume of the +# domain, which is written to the variable `vol`. +# +# ### Solution of the Topology Optimization Problem +# +# Finally, we can define the topology optimization problem and solve it via +# the lines + +top = cashocs.TopologyOptimizationProblem( + F, bcs, J, up, vq, psi, dJ_in, dJ_out, update_level_set, config=cfg +) +top.solve(algorithm="bfgs", rtol=0.0, atol=0.0, angle_tol=5.0, max_iter=500) + +# :::{note} +# For solving this problem, we choose a rather high tolerance (regarding the angle) +# of 5 degrees. This means, our optimization algorithm has not yet converged. We do +# this to ensure a low running time of the demo. If one wishes, they can run the demo +# with `angle_tol=1.0` and still recieve a result, however, it may take a while +# until this lower tolerance can be reached. +# ::: +# +# We visualize the result with the code +# +# + + +import matplotlib.pyplot as plt + +top.plot_shape() +plt.title("Obtained Pipe Bend Design") +plt.tight_layout() +# plt.savefig("./img_pipe_bend.png", dpi=150, bbox_inches="tight") +# - +# +# and the results looks as follows +# ![](/../../demos/documented/topology_optimization/pipe_bend/img_pipe_bend.png) diff --git a/demos/documented/topology_optimization/pipe_bend/img_pipe_bend.png b/demos/documented/topology_optimization/pipe_bend/img_pipe_bend.png new file mode 100644 index 0000000000000000000000000000000000000000..9a5101b0d7355c11b96251d940e4487ab1c0c560 GIT binary patch literal 28385 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 nt+Le&j@JLXeA?eP<)04wFOFenU)kAA#) +# +# Then, the values of the jumping coefficients {math}`\alpha` and {math\`f` are +# computed for this level-set function, using the +# {py:func}`cashocs.interpolate_levelset_function_to_cells`, which sets the piecewise +# constant functions `alpha` and `f` to the appropriate values according to the +# level-set function: +# :::{code-block} python +# cashocs.interpolate_levelset_function_to_cells(psi_des, alpha_in, alpha_out, alpha) +# cashocs.interpolate_levelset_function_to_cells(psi_des, f_in, f_out, f) +# ::: +# +# Finally, the state system corresponding to the desired shape is written out and +# solved with cashocs using the lines +# :::{code-block} python +# y_des = Function(V) +# v = TestFunction(V) +# +# F = dot(grad(y_des), grad(v)) * dx + alpha * y_des * v * dx - f * v * dx +# bcs = cashocs.create_dirichlet_bcs(V, Constant(0.0), boundaries, [1, 2, 3, 4]) +# +# cashocs.newton_solve(F, y_des, bcs, is_linear=True) +# +# return y_des, psi_des +# ::: +# +# and in the end, the desired state is returned together with the desired level-set +# function (the latter is only used for visualization purposes). +# :::: +# +# Now, the function we defined previously is called to compute the desired state. + y_des, psi_des = create_desired_state() +# ### Definition of the State Problem, Cost Functional, and Topological Derivative +# +# Next, we define the state problem, i.e., the PDE constraint for our minimization +# problem, with the lines + y = Function(V) p = Function(V) F = dot(grad(y), grad(p)) * dx + alpha * y * p * dx - f * p * dx bcs = cashocs.create_dirichlet_bcs(V, Constant(0.0), boundaries, [1, 2, 3, 4]) J = cashocs.IntegralFunctional(Constant(0.5) * pow(y - y_des, 2) * dx) -# Now, we have to define the topological derivative of the problem, which we do with +# Now, we define the generalized topological derivative of the problem, which is given +# above. In python code, this can be done as follows + +dJ_in = Constant(alpha_in - alpha_out) * y * p - Constant(f_in - f_out) * p +dJ_out = Constant(alpha_in - alpha_out) * y * p - Constant(f_in - f_out) * p -dJ_in = Constant(alpha_diff) * y * p - Constant(f_diff) * p -dJ_out = Constant(alpha_diff) * y * p - Constant(f_diff) * p +# :::{attention} +# There is a typo in our paper [Blauth and Sturm - Quasi-Newton methods for topology +# optimization using a level-set method](https://doi.org/10.1007/s00158-023-03653-2), +# i.e., in the paper a minus sign is missing. The topological derivative as stated +# here is, in fact, correct. +# ::: # ::::{note} # We remark that the generalized topological derivative for this problem is identical @@ -243,18 +309,46 @@ def create_desired_state(): # topological_derivative_is_identical = True # ``` # :::: +# +# Finally, we have to define what happens, when the geometry, i.e., the level-set +# function is updated. Of course, a change in the level-set function changes the +# geometry, so that the jumping coefficients {math}`\alpha` and {math}`f` have to be +# updated. This is, as before, done with the function +# {py:func}`cashocs.interpolate_levelset_function_to_cells`, which is called twice, once +# for updating `alpha` and once for `f`. def update_level_set(): - cashocs.interpolate_levelset_function_to_cells(psi, alpha_1, alpha_2, alpha) - cashocs.interpolate_levelset_function_to_cells(psi, f_1, f_2, f) + cashocs.interpolate_levelset_function_to_cells(psi, alpha_in, alpha_out, alpha) + cashocs.interpolate_levelset_function_to_cells(psi, f_in, f_out, f) + +# ### Definition and Solution of the Topology Optimization Problem +# +# Now, defining a topology optimization problem is nearly as easy as defining a shape +# optimization or optimal control problem, namely we instantiate a +# {py:class}`cashocs.TopologyOptimizationProblem` and then can call it's +# {py:meth}`solve ` method. top = cashocs.TopologyOptimizationProblem( F, bcs, J, y, p, psi, dJ_in, dJ_out, update_level_set, config=cfg ) top.solve(algorithm="bfgs", rtol=0.0, atol=0.0, angle_tol=1.0, max_iter=100) +# :::{note} +# Note, that there are several algorithms available for solving such topology +# optimization problems. Among them are the usual "sphere combination" method of +# [Amstutz and Andrä - A new algorithm for topology optimization using a level-set +# method](https://doi.org/10.1016/j.jcp.2005.12.015), which is invoked with +# `algorithm="sphere_combination"`, a convex combination approach, invoked with +# `algorithm="convex_combination"`, and all optimizaton algorithms available for shape +# optimization, i.e., gradient descent (`algorithm="gd"`), nonlinear CG +# (`algorithm="ncg"`) and L-BFGS (`algorithm="bfgs"`) methods. For a comparison of these +# optimization algorithms, we refer the reader to [Blauth +# and Sturm - Quasi-Newton methods for topology optimization using a level-set +# method](https://doi.org/10.1007/s00158-023-03653-2) +# ::: + # We visualize the results with the following code # + @@ -277,9 +371,14 @@ def update_level_set(): # and the result looks like this # ![](/../../demos/documented/topology_optimization/poisson_clover/img_poisson_clover.png) +# As we can see, the BFGS method is able to reconstruct the desired clover shape after +# only about 100 iterations. We encourage readers to try the other (established) methods +# to compare the novel BFGS approach to established techniques and see that the new +# methods are significantly more efficient. # # :::{note} # The method {py:meth}`plot_shape ` can # be used to visualize the current shape based on the level-set function `psi`. Note # that the cells inside {math}`\Omega` are colored in yellow and the ones outside are # colored blue. +# ::: diff --git a/docs/source/user/demos/shape_optimization/index.rst b/docs/source/user/demos/shape_optimization/index.rst index a1cd8bf6..05b8b152 100644 --- a/docs/source/user/demos/shape_optimization/index.rst +++ b/docs/source/user/demos/shape_optimization/index.rst @@ -6,7 +6,7 @@ be treated with cashocs. .. toctree:: :maxdepth: 1 - :caption: List of all shape optimization problems: + :caption: List of all shape optimization demos: demo_shape_poisson.md doc_config diff --git a/docs/source/user/demos/topology_optimization/index.rst b/docs/source/user/demos/topology_optimization/index.rst index a54c7dbb..4f048427 100644 --- a/docs/source/user/demos/topology_optimization/index.rst +++ b/docs/source/user/demos/topology_optimization/index.rst @@ -6,6 +6,15 @@ be treated with cashocs. .. toctree:: :maxdepth: 1 - :caption: List of all topology optimization problems: + :caption: List of all topology optimization demos: demo_poisson_clover.md + demo_cantilever.md + demo_pipe_bend.md + + +.. note:: + + As topology optimization problems are very involved from a theoretical point of view, it is, at the moment, not possible to automatically derive topological derivatives. Therefore, cashocs cannot be used as "black-box" solver for topology optimization problems in general. + + Moreover, our framework for topology optimization of using a level-set function is quite flexible, but requires a lot of theoretical understanding. In :ref:`demo_poisson_clover`, we briefly go over some theoretical foundations required for using cashocs' topology optimization features. We refer the reader, e.g., to `Sokolowski and Novotny - Topological Derivatives in Shape Optimization `_ for an exhaustive treatment of these topics. From 88e0de186af9a03333bbe5305513a3fd282ed5cc Mon Sep 17 00:00:00 2001 From: Sebastian Blauth Date: Thu, 12 Oct 2023 13:30:13 +0200 Subject: [PATCH 3/3] Update the demos --- .../cantilever/demo_cantilever.py | 19 +++++---- .../pipe_bend/demo_pipe_bend.py | 39 +++++++++++++------ .../poisson_clover/demo_poisson_clover.py | 32 ++++++++------- 3 files changed, 55 insertions(+), 35 deletions(-) diff --git a/demos/documented/topology_optimization/cantilever/demo_cantilever.py b/demos/documented/topology_optimization/cantilever/demo_cantilever.py index 475d1de8..a911a7f5 100644 --- a/demos/documented/topology_optimization/cantilever/demo_cantilever.py +++ b/demos/documented/topology_optimization/cantilever/demo_cantilever.py @@ -91,21 +91,21 @@ # ### Initialization and Setup # # As with all other demos, we start by importing FEniCS and cashocs. -# -# + +# + from fenics import * import cashocs # - -# + # Next, we load the configuration file of the problem with the line cfg = cashocs.load_config("config.ini") # Following this, we define the computational domain, using the built-in -# {py:func}`cashocs.regular_mesh` function, so that our hold-all domain is given by +# {py:func}`regular_mesh ` function, so that our hold-all domain +# is given by # {math}`\mathrm{D} = (0,2) \times (0,1)`. mesh, subdomains, boundaries, dx, ds, dS = cashocs.regular_mesh( @@ -179,9 +179,8 @@ # In the following, we define two python functions which return Hooke's tensor and # the symmetrized gradient -# + - +# + def eps(u): return Constant(0.5) * (grad(u) + grad(u).T) @@ -191,7 +190,7 @@ def sigma(u): # - -# + # For the load applied to the system we use a unitary point load at (2, 0.5), i.e., in # the middle of the outer rightmost boundary. To do so, a Dirac-Delta function can be # defined via a FEniCS `UserExpression` as follows. @@ -247,7 +246,6 @@ def value_shape(self): # derivative of the problem, which has been derived above. This is done with the lines # + - kappa = (lambd + 3.0 * mu) / (lambd + mu) r_in = alpha_out / alpha_in r_out = alpha_in / alpha_out @@ -272,11 +270,12 @@ def value_shape(self): ) + Constant(gamma) # - -# + # As in {ref}`demo_poisson_clover`, we now only have to specify what needs to happen # when the level-set function is updated, i.e., when the geometry changes. Of course, # the jumping coefficient {math}`\alpha` needs to be updated with the -# {py:func}`cashocs.interpolate_levelset_function_to_cells` function, but so does the +# {py:func}`interpolate_levelset_function_to_cells +# ` function, but so does the # indicator function of the geometry. This is specified in the following function. diff --git a/demos/documented/topology_optimization/pipe_bend/demo_pipe_bend.py b/demos/documented/topology_optimization/pipe_bend/demo_pipe_bend.py index f65351de..9f8693b3 100644 --- a/demos/documented/topology_optimization/pipe_bend/demo_pipe_bend.py +++ b/demos/documented/topology_optimization/pipe_bend/demo_pipe_bend.py @@ -90,16 +90,15 @@ # ### Initialization and Setup # # We start by importing cashocs and FEniCS into our script -# -# + +# + from fenics import * import numpy as np import cashocs # - -# + # Next, we load the configuration file for the problem and define the mesh with the # lines @@ -176,9 +175,8 @@ # with the following expressions.Additionally, we specify the pressure at the point # {math}`x^* = (0,0)` to obtain uniqueness of the pressure. # Altogether, the boundary conditions are specified using the code -# -# + +# + v_max = 1e0 v_in = Expression( ("(x[1] >= 0.7 && x[1] <= 0.9) ? v_max*(1 - 100*pow(x[1] - 0.8, 2)) : 0.0", "0.0"), @@ -200,9 +198,8 @@ def pressure_point(x, on_boundary): bcs += cashocs.create_dirichlet_bcs(V.sub(0), v_out, boundaries, 3) bcs += cashocs.create_dirichlet_bcs(V.sub(0), Constant((0.0, 0.0)), boundaries, [2, 4]) bcs += [DirichletBC(V.sub(1), Constant(0), pressure_point, method="pointwise")] - # - -# + # ### Cost Functional and Topological Derivative # # Now, we define the cost functional of our problem as well as its corresponding @@ -226,6 +223,18 @@ def pressure_point(x, on_boundary): # `update_level_set` function, which is defined below, the variable `vol` holds the # correct value in each iteration. # ::: +# +# ::::{note} +# As in {ref}`demo_poisson_clover`, the generalized topological derivative for this +# problem is identical in {math}`\Omega` and {math}`\Omega^c`, which is usually not the +# case. For this reason, the special structure of the problem can be exploited with the +# following lines in the configuration file +# ```{code-block} ini +# :caption: config.ini +# [TopologyOptimization] +# topological_derivative_is_identical = True +# ``` +# :::: # As in the previous demos, we have to specify the update routine of the level-set # function `update_level_set`, which we do as follows: @@ -238,7 +247,8 @@ def update_level_set(): # That is, in the `update_level_set` function, first the jumping coefficient is updated -# with the {py:func}`cashocs.interpolate_levelset_function_to_cells` function. +# with the {py:func}`interpolate_levelset_function_to_cells +# ` function. # Then, the indicator function is updated and used to compute the current volume of the # domain, which is written to the variable `vol`. # @@ -261,9 +271,8 @@ def update_level_set(): # ::: # # We visualize the result with the code -# -# + +# + import matplotlib.pyplot as plt top.plot_shape() @@ -271,6 +280,14 @@ def update_level_set(): plt.tight_layout() # plt.savefig("./img_pipe_bend.png", dpi=150, bbox_inches="tight") # - -# + # and the results looks as follows # ![](/../../demos/documented/topology_optimization/pipe_bend/img_pipe_bend.png) +# +# :::{note} +# Note that this design is not final due to the following: First, the tolerance for +# the optimization algorithm is chosen too large, as explained previously. Second, +# the chosen mesh is rather coarse and, thus, the discretization of the shape is rather +# coarse, too. These problems can be overcome by using a finer discretization and a +# lower tolerance. +# ::: diff --git a/demos/documented/topology_optimization/poisson_clover/demo_poisson_clover.py b/demos/documented/topology_optimization/poisson_clover/demo_poisson_clover.py index 9b582788..2923f6a7 100644 --- a/demos/documented/topology_optimization/poisson_clover/demo_poisson_clover.py +++ b/demos/documented/topology_optimization/poisson_clover/demo_poisson_clover.py @@ -108,6 +108,13 @@ # $$ # where {math}`u` solves the state equation and {math}`p` solves the adjoint equation. # +# :::{attention} +# There is a typo in our paper [Blauth and Sturm - Quasi-Newton methods for topology +# optimization using a level-set method](https://doi.org/10.1007/s00158-023-03653-2), +# i.e., in the paper a minus sign is missing. The topological derivative as stated +# here is, in fact, correct. +# ::: +# # ## Implementation # # The complete python code can be found in the file {download}`demo_poisson_clover.py @@ -131,13 +138,14 @@ cashocs.set_log_level(cashocs.LogLevel.INFO) # As with the other problem types, the solution algorithms of cashocs can be adapted -# with the help of configuration files, which is loaded with the {py:func}`load_config < -# cashocs.load_config>` function +# with the help of configuration files, which is loaded with the +# {py:func}`load_config ` function cfg = cashocs.load_config("config.ini") # In the next step, we define the mesh used for discretization of the hold-all domain -# {math}`\mathrm{D}`. To do so, we use the in-built {py:func}`cashocs.regular_box_mesh` +# {math}`\mathrm{D}`. To do so, we use the built-in +# {py:func}`regular_box_mesh ` # function, which gives us a discretization of {math}`\mathrm{D} = (-2, 2)^2`. mesh, subdomains, boundaries, dx, ds, dS = cashocs.regular_box_mesh( @@ -242,9 +250,10 @@ def create_desired_state(): # # Note, that this level-set function is taken from the [NGSolve Tutorials](https://docu.ngsolve.org/latest/i-tutorials/unit-7-optimization/04_Topological_Derivative_Levelset.html>) # -# Then, the values of the jumping coefficients {math}`\alpha` and {math\`f` are +# Then, the values of the jumping coefficients {math}`\alpha` and {math}`f` are # computed for this level-set function, using the -# {py:func}`cashocs.interpolate_levelset_function_to_cells`, which sets the piecewise +# {py:func}`interpolate_levelset_function_to_cells +# `, which sets the piecewise # constant functions `alpha` and `f` to the appropriate values according to the # level-set function: # :::{code-block} python @@ -291,13 +300,6 @@ def create_desired_state(): dJ_in = Constant(alpha_in - alpha_out) * y * p - Constant(f_in - f_out) * p dJ_out = Constant(alpha_in - alpha_out) * y * p - Constant(f_in - f_out) * p -# :::{attention} -# There is a typo in our paper [Blauth and Sturm - Quasi-Newton methods for topology -# optimization using a level-set method](https://doi.org/10.1007/s00158-023-03653-2), -# i.e., in the paper a minus sign is missing. The topological derivative as stated -# here is, in fact, correct. -# ::: - # ::::{note} # We remark that the generalized topological derivative for this problem is identical # in {math}`\Omega` and {math}`\Omega^c`, which is usually not the case. For this @@ -314,7 +316,8 @@ def create_desired_state(): # function is updated. Of course, a change in the level-set function changes the # geometry, so that the jumping coefficients {math}`\alpha` and {math}`f` have to be # updated. This is, as before, done with the function -# {py:func}`cashocs.interpolate_levelset_function_to_cells`, which is called twice, once +# {py:func}`interpolate_levelset_function_to_cells +# `, which is called twice, once # for updating `alpha` and once for `f`. @@ -327,7 +330,8 @@ def update_level_set(): # # Now, defining a topology optimization problem is nearly as easy as defining a shape # optimization or optimal control problem, namely we instantiate a -# {py:class}`cashocs.TopologyOptimizationProblem` and then can call it's +# {py:class}`TopologyOptimizationProblem ` and +# then can call it's # {py:meth}`solve ` method. top = cashocs.TopologyOptimizationProblem(