Skip to content

Commit 072f763

Browse files
authored
Merge pull request #24 from Universite-Gustave-Eiffel/dev
Thumbnail demo gallery
2 parents c43cc32 + 74b261d commit 072f763

23 files changed

+513
-244
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
# *.JPG
77
*.npz
88
# *.npy
9-
# *.mp4
9+
demo/*.gif
10+
demo/*.mp4
1011
# *.html
1112
# *.sqlite
1213
# *.hdf5

Dockerfile.lab

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM dolfinx/lab:v0.7.2
1+
FROM dolfinx/lab:v0.7.3
22
LABEL maintainer="Pierric Mora <pierric.mora@univ-eiffel.fr>"
33
LABEL description=" elastodynamics with FEniCSx/DOLFINx "
44

Dockerfile.shell

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM dolfinx/dolfinx:v0.7.2
1+
FROM dolfinx/dolfinx:v0.7.3
22
LABEL maintainer="Pierric Mora <pierric.mora@univ-eiffel.fr>"
33
LABEL description=" elastodynamics with FEniCSx/DOLFINx "
44

demo/eigs_3D_AluminumCube.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
from dolfinx import mesh, fem, default_scalar_type
2525
from mpi4py import MPI
26-
from petsc4py import PETSc
2726

2827
from elastodynamicsx.pde import material, PDE
2928
from elastodynamicsx.solvers import EigenmodesSolver
@@ -83,7 +82,7 @@
8382
eigenfreqs = eps.getEigenfrequencies()
8483
# eigenmodes = eps.getEigenmodes()
8584

86-
eps.plot(V, slice(6,6+9), wireframe=True, factor=30) # Avoid the first 6 rigid body modes
85+
eps.plot(V, slice(6, 6+9), wireframe=True, factor=30) # Avoid the first 6 rigid body modes
8786
# -
8887

8988

@@ -102,14 +101,15 @@
102101
310.109, 316.197, 317.392, 326.462, 329.034, 332.441, 333.364, 336.65,
103102
337.359, 338.276])
104103

105-
freqs_OgiEtAl_calc = np.array([116.32 , 143.186, 158.44 , 166.113, 169.338, 178.36 , 184.57 , 185.078, \
106-
190.206, 197.692, 201.462, 207.096, 211 , 215.613, 223.219, 230.804, \
107-
233.329, 234.758, 250.777, 251.038, 252.303, 256.849, 258.064, 258.874, \
108-
259.203, 267.746, 276.736, 279.144, 282.773, 293.016, 304.593, 305.316, \
109-
309.591, 315.775, 317.931, 326.556, 329.369, 332.732, 332.271, 336.218, \
104+
freqs_OgiEtAl_calc = np.array([116.32 , 143.186, 158.44 , 166.113, 169.338, 178.36 , 184.57 , 185.078,
105+
190.206, 197.692, 201.462, 207.096, 211 , 215.613, 223.219, 230.804,
106+
233.329, 234.758, 250.777, 251.038, 252.303, 256.849, 258.064, 258.874,
107+
259.203, 267.746, 276.736, 279.144, 282.773, 293.016, 304.593, 305.316,
108+
309.591, 315.775, 317.931, 326.556, 329.369, 332.732, 332.271, 336.218,
110109
337.511, 337.71])
111110

112111
print('Eigenfrequencies: comparison with litterature values')
113112
print(' FE \tOgi et al, calc.\t Ogi et al, exp. \t(kHz)')
114-
for fFE, fOgi_calc, fOgi_exp in zip(eigenfreqs[6:]*1e3, freqs_OgiEtAl_calc, freqs_OgiEtAl_exp): # *1e3 to convert MHz into kHz
115-
print(str(round(fFE, 3)) +"\t "+ str(round(fOgi_calc, 3)) +"\t\t "+ str(round(fOgi_exp, 3)))
113+
for fFE, fOgi_calc, fOgi_exp in zip(eigenfreqs[6:] * 1e3, freqs_OgiEtAl_calc, freqs_OgiEtAl_exp):
114+
# *1e3 to convert MHz into kHz
115+
print(f"{fFE:.3f}\t {fOgi_calc:.3f}\t\t {fOgi_exp:.3f}")

demo/eigs_3D_ElasticBeam.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,10 @@
3030

3131
from dolfinx import mesh, fem, default_scalar_type
3232
from mpi4py import MPI
33-
from petsc4py import PETSc
3433

3534
from elastodynamicsx.pde import material, boundarycondition, PDE
3635
from elastodynamicsx.solvers import EigenmodesSolver
3736
from elastodynamicsx.utils import make_facet_tags
38-
39-
#import pyvista
40-
#pyvista.start_xvfb()
41-
#pyvista.set_jupyter_backend("static")
4237
# -
4338

4439
# ### FE domain
@@ -48,8 +43,8 @@
4843

4944
# Nb of elts.
5045
Nx = 20
51-
Ny = int(B_/L_ * Nx) + 1
52-
Nz = int(H_/L_ * Nx) + 1
46+
Ny = int(B_ / L_ * Nx) + 1
47+
Nz = int(H_ / L_ * Nx) + 1
5348

5449
extent = [[0., 0., 0.], [L_, B_, H_]]
5550

@@ -61,11 +56,11 @@
6156

6257
# define some tags
6358
tag_left, tag_top, tag_right, tag_bottom, tag_back, tag_front = 1, 2, 3, 4, 5, 6
64-
boundaries = [(tag_left , lambda x: np.isclose(x[0], 0 )),\
65-
(tag_right , lambda x: np.isclose(x[0], L_)),\
66-
(tag_bottom, lambda x: np.isclose(x[1], 0 )),\
67-
(tag_top , lambda x: np.isclose(x[1], B_)),\
68-
(tag_back , lambda x: np.isclose(x[2], 0 )),\
59+
boundaries = [(tag_left , lambda x: np.isclose(x[0], 0.)),
60+
(tag_right , lambda x: np.isclose(x[0], L_)),
61+
(tag_bottom, lambda x: np.isclose(x[1], 0.)),
62+
(tag_top , lambda x: np.isclose(x[1], B_)),
63+
(tag_back , lambda x: np.isclose(x[2], 0.)),
6964
(tag_front , lambda x: np.isclose(x[2], H_))]
7065

7166
facet_tags = make_facet_tags(domain, boundaries)
@@ -88,7 +83,7 @@
8883

8984
# Scaling
9085
scaleRHO = 1e6 # a scaling factor to avoid blowing the solver
91-
scaleFREQ= np.sqrt(scaleRHO) # the frequencies must be scaled accordingly
86+
scaleFREQ = np.sqrt(scaleRHO) # the frequencies must be scaled accordingly
9287
rho *= scaleRHO
9388

9489
# Convert Young & Poisson to Lamé's constants
@@ -134,12 +129,13 @@
134129
# Exact solution computation
135130
from scipy.optimize import root
136131
from math import cos, cosh
137-
falpha = lambda x: cos(x)*cosh(x)+1
138-
alpha = lambda n: root(falpha, (2*n+1)*np.pi/2)['x'][0]
132+
falpha = lambda x: cos(x) * cosh(x) + 1
133+
alpha = lambda n: root(falpha, (2 * n + 1) * np.pi / 2)['x'][0]
139134

140135
nev = eigenfreqs.size
141-
I_bend = H_*B_**3/12*(np.arange(nev)%2==0) + B_*H_**3/12*(np.arange(nev)%2==1)
142-
freq_beam = np.array([alpha(i//2) for i in range(nev)])**2 *np.sqrt(E*I_bend/(rho.value*B_*H_*L_**4))/2/np.pi
136+
I_bend = H_ * B_**3 / 12 * (np.arange(nev) % 2 == 0) + B_ * H_**3 / 12 * (np.arange(nev) % 2 == 1)
137+
freq_beam = np.array([alpha(i // 2) for i in range(nev)])**2 \
138+
* np.sqrt(E * I_bend / (rho.value * B_ * H_ * L_**4)) / 2 / np.pi
143139

144140
print('Eigenfrequencies: comparison with beam theory\n')
145141
print('mode || FE (Hz)\t|| Beam theory (Hz)\t|| Difference (%)')

demo/freq_2D-PSV_FullSpace.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@
2626
from dolfinx import mesh, fem, default_scalar_type
2727
import ufl
2828
from mpi4py import MPI
29-
from petsc4py import PETSc
3029

3130
from elastodynamicsx.pde import material, BodyForce, boundarycondition, PDE
3231
from elastodynamicsx.solvers import FrequencyDomainSolver
3332
from elastodynamicsx.plot import plotter, live_plotter
34-
from elastodynamicsx.utils import make_facet_tags, make_cell_tags, ParallelEvaluator
33+
from elastodynamicsx.utils import make_facet_tags, ParallelEvaluator
3534

3635
from analyticalsolutions import u_2D_PSV_xw, int_Fraunhofer_2D
3736

@@ -44,7 +43,7 @@
4443
# +
4544
degElement = 1
4645
length, height = 10, 10
47-
Nx, Ny = 100//degElement, 100//degElement
46+
Nx, Ny = 100 // degElement, 100 // degElement
4847

4948
# create the mesh
5049
extent = [[0., 0.], [length, height]]
@@ -55,9 +54,9 @@
5554

5655
tag_left, tag_top, tag_right, tag_bottom = 1, 2, 3, 4
5756
all_tags = (tag_left, tag_top, tag_right, tag_bottom)
58-
boundaries = [(tag_left , lambda x: np.isclose(x[0], 0 )),\
59-
(tag_right , lambda x: np.isclose(x[0], length)),\
60-
(tag_bottom, lambda x: np.isclose(x[1], 0 )),\
57+
boundaries = [(tag_left , lambda x: np.isclose(x[0], 0 )),
58+
(tag_right , lambda x: np.isclose(x[0], length)),
59+
(tag_bottom, lambda x: np.isclose(x[1], 0 )),
6160
(tag_top , lambda x: np.isclose(x[1], height))]
6261

6362
# define some tags
@@ -84,7 +83,7 @@
8483

8584
# +
8685
Z_N, Z_T = mat.Z_N, mat.Z_T # P and S mechanical impedances
87-
bc = boundarycondition((V, facet_tags, all_tags), 'Dashpot', Z_N, Z_T)
86+
bc = boundarycondition((V, facet_tags, all_tags), 'Dashpot', Z_N, Z_T)
8887

8988
bcs = [bc]
9089
# -
@@ -96,10 +95,10 @@
9695
# +
9796
F0 = fem.Constant(domain, default_scalar_type([1, 0])) # amplitude
9897
R0 = 0.1 # radius
99-
X0 = np.array([length/2, height/2, 0]) # center
98+
X0 = np.array([length / 2, height / 2, 0]) # center
10099

101-
x = ufl.SpatialCoordinate(domain)
102-
gaussianBF = F0 * ufl.exp(-((x[0]-X0[0])**2+(x[1]-X0[1])**2)/2/R0**2) / (2*np.pi*R0**2)
100+
x = ufl.SpatialCoordinate(domain)
101+
gaussianBF = F0 * ufl.exp(-((x[0] - X0[0])**2 + (x[1] - X0[1])**2) / 2 / R0**2) / (2 * np.pi * R0**2)
103102

104103
bf = BodyForce(V, gaussianBF)
105104
# -
@@ -156,9 +155,10 @@
156155
from scipy.spatial.transform import Rotation as R
157156
theta = np.radians(35)
158157
pts = np.linspace(0, length / 2, endpoint=False)[1:]
159-
points_out = X0[:,np.newaxis] + R.from_rotvec([0, 0, theta]).as_matrix() @ np.array([pts,
160-
np.zeros_like(pts),
161-
np.zeros_like(pts)])
158+
points_out = X0[:, np.newaxis] + \
159+
R.from_rotvec([0, 0, theta]).as_matrix() @ np.array([pts,
160+
np.zeros_like(pts),
161+
np.zeros_like(pts)])
162162

163163
# Declare a convenience ParallelEvaluator
164164
paraEval = ParallelEvaluator(domain, points_out)
@@ -167,19 +167,25 @@
167167
u_at_pts_local = np.zeros((paraEval.nb_points_local,
168168
V.num_sub_spaces,
169169
omegas.size),
170-
dtype=default_scalar_type) # <- output stored here
170+
dtype=default_scalar_type) # <- output stored here
171+
171172

172173
# Callback function: post process solution
173174
def cbck_storeAtPoints(i, out):
174175
if paraEval.nb_points_local > 0:
175-
u_at_pts_local[:,:,i] = u_res.eval(paraEval.points_local, paraEval.cells_local)
176+
u_at_pts_local[:, :, i] = u_res.eval(paraEval.points_local, paraEval.cells_local)
177+
176178

177179
# Live plotting
178-
enable_plot = True
179-
if domain.comm.rank == 0 and enable_plot:
180-
p = live_plotter(u_res, clim=0.25 * np.linalg.norm(mu.value * F0.value) * np.array([0, 1]))
180+
if domain.comm.rank == 0:
181+
p = live_plotter(u_res,
182+
show_edges=False,
183+
clim=0.25 * np.linalg.norm(mu.value * F0.value) * np.array([0, 1]))
181184
if paraEval.nb_points_local > 0:
182185
p.add_points(paraEval.points_local) # add points to live_plotter
186+
if p.off_screen:
187+
p.window_size = [640, 480]
188+
p.open_movie('freq_2D-PSV_FullSpace.mp4', framerate=1)
183189
else:
184190
p = None
185191

@@ -196,25 +202,25 @@ def cbck_storeAtPoints(i, out):
196202
u_at_pts = paraEval.gather(u_at_pts_local, root=0)
197203

198204
if domain.comm.rank == 0:
199-
### -> Exact solution, At few points
205+
# -> Exact solution, At few points
200206
x = points_out.T
201207

202208
# account for the size of the source in the analytical formula
203209
fn_kdomain_finite_size = int_Fraunhofer_2D['gaussian'](R0)
204-
u_at_pts_anal = u_2D_PSV_xw(x-X0[np.newaxis,:], omegas, F0.value, rho.value,
210+
u_at_pts_anal = u_2D_PSV_xw(x - X0[np.newaxis, :], omegas, F0.value, rho.value,
205211
lambda_.value, mu.value, fn_kdomain_finite_size)
206212

207213
#
208214
fn = np.real
209215

210216
icomp = 0
211-
fig, ax = plt.subplots(len(omegas),1)
217+
fig, ax = plt.subplots(len(omegas), 1)
212218
fig.suptitle(r'u at few points, $\theta$=' + str(int(round(np.degrees(theta), 0))) + r'$^{\circ}$')
213-
r = np.linalg.norm(x - X0[np.newaxis,:], axis=1)
219+
r = np.linalg.norm(x - X0[np.newaxis, :], axis=1)
214220
for i in range(len(omegas)):
215221
ax[i].text(0.15, 0.95, r'$\omega$=' + str(round(omegas[i], 2)),
216222
ha='left', va='top', transform=ax[i].transAxes)
217-
ax[i].plot(r, fn(u_at_pts[:, icomp, i]), ls='-' , label='FEM')
223+
ax[i].plot(r, fn(u_at_pts[:, icomp, i]), ls='-', label='FEM')
218224
ax[i].plot(r, fn(u_at_pts_anal[:, icomp, i]), ls='--', label='analytical')
219225
ax[0].legend()
220226
ax[-1].set_xlabel('Distance to source')

demo/freq_2D-SH_FullSpace.py

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# ---
22
# jupyter:
33
# jupytext:
4+
# cell_metadata_json: true
45
# text_representation:
56
# extension: .py
67
# format_name: light
@@ -28,13 +29,11 @@
2829
from dolfinx import mesh, fem, default_scalar_type
2930
import ufl
3031
from mpi4py import MPI
31-
from petsc4py import PETSc
3232

3333
from elastodynamicsx.pde import material, BodyForce, boundarycondition, PDE
3434
from elastodynamicsx.solvers import FrequencyDomainSolver
3535
from elastodynamicsx.plot import plotter, live_plotter
36-
from elastodynamicsx.utils import make_facet_tags, make_cell_tags, ParallelEvaluator
37-
from analyticalsolutions import green_2D_SH_rw, int_Fraunhofer_2D
36+
from elastodynamicsx.utils import make_facet_tags, ParallelEvaluator
3837

3938
assert np.issubdtype(default_scalar_type, np.complexfloating), \
4039
"Demo should only be executed with DOLFINx complex mode"
@@ -46,20 +45,20 @@
4645
# +
4746
degElement = 1
4847
length, height = 10, 10
49-
Nx, Ny = 100//degElement, 100//degElement
48+
Nx, Ny = 100 // degElement, 100 // degElement
5049

5150
# create the mesh
5251
extent = [[0., 0.], [length, height]]
5352
domain = mesh.create_rectangle(MPI.COMM_WORLD, extent, [Nx, Ny], mesh.CellType.triangle)
5453

5554
# create the function space
56-
V = fem.FunctionSpace(domain, ("Lagrange", degElement))
55+
V = fem.FunctionSpace(domain, ("Lagrange", degElement))
5756

5857
tag_left, tag_top, tag_right, tag_bottom = 1, 2, 3, 4
5958
all_tags = (tag_left, tag_top, tag_right, tag_bottom)
60-
boundaries = [(tag_left , lambda x: np.isclose(x[0], 0 )),\
61-
(tag_right , lambda x: np.isclose(x[0], length)),\
62-
(tag_bottom, lambda x: np.isclose(x[1], 0 )),\
59+
boundaries = [(tag_left , lambda x: np.isclose(x[0], 0 )),
60+
(tag_right , lambda x: np.isclose(x[0], length)),
61+
(tag_bottom, lambda x: np.isclose(x[1], 0 )),
6362
(tag_top , lambda x: np.isclose(x[1], height))]
6463

6564
# define some tags
@@ -161,30 +160,35 @@
161160
from scipy.spatial.transform import Rotation as R
162161
theta = np.radians(35)
163162
pts = np.linspace(0, length / 2, endpoint=False)[1:]
164-
points_out = X0[:,np.newaxis] + R.from_rotvec([0, 0, theta]).as_matrix() @ np.array([pts,
165-
np.zeros_like(pts),
166-
np.zeros_like(pts)])
163+
points_out = X0[:, np.newaxis] + \
164+
R.from_rotvec([0, 0, theta]).as_matrix() @ np.array([pts,
165+
np.zeros_like(pts),
166+
np.zeros_like(pts)])
167167

168168
# Declare a convenience ParallelEvaluator
169169
paraEval = ParallelEvaluator(domain, points_out)
170170

171171
# Declare data (local)
172-
u_at_pts_local = np.zeros((paraEval.nb_points_local,
173-
1,
174-
omegas.size),
175-
dtype=default_scalar_type) # <- output stored here
172+
u_at_pts_local = np.zeros((paraEval.nb_points_local, 1, omegas.size),
173+
dtype=default_scalar_type) # <- output stored here
174+
176175

177176
# Callback function: post process solution
178177
def cbck_storeAtPoints(i, out):
179178
if paraEval.nb_points_local > 0:
180-
u_at_pts_local[:,:,i] = u_res.eval(paraEval.points_local, paraEval.cells_local)
179+
u_at_pts_local[:, :, i] = u_res.eval(paraEval.points_local, paraEval.cells_local)
180+
181181

182182
# Live plotting
183-
enable_plot = True
184-
if domain.comm.rank == 0 and enable_plot:
185-
p = live_plotter(u_res, clim=0.25 * np.linalg.norm(mu.value * F0.value) * np.array([-1, 1]))
183+
if domain.comm.rank == 0:
184+
p = live_plotter(u_res,
185+
show_edges=False,
186+
clim=0.25 * np.linalg.norm(mu.value * F0.value) * np.array([-1, 1]))
186187
if paraEval.nb_points_local > 0:
187188
p.add_points(paraEval.points_local) # add points to live_plotter
189+
if p.off_screen:
190+
p.window_size = [640, 480]
191+
p.open_movie('freq_2D-SH_FullSpace.mp4', framerate=1)
188192
else:
189193
p = None
190194

@@ -201,27 +205,27 @@ def cbck_storeAtPoints(i, out):
201205
u_at_pts = paraEval.gather(u_at_pts_local, root=0)
202206

203207
if domain.comm.rank == 0:
204-
### -> Exact solution, At few points
208+
# -> Exact solution, At few points
205209
x = points_out.T
206-
r = np.linalg.norm(x - X0[np.newaxis,:], axis=1)
210+
r = np.linalg.norm(x - X0[np.newaxis, :], axis=1)
207211

208212
# account for the size of the source in the analytical formula
213+
from analyticalsolutions import green_2D_SH_rw, int_Fraunhofer_2D
209214
fn_kdomain_finite_size = int_Fraunhofer_2D['gaussian'](R0)
210215
u_at_pts_anal = green_2D_SH_rw(r, omegas, rho.value, mu.value, fn_kdomain_finite_size)
211216

212217
#
213218
fn = np.real
214219

215-
fig, ax = plt.subplots(len(omegas),1)
220+
fig, ax = plt.subplots(len(omegas), 1)
216221
fig.suptitle(r'u at few points, $\theta$=' + str(int(round(np.degrees(theta), 0))) + r'$^{\circ}$')
217222
for i in range(len(omegas)):
218223
ax[i].text(0.15, 0.95, r'$\omega$=' + str(round(omegas[i], 2)),
219224
ha='left', va='top', transform=ax[i].transAxes)
220-
ax[i].plot(r, fn(u_at_pts[:, 0, i]), ls='-' , label='FEM')
225+
ax[i].plot(r, fn(u_at_pts[:, 0, i]), ls='-', label='FEM')
221226
ax[i].plot(r, fn(u_at_pts_anal[:, i]), ls='--', label='analytical')
222227
ax[0].legend()
223228
ax[-1].set_xlabel('Distance to source')
224229
plt.show()
225230
#
226231
# ------------------ end of Ex 2 ----------------------
227-

0 commit comments

Comments
 (0)