This code implements a finite element-based topology optimization algorithm for 3D printed structures. It is written in Python and uses Ansys as finite element solver through the Python interface PyAnsys/PyMAPDL.
For each element, a density value
The fiber orientation within each element is defined by two angles: in-plane orientation
The problem formulation, for which the three sets of variables are simultaneously optimized, is (SCHMIDT et al, 2020):
where
To avoid the appearance of checkerboard patterns and to ensure the mesh-independence of the result, the element sensitivities are filtered by a linear decaying convolution filter (SIGMUND, 2001):
where
For orientation smoothing, a similar convolution filter was applied directly to the angles at each iteration, adjusted to reduce the weight of void elements on the average:
where
For 2D optimizations, the implementation assumes a 4-node quadrilateral element (Ansys element type PLANE182), whose form functions
For 3D optimizations, the implementation assumes an 8-node brick element (SOLID185):
The materials are modeled as transverse isotropic, suitable for matrices reinforced by unidirectional fibers. The fibers were considered to be aligned with the local
where
The constitutive matrix
where
The variable updating scheme is the Method of Moving Asymptotes - MMA (SVANBERG, 1987). It is then necessary to calculate at each iteration the sensitivities of the compliance with respect to the design variables:
The derivatives
For multiple load cases, the compliances are aggregated using a
The new sensitivities are:
To facilitate the convergence and avoid local minima, a continuation method in is applied on the penalization factor
The environmental impact of the structure is measured in terms of the mass of
Firstly, the material density
The impact of the material production
where
The impact of the use phase
The value used to compare different designs is the total footprint
All dependencies can be installed in a fresh venv
pip install -r requirements.txt
Some dependencies are automatically installed with pyansys, namely:
- NumPy
- SciPy
- Matplotlib
- jsonpickle
Examples may have additional dependencies:
- mpi4py 3.0.1
- pyvista 0.38.6
SimpleExample.ipynb
: Jupyter notebook with an example of 2D and 3D optimizationsSIMP.ipynb
,Bracket_SIMP.ipynb
: examples of using the SIMP method for 3D optimizationsglobal3d.py
: example of a complete 3D optimization. Launches multiple optimizations with different materials. Results inexamples/global3d.out
, visualized withglobal3d_plots.py
NaturalFibers.ipynb
: Jupyter notebook comparing different materialsbridge.py
: example of a complete 2D optimization. Uses MPI to launch parallel processes with different initial orientationsBracket_material_selection.ipynb
,Bracket.ipynb
,Bracket_printing_direction
,Bracket_refine
: Jupyter notebook with the optimization steps for the design of a 3D structure
- For 2D optimization: use 4-node 2D quad elements (PLANE182), with KEYOPT(3) = 3 (plane stress with thk)
- For 3D optimization: use 8-node 3D hex elements (SOLID185)
Minimal example of an optimization of the model models/mbb3d.db
made of bamboo and cellulose with a volume fraction of 0.3. Results are saved in the folder results/
The next sections present all available functions
from optim import TopOpt, Post2D, Post3D
# -------------------- Define materials -------------------
# {t/mm^3, MPa, -, kgCO2/kg}
bamboo = {'rho': 700e-12, 'E': 17.5e3, 'v': 0.04, 'CO2': 1.0565}
cellulose = {'rho': 990e-12, 'E': 3.25e3, 'v': 0.355, 'CO2': 3.8}
Vfiber = 0.5
Ex, Ey, nuxy, nuyz, Gxy, rho, CO2mat = TopOpt.rule_mixtures(fiber=bamboo, matrix=cellulose, Vfiber=Vfiber)
# --------------------- Define problem --------------------
solver = TopOpt(inputfile='models/mbb3d.db', res_dir='results/', dim='3D_layer', jobname='3d')
solver.set_material(Ex=Ex, Ey=Ey, nuxy=nuxy, nuyz=nuxy, Gxy=Gxy)
solver.set_volfrac(0.3)
solver.set_filters(r_rho=8, r_theta=20)
solver.set_initial_conditions('random')
# ---------------------- Run and save ---------------------
solver.run()
solver.save()
# ---------------------- Visualization --------------------
post = Post3D(solver)
post.plot_convergence()
post.plot(colorful=False)
-
TopOpt.rule_mixtures(fiber, matrix, Vfiber)
Returns
Ex
,Ey
,nuxy
,nuyz
,Gxy
,rho
,CO2mat
by the rule of mixtures for a material with fiber volume fractionVfiber
. Componentsfiber
andmatrix
are given as dictionaries with fields'rho'
,'E'
,'v'
,'CO2'
-
TopOpt(inputfile, res_dir, load_cases=None, dim='3D_layer', jobname='file', echo=True)
-
inputfile
: path to .db file -
res_dir
: path to folder to store results -
load_cases
: tuple/list of paths to load step files (.sn generated byLSWRITE
command - does not need to follow Ansys nomenclature convention). Optional for single load case, .db is executed if no load step given -
dim
: optimization type-
'SIMP2D'
and'SIMP3D'
: SIMP method, 2D or 3D optimization -
'2D'
: 2D optimization -
'3D_layer'
: 3D optimization, structure will be printed in layers and fibers can rotate in the plane$x'y'$ (defined by the normal vectorprint_direction
) -
'3D_free'
: 3D optimization, fiber orientations defined by rotations around$z$ and$x$ respectively
-
-
jobname
: subfolder ofTopOpt.res_dir
to store results for this optim. Default: no subfolder, stores results directly onTopOpt.res_dir
-
echo
: print compliance at each iteration?
-
All setter functions are optional, the optimization can be performed with the default parameters
-
set_material(Ex=1, Ey=1, nuxy=0.3, nuyz=0.3, Gxy=1/(2*(1+0.3)))
Defines material properties, considered transverse isotropic with symmetry plane
$yz$ -
set_volfrac(volfrac=0.3)
Defines the volume fraction constraint for the optimization
-
set_filters(r_rho=0, r_theta=0)
Defines the filters applied on each iteration, defaults to no filtering
-
r_rho
: radius of the density filter (adjusts minimum feature size) -
r_theta
: radius of the orientation filter (adjusts fiber curvature)
-
-
set_solid_elem(solid_elem=[])
List of elements whose densities will be fixed on 1. Indexing starting at 0
-
set_print_direction(print_direction=(0.,0.,1.), overhang_angle=45, overhang_constraint=False)
Defines the printing direction, to verify if the structure is printable without adding supports. Depends on
r_rho
, so should be called afterset_filters()
-
print_direction
: vector pointing upwards in the printing coordinate system. Does not need to be unitary -
overhang_angle
: minimum self-supporting angle. Minimum angle with respect to the horizontal at which features may be created -
overhang_constraint
: add overhang constraint to the optimization? Results in self-supported structures
-
-
set_initial_conditions(angle_type='random', theta0=0, alpha0=0)
-
angle_type
: method for setting the initial orientations-
'fix'
: initial orientations are given -
'noise'
: gaussian distribution around the given values -
'random'
: random orientation for each element -
'principal'
: initial orientations follow the principal stress directions considering the whole domain with an isotropic material
-
-
theta0
: initial orientation (around$z'$ ) of the fibers, in degrees. Only used with'fix'
and'noise'
-
alpha0
: initial orientation (around$x'$ ) of the fibers, in degrees. Only used with'fix'
and'noise'
-
-
set_optim_options(max_iter=200, tol=0, continuation=False, move=0.2, max_grey=0.3, void_thr=0.1, filled_thr=0.9)
-
max_iter
: maximum number of iterations -
tol
: stopping criterion, relative change in the objective function. Defaults at 0, i.e., will run until max_iter -
continuation
: use a continuation method in the penalization factor? -
move
: move limit for variables updating, given as fraction of the allowable range -
max_grey
: stopping criterion for continuation, maximum fraction of grey elements, i.e. nor void nor filled -
void_thr
: elements are considered void if they have density less or equalvoid_thr
-
filled_thr
: elements are considered void if they have density greater or equalfilled_thr
-
-
run()
Runs the optimization, saves all results within the
TopOpt
object -
print_timing()
Prints the optimization timing:
- total elapsed time
- time spent solving the finite element model
- time spent calculating sensitivities
- time spent updating variables
-
save(filename=None)
Saves object into a JSON file
-
TopOpt.load(filename)
Returns
TopOpt
object from JSON file created bysave()
-
get_printability()
Returns the printability score of the final design (fraction of self-supported elements) and a boolean list indicating whether each element is self-supported
-
get_greyness()
Returns the greyness of the final design (fraction of grey elements)
-
get_mass(dens)
Returns the mass of the final design
-
get_max_disp(load_case=1)
Returns the maximum nodal displacement for the
load_case
-th load case, indexing starting on 1 -
get_CO2_footprint(dens, CO2mat, CO2veh)
Returns the
$CO_2$ footprint of the final design-
dens
: material density -
CO2mat
: mass$CO_2$ emmited per mass material (material production) -
CO2veh
: mass$CO_2$ emitted per mass material during life (use in a vehicle) = mass fuel per mass transported per lifetime * service life * mass$CO_2$ emmited per mass fuel
-
-
fea(x)
Returns the aggregated compliance obtained for a specific set of design variables
x
. Useful to evaluate a design obtained from other optimization, but using the current load cases (given the meshes and design variables are the same)
For 2D optimizations: Post2D(solver)
For 3D optimizations: Post3D(solver)
-
plot_convergence(start_iter=0, penal=False, filename=None, save=True)
Simple plot of the convergence history
-
start_iter
: first iteration shown, may be used to eliminate first values that break the compliance scale -
penal
: show subplot with penalization factor evolution?
-
-
plot(iteration=-1, colorful=True, printability=False, elev=None, azim=None, domain_stl=None, filename=None, save=True, fig=None, ax=None, zoom=None)
Plots the configuration at iteration
iteration
-
colorful
: color arrows based on their orientation? RGB values based on the vector components:-
$x$ : blue -
$y$ : green -
$z$ : red
-
-
zoom
: only forPost2D
. Inner plot with zoom to a specific area can be added by passing a dict with fields-
'xmin'
,'xmax'
,'ymin'
,'ymax'
: area of interest (in model length units) -
'xpos'
,'ypos'
,'width'
,'height'
: position and size of the zoomed plot (fraction of the original axes) -
'color'
: color of the outline boxes
-
-
printability
: only forPost3D
. Color arrows according to their printability? Self-supported elements are shown in black, non-self-supported elements are shown in red. IfTrue
, ignorescolorful
-
elev
,azim
: only forPost3D
. Angles defining the point of view -
domain_stl
: only forPost3D
. Filename of the .stl file with the original domain. If defined, plots an outline of the domain
-
-
animate(filename=None, colorful=True, printability=False, elev=None, azim=None)
Creates an animation where each frame is the
plot()
for an iteration -
plot_fibers(iteration=-1, layer=None, elev=None, azim=None, filename=None, save=True, fig=None, ax=None)
Plots continuous fibers obtained as streamlines of the orientation field at iteration
iteration
-
layer
: only forPost3D
. IfNone
, plots all layers in a 3D plot, colored from blue (bottom layers) to red (top layers). If a tuple of integers, plots the selected layers in a 3D plot with the same color scheme. If integer, plots the selected layer in a 2D plot, analogous toPost2D
-
elev
,azim
: only forPost3D
. Angles defining the point of view
-
-
animate_fibers(layer=None, filename=None)
Creates an animation where each frame is the
plot_fibers()
for an iteration
-
plot_density(iteration=-1, filename=None, save=True, fig=None, ax=None)
Plots the density distribution
-
plot_layer_density(iteration=-1, layer=0, filename=None, save=True, fig=None, ax=None)
Plots the deinsties in the
layer
-th layer, analogous toplot_density()
forPlot2D
-
plot_layer(iteration=-1, layer=0, colorful=False, printability=False, filename=None, save=True, fig=None, ax=None, zoom=None)
Plots the
layer
-th layer, analogous toplot()
forPlot2D
-
animate_layer(layer=0, colorful=False, filename=None)
Creates an animation where each frame is the
plot_layer(layer)
for a iteration -
animate_print(fibers=True, colorful=False, printability=False, filename=None)
Creates an animation where each frame is a layer plot in the final configuration. If
fibers
is True, usesplot_fibers(layer)
and iffibers
is False, cusesplot_layer(layer)
-
plot_iso(self, iteration=-1, threshold=0.8, elev=30, azim=-60, filename=None)
Plots the isosurface of density greater or equal to
threshold
. Elemental data is interpolated to nodes by PyDPF (Ansys Data Procssing Framework), which needs the results stored in the.rst
file.
- A. Castro Almeida, E. Duriez, F. Lachaud, J. Morlier. New topology optimization for low CO2 footprint AFP composite structures, Poster session, WCSMO 2023.
- R. M. Christensen. Tensor transformations and failure criteria for the analysis of fiber composite materials, Journal of Composite Materials 22.9, 874-897, 1988.
- E. Duriez, J. Morlier, C. Azzaro-Pantel, M. Charlotte. Ecodesign with topology optimization, Procedia CIRP, Elsevier, 454-459, 2022.
- M. Schmidt, L. Couret, C. Gout, C. Pedersen. Structural topology optimization with smoothly varying fiber orientations, Structural and Multidisciplinary Optimization, Springer, 3105-3126, 2020.
- O. Sigmund. A 99 line topology optimization code written in Matlab, Structural and multidisciplinary optimization, Springer, 120-127, 2001.
- K. Svanberg. The method of moving asymptotes—a new method for structural optimization, International journal for numerical methods in engineering, Wiley Online Library, 359-373, 1987.