Skip to content

Commit ce5b524

Browse files
committed
Phase field defined at nodes
1 parent b4a9722 commit ce5b524

27 files changed

+7085
-2291
lines changed
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
"""
2+
@file example_with_parallel_l_bfgs.py
3+
4+
@author Indre Jödicke <[email protected]>
5+
6+
@date 19 Sep 2024
7+
8+
@brief Example for a topology optimization using
9+
the scipy L-BFGS-B optimizer.
10+
"""
11+
import sys
12+
import os
13+
import shutil
14+
15+
import numpy as np
16+
import time
17+
18+
# Default path of the library
19+
sys.path.insert(0, os.path.join(os.getcwd(), "../muspectre/builddir/language_bindings/python"))
20+
sys.path.insert(0, os.path.join(os.getcwd(), "../muspectre/builddir/language_bindings/libmufft/python"))
21+
sys.path.insert(0, os.path.join(os.getcwd(), "../muspectre/builddir/language_bindings/libmugrid/python"))
22+
import muSpectre as µ
23+
import muFFT
24+
25+
from NuMPI.Optimization import LBFGS
26+
from NuMPI import MPI
27+
from NuMPI.IO import save_npy
28+
from NuMPI.IO import load_npy
29+
from NuMPI.Tools import Reduction
30+
31+
from muTopOpt.Controller import wrapper
32+
from muTopOpt.MaterialDensity import node_to_quad_pt_2_quad_pts_sequential
33+
34+
35+
################################################################################
36+
### ------------------------ Parameter declarations ------------------------ ###
37+
################################################################################
38+
t = time.time()
39+
if MPI.COMM_WORLD.rank == 0:
40+
print(f'MPI: Rank {MPI.COMM_WORLD.rank}, size {MPI.COMM_WORLD.size}')
41+
42+
### ----- Geometry ----- ###
43+
nb_grid_pts = [16, 16]
44+
lengths = [1, 1]
45+
46+
dim = len(nb_grid_pts)
47+
hx = lengths[0] / nb_grid_pts[0]
48+
hy = lengths[1] / nb_grid_pts[1]
49+
50+
gradient, weights = µ.linear_finite_elements.gradient_2d
51+
52+
### ----- Target elastic constants ----- ###
53+
poisson_target = -0.5
54+
young_target = 0.25
55+
56+
mu_target = young_target / 2 / (1 + poisson_target)
57+
lambda_target = young_target * poisson_target
58+
lambda_target = lambda_target / (1 + poisson_target) / (1 - 2 * poisson_target)
59+
60+
### ----- Weighting parameters ----- ###
61+
eta = 0.01
62+
weight_phase_field = 0.01
63+
64+
### ----- Formulation ----- ###
65+
formulation = µ.Formulation.small_strain
66+
67+
### ----- FFT-engine ----- ###
68+
fft = 'mpi' #'fftwmpi' # Parallel
69+
# fft = 'fftw' # Seriel
70+
71+
### ----- Random seed ----- ###
72+
# For random phase distribution
73+
rng = np.random.default_rng(132131888)
74+
75+
### ----- Macroscopic strain ----- ###
76+
DelFs = [np.zeros([dim, dim])]
77+
DelFs[0][1, 1] = 1.
78+
DelFs[0][0, 0] = 1.
79+
80+
### ----- Material parameter ----- ###
81+
order = 2 # Interpolation order
82+
83+
# First material: Solid
84+
young = 1
85+
poisson = 0.2
86+
mu_1 = young / 2 / (1 + poisson)
87+
lambda_1 = young * poisson / (1 + poisson) / (1 - 2 * poisson)
88+
89+
# Second material: Void
90+
mu_0 = 0
91+
lambda_0 = 0
92+
93+
94+
### ----- muSpectre solver parameters ----- ###
95+
newton_tol = 1e-4
96+
cg_tol = 1e-5 # tolerance for cg algo
97+
equil_tol = 1e-4 # tolerance for equilibrium
98+
maxiter = 15000
99+
verbose = µ.Verbosity.Silent
100+
krylov_solver_args = (cg_tol, maxiter, verbose)
101+
solver_args = (newton_tol, equil_tol, verbose)
102+
103+
104+
### ----- Optimization parameters ----- ###
105+
gtol = 1e-5
106+
ftol = 1e-15
107+
maxcor = 10
108+
109+
### ----- Folder for saving data ----- ###
110+
folder = f'examples/results_sequential/'
111+
112+
################################################################################
113+
### ----------------------------- Target stress ---------------------------- ###
114+
################################################################################
115+
### ----- Construct cell with aimed for material properties ----- ###
116+
cell = µ.Cell([1, 1], lengths, formulation, fft=fft)
117+
mat = µ.material.MaterialElasticLocalLame_2d.make(cell, "material")
118+
mat.add_pixel_lame(0, lambda_target, mu_target)
119+
120+
### ----- Calculate the aimed for average stresses ----- ###
121+
target_stresses = []
122+
for DelF in DelFs:
123+
strain = DelF
124+
strain = strain.reshape([*DelF.shape, 1, 1], order='F')
125+
target_stress = cell.evaluate_stress(strain)
126+
target_stress = np.average(target_stress, axis=(2, 3, 4))
127+
target_stresses.append(target_stress)
128+
129+
# Arguments passed to the aim function
130+
aim_args = (target_stresses, eta, weight_phase_field)
131+
132+
133+
################################################################################
134+
### ---------------------- Save important information ---------------------- ###
135+
################################################################################
136+
### ----- Create folder for saving data ----- ###
137+
if MPI.COMM_WORLD.rank == 0:
138+
if not os.path.exists(folder):
139+
os.makedirs(folder)
140+
else:
141+
shutil.rmtree(folder)
142+
os.makedirs(folder)
143+
144+
### ----- Print some data ----- ###
145+
if MPI.COMM_WORLD.rank == 0:
146+
print(µ.version.info())
147+
148+
print('Optimize')
149+
print(' mu_target =', mu_target)
150+
print(' lambda_target =', lambda_target)
151+
print(' nb_grid_pts =', nb_grid_pts)
152+
153+
### ----- Save metadata of simulation ----- ###
154+
file_name = folder + '/metadata.txt'
155+
if MPI.COMM_WORLD.rank == 0:
156+
with open(file_name, 'w') as f:
157+
### ----- Save metadata ----- ###
158+
print('nb_grid_pts, lenghts, weight_eta, weight_phase_field, first_lame_1, '
159+
'second_lame_1, first_lame_0, second_lame_0, '
160+
'newton_tolerance, cg_tolerance, equil_tolerance, maxiter, '
161+
'gtol, ftol, maxcor, first_lame_target, second_lame_target, ',
162+
'loading', file=f)
163+
np.savetxt(f, nb_grid_pts, delimiter=' ', newline=' ')
164+
np.savetxt(f, lengths, delimiter=' ', newline=' ')
165+
np.savetxt(f, [eta], delimiter=' ', newline=' ')
166+
np.savetxt(f, [weight_phase_field], delimiter=' ', newline=' ')
167+
np.savetxt(f, [lambda_1], delimiter=' ', newline=' ')
168+
np.savetxt(f, [mu_1], delimiter=' ', newline=' ')
169+
np.savetxt(f, [lambda_0], delimiter=' ', newline=' ')
170+
np.savetxt(f, [mu_0], delimiter=' ', newline=' ')
171+
np.savetxt(f, [newton_tol], delimiter=' ', newline=' ')
172+
np.savetxt(f, [cg_tol], delimiter=' ', newline=' ')
173+
np.savetxt(f, [equil_tol], delimiter=' ', newline=' ')
174+
np.savetxt(f, [maxiter], delimiter=' ', newline=' ')
175+
np.savetxt(f, [gtol], delimiter=' ', newline=' ')
176+
np.savetxt(f, [ftol], delimiter=' ', newline=' ')
177+
np.savetxt(f, [maxcor], delimiter=' ', newline=' ')
178+
np.savetxt(f, [lambda_target], delimiter=' ', newline=' ')
179+
np.savetxt(f, [mu_target], delimiter=' ')
180+
181+
print('nb_loading_cases, loadings', file=f)
182+
np.savetxt(f, [len(DelFs)], delimiter=' ', newline=' ')
183+
for i in range(len(DelFs)):
184+
np.savetxt(f, DelFs[i].flatten(), delimiter=' ', newline=' ')
185+
print('', file=f)
186+
187+
print('MPI_size', file=f)
188+
print(MPI.COMM_WORLD.size, file=f)
189+
190+
### ----- Save initial phase distribution ----- ###
191+
# Cell construction
192+
cell = µ.Cell(nb_grid_pts, lengths, formulation, gradient,
193+
weights=weights, fft=fft, communicator=MPI.COMM_WORLD)
194+
195+
# Initial phase distribution
196+
phase_ini = rng.random(cell.nb_domain_grid_pts)
197+
phase_ini = phase_ini[cell.fft_engine.subdomain_slices]
198+
199+
# Saving
200+
file_name = folder + f'phase_initial.npy'
201+
save_npy(file_name, phase_ini, tuple(cell.subdomain_locations),
202+
tuple(cell.nb_domain_grid_pts), MPI.COMM_WORLD)
203+
phase = phase_ini
204+
205+
# Copy this file into the folder
206+
if MPI.COMM_WORLD.rank == 0:
207+
helper = 'cp examples/example_with_parallel_l_bfgs.py ' + folder
208+
os.system(helper)
209+
210+
################################################################################
211+
### ----------------------------- Optimization ----------------------------- ###
212+
################################################################################
213+
### ----- Initialisation ----- ###
214+
# Calculate material density
215+
density = node_to_quad_pt_2_quad_pts_sequential(phase)
216+
217+
# Material
218+
density = density.reshape([cell.nb_quad_pts, -1], order='F')
219+
mat2 = µ.material.MaterialElasticLocalLame_2d.make(cell, "material")
220+
lame1 = density ** order * (lambda_1 - lambda_0) + lambda_0
221+
lame2 = density ** order * (mu_1 - mu_0) + mu_0
222+
for pixel_id, pixel in cell.pixels.enumerate():
223+
mat2.add_pixel_lame(pixel_id, lame1[:, pixel_id], lame2[:, pixel_id])
224+
cell.initialise()
225+
226+
# Save evolution of aim function
227+
if MPI.COMM_WORLD.rank == 0:
228+
name = folder + 'evolution.txt'
229+
with open(name, 'w') as f:
230+
title = 'aim_function norm_sensitivity average_stresses'
231+
print(title, file=f)
232+
233+
234+
# Passing function arguments in NuMPI conform way
235+
opt_args = (cell, mat2, lambda_1, mu_1, lambda_0, mu_0, order,
236+
DelFs, krylov_solver_args, solver_args, aim_args,
237+
True, folder)
238+
def fun(x):
239+
return wrapper(x, *opt_args)
240+
241+
242+
### ----- Optimization ----- ###
243+
t = time.time()
244+
# With NuMPI optimizer
245+
opt_result = LBFGS(fun, phase, args=opt_args, jac=True, gtol=gtol,
246+
ftol=ftol, maxcor=maxcor, comm=MPI.COMM_WORLD, max_ls=100)
247+
248+
################################################################################
249+
### ----------------------------- Save results ----------------------------- ###
250+
################################################################################
251+
### ----- Save metadata of convergence ----- ###
252+
if MPI.COMM_WORLD.rank == 0:
253+
file_name = folder + 'metadata_convergence.txt'
254+
with open(file_name, 'a') as f:
255+
print('opt_success, opt_time (min), opt_nb_of_iteration', file=f)
256+
np.savetxt(f, [opt_result.success, (time.time() - t) / 60, opt_result.nit],
257+
delimiter=' ', newline=' ')
258+
if not opt_result.success:
259+
print(file=f)
260+
print(opt_result.message, file=f)
261+
262+
### ----- Save final phase distribution ----- ###
263+
# Final phase
264+
phase_final = opt_result.x.reshape(*cell.nb_subdomain_grid_pts, order='F')
265+
file_name = folder + 'phase_last.npy'
266+
save_npy(file_name, phase_final, tuple(cell.subdomain_locations),
267+
tuple(cell.nb_domain_grid_pts), MPI.COMM_WORLD)
268+
269+
t = time.time() - t
270+
if MPI.COMM_WORLD.rank == 0:
271+
print()
272+
if opt_result.success:
273+
print('Optimization successful')
274+
else:
275+
print('Optimization failed')
276+
print('Time (min) =', t/60)

0 commit comments

Comments
 (0)