Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/callbacks_step/analysis_dg1d.jl
2802 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
function create_cache_analysis(analyzer, mesh::TreeMesh{1},
9
equations, dg::DG, cache,
10
RealT, uEltype)
11
12
# pre-allocate buffers
13
# We use `StrideArray`s here since these buffers are used in performance-critical
14
# places and the additional information passed to the compiler makes them faster
15
# than native `Array`s.
16
u_local = StrideArray(undef, uEltype,
17
StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)))
18
x_local = StrideArray(undef, RealT,
19
StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)))
20
21
return (; u_local, x_local)
22
end
23
24
function create_cache_analysis(analyzer, mesh::StructuredMesh{1},
25
equations, dg::DG, cache,
26
RealT, uEltype)
27
28
# pre-allocate buffers
29
# We use `StrideArray`s here since these buffers are used in performance-critical
30
# places and the additional information passed to the compiler makes them faster
31
# than native `Array`s.
32
u_local = StrideArray(undef, uEltype,
33
StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)))
34
x_local = StrideArray(undef, RealT,
35
StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)))
36
jacobian_local = StrideArray(undef, RealT,
37
StaticInt(nnodes(analyzer)))
38
39
return (; u_local, x_local, jacobian_local)
40
end
41
42
function calc_error_norms(func, u, t, analyzer,
43
mesh::TreeMesh{1}, equations, initial_condition,
44
dg::DGSEM, cache, cache_analysis)
45
@unpack vandermonde, weights = analyzer
46
@unpack node_coordinates = cache.elements
47
@unpack u_local, x_local = cache_analysis
48
49
# Set up data structures
50
l2_error = zero(func(get_node_vars(u, equations, dg, 1, 1), equations))
51
linf_error = copy(l2_error)
52
53
# Iterate over all elements for error calculations
54
for element in eachelement(dg, cache)
55
# Interpolate solution and node locations to analysis nodes
56
multiply_dimensionwise!(u_local, vandermonde, view(u, :, :, element))
57
multiply_dimensionwise!(x_local, vandermonde,
58
view(node_coordinates, :, :, element))
59
60
# Calculate errors at each analysis node
61
volume_jacobian_ = volume_jacobian(element, mesh, cache)
62
63
for i in eachnode(analyzer)
64
u_exact = initial_condition(get_node_coords(x_local, equations, dg, i), t,
65
equations)
66
diff = func(u_exact, equations) -
67
func(get_node_vars(u_local, equations, dg, i), equations)
68
l2_error += diff .^ 2 * (weights[i] * volume_jacobian_)
69
linf_error = @. max(linf_error, abs(diff))
70
end
71
end
72
73
# For L2 error, divide by total volume
74
total_volume_ = total_volume(mesh)
75
l2_error = @. sqrt(l2_error / total_volume_)
76
77
return l2_error, linf_error
78
end
79
80
function calc_error_norms(func, u, t, analyzer,
81
mesh::StructuredMesh{1}, equations, initial_condition,
82
dg::DGSEM, cache, cache_analysis)
83
@unpack vandermonde, weights = analyzer
84
@unpack node_coordinates, inverse_jacobian = cache.elements
85
@unpack u_local, x_local, jacobian_local = cache_analysis
86
87
# Set up data structures
88
l2_error = zero(func(get_node_vars(u, equations, dg, 1, 1), equations))
89
linf_error = copy(l2_error)
90
total_volume = zero(real(mesh))
91
92
# Iterate over all elements for error calculations
93
for element in eachelement(dg, cache)
94
# Interpolate solution and node locations to analysis nodes
95
multiply_dimensionwise!(u_local, vandermonde, view(u, :, :, element))
96
multiply_dimensionwise!(x_local, vandermonde,
97
view(node_coordinates, :, :, element))
98
multiply_scalar_dimensionwise!(jacobian_local, vandermonde,
99
inv.(view(inverse_jacobian, :, element)))
100
101
# Calculate errors at each analysis node
102
for i in eachnode(analyzer)
103
u_exact = initial_condition(get_node_coords(x_local, equations, dg, i), t,
104
equations)
105
diff = func(u_exact, equations) -
106
func(get_node_vars(u_local, equations, dg, i), equations)
107
# We take absolute value as we need the Jacobian here for the volume calculation
108
abs_jacobian_local_i = abs(jacobian_local[i])
109
110
l2_error += diff .^ 2 * (weights[i] * abs_jacobian_local_i)
111
linf_error = @. max(linf_error, abs(diff))
112
total_volume += weights[i] * abs_jacobian_local_i
113
end
114
end
115
116
# For L2 error, divide by total volume
117
l2_error = @. sqrt(l2_error / total_volume)
118
119
return l2_error, linf_error
120
end
121
122
# Use quadrature to numerically integrate a single element.
123
# We do not multiply by the Jacobian to stay in reference space.
124
# This avoids the need to divide the RHS of the DG scheme by the Jacobian when computing
125
# the time derivative of entropy, see `entropy_change_reference_element`.
126
function integrate_reference_element(func::Func, u, element,
127
mesh::AbstractMesh{1}, equations, dg::DGSEM, cache,
128
args...) where {Func}
129
@unpack weights = dg.basis
130
131
# Initialize integral with zeros of the right shape
132
element_integral = zero(func(u, 1, element, equations, dg, args...))
133
134
for i in eachnode(dg)
135
element_integral += weights[i] *
136
func(u, i, element, equations, dg, args...)
137
end
138
139
return element_integral
140
end
141
142
# Calculate ∫_e (∂S/∂u ⋅ ∂u/∂t) dΩ_e where the result on element 'e' is kept in reference space
143
# Note that ∂S/∂u = w(u) with entropy variables w
144
function entropy_change_reference_element(du, u, element,
145
mesh::AbstractMesh{1},
146
equations, dg::DGSEM, cache, args...)
147
return integrate_reference_element(u, element, mesh, equations, dg, cache,
148
du) do u, i, element, equations, dg, du
149
u_node = get_node_vars(u, equations, dg, i, element)
150
du_node = get_node_vars(du, equations, dg, i, element)
151
152
dot(cons2entropy(u_node, equations), du_node)
153
end
154
end
155
156
# calculate surface integral of func(u, equations) * normal on the reference element.
157
function surface_integral_reference_element(func::Func, u, element,
158
mesh::Union{TreeMesh{1}, StructuredMesh{1}},
159
equations, dg::DGSEM,
160
cache, args...) where {Func}
161
u_left = get_node_vars(u, equations, dg, 1, element)
162
u_right = get_node_vars(u, equations, dg, nnodes(dg), element)
163
164
surface_integral = func(u_right, 1, equations) - func(u_left, 1, equations)
165
return surface_integral
166
end
167
168
function integrate_via_indices(func::Func, u,
169
mesh::TreeMesh{1}, equations, dg::DGSEM, cache,
170
args...; normalize = true) where {Func}
171
@unpack weights = dg.basis
172
173
# Initialize integral with zeros of the right shape
174
integral = zero(func(u, 1, 1, equations, dg, args...))
175
176
# Use quadrature to numerically integrate over entire domain
177
@batch reduction=(+, integral) for element in eachelement(dg, cache)
178
volume_jacobian_ = volume_jacobian(element, mesh, cache)
179
for i in eachnode(dg)
180
integral += volume_jacobian_ * weights[i] *
181
func(u, i, element, equations, dg, args...)
182
end
183
end
184
185
# Normalize with total volume
186
if normalize
187
integral = integral / total_volume(mesh)
188
end
189
190
return integral
191
end
192
193
function integrate_via_indices(func::Func, u,
194
mesh::StructuredMesh{1}, equations, dg::DGSEM, cache,
195
args...; normalize = true) where {Func}
196
@unpack weights = dg.basis
197
198
# Initialize integral with zeros of the right shape
199
integral = zero(func(u, 1, 1, equations, dg, args...))
200
total_volume = zero(real(mesh))
201
202
# Use quadrature to numerically integrate over entire domain
203
@batch reduction=((+, integral), (+, total_volume)) for element in eachelement(dg,
204
cache)
205
for i in eachnode(dg)
206
jacobian_volume = abs(inv(cache.elements.inverse_jacobian[i, element]))
207
integral += jacobian_volume * weights[i] *
208
func(u, i, element, equations, dg, args...)
209
total_volume += jacobian_volume * weights[i]
210
end
211
end
212
# Normalize with total volume
213
if normalize
214
integral = integral / total_volume
215
end
216
217
return integral
218
end
219
220
function integrate(func::Func, u,
221
mesh::Union{TreeMesh{1}, StructuredMesh{1}},
222
equations, dg::Union{DGSEM, FDSBP}, cache;
223
normalize = true) where {Func}
224
integrate_via_indices(u, mesh, equations, dg, cache;
225
normalize = normalize) do u, i, element, equations, dg
226
u_local = get_node_vars(u, equations, dg, i, element)
227
return func(u_local, equations)
228
end
229
end
230
231
function analyze(::typeof(entropy_timederivative), du, u, t,
232
mesh::Union{TreeMesh{1}, StructuredMesh{1}}, equations,
233
dg::Union{DGSEM, FDSBP}, cache)
234
# Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ
235
integrate_via_indices(u, mesh, equations, dg, cache,
236
du) do u, i, element, equations, dg, du
237
u_node = get_node_vars(u, equations, dg, i, element)
238
du_node = get_node_vars(du, equations, dg, i, element)
239
return dot(cons2entropy(u_node, equations), du_node)
240
end
241
end
242
243
function analyze(::Val{:l2_divb}, du, u, t,
244
mesh::TreeMesh{1}, equations::IdealGlmMhdEquations1D,
245
dg::DGSEM, cache)
246
integrate_via_indices(u, mesh, equations, dg, cache,
247
dg.basis.derivative_matrix) do u, i, element, equations, dg,
248
derivative_matrix
249
divb = zero(eltype(u))
250
for k in eachnode(dg)
251
divb += derivative_matrix[i, k] * u[6, k, element]
252
end
253
divb *= cache.elements.inverse_jacobian[element]
254
return divb^2
255
end |> sqrt
256
end
257
258
function analyze(::Val{:linf_divb}, du, u, t,
259
mesh::TreeMesh{1}, equations::IdealGlmMhdEquations1D,
260
dg::DGSEM, cache)
261
@unpack derivative_matrix, weights = dg.basis
262
263
# integrate over all elements to get the divergence-free condition errors
264
linf_divb = zero(eltype(u))
265
@batch reduction=(max, linf_divb) for element in eachelement(dg, cache)
266
for i in eachnode(dg)
267
divb = zero(eltype(u))
268
for k in eachnode(dg)
269
divb += derivative_matrix[i, k] * u[6, k, element]
270
end
271
divb *= cache.elements.inverse_jacobian[element]
272
linf_divb = max(linf_divb, abs(divb))
273
end
274
end
275
276
return linf_divb
277
end
278
end # @muladd
279
280