Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/solvers/dgsem_p4est/dg_2d_parallel.jl
5616 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 prolong2mpiinterfaces!(cache, u,
9
mesh::Union{P4estMeshParallel{2},
10
T8codeMeshParallel{2}},
11
equations, surface_integral, dg::DG)
12
@unpack mpi_interfaces = cache
13
index_range = eachnode(dg)
14
15
@threaded for interface in eachmpiinterface(dg, cache)
16
# Copy solution data from the local element using "delayed indexing" with
17
# a start value and a step size to get the correct face and orientation.
18
# Note that in the current implementation, the interface will be
19
# "aligned at the primary element", i.e., the index of the primary side
20
# will always run forwards.
21
local_side = mpi_interfaces.local_sides[interface]
22
local_element = mpi_interfaces.local_neighbor_ids[interface]
23
local_indices = mpi_interfaces.node_indices[interface]
24
25
i_element_start, i_element_step = index_to_start_step_2d(local_indices[1],
26
index_range)
27
j_element_start, j_element_step = index_to_start_step_2d(local_indices[2],
28
index_range)
29
30
i_element = i_element_start
31
j_element = j_element_start
32
for i in eachnode(dg)
33
for v in eachvariable(equations)
34
mpi_interfaces.u[local_side, v, i, interface] = u[v, i_element,
35
j_element,
36
local_element]
37
end
38
i_element += i_element_step
39
j_element += j_element_step
40
end
41
end
42
43
return nothing
44
end
45
46
function calc_mpi_interface_flux!(surface_flux_values,
47
mesh::Union{P4estMeshParallel{2},
48
T8codeMeshParallel{2}},
49
have_nonconservative_terms,
50
equations, surface_integral, dg::DG, cache)
51
@unpack local_neighbor_ids, node_indices, local_sides = cache.mpi_interfaces
52
@unpack contravariant_vectors = cache.elements
53
index_range = eachnode(dg)
54
index_end = last(index_range)
55
56
@threaded for interface in eachmpiinterface(dg, cache)
57
# Get element and side index information on the local element
58
local_element = local_neighbor_ids[interface]
59
local_indices = node_indices[interface]
60
local_direction = indices2direction(local_indices)
61
local_side = local_sides[interface]
62
63
# Create the local i,j indexing on the local element used to pull normal direction information
64
i_element_start, i_element_step = index_to_start_step_2d(local_indices[1],
65
index_range)
66
j_element_start, j_element_step = index_to_start_step_2d(local_indices[2],
67
index_range)
68
69
i_element = i_element_start
70
j_element = j_element_start
71
72
# Initiate the node index to be used in the surface for loop,
73
# the surface flux storage must be indexed in alignment with the local element indexing
74
if :i_backward in local_indices
75
surface_node = index_end
76
surface_node_step = -1
77
else
78
surface_node = 1
79
surface_node_step = 1
80
end
81
82
for node in eachnode(dg)
83
# Get the normal direction on the local element
84
# Contravariant vectors at interfaces in negative coordinate direction
85
# are pointing inwards. This is handled by `get_normal_direction`.
86
normal_direction = get_normal_direction(local_direction,
87
contravariant_vectors,
88
i_element, j_element, local_element)
89
90
calc_mpi_interface_flux!(surface_flux_values, mesh,
91
have_nonconservative_terms,
92
equations,
93
surface_integral, dg, cache,
94
interface, normal_direction,
95
node, local_side,
96
surface_node, local_direction, local_element)
97
98
# Increment local element indices to pull the normal direction
99
i_element += i_element_step
100
j_element += j_element_step
101
102
# Increment the surface node index along the local element
103
surface_node += surface_node_step
104
end
105
end
106
107
return nothing
108
end
109
110
# Inlined version of the interface flux computation for conservation laws
111
@inline function calc_mpi_interface_flux!(surface_flux_values,
112
mesh::Union{P4estMeshParallel{2},
113
T8codeMeshParallel{2}},
114
have_nonconservative_terms::False, equations,
115
surface_integral, dg::DG, cache,
116
interface_index, normal_direction,
117
interface_node_index, local_side,
118
surface_node_index, local_direction_index,
119
local_element_index)
120
@unpack u = cache.mpi_interfaces
121
@unpack surface_flux = surface_integral
122
123
u_ll, u_rr = get_surface_node_vars(u, equations, dg, interface_node_index,
124
interface_index)
125
126
if local_side == 1
127
flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)
128
else # local_side == 2
129
flux_ = -surface_flux(u_ll, u_rr, -normal_direction, equations)
130
end
131
132
for v in eachvariable(equations)
133
surface_flux_values[v, surface_node_index, local_direction_index, local_element_index] = flux_[v]
134
end
135
136
return nothing
137
end
138
139
# Inlined version of the interface flux computation for non-conservative equations
140
@inline function calc_mpi_interface_flux!(surface_flux_values,
141
mesh::Union{P4estMeshParallel{2},
142
T8codeMeshParallel{2}},
143
have_nonconservative_terms::True, equations,
144
surface_integral, dg::DG, cache,
145
interface_index, normal_direction,
146
interface_node_index, local_side,
147
surface_node_index, local_direction_index,
148
local_element_index)
149
@unpack u = cache.mpi_interfaces
150
surface_flux, nonconservative_flux = surface_integral.surface_flux
151
152
u_ll, u_rr = get_surface_node_vars(u, equations, dg,
153
interface_node_index,
154
interface_index)
155
156
# Compute flux and non-conservative term for this side of the interface
157
if local_side == 1
158
flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)
159
noncons_flux_ = nonconservative_flux(u_ll, u_rr, normal_direction, equations)
160
else # local_side == 2
161
flux_ = -surface_flux(u_ll, u_rr, -normal_direction, equations)
162
noncons_flux_ = -nonconservative_flux(u_rr, u_ll, -normal_direction, equations)
163
end
164
165
for v in eachvariable(equations)
166
surface_flux_values[v, surface_node_index,
167
local_direction_index, local_element_index] = flux_[v] +
168
0.5f0 * noncons_flux_[v]
169
end
170
171
return nothing
172
end
173
174
function prolong2mpimortars!(cache, u,
175
mesh::Union{P4estMeshParallel{2}, T8codeMeshParallel{2}},
176
equations,
177
mortar_l2::LobattoLegendreMortarL2,
178
dg::DGSEM)
179
@unpack node_indices = cache.mpi_mortars
180
index_range = eachnode(dg)
181
182
@threaded for mortar in eachmpimortar(dg, cache)
183
local_neighbor_ids = cache.mpi_mortars.local_neighbor_ids[mortar]
184
local_neighbor_positions = cache.mpi_mortars.local_neighbor_positions[mortar]
185
186
# Get start value and step size for indices on both sides to get the correct face
187
# and orientation
188
small_indices = node_indices[1, mortar]
189
i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],
190
index_range)
191
j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],
192
index_range)
193
194
large_indices = node_indices[2, mortar]
195
i_large_start, i_large_step = index_to_start_step_2d(large_indices[1],
196
index_range)
197
j_large_start, j_large_step = index_to_start_step_2d(large_indices[2],
198
index_range)
199
200
for (element, position) in zip(local_neighbor_ids, local_neighbor_positions)
201
if position == 3 # -> large element
202
# Buffer to copy solution values of the large element in the correct orientation
203
# before interpolating
204
u_buffer = cache.u_threaded[Threads.threadid()]
205
i_large = i_large_start
206
j_large = j_large_start
207
for i in eachnode(dg)
208
for v in eachvariable(equations)
209
u_buffer[v, i] = u[v, i_large, j_large, element]
210
end
211
212
i_large += i_large_step
213
j_large += j_large_step
214
end
215
216
# Interpolate large element face data from buffer to small face locations
217
multiply_dimensionwise!(view(cache.mpi_mortars.u, 2, :, 1, :, mortar),
218
mortar_l2.forward_lower, u_buffer)
219
multiply_dimensionwise!(view(cache.mpi_mortars.u, 2, :, 2, :, mortar),
220
mortar_l2.forward_upper, u_buffer)
221
else # position in (1, 2) -> small element
222
# Copy solution data from the small elements
223
i_small = i_small_start
224
j_small = j_small_start
225
for i in eachnode(dg)
226
for v in eachvariable(equations)
227
cache.mpi_mortars.u[1, v, position, i, mortar] = u[v, i_small,
228
j_small,
229
element]
230
end
231
i_small += i_small_step
232
j_small += j_small_step
233
end
234
end
235
end
236
end
237
238
return nothing
239
end
240
241
function calc_mpi_mortar_flux!(surface_flux_values,
242
mesh::Union{P4estMeshParallel{2}, T8codeMeshParallel{2}},
243
have_nonconservative_terms, equations,
244
mortar_l2::LobattoLegendreMortarL2,
245
surface_integral, dg::DG, cache)
246
@unpack local_neighbor_ids, local_neighbor_positions, node_indices = cache.mpi_mortars
247
@unpack contravariant_vectors = cache.elements
248
@unpack fstar_primary_upper_threaded, fstar_primary_lower_threaded = cache
249
@unpack fstar_secondary_upper_threaded, fstar_secondary_lower_threaded = cache
250
index_range = eachnode(dg)
251
252
@threaded for mortar in eachmpimortar(dg, cache)
253
# Choose thread-specific pre-allocated container
254
fstar_primary = (fstar_primary_lower_threaded[Threads.threadid()],
255
fstar_primary_upper_threaded[Threads.threadid()])
256
fstar_secondary = (fstar_secondary_lower_threaded[Threads.threadid()],
257
fstar_secondary_upper_threaded[Threads.threadid()])
258
259
# Get index information on the small elements
260
small_indices = node_indices[1, mortar]
261
262
i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],
263
index_range)
264
j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],
265
index_range)
266
267
for position in 1:2
268
i_small = i_small_start
269
j_small = j_small_start
270
for node in eachnode(dg)
271
# Get the normal direction on the small element.
272
normal_direction = get_normal_direction(cache.mpi_mortars, node,
273
position, mortar)
274
275
calc_mpi_mortar_flux!(fstar_primary, fstar_secondary, mesh,
276
have_nonconservative_terms, equations,
277
surface_integral, dg, cache,
278
mortar, position, normal_direction, node)
279
280
i_small += i_small_step
281
j_small += j_small_step
282
end
283
end
284
285
# Buffer to interpolate flux values of the large element to before
286
# copying in the correct orientation
287
u_buffer = cache.u_threaded[Threads.threadid()]
288
289
mpi_mortar_fluxes_to_elements!(surface_flux_values,
290
mesh, equations, mortar_l2, dg, cache,
291
mortar, fstar_primary, fstar_secondary, u_buffer)
292
end
293
294
return nothing
295
end
296
297
# Inlined version of the mortar flux computation on small elements for conservation laws
298
@inline function calc_mpi_mortar_flux!(fstar_primary, fstar_secondary,
299
mesh::Union{P4estMeshParallel{2},
300
T8codeMeshParallel{2}},
301
have_nonconservative_terms::False, equations,
302
surface_integral, dg::DG, cache,
303
mortar_index, position_index, normal_direction,
304
node_index)
305
@unpack u = cache.mpi_mortars
306
@unpack surface_flux = surface_integral
307
308
u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index, node_index,
309
mortar_index)
310
311
flux = surface_flux(u_ll, u_rr, normal_direction, equations)
312
313
# Copy flux to buffer
314
set_node_vars!(fstar_primary[position_index], flux, equations, dg, node_index)
315
set_node_vars!(fstar_secondary[position_index], flux, equations, dg, node_index)
316
317
return nothing
318
end
319
320
# Inlined version of the mortar flux computation on small elements for non-conservative equations
321
@inline function calc_mpi_mortar_flux!(fstar_primary, fstar_secondary,
322
mesh::Union{P4estMeshParallel{2},
323
T8codeMeshParallel{2}},
324
have_nonconservative_terms::True, equations,
325
surface_integral, dg::DG, cache,
326
mortar_index, position_index, normal_direction,
327
node_index)
328
@unpack u = cache.mpi_mortars
329
surface_flux, nonconservative_flux = surface_integral.surface_flux
330
331
u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index, node_index,
332
mortar_index)
333
334
flux = surface_flux(u_ll, u_rr, normal_direction, equations)
335
noncons_flux_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)
336
noncons_flux_secondary = nonconservative_flux(u_rr, u_ll, normal_direction,
337
equations)
338
339
for v in eachvariable(equations)
340
fstar_primary[position_index][v, node_index] = flux[v] +
341
0.5f0 * noncons_flux_primary[v]
342
fstar_secondary[position_index][v, node_index] = flux[v] +
343
0.5f0 *
344
noncons_flux_secondary[v]
345
end
346
347
return nothing
348
end
349
350
@inline function mpi_mortar_fluxes_to_elements!(surface_flux_values,
351
mesh::Union{P4estMeshParallel{2},
352
T8codeMeshParallel{2}},
353
equations,
354
mortar_l2::LobattoLegendreMortarL2,
355
dg::DGSEM, cache, mortar, fstar_primary,
356
fstar_secondary,
357
u_buffer)
358
@unpack local_neighbor_ids, local_neighbor_positions, node_indices = cache.mpi_mortars
359
360
small_indices = node_indices[1, mortar]
361
small_direction = indices2direction(small_indices)
362
large_indices = node_indices[2, mortar]
363
large_direction = indices2direction(large_indices)
364
365
for (element, position) in zip(local_neighbor_ids[mortar],
366
local_neighbor_positions[mortar])
367
if position == 3 # -> large element
368
# Project small fluxes to large element.
369
multiply_dimensionwise!(u_buffer,
370
mortar_l2.reverse_upper, fstar_secondary[2],
371
mortar_l2.reverse_lower, fstar_secondary[1])
372
# The flux is calculated in the outward direction of the small elements,
373
# so the sign must be switched to get the flux in outward direction
374
# of the large element.
375
# The contravariant vectors of the large element (and therefore the normal
376
# vectors of the large element as well) are twice as large as the
377
# contravariant vectors of the small elements. Therefore, the flux needs
378
# to be scaled by a factor of 2 to obtain the flux of the large element.
379
u_buffer .*= -2
380
# Copy interpolated flux values from buffer to large element face in the
381
# correct orientation.
382
# Note that the index of the small sides will always run forward but
383
# the index of the large side might need to run backwards for flipped sides.
384
if :i_backward in large_indices
385
for i in eachnode(dg)
386
for v in eachvariable(equations)
387
surface_flux_values[v, end + 1 - i, large_direction, element] = u_buffer[v,
388
i]
389
end
390
end
391
else
392
for i in eachnode(dg)
393
for v in eachvariable(equations)
394
surface_flux_values[v, i, large_direction, element] = u_buffer[v,
395
i]
396
end
397
end
398
end
399
else # position in (1, 2) -> small element
400
# Copy solution small to small
401
for i in eachnode(dg)
402
for v in eachvariable(equations)
403
surface_flux_values[v, i, small_direction, element] = fstar_primary[position][v,
404
i]
405
end
406
end
407
end
408
end
409
410
return nothing
411
end
412
end # muladd
413
414