29template <
typename T>
struct TypeID { };
31template <>
struct TypeID<float> {
static hid_t Get() {
return H5T_NATIVE_FLOAT; } };
32template <>
struct TypeID<double> {
static hid_t Get() {
return H5T_NATIVE_DOUBLE; } };
33template <>
struct TypeID<int32_t> {
static hid_t Get() {
return H5T_NATIVE_INT32; } };
34template <>
struct TypeID<uint64_t> {
static hid_t Get() {
return H5T_NATIVE_UINT64; } };
35template <>
struct TypeID<unsigned char> {
static hid_t Get() {
return H5T_NATIVE_UCHAR; } };
39hsize_t VTKHDF::Dims::TotalSize()
const
41 return std::accumulate(data.begin(), data.begin() + ndims, 1,
42 std::multiplies<hsize_t>());
46hid_t VTKHDF::GetTypeID() {
return TypeID<typename std::decay<T>::type>::Get(); }
48void VTKHDF::SetupVTKHDF()
50 vtk = H5Gcreate2(file,
"VTKHDF", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
53 const long version_buf[2] = {2, 2};
54 H5LTset_attribute_long(vtk,
".",
"Version", version_buf, 2);
59 const std::string type_str =
"UnstructuredGrid";
60 const hid_t type_id = H5Tcopy(H5T_C_S1);
61 H5Tset_size(type_id, type_str.size());
62 H5Tset_strpad(type_id, H5T_STR_NULLPAD);
64 const hid_t data_space = H5Screate(H5S_SCALAR);
65 const hid_t type_attr = H5Acreate2(vtk,
"Type", type_id, data_space,
66 H5P_DEFAULT, H5P_DEFAULT);
67 H5Awrite(type_attr, type_id, type_str.data());
74void VTKHDF::EnsureSteps()
77 if (steps != H5I_INVALID_HID) {
return; }
80 EnsureGroup(
"Steps", steps);
81 hid_t pd_offsets = H5I_INVALID_HID;
82 EnsureGroup(
"Steps/PointDataOffsets", pd_offsets);
86hid_t VTKHDF::EnsureDataset(hid_t
f,
const std::string &name, hid_t type,
89 const char *name_c = name.c_str();
91 const herr_t status = H5LTfind_dataset(
f, name_c);
97 const int ndims = dims.ndims;
101 Dims max_dims = dims;
102 max_dims[0] = H5S_UNLIMITED;
103 const hid_t fspace = H5Screate_simple(ndims, dims, max_dims);
106 size_t chunk_size_bytes = 1024 * 1024 / 2;
107 const size_t t_bytes = H5Tget_size(type);
108 for (
int i = 1; i < ndims; ++i)
111 chunk_size_bytes /= dims[i];
113 chunk[0] = chunk_size_bytes / t_bytes;
114 const hid_t dcpl = H5Pcreate(H5P_DATASET_CREATE);
115 H5Pset_chunk(dcpl, ndims, chunk);
116 if (compression_level >= 0)
118 H5Pset_shuffle(dcpl);
119 H5Pset_deflate(dcpl, compression_level);
122 const hid_t d = H5Dcreate2(
f, name_c, type, fspace, H5P_DEFAULT,
130 const hid_t d = H5Dopen2(
f, name_c, H5P_DEFAULT);
133 Dims old_dims(dims.ndims);
134 const hid_t dspace = H5Dget_space(d);
135 const int ndims_dset = H5Sget_simple_extent_ndims(dspace);
136 MFEM_VERIFY(ndims_dset == dims.ndims,
"");
137 H5Sget_simple_extent_dims(dspace, old_dims, NULL);
139 dims[0] += old_dims[0];
140 H5Dset_extent(d, dims);
147 MFEM_ABORT(
"Error finding HDF5 dataset " << name);
151void VTKHDF::EnsureGroup(
const std::string &name, hid_t &group)
153 if (group != H5I_INVALID_HID) {
return; }
155 const char *cname = name.c_str();
156 const htri_t found = H5Lexists(vtk, cname, H5P_DEFAULT);
161 group = H5Gopen(vtk, cname, H5P_DEFAULT);
165 group = H5Gcreate2(vtk, cname, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
169 MFEM_ABORT(
"Error finding HDF5 group " << name);
174void VTKHDF::AppendParData(hid_t
f,
const std::string &name, hsize_t locsize,
175 hsize_t offset, Dims globsize, T *data)
177 const int ndims = globsize.ndims;
178 Dims dims = globsize;
179 const hid_t d = EnsureDataset(
f, name, GetTypeID<T>(), dims);
182 const hid_t dspace = H5Dget_space(d);
184 start[0] = dims[0] - globsize[0] + offset;
187 for (
int i = 1; i < ndims; ++i) { count[i] = globsize[i]; }
189 H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, NULL, count, NULL);
190 H5Dwrite(d, GetTypeID<T>(), H5S_BLOCK, dspace, dxpl, data);
197std::vector<T> VTKHDF::AllGather(
const T loc)
const
199 std::vector<T> all(mpi_size);
203 const MPI_Datatype
type = MPITypeMap<T>::mpi_type;
204 MPI_Allgather(&loc, 1, type, all.data(), 1, type, comm);
214VTKHDF::OffsetTotal VTKHDF::GetOffsetAndTotal(
const size_t loc)
const
216 const auto all = AllGather(uint64_t(loc));
219 for (
int i = 0; i < mpi_rank; ++i)
223 size_t total = offset;
224 for (
int i = mpi_rank; i < mpi_size; ++i)
229 return {offset, total};
233VTKHDF::OffsetTotal VTKHDF::AppendParVector(
234 hid_t
f,
const std::string &name,
const std::vector<T> &data, Dims dims)
236 const size_t locsize = data.size();
237 const auto offset_total = GetOffsetAndTotal(locsize);
238 const auto offset = offset_total.offset;
239 const auto total = offset_total.total;
242 for (
int i = 1; i < dims.ndims; ++i) { m *= dims[i]; }
245 AppendParData(
f, name, locsize/m, offset/m, dims, data.data());
247 return {offset/m, total/m};
250bool VTKHDF::UsingMpi()
const
253 return comm != MPI_COMM_NULL;
259void VTKHDF::Barrier()
const
262 if (UsingMpi()) { MPI_Barrier(comm); }
267std::vector<T> VTKHDF::ReadDataset(
const std::string &name)
const
269 const char *cname = name.c_str();
271 H5LTget_dataset_ndims(vtk, cname, &ndims);
273 H5LTget_dataset_info(vtk, cname, dims,
nullptr,
nullptr);
274 std::vector<T> vals(dims.TotalSize());
275 H5LTread_dataset(vtk, cname, GetTypeID<T>(), vals.data());
280T VTKHDF::ReadValue(
const std::string &name, hsize_t
index)
const
282 const char *cname = name.c_str();
285 H5LTget_dataset_ndims(vtk, cname, &ndims);
287 const hid_t d = H5Dopen(vtk, cname, H5P_DEFAULT);
290 const hid_t dspace = H5Dget_space(d);
294 for (
int i = 0; i < ndims; ++i) { count[i] = 1; }
296 H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, NULL, count, NULL);
297 const hid_t memspace = H5Screate_simple(ndims, count, count);
300 H5Dread(d, GetTypeID<T>(), memspace, dspace, dxpl, &value);
309void VTKHDF::TruncateDataset(
const std::string &name, hsize_t size)
311 const hid_t d = H5Dopen2(vtk, name.c_str(), H5P_DEFAULT);
312 const hid_t dspace = H5Dget_space(d);
313 const int ndims = H5Sget_simple_extent_ndims(dspace);
315 H5Sget_simple_extent_dims(dspace, dims, NULL);
318 H5Dset_extent(d, dims);
322void VTKHDF::Truncate(
const real_t t)
326 const std::vector<real_t> tvals = ReadDataset<real_t>(
"Steps/Values");
327 auto it = std::find_if(tvals.begin(), tvals.end(), [t](
real_t t2) { return t2 >= t; });
333 H5LTget_dataset_info(vtk,
"NumberOfCells", dims,
nullptr,
nullptr);
334 MFEM_VERIFY(dims[0] == tvals.size() * mpi_size,
"Incompatible VTKHDF sizes.");
338 const ptrdiff_t i = std::distance(tvals.begin(), it);
341 const bool truncate = it != tvals.end();
345 H5LTset_attribute_ulong(vtk,
"Steps",
"NSteps", &nsteps, 1);
353 point_offsets.next = ReadValue<hsize_t>(
"Steps/PointOffsets", i - 1);
354 cell_offsets.next = ReadValue<hsize_t>(
"Steps/CellOffsets", i - 1);
355 connectivity_offsets.next =
356 ReadValue<hsize_t>(
"Steps/ConnectivityIdOffsets", i - 1);
358 for (
int part = 0; part < mpi_size; ++part)
360 const hsize_t p_i = ReadValue<hsize_t>(
"Steps/PartOffsets", i - 1 + part);
361 npoints += ReadValue<hsize_t>(
"NumberOfPoints", p_i);
362 cell_offsets.next += ReadValue<hsize_t>(
"NumberOfCells", p_i);
363 connectivity_offsets.next +=
364 ReadValue<hsize_t>(
"NumberOfConnectivityIds", p_i);
366 point_offsets.next += npoints;
370 const hid_t g = H5Gopen2(vtk,
"Steps/PointDataOffsets", H5P_DEFAULT);
371 if (g != H5I_INVALID_HID)
373 std::vector<std::string> names;
374 auto itfn = [](hid_t,
const char *name,
const H5L_info2_t*,
void *data)
376 auto names_ptr =
static_cast<std::vector<std::string>*
>(data);
377 names_ptr->emplace_back(name);
380 H5Literate2(g, H5_INDEX_NAME, H5_ITER_NATIVE,
nullptr, itfn, &names);
383 for (
auto name : names)
385 const std::string dset_name =
"Steps/PointDataOffsets/" + name;
389 offset = ReadValue<hsize_t>(dset_name, i - 1) + npoints;
391 point_data_offsets[name].next = offset;
394 TruncateDataset(dset_name, nsteps);
395 TruncateDataset(
"PointData/" + name, offset);
402 TruncateDataset(
"Steps/Values", nsteps);
403 TruncateDataset(
"Steps/PartOffsets", nsteps);
404 TruncateDataset(
"Steps/PointOffsets", nsteps);
405 TruncateDataset(
"Steps/CellOffsets", nsteps);
406 TruncateDataset(
"Steps/ConnectivityIdOffsets", nsteps);
408 TruncateDataset(
"NumberOfCells", nsteps * mpi_size);
409 TruncateDataset(
"NumberOfConnectivityIds", nsteps * mpi_size);
410 TruncateDataset(
"NumberOfPoints", nsteps * mpi_size);
412 TruncateDataset(
"CellData/attribute", cell_offsets.next);
413 TruncateDataset(
"Types", cell_offsets.next);
414 TruncateDataset(
"Points", point_offsets.next);
415 TruncateDataset(
"Connectivity", connectivity_offsets.next);
416 TruncateDataset(
"Offsets", cell_offsets.next + nsteps * mpi_size);
420void VTKHDF::CreateFile(
const std::string &filename, Restart restart)
424 bool file_exists = mpi_rank == 0 && [&filename]()
426 std::ifstream
f(filename);
433 MPI_Allreduce(MPI_IN_PLACE, &file_exists, 1, MPI_CXX_BOOL, MPI_LOR, comm);
441 H5Pset_file_locking(fapl,
false,
true);
443 file = H5Fopen(filename.c_str(), H5F_ACC_RDWR, fapl);
444 vtk = H5Gopen(file,
"VTKHDF", H5P_DEFAULT);
445 Truncate(restart.time);
453 std::remove(filename.c_str());
455 file = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
462 fapl = H5Pcreate(H5P_FILE_ACCESS);
463 CreateFile(filename, restart);
466#ifdef MFEM_PARALLEL_HDF5
468static int MpiCommSize(MPI_Comm comm)
471 MPI_Comm_size(comm, &comm_size);
475static int MpiCommRank(MPI_Comm comm)
478 MPI_Comm_rank(comm, &rank);
484 mpi_size(MpiCommSize(comm)),
485 mpi_rank(MpiCommRank(comm))
488 fapl = H5Pcreate(H5P_FILE_ACCESS);
489 const MPI_Info info = MPI_INFO_NULL;
490 H5Pset_fapl_mpio(fapl, comm, info);
492 dxpl = H5Pcreate(H5P_DATASET_XFER);
493 H5Pset_dxpl_mpio(dxpl, H5FD_MPIO_COLLECTIVE);
495 CreateFile(filename, restart);
501void VTKHDF::AppendValue(
const hid_t
f,
const std::string &name, T value)
503 const hsize_t locsize = (mpi_rank == 0) ? 1 : 0;
504 AppendParData(
f, name, locsize, 0, Dims({1}), &value);
513 H5LTset_attribute_ulong(steps,
".",
"NSteps", &nsteps, 1);
515 AppendValue(steps,
"Values", t);
516 AppendValue(steps,
"PartOffsets", part_offset);
517 AppendValue(steps,
"PointOffsets", point_offsets.current);
518 AppendValue(steps,
"CellOffsets", cell_offsets.current);
519 AppendValue(steps,
"ConnectivityIdOffsets", connectivity_offsets.current);
521 if (!point_data_offsets.empty())
523 const hid_t g = H5Gopen2(steps,
"PointDataOffsets", H5P_DEFAULT);
524 for (
const auto &pd : point_data_offsets)
526 const char *name = pd.first.c_str();
527 AppendValue(g, name, pd.second.current);
533template <
typename FP_T>
544 ref = nodal_space->GetMaxElementOrder();
549 const Dims mpi_dims({mpi_size});
552 if (!mesh_id.HasChanged(mesh, high_order, ref))
562 const hsize_t zero = 0;
563 AppendParData(vtk,
"NumberOfPoints", 1, mpi_rank, mpi_dims, &zero);
564 AppendParData(vtk,
"NumberOfCells", 1, mpi_rank, mpi_dims, &zero);
565 AppendParData(vtk,
"NumberOfConnectivityIds", 1, mpi_rank, mpi_dims, &zero);
566 const int zero_int = 0;
567 AppendParData(vtk,
"Offsets", 1, mpi_rank, mpi_dims, &zero_int);
572 mesh_id.Set(mesh, high_order, ref);
575 part_offset = nsteps * mpi_size;
578 const int ref_0 = high_order ? 1 : ref;
586 auto get_nv = [&](
int e)
591 auto get_ne_ref = [&](
int e,
int r)
593 return get_ref_geom(e, r).RefGeoms.Size() / get_nv(e);
598 std::vector<FP_T> points;
602 const int ne = mesh.
GetNE();
603 for (
int e = 0; e < ne; e++)
610 points.reserve(np * 3);
614 for (
int e = 0; e < ne; ++e)
620 for (
int i = 0; i < pmat.
Width(); i++)
622 points.push_back(FP_T(pmat(0,i)));
623 if (pmat.
Height() > 1) { points.push_back(FP_T(pmat(1,i))); }
624 else { points.push_back(0.0); }
625 if (pmat.
Height() > 2) { points.push_back(FP_T(pmat(2,i))); }
626 else { points.push_back(0.0); }
631 const int ne_0 = mesh.
GetNE();
632 const hsize_t ne = high_order ? ne_0 : ne_ref;
634 AppendParData(vtk,
"NumberOfPoints", 1, mpi_rank, mpi_dims, &np);
635 AppendParData(vtk,
"NumberOfCells", 1, mpi_rank, mpi_dims, &ne);
641 auto point_offset_total = AppendParVector(vtk,
"Points", points, Dims({0, 3}));
642 point_offsets.Update(point_offset_total.total);
646 const auto e_offset_total = GetOffsetAndTotal(ne);
647 const auto e_offset = e_offset_total.offset;
648 const auto ne_total = e_offset_total.total;
650 cell_offsets.Update(ne_total);
654 std::vector<int> offsets(ne + 1);
655 std::vector<int> connectivity;
661 for (
int e = 0; e < int(ne); ++e)
666 const int nnodes = local_connectivity.
Size();
667 for (
int i = 0; i < nnodes; ++i)
669 connectivity.push_back(off + local_connectivity[i]);
673 offsets.back() = off;
679 for (
int e = 0; e < ne_0; ++e)
682 const int nv = get_nv(e);
685 for (
int r = 0; r < rg.
Size(); ++e_ref)
687 offsets[e_ref] = off;
690 for (
int k = 0; k < nv; ++k, ++r)
692 connectivity.push_back(off_0 + rg[
p ? (r - k +
p[k]) : r]);
697 offsets.back() = off;
700 const hsize_t n = connectivity.size();
701 AppendParData(vtk,
"NumberOfConnectivityIds", 1, mpi_rank, mpi_dims, &n);
703 auto connectivity_offset_total
704 = AppendParVector(vtk,
"Connectivity", connectivity);
705 connectivity_offsets.Update(connectivity_offset_total.total);
707 AppendParData(vtk,
"Offsets", ne + 1, e_offset + mpi_rank,
708 Dims({ne_total + mpi_size}), offsets.data());
714 std::vector<unsigned char> cell_types(ne);
715 const int *vtk_geom_map =
718 for (
int e = 0; e < ne_0; ++e)
720 const int ne_ref_e = get_ne_ref(e, ref_0);
721 for (
int i = 0; i < ne_ref_e; ++i, ++e_ref)
723 cell_types[e_ref] =
static_cast<unsigned char>(
727 AppendParData(vtk,
"Types", ne, e_offset, Dims({ne_total}),
734 EnsureGroup(
"CellData", cell_data);
735 std::vector<int> attributes(ne);
737 for (
int e = 0; e < ne_0; ++e)
740 const int ne_ref_e = get_ne_ref(e, ref_0);
741 for (
int i = 0; i < ne_ref_e; ++i, ++e_ref)
743 attributes[e_ref] = attr;
746 AppendParData(cell_data,
"attribute", ne, e_offset, Dims({ne_total}),
752template <
typename FP_T>
756 EnsureGroup(
"PointData", point_data);
760 MFEM_VERIFY(!mesh_id.HasChanged(mesh),
"Mesh must be saved first");
761 const int ref = mesh_id.GetRefinementLevel();
765 std::vector<FP_T> point_values(vdim * last_np);
768 for (
int e = 0; e < mesh.
GetNE(); e++)
773 for (
int i = 0; i < vec_val.
Width(); ++i)
775 for (
int vd = 0; vd < vdim; ++vd)
777 point_values[off] = FP_T(vec_val(vd, i));
783 Dims dims(vdim == 1 ? 1 : 2);
784 if (vdim > 1) { dims[1] = vdim; }
785 auto offset_total = AppendParVector(point_data, name, point_values, dims);
786 point_data_offsets[name].Update(offset_total.total);
791 H5Fflush(file, H5F_SCOPE_GLOBAL);
796 if (steps != H5I_INVALID_HID) { H5Gclose(steps); }
797 if (cell_data != H5I_INVALID_HID) { H5Gclose(cell_data); }
798 if (point_data != H5I_INVALID_HID) { H5Gclose(point_data); }
799 if (dxpl != H5P_DEFAULT) { H5Pclose(dxpl); }
800 if (vtk != H5I_INVALID_HID) { H5Gclose(vtk); }
801 if (fapl != H5I_INVALID_HID) { H5Pclose(fapl); }
802 if (file != H5I_INVALID_HID) { H5Fclose(file); }
int Size() const
Return the logical size of the array.
Data type dense matrix using column-major storage.
Mesh * GetMesh() const
Returns the mesh.
RefinedGeometry * Refine(Geometry::Type Geom, int Times, int ETimes=1)
static const int NumVerts[NumGeom]
Class for grid function - Vector with associated FE space.
FiniteElementSpace * FESpace()
int VectorDim() const
Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying fes.
void GetVectorValues(int i, const IntegrationRule &ir, DenseMatrix &vals, DenseMatrix &tr) const
int GetNPoints() const
Returns the number of the points in the integration rule.
Geometry::Type GetElementGeometry(int i) const
int GetAttribute(int i) const
Return the attribute of element i.
const FiniteElementSpace * GetNodalFESpace() const
int GetNE() const
Returns number of elements.
void GetElementTransformation(int i, IsoparametricTransformation *ElTr) const
Builds the transformation defining the i-th element in ElTr. ElTr must be allocated in advance and wi...
Geometry::Type GetElementBaseGeometry(int i) const
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
void SaveGridFunction(const GridFunction &gf, const std::string &name)
Save the grid function with the given name, appending as a new time step.
void SaveMesh(const Mesh &mesh, bool high_order=true, int ref=-1)
Save the mesh, appending as a new time step.
void UpdateSteps(real_t t)
Update the time step data after saving the mesh and grid functions.
VTKHDF(const std::string &filename, Restart restart=Restart::Disabled())
Create a new VTKHDF file for serial I/O.
void Flush()
Flush the file.
int index(int i, int j, int nx, int ny)
MFEM_HOST_DEVICE constexpr auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.
GeometryRefiner GlobGeometryRefiner
void CreateVTKElementConnectivity(Array< int > &con, Geometry::Type geom, int ref)
Create the VTK element connectivity array for a given element geometry and refinement level.
std::function< real_t(const Vector &)> f(real_t mass_coeff)
real_t p(const Vector &x, real_t t)
static const int HighOrderMap[Geometry::NUM_GEOMETRIES]
Map from MFEM's Geometry::Type to arbitrary-order Lagrange VTK geometries.
static const int * VertexPermutation[Geometry::NUM_GEOMETRIES]
Permutation from MFEM's vertex ordering to VTK's vertex ordering.
static const int Map[Geometry::NUM_GEOMETRIES]
Map from MFEM's Geometry::Type to linear VTK geometries.
Helper used in VTKHDF::VTKHDF for enabling/disabling restart mode.