Skip to content

Commit 9e35dc1

Browse files
ax3latmyers
andauthored
Ascent: SoA Particle Support (#3350)
## Summary Add support for pure SoA layouted particle containers for Ascent. ## Additional background Follow-up to #2878. ## Checklist The proposed changes: - [ ] fix a bug or incorrect behavior in AMReX - [x] add new capabilities to AMReX - [ ] changes answers in the test suite to more than roundoff level - [ ] are likely to significantly affect the results of downstream AMReX users - [ ] include documentation in the code and/or rst files, if appropriate --------- Co-authored-by: Andrew Myers <[email protected]>
1 parent c51e0a3 commit 9e35dc1

File tree

6 files changed

+491
-74
lines changed

6 files changed

+491
-74
lines changed

Src/Extern/Conduit/AMReX_Conduit_Blueprint.H

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,8 @@ namespace amrex
9696
// coordset and fields used to represent the passed particle container.
9797
// This allows you to use unique names to wrap multiple particle containers
9898
// into a single blueprint tree.
99-
template <int NStructReal, int NStructInt, int NArrayReal, int NArrayInt>
100-
void ParticleContainerToBlueprint (const ParticleContainer<NStructReal,
101-
NStructInt,
99+
template <typename ParticleType, int NArrayReal, int NArrayInt>
100+
void ParticleContainerToBlueprint (const ParticleContainer_impl<ParticleType,
102101
NArrayReal,
103102
NArrayInt> &pc,
104103
const Vector<std::string> &real_comp_names,

Src/Extern/Conduit/AMReX_Conduit_Blueprint_ParticlesI.H

Lines changed: 136 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,21 @@ namespace amrex
2020
// Note:
2121
// This is a helper function, it's not part of the AMReX Blueprint Interface.
2222
//---------------------------------------------------------------------------//
23-
template <int NStructReal, int NStructInt, int NArrayReal, int NArrayInt>
23+
template <typename ParticleType, int NArrayReal, int NArrayInt>
2424
void
25-
ParticleTileToBlueprint(const ParticleTile<amrex::Particle<NStructReal,
26-
NStructInt>,
25+
ParticleTileToBlueprint(const ParticleTile<ParticleType,
2726
NArrayReal,
2827
NArrayInt> &ptile,
2928
const Vector<std::string> &real_comp_names,
3029
const Vector<std::string> &int_comp_names,
3130
conduit::Node &res,
3231
const std::string &topology_name)
3332
{
34-
int num_particles = ptile.GetArrayOfStructs().size();
35-
int struct_size = sizeof(Particle<NStructReal, NStructInt>);
33+
int num_particles = ptile.size();
3634

3735
// knowing the above, we can zero copy the x,y,z positions + id, cpu
3836
// and any user fields in the AOS
3937

40-
// get the first particle's struct
41-
const auto &pstruct = ptile.GetArrayOfStructs();
42-
4338
// setup a blueprint description for the particle mesh
4439
// create a coordinate set
4540
std::string coordset_name = topology_name + "_coords";
@@ -63,29 +58,56 @@ ParticleTileToBlueprint(const ParticleTile<amrex::Particle<NStructReal,
6358
//----------------------------------//
6459
// point locations from from aos
6560
//----------------------------------//
61+
char* pbuf = nullptr;
6662

67-
const char* pbuf_const = reinterpret_cast<const char*>(pstruct.data());
68-
char* pbuf = const_cast<char*>(pbuf_const);
63+
if constexpr(ParticleType::is_soa_particle)
64+
{
65+
amrex::ignore_unused(pbuf);
6966

70-
ParticleReal* xp = reinterpret_cast<ParticleReal*>(pbuf); pbuf += sizeof(ParticleReal);
71-
n_coords["values/x"].set_external(xp,
72-
num_particles,
73-
0,
74-
struct_size);
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)[0]),
73+
num_particles);
7574
#if AMREX_SPACEDIM > 1
76-
ParticleReal* yp = reinterpret_cast<ParticleReal*>(pbuf); pbuf += sizeof(ParticleReal);
77-
n_coords["values/y"].set_external(yp,
78-
num_particles,
79-
0,
80-
struct_size);
75+
n_coords["values/y"].set_external(const_cast<ParticleReal*>(&soa.GetRealData(1)[0]),
76+
num_particles);
8177
#endif
8278
#if AMREX_SPACEDIM > 2
83-
ParticleReal* zp = reinterpret_cast<ParticleReal*>(pbuf); pbuf += sizeof(ParticleReal);
84-
n_coords["values/z"].set_external(zp,
85-
num_particles,
86-
0,
87-
struct_size);
79+
n_coords["values/z"].set_external(const_cast<ParticleReal*>(&soa.GetRealData(2)[0]),
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);
88109
#endif
110+
}
89111

90112
// fields
91113
conduit::Node &n_fields = res["fields"];
@@ -95,65 +117,104 @@ ParticleTileToBlueprint(const ParticleTile<amrex::Particle<NStructReal,
95117
// -----------------------------
96118

97119
int vname_real_idx = 0;
98-
// struct real fields, the first set are always the particle positions
99-
// which we wrap above
100-
for (int i = 0; i < NStructReal; i++)
120+
if constexpr(!ParticleType::is_soa_particle)
101121
{
102-
ParticleReal* val = reinterpret_cast<ParticleReal*>(pbuf); pbuf += sizeof(ParticleReal);
103-
conduit::Node &n_f = n_fields[real_comp_names.at(vname_real_idx)];
104-
n_f["topology"] = topology_name;
105-
n_f["association"] = "element";
106-
n_f["values"].set_external(val,
107-
num_particles,
108-
0,
109-
struct_size);
122+
constexpr int struct_size = sizeof(ParticleType);
123+
constexpr int NStructReal = ParticleType::NReal;
110124

111-
vname_real_idx++;
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+
}
112140
}
113141

114142
//----------------------------------//
115143
// standard integer fields from aos
116144
// (id, cpu)
117145
//----------------------------------//
118146

119-
// id is the first int entry
120-
int* id = reinterpret_cast<int*>(pbuf); pbuf += sizeof(int);
121-
conduit::Node &n_f_id = n_fields[topology_name + "_id"];
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
122177

123-
n_f_id["topology"] = topology_name;
124-
n_f_id["association"] = "element";
125-
n_f_id["values"].set_external(id,
126-
num_particles,
127-
0,
128-
struct_size);
178+
// id is the first int entry
179+
conduit::Node &n_f_id = n_fields[topology_name + "_id"];
129180

130-
// cpu is the second int entry
131-
int* cpu = reinterpret_cast<int*>(pbuf); pbuf += sizeof(int);
132-
conduit::Node &n_f_cpu = n_fields[topology_name + "_cpu"];
181+
n_f_id["topology"] = topology_name;
182+
n_f_id["association"] = "element";
183+
n_f_id["values"].set_external(const_cast<int*>(&soa.GetIntData(0)[0]),
184+
num_particles);
133185

134-
n_f_cpu["topology"] = topology_name;
135-
n_f_cpu["association"] = "element";
136-
n_f_cpu["values"].set_external(cpu,
137-
num_particles,
138-
0,
139-
struct_size);
186+
// cpu is the second int entry
187+
conduit::Node &n_f_cpu = n_fields[topology_name + "_cpu"];
188+
189+
n_f_cpu["topology"] = topology_name;
190+
n_f_cpu["association"] = "element";
191+
n_f_cpu["values"].set_external(const_cast<int*>(&soa.GetIntData(0)[0]),
192+
num_particles);
193+
194+
}
140195

141196
// --------------------------------
142197
// user defined, integer aos fields
143198
// --------------------------------
144199

145200
int vname_int_idx = 0;
146-
for (int i = 0; i < NStructInt; i++)
201+
if constexpr(!ParticleType::is_soa_particle)
147202
{
148-
int* val = reinterpret_cast<int*>(pbuf); pbuf += sizeof(int);
149-
conduit::Node &n_f = n_fields[int_comp_names.at(vname_int_idx)];
150-
n_f["topology"] = topology_name;
151-
n_f["association"] = "element";
152-
n_f["values"].set_external(val,
153-
num_particles,
154-
0,
155-
struct_size);
156-
vname_int_idx++;
203+
constexpr int struct_size = sizeof(ParticleType);
204+
constexpr int NStructInt = ParticleType::NInt;
205+
206+
for (int i = 0; i < NStructInt; i++)
207+
{
208+
int* val = reinterpret_cast<int*>(pbuf); pbuf += sizeof(int);
209+
conduit::Node &n_f = n_fields[int_comp_names.at(vname_int_idx)];
210+
n_f["topology"] = topology_name;
211+
n_f["association"] = "element";
212+
n_f["values"].set_external(val,
213+
num_particles,
214+
0,
215+
struct_size);
216+
vname_int_idx++;
217+
}
157218
}
158219

159220
// -------------------------
@@ -193,10 +254,9 @@ ParticleTileToBlueprint(const ParticleTile<amrex::Particle<NStructReal,
193254
//---------------------------------------------------------------------------//
194255
// Converts a AMReX Particle Container into a Conduit Mesh Blueprint Hierarchy.
195256
//---------------------------------------------------------------------------//
196-
template <int NStructReal, int NStructInt, int NArrayReal, int NArrayInt>
257+
template <typename ParticleType, int NArrayReal, int NArrayInt>
197258
void
198-
ParticleContainerToBlueprint(const ParticleContainer<NStructReal,
199-
NStructInt,
259+
ParticleContainerToBlueprint(const ParticleContainer_impl<ParticleType,
200260
NArrayReal,
201261
NArrayInt> &pc,
202262
const Vector<std::string> &real_comp_names,
@@ -209,8 +269,13 @@ ParticleContainerToBlueprint(const ParticleContainer<NStructReal,
209269
// validate varnames, which are used to provide field names
210270
// for user defined aos and soa values.
211271

212-
BL_ASSERT(real_comp_names.size() == (NStructReal + NArrayReal) );
213-
BL_ASSERT(int_comp_names.size() == (NStructInt + NArrayInt) );
272+
if constexpr(ParticleType::is_soa_particle) {
273+
BL_ASSERT(real_comp_names.size() == NArrayReal);
274+
BL_ASSERT(int_comp_names.size() == NArrayInt);
275+
} else {
276+
BL_ASSERT(real_comp_names.size() == (ParticleType::NReal + NArrayReal) );
277+
BL_ASSERT(int_comp_names.size() == (ParticleType::NInt + NArrayInt) );
278+
}
214279

215280
int num_levels = pc.maxLevel() + 1;
216281
int num_domains = 0;
@@ -224,7 +289,7 @@ ParticleContainerToBlueprint(const ParticleContainer<NStructReal,
224289
int rank = ParallelDescriptor::MyProc();
225290
int nprocs = ParallelDescriptor::NProcs();
226291

227-
using MyParConstIter = ParConstIter<NStructReal, NStructInt, NArrayReal, NArrayInt>;
292+
using MyParConstIter = ParConstIter_impl<ParticleType, NArrayReal, NArrayInt>;
228293

229294
//
230295
// blueprint expects unique ids for each domain published
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
if ( NOT AMReX_ASCENT )
2+
return ()
3+
endif ()
4+
5+
foreach(D IN LISTS AMReX_SPACEDIM)
6+
set(_sources main.cpp)
7+
set(_input_files inputs.rt )
8+
9+
setup_test(${D} _sources _input_files NTASKS 2)
10+
11+
unset(_sources)
12+
unset(_input_files)
13+
endforeach()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
AMREX_HOME = ../../../
2+
3+
DEBUG = FALSE
4+
5+
DIM = 3
6+
7+
COMP = gcc
8+
9+
USE_MPI = TRUE
10+
USE_OMP = FALSE
11+
USE_CUDA = FALSE
12+
13+
TINY_PROFILE = TRUE
14+
USE_PARTICLES = TRUE
15+
USE_ASCENT = TRUE
16+
17+
include $(AMREX_HOME)/Tools/GNUMake/Make.defs
18+
19+
include ./Make.package
20+
include $(AMREX_HOME)/Src/Base/Make.package
21+
include $(AMREX_HOME)/Src/Particle/Make.package
22+
include $(AMREX_HOME)/Src/Extern/Conduit/Make.package
23+
24+
include $(AMREX_HOME)/Tools/GNUMake/Make.rules
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
ascent.size = (32, 64, 64)
2+
ascent.max_grid_size = 32
3+
ascent.is_periodic = 1
4+
ascent.num_ppc = 1
5+
ascent.nlevs = 1
6+
7+
ascent.num_runtime_real = 0
8+
ascent.num_runtime_int = 0
9+
10+
particles.do_tiling = 1

0 commit comments

Comments
 (0)