Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/auxiliary/p4est.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
init_p4est()
10
11
Initialize `p4est` by calling `p4est_init` and setting the log level to `SC_LP_ERROR`.
12
This function will check if `p4est` is already initialized
13
and if yes, do nothing, thus it is safe to call it multiple times.
14
"""
15
function init_p4est()
16
# Only initialize p4est if P4est.jl can be used
17
if P4est.preferences_set_correctly()
18
p4est_package_id = P4est.package_id()
19
if p4est_package_id >= 0
20
return nothing
21
end
22
23
# Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations
24
p4est_init(C_NULL, SC_LP_ERROR)
25
else
26
@warn "Preferences for P4est.jl are not set correctly. Until fixed, using `P4estMesh` will result in a crash. " *
27
"See also https://trixi-framework.github.io/TrixiDocumentation/stable/parallelization/#parallel_system_MPI"
28
end
29
30
return nothing
31
end
32
33
# for convenience to either pass a Ptr or a PointerWrapper
34
const PointerOrWrapper = Union{Ptr{T}, PointerWrapper{T}} where {T}
35
36
# Convert sc_array of type T to Julia array
37
function unsafe_wrap_sc(::Type{T}, sc_array_ptr::Ptr{sc_array}) where {T}
38
sc_array_obj = unsafe_load(sc_array_ptr)
39
return unsafe_wrap_sc(T, sc_array_obj)
40
end
41
42
function unsafe_wrap_sc(::Type{T}, sc_array_obj::sc_array) where {T}
43
elem_count = sc_array_obj.elem_count
44
array = sc_array_obj.array
45
return unsafe_wrap(Array, Ptr{T}(array), elem_count)
46
end
47
48
function unsafe_wrap_sc(::Type{T}, sc_array_pw::PointerWrapper{sc_array}) where {T}
49
elem_count = sc_array_pw.elem_count[]
50
array = sc_array_pw.array
51
52
return unsafe_wrap(Array, Ptr{T}(pointer(array)), elem_count)
53
end
54
55
# Load the ith element (1-indexed) of an sc array of type T as PointerWrapper
56
function load_pointerwrapper_sc(::Type{T}, sc_array::PointerWrapper{sc_array},
57
i::Integer = 1) where {T}
58
return PointerWrapper(T, pointer(sc_array.array) + (i - 1) * sizeof(T))
59
end
60
61
function unsafe_load_sc(::Type{T}, sc_array::PointerWrapper{sc_array},
62
i::Integer = 1) where {T}
63
return unsafe_load(Ptr{T}(pointer(sc_array.array)), i)
64
end
65
66
function unsafe_store_sc!(sc_array::PointerWrapper{sc_array}, x::T,
67
i::Integer = 1) where {T}
68
return unsafe_store!(Ptr{T}(pointer(sc_array.array)), x, i)
69
end
70
71
# Create new `p4est` from a p4est_connectivity
72
# 2D
73
function new_p4est(connectivity::PointerOrWrapper{p4est_connectivity_t},
74
initial_refinement_level)
75
comm = P4est.uses_mpi() ? mpi_comm() : 0 # Use Trixi.jl's MPI communicator if p4est supports MPI
76
p4est_new_ext(comm,
77
connectivity,
78
0, # No minimum initial qudrants per processor
79
initial_refinement_level,
80
true, # Refine uniformly
81
2 * sizeof(Int), # Use Int-Vector of size 2 as quadrant user data
82
C_NULL, # No init function
83
C_NULL) # No user pointer
84
end
85
86
# 3D
87
function new_p4est(connectivity::PointerOrWrapper{p8est_connectivity_t},
88
initial_refinement_level)
89
comm = P4est.uses_mpi() ? mpi_comm() : 0 # Use Trixi.jl's MPI communicator if p4est supports MPI
90
p8est_new_ext(comm, connectivity, 0, initial_refinement_level, true,
91
2 * sizeof(Int), C_NULL, C_NULL)
92
end
93
94
# Save `p4est` data to file
95
# 2D
96
function save_p4est!(file, p4est::PointerOrWrapper{p4est_t})
97
# Don't save user data of the quads
98
p4est_save(file, p4est, false)
99
end
100
101
# 3D
102
function save_p4est!(file, p8est::PointerOrWrapper{p8est_t})
103
# Don't save user data of the quads
104
p8est_save(file, p8est, false)
105
end
106
107
# Load `p4est` from file
108
# 2D
109
function load_p4est(file, ::Val{2})
110
conn_vec = Vector{Ptr{p4est_connectivity_t}}(undef, 1)
111
comm = P4est.uses_mpi() ? mpi_comm() : C_NULL # Use Trixi.jl's MPI communicator if p4est supports MPI
112
p4est = p4est_load_ext(file,
113
comm,
114
0, # Size of user data
115
0, # Flag to load user data
116
1, # Autopartition: ignore saved partition
117
0, # Have only rank 0 read headers and bcast them
118
C_NULL, # No pointer to user data
119
pointer(conn_vec))
120
# p4est_load_ext only allocates memory when also data is read
121
# use p4est_reset_data to allocate uninitialized memory
122
p4est_reset_data(p4est,
123
2 * sizeof(Int), # Use Int-Vector of size 2 as quadrant user data
124
C_NULL, # No init function
125
C_NULL) # No pointer to user data
126
return p4est
127
end
128
129
# 3D
130
function load_p4est(file, ::Val{3})
131
conn_vec = Vector{Ptr{p8est_connectivity_t}}(undef, 1)
132
comm = P4est.uses_mpi() ? mpi_comm() : C_NULL # Use Trixi.jl's MPI communicator if p4est supports MPI
133
p4est = p8est_load_ext(file, comm, 0, 0, 1, 0, C_NULL, pointer(conn_vec))
134
p8est_reset_data(p4est, 2 * sizeof(Int), C_NULL, C_NULL)
135
return p4est
136
end
137
138
# Read `p4est` connectivity from Abaqus mesh file (.inp)
139
# 2D
140
read_inp_p4est(meshfile, ::Val{2}) = p4est_connectivity_read_inp(meshfile)
141
# 3D
142
read_inp_p4est(meshfile, ::Val{3}) = p8est_connectivity_read_inp(meshfile)
143
144
# Refine `p4est` if refine_fn_c returns 1
145
# 2D
146
function refine_p4est!(p4est::PointerOrWrapper{p4est_t}, recursive, refine_fn_c,
147
init_fn_c)
148
p4est_refine(p4est, recursive, refine_fn_c, init_fn_c)
149
end
150
# 3D
151
function refine_p4est!(p8est::PointerOrWrapper{p8est_t}, recursive, refine_fn_c,
152
init_fn_c)
153
p8est_refine(p8est, recursive, refine_fn_c, init_fn_c)
154
end
155
156
# Refine `p4est` if coarsen_fn_c returns 1
157
# 2D
158
function coarsen_p4est!(p4est::PointerOrWrapper{p4est_t}, recursive, coarsen_fn_c,
159
init_fn_c)
160
p4est_coarsen(p4est, recursive, coarsen_fn_c, init_fn_c)
161
end
162
# 3D
163
function coarsen_p4est!(p8est::PointerOrWrapper{p8est_t}, recursive, coarsen_fn_c,
164
init_fn_c)
165
p8est_coarsen(p8est, recursive, coarsen_fn_c, init_fn_c)
166
end
167
168
# Create new ghost layer from p4est, only connections via faces are relevant
169
# 2D
170
function ghost_new_p4est(p4est::PointerOrWrapper{p4est_t})
171
p4est_ghost_new(p4est, P4est.P4EST_CONNECT_FACE)
172
end
173
# 3D
174
# In 3D it is not sufficient to use `P8EST_CONNECT_FACE`. Consider the neighbor elements of a mortar
175
# in 3D. We have to determine which MPI ranks are involved in this mortar.
176
# ┌─────────────┬─────────────┐ ┌───────────────────────────┐
177
# │ │ │ │ │
178
# │ small │ small │ │ │
179
# │ 3 │ 4 │ │ │
180
# │ │ │ │ large │
181
# ├─────────────┼─────────────┤ │ 5 │
182
# │ │ │ │ │
183
# │ small │ small │ │ │
184
# │ 1 │ 2 │ │ │
185
# │ │ │ │ │
186
# └─────────────┴─────────────┘ └───────────────────────────┘
187
# Suppose one process only owns element 1. Since element 4 is not connected to element 1 via a face,
188
# there is no guarantee that element 4 will be in the ghost layer, if it is constructed with
189
# `P8EST_CONNECT_FACE`. But if it is not in the ghost layer, it will not be available in
190
# `iterate_p4est` and thus we cannot determine its MPI rank
191
# (see https://github.com/cburstedde/p4est/blob/439bc9aae849555256ddfe4b03d1f9fe8d18ff0e/src/p8est_iterate.h#L66-L72).
192
function ghost_new_p4est(p8est::PointerOrWrapper{p8est_t})
193
p8est_ghost_new(p8est, P4est.P8EST_CONNECT_FULL)
194
end
195
196
# Check if ghost layer is valid
197
# 2D
198
function ghost_is_valid_p4est(p4est::PointerOrWrapper{p4est_t},
199
ghost_layer::Ptr{p4est_ghost_t})
200
return p4est_ghost_is_valid(p4est, ghost_layer)
201
end
202
# 3D
203
function ghost_is_valid_p4est(p4est::PointerOrWrapper{p8est_t},
204
ghost_layer::Ptr{p8est_ghost_t})
205
return p8est_ghost_is_valid(p4est, ghost_layer)
206
end
207
208
# Destroy ghost layer
209
# 2D
210
function ghost_destroy_p4est(ghost_layer::PointerOrWrapper{p4est_ghost_t})
211
p4est_ghost_destroy(ghost_layer)
212
end
213
# 3D
214
function ghost_destroy_p4est(ghost_layer::PointerOrWrapper{p8est_ghost_t})
215
p8est_ghost_destroy(ghost_layer)
216
end
217
218
# Let `p4est` iterate over each cell volume and cell face.
219
# Call iter_volume_c for each cell and iter_face_c for each face.
220
# 2D
221
function iterate_p4est(p4est::PointerOrWrapper{p4est_t}, user_data;
222
ghost_layer = C_NULL,
223
iter_volume_c = C_NULL, iter_face_c = C_NULL)
224
if user_data === C_NULL
225
user_data_ptr = user_data
226
elseif user_data isa AbstractArray
227
user_data_ptr = pointer(user_data)
228
else
229
user_data_ptr = pointer_from_objref(user_data)
230
end
231
232
GC.@preserve user_data begin
233
p4est_iterate(p4est,
234
ghost_layer,
235
user_data_ptr,
236
iter_volume_c, # iter_volume
237
iter_face_c, # iter_face
238
C_NULL) # iter_corner
239
end
240
241
return nothing
242
end
243
244
# 3D
245
function iterate_p4est(p8est::PointerOrWrapper{p8est_t}, user_data;
246
ghost_layer = C_NULL,
247
iter_volume_c = C_NULL, iter_face_c = C_NULL)
248
if user_data === C_NULL
249
user_data_ptr = user_data
250
elseif user_data isa AbstractArray
251
user_data_ptr = pointer(user_data)
252
else
253
user_data_ptr = pointer_from_objref(user_data)
254
end
255
256
GC.@preserve user_data begin
257
p8est_iterate(p8est,
258
ghost_layer,
259
user_data_ptr,
260
iter_volume_c, # iter_volume
261
iter_face_c, # iter_face
262
C_NULL, # iter_edge
263
C_NULL) # iter_corner
264
end
265
266
return nothing
267
end
268
269
# Load i-th element of the sc_array info.sides of the type p[48]est_iter_face_side_t
270
# 2D version
271
function load_pointerwrapper_side(info::PointerWrapper{p4est_iter_face_info_t},
272
i::Integer = 1)
273
return load_pointerwrapper_sc(p4est_iter_face_side_t, info.sides, i)
274
end
275
276
# 3D version
277
function load_pointerwrapper_side(info::PointerWrapper{p8est_iter_face_info_t},
278
i::Integer = 1)
279
return load_pointerwrapper_sc(p8est_iter_face_side_t, info.sides, i)
280
end
281
282
# Load i-th element of the sc_array p4est.trees of the type p[48]est_tree_t
283
# 2D version
284
function load_pointerwrapper_tree(p4est::PointerWrapper{p4est_t}, i::Integer = 1)
285
return load_pointerwrapper_sc(p4est_tree_t, p4est.trees, i)
286
end
287
288
# 3D version
289
function load_pointerwrapper_tree(p8est::PointerWrapper{p8est_t}, i::Integer = 1)
290
return load_pointerwrapper_sc(p8est_tree_t, p8est.trees, i)
291
end
292
end # @muladd
293
294