Peano
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
3 import sympy
4 
5 """
6 We import the required modules for this project.
7 We always need the 'peano4' module as this is our core project.
8 Since we are creating a SymHyPE/ExaHyPE 2 application, we additionally
9 need to import the 'exahype2' and 'symhype' modules.
10 """
11 import peano4
12 import exahype2
13 import symhype
14 
15 """
16 We specify the space dimensions here.
17 We support either 2- or 3-dimensional problems.
18 """
19 dimensions = 2
20 
21 """
22 The number of finite volumes per axis in one patch.
23 """
24 patch_size = 16
25 
26 """
27 The number of levels the mesh is refined.
28 """
29 depth = 5
30 
31 """
32 The simulation end time.
33 """
34 end_time = 1.0
35 
36 """
37 Choose domain size and offset.
38 """
39 size = [20.0, 20.0, 20.0]
40 offset = [-5.0, 0.0, 0.0]
41 
42 """
43 Choose how often a snapshot is written.
44 """
45 time_in_between_two_snapshots = end_time / 10
46 # time_in_between_two_snapshots = 0 # Set to 0 to disable I/O
47 
48 """
49 Switch between 'Release', 'Debug', 'Asserts', 'Trace', 'Stats'.
50 """
51 compile_mode = "Release"
52 
53 """
54 We first create a new ExaHyPE 2 project.
55 For this, we specify the (nested) namespaces, the name of our main file and our executable name.
56 """
57 my_project = exahype2.Project(
58  namespace=["tutorials", "symhype", "elastic"],
59  project_name="Elastic",
60  directory=".",
61  executable="Elastic",
62 )
63 
64 """
65 Add the Finite Volumes solver using named arguments.
66 This is the way you can add further PDE terms.
67 This requires the 'BlockStructured' toolbox and 'ExaHyPE' to be built.
68 """
69 my_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 """
83 We want to define our PDE symbolically.
84 """
85 my_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]
93 v = my_pde.name_Q_entries(0, dimensions, "v")
94 # sigma: stress components [σ_xx, σ_yy, σ_zz, σ_xy, σ_xz, σ_yz]
95 sigma = my_pde.name_Q_entries(dimensions, 3 if dimensions == 2 else 6, "sigma")
96 
97 # Define material parameters.
98 rho = sympy.symbols("rho")
99 cp = sympy.symbols("cp")
100 cs = sympy.symbols("cs")
101 
102 # Lamé parameters.
103 mu = rho * cs * cs
104 lamb = rho * cp * cp - 2.0 * mu
105 
106 if 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
175 else:
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 """
197 Since 'my_pde' only holds the PDE without initial- or boundary conditions,
198 we still need to properly define initial- and boundary conditions.
199 This gives us then a complete description of a 'scenario'.
200 """
201 for 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 """
206 Define a source term.
207 Here we add a time-dependent point force applied to one of the
208 shear stress components (σ_xy in this case).
209 """
210 t0 = sympy.symbols("t0")
211 M0 = sympy.symbols("M0")
212 t = sympy.symbols("t")
213 force = M0 * t / (t0 * t0) * sympy.exp(-t / t0)
214 
215 max_h = sympy.symbols("MaxAdmissibleVolumeH")
216 
217 if 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
232 else:
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.
242 my_pde.substitute_expression(rho, 2.7)
243 my_pde.substitute_expression(cp, 6.0)
244 my_pde.substitute_expression(cs, 3.464)
245 my_pde.substitute_expression(t0, 0.1)
246 my_pde.substitute_expression(M0, 1000.0)
247 
248 """
249 Specify which implementation our solvers uses.
250 Here we want to set the implementation we get from our symbolically defined PDE,
251 i.e., we get the C++ implementation which is generated by SymHyPE.
252 """
253 my_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 """
262 To see which variables (unknowns + auxiliary variables) we can visualise,
263 let's add a plot description for the variables to our solver.
264 """
265 my_solver.plot_description = my_pde.unknown_identifier_for_plotter()
266 
267 """
268 Add the solver to our project
269 """
270 my_project.add_solver(my_solver)
271 
272 """
273 Configure some global parameters
274 """
275 my_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 """
287 This defines where the output files should go.
288 If you omit this, output files are automatically put into the application's folder.
289 """
290 my_project.set_output_path("solutions")
291 
292 """
293 Configure load balancer for parallel execution.
294 """
295 my_project.set_load_balancer("new ::exahype2::LoadBalancingConfiguration")
296 
297 """
298 We need to set the location of our core libraries ('Peano4').
299 This helps us to resolve any dependencies.
300 Additionally, we specify the build mode which you can also change to a different mode.
301 """
302 my_project.set_Peano4_installation(
303  "../../../", mode=peano4.output.string_to_mode(compile_mode)
304 )
305 
306 """
307 We generate and grab the underlying core project of 'Peano4'.
308 This gives us access to some functions we want to use to finalise and build this project.
309 """
310 my_project = my_project.generate_Peano4_project(verbose=False)
311 
312 """
313 Finally, we want to build our project.
314 First, all of the necessary glue code is generated in the application folder,
315 then 'make' is invoked automatically which compiles the generated code and links against our core libraries
316 and toolboxes which have been built before.
317 You can also always invoke 'make' yourself to compile, or cleanup with 'make clean'.
318 """
319 my_project.build(make=True, make_clean_first=True, throw_away_data_after_build=True)
320 
321 print("\nPDE is: ")
322 print(my_pde.__str__())
323 print(my_solver)
324 print(my_project)
static double min(double const x, double const y)