ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
h5md_core.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2022 The ESPResSo project
3 * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
4 * Max-Planck-Institute for Polymer Research, Theory Group
5 *
6 * This file is part of ESPResSo.
7 *
8 * ESPResSo is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * ESPResSo is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "hdf5_patches.hpp" // must appear first
23
24#include "h5md_core.hpp"
25#include "h5md_dataset.hpp"
27
28#include "BoxGeometry.hpp"
29#include "Particle.hpp"
31
32#include "config/version.hpp"
33
34#include <utils/Vector.hpp>
35
36#include <boost/array.hpp>
37#include <boost/filesystem.hpp>
38#include <boost/mpi/collectives.hpp>
39#include <boost/multi_array.hpp>
40
41#include <h5xx/h5xx.hpp>
42
43#include <mpi.h>
44
45#include <algorithm>
46#include <cstddef>
47#include <fstream>
48#include <functional>
49#include <iterator>
50#include <memory>
51#include <stdexcept>
52#include <string>
53#include <vector>
54
55namespace h5xx {
56template <typename T, std::size_t size>
57struct is_array<Utils::Vector<T, size>> : std::true_type {};
58} // namespace h5xx
59
60namespace Writer {
61namespace H5md {
62
63using MultiArray3i = boost::multi_array<int, 3>;
67
68static void backup_file(const std::string &from, const std::string &to) {
69 /*
70 * If the file itself *and* a backup file exists, something must
71 * have gone wrong.
72 */
73 boost::filesystem::path pfrom(from), pto(to);
74 auto constexpr option_fail_if_exists = boost::filesystem::copy_options::none;
75 try {
76 boost::filesystem::copy_file(pfrom, pto, option_fail_if_exists);
77 } catch (const boost::filesystem::filesystem_error &) {
78 throw left_backupfile();
79 }
80}
81
82template <typename extent_type>
83static void extend_dataset(h5xx::dataset &dataset,
84 extent_type const &change_extent) {
85 auto const rank = static_cast<h5xx::dataspace>(dataset).rank();
86 auto extents = static_cast<h5xx::dataspace>(dataset).extents();
87 /* Extend the dataset for another timestep */
88 for (auto i = 0u; i < rank; i++) {
89 extents[i] += change_extent[i];
90 }
91 H5Dset_extent(dataset.hid(), extents.data()); // extend all dims is collective
92}
93
94template <typename value_type, typename extent_type>
95static void write_dataset(value_type const &data, h5xx::dataset &dataset,
96 extent_type const &change_extent,
97 extent_type const &offset, extent_type const &count) {
98 extend_dataset(dataset, change_extent);
99 /* write the data to the dataset. */
100 h5xx::write_dataset(dataset, data, h5xx::slice(offset, count));
101}
102
103static void write_script(std::string const &target,
104 boost::filesystem::path const &script_path) {
105 if (!script_path.empty()) {
106 std::ifstream scriptfile(script_path.string());
107 std::string buffer((std::istreambuf_iterator<char>(scriptfile)),
108 std::istreambuf_iterator<char>());
109 auto file = h5xx::file(target, h5xx::file::out);
110 auto const group = h5xx::group(file, "parameters/files");
111 h5xx::write_attribute(group, "script", buffer);
112 file.close();
113 }
114}
115
116/* Initialize the file-related variables after parameters have been set. */
117void File::init_file(std::string const &file_path) {
118 m_backup_filename = file_path + ".bak";
119 if (m_script_path.empty()) {
120 m_absolute_script_path = boost::filesystem::path();
121 } else {
122 boost::filesystem::path script_path(m_script_path);
123 m_absolute_script_path = boost::filesystem::canonical(script_path);
124 }
125 auto const file_exists = boost::filesystem::exists(file_path);
126 auto const backup_file_exists = boost::filesystem::exists(m_backup_filename);
127 /* Perform a barrier synchronization. Otherwise one process might already
128 * create the file while another still checks for its existence. */
129 m_comm.barrier();
130 if (file_exists) {
131 if (m_h5md_specification.is_compliant(file_path)) {
132 /*
133 * If the file exists and has a valid H5MD structure, let's create a
134 * backup of it. This has the advantage, that the new file can
135 * just be deleted if the simulation crashes at some point and we
136 * still have a valid trajectory backed up, from which we can restart.
137 */
138 if (m_comm.rank() == 0)
139 backup_file(file_path, m_backup_filename);
140 load_file(file_path);
141 } else {
142 throw incompatible_h5mdfile();
143 }
144 } else {
145 if (backup_file_exists)
146 throw left_backupfile();
147 create_file(file_path);
148 }
149}
150
151void File::load_datasets() {
152 auto &datasets = *m_datasets;
153 for (auto const &ds : m_h5md_specification.get_datasets()) {
154 if (ds.is_link)
155 continue;
156 datasets[ds.path()] = h5xx::dataset(*m_h5md_file, ds.path());
157 }
158}
159
160void File::create_groups() {
161 h5xx::group group(*m_h5md_file);
162 for (auto const &ds : m_h5md_specification.get_datasets()) {
163 h5xx::group new_group(group, ds.group);
164 }
165}
166
167static std::vector<hsize_t> create_dims(hsize_t rank, hsize_t data_dim) {
168 switch (rank) {
169 case 3ul:
170 return std::vector<hsize_t>{0ul, 0ul, data_dim};
171 case 2ul:
172 return std::vector<hsize_t>{0ul, data_dim};
173 case 1ul:
174 return std::vector<hsize_t>{data_dim};
175 default:
176 throw std::runtime_error(
177 "H5MD Error: datasets with this dimension are not implemented\n");
178 }
179}
180
181static std::vector<hsize_t> create_chunk_dims(hsize_t rank, hsize_t data_dim) {
182 hsize_t chunk_size = (rank > 1ul) ? 1000ul : 1ul;
183 switch (rank) {
184 case 3ul:
185 return {1ul, chunk_size, data_dim};
186 case 2ul:
187 return {1ul, chunk_size};
188 case 1ul:
189 return {chunk_size};
190 default:
191 throw std::runtime_error(
192 "H5MD Error: datasets with this dimension are not implemented\n");
193 }
194}
195
196void File::create_datasets() {
197 namespace hps = h5xx::policy::storage;
198 auto &datasets = *m_datasets;
199 for (auto const &ds : m_h5md_specification.get_datasets()) {
200 if (ds.is_link)
201 continue;
202 auto maxdims = std::vector<hsize_t>(ds.rank, H5S_UNLIMITED);
203 auto dataspace =
204 h5xx::dataspace(create_dims(ds.rank, ds.data_dim), maxdims);
205 auto storage = hps::chunked(create_chunk_dims(ds.rank, ds.data_dim))
206 .set(hps::fill_value(-10));
207 datasets[ds.path()] =
208 h5xx::dataset(*m_h5md_file, ds.path(), ds.type, dataspace, storage,
209 H5P_DEFAULT, H5P_DEFAULT);
210 }
211}
212
213void File::load_file(const std::string &file_path) {
214 *m_h5md_file = h5xx::file(file_path, m_comm, MPI_INFO_NULL, h5xx::file::out);
215 load_datasets();
216}
217
218static void write_attributes(h5xx::file &h5md_file) {
219 auto h5md_group = h5xx::group(h5md_file, "h5md");
220 h5xx::write_attribute(h5md_group, "version",
221 boost::array<hsize_t, 2>{{1ul, 1ul}});
222 auto h5md_creator_group = h5xx::group(h5md_group, "creator");
223 h5xx::write_attribute(h5md_creator_group, "name", "ESPResSo");
224 h5xx::write_attribute(h5md_creator_group, "version", ESPRESSO_VERSION);
225 auto h5md_author_group = h5xx::group(h5md_group, "author");
226 h5xx::write_attribute(h5md_author_group, "name", "N/A");
227 auto group = h5xx::group(h5md_file, "particles/atoms/box");
228 h5xx::write_attribute(group, "dimension", 3);
229 h5xx::write_attribute(group, "boundary", "periodic");
230}
231
232void File::write_units() {
233 auto const &datasets = *m_datasets;
234 if (!mass_unit().empty() and (m_fields & H5MD_OUT_MASS)) {
235 h5xx::write_attribute(datasets.at("particles/atoms/mass/value"), "unit",
236 mass_unit());
237 }
238 if (!charge_unit().empty() and (m_fields & H5MD_OUT_CHARGE)) {
239 h5xx::write_attribute(datasets.at("particles/atoms/charge/value"), "unit",
240 charge_unit());
241 }
242 if (!length_unit().empty() and (m_fields & H5MD_OUT_BOX_L)) {
243 h5xx::write_attribute(datasets.at("particles/atoms/position/value"), "unit",
244 length_unit());
245 h5xx::write_attribute(datasets.at("particles/atoms/box/edges/value"),
246 "unit", length_unit());
247 }
248 if (!length_unit().empty() and (m_fields & H5MD_OUT_LE_OFF)) {
249 h5xx::write_attribute(
250 datasets.at("particles/atoms/lees_edwards/offset/value"), "unit",
251 length_unit());
252 }
253 if (!velocity_unit().empty() and (m_fields & H5MD_OUT_VEL)) {
254 h5xx::write_attribute(datasets.at("particles/atoms/velocity/value"), "unit",
255 velocity_unit());
256 }
257 if (!force_unit().empty() and (m_fields & H5MD_OUT_FORCE)) {
258 h5xx::write_attribute(datasets.at("particles/atoms/force/value"), "unit",
259 force_unit());
260 }
261 if (!time_unit().empty()) {
262 h5xx::write_attribute(datasets.at("particles/atoms/id/time"), "unit",
263 time_unit());
264 }
265}
266
267void File::create_hard_links() {
268 std::string path_step = "particles/atoms/id/step";
269 std::string path_time = "particles/atoms/id/time";
270 for (auto &ds : m_h5md_specification.get_datasets()) {
271 if (ds.is_link) {
272 char const *from = nullptr;
273 if (ds.name == "step") {
274 from = path_step.c_str();
275 } else if (ds.name == "time") {
276 from = path_time.c_str();
277 }
278 assert(from != nullptr);
279 if (H5Lcreate_hard(m_h5md_file->hid(), from, m_h5md_file->hid(),
280 ds.path().c_str(), H5P_DEFAULT, H5P_DEFAULT) < 0) {
281 throw std::runtime_error("Error creating hard link for " + ds.path());
282 }
283 }
284 }
285}
286
287void File::create_file(const std::string &file_path) {
288 if (m_comm.rank() == 0)
289 write_script(file_path, m_absolute_script_path);
290 m_comm.barrier();
291 m_h5md_file = std::make_unique<h5xx::file>(file_path, m_comm, MPI_INFO_NULL,
292 h5xx::file::out);
293 create_groups();
294 create_datasets();
295 write_attributes(*m_h5md_file);
296 write_units();
297 create_hard_links();
298}
299
301 if (m_comm.rank() == 0)
302 boost::filesystem::remove(m_backup_filename);
303}
304
305namespace detail {
306
307template <std::size_t rank> struct slice_info {};
308
309template <> struct slice_info<3> {
310 static auto extent(hsize_t n_part_diff) {
311 return Vector3hs{1, n_part_diff, 0};
312 }
313 static constexpr auto count() { return Vector3hs{1, 1, 3}; }
314 static auto offset(hsize_t n_time_steps, hsize_t prefix) {
315 return Vector3hs{n_time_steps, prefix, 0};
316 }
317};
318
319template <> struct slice_info<2> {
320 static auto extent(hsize_t n_part_diff) { return Vector2hs{1, n_part_diff}; }
321 static constexpr auto count() { return Vector2hs{1, 1}; }
322 static auto offset(hsize_t n_time_steps, hsize_t prefix) {
323 return Vector2hs{n_time_steps, prefix};
324 }
325};
326
327} // namespace detail
328
329template <std::size_t dim, typename Op>
330void write_td_particle_property(hsize_t prefix, hsize_t n_part_global,
331 ParticleRange const &particles,
332 h5xx::dataset &dataset, Op op) {
333 auto const old_extents = static_cast<h5xx::dataspace>(dataset).extents();
334 auto const extent_particle_number =
335 std::max(n_part_global, old_extents[1]) - old_extents[1];
336 extend_dataset(dataset,
337 detail::slice_info<dim>::extent(extent_particle_number));
338 auto const count = detail::slice_info<dim>::count();
339 auto offset = detail::slice_info<dim>::offset(old_extents[0], prefix);
340 for (auto const &p : particles) {
341 h5xx::write_dataset(dataset, op(p), h5xx::slice(offset, count));
342 // advance in the particle dimension
343 offset[1] += 1;
344 }
345}
346
347static void write_box(BoxGeometry const &box_geo, h5xx::dataset &dataset) {
348 auto const extents = static_cast<h5xx::dataspace>(dataset).extents();
349 extend_dataset(dataset, Vector2hs{1, 0});
350 h5xx::write_dataset(dataset, box_geo.length(),
351 h5xx::slice(Vector2hs{extents[0], 0}, Vector2hs{1, 3}));
352}
353
354static void write_le_off(LeesEdwardsBC const &lebc, h5xx::dataset &dataset) {
355 auto const extents = static_cast<h5xx::dataspace>(dataset).extents();
356 extend_dataset(dataset, Vector2hs{1, 0});
357 h5xx::write_dataset(dataset, Utils::Vector<double, 1>{lebc.pos_offset},
358 h5xx::slice(Vector2hs{extents[0], 0}, Vector2hs{1, 1}));
359}
360
361static void write_le_dir(LeesEdwardsBC const &lebc, h5xx::dataset &dataset) {
362 auto const shear_direction = static_cast<int>(lebc.shear_direction);
363 auto const extents = static_cast<h5xx::dataspace>(dataset).extents();
364 extend_dataset(dataset, Vector2hs{1, 0});
365 h5xx::write_dataset(dataset, Utils::Vector<int, 1>{shear_direction},
366 h5xx::slice(Vector2hs{extents[0], 0}, Vector2hs{1, 1}));
367}
368
369static void write_le_normal(LeesEdwardsBC const &lebc, h5xx::dataset &dataset) {
370 auto const shear_plane_normal = static_cast<int>(lebc.shear_plane_normal);
371 auto const extents = static_cast<h5xx::dataspace>(dataset).extents();
372 extend_dataset(dataset, Vector2hs{1, 0});
373 h5xx::write_dataset(dataset, Utils::Vector<int, 1>{shear_plane_normal},
374 h5xx::slice(Vector2hs{extents[0], 0}, Vector2hs{1, 1}));
375}
376
377void File::write(const ParticleRange &particles, double time, int step,
378 BoxGeometry const &box_geo) {
379 auto &datasets = *m_datasets;
380 if (m_fields & H5MD_OUT_BOX_L) {
381 write_box(box_geo, datasets["particles/atoms/box/edges/value"]);
382 }
383 auto const &lebc = box_geo.lees_edwards_bc();
384 if (m_fields & H5MD_OUT_LE_OFF) {
385 write_le_off(lebc, datasets["particles/atoms/lees_edwards/offset/value"]);
386 }
387 if (m_fields & H5MD_OUT_LE_DIR) {
388 write_le_dir(lebc,
389 datasets["particles/atoms/lees_edwards/direction/value"]);
390 }
391 if (m_fields & H5MD_OUT_LE_NORMAL) {
392 write_le_normal(lebc,
393 datasets["particles/atoms/lees_edwards/normal/value"]);
394 }
395
396 auto const n_part_local = static_cast<int>(particles.size());
397 // calculate count and offset
398 int prefix = 0;
399 // calculate prefix for write of the current process
400 BOOST_MPI_CHECK_RESULT(MPI_Exscan,
401 (&n_part_local, &prefix, 1, MPI_INT, MPI_SUM, m_comm));
402
403 auto const n_part_global =
404 boost::mpi::all_reduce(m_comm, n_part_local, std::plus<int>());
405
406 write_td_particle_property<2>(
407 prefix, n_part_global, particles, datasets["particles/atoms/id/value"],
408 [](auto const &p) { return Utils::Vector<int, 1>{p.id()}; });
409
410 {
411 h5xx::dataset &dataset = datasets["particles/atoms/id/value"];
412 auto const extents = static_cast<h5xx::dataspace>(dataset).extents();
414 datasets["particles/atoms/id/time"], Vector1hs{1},
415 Vector1hs{extents[0]}, Vector1hs{1});
417 datasets["particles/atoms/id/step"], Vector1hs{1},
418 Vector1hs{extents[0]}, Vector1hs{1});
419 }
420
421 if (m_fields & H5MD_OUT_TYPE) {
422 write_td_particle_property<2>(
423 prefix, n_part_global, particles,
424 datasets["particles/atoms/species/value"],
425 [](auto const &p) { return Utils::Vector<int, 1>{p.type()}; });
426 }
427 if (m_fields & H5MD_OUT_MASS) {
428 write_td_particle_property<2>(
429 prefix, n_part_global, particles,
430 datasets["particles/atoms/mass/value"],
431 [](auto const &p) { return Utils::Vector<double, 1>{p.mass()}; });
432 }
433 if (m_fields & H5MD_OUT_POS) {
434 write_td_particle_property<3>(
435 prefix, n_part_global, particles,
436 datasets["particles/atoms/position/value"],
437 [&](auto const &p) { return box_geo.folded_position(p.pos()); });
438 }
439 if (m_fields & H5MD_OUT_IMG) {
440 write_td_particle_property<3>(
441 prefix, n_part_global, particles,
442 datasets["particles/atoms/image/value"], [&](auto const &p) {
443 return box_geo.folded_image_box(p.pos(), p.image_box());
444 });
445 }
446 if (m_fields & H5MD_OUT_VEL) {
447 write_td_particle_property<3>(prefix, n_part_global, particles,
448 datasets["particles/atoms/velocity/value"],
449 [](auto const &p) { return p.v(); });
450 }
451 if (m_fields & H5MD_OUT_FORCE) {
452 write_td_particle_property<3>(prefix, n_part_global, particles,
453 datasets["particles/atoms/force/value"],
454 [](auto const &p) { return p.force(); });
455 }
456 if (m_fields & H5MD_OUT_CHARGE) {
457 write_td_particle_property<2>(
458 prefix, n_part_global, particles,
459 datasets["particles/atoms/charge/value"],
460 [](auto const &p) { return Utils::Vector<double, 1>{p.q()}; });
461 }
462 if (m_fields & H5MD_OUT_BONDS) {
463 write_connectivity(particles);
464 }
465}
466
467void File::write_connectivity(const ParticleRange &particles) {
468 MultiArray3i bond(boost::extents[0][0][0]);
469 for (auto const &p : particles) {
470 auto nbonds_local = static_cast<decltype(bond)::index>(bond.shape()[1]);
471 for (auto const b : p.bonds()) {
472 auto const partner_ids = b.partner_ids();
473 if (partner_ids.size() == 1u) {
474 bond.resize(boost::extents[1][nbonds_local + 1][2]);
475 bond[0][nbonds_local][0] = p.id();
476 bond[0][nbonds_local][1] = partner_ids[0];
477 nbonds_local++;
478 }
479 }
480 }
481
482 auto const n_bonds_local = static_cast<int>(bond.shape()[1]);
483 auto &datasets = *m_datasets;
484 int prefix_bonds = 0;
485 BOOST_MPI_CHECK_RESULT(
486 MPI_Exscan, (&n_bonds_local, &prefix_bonds, 1, MPI_INT, MPI_SUM, m_comm));
487 auto const n_bonds_total =
488 boost::mpi::all_reduce(m_comm, n_bonds_local, std::plus<int>());
489 auto const extents =
490 static_cast<h5xx::dataspace>(datasets["connectivity/atoms/value"])
491 .extents();
492 Vector3hs offset_bonds = {extents[0], static_cast<hsize_t>(prefix_bonds), 0};
493 Vector3hs count_bonds = {1, static_cast<hsize_t>(n_bonds_local), 2};
494 auto const n_bond_diff =
495 std::max(static_cast<hsize_t>(n_bonds_total), extents[1]) - extents[1];
496 Vector3hs change_extent_bonds = {1, static_cast<hsize_t>(n_bond_diff), 0};
497 write_dataset(bond, datasets["connectivity/atoms/value"], change_extent_bonds,
498 offset_bonds, count_bonds);
499}
500
501void File::flush() { m_h5md_file->flush(); }
502
503std::string File::file_path() const { return m_h5md_file->name(); }
504
505File::File(std::string file_path, std::string script_path,
506 std::vector<std::string> const &output_fields, std::string mass_unit,
507 std::string length_unit, std::string time_unit,
508 std::string force_unit, std::string velocity_unit,
509 std::string charge_unit)
510 : m_script_path(std::move(script_path)), m_mass_unit(std::move(mass_unit)),
511 m_length_unit(std::move(length_unit)), m_time_unit(std::move(time_unit)),
512 m_force_unit(std::move(force_unit)),
513 m_velocity_unit(std::move(velocity_unit)),
514 m_charge_unit(std::move(charge_unit)), m_comm(boost::mpi::communicator()),
515 m_fields(fields_list_to_bitfield(output_fields)),
516 m_h5md_file(std::make_unique<h5xx::file>()),
517 m_datasets(std::make_unique<decltype(m_datasets)::element_type>()),
518 m_h5md_specification(m_fields) {
519 init_file(file_path);
520}
521
522File::~File() = default;
523
524} /* namespace H5md */
525} /* namespace Writer */
Vector implementation and trait types for boost qvm interoperability.
auto folded_position(Utils::Vector3d const &pos) const
Calculate coordinates folded to primary simulation box.
Utils::Vector3d const & length() const
Box length.
LeesEdwardsBC const & lees_edwards_bc() const
auto folded_image_box(Utils::Vector3d const &pos, Utils::Vector3i const &image_box) const
Calculate image box of coordinates folded to primary simulation box.
A range of particles.
base_type::size_type size() const
void write(const ParticleRange &particles, double time, int step, BoxGeometry const &geometry)
Write data to the hdf5 file.
auto const & length_unit() const
Retrieve the set length unit.
auto const & time_unit() const
Retrieve the set time unit.
void close()
Method to perform the renaming of the temporary file from "filename" + ".bak" to "filename".
File(std::string file_path, std::string script_path, std::vector< std::string > const &output_fields, std::string mass_unit, std::string length_unit, std::string time_unit, std::string force_unit, std::string velocity_unit, std::string charge_unit)
Constructor.
auto const & force_unit() const
Retrieve the set force unit.
auto const & mass_unit() const
Retrieve the set mass unit.
auto const & charge_unit() const
Retrieve the set charge unit.
auto const & velocity_unit() const
Retrieve the set velocity unit.
std::string file_path() const
Retrieve the path to the hdf5 file.
auto const & script_path() const
Retrieve the path to the simulation script.
void flush()
Method to enforce flushing the buffer to disk.
Communicator communicator
static void write_script(std::string const &target, boost::filesystem::path const &script_path)
auto fields_list_to_bitfield(std::vector< std::string > const &fields)
Definition h5md_core.hpp:87
boost::multi_array< int, 3 > MultiArray3i
Definition h5md_core.cpp:63
Utils::Vector< hsize_t, 3 > Vector3hs
Definition h5md_core.cpp:66
static void write_le_dir(LeesEdwardsBC const &lebc, h5xx::dataset &dataset)
static void write_le_normal(LeesEdwardsBC const &lebc, h5xx::dataset &dataset)
static void backup_file(const std::string &from, const std::string &to)
Definition h5md_core.cpp:68
static void write_dataset(value_type const &data, h5xx::dataset &dataset, extent_type const &change_extent, extent_type const &offset, extent_type const &count)
Definition h5md_core.cpp:95
static std::vector< hsize_t > create_dims(hsize_t rank, hsize_t data_dim)
static void write_box(BoxGeometry const &box_geo, h5xx::dataset &dataset)
Utils::Vector< hsize_t, 2 > Vector2hs
Definition h5md_core.cpp:65
static void write_attributes(h5xx::file &h5md_file)
static void extend_dataset(h5xx::dataset &dataset, extent_type const &change_extent)
Definition h5md_core.cpp:83
static void write_le_off(LeesEdwardsBC const &lebc, h5xx::dataset &dataset)
static std::vector< hsize_t > create_chunk_dims(hsize_t rank, hsize_t data_dim)
void write_td_particle_property(hsize_t prefix, hsize_t n_part_global, ParticleRange const &particles, h5xx::dataset &dataset, Op op)
unsigned int shear_plane_normal
unsigned int shear_direction
bool is_compliant(std::string const &filename) const