Block-Structured AMR Software Framework
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
AMReX_Conduit_Blueprint_ParticlesI.H
Go to the documentation of this file.
1
2//
3// Template implementation of functions for Conduit Mesh Blueprint Support
4// for AMReX Particle Containers
5//
6// This file is included in AMReX_Conduit_Blueprint.H when
7// when USE_PARTICLES = TRUE
8//
10
11#include <sstream>
12#include <conduit/conduit_blueprint.hpp>
13#include <conduit/conduit_relay.hpp>
14
15namespace amrex
16{
17//---------------------------------------------------------------------------//
18// Converts a AMReX Particle Tile into a Conduit Mesh Blueprint Hierarchy.
19//---------------------------------------------------------------------------//
20// Note:
21// This is a helper function, it's not part of the AMReX Blueprint Interface.
22//---------------------------------------------------------------------------//
23template <typename ParticleType, int NArrayReal, int NArrayInt>
24void
26 NArrayReal,
27 NArrayInt> &ptile,
28 const Vector<std::string> &real_comp_names,
29 const Vector<std::string> &int_comp_names,
30 conduit::Node &res,
31 const std::string &topology_name)
32{
33 int num_particles = ptile.size();
34
35 // knowing the above, we can zero copy the x,y,z positions + id, cpu
36 // and any user fields in the AOS
37
38 // setup a blueprint description for the particle mesh
39 // create a coordinate set
40 std::string coordset_name = topology_name + "_coords";
41 conduit::Node &n_coords = res["coordsets"][coordset_name];
42 n_coords["type"] = "explicit";
43
44 // create an explicit points topology
45 conduit::Node &n_topo = res["topologies"][topology_name];
46 n_topo["coordset"] = coordset_name;
47 n_topo["type"] = "unstructured";
48 n_topo["elements/shape"] = "point";
49 n_topo["elements/connectivity"].set(
50 conduit::DataType::c_int(num_particles));
51 int *conn = n_topo["elements/connectivity"].value();
52
53 for(int i = 0; i < num_particles ; i++)
54 {
55 conn[i] = i;
56 }
57
58 //----------------------------------//
59 // point locations from from aos
60 //----------------------------------//
61 char* pbuf = nullptr;
62
63 if constexpr(ParticleType::is_soa_particle)
64 {
66
67 const auto &soa = ptile.GetStructOfArrays();
68
69 // for soa entries, we can use standard strides,
70 // since these are contiguous arrays
71
72 n_coords["values/x"].set_external(const_cast<ParticleReal*>(soa.GetRealData(0).data()),
73 num_particles);
74#if AMREX_SPACEDIM > 1
75 n_coords["values/y"].set_external(const_cast<ParticleReal*>(soa.GetRealData(1).data()),
76 num_particles);
77#endif
78#if AMREX_SPACEDIM > 2
79 n_coords["values/z"].set_external(const_cast<ParticleReal*>(soa.GetRealData(2).data()),
80 num_particles);
81#endif
82 } else
83 {
84 // get the first particle's struct
85 const auto &pstruct = ptile.GetArrayOfStructs();
86 const int struct_size = sizeof(ParticleType);
87
88 const char* pbuf_const = reinterpret_cast<const char*>(pstruct.data());
89 pbuf = const_cast<char*>(pbuf_const);
90
91 ParticleReal* xp = reinterpret_cast<ParticleReal*>(pbuf); pbuf += sizeof(ParticleReal);
92 n_coords["values/x"].set_external(xp,
93 num_particles,
94 0,
95 struct_size);
96#if AMREX_SPACEDIM > 1
97 ParticleReal* yp = reinterpret_cast<ParticleReal*>(pbuf); pbuf += sizeof(ParticleReal);
98 n_coords["values/y"].set_external(yp,
99 num_particles,
100 0,
101 struct_size);
102#endif
103#if AMREX_SPACEDIM > 2
104 ParticleReal* zp = reinterpret_cast<ParticleReal*>(pbuf); pbuf += sizeof(ParticleReal);
105 n_coords["values/z"].set_external(zp,
106 num_particles,
107 0,
108 struct_size);
109#endif
110 }
111
112 // fields
113 conduit::Node &n_fields = res["fields"];
114
115 // -----------------------------
116 // user defined, real aos fields
117 // -----------------------------
118
119 int vname_real_idx = 0;
120 if constexpr(!ParticleType::is_soa_particle)
121 {
122 constexpr int struct_size = sizeof(ParticleType);
123 constexpr int NStructReal = ParticleType::NReal;
124
125 // struct real fields, the first set are always the particle positions
126 // which we wrap above
127 for (int i = 0; i < NStructReal; i++)
128 {
129 ParticleReal* val = reinterpret_cast<ParticleReal*>(pbuf); pbuf += sizeof(ParticleReal);
130 conduit::Node &n_f = n_fields[real_comp_names.at(vname_real_idx)];
131 n_f["topology"] = topology_name;
132 n_f["association"] = "element";
133 n_f["values"].set_external(val,
134 num_particles,
135 0,
136 struct_size);
137
138 vname_real_idx++;
139 }
140 }
141
142 //----------------------------------//
143 // standard integer fields from aos
144 // (id, cpu)
145 //----------------------------------//
146
147 if constexpr(!ParticleType::is_soa_particle)
148 {
149 const int struct_size = sizeof(ParticleType);
150
151 // id is the first int entry
152 int* id = reinterpret_cast<int*>(pbuf); pbuf += sizeof(int);
153 conduit::Node &n_f_id = n_fields[topology_name + "_id"];
154
155 n_f_id["topology"] = topology_name;
156 n_f_id["association"] = "element";
157 n_f_id["values"].set_external(id,
158 num_particles,
159 0,
160 struct_size);
161
162 // cpu is the second int entry
163 int* cpu = reinterpret_cast<int*>(pbuf); pbuf += sizeof(int);
164 conduit::Node &n_f_cpu = n_fields[topology_name + "_cpu"];
165
166 n_f_cpu["topology"] = topology_name;
167 n_f_cpu["association"] = "element";
168 n_f_cpu["values"].set_external(cpu,
169 num_particles,
170 0,
171 struct_size);
172 } else {
173 const auto &soa = ptile.GetStructOfArrays();
174
175 // for soa entries, we can use standard strides,
176 // since these are contiguous arrays
177
178 conduit::Node &n_f_idcpu = n_fields[topology_name + "_idcpu"];
179
180 n_f_idcpu["topology"] = topology_name;
181 n_f_idcpu["association"] = "element";
182 n_f_idcpu["values"].set_external(const_cast<uint64_t*>(soa.GetIdCPUData().data()),
183 num_particles);
184 }
185
186 // --------------------------------
187 // user defined, integer aos fields
188 // --------------------------------
189
190 int vname_int_idx = 0;
191 if constexpr(!ParticleType::is_soa_particle)
192 {
193 constexpr int struct_size = sizeof(ParticleType);
194 constexpr int NStructInt = ParticleType::NInt;
195
196 for (int i = 0; i < NStructInt; i++)
197 {
198 int* val = reinterpret_cast<int*>(pbuf); pbuf += sizeof(int);
199 conduit::Node &n_f = n_fields[int_comp_names.at(vname_int_idx)];
200 n_f["topology"] = topology_name;
201 n_f["association"] = "element";
202 n_f["values"].set_external(val,
203 num_particles,
204 0,
205 struct_size);
206 vname_int_idx++;
207 }
208 }
209
210 // -------------------------
211 // user defined soa fields
212 // -------------------------
213
214 const auto &soa = ptile.GetStructOfArrays();
215
216 // for soa entries, we can use standard strides,
217 // since these are contiguous arrays
218
219 // array real fields
220 for (int i = 0; i < NArrayReal; i++)
221 {
222 conduit::Node &n_f = n_fields[real_comp_names.at(vname_real_idx)];
223 n_f["topology"] = topology_name;
224 n_f["association"] = "element";
225 n_f["values"].set_external(const_cast<ParticleReal*>(soa.GetRealData(i).data()),
226 num_particles);
227
228 vname_real_idx++;
229 }
230
231 // array int fields
232 for (int i = 0; i < NArrayInt; i++)
233 {
234 conduit::Node &n_f = n_fields[int_comp_names.at(vname_int_idx)];
235 n_f["topology"] = topology_name;
236 n_f["association"] = "element";
237 n_f["values"].set_external(const_cast<int*>(soa.GetIntData(i).data()),
238 num_particles);
239
240 vname_int_idx++;
241 }
242}
243
244//---------------------------------------------------------------------------//
245// Converts a AMReX Particle Container into a Conduit Mesh Blueprint Hierarchy.
246//---------------------------------------------------------------------------//
247template <typename ParticleType, int NArrayReal, int NArrayInt>
248void
250 NArrayReal,
251 NArrayInt> &pc,
252 const Vector<std::string> &real_comp_names,
253 const Vector<std::string> &int_comp_names,
254 conduit::Node &res,
255 const std::string &topology_name)
256{
257 BL_PROFILE("ParticleContainerToBlueprint()");
258
259 // validate varnames, which are used to provide field names
260 // for user defined aos and soa values.
261
262 if constexpr(ParticleType::is_soa_particle) {
263 BL_ASSERT(real_comp_names.size() == NArrayReal);
264 BL_ASSERT(int_comp_names.size() == NArrayInt);
265 } else {
266 BL_ASSERT(real_comp_names.size() == (ParticleType::NReal + NArrayReal) );
267 BL_ASSERT(int_comp_names.size() == (ParticleType::NInt + NArrayInt) );
268 }
269
270 int num_levels = pc.maxLevel() + 1;
271 int num_domains = 0;
272
273 // get global domains already present in node
274 long domain_offset = (long)res.number_of_children();
276
277 // loop over levels
278
279 int rank = ParallelDescriptor::MyProc();
280 int nprocs = ParallelDescriptor::NProcs();
281
283
284 //
285 // blueprint expects unique ids for each domain published
286 // for particle data, this means we need a unique id for each
287 // tile across levels.
288 //
289 // to generate unique domain ids, we use a counting loop to
290 // calculate a per-level, per-rank offset
291 //
292
293 // this holds our unique offset for each level for the current rank
294 std::vector<int> my_lvl_offsets;
295 my_lvl_offsets.resize(num_levels);
296
297 // these hold the values we need to do a scan to calc offsets
298 std::vector<int> within_lvl_counts;
299 std::vector<int> within_lvl_offsets;
300 within_lvl_counts.resize(nprocs);
301 within_lvl_offsets.resize(nprocs);
302
303 int total_num_domains = 0;
304 for (int lev = 0; lev < num_levels; ++lev)
305 {
306 // count how many tiles this mpi task has
307 long level_num_domains = 0;
308 for (MyParConstIter pti(pc, lev); pti.isValid(); ++pti)
309 {
310 level_num_domains+=1;
311 }
312
313 // clear the within_lvl_counts and offsets data
314 for(int rank_idx = 0; rank_idx < nprocs ; rank_idx++)
315 {
316 within_lvl_counts[rank_idx] = 0;
317 within_lvl_offsets[rank_idx] = 0;
318 }
319
320 // set number of domains for current rank at current level
321 within_lvl_counts[rank] = level_num_domains;
322 // mpi sum reduce to get all rank's counts for this level
323 ParallelDescriptor::ReduceIntSum(&within_lvl_counts[0],
324 nprocs);
325
326 long level_total_num_domains = 0;
327 // scan to calc offset and total number of domains
328 for(int rank_idx = 0; rank_idx < nprocs ; rank_idx++)
329 {
330 within_lvl_offsets[rank_idx] = level_total_num_domains;
331 level_total_num_domains += within_lvl_counts[rank_idx];
332 }
333
334 // the offset for this rank at this level is the current
335 // total number of domains + the offset for this level
336 my_lvl_offsets[lev] = within_lvl_offsets[rank] + total_num_domains;
337
338 total_num_domains += level_num_domains;
339 }
340
341 for (int lev = 0; lev < num_levels; ++lev)
342 {
343 // get our unique global offset for this rank at this level
344 int lvl_offset = my_lvl_offsets[lev];
345 // keep a counter of local # of tiles
346 int num_lvl_local_tiles = 0;
347 for (MyParConstIter pti(pc, lev); pti.isValid(); ++pti)
348 {
349 // this will give us a unique id across levels for each tile
350 int domain_id = lvl_offset + num_lvl_local_tiles + domain_offset;
351
352 const auto& ptile = pti.GetParticleTile();
353 const std::string& patch_name = amrex::Concatenate("domain_",
354 domain_id,
355 6);
356 conduit::Node &patch = res[patch_name];
357 // add basic state info
358 patch["state/domain_id"] = domain_id;
360 real_comp_names,
361 int_comp_names,
362 patch,
363 topology_name);
364 num_lvl_local_tiles += 1;
365 }
366 }
367
368 conduit::Node info;
369 // blueprint verify makes sure we conform to what's expected
370 // for a multi-domain mesh
371 if(!conduit::blueprint::mesh::verify(res,info))
372 {
373 // ERROR -- doesn't conform to the mesh blueprint
374 // show what went wrong
375 amrex::Print() << "ERROR: Conduit Mesh Blueprint Verify Failed!\n"
376 << info.to_yaml();
377 }
378}
379
380
381
382}
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:551
#define BL_ASSERT(EX)
Definition AMReX_BLassert.H:39
Definition AMReX_ParIter.H:142
A distributed container for Particles sorted onto the levels, grids, and tiles of a block-structured ...
Definition AMReX_ParticleContainer.H:146
This class provides the user with a few print options.
Definition AMReX_Print.H:35
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:27
Long size() const noexcept
Definition AMReX_Vector.H:50
void ReduceIntSum(int &)
Integer sum reduction.
Definition AMReX_ParallelDescriptor.cpp:1255
int MyProc() noexcept
return the rank number local to the current Parallel Context
Definition AMReX_ParallelDescriptor.H:125
void ReduceLongSum(Long &)
Long sum reduction.
Definition AMReX_ParallelDescriptor.cpp:1226
int NProcs() noexcept
return the number of MPI ranks local to the current Parallel Context
Definition AMReX_ParallelDescriptor.H:243
Definition AMReX_Amr.cpp:49
std::string Concatenate(const std::string &root, int num, int mindigits)
Returns rootNNNN where NNNN == num.
Definition AMReX_String.cpp:34
void ParticleTileToBlueprint(const ParticleTile< ParticleType, NArrayReal, NArrayInt > &ptile, const Vector< std::string > &real_comp_names, const Vector< std::string > &int_comp_names, conduit::Node &res, const std::string &topology_name)
Definition AMReX_Conduit_Blueprint_ParticlesI.H:25
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void ignore_unused(const Ts &...)
This shuts up the compiler about unused variables.
Definition AMReX.H:127
const int[]
Definition AMReX_BLProfiler.cpp:1664
void ParticleContainerToBlueprint(const ParticleContainer_impl< ParticleType, NArrayReal, NArrayInt > &pc, const Vector< std::string > &real_comp_names, const Vector< std::string > &int_comp_names, conduit::Node &res, const std::string &topology_name)
Definition AMReX_Conduit_Blueprint_ParticlesI.H:249
Definition AMReX_ParticleTile.H:702