Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/semidiscretization/semidiscretization_hyperbolic.jl
2055 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
"""
9
SemidiscretizationHyperbolic
10
11
A struct containing everything needed to describe a spatial semidiscretization
12
of a hyperbolic conservation law.
13
"""
14
mutable struct SemidiscretizationHyperbolic{Mesh, Equations, InitialCondition,
15
BoundaryConditions,
16
SourceTerms, Solver, Cache} <:
17
AbstractSemidiscretization
18
mesh::Mesh
19
equations::Equations
20
21
# This guy is a bit messy since we abuse it as some kind of "exact solution"
22
# although this doesn't really exist...
23
initial_condition::InitialCondition
24
25
boundary_conditions::BoundaryConditions
26
source_terms::SourceTerms
27
solver::Solver
28
cache::Cache
29
performance_counter::PerformanceCounter
30
end
31
32
"""
33
SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;
34
source_terms=nothing,
35
boundary_conditions=boundary_condition_periodic,
36
RealT=real(solver),
37
uEltype=RealT)
38
39
Construct a semidiscretization of a hyperbolic PDE.
40
"""
41
function SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;
42
source_terms = nothing,
43
boundary_conditions = boundary_condition_periodic,
44
# `RealT` is used as real type for node locations etc.
45
# while `uEltype` is used as element type of solutions etc.
46
RealT = real(solver), uEltype = RealT)
47
@assert ndims(mesh) == ndims(equations)
48
49
cache = create_cache(mesh, equations, solver, RealT, uEltype)
50
_boundary_conditions = digest_boundary_conditions(boundary_conditions, mesh, solver,
51
cache)
52
53
check_periodicity_mesh_boundary_conditions(mesh, _boundary_conditions)
54
55
performance_counter = PerformanceCounter()
56
57
SemidiscretizationHyperbolic{typeof(mesh), typeof(equations),
58
typeof(initial_condition),
59
typeof(_boundary_conditions), typeof(source_terms),
60
typeof(solver), typeof(cache)}(mesh, equations,
61
initial_condition,
62
_boundary_conditions,
63
source_terms, solver,
64
cache,
65
performance_counter)
66
end
67
68
# @eval due to @muladd
69
@eval Adapt.@adapt_structure(SemidiscretizationHyperbolic)
70
71
# Create a new semidiscretization but change some parameters compared to the input.
72
# `Base.similar` follows a related concept but would require us to `copy` the `mesh`,
73
# which would impact the performance. Instead, `SciMLBase.remake` has exactly the
74
# semantics we want to use here. In particular, it allows us to re-use mutable parts,
75
# e.g. `remake(semi).mesh === semi.mesh`.
76
function remake(semi::SemidiscretizationHyperbolic; uEltype = real(semi.solver),
77
mesh = semi.mesh,
78
equations = semi.equations,
79
initial_condition = semi.initial_condition,
80
solver = semi.solver,
81
source_terms = semi.source_terms,
82
boundary_conditions = semi.boundary_conditions)
83
# TODO: Which parts do we want to `remake`? At least the solver needs some
84
# special care if shock-capturing volume integrals are used (because of
85
# the indicators and their own caches...).
86
SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;
87
source_terms, boundary_conditions, uEltype)
88
end
89
90
# general fallback
91
function digest_boundary_conditions(boundary_conditions, mesh, solver, cache)
92
boundary_conditions
93
end
94
95
# general fallback
96
function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,
97
mesh, solver, cache)
98
boundary_conditions
99
end
100
101
# resolve ambiguities with definitions below
102
function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,
103
mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,
104
cache)
105
boundary_conditions
106
end
107
108
function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,
109
mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,
110
cache)
111
boundary_conditions
112
end
113
114
function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,
115
mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,
116
cache)
117
boundary_conditions
118
end
119
120
# allow passing a single BC that get converted into a tuple of BCs
121
# on (mapped) hypercube domains
122
function digest_boundary_conditions(boundary_conditions,
123
mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,
124
cache)
125
(; x_neg = boundary_conditions, x_pos = boundary_conditions)
126
end
127
128
function digest_boundary_conditions(boundary_conditions,
129
mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,
130
cache)
131
(; x_neg = boundary_conditions, x_pos = boundary_conditions,
132
y_neg = boundary_conditions, y_pos = boundary_conditions)
133
end
134
135
function digest_boundary_conditions(boundary_conditions,
136
mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,
137
cache)
138
(; x_neg = boundary_conditions, x_pos = boundary_conditions,
139
y_neg = boundary_conditions, y_pos = boundary_conditions,
140
z_neg = boundary_conditions, z_pos = boundary_conditions)
141
end
142
143
# allow passing a tuple of BCs that get converted into a named tuple to make it
144
# self-documenting on (mapped) hypercube domains
145
function digest_boundary_conditions(boundary_conditions::NTuple{2, Any},
146
mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,
147
cache)
148
(; x_neg = boundary_conditions[1], x_pos = boundary_conditions[2])
149
end
150
151
function digest_boundary_conditions(boundary_conditions::NTuple{4, Any},
152
mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,
153
cache)
154
(; x_neg = boundary_conditions[1], x_pos = boundary_conditions[2],
155
y_neg = boundary_conditions[3], y_pos = boundary_conditions[4])
156
end
157
158
function digest_boundary_conditions(boundary_conditions::NTuple{6, Any},
159
mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,
160
cache)
161
(; x_neg = boundary_conditions[1], x_pos = boundary_conditions[2],
162
y_neg = boundary_conditions[3], y_pos = boundary_conditions[4],
163
z_neg = boundary_conditions[5], z_pos = boundary_conditions[6])
164
end
165
166
# allow passing named tuples of BCs constructed in an arbitrary order
167
# on (mapped) hypercube domains
168
function digest_boundary_conditions(boundary_conditions::NamedTuple{Keys, ValueTypes},
169
mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,
170
cache) where {Keys, ValueTypes <: NTuple{2, Any}}
171
@unpack x_neg, x_pos = boundary_conditions
172
(; x_neg, x_pos)
173
end
174
175
function digest_boundary_conditions(boundary_conditions::NamedTuple{Keys, ValueTypes},
176
mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,
177
cache) where {Keys, ValueTypes <: NTuple{4, Any}}
178
@unpack x_neg, x_pos, y_neg, y_pos = boundary_conditions
179
(; x_neg, x_pos, y_neg, y_pos)
180
end
181
182
function digest_boundary_conditions(boundary_conditions::NamedTuple{Keys, ValueTypes},
183
mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,
184
cache) where {Keys, ValueTypes <: NTuple{6, Any}}
185
@unpack x_neg, x_pos, y_neg, y_pos, z_neg, z_pos = boundary_conditions
186
(; x_neg, x_pos, y_neg, y_pos, z_neg, z_pos)
187
end
188
189
# sort the boundary conditions from a dictionary and into tuples
190
function digest_boundary_conditions(boundary_conditions::Dict, mesh, solver, cache)
191
UnstructuredSortedBoundaryTypes(boundary_conditions, cache)
192
end
193
194
function digest_boundary_conditions(boundary_conditions::AbstractArray, mesh, solver,
195
cache)
196
throw(ArgumentError("Please use a (named) tuple instead of an (abstract) array to supply multiple boundary conditions (to improve performance)."))
197
end
198
199
# No checks for these meshes yet available
200
function check_periodicity_mesh_boundary_conditions(mesh::Union{P4estMesh,
201
P4estMeshView,
202
UnstructuredMesh2D,
203
T8codeMesh,
204
DGMultiMesh},
205
boundary_conditions)
206
end
207
208
# No actions needed for periodic boundary conditions
209
function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh,
210
StructuredMesh,
211
StructuredMeshView},
212
boundary_conditions::BoundaryConditionPeriodic)
213
end
214
215
function check_periodicity_mesh_boundary_conditions_x(mesh, x_neg, x_pos)
216
if isperiodic(mesh, 1) &&
217
(x_neg != BoundaryConditionPeriodic() ||
218
x_pos != BoundaryConditionPeriodic())
219
@error "For periodic mesh non-periodic boundary conditions in x-direction are supplied."
220
end
221
end
222
223
function check_periodicity_mesh_boundary_conditions_y(mesh, y_neg, y_pos)
224
if isperiodic(mesh, 2) &&
225
(y_neg != BoundaryConditionPeriodic() ||
226
y_pos != BoundaryConditionPeriodic())
227
@error "For periodic mesh non-periodic boundary conditions in y-direction are supplied."
228
end
229
end
230
231
function check_periodicity_mesh_boundary_conditions_z(mesh, z_neg, z_pos)
232
if isperiodic(mesh, 3) &&
233
(z_neg != BoundaryConditionPeriodic() ||
234
z_pos != BoundaryConditionPeriodic())
235
@error "For periodic mesh non-periodic boundary conditions in z-direction are supplied."
236
end
237
end
238
239
function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{1},
240
StructuredMesh{1}},
241
boundary_conditions::Union{NamedTuple,
242
Tuple})
243
check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions[1],
244
boundary_conditions[2])
245
end
246
247
function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{2},
248
StructuredMesh{2},
249
StructuredMeshView{2}},
250
boundary_conditions::Union{NamedTuple,
251
Tuple})
252
check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions[1],
253
boundary_conditions[2])
254
check_periodicity_mesh_boundary_conditions_y(mesh, boundary_conditions[3],
255
boundary_conditions[4])
256
end
257
258
function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{3},
259
StructuredMesh{3}},
260
boundary_conditions::Union{NamedTuple,
261
Tuple})
262
check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions[1],
263
boundary_conditions[2])
264
check_periodicity_mesh_boundary_conditions_y(mesh, boundary_conditions[3],
265
boundary_conditions[4])
266
check_periodicity_mesh_boundary_conditions_z(mesh, boundary_conditions[5],
267
boundary_conditions[6])
268
end
269
270
function Base.show(io::IO, semi::SemidiscretizationHyperbolic)
271
@nospecialize semi # reduce precompilation time
272
273
print(io, "SemidiscretizationHyperbolic(")
274
print(io, semi.mesh)
275
print(io, ", ", semi.equations)
276
print(io, ", ", semi.initial_condition)
277
print(io, ", ", semi.boundary_conditions)
278
print(io, ", ", semi.source_terms)
279
print(io, ", ", semi.solver)
280
print(io, ", cache(")
281
for (idx, key) in enumerate(keys(semi.cache))
282
idx > 1 && print(io, " ")
283
print(io, key)
284
end
285
print(io, "))")
286
end
287
288
function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationHyperbolic)
289
@nospecialize semi # reduce precompilation time
290
291
if get(io, :compact, false)
292
show(io, semi)
293
else
294
summary_header(io, "SemidiscretizationHyperbolic")
295
summary_line(io, "#spatial dimensions", ndims(semi.equations))
296
summary_line(io, "mesh", semi.mesh)
297
summary_line(io, "equations", semi.equations |> typeof |> nameof)
298
summary_line(io, "initial condition", semi.initial_condition)
299
300
print_boundary_conditions(io, semi)
301
302
summary_line(io, "source terms", semi.source_terms)
303
summary_line(io, "solver", semi.solver |> typeof |> nameof)
304
summary_line(io, "total #DOFs per field", ndofsglobal(semi))
305
summary_footer(io)
306
end
307
end
308
309
# type alias for dispatch in printing of boundary conditions
310
#! format: off
311
const SemiHypMeshBCSolver{Mesh, BoundaryConditions, Solver} =
312
SemidiscretizationHyperbolic{Mesh,
313
Equations,
314
InitialCondition,
315
BoundaryConditions,
316
SourceTerms,
317
Solver} where {Equations,
318
InitialCondition,
319
SourceTerms}
320
#! format: on
321
322
# generic fallback: print the type of semi.boundary_condition.
323
function print_boundary_conditions(io, semi::SemiHypMeshBCSolver)
324
summary_line(io, "boundary conditions", typeof(semi.boundary_conditions))
325
end
326
327
function print_boundary_conditions(io,
328
semi::SemiHypMeshBCSolver{<:Any,
329
<:UnstructuredSortedBoundaryTypes})
330
@unpack boundary_conditions = semi
331
@unpack boundary_dictionary = boundary_conditions
332
summary_line(io, "boundary conditions", length(boundary_dictionary))
333
for (boundary_name, boundary_condition) in boundary_dictionary
334
summary_line(increment_indent(io), boundary_name, typeof(boundary_condition))
335
end
336
end
337
338
function print_boundary_conditions(io, semi::SemiHypMeshBCSolver{<:Any, <:NamedTuple})
339
@unpack boundary_conditions = semi
340
summary_line(io, "boundary conditions", length(boundary_conditions))
341
bc_names = keys(boundary_conditions)
342
for (i, bc_name) in enumerate(bc_names)
343
summary_line(increment_indent(io), String(bc_name),
344
typeof(boundary_conditions[i]))
345
end
346
end
347
348
function print_boundary_conditions(io,
349
semi::SemiHypMeshBCSolver{<:Union{TreeMesh,
350
StructuredMesh},
351
<:Union{Tuple, NamedTuple,
352
AbstractArray}})
353
summary_line(io, "boundary conditions", 2 * ndims(semi))
354
bcs = semi.boundary_conditions
355
356
summary_line(increment_indent(io), "negative x", bcs[1])
357
summary_line(increment_indent(io), "positive x", bcs[2])
358
if ndims(semi) > 1
359
summary_line(increment_indent(io), "negative y", bcs[3])
360
summary_line(increment_indent(io), "positive y", bcs[4])
361
end
362
if ndims(semi) > 2
363
summary_line(increment_indent(io), "negative z", bcs[5])
364
summary_line(increment_indent(io), "positive z", bcs[6])
365
end
366
end
367
368
@inline Base.ndims(semi::SemidiscretizationHyperbolic) = ndims(semi.mesh)
369
370
@inline nvariables(semi::SemidiscretizationHyperbolic) = nvariables(semi.equations)
371
372
@inline Base.real(semi::SemidiscretizationHyperbolic) = real(semi.solver)
373
374
@inline function mesh_equations_solver_cache(semi::SemidiscretizationHyperbolic)
375
@unpack mesh, equations, solver, cache = semi
376
return mesh, equations, solver, cache
377
end
378
379
function calc_error_norms(func, u_ode, t, analyzer, semi::SemidiscretizationHyperbolic,
380
cache_analysis)
381
@unpack mesh, equations, initial_condition, solver, cache = semi
382
u = wrap_array(u_ode, mesh, equations, solver, cache)
383
384
calc_error_norms(func, u, t, analyzer, mesh, equations, initial_condition, solver,
385
cache, cache_analysis)
386
end
387
388
function compute_coefficients(t, semi::SemidiscretizationHyperbolic)
389
# Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl`
390
compute_coefficients(semi.initial_condition, t, semi)
391
end
392
393
function compute_coefficients!(u_ode, t, semi::SemidiscretizationHyperbolic)
394
compute_coefficients!(u_ode, semi.initial_condition, t, semi)
395
end
396
397
function rhs!(du_ode, u_ode, semi::SemidiscretizationHyperbolic, t)
398
@unpack mesh, equations, boundary_conditions, source_terms, solver, cache = semi
399
400
u = wrap_array(u_ode, mesh, equations, solver, cache)
401
du = wrap_array(du_ode, mesh, equations, solver, cache)
402
403
# TODO: Taal decide, do we need to pass the mesh?
404
time_start = time_ns()
405
@trixi_timeit timer() "rhs!" rhs!(du, u, t, mesh, equations,
406
boundary_conditions, source_terms, solver, cache)
407
runtime = time_ns() - time_start
408
put!(semi.performance_counter, runtime)
409
410
return nothing
411
end
412
end # @muladd
413
414