pyAMReX
Particle.H
Go to the documentation of this file.
1 /* Copyright 2022 The AMReX Community
2  *
3  * Authors: Ryan Sandberg
4  * License: BSD-3-Clause-LBNL
5  */
6 #pragma once
7 
8 #include "pyAMReX.H"
9 
10 #include <AMReX_Config.H>
11 #include <AMReX_BoxArray.H>
12 #include <AMReX_IntVect.H>
13 #include <AMReX_RealVect.H>
14 #include <AMReX_Particle.H>
15 
16 #include <array>
17 #include <stdexcept>
18 #include <string>
19 #include <sstream>
20 #include <stdexcept>
21 #include <unordered_set>
22 #include <utility>
23 #include <cmath>
24 #include <regex>
25 
26 
27 namespace
28 {
30  template<typename T, std::size_t... I>
31  constexpr auto
32  build_array (T a[], std::index_sequence<I...> s)
33  {
34  return std::array<T, s.size()>{a[I]...};
35  }
36 }
37 
38 template <int T_NReal, int T_NInt=0>
39 void make_Particle(py::module &m)
40 {
41  // For legacy AoS + SoA particles, we register a particle and superparticle.
42  // The latter adds the SoA attributes. Avoid to double-register those types.
43  /*
44  static std::unordered_set<std::array<int, 2>> registered;
45  std::array<int, 2> const this_p = {T_NReal, T_NInt}
46  if (auto search = registered.find(this_p); search != registered.end()) {
47  return;
48  }
49  registered.insert(this_p);
50  */
51 
52  using namespace amrex;
53 
54  using ParticleType = Particle<T_NReal, T_NInt>;
55  auto const particle_name = std::string("Particle_").append(std::to_string(T_NReal) + "_" + std::to_string(T_NInt));
56  py::class_<ParticleType> (m, particle_name.c_str())
57  .def(py::init<>())
58  .def(py::init([](AMREX_D_DECL(ParticleReal x, ParticleReal y, ParticleReal z)) {
59  std::unique_ptr<ParticleType> part(new ParticleType());
60  part->m_pos[0] = x;
61  #if (AMREX_SPACEDIM >= 2)
62  part->m_pos[1] = y;
63  #endif
64  #if (AMREX_SPACEDIM >= 3)
65  part->m_pos[2] = z;
66  #endif
67  return part;
68  }
69  )
70  )
71  .def(py::init(
72  [](AMREX_D_DECL(ParticleReal x, ParticleReal y, ParticleReal z), py::args& args)
73  {
74  std::unique_ptr<ParticleType> part(new ParticleType());
75  AMREX_D_TERM(part->m_pos[0] = x;, part->m_pos[1] = y;, part->m_pos[2] = z;)
76  int T_NTotal = T_NReal + T_NInt;
77  int argn = args.size();
78  if(argn != T_NTotal) {
79  throw std::runtime_error("Must supply all " + std::to_string(T_NTotal) + " rdata, idata elements");
80  }
81  if constexpr (T_NReal > 0) {
82  for (int ii = 0; ii < T_NReal; ii++) {
83  part->m_rdata[ii] = py::cast<ParticleReal>(args[ii]);
84  }
85  }
86  if constexpr (T_NInt > 0) {
87  for (int ii = 0; ii < T_NInt; ii++) {
88  part->m_idata[ii] = py::cast<int>(args[ii+T_NReal]);
89  }
90  }
91  return part;
92  })
93  )
94 
95  .def(py::init(
96  [](AMREX_D_DECL(ParticleReal x, ParticleReal y, ParticleReal z), py::kwargs& kwargs)
97  {
98  std::unique_ptr<ParticleType> part(new ParticleType());
99  AMREX_D_TERM(part->m_pos[0] = x;, part->m_pos[1] = y;, part->m_pos[2] = z;)
100 
101  for (auto const& item : kwargs) {
102  std::regex component_separator("(.*)_([0-9]*)");
103  std::smatch sm;
104  std::string varname = item.first.cast<std::string>();
105  std::regex_match(varname, sm, component_separator, std::regex_constants::match_default);
106  int comp = std::stoi(sm[2]);
107  if constexpr (T_NReal > 0) {
108  if (comp >= 0 && comp < T_NReal && sm[1] == "rdata") {
109  part->m_rdata[comp] = item.second.cast<ParticleReal>();
110  }
111  }
112  if constexpr (T_NInt > 0) {
113  if (comp >= 0 && comp < T_NInt && sm[1] == "idata") {
114  part->m_idata[comp] = item.second.cast<int>();
115  }
116  }
117  }
118  return part;
119  })
120  )
121 
122  .def(py::init(
123  [](py::kwargs& kwargs) {
124  std::unique_ptr<ParticleType> part(new ParticleType());
125  for (auto const& item : kwargs) {
126  std::regex component_separator("(.*)_([0-9]*)");
127  std::smatch sm;
128  std::string varname = item.first.cast<std::string>();
129  std::regex_match(varname, sm, component_separator, std::regex_constants::match_default);
130  int comp = -1;
131  if (varname == "x") { part->m_pos[0] = item.second.cast<ParticleReal>(); }
132 #if AMREX_SPACEDIM >= 2
133  if (varname == "y") { part->m_pos[1] = item.second.cast<ParticleReal>(); }
134 #elif AMREX_SPACEDIM == 3
135  if (varname == "z") { part->m_pos[2] = item.second.cast<ParticleReal>(); }
136 #endif
137  if (sm.size() > 2) {
138  comp = std::stoi(sm[2]);
139  if constexpr (T_NReal > 0) {
140  if(comp >= 0 && comp < T_NReal && sm[1] == "rdata") {
141  part->m_rdata[comp] = item.second.cast<ParticleReal>();
142  }
143  }
144  if constexpr (T_NInt > 0) {
145  if (comp >= 0 && comp < T_NInt && sm[1] == "idata") {
146  part->m_idata[comp] = item.second.cast<int>();
147  }
148  }
149  }
150  }
151  return part;
152  })
153  )
154  .def("__repr__",
155  [](py::object& obj) {
156  py::str py_name = obj.attr("__class__").attr("__name__");
157  const std::string name = py_name;
158  const auto p = obj.cast<ParticleType>();
159  std::stringstream s;
160  s << p;
161  return "<amrex." + name + " with attributes\nid cpu pos rdata idata \n" + s.str() + ">";
162  }
163  )
164  .def("__str__",
165  [](const ParticleType& p) {
166  std::stringstream s;
167  s << p;
168  return s.str();
169  })
170  .def_readonly_static("NReal", &ParticleType::NReal)
171  .def_readonly_static("NInt", &ParticleType::NInt)
172  .def("pos", [](const ParticleType &p, int index) { return p.pos(index); })
173  .def("pos", [](const ParticleType &p) { return p.pos(); })
174  .def("setPos", [](ParticleType &p, int index, Real val) { AMREX_ASSERT(index > 0 && index < AMREX_SPACEDIM); p.m_pos[index] = val; })
175  .def("setPos", [](ParticleType &p, const RealVect & vals) { for (int ii=0; ii < AMREX_SPACEDIM; ii++) { p.m_pos[ii] = vals[ii]; } })
176  .def("setPos", [](ParticleType &p, const std::array<Real, AMREX_SPACEDIM>& vals) { for (int ii=0; ii < AMREX_SPACEDIM; ii++) { p.m_pos[ii] = vals[ii]; } })
177 
178  .def("get_rdata", [](ParticleType &p, int index) {
179  if constexpr (T_NReal > 0) {
180  if(index < 0 || index >= T_NReal) {
181  throw std::range_error("index not in range. Valid range : [0, " + std::to_string(T_NReal));
182  }
183  return p.m_rdata[index];
184  } else {
185  amrex::ignore_unused(p, index);
186  return py::none();
187  }
188  }
189  )
190  .def("get_rdata", [](ParticleType &p) {
191  if constexpr (T_NReal > 0) {
192  return build_array(
193  p.m_rdata,
194  std::make_index_sequence<T_NReal>{}
195  );
196  } else {
198  return py::none();
199  }
200  }
201  )
202  .def("set_rdata", [](ParticleType &p, int index, Real val) {
203  if constexpr (T_NReal > 0) {
204  if(index < 0 || index >= T_NReal) {
205  // std::string error_msg = ""
206  throw std::range_error("index not in range. Valid range : [0, " + std::to_string(T_NReal) + ")");
207  }
208  p.m_rdata[index] = val;
209  } else {
210  amrex::ignore_unused(index, val);
211  }
212  }
213  )
214  .def("set_rdata", [](ParticleType &p, const std::array<Real, T_NReal>& vals) {
215  if constexpr (T_NReal > 0) {
216  for (int ii=0; ii < T_NReal; ii++) {
217  p.m_rdata[ii] = vals[ii];
218  }
219  } else {
220  amrex::ignore_unused(p, vals);
221  }
222  }
223  )
224  .def("get_idata", [](ParticleType &p, int index) {
225  if constexpr (T_NInt > 0) {
226  if(index < 0 || index >= T_NInt) {
227  throw std::range_error("index not in range. Valid range : [0, " + std::to_string(T_NInt));
228  }
229  return p.m_idata[index];
230  } else {
231  amrex::ignore_unused(p, index);
232  return py::none();
233  }
234  }
235  )
236  .def("get_idata", [](ParticleType &p) {
237  if constexpr (T_NInt > 0) {
238  return build_array(
239  p.m_idata,
240  std::make_index_sequence<T_NInt>{}
241  );
242  }
243  else {
245  return py::none();
246  }
247  }
248  )
249  .def("set_idata", [](ParticleType &p, int index, int val) {
250  if constexpr (T_NInt > 0) {
251  if(index < 0 || index >= T_NInt) {
252  throw std::range_error("index not in range. Valid range : [0, " + std::to_string(T_NInt) + ")");
253  }
254  p.m_idata[index] = val;
255  } else {
256  amrex::ignore_unused(index, val);
257  }
258  }
259  )
260  .def("set_idata", [](ParticleType &p, const std::array<int, T_NInt>& vals) {
261  if constexpr (T_NInt > 0) {
262  for (int ii=0; ii < T_NInt; ii++) {
263  p.m_idata[ii] = vals[ii];
264  }
265  } else {
266  amrex::ignore_unused(vals);
267  }
268  }
269  )
270  .def("cpu", [](const ParticleType &p) { const int m_cpu = p.cpu(); return m_cpu; })
271  .def("id", [](const ParticleType &p) { const int m_id = p.id(); return m_id; })
272  .def("NextID", [](const ParticleType &p) {return p.NextID();})
273  .def("NextID", [](const ParticleType &p, Long nextid) { p.NextID(nextid); })
274  .def_property("x", [](ParticleType &p){ return p.pos(0);}, [](ParticleType &p, Real val){ p.m_pos[0] = val; })
275 #if AMREX_SPACEDIM >= 2
276  .def_property("y", [](ParticleType &p){ return p.pos(1);}, [](ParticleType &p, Real val){ p.m_pos[1] = val; })
277 #endif
278 #if AMREX_SPACEDIM == 3
279  .def_property("z", [](ParticleType &p){ return p.pos(2);}, [](ParticleType &p, Real val){ p.m_pos[2] = val; })
280 #endif
281  ;
282 }
#define AMREX_ASSERT(EX)
#define AMREX_D_TERM(a, b, c)
#define AMREX_D_DECL(a, b, c)
void make_Particle(py::module &m)
Definition: Particle.H:39
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void ignore_unused(const Ts &...)