Peano
Loading...
Searching...
No Matches
elastic.py
Go to the documentation of this file.
1# This file is part of the ExaHyPE2 project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3import sympy
4
5"""
6We import the required modules for this project.
7We always need the 'peano4' module as this is our core project.
8Since we are creating a SymHyPE/ExaHyPE 2 application, we additionally
9need to import the 'exahype2' and 'symhype' modules.
10"""
11import peano4
12import exahype2
13import symhype
14
15"""
16We specify the space dimensions here.
17We support either 2- or 3-dimensional problems.
18"""
19dimensions = 2
20
21"""
22The number of finite volumes per axis in one patch.
23"""
24patch_size = 16
25
26"""
27The number of levels the mesh is refined.
28"""
29depth = 5
30
31"""
32The simulation end time.
33"""
34end_time = 1.0
35
36"""
37Choose domain size and offset.
38"""
39size = [20.0, 20.0, 20.0]
40offset = [-5.0, 0.0, 0.0]
41
42"""
43Choose how often a snapshot is written.
44"""
45time_in_between_two_snapshots = end_time / 10
46# time_in_between_two_snapshots = 0 # Set to 0 to disable I/O
47
48"""
49Switch between 'Release', 'Debug', 'Asserts', 'Trace', 'Stats'.
50"""
51compile_mode = "Release"
52
53"""
54We first create a new ExaHyPE 2 project.
55For this, we specify the (nested) namespaces, the name of our main file and our executable name.
56"""
57my_project = exahype2.Project(
58 namespace=["tutorials", "symhype", "elastic"],
59 project_name="Elastic",
60 directory=".",
61 executable="Elastic",
62)
63
64"""
65Add the Finite Volumes solver using named arguments.
66This is the way you can add further PDE terms.
67This requires the 'BlockStructured' toolbox and 'ExaHyPE' to be built.
68"""
69my_solver = exahype2.solvers.fv.godunov.GlobalAdaptiveTimeStep(
70 name="FVSolver",
71 patch_size=patch_size,
72 unknowns=dimensions
73 + (
74 3 if dimensions == 2 else 6
75 ), # 3 (v_x, v_y, v_z) + 6 (σ_xx, σ_yy, σ_zz, σ_xy, σ_xz, σ_yz)
76 auxiliary_variables=0,
77 min_volume_h=(1.1 * min(size[0:dimensions]) / (3.0**depth)),
78 max_volume_h=(1.1 * min(size[0:dimensions]) / (3.0**depth)),
79 time_step_relaxation=0.5,
80)
81
82"""
83We want to define our PDE symbolically.
84"""
85my_pde = symhype.FirstOrderConservativePDEFormulation(
86 unknowns=dimensions + (3 if dimensions == 2 else 6),
87 auxiliary_variables=0,
88 dimensions=dimensions,
89)
90
91# Define the unknowns.
92# v: velocity components [v_x, v_y, v_z]
93v = my_pde.name_Q_entries(0, dimensions, "v")
94# sigma: stress components [σ_xx, σ_yy, σ_zz, σ_xy, σ_xz, σ_yz]
95sigma = my_pde.name_Q_entries(dimensions, 3 if dimensions == 2 else 6, "sigma")
96
97# Define material parameters.
98rho = sympy.symbols("rho")
99cp = sympy.symbols("cp")
100cs = sympy.symbols("cs")
101
102# Lamé parameters.
103mu = rho * cs * cs
104lamb = rho * cp * cp - 2.0 * mu
105
106if dimensions == 3:
107 """
108 Define the fluxes in each spatial direction.
109 The overall conservative form is:
110 ∂ₜQ + ∂ₓ(F_x(Q)) + ∂ᵧ(F_y(Q)) + ∂𝓏(F_z(Q)) = S
111 where Q = [v_x, v_y, v_z, σ_xx, σ_yy, σ_zz, σ_xy, σ_xz, σ_yz]^T.
112 """
113 # Flux [unknowns, dimensions]
114 # --- x-direction (direction index 0) ---
115 # Momentum equations:
116 my_pde.F[0, 0] = -1.0 / rho * sigma[0] # v_x: -σ_xx/ρ
117 my_pde.F[1, 0] = -1.0 / rho * sigma[3] # v_y: -σ_xy/ρ
118 my_pde.F[2, 0] = -1.0 / rho * sigma[4] # v_z: -σ_xz/ρ
119
120 # Stress equations:
121 my_pde.F[3, 0] = -(lamb + 2.0 * mu) * v[0] # σ_xx: -(λ+2μ)v_x
122 my_pde.F[4, 0] = -lamb * v[0] # σ_yy: -λ v_x
123 my_pde.F[5, 0] = -lamb * v[0] # σ_zz: -λ v_x
124 my_pde.F[6, 0] = -mu * v[1] # σ_xy: -μ v_y
125 my_pde.F[7, 0] = -mu * v[2] # σ_xz: -μ v_z
126 my_pde.F[8, 0] = 0 # σ_yz: 0
127
128 # --- y-direction (direction index 1) ---
129 # Momentum equations:
130 my_pde.F[0, 1] = -1.0 / rho * sigma[3] # v_x: -σ_xy/ρ
131 my_pde.F[1, 1] = -1.0 / rho * sigma[1] # v_y: -σ_yy/ρ
132 my_pde.F[2, 1] = -1.0 / rho * sigma[5] # v_z: -σ_yz/ρ
133
134 # Stress equations:
135 my_pde.F[3, 1] = -lamb * v[1] # σ_xx: -λ v_y
136 my_pde.F[4, 1] = -(lamb + 2.0 * mu) * v[1] # σ_yy: -(λ+2μ)v_y
137 my_pde.F[5, 1] = -lamb * v[1] # σ_zz: -λ v_y
138 my_pde.F[6, 1] = -mu * v[0] # σ_xy: -μ v_x
139 my_pde.F[7, 1] = 0 # σ_xz: 0
140 my_pde.F[8, 1] = -mu * v[2] # σ_yz: -μ v_z
141
142 # --- z-direction (direction index 2) ---
143 # Momentum equations:
144 my_pde.F[0, 2] = -1.0 / rho * sigma[4] # v_x: -σ_xz/ρ
145 my_pde.F[1, 2] = -1.0 / rho * sigma[5] # v_y: -σ_yz/ρ
146 my_pde.F[2, 2] = -1.0 / rho * sigma[2] # v_z: -σ_zz/ρ
147
148 # Stress equations:
149 my_pde.F[3, 2] = -lamb * v[2] # σ_xx: -λ v_z
150 my_pde.F[4, 2] = -lamb * v[2] # σ_yy: -λ v_z
151 my_pde.F[5, 2] = -(lamb + 2.0 * mu) * v[2] # σ_zz: -(λ+2μ)v_z
152 my_pde.F[6, 2] = 0 # σ_xy: 0
153 my_pde.F[7, 2] = -mu * v[0] # σ_xz: -μ v_x
154 my_pde.F[8, 2] = -mu * v[1] # σ_yz: -μ v_y
155
156 """
157 Define the eigenvalues.
158 For a 3D elastic system the characteristic speeds in a given
159 direction are:
160 { cp, cs, cs, 0, 0, 0, -cs, -cs, -cp }
161 where:
162 cp = sqrt((λ+2μ)/ρ) is the P-wave (compressional) speed,
163 cs = sqrt(μ/ρ) is the S-wave (shear) speed.
164 """
165 # Eigenvalues [unknowns, dimensions]
166 my_pde.eigenvalues[0] = cp
167 my_pde.eigenvalues[1] = cs
168 my_pde.eigenvalues[2] = cs
169 my_pde.eigenvalues[3] = 0
170 my_pde.eigenvalues[4] = 0
171 my_pde.eigenvalues[5] = 0
172 my_pde.eigenvalues[6] = -cs
173 my_pde.eigenvalues[7] = -cs
174 my_pde.eigenvalues[8] = -cp
175else:
176 # Flux [unknowns, dimensions]
177 my_pde.F[0, 0] = -1.0 / rho * sigma[0]
178 my_pde.F[1, 0] = -1.0 / rho * sigma[2]
179 my_pde.F[2, 0] = -(2.0 * mu + lamb) * v[0]
180 my_pde.F[3, 0] = -lamb * v[0]
181 my_pde.F[4, 0] = -mu * v[1]
182
183 my_pde.F[0, 1] = -1.0 / rho * sigma[2]
184 my_pde.F[1, 1] = -1.0 / rho * sigma[1]
185 my_pde.F[2, 1] = -lamb * v[1]
186 my_pde.F[3, 1] = -(2.0 * mu + lamb) * v[1]
187 my_pde.F[4, 1] = -mu * v[0]
188
189 # Eigenvalues [unknowns, dimensions]
190 my_pde.eigenvalues[0] = cp
191 my_pde.eigenvalues[1] = cs
192 my_pde.eigenvalues[2] = 0
193 my_pde.eigenvalues[3] = -cs
194 my_pde.eigenvalues[4] = -cp
195
196"""
197Since 'my_pde' only holds the PDE without initial- or boundary conditions,
198we still need to properly define initial- and boundary conditions.
199This gives us then a complete description of a 'scenario'.
200"""
201for i in range(dimensions + (3 if dimensions == 2 else 6)):
202 my_pde.initial_values[i] = 0
203 my_pde.boundary_values[i] = 0
204
205"""
206Define a source term.
207Here we add a time-dependent point force applied to one of the
208shear stress components (σ_xy in this case).
209"""
210t0 = sympy.symbols("t0")
211M0 = sympy.symbols("M0")
212t = sympy.symbols("t")
213force = M0 * t / (t0 * t0) * sympy.exp(-t / t0)
214
215max_h = sympy.symbols("MaxAdmissibleVolumeH")
216
217if dimensions == 3:
218 point_source = sympy.sqrt(
219 (10 - my_pde.x[0]) ** 2 + (10 - my_pde.x[1]) ** 2 + (10 - my_pde.x[2]) ** 2
220 )
221
222 # Apply the force to σ_xy, which is the 7th component in the overall Q (index 6).
223 my_pde.sources[0] = 0
224 my_pde.sources[1] = 0
225 my_pde.sources[2] = 0
226 my_pde.sources[3] = 0
227 my_pde.sources[4] = 0
228 my_pde.sources[5] = 0
229 my_pde.sources[6] = sympy.Piecewise((force, point_source <= max_h), (0, True))
230 my_pde.sources[7] = 0
231 my_pde.sources[8] = 0
232else:
233 point_source = sympy.sqrt((10 - my_pde.x[0]) ** 2 + (10 - my_pde.x[1]) ** 2)
234
235 my_pde.sources[0] = 0
236 my_pde.sources[1] = 0
237 my_pde.sources[2] = 0
238 my_pde.sources[3] = 0
239 my_pde.sources[4] = sympy.Piecewise((force, point_source <= max_h), (0, True))
240
241# Substitute numerical values for the material and source parameters.
242my_pde.substitute_expression(rho, 2.7)
243my_pde.substitute_expression(cp, 6.0)
244my_pde.substitute_expression(cs, 3.464)
245my_pde.substitute_expression(t0, 0.1)
246my_pde.substitute_expression(M0, 1000.0)
247
248"""
249Specify which implementation our solvers uses.
250Here we want to set the implementation we get from our symbolically defined PDE,
251i.e., we get the C++ implementation which is generated by SymHyPE.
252"""
253my_solver.set_implementation(
254 initial_conditions=my_pde.implementation_of_initial_conditions(),
255 boundary_conditions=my_pde.implementation_of_boundary_conditions(),
256 flux=my_pde.implementation_of_flux(),
257 max_eigenvalue=my_pde.implementation_of_max_eigenvalue(),
258 source_term=my_pde.implementation_of_sources(),
259)
260
261"""
262To see which variables (unknowns + auxiliary variables) we can visualise,
263let's add a plot description for the variables to our solver.
264"""
265my_solver.plot_description = my_pde.unknown_identifier_for_plotter()
266
267"""
268Add the solver to our project
269"""
270my_project.add_solver(my_solver)
271
272"""
273Configure some global parameters
274"""
275my_project.set_global_simulation_parameters(
276 dimensions=dimensions,
277 size=size[0:dimensions],
278 offset=offset[0:dimensions],
279 min_end_time=end_time,
280 max_end_time=end_time,
281 first_plot_time_stamp=0.0,
282 time_in_between_plots=time_in_between_two_snapshots,
283 periodic_BC=[False, False, False],
284)
285
286"""
287This defines where the output files should go.
288If you omit this, output files are automatically put into the application's folder.
289"""
290my_project.set_output_path("solutions")
291
292"""
293Configure load balancer for parallel execution.
294"""
295my_project.set_load_balancer("new ::exahype2::LoadBalancingConfiguration")
296
297"""
298We need to set the location of our core libraries ('Peano4').
299This helps us to resolve any dependencies.
300Additionally, we specify the build mode which you can also change to a different mode.
301"""
302my_project.set_Peano4_installation(
303 "../../../", mode=peano4.output.string_to_mode(compile_mode)
304)
305
306"""
307We generate and grab the underlying core project of 'Peano4'.
308This gives us access to some functions we want to use to finalise and build this project.
309"""
310my_project = my_project.generate_Peano4_project(verbose=False)
311
312"""
313Finally, we want to build our project.
314First, all of the necessary glue code is generated in the application folder,
315then 'make' is invoked automatically which compiles the generated code and links against our core libraries
316and toolboxes which have been built before.
317You can also always invoke 'make' yourself to compile, or cleanup with 'make clean'.
318"""
319my_project.build(make=True, make_clean_first=True, throw_away_data_after_build=True)
320
321print("\nPDE is: ")
322print(my_pde.__str__())
323print(my_solver)
324print(my_project)