|
29 | 29 | #include "openPMD/snapshots/StatefulIterator.hpp" |
30 | 30 |
|
31 | 31 | #include "openPMD/binding/python/Common.hpp" |
| 32 | +#include <filesystem> |
32 | 33 | #include <optional> |
33 | 34 |
|
34 | 35 | #if openPMD_HAVE_MPI |
|
41 | 42 | #include <sstream> |
42 | 43 | #include <string> |
43 | 44 |
|
| 45 | +namespace auxiliary |
| 46 | +{ |
| 47 | +template <typename Functor, typename... Types> |
| 48 | +struct ForEachType; |
| 49 | + |
| 50 | +template <typename Functor, typename FirstType, typename... OtherTypes> |
| 51 | +struct ForEachType<Functor, FirstType, OtherTypes...> |
| 52 | +{ |
| 53 | + template <typename... Args> |
| 54 | + static void call(Args &&...args) |
| 55 | + { |
| 56 | + Functor::template call<FirstType>(args...); |
| 57 | + ForEachType<Functor, OtherTypes...>::template call<Args...>(args...); |
| 58 | + } |
| 59 | +}; |
| 60 | + |
| 61 | +template <typename Functor> |
| 62 | +struct ForEachType<Functor> |
| 63 | +{ |
| 64 | + template <typename... Args> |
| 65 | + static constexpr void call(Args &&...) |
| 66 | + { /* no-op */ |
| 67 | + } |
| 68 | +}; |
| 69 | +} // namespace auxiliary |
| 70 | + |
| 71 | +namespace internal |
| 72 | +{ |
| 73 | +struct DefineSeriesConstructorPerPathType |
| 74 | +{ |
| 75 | + template <typename PathType> |
| 76 | + static void call(py::class_<Series, Attributable> &py_class) |
| 77 | + { |
| 78 | + py_class |
| 79 | + .def( |
| 80 | + py::init([](PathType const &filepath, |
| 81 | + Access at, |
| 82 | + std::string const &options) { |
| 83 | + py::gil_scoped_release release; |
| 84 | + return new Series(filepath, at, options); |
| 85 | + }), |
| 86 | + py::arg("filepath"), |
| 87 | + py::arg("access"), |
| 88 | + py::arg("options") = "{}", |
| 89 | + R"END( |
| 90 | +Construct a new Series. Parameters: |
| 91 | +
|
| 92 | +* filepath: The file path. |
| 93 | +* at: Access mode. |
| 94 | +* options: Advanced backend configuration via JSON. |
| 95 | + May be specified as a JSON-formatted string directly, or as a path |
| 96 | + to a JSON textfile, prepended by an at sign '@'. |
| 97 | +
|
| 98 | +For details on access modes, JSON/TOML configuration and iteration encoding, |
| 99 | +refer to: |
| 100 | +
|
| 101 | +* https://openpmd-api.readthedocs.io/en/latest/usage/workflow.html#access-modes |
| 102 | +* https://openpmd-api.readthedocs.io/en/latest/details/backendconfig.html |
| 103 | +* https://openpmd-api.readthedocs.io/en/latest/usage/concepts.html#iteration-and-series |
| 104 | +
|
| 105 | +In case of file-based iteration encoding, the file names for each |
| 106 | +iteration are determined by an expansion pattern that must be specified. |
| 107 | +It takes one out of two possible forms: |
| 108 | +
|
| 109 | +1. Simple form: %T is replaced with the iteration index, e.g. |
| 110 | + `simData_%T.bp` becomes `simData_50.bp`. |
| 111 | +2. Padded form: e.g. %06T is replaced with the iteration index padded to |
| 112 | + at least six digits. `simData_%06T.bp` becomes `simData_000050.bp`. |
| 113 | +
|
| 114 | +The backend is determined: |
| 115 | +
|
| 116 | +1. Explicitly via the JSON/TOML parameter `backend`, e.g. `{"backend": |
| 117 | + "adios2"}`. |
| 118 | +2. Otherwise implicitly from the filename extension, e.g. |
| 119 | + `simData_%T.h5`. |
| 120 | +
|
| 121 | +The filename extension can be replaced with a globbing pattern %E. |
| 122 | +It will be replaced with an automatically determined file name extension: |
| 123 | +
|
| 124 | +1. In CREATE mode: The extension is set to a backend-specific default |
| 125 | + extension. This requires that the backend is specified via JSON/TOML. |
| 126 | +2. In READ_ONLY, READ_WRITE and READ_LINEAR modes: These modes require |
| 127 | + that files already exist on disk. The disk will be scanned for files |
| 128 | + that match the pattern and the resulting file extension will be used. |
| 129 | + If the result is ambiguous or no such file is found, an error is |
| 130 | + raised. |
| 131 | +3. In APPEND mode: Like (2.), except if no matching file is found. In |
| 132 | + that case, the procedure of (1.) is used, owing to the fact that |
| 133 | + APPEND mode can be used to create new datasets. |
| 134 | + )END") |
| 135 | +#if openPMD_HAVE_MPI |
| 136 | + .def( |
| 137 | + py::init([](PathType const &filepath, |
| 138 | + Access at, |
| 139 | + py::object &comm, |
| 140 | + std::string const &options) { |
| 141 | + auto variant = pythonObjectAsMpiComm(comm); |
| 142 | + if (auto errorMsg = std::get_if<std::string>(&variant)) |
| 143 | + { |
| 144 | + throw std::runtime_error("[Series] " + *errorMsg); |
| 145 | + } |
| 146 | + else |
| 147 | + { |
| 148 | + py::gil_scoped_release release; |
| 149 | + return new Series( |
| 150 | + filepath, at, std::get<MPI_Comm>(variant), options); |
| 151 | + } |
| 152 | + }), |
| 153 | + py::arg("filepath"), |
| 154 | + py::arg("access"), |
| 155 | + py::arg("mpi_communicator"), |
| 156 | + py::arg("options") = "{}", |
| 157 | + R"END( |
| 158 | +Construct a new Series. Parameters: |
| 159 | +
|
| 160 | +* filepath: The file path. |
| 161 | +* at: Access mode. |
| 162 | +* options: Advanced backend configuration via JSON. |
| 163 | + May be specified as a JSON-formatted string directly, or as a path |
| 164 | + to a JSON textfile, prepended by an at sign '@'. |
| 165 | +* mpi_communicator: The MPI communicator |
| 166 | +
|
| 167 | +For further details, refer to the non-MPI overload. |
| 168 | + )END"); |
| 169 | +#endif |
| 170 | + } |
| 171 | +}; |
| 172 | +} // namespace internal |
| 173 | + |
44 | 174 | struct StatefulIteratorPythonAdaptor : LegacyIteratorAdaptor |
45 | 175 | { |
46 | 176 | StatefulIteratorPythonAdaptor(LegacyIteratorAdaptor it) |
@@ -166,107 +296,13 @@ not possible once it has been closed. |
166 | 296 | // keep handle alive while iterator exists |
167 | 297 | py::keep_alive<0, 1>()); |
168 | 298 |
|
169 | | - // `clang-format on/off` doesn't help here. |
170 | | - // Writing this without a macro would lead to a huge diff due to |
171 | | - // clang-format. |
172 | | -#define OPENPMD_AVOID_CLANG_FORMAT auto cl = |
173 | | - OPENPMD_AVOID_CLANG_FORMAT |
174 | | -#undef OPENPMD_AVOID_CLANG_FORMAT |
175 | | - |
176 | | - py::class_<Series, Attributable>(m, "Series") |
177 | | - |
178 | | - .def( |
179 | | - py::init([](std::string const &filepath, |
180 | | - Access at, |
181 | | - std::string const &options) { |
182 | | - py::gil_scoped_release release; |
183 | | - return new Series(filepath, at, options); |
184 | | - }), |
185 | | - py::arg("filepath"), |
186 | | - py::arg("access"), |
187 | | - py::arg("options") = "{}", |
188 | | - R"END( |
189 | | -Construct a new Series. Parameters: |
190 | | -
|
191 | | -* filepath: The file path. |
192 | | -* at: Access mode. |
193 | | -* options: Advanced backend configuration via JSON. |
194 | | - May be specified as a JSON-formatted string directly, or as a path |
195 | | - to a JSON textfile, prepended by an at sign '@'. |
196 | | -
|
197 | | -For details on access modes, JSON/TOML configuration and iteration encoding, |
198 | | -refer to: |
199 | | -
|
200 | | -* https://openpmd-api.readthedocs.io/en/latest/usage/workflow.html#access-modes |
201 | | -* https://openpmd-api.readthedocs.io/en/latest/details/backendconfig.html |
202 | | -* https://openpmd-api.readthedocs.io/en/latest/usage/concepts.html#iteration-and-series |
203 | | -
|
204 | | -In case of file-based iteration encoding, the file names for each |
205 | | -iteration are determined by an expansion pattern that must be specified. |
206 | | -It takes one out of two possible forms: |
207 | | -
|
208 | | -1. Simple form: %T is replaced with the iteration index, e.g. |
209 | | - `simData_%T.bp` becomes `simData_50.bp`. |
210 | | -2. Padded form: e.g. %06T is replaced with the iteration index padded to |
211 | | - at least six digits. `simData_%06T.bp` becomes `simData_000050.bp`. |
212 | | -
|
213 | | -The backend is determined: |
214 | | -
|
215 | | -1. Explicitly via the JSON/TOML parameter `backend`, e.g. `{"backend": |
216 | | - "adios2"}`. |
217 | | -2. Otherwise implicitly from the filename extension, e.g. |
218 | | - `simData_%T.h5`. |
219 | | -
|
220 | | -The filename extension can be replaced with a globbing pattern %E. |
221 | | -It will be replaced with an automatically determined file name extension: |
222 | | -
|
223 | | -1. In CREATE mode: The extension is set to a backend-specific default |
224 | | - extension. This requires that the backend is specified via JSON/TOML. |
225 | | -2. In READ_ONLY, READ_WRITE and READ_LINEAR modes: These modes require |
226 | | - that files already exist on disk. The disk will be scanned for files |
227 | | - that match the pattern and the resulting file extension will be used. |
228 | | - If the result is ambiguous or no such file is found, an error is |
229 | | - raised. |
230 | | -3. In APPEND mode: Like (2.), except if no matching file is found. In |
231 | | - that case, the procedure of (1.) is used, owing to the fact that |
232 | | - APPEND mode can be used to create new datasets. |
233 | | - )END") |
234 | | -#if openPMD_HAVE_MPI |
235 | | - .def( |
236 | | - py::init([](std::string const &filepath, |
237 | | - Access at, |
238 | | - py::object &comm, |
239 | | - std::string const &options) { |
240 | | - auto variant = pythonObjectAsMpiComm(comm); |
241 | | - if (auto errorMsg = std::get_if<std::string>(&variant)) |
242 | | - { |
243 | | - throw std::runtime_error("[Series] " + *errorMsg); |
244 | | - } |
245 | | - else |
246 | | - { |
247 | | - py::gil_scoped_release release; |
248 | | - return new Series( |
249 | | - filepath, at, std::get<MPI_Comm>(variant), options); |
250 | | - } |
251 | | - }), |
252 | | - py::arg("filepath"), |
253 | | - py::arg("access"), |
254 | | - py::arg("mpi_communicator"), |
255 | | - py::arg("options") = "{}", |
256 | | - R"END( |
257 | | -Construct a new Series. Parameters: |
258 | | -
|
259 | | -* filepath: The file path. |
260 | | -* at: Access mode. |
261 | | -* options: Advanced backend configuration via JSON. |
262 | | - May be specified as a JSON-formatted string directly, or as a path |
263 | | - to a JSON textfile, prepended by an at sign '@'. |
264 | | -* mpi_communicator: The MPI communicator |
| 299 | + py::class_<Series, Attributable> cl(m, "Series"); |
| 300 | + ::auxiliary::ForEachType< |
| 301 | + ::internal::DefineSeriesConstructorPerPathType, |
| 302 | + std::string, |
| 303 | + std::filesystem::path>::template call(cl); |
265 | 304 |
|
266 | | -For further details, refer to the non-MPI overload. |
267 | | - )END") |
268 | | -#endif |
269 | | - .def("__bool__", &Series::operator bool) |
| 305 | + cl.def("__bool__", &Series::operator bool) |
270 | 306 | .def("__len__", [](Series const &s) { return s.iterations.size(); }) |
271 | 307 | .def( |
272 | 308 | "__repr__", |
|
0 commit comments