Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/callbacks_step/analysis_dgmulti.jl
5586 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 calc_error_norms(func, u, t, analyzer,
9
mesh::DGMultiMesh{NDIMS}, equations, initial_condition,
10
dg::DGMulti{NDIMS}, cache, cache_analysis) where {NDIMS}
11
rd = dg.basis
12
md = mesh.md
13
(; u_values) = cache.solution_container
14
15
# interpolate u to quadrature points
16
apply_to_each_field(mul_by!(rd.Vq), u_values, u)
17
18
component_l2_errors = zero(eltype(u_values))
19
component_linf_errors = zero(eltype(u_values))
20
for i in each_quad_node_global(mesh, dg, cache)
21
u_exact = initial_condition(SVector(getindex.(md.xyzq, i)), t, equations)
22
error_at_node = func(u_values[i], equations) - func(u_exact, equations)
23
component_l2_errors += md.wJq[i] * error_at_node .^ 2
24
component_linf_errors = max.(component_linf_errors, abs.(error_at_node))
25
end
26
total_volume = sum(md.wJq)
27
return sqrt.(component_l2_errors ./ total_volume), component_linf_errors
28
end
29
30
function integrate(func::Func, u, mesh::DGMultiMesh,
31
equations, dg::DGMulti, cache; normalize = true) where {Func}
32
rd = dg.basis
33
md = mesh.md
34
(; u_values) = cache.solution_container
35
36
# interpolate u to quadrature points
37
apply_to_each_field(mul_by!(rd.Vq), u_values, u)
38
39
integral = sum(md.wJq .* func.(u_values, equations))
40
if normalize == true
41
integral /= sum(md.wJq)
42
end
43
return integral
44
end
45
46
function analyze(::typeof(entropy_timederivative), du, u, t,
47
mesh::DGMultiMesh, equations, dg::DGMulti, cache)
48
rd = dg.basis
49
md = mesh.md
50
(; u_values) = cache.solution_container
51
52
# interpolate u, du to quadrature points
53
du_values = similar(u_values) # Todo: DGMulti. Can we move this to the analysis cache somehow?
54
apply_to_each_field(mul_by!(rd.Vq), du_values, du)
55
apply_to_each_field(mul_by!(rd.Vq), u_values, u)
56
57
# compute ∫v(u) * du/dt = ∫dS/dt. We can directly compute v(u) instead of computing the entropy
58
# projection here, since the RHS will be projected to polynomials of degree N and testing with
59
# the L2 projection of v(u) would be equivalent to testing with v(u) due to the moment-preserving
60
# property of the L2 projection.
61
dS_dt = zero(eltype(first(du)))
62
for i in Base.OneTo(length(md.wJq))
63
dS_dt += dot(cons2entropy(u_values[i], equations), du_values[i]) * md.wJq[i]
64
end
65
return dS_dt
66
end
67
68
# This function is used in `analyze(::Val{:l2_divb},...)` and `analyze(::Val{:linf_divb},...)`
69
function compute_local_divergence!(local_divergence, element, vector_field,
70
mesh, dg::DGMulti, cache)
71
@unpack md = mesh
72
rd = dg.basis
73
uEltype = eltype(first(vector_field))
74
75
fill!(local_divergence, zero(uEltype))
76
77
# computes dU_i/dx_i = ∑_j dxhat_j/dx_i * dU_i / dxhat_j
78
# dU_i/dx_i is then accumulated into local_divergence.
79
# TODO: DGMulti. Extend to curved elements.
80
for i in eachdim(mesh)
81
for j in eachdim(mesh)
82
geometric_scaling = md.rstxyzJ[i, j][1, element]
83
jth_ref_derivative_matrix = rd.Drst[j]
84
mul!(local_divergence, jth_ref_derivative_matrix, vector_field[i],
85
geometric_scaling, one(uEltype))
86
end
87
end
88
end
89
90
get_component(u::StructArray, i::Int) = StructArrays.component(u, i)
91
get_component(u::AbstractArray{<:SVector}, i::Int) = getindex.(u, i)
92
93
function analyze(::Val{:l2_divb}, du, u, t,
94
mesh::DGMultiMesh, equations::IdealGlmMhdEquations2D,
95
dg::DGMulti, cache)
96
@unpack md = mesh
97
rd = dg.basis
98
B1 = get_component(u, 6)
99
B2 = get_component(u, 7)
100
B = (B1, B2)
101
102
uEltype = eltype(B1)
103
l2norm_divB = zero(uEltype)
104
local_divB = zeros(uEltype, size(B1, 1))
105
for e in eachelement(mesh, dg, cache)
106
compute_local_divergence!(local_divB, e, view.(B, :, e), mesh, dg, cache)
107
108
# TODO: DGMulti. Extend to curved elements.
109
# compute L2 norm squared via J[1, e] * u' * M * u
110
local_l2norm_divB = md.J[1, e] * dot(local_divB, rd.M, local_divB)
111
l2norm_divB += local_l2norm_divB
112
end
113
114
return sqrt(l2norm_divB)
115
end
116
117
function analyze(::Val{:linf_divb}, du, u, t,
118
mesh::DGMultiMesh, equations::IdealGlmMhdEquations2D,
119
dg::DGMulti, cache)
120
B1 = get_component(u, 6)
121
B2 = get_component(u, 7)
122
B = (B1, B2)
123
124
uEltype = eltype(B1)
125
linf_divB = zero(uEltype)
126
local_divB = zeros(uEltype, size(B1, 1))
127
@batch reduction=(max, linf_divb) for e in eachelement(mesh, dg, cache)
128
compute_local_divergence!(local_divB, e, view.(B, :, e), mesh, dg, cache)
129
130
# compute maximum norm
131
linf_divB = max(linf_divB, maximum(abs, local_divB))
132
end
133
134
return linf_divB
135
end
136
137
# Calculate ∫_e (∂S/∂u ⋅ ∂u/∂t) dΩ_e where the result on element 'e' is kept in reference space
138
# Note that ∂S/∂u = w(u) with entropy variables w.
139
# This assumes that both du and u are already interpolated to the quadrature points
140
function entropy_change_reference_element(du_values_local, u_values_local,
141
mesh::DGMultiMesh, equations,
142
dg::DGMulti, cache)
143
rd = dg.basis
144
@unpack Nq, wq = rd
145
146
# Compute entropy change for this element
147
dS_dt_elem = zero(eltype(first(du_values_local)))
148
for i in Base.OneTo(Nq) # Loop over quadrature points in the element
149
dS_dt_elem += dot(cons2entropy(u_values_local[i], equations),
150
du_values_local[i]) * wq[i]
151
end
152
153
return dS_dt_elem
154
end
155
156
# calculate surface integral of func(u, normal_direction, equations) on the reference element.
157
# For DGMulti, we loop over all faces of the element and integrate using face quadrature weights.
158
# Restricted to `Polynomial` approximation type which requires interpolation to face quadrature nodes
159
function surface_integral_reference_element(func::Func, u, element,
160
mesh::DGMultiMesh, equations,
161
dg::DGMulti,
162
cache, args...) where {Func}
163
rd = dg.basis
164
@unpack Nfq, wf, Vf = rd
165
md = mesh.md
166
@unpack nxyzJ = md
167
168
# Interpolate volume solution to face quadrature nodes for this element
169
@unpack u_face_local_threaded = cache
170
u_face_local = u_face_local_threaded[Threads.threadid()]
171
u_elem = view(u, :, element)
172
apply_to_each_field(mul_by!(Vf), u_face_local, u_elem)
173
174
surface_integral = zero(eltype(first(u)))
175
# Loop over all face nodes for this element
176
for i in 1:Nfq
177
# Get solution at this face node
178
u_node = u_face_local[i]
179
180
# Get face normal; nxyzJ stores components as (nxJ, nyJ, nxJ)
181
normal_direction = SVector(getindex.(nxyzJ, i, element))
182
183
# Multiply with face quadrature weight and accumulate
184
surface_integral += wf[i] * func(u_node, normal_direction, equations)
185
end
186
187
return surface_integral
188
end
189
190
function create_cache_analysis(analyzer, mesh::DGMultiMesh,
191
equations, dg::DGMulti, cache,
192
RealT, uEltype)
193
return (;)
194
end
195
196
SolutionAnalyzer(rd::RefElemData) = rd
197
198
nelements(mesh::DGMultiMesh, ::DGMulti, other_args...) = mesh.md.num_elements
199
function nelementsglobal(mesh::DGMultiMesh, solver::DGMulti, cache)
200
if mpi_isparallel()
201
error("`nelementsglobal` is not implemented for `DGMultiMesh` when used in parallel with MPI")
202
else
203
return nelements(mesh, solver)
204
end
205
end
206
function ndofsglobal(mesh::DGMultiMesh, solver::DGMulti, cache)
207
if mpi_isparallel()
208
error("`ndofsglobal` is not implemented for `DGMultiMesh` when used in parallel with MPI")
209
else
210
return ndofs(mesh, solver, cache)
211
end
212
end
213
end # @muladd
214
215