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
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
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
function integrate_via_indices(func::Func, u,
123
mesh::TreeMesh{1}, equations, dg::DGSEM, cache,
124
args...; normalize = true) where {Func}
125
@unpack weights = dg.basis
126
127
# Initialize integral with zeros of the right shape
128
integral = zero(func(u, 1, 1, equations, dg, args...))
129
130
# Use quadrature to numerically integrate over entire domain
131
@batch reduction=(+, integral) for element in eachelement(dg, cache)
132
volume_jacobian_ = volume_jacobian(element, mesh, cache)
133
for i in eachnode(dg)
134
integral += volume_jacobian_ * weights[i] *
135
func(u, i, element, equations, dg, args...)
136
end
137
end
138
139
# Normalize with total volume
140
if normalize
141
integral = integral / total_volume(mesh)
142
end
143
144
return integral
145
end
146
147
function integrate_via_indices(func::Func, u,
148
mesh::StructuredMesh{1}, equations, dg::DGSEM, cache,
149
args...; normalize = true) where {Func}
150
@unpack weights = dg.basis
151
152
# Initialize integral with zeros of the right shape
153
integral = zero(func(u, 1, 1, equations, dg, args...))
154
total_volume = zero(real(mesh))
155
156
# Use quadrature to numerically integrate over entire domain
157
@batch reduction=((+, integral), (+, total_volume)) for element in eachelement(dg,
158
cache)
159
for i in eachnode(dg)
160
jacobian_volume = abs(inv(cache.elements.inverse_jacobian[i, element]))
161
integral += jacobian_volume * weights[i] *
162
func(u, i, element, equations, dg, args...)
163
total_volume += jacobian_volume * weights[i]
164
end
165
end
166
# Normalize with total volume
167
if normalize
168
integral = integral / total_volume
169
end
170
171
return integral
172
end
173
174
function integrate(func::Func, u,
175
mesh::Union{TreeMesh{1}, StructuredMesh{1}},
176
equations, dg::DG, cache; normalize = true) where {Func}
177
integrate_via_indices(u, mesh, equations, dg, cache;
178
normalize = normalize) do u, i, element, equations, dg
179
u_local = get_node_vars(u, equations, dg, i, element)
180
return func(u_local, equations)
181
end
182
end
183
184
function analyze(::typeof(entropy_timederivative), du, u, t,
185
mesh::Union{TreeMesh{1}, StructuredMesh{1}}, equations, dg::DG, cache)
186
# Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ
187
integrate_via_indices(u, mesh, equations, dg, cache,
188
du) do u, i, element, equations, dg, du
189
u_node = get_node_vars(u, equations, dg, i, element)
190
du_node = get_node_vars(du, equations, dg, i, element)
191
dot(cons2entropy(u_node, equations), du_node)
192
end
193
end
194
195
function analyze(::Val{:l2_divb}, du, u, t,
196
mesh::TreeMesh{1}, equations::IdealGlmMhdEquations1D,
197
dg::DG, cache)
198
integrate_via_indices(u, mesh, equations, dg, cache,
199
dg.basis.derivative_matrix) do u, i, element, equations, dg,
200
derivative_matrix
201
divb = zero(eltype(u))
202
for k in eachnode(dg)
203
divb += derivative_matrix[i, k] * u[6, k, element]
204
end
205
divb *= cache.elements.inverse_jacobian[element]
206
divb^2
207
end |> sqrt
208
end
209
210
function analyze(::Val{:linf_divb}, du, u, t,
211
mesh::TreeMesh{1}, equations::IdealGlmMhdEquations1D,
212
dg::DG, cache)
213
@unpack derivative_matrix, weights = dg.basis
214
215
# integrate over all elements to get the divergence-free condition errors
216
linf_divb = zero(eltype(u))
217
@batch reduction=(max, linf_divb) for element in eachelement(dg, cache)
218
for i in eachnode(dg)
219
divb = zero(eltype(u))
220
for k in eachnode(dg)
221
divb += derivative_matrix[i, k] * u[6, k, element]
222
end
223
divb *= cache.elements.inverse_jacobian[element]
224
linf_divb = max(linf_divb, abs(divb))
225
end
226
end
227
228
return linf_divb
229
end
230
end # @muladd
231
232