Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
VtkHdfV2PostProcessor.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* VtkHdfV2PostProcessor.cc (C) 2000-2026 */
9/* */
10/* Post-processing in VTK HDF format. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/Collection.h"
15#include "arcane/utils/Enumerator.h"
16#include "arcane/utils/Iostream.h"
17#include "arcane/utils/StringBuilder.h"
18#include "arcane/utils/IOException.h"
19#include "arcane/utils/FixedArray.h"
20#include "arcane/utils/MemoryView.h"
21
22#include "arcane/core/PostProcessorWriterBase.h"
23#include "arcane/core/Directory.h"
24#include "arcane/core/FactoryService.h"
25#include "arcane/core/IDataWriter.h"
26#include "arcane/core/IData.h"
27#include "arcane/core/IItemFamily.h"
28#include "arcane/core/MeshKind.h"
29#include "arcane/core/VariableCollection.h"
30#include "arcane/core/IParallelMng.h"
31#include "arcane/core/IMesh.h"
32#include "arcane/core/internal/IParallelMngInternal.h"
33#include "arcane/core/internal/VtkCellTypes.h"
34#include "arcane/core/internal/GatherGroup.h"
35
36#include "arcane/core/materials/IMeshMaterialMng.h"
37#include "arcane/core/materials/IMeshEnvironment.h"
38
39#include "arcane/hdf5/Hdf5Utils.h"
40#include "arcane/hdf5/VtkHdfV2PostProcessor_axl.h"
41
42#include <map>
43
44// This format is described on the following web page:
45//
46// https://kitware.github.io/vtk-examples/site/VTKFileFormats/#hdf-file-formats
47//
48// Format 2.0 with integrated temporal evolution support is only available
49// in the VTK master branch starting from April 2023.
50
51/*---------------------------------------------------------------------------*/
52/*---------------------------------------------------------------------------*/
53
54// TODO: Add verification test for saved values
55
56// TODO: Look into saving uniqueId() (via vtkOriginalCellIds)
57
58// TODO: Look into how to avoid saving the mesh at every iteration if it
59// does not change.
60
61// TODO: Look into compression
62
63// TODO: handle 2D variables
64
65// TODO: outside of HDF5, implement a mechanism that groups several parts
66// of the cell into one. This will allow reducing the number of ghost cells
67// and using MPI/IO in hybrid mode.
68
69/*---------------------------------------------------------------------------*/
70/*---------------------------------------------------------------------------*/
71
72namespace Arcane
73{
74using namespace Hdf5Utils;
75using namespace Materials;
76
77namespace
78{
79 template <typename T> Span<const T>
80 asConstSpan(const T* v)
81 {
82 return Span<const T>(v, 1);
83 }
84} // namespace
85
86/*---------------------------------------------------------------------------*/
87/*---------------------------------------------------------------------------*/
88
89class VtkHdfV2DataWriter
90: public TraceAccessor
91, public IDataWriter
92{
93 public:
94
101 struct DatasetGroupAndName
102 {
103 public:
104
105 DatasetGroupAndName(HGroup& group_, const String& name_)
106 : group(group_)
107 , name(name_)
108 {}
109
110 public:
111
112 HGroup& group;
113 String name;
114 };
115
127 struct DatasetInfo
128 {
129 DatasetInfo() = default;
130 explicit DatasetInfo(const String& name)
131 : m_name(name)
132 {}
133 DatasetInfo(HGroup& _group, const String& name)
134 : m_group(&_group)
135 , m_name(name)
136 {}
137 bool isNull() const { return m_name.null(); }
138
139 HGroup* group() const { return m_group; }
140 const String& name() const { return m_name; }
142 Int64 offset() const { return m_offset; }
143 void setOffset(Int64 v) { m_offset = v; }
144 friend bool operator<(const DatasetInfo& s1, const DatasetInfo& s2)
145 {
146 return (s1.m_name < s2.m_name);
147 }
148
149 private:
150
151 HGroup* m_group = nullptr;
152 String m_name;
153 Int64 m_offset = -1;
154 };
155
158 {
159 public:
160
161 void setTotalSize(Int64 v) { m_total_size = v; }
162 void setSize(Int64 v) { m_size = v; }
163 void setOffset(Int64 v) { m_offset = v; }
164
165 Int64 totalSize() const { return m_total_size; }
166 Int64 size() const { return m_size; }
167 Int64 offset() const { return m_offset; }
168
169 private:
170
177 };
178
180 struct ItemGroupCollectiveInfo
181 {
182 public:
183
184 explicit ItemGroupCollectiveInfo(const ItemGroup& g)
185 : m_item_group(g)
186 {}
187
188 public:
189
190 void setWritePartInfo(const WritePartInfo& part_info) { m_write_part_info = part_info; }
191 const WritePartInfo& writePartInfo() const { return m_write_part_info; }
192
193 public:
194
199 };
200
204 struct DataInfo
205 {
206 public:
207
208 DataInfo(const DatasetGroupAndName& dname, const DatasetInfo& dataset_info)
209 : dataset(dname)
210 , m_dataset_info(dataset_info)
211 {
212 }
213 DataInfo(const DatasetGroupAndName& dname, const DatasetInfo& dataset_info,
214 ItemGroupCollectiveInfo* group_info)
215 : dataset(dname)
216 , m_dataset_info(dataset_info)
217 , m_group_info(group_info)
218 {
219 }
220 DataInfo(HGroup& dataset_group, const String& dataset_name, const DatasetInfo& dataset_info)
221 : dataset(dataset_group, dataset_name)
222 , m_dataset_info(dataset_info)
223 {
224 }
225 DataInfo(HGroup& dataset_group, const String& dataset_name, const DatasetInfo& dataset_info,
226 ItemGroupCollectiveInfo* group_info)
227 : dataset(dataset_group, dataset_name)
228 , m_dataset_info(dataset_info)
229 , m_group_info(group_info)
230 {
231 }
232
233 public:
234
235 DatasetInfo datasetInfo() const { return m_dataset_info; }
236
237 public:
238
239 DatasetGroupAndName dataset;
240 DatasetInfo m_dataset_info;
241 ItemGroupCollectiveInfo* m_group_info = nullptr;
242 };
243
244 public:
245
246 VtkHdfV2DataWriter(IMesh* mesh, const ItemGroupCollection& groups, bool is_collective_io);
247
248 public:
249
250 void beginWrite(const VariableCollection& vars) override;
251 void endWrite() override;
252 void setMetaData(const String& meta_data) override;
253 void write(IVariable* var, IData* data) override;
254
255 public:
256
257 void setTimes(RealConstArrayView times) { m_times = times; }
258 void setDirectoryName(const String& dir_name) { m_directory_name = dir_name; }
259 void setMaxWriteSize(Int64 v) { m_max_write_size = v; }
260
261 private:
262
264 IMesh* m_mesh = nullptr;
265
268
271
274
277
280
283
284 HGroup m_top_group;
285 HGroup m_cell_data_group;
286 HGroup m_node_data_group;
287
288 HGroup m_steps_group;
289 HGroup m_point_data_offsets_group;
290 HGroup m_cell_data_offsets_group;
291 HGroup m_field_data_offsets_group;
292
293 bool m_is_parallel = false;
294 bool m_is_collective_io = false;
295 bool m_is_first_call = false;
296 bool m_is_writer = false;
297
307
308 Int32 m_writer = 0;
309
310 DatasetInfo m_cell_offset_info;
311 DatasetInfo m_point_offset_info;
312 DatasetInfo m_connectivity_offset_info;
313 DatasetInfo m_face_connectivity_offset_info;
314 DatasetInfo m_polyhedron_to_face_offset_info;
315 DatasetInfo m_face_offsets_offset_info;
316 DatasetInfo m_offset_for_cell_offset_info;
317 DatasetInfo m_offset_for_face_offset_info;
318 DatasetInfo m_offset_for_polyhedron_offset_info;
319 DatasetInfo m_part_offset_info;
320 DatasetInfo m_time_offset_info;
321 std::map<DatasetInfo, Int64> m_offset_info_list;
322
323 StandardTypes m_standard_types{ false };
324
325 ItemGroupCollectiveInfo m_all_cells_info;
326 ItemGroupCollectiveInfo m_all_nodes_info;
328
329 GatherGroupInfo m_all_cells_gather_group_info;
330 GatherGroupInfo m_all_nodes_gather_group_info;
331 UniqueArray<Ref<GatherGroupInfo>> m_gather_info_materials_groups;
332
340
341 private:
342
343 void _addInt64ArrayAttribute(Hid& hid, const char* name, Span<const Int64> values);
344 void _addStringAttribute(Hid& hid, const char* name, const String& value);
345
346 template <typename DataType> void
347 _writeDataSet1D(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values);
348 template <typename DataType> void
349 _writeDataSet1DUsingCollectiveIO(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values);
350 template <typename DataType> void
351 _writeDataSet1DCollective(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values);
352 template <typename DataType> void
353 _writeDataSet2D(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values);
354 template <typename DataType> void
355 _writeDataSet2DUsingCollectiveIO(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values);
356 template <typename DataType> void
357 _writeDataSet2DCollective(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values);
358 template <typename DataType> void
359 _writeBasicTypeDataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data);
360 void _writeReal3Dataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data);
361 void _writeReal2Dataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data);
362
363 String _getFileName()
364 {
365 StringBuilder sb(m_mesh->name());
366 sb += ".hdf";
367 return sb.toString();
368 }
369 template <typename DataType> void
370 _writeDataSetGeneric(const DataInfo& data_info, GatherGroupInfo* gather_info, Int32 nb_dim,
371 Int64 dim1_size, Int64 dim2_size, const DataType* values_data,
372 bool is_collective);
373 void _writeDataSetGeneric(const DataInfo& data_info, GatherGroupInfo* gather_info, Int32 nb_dim,
374 Int64 dim1_size, Int64 dim2_size, ConstMemoryView values_data,
375 const hid_t hdf_datatype_type, bool is_collective);
376 void _addInt64Attribute(Hid& hid, const char* name, Int64 value);
377 Int64 _readInt64Attribute(Hid& hid, const char* name);
378 void _openOrCreateGroups();
379 void _closeGroups();
380 void _readAndSetOffset(DatasetInfo& offset_info, Int32 wanted_step);
381 void _initializeOffsets();
382 void _initializeItemGroupCollectiveInfos(ItemGroupCollectiveInfo& group_info, GatherGroupInfo& gather_info);
383 WritePartInfo _computeWritePartInfo(Int64 local_size);
384 void _writeConstituentsGroups();
385};
386
387/*---------------------------------------------------------------------------*/
388/*---------------------------------------------------------------------------*/
389
390VtkHdfV2DataWriter::
391VtkHdfV2DataWriter(IMesh* mesh, const ItemGroupCollection& groups, bool is_collective_io)
392: TraceAccessor(mesh->traceMng())
393, m_mesh(mesh)
394, m_groups(groups)
395, m_is_collective_io(is_collective_io)
396, m_all_cells_info(mesh->allCells())
397, m_all_nodes_info(mesh->allNodes())
398, m_all_cells_gather_group_info(mesh->parallelMng(), is_collective_io)
399, m_all_nodes_gather_group_info(mesh->parallelMng(), is_collective_io)
400{
401}
402
403/*---------------------------------------------------------------------------*/
404/*---------------------------------------------------------------------------*/
405
406void VtkHdfV2DataWriter::
407beginWrite(const VariableCollection& vars)
408{
409 ARCANE_UNUSED(vars);
410
411 // Retrieve the material manager if it exists
413
414 IParallelMng* pm = m_mesh->parallelMng();
415 const Int32 nb_rank = pm->commSize();
416 m_is_parallel = nb_rank > 1;
417
418 Int32 time_index = m_times.size();
419 const bool is_first_call = (time_index < 2);
420 m_is_first_call = is_first_call;
421 if (is_first_call)
422 info() << "WARNING: The 'VtkHdfV2' implementation is experimental";
423
424 String filename = _getFileName();
425
426 Directory dir(m_directory_name);
427
428 m_full_filename = dir.file(filename);
429 info(4) << "VtkHdfV2DataWriter::beginWrite() file=" << m_full_filename;
430
431 HInit();
432 HInit::useMutex(pm->isThreadImplementation(), pm);
433
434 // It is possible to use the collective mode of HDF5 via MPI-IO in the following cases:
435 // * Hdf5 was compiled with MPI,
436 // * we are in pure MPI mode (neither shared memory mode nor hybrid mode).
437 m_is_collective_io = m_is_collective_io && (pm->isParallel() && HInit::hasParallelHdf5());
438 if (pm->isThreadImplementation() && !pm->isHybridImplementation())
439 m_is_collective_io = false;
440
441 if (is_first_call) {
442 info() << "VtkHdfV2DataWriter: using collective MPI/IO ?=" << m_is_collective_io;
443 info() << "VtkHdfV2DataWriter: max_write_size (kB) =" << m_max_write_size;
444 info() << "VtkHdfV2DataWriter: has_material?=" << (m_material_mng != nullptr);
445 }
446
447 bool is_master_io = pm->isMasterIO();
448
449 // True if we must participate in the writes
450 // If we use MPI/IO with HDF5, all
451 // ranks must perform all write operations to guarantee
452 // metadata consistency.
453 if (m_is_collective_io) {
454 m_writer = pm->_internalApi()->masterParallelIORank();
455 m_is_writer = (m_writer == pm->commRank());
456 }
457 else {
458 m_writer = pm->masterIORank();
459 m_is_writer = is_master_io;
460 }
461
462 // Indicates that we are using MPI/IO if requested
463 HProperty plist_id;
464 if (m_is_collective_io && m_is_writer)
465 plist_id.createFilePropertyMPIIO(pm);
466
467 // Even with MPI-IO, only one proc must create the directory.
468 if (is_first_call && is_master_io)
469 dir.createDirectory();
470
471 if (m_is_collective_io)
472 pm->barrier();
473
474 if (m_is_writer) {
475 m_standard_types.initialize();
476
477 if (is_first_call)
478 m_file_id.openTruncate(m_full_filename, plist_id.id());
479 else
480 m_file_id.openAppend(m_full_filename, plist_id.id());
481
482 _openOrCreateGroups();
483
484 if (is_first_call) {
485 std::array<Int64, 2> version = { 2, 5 };
486 _addInt64ArrayAttribute(m_top_group, "Version", version);
487 _addStringAttribute(m_top_group, "Type", "UnstructuredGrid");
488 }
489 }
490
491 // Initializes collective information on cell and node groups
492 _initializeItemGroupCollectiveInfos(m_all_cells_info, m_all_cells_gather_group_info);
493 _initializeItemGroupCollectiveInfos(m_all_nodes_info, m_all_nodes_gather_group_info);
494
495 CellGroup all_cells = m_mesh->allCells();
496 NodeGroup all_nodes = m_mesh->allNodes();
497
498 const Int32 nb_cell = all_cells.size();
499 const Int32 nb_node = all_nodes.size();
500
501 Int64 total_nb_connected_node = 0;
502 ENUMERATE_ (Cell, icell, all_cells) {
503 Cell cell = *icell;
504 total_nb_connected_node += cell.nodeIds().size();
505 }
506
507 // For the offsets, the array size is equal
508 // to the number of cells plus 1.
509 UniqueArray<Int64> cells_connectivity(total_nb_connected_node);
510 UniqueArray<Int64> cells_offset(nb_cell + 1);
511 UniqueArray<unsigned char> cells_ghost_type(nb_cell);
512 UniqueArray<unsigned char> cells_type(nb_cell);
513 UniqueArray<Int64> cells_uid(nb_cell);
514
515 // Arrays for the polyhedron cells.
516 //
517 // The 'faces_connectivity' array contains the list of nodes of each face
518 // of each polyhedron cell and 'faces_offset' contains the offset of each
519 // face in 'faces_connectivity' (its size is equal to the number of faces
520 // plus 1). Each polyhedron cell has its own list of faces (a face shared
521 // by two cells is saved twice because VTK requires that the nodes of a
522 // face are ordered so that the face normal points outwards from the cell).
523 //
524 // The 'polyhedron_to_faces' array contains for each cell the list of its
525 // faces (this list is empty for cells that are not polyhedron cells) and
526 // 'polyhedron_offset' contains the offset of each cell in
527 // 'polyhedron_to_faces' (its size is equal to the number of cells plus 1).
528 UniqueArray<Int64> faces_connectivity;
529 UniqueArray<Int64> faces_offset;
530 UniqueArray<Int64> polyhedron_to_faces;
531 UniqueArray<Int64> polyhedron_offset(nb_cell + 1);
532 faces_offset.add(0);
533 polyhedron_offset[0] = 0;
534 Int64 nb_face = 0;
535
536 // In a polyhedral mesh, all the cells with at least one face are
537 // considered as polyhedron cells. For the other meshes, only the cells
538 // with a polyhedron type are considered as polyhedron cells.
539 const bool mesh_is_polyhedral = (m_mesh->meshKind().meshStructure() == eMeshStructure::Polyhedral);
540
541 cells_offset[0] = 0;
542 {
543 Int32 connected_node_index = 0;
544 ENUMERATE_CELL (icell, all_cells) {
545 Int32 index = icell.index();
546 Cell cell = *icell;
547
548 cells_uid[index] = cell.uniqueId();
549
550 Byte ghost_type = 0;
551 bool is_ghost = !cell.isOwn();
552 if (is_ghost)
553 ghost_type = VtkUtils::CellGhostTypes::DUPLICATECELL;
554 cells_ghost_type[index] = ghost_type;
555
556 const bool is_polyhedron = cell.typeInfo()->isPolyhedron() || (mesh_is_polyhedral && cell.nbFace() > 0);
557 unsigned char vtk_type = VtkUtils::VTK_POLYHEDRON;
558 if (!is_polyhedron)
559 vtk_type = VtkUtils::arcaneToVtkCellType(cell.type());
560 cells_type[index] = vtk_type;
561 for (NodeLocalId node : cell.nodeIds()) {
562 cells_connectivity[connected_node_index] = node;
563 ++connected_node_index;
564 }
565 cells_offset[index + 1] = connected_node_index;
566
567 if (is_polyhedron) {
568 for (Face face : cell.faces()) {
569 // VTK requires that the nodes of a polyhedron face are ordered so
570 // that the normal to that face points outwards from the cell. In
571 // Arcane, the stored orientation of a face is outwards for its
572 // back cell. For the front cell, the order of the nodes must
573 // therefore be reversed.
574 Int32 face_nb_node = face.nbNode();
575 bool is_back_cell = (face.backCellId().localId() == cell.localId());
576 if (is_back_cell) {
577 for (NodeLocalId node : face.nodeIds())
578 faces_connectivity.add(node);
579 }
580 else {
581 NodeLocalIdView node_ids = face.nodeIds();
582 for (Int32 i = face_nb_node - 1; i >= 0; --i)
583 faces_connectivity.add(node_ids[i]);
584 }
585 faces_offset.add(faces_connectivity.size());
586 polyhedron_to_faces.add(nb_face);
587 ++nb_face;
588 }
589 }
590 polyhedron_offset[index + 1] = polyhedron_to_faces.size();
591 }
592 }
593
594 // Determines whether the datasets related to the polyhedron cells need to
595 // be written. This is the case either when the mesh contains at least one
596 // polyhedron cell or when these datasets have already been created for a
597 // previous time step (their content must then be updated at each time step
598 // to keep the temporal offsets of the file consistent).
599 {
600 Int32 local_has_polyhedron = (nb_face > 0) ? 1 : 0;
601 Int32 global_has_polyhedron = pm->reduce(MessagePassing::ReduceMax, local_has_polyhedron);
602 Int32 global_has_polyhedron_datasets = 0;
603 if (!m_is_first_call) {
604 Int32 local_has_polyhedron_datasets = 0;
605 if (m_is_writer) {
606 HDataset polyhedron_dataset;
607 polyhedron_dataset.openIfExists(m_top_group, "FaceConnectivity");
608 local_has_polyhedron_datasets = (polyhedron_dataset.id() > 0) ? 1 : 0;
609 }
610 global_has_polyhedron_datasets = pm->reduce(MessagePassing::ReduceMax, local_has_polyhedron_datasets);
611 }
612 if (global_has_polyhedron != 0 && global_has_polyhedron_datasets == 0 && !m_is_first_call)
613 ARCANE_FATAL("The mesh contains polyhedron cells but the associated datasets "
614 "do not exist in the file '{0}'. Polyhedron cells must be present "
615 "from the first output",
617 m_write_polyhedron_datasets = (global_has_polyhedron != 0) || (global_has_polyhedron_datasets != 0);
618 }
619
620 _initializeOffsets();
621
622 // ggi is a GatherGroupInfo for arrays that are not of size nb_cell or nb_node.
623 Ref<GatherGroupInfo> ggi_ref = createRef<GatherGroupInfo>(m_mesh->parallelMng(), m_is_collective_io);
624 GatherGroupInfo* ggi = ggi_ref.get();
625
626 // TODO: create an offset for this object (or look into how to calculate it automatically
627 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "Offsets", m_offset_for_cell_offset_info),
628 ggi, cells_offset);
629
631 // Saves the offset of the faces of each cell in 'PolyhedronToFaces'.
632 // This dataset has the same structure as 'Offsets' ((nb_cell + 1) values
633 // per part) and is therefore written with the same gather information.
634 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "PolyhedronOffsets", m_offset_for_polyhedron_offset_info),
635 ggi, polyhedron_offset);
636 }
637 ggi->setNeedRecompute();
638
639 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "Connectivity", m_connectivity_offset_info),
640 ggi, cells_connectivity);
641 ggi->setNeedRecompute();
642
644 // Saves the nodes of the faces of the polyhedron cells.
645 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "FaceConnectivity", m_face_connectivity_offset_info),
646 ggi, faces_connectivity);
647 ggi->setNeedRecompute();
648
649 // Saves the faces of each cell. Cells that are not polyhedron cells
650 // have an empty list of faces.
651 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "PolyhedronToFaces", m_polyhedron_to_face_offset_info),
652 ggi, polyhedron_to_faces);
653 ggi->setNeedRecompute();
654
655 // Saves the offsets of the faces in 'FaceConnectivity'. The array size
656 // is equal to the number of faces plus 1.
657 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "FaceOffsets", m_offset_for_face_offset_info),
658 ggi, faces_offset);
659 ggi->setNeedRecompute();
660 }
661
662 _writeDataSet1DCollective<unsigned char>(DataInfo(m_top_group, "Types", m_cell_offset_info),
663 &m_all_cells_gather_group_info, cells_type);
664
665 {
666 Int64 nb_cell_int64 = nb_cell;
667 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "NumberOfCells", m_part_offset_info),
668 ggi, asConstSpan(&nb_cell_int64));
669
670 Int64 nb_node_int64 = nb_node;
671 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "NumberOfPoints", m_part_offset_info),
672 ggi, asConstSpan(&nb_node_int64));
673
674 Int64 number_of_connectivity_ids = cells_connectivity.size();
675 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "NumberOfConnectivityIds", m_part_offset_info),
676 ggi, asConstSpan(&number_of_connectivity_ids));
677
679 Int64 nb_face_int64 = nb_face;
680 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "NumberOfFaces", m_part_offset_info),
681 ggi, asConstSpan(&nb_face_int64));
682
683 Int64 number_of_face_connectivity_ids = faces_connectivity.size();
684 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "NumberOfFaceConnectivityIds", m_part_offset_info),
685 ggi, asConstSpan(&number_of_face_connectivity_ids));
686
687 Int64 number_of_polyhedron_to_face_ids = polyhedron_to_faces.size();
688 _writeDataSet1DCollective<Int64>(DataInfo(m_top_group, "NumberOfPolyhedronToFaceIds", m_part_offset_info),
689 ggi, asConstSpan(&number_of_polyhedron_to_face_ids));
690 }
691 }
692 ggi->setNeedRecompute();
693
694 // Saves the unique IDs, types, and coordinates of the nodes.
695 {
696 UniqueArray<Int64> nodes_uid(nb_node);
697 UniqueArray<unsigned char> nodes_ghost_type(nb_node);
698 VariableNodeReal3& nodes_coordinates(m_mesh->nodesCoordinates());
699 UniqueArray2<Real> points;
700 points.resize(nb_node, 3);
701 ENUMERATE_ (Node, inode, all_nodes) {
702 Int32 index = inode.index();
703 Node node = *inode;
704
705 nodes_uid[index] = node.uniqueId();
706
707 Byte ghost_type = 0;
708 bool is_ghost = !node.isOwn();
709 if (is_ghost)
710 ghost_type = VtkUtils::PointGhostTypes::DUPLICATEPOINT;
711 nodes_ghost_type[index] = ghost_type;
712
713 Real3 pos = nodes_coordinates[inode];
714 points[index][0] = pos.x;
715 points[index][1] = pos.y;
716 points[index][2] = pos.z;
717 }
718
719 // Saves the unique ID of each node in the dataset "GlobalNodeId".
720 _writeDataSet1DCollective<Int64>(DataInfo(DatasetGroupAndName(m_node_data_group, "GlobalIds"), m_cell_offset_info),
721 &m_all_nodes_gather_group_info, nodes_uid);
722
723 // Saves the information about the node type (real or ghost).
724 _writeDataSet1DCollective<unsigned char>(DataInfo(DatasetGroupAndName(m_node_data_group, "vtkGhostType"), m_cell_offset_info),
725 &m_all_nodes_gather_group_info, nodes_ghost_type);
726
727 // Saves the coordinates of the nodes.
728 _writeDataSet2DCollective<Real>(DataInfo(DatasetGroupAndName(m_top_group, "Points"), m_point_offset_info),
729 ggi, points);
730 ggi->setNeedRecompute();
731 }
732
733 // Saves the information about the cell type (real or ghost)
734 _writeDataSet1DCollective<unsigned char>(DataInfo(DatasetGroupAndName(m_cell_data_group, "vtkGhostType"), m_cell_offset_info),
735 &m_all_cells_gather_group_info, cells_ghost_type);
736
737 // Saves the unique ID of each cell in the dataset "GlobalCellId".
738 // Using the dataset "vtkOriginalCellIds" does not work in Paraview.
739 _writeDataSet1DCollective<Int64>(DataInfo(DatasetGroupAndName(m_cell_data_group, "GlobalIds"), m_cell_offset_info),
740 &m_all_cells_gather_group_info, cells_uid);
741
742 if (m_is_writer) {
743 // List of times.
744 Real current_time = m_times[time_index - 1];
745 // TODO: Replace ggi with nullptr in non-collective mode?
746 _writeDataSet1D<Real>(DataInfo(DatasetGroupAndName(m_steps_group, "Values"), m_time_offset_info),
747 ggi, asConstSpan(&current_time));
748
749 // Part offset.
750 Int64 comm_size = pm->commSize();
751 Int64 part_offset = (time_index - 1) * comm_size;
752 _writeDataSet1D<Int64>(DataInfo(DatasetGroupAndName(m_steps_group, "PartOffsets"), m_time_offset_info),
753 ggi, asConstSpan(&part_offset));
754
756 // Offset of the 'FaceOffsets' dataset for the current time step. This
757 // value is the number of faces saved before the current time step. It
758 // is deduced from the write offset of the 'FaceOffsets' dataset (which
759 // also contains one offset value per part and per time step) by
760 // removing the number of these offset values.
761 auto face_offsets_write_offset = m_offset_info_list.find(m_offset_for_face_offset_info);
762 if (face_offsets_write_offset == m_offset_info_list.end())
763 ARCANE_FATAL("The dataset 'FaceOffsets' has not been written");
764 Int64 nb_offset_value = (time_index - 1) * comm_size;
765 Int64 face_offsets_offset = face_offsets_write_offset->second - nb_offset_value;
766 _writeDataSet1D<Int64>({ { m_steps_group, "FaceOffsetsOffsets" }, m_time_offset_info }, ggi,
767 asConstSpan(&face_offsets_offset));
768 }
769
770 // Number of times
771 _addInt64Attribute(m_steps_group, "NSteps", time_index);
772 }
773 //ggi->needRecompute();
774
775 _writeConstituentsGroups();
776}
777
778/*---------------------------------------------------------------------------*/
779/*---------------------------------------------------------------------------*/
780
781void VtkHdfV2DataWriter::
782_writeConstituentsGroups()
783{
784 if (!m_material_mng)
785 return;
786
787 // Fills the information for groups related to constituents
788 // NOTE: For now, we only process the media.
789 for (IMeshEnvironment* env : m_material_mng->environments()) {
790 CellGroup cells = env->cells();
791
792 Ref<ItemGroupCollectiveInfo> group_info_ref = createRef<ItemGroupCollectiveInfo>(cells);
793 m_materials_groups.add(group_info_ref);
794
795 Ref<GatherGroupInfo> gather_info_ref = createRef<GatherGroupInfo>(m_material_mng->mesh()->parallelMng(), m_is_collective_io);
796 m_gather_info_materials_groups.add(gather_info_ref);
797
798 ItemGroupCollectiveInfo& group_info = *group_info_ref.get();
799 GatherGroupInfo& gather_info = *gather_info_ref.get();
800 _initializeItemGroupCollectiveInfos(group_info, gather_info);
801
802 ConstArrayView<Int32> groups_ids = cells.view().localIds();
803 DatasetGroupAndName dataset_group_name(m_top_group, String("Constituent_") + cells.name());
804 if (m_is_first_call)
805 info() << "Writing infos for group '" << cells.name() << "'";
806 _writeDataSet1DCollective<Int32>({ dataset_group_name, m_cell_offset_info, group_info_ref.get() }, gather_info_ref.get(), groups_ids);
807 }
808}
809
810/*---------------------------------------------------------------------------*/
811/*---------------------------------------------------------------------------*/
817{
818 // TODO: look into using a scan.
819 IParallelMng* pm = m_mesh->parallelMng();
820 Int32 nb_rank = pm->commSize();
821 Int32 my_rank = pm->commRank();
822
823 UniqueArray<Int64> ranks_size(nb_rank);
824 ArrayView<Int64> all_sizes(ranks_size);
825 Int64 dim1_size = local_size;
826 pm->allGather(ConstArrayView<Int64>(1, &dim1_size), all_sizes);
827
828 Int64 total_size = 0;
829 for (Integer i = 0; i < nb_rank; ++i)
830 total_size += all_sizes[i];
831
832 Int64 my_index = 0;
833 for (Integer i = 0; i < my_rank; ++i)
834 my_index += all_sizes[i];
835
836 WritePartInfo part_info;
837 part_info.setTotalSize(total_size);
838 part_info.setSize(local_size);
839 part_info.setOffset(my_index);
840 return part_info;
841}
842
843/*---------------------------------------------------------------------------*/
844/*---------------------------------------------------------------------------*/
845
846void VtkHdfV2DataWriter::
847_initializeItemGroupCollectiveInfos(ItemGroupCollectiveInfo& group_info, GatherGroupInfo& gather_info)
848{
849 Int32 dim1_size = group_info.m_item_group.size();
850
851 gather_info.setCollectiveIO(m_is_collective_io);
852 gather_info.computeSize(dim1_size);
853
854 Int32 computed_nb_elem = gather_info.nbElemOutput();
855
856 group_info.setWritePartInfo(_computeWritePartInfo(computed_nb_elem));
857}
858
859namespace
860{
861 std::pair<Int64, Int64> _getInterval(Int64 index, Int64 nb_interval, Int64 total_size)
862 {
863 Int64 n = total_size;
864 Int64 isize = n / nb_interval;
865 Int64 ibegin = index * isize;
866 // For the last interval, take the remaining elements
867 if ((index + 1) == nb_interval)
868 isize = n - ibegin;
869 return { ibegin, isize };
870 }
871} // namespace
872
873/*---------------------------------------------------------------------------*/
874/*---------------------------------------------------------------------------*/
881void VtkHdfV2DataWriter::
882_writeDataSetGeneric(const DataInfo& data_info, GatherGroupInfo* gather_info, Int32 nb_dim,
883 Int64 dim1_size, Int64 dim2_size,
884 ConstMemoryView values_data,
885 const hid_t hdf_type, bool is_collective)
886{
887 ARCANE_CHECK_POINTER(gather_info);
888 if (nb_dim == 1)
889 dim2_size = 1;
890
891 HGroup& group = data_info.dataset.group;
892 const String& name = data_info.dataset.name;
893
894 // If positive or zero, indicates the write offset.
895 // Otherwise, we write to the end of the current dataset.
896 Int64 wanted_offset = data_info.datasetInfo().offset();
897
898 static constexpr int MAX_DIM = 2;
899 HDataset dataset;
900
901 // In case of collective operation, local_dims and global_dims are
902 // different on the first dimension. The second dimension is always
903 // identical for local_dims and global_dims and should not be modified during
904 // the entire calculation.
905
906 // Dimensions of the dataset that the current rank will write.
908 local_dims[0] = dim1_size;
909 local_dims[1] = dim2_size;
910
911 // Cumulative dimensions of all ranks for writing.
913
914 // Maximum dimensions of the DataSet
915 // For the second dimension, we assume it is constant over time.
917 max_dims[0] = H5S_UNLIMITED;
918 max_dims[1] = dim2_size;
919
920 herr_t herror = 0;
921 Int64 write_offset = 0;
922
923 Int64 my_index = 0;
924 Int64 global_dim1_size = dim1_size;
925 Int32 nb_participating_rank = 1;
926
927 if (is_collective) {
928 nb_participating_rank = gather_info->nbWriterGlobal();
929 WritePartInfo part_info;
930 if (data_info.m_group_info) {
931 // If the data is associated with a group, then the information
932 // about the offset has already been calculated
933 part_info = data_info.m_group_info->writePartInfo();
934 }
935 else {
936 part_info = _computeWritePartInfo(dim1_size);
937 }
938 global_dim1_size = part_info.totalSize();
939 my_index = part_info.offset();
940 }
941
942 // The only collective operation was _computeWritePartInfo().
943 if (!m_is_writer) {
944 return;
945 }
946
947 HProperty write_plist_id;
948 if constexpr (HInit::hasParallelHdf5()) {
949 if (is_collective)
951 else
953 }
954
955 HSpace file_space;
956 FixedArray<hsize_t, MAX_DIM> hyperslab_offsets;
957
958 if (m_is_first_call) {
959 // TODO: look into how to better calculate the chunk
961 global_dims[0] = global_dim1_size;
962 global_dims[1] = dim2_size;
963 // It is important that everyone has the same chunk size.
964 Int64 chunk_size = global_dim1_size / nb_participating_rank;
965 if (chunk_size < 1024)
966 chunk_size = 1024;
967 const Int64 max_chunk_size = 1024 * 1024 * 10;
968 chunk_size = math::min(chunk_size, max_chunk_size);
969 chunk_dims[0] = chunk_size;
970 chunk_dims[1] = dim2_size;
971 info() << "CHUNK nb_dim=" << nb_dim
972 << " global_dim1_size=" << global_dim1_size
973 << " chunk0=" << chunk_dims[0]
974 << " chunk1=" << chunk_dims[1]
975 << " name=" << name;
976 file_space.createSimple(nb_dim, global_dims.data(), max_dims.data());
977 HProperty plist_id;
978 plist_id.create(H5P_DATASET_CREATE);
979 H5Pset_chunk(plist_id.id(), nb_dim, chunk_dims.data());
980 dataset.create(group, name.localstr(), hdf_type, file_space, HProperty{}, plist_id, HProperty{});
981
982 if (is_collective) {
983 hyperslab_offsets[0] = my_index;
984 hyperslab_offsets[1] = 0;
985 }
986 }
987 else {
988 // Expands the first dimension of the dataset.
989 // We are going to add 'global_dim1_size' to this dimension.
990 dataset.open(group, name.localstr());
991 file_space = dataset.getSpace();
992 int nb_dimension = file_space.nbDimension();
993 if (nb_dimension != nb_dim)
994 ARCANE_THROW(IOException, "Bad dimension '{0}' for dataset '{1}' (should be 1)",
995 nb_dimension, name);
996 // TODO: Check that the second dimension is the same as the one saved.
997 FixedArray<hsize_t, MAX_DIM> original_dims;
998 file_space.getDimensions(original_dims.data(), nullptr);
999 hsize_t offset0 = original_dims[0];
1000 // If we have a positive offset from DatasetInfo, we take it.
1001 // This means we have performed a rollback.
1002 if (wanted_offset >= 0) {
1003 offset0 = wanted_offset;
1004 info() << "Forcing offset to " << wanted_offset;
1005 }
1006 global_dims[0] = offset0 + global_dim1_size;
1007 global_dims[1] = dim2_size;
1008 write_offset = offset0;
1009 // Expands the dataset.
1010 // WARNING this invalidates file_space. It must therefore be re-read immediately after.
1011 if ((herror = dataset.setExtent(global_dims.data())) < 0)
1012 ARCANE_THROW(IOException, "Can not extent dataset '{0}' (err={1})", name, herror);
1013 file_space = dataset.getSpace();
1014
1015 hyperslab_offsets[0] = offset0 + my_index;
1016 hyperslab_offsets[1] = 0;
1017 info(4) << "APPEND nb_dim=" << nb_dim
1018 << " dim0=" << global_dims[0]
1019 << " count0=" << local_dims[0]
1020 << " offsets0=" << hyperslab_offsets[0] << " name=" << name;
1021 }
1022
1023 Int64 nb_write_byte = global_dim1_size * dim2_size * values_data.datatypeSize();
1024
1025 // Performs the writing in multiple parts if requested.
1026 // This is only possible for collective writing.
1027 Int64 nb_interval = 1;
1028 if (is_collective && m_max_write_size > 0) {
1029 nb_interval = 1 + nb_write_byte / (m_max_write_size * 1024);
1030 }
1031 info(4) << "WRITE global_size=" << nb_write_byte << " max_size=" << m_max_write_size << " nb_interval=" << nb_interval;
1032
1033 for (Int64 i = 0; i < nb_interval; ++i) {
1034 auto [index, nb_element] = _getInterval(i, nb_interval, dim1_size);
1035 // Selects the part of the data to write
1037 dims[0] = nb_element;
1038 dims[1] = dim2_size;
1039 FixedArray<hsize_t, 2> offsets;
1040 offsets[0] = hyperslab_offsets[0] + index;
1041 offsets[1] = 0;
1042 if ((herror = H5Sselect_hyperslab(file_space.id(), H5S_SELECT_SET, offsets.data(), nullptr, dims.data(), nullptr)) < 0)
1043 ARCANE_THROW(IOException, "Can not select hyperslab '{0}' (err={1})", name, herror);
1044
1045 HSpace memory_space;
1046 memory_space.createSimple(nb_dim, dims.data());
1047 Int64 data_offset = index * values_data.datatypeSize() * dim2_size;
1048 // Performs the writing
1049 if ((herror = dataset.write(hdf_type, values_data.data() + data_offset, memory_space, file_space, write_plist_id)) < 0)
1050 ARCANE_THROW(IOException, "Can not write dataset '{0}' (err={1})", name, herror);
1051
1052 if (dataset.isBad())
1053 ARCANE_THROW(IOException, "Can not write dataset '{0}'", name);
1054 }
1055
1056 if (!data_info.datasetInfo().isNull())
1057 m_offset_info_list.insert(std::make_pair(data_info.datasetInfo(), write_offset));
1058}
1059
1060/*---------------------------------------------------------------------------*/
1061/*---------------------------------------------------------------------------*/
1062
1063template <typename DataType> void VtkHdfV2DataWriter::
1064_writeDataSetGeneric(const DataInfo& data_info, GatherGroupInfo* gather_info, Int32 nb_dim,
1065 Int64 dim1_size, Int64 dim2_size, const DataType* values_data,
1066 bool is_collective)
1067{
1068 const hid_t hdf_type = m_standard_types.nativeType(DataType{});
1069 ConstMemoryView mem_view = makeConstMemoryView(values_data, sizeof(DataType), dim1_size * dim2_size);
1070 _writeDataSetGeneric(data_info, gather_info, nb_dim, dim1_size, dim2_size, mem_view, hdf_type, is_collective);
1071}
1072
1073/*---------------------------------------------------------------------------*/
1074/*---------------------------------------------------------------------------*/
1075
1076template <typename DataType> void VtkHdfV2DataWriter::
1077_writeDataSet1D(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values)
1078{
1079 _writeDataSetGeneric(data_info, gather_info, 1, values.size(), 1, values.data(), false);
1080}
1081
1082/*---------------------------------------------------------------------------*/
1083/*---------------------------------------------------------------------------*/
1084
1085template <typename DataType> void VtkHdfV2DataWriter::
1086_writeDataSet1DUsingCollectiveIO(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values)
1087{
1088 _writeDataSetGeneric(data_info, gather_info, 1, values.size(), 1, values.data(), true);
1089}
1090
1091/*---------------------------------------------------------------------------*/
1092/*---------------------------------------------------------------------------*/
1093
1094template <typename DataType> void VtkHdfV2DataWriter::
1095_writeDataSet1DCollective(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values)
1096{
1097 ARCANE_CHECK_POINTER(gather_info);
1098
1099 GatherGroup gg;
1100
1101 gather_info->computeSizeT(values);
1102 gg.setGatherGroupInfo(gather_info);
1103
1104 if (gg.isNeedGather()) {
1105 UniqueArray<DataType> all_values;
1106 gg.gatherToMasterIOT(values, all_values);
1107
1108 if (m_is_collective_io)
1109 _writeDataSet1DUsingCollectiveIO(data_info, gather_info, all_values.constSpan());
1110 else
1111 _writeDataSet1D(data_info, gather_info, all_values.constSpan());
1112 }
1113 else {
1114 if (m_is_collective_io)
1115 _writeDataSet1DUsingCollectiveIO(data_info, gather_info, values);
1116 else
1117 _writeDataSet1D(data_info, gather_info, values);
1118 }
1119}
1120
1121/*---------------------------------------------------------------------------*/
1122/*---------------------------------------------------------------------------*/
1123
1124template <typename DataType> void VtkHdfV2DataWriter::
1125_writeDataSet2D(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values)
1126{
1127 _writeDataSetGeneric(data_info, gather_info, 2, values.dim1Size(), values.dim2Size(), values.data(), false);
1128}
1129
1130/*---------------------------------------------------------------------------*/
1131/*---------------------------------------------------------------------------*/
1132
1133template <typename DataType> void VtkHdfV2DataWriter::
1134_writeDataSet2DUsingCollectiveIO(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values)
1135{
1136 _writeDataSetGeneric(data_info, gather_info, 2, values.dim1Size(), values.dim2Size(), values.data(), true);
1137}
1138
1139/*---------------------------------------------------------------------------*/
1140/*---------------------------------------------------------------------------*/
1141
1142template <typename DataType> void VtkHdfV2DataWriter::
1143_writeDataSet2DCollective(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values)
1144{
1145 ARCANE_CHECK_POINTER(gather_info);
1146
1147 GatherGroup gg;
1148
1149 gather_info->computeSizeT(values);
1150 gg.setGatherGroupInfo(gather_info);
1151
1152 if (gg.isNeedGather()) {
1153 UniqueArray2<DataType> all_values;
1154 gg.gatherToMasterIOT(values, all_values);
1155
1156 if (m_is_collective_io)
1157 _writeDataSet2DUsingCollectiveIO(data_info, gather_info, all_values.constSpan());
1158 else
1159 _writeDataSet2D(data_info, gather_info, all_values.constSpan());
1160 }
1161 else {
1162 if (m_is_collective_io)
1163 _writeDataSet2DUsingCollectiveIO(data_info, gather_info, values);
1164 else
1165 _writeDataSet2D(data_info, gather_info, values);
1166 }
1167}
1168
1169/*---------------------------------------------------------------------------*/
1170/*---------------------------------------------------------------------------*/
1171
1172void VtkHdfV2DataWriter::
1173_addInt64ArrayAttribute(Hid& hid, const char* name, Span<const Int64> values)
1174{
1175 hsize_t len = values.size();
1176 hid_t aid = H5Screate_simple(1, &len, nullptr);
1177 hid_t attr = H5Acreate2(hid.id(), name, H5T_NATIVE_INT64, aid, H5P_DEFAULT, H5P_DEFAULT);
1178 if (attr < 0)
1179 ARCANE_FATAL("Can not create attribute '{0}'", name);
1180 int ret = H5Awrite(attr, H5T_NATIVE_INT64, values.data());
1181 if (ret < 0)
1182 ARCANE_FATAL("Can not write attribute '{0}'", name);
1183 H5Aclose(attr);
1184 H5Sclose(aid);
1185}
1186
1187/*---------------------------------------------------------------------------*/
1188/*---------------------------------------------------------------------------*/
1189
1190void VtkHdfV2DataWriter::
1191_addInt64Attribute(Hid& hid, const char* name, Int64 value)
1192{
1193 HSpace aid(H5Screate(H5S_SCALAR));
1194 HAttribute attr;
1195 if (m_is_first_call)
1196 attr.create(hid, name, H5T_NATIVE_INT64, aid);
1197 else
1198 attr.open(hid, name);
1199 if (attr.isBad())
1200 ARCANE_FATAL("Can not create attribute '{0}'", name);
1201 herr_t ret = attr.write(H5T_NATIVE_INT64, &value);
1202 if (ret < 0)
1203 ARCANE_FATAL("Can not write attribute '{0}'", name);
1204}
1205
1206/*---------------------------------------------------------------------------*/
1207/*---------------------------------------------------------------------------*/
1208
1209Int64 VtkHdfV2DataWriter::
1210_readInt64Attribute(Hid& hid, const char* name)
1211{
1212 HAttribute attr;
1213 attr.open(hid, name);
1214 if (attr.isBad())
1215 ARCANE_FATAL("Can not open attribute '{0}'", name);
1216 Int64 value;
1217 herr_t ret = attr.read(H5T_NATIVE_INT64, &value);
1218 if (ret < 0)
1219 ARCANE_FATAL("Can not read attribute '{0}'", name);
1220 return value;
1221}
1222
1223/*---------------------------------------------------------------------------*/
1224/*---------------------------------------------------------------------------*/
1225
1226void VtkHdfV2DataWriter::
1227_addStringAttribute(Hid& hid, const char* name, const String& value)
1228{
1229 hid_t aid = H5Screate(H5S_SCALAR);
1230 hid_t attr_type = H5Tcopy(H5T_C_S1);
1231 H5Tset_size(attr_type, value.length());
1232 hid_t attr = H5Acreate2(hid.id(), name, attr_type, aid, H5P_DEFAULT, H5P_DEFAULT);
1233 if (attr < 0)
1234 ARCANE_FATAL("Can not create attribute {0}", name);
1235 int ret = H5Awrite(attr, attr_type, value.localstr());
1236 ret = H5Tclose(attr_type);
1237 if (ret < 0)
1238 ARCANE_FATAL("Can not write attribute '{0}'", name);
1239 H5Aclose(attr);
1240 H5Sclose(aid);
1241}
1242
1243/*---------------------------------------------------------------------------*/
1244/*---------------------------------------------------------------------------*/
1245
1246void VtkHdfV2DataWriter::
1247endWrite()
1248{
1249 // Save the recorded offsets
1250
1251 if (m_is_writer) {
1252 for (const auto& i : m_offset_info_list) {
1253 Int64 offset = i.second;
1254 const DatasetInfo& offset_info = i.first;
1255 HGroup* hdf_group = offset_info.group();
1256 //info() << "OFFSET_INFO name=" << offset_info.name() << " offset=" << offset;
1257 if (hdf_group) {
1258 Ref<GatherGroupInfo> ggi = createRef<GatherGroupInfo>(m_mesh->parallelMng(), m_is_collective_io);
1259 _writeDataSet1D<Int64>({ { *hdf_group, offset_info.name() }, m_time_offset_info }, ggi.get(), asConstSpan(&offset));
1260 }
1261 }
1262 }
1263 _closeGroups();
1264 m_file_id.close();
1265}
1266
1267/*---------------------------------------------------------------------------*/
1268/*---------------------------------------------------------------------------*/
1269
1270void VtkHdfV2DataWriter::
1271_openOrCreateGroups()
1272{
1273 // Any group opened here must be closed in closeGroups().
1274 m_top_group.openOrCreate(m_file_id, "VTKHDF");
1275 m_cell_data_group.openOrCreate(m_top_group, "CellData");
1276 m_node_data_group.openOrCreate(m_top_group, "PointData");
1277 m_steps_group.openOrCreate(m_top_group, "Steps");
1278 m_point_data_offsets_group.openOrCreate(m_steps_group, "PointDataOffsets");
1279 m_cell_data_offsets_group.openOrCreate(m_steps_group, "CellDataOffsets");
1280 m_field_data_offsets_group.openOrCreate(m_steps_group, "FieldDataOffsets");
1281}
1282
1283/*---------------------------------------------------------------------------*/
1284/*---------------------------------------------------------------------------*/
1285
1286void VtkHdfV2DataWriter::
1287_closeGroups()
1288{
1289 m_cell_data_group.close();
1290 m_node_data_group.close();
1291 m_point_data_offsets_group.close();
1292 m_cell_data_offsets_group.close();
1293 m_field_data_offsets_group.close();
1294 m_steps_group.close();
1295 m_top_group.close();
1296}
1297
1298/*---------------------------------------------------------------------------*/
1299/*---------------------------------------------------------------------------*/
1300
1302setMetaData(const String& meta_data)
1303{
1304 ARCANE_UNUSED(meta_data);
1305}
1306
1307/*---------------------------------------------------------------------------*/
1308/*---------------------------------------------------------------------------*/
1309
1311write(IVariable* var, IData* data)
1312{
1313 info(4) << "Write VtkHdfV2 var=" << var->name();
1314
1315 eItemKind item_kind = var->itemKind();
1316
1317 if (var->dimension() != 1)
1318 ARCANE_FATAL("Only export of scalar item variable is implemented (name={0})", var->name());
1319 if (var->isPartial())
1320 ARCANE_FATAL("Export of partial variable is not implemented");
1321
1322 HGroup* group = nullptr;
1323 DatasetInfo offset_info;
1324 ItemGroupCollectiveInfo* group_info = nullptr;
1325 GatherGroupInfo* gather_info = nullptr;
1326 switch (item_kind) {
1327 case IK_Cell:
1328 group = &m_cell_data_group;
1329 offset_info = m_cell_offset_info;
1330 group_info = &m_all_cells_info;
1331 gather_info = &m_all_cells_gather_group_info;
1332 break;
1333 case IK_Node:
1334 group = &m_node_data_group;
1335 offset_info = m_point_offset_info;
1336 group_info = &m_all_nodes_info;
1337 gather_info = &m_all_nodes_gather_group_info;
1338 break;
1339 default:
1340 ARCANE_FATAL("Only export of 'Cell' or 'Node' variable is implemented (name={0})", var->name());
1341 }
1342
1343 ARCANE_CHECK_POINTER(group);
1344
1345 DataInfo data_info(DatasetGroupAndName{ *group, var->name() }, offset_info, group_info);
1346 eDataType data_type = var->dataType();
1347 switch (data_type) {
1348 case DT_Real:
1349 _writeBasicTypeDataset<Real>(data_info, gather_info, data);
1350 break;
1351 case DT_Int64:
1352 _writeBasicTypeDataset<Int64>(data_info, gather_info, data);
1353 break;
1354 case DT_Int32:
1355 _writeBasicTypeDataset<Int32>(data_info, gather_info, data);
1356 break;
1357 case DT_Real3:
1358 _writeReal3Dataset(data_info, gather_info, data);
1359 break;
1360 case DT_Real2:
1361 _writeReal2Dataset(data_info, gather_info, data);
1362 break;
1363 default:
1364 warning() << String::format("Export for datatype '{0}' is not supported (var_name={1})", data_type, var->name());
1365 }
1366}
1367
1368/*---------------------------------------------------------------------------*/
1369/*---------------------------------------------------------------------------*/
1370
1371template <typename DataType> void VtkHdfV2DataWriter::
1372_writeBasicTypeDataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data)
1373{
1374 auto* true_data = dynamic_cast<IArrayDataT<DataType>*>(data);
1375 ARCANE_CHECK_POINTER(true_data);
1376 _writeDataSet1DCollective(data_info, gather_info, Span<const DataType>(true_data->view()));
1377}
1378
1379/*---------------------------------------------------------------------------*/
1380/*---------------------------------------------------------------------------*/
1381
1382void VtkHdfV2DataWriter::
1383_writeReal3Dataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data)
1384{
1385 auto* true_data = dynamic_cast<IArrayDataT<Real3>*>(data);
1386 ARCANE_CHECK_POINTER(true_data);
1387 SmallSpan<const Real3> values(true_data->view());
1388 Int32 nb_value = values.size();
1389 // TODO: optimize this without passing through a temporary array
1390 UniqueArray2<Real> scalar_values;
1391 scalar_values.resize(nb_value, 3);
1392 for (Int32 i = 0; i < nb_value; ++i) {
1393 Real3 v = values[i];
1394 scalar_values[i][0] = v.x;
1395 scalar_values[i][1] = v.y;
1396 scalar_values[i][2] = v.z;
1397 }
1398 _writeDataSet2DCollective<Real>(data_info, gather_info, scalar_values);
1399}
1400
1401/*---------------------------------------------------------------------------*/
1402/*---------------------------------------------------------------------------*/
1403
1404void VtkHdfV2DataWriter::
1405_writeReal2Dataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data)
1406{
1407 // Convert to an array of 3 components where the last one will be 0.
1408 auto* true_data = dynamic_cast<IArrayDataT<Real2>*>(data);
1409 ARCANE_CHECK_POINTER(true_data);
1410 SmallSpan<const Real2> values(true_data->view());
1411 Int32 nb_value = values.size();
1412 UniqueArray2<Real> scalar_values;
1413 scalar_values.resize(nb_value, 3);
1414 for (Int32 i = 0; i < nb_value; ++i) {
1415 Real2 v = values[i];
1416 scalar_values[i][0] = v.x;
1417 scalar_values[i][1] = v.y;
1418 scalar_values[i][2] = 0.0;
1419 }
1420 _writeDataSet2DCollective<Real>(data_info, gather_info, scalar_values);
1421}
1422
1423/*---------------------------------------------------------------------------*/
1424/*---------------------------------------------------------------------------*/
1425
1426void VtkHdfV2DataWriter::
1427_readAndSetOffset(DatasetInfo& offset_info, Int32 wanted_step)
1428{
1429 HGroup* hgroup = offset_info.group();
1430 ARCANE_CHECK_POINTER(hgroup);
1431 StandardArrayT<Int64> a(hgroup->id(), offset_info.name());
1432 UniqueArray<Int64> values;
1433 a.directRead(m_standard_types, values);
1434 Int64 offset_value = values[wanted_step];
1435 offset_info.setOffset(offset_value);
1436 info() << "VALUES name=" << offset_info.name() << " values=" << values
1437 << " wanted_step=" << wanted_step << " v=" << offset_value;
1438}
1439
1440/*---------------------------------------------------------------------------*/
1441/*---------------------------------------------------------------------------*/
1442
1443void VtkHdfV2DataWriter::
1444_initializeOffsets()
1445{
1446 // There are 5 offset values used:
1447 //
1448 // - offset on the number of cells (CellOffsets). This offset has a number of elements
1449 // equal to the number of saved time steps and is increased at each number of cells output. This offset
1450 // is also used for cell variables
1451 // - offset on the number of nodes (PointOffsets). It is equivalent to 'CellOffsets' but
1452 // for nodes.
1453 // - offset for "NumberOfCells", "NumberOfPoints" and "NumberOfConnectivityIds". For each
1454 // of these fields there are NbPart values per time step, with 'NbPart' being the number of parts (and
1455 // thus the number of sub-domains if no grouping is done). There are thus a total of
1456 // NbPart * NbTimeStep in this offset field.
1457 // - offset for the "Connectivity" field, which is called "ConnectivityIdOffsets".
1458 // This offset has a number of elements equal to the number of saved time steps.
1459 // - offset for the "Offsets" field. "Offset" contains for each cell the offset in
1460 // "Connectivity" of the cell node connectivity. This offset is not saved,
1461 // but since this field has a number of values equal to the number of cells plus one it is possible
1462 // to deduce it from "CellOffsets" (it equals "CellOffsets" plus the current time index).
1463 //
1464 // When the mesh contains polyhedron cells, 4 additional offset values are used:
1465 //
1466 // - offset for the "FaceConnectivity" field, which is called "FaceConnectivityOffsets".
1467 // It is equivalent to "ConnectivityIdOffsets" but for the faces of the polyhedron cells.
1468 // - offset for the "PolyhedronToFaces" field, which is called "PolyhedronToFaceIdOffsets".
1469 // It is equivalent to "ConnectivityIdOffsets" but for the list of faces of each cell.
1470 // - offset for the "FaceOffsets" field, which is called "FaceOffsetsOffsets". It contains
1471 // the number of faces saved before the current time step. As for "Offsets", the write
1472 // offset of the "FaceOffsets" dataset can be deduced from it (it equals "FaceOffsetsOffsets"
1473 // plus the number of parts saved before the current time step).
1474 // - offset for the "PolyhedronOffsets" field. This offset is not saved but, as this field
1475 // has the same structure as "Offsets", its write offset is equal to the one of "Offsets".
1476
1477 m_cell_offset_info = DatasetInfo(m_steps_group, "CellOffsets");
1478 m_point_offset_info = DatasetInfo(m_steps_group, "PointOffsets");
1479 m_connectivity_offset_info = DatasetInfo(m_steps_group, "ConnectivityIdOffsets");
1480 m_face_connectivity_offset_info = DatasetInfo(m_steps_group, "FaceConnectivityOffsets");
1481 m_polyhedron_to_face_offset_info = DatasetInfo(m_steps_group, "PolyhedronToFaceIdOffsets");
1482 m_face_offsets_offset_info = DatasetInfo(m_steps_group, "FaceOffsetsOffsets");
1483 // These five offsets are not saved in the VTK format
1484 m_offset_for_cell_offset_info = DatasetInfo("_OffsetForCellOffsetInfo");
1485 m_offset_for_face_offset_info = DatasetInfo("_OffsetForFaceOffsetInfo");
1486 m_offset_for_polyhedron_offset_info = DatasetInfo("_OffsetForPolyhedronOffsetInfo");
1487 m_part_offset_info = DatasetInfo("_PartOffsetInfo");
1488 m_time_offset_info = DatasetInfo("_TimeOffsetInfo");
1489
1490 // Check if we haven't done a backward run.
1491 // This is the case if the number of saved time steps is greater than the number
1492 // of values in \a m_times.
1493 if (m_is_writer && !m_is_first_call) {
1494 IParallelMng* pm = m_mesh->parallelMng();
1495 const Int32 nb_rank = pm->commSize();
1496 Int64 nb_current_step = _readInt64Attribute(m_steps_group, "NSteps");
1497 Int32 time_index = m_times.size();
1498 info(4) << "NB_STEP=" << nb_current_step << " time_index=" << time_index
1499 << " current_time=" << m_times.back();
1500 const bool debug_times = false;
1501 if (debug_times) {
1502 StandardArrayT<Real> a1(m_steps_group.id(), "Values");
1503 UniqueArray<Real> times;
1504 a1.directRead(m_standard_types, times);
1505 info() << "TIMES=" << times;
1506 }
1507 if ((nb_current_step + 1) != time_index) {
1508 info() << "[VtkHdf] go_backward detected";
1509 Int32 wanted_step = time_index - 1;
1510 // This means a backward run has been performed.
1511 // In this case, the offsets must be reread
1512 _readAndSetOffset(m_cell_offset_info, wanted_step);
1513 _readAndSetOffset(m_point_offset_info, wanted_step);
1514 _readAndSetOffset(m_connectivity_offset_info, wanted_step);
1516 _readAndSetOffset(m_face_connectivity_offset_info, wanted_step);
1517 _readAndSetOffset(m_polyhedron_to_face_offset_info, wanted_step);
1518 _readAndSetOffset(m_face_offsets_offset_info, wanted_step);
1519 // The 'FaceOffsets' dataset contains (nb_face + 1) values per part:
1520 // its write offset is deduced from 'FaceOffsetsOffsets'.
1521 m_offset_for_face_offset_info.setOffset(m_face_offsets_offset_info.offset() + wanted_step * nb_rank);
1522 // The 'PolyhedronOffsets' dataset has the same structure as 'Offsets':
1523 // its write offset is therefore the same.
1524 m_offset_for_polyhedron_offset_info.setOffset(m_offset_for_cell_offset_info.offset());
1525 }
1526 m_part_offset_info.setOffset(wanted_step * nb_rank);
1527 m_time_offset_info.setOffset(wanted_step);
1528 m_offset_for_cell_offset_info.setOffset(m_cell_offset_info.offset() + wanted_step * nb_rank);
1529 }
1530 }
1531}
1532
1533/*---------------------------------------------------------------------------*/
1534/*---------------------------------------------------------------------------*/
1535
1536/*---------------------------------------------------------------------------*/
1537/*---------------------------------------------------------------------------*/
1541class VtkHdfV2PostProcessor
1543{
1544 public:
1545
1546 explicit VtkHdfV2PostProcessor(const ServiceBuildInfo& sbi)
1548 {
1549 }
1550
1551 IDataWriter* dataWriter() override { return m_writer.get(); }
1552 void notifyBeginWrite() override
1553 {
1554 bool use_collective_io = true;
1555 Int64 max_write_size = 0;
1556 if (options()) {
1557 use_collective_io = options()->useCollectiveWrite();
1558 max_write_size = options()->maxWriteSize();
1559 }
1560 auto w = std::make_unique<VtkHdfV2DataWriter>(mesh(), groups(), use_collective_io);
1561 w->setMaxWriteSize(max_write_size);
1562 w->setTimes(times());
1564 w->setDirectoryName(dir.file("vtkhdfv2"));
1565 m_writer = std::move(w);
1566 }
1567 void notifyEndWrite() override
1568 {
1569 m_writer = nullptr;
1570 }
1571 void close() override {}
1572
1573 private:
1574
1575 std::unique_ptr<IDataWriter> m_writer;
1576};
1577
1578/*---------------------------------------------------------------------------*/
1579/*---------------------------------------------------------------------------*/
1580
1581ARCANE_REGISTER_SERVICE_VTKHDFV2POSTPROCESSOR(VtkHdfV2PostProcessor,
1582 VtkHdfV2PostProcessor);
1583
1584/*---------------------------------------------------------------------------*/
1585/*---------------------------------------------------------------------------*/
1586
1587} // namespace Arcane
1588
1589/*---------------------------------------------------------------------------*/
1590/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro returning the pointer ptr if it is not null or throwing an exception if it is null.
#define ARCANE_THROW(exception_class,...)
Macro for throwing an exception with formatting.
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_(type, name, group)
Generic enumerator for an entity group.
#define ENUMERATE_CELL(name, group)
Generic enumerator for a cell group.
CaseOptionsVtkHdfV2PostProcessor * options() const
Options du jeu de données du service.
ArcaneVtkHdfV2PostProcessorObject(const Arcane::ServiceBuildInfo &sbi)
Constructeur.
Modifiable view of an array of type T.
Constant view of an array of type T.
Constant view on a contiguous memory region containing fixed-size elements.
constexpr Int32 datatypeSize() const
Size of the associated data type (1 by default).
constexpr const std::byte * data() const
Pointer to the memory region.
String file(const String &file_name) const override
Returns the full path of the file file_name in the directory.
Definition Directory.cc:120
Class allowing the calculation and storage of grouping information.
void computeSize(Int32 nb_elem_in) override
Method allowing the calculation of grouping information.
Int32 nbWriterGlobal() override
Method allowing knowledge of the number of writing subdomains.
Int32 nbElemOutput() override
Method allowing knowledge of the number of elements that our subdomain must process after reception.
Encapsulates a hid_t for a dataset.
Encapsulates a hid_t for a file.
Encapsulates a hid_t for a group.
static void useMutex(bool is_active, IParallelMng *pm)
Function allowing activation or deactivation of locks on each HDF5 call.
Definition Hdf5Utils.cc:90
static constexpr bool hasParallelHdf5()
True HDF5 is compiled with MPI support.
Encapsulates a hid_t for a property (H5P*).
void createDatasetTransfertCollectiveMPIIO()
Creates a dataset property for MPIIO.
Definition Hdf5Utils.cc:897
void createDatasetTransfertIndependentMPIIO()
Creates a dataset property for MPIIO.
Definition Hdf5Utils.cc:916
Encapsulates a hid_t for a dataspace.
Encapsulates a hid_t.
Definition of standard Arcane types for hdf5.
Interface of a 1D array data item of type T.
Definition IData.h:300
Interface for writing variable data.
Definition IDataWriter.h:45
Interface of a data item.
Definition IData.h:34
Exception when an input/output error is detected.
Definition IOException.h:34
Interface of the parallelism manager for a subdomain.
virtual Int32 commRank() const =0
Rank of this instance in the communicator.
virtual Int32 commSize() const =0
Number of instances in the communicator.
virtual void allGather(ConstArrayView< char > send_buf, ArrayView< char > recv_buf)=0
Performs an all-gather operation across all processors. This is a collective operation....
Interface of a variable.
Definition IVariable.h:40
virtual eDataType dataType() const =0
Data type managed by the variable (Real, Integer, ...).
virtual eItemKind itemKind() const =0
Kind of mesh entities on which the variable is based.
virtual bool isPartial() const =0
Indicates if the variable is partial.
virtual Integer dimension() const =0
Dimension of the variable.
virtual String name() const =0
Variable name.
Mesh entity group.
Definition ItemGroup.h:51
Interface for the material and environment manager of a mesh.
static IMeshMaterialMng * getReference(const MeshHandleOrMesh &mesh_handle, bool create=true)
Retrieves or creates the reference associated with mesh.
ItemGroupCollection groups() override
List of groups to save.
ConstArrayView< Real > times() override
List of saved times.
const String & baseDirectoryName() override
Name of the output directory for files.
Structure containing the information to create a service.
View for a 2D array whose size is an 'Int64'.
Definition Span2.h:320
View of an array of elements of type T.
Definition Span.h:633
Unicode character string constructor.
const char * localstr() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:229
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessage info() const
Flow for an information message.
TraceMessage warning() const
Flow for a warning message.
1D data vector with value semantics (STL style).
void write(IVariable *var, IData *data) override
Writes the data data of the variable var.
IMeshMaterialMng * m_material_mng
Associated material manager (may be null).
Int64 m_max_write_size
Maximum size (in kilobytes) for a write operation.
String m_directory_name
Output directory.
WritePartInfo _computeWritePartInfo(Int64 local_size)
Computes the offset of our part and the total number of elements.
bool m_write_polyhedron_datasets
True if the datasets related to the polyhedron cells must be written for the current time step.
HFile m_file_id
HDF file identifier.
ItemGroupCollection m_groups
List of groups to save.
UniqueArray< Real > m_times
List of times.
void setMetaData(const String &meta_data) override
Sets the metadata information.
String m_full_filename
Current HDF filename.
IDataWriter * dataWriter() override
Returns the writer associated with this post-processor.
void close() override
Closes the writer. After closing, it can no longer be used.
void notifyBeginWrite() override
Notifies that an output is going to be performed with the current parameters.
void notifyEndWrite() override
Notifies that an output has just been performed.
__host__ __device__ Real2 min(Real2 a, Real2 b)
Returns the minimum of two Real2.
Definition MathUtils.h:346
ItemLocalIdListViewT< Node > NodeLocalIdView
View over the localId() of a list of nodes.
Definition ItemTypes.h:407
ItemGroupT< Cell > CellGroup
Group of cells.
Definition ItemTypes.h:184
ItemGroupT< Node > NodeGroup
Group of nodes.
Definition ItemTypes.h:168
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Coordinate type quantity at node.
Integer len(const char *s)
Returns the length of the string s.
Utility functions for Hdf5.
Definition Hdf5Utils.cc:35
Always enables tracing in Arcane parts concerning materials.
@ ReduceMax
Maximum of values.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Ref< TrueType > createRef(Args &&... args)
Creates an instance of type TrueType with arguments Args and returns a reference to it.
Collection< ItemGroup > ItemGroupCollection
Collection of mesh item groups.
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
@ Polyhedral
Polyhedral mesh.
Definition MeshKind.h:38
bool operator<(const Item &item1, const Item &item2)
Compare two entities.
Definition Item.h:566
ConstMemoryView makeConstMemoryView(const void *ptr, Int32 datatype_size, Int64 nb_element)
Creates a read-only memory view.
Definition MemoryView.cc:36
eItemKind
Mesh entity type.
@ IK_Node
Node mesh entity.
@ IK_Cell
Cell mesh entity.
double Real
Type representing a real number.
unsigned char Byte
Type of a byte.
Definition BaseTypes.h:42
eDataType
Data type.
Definition DataTypes.h:41
@ DT_Int32
32-bit integer data type
Definition DataTypes.h:45
@ DT_Real3
Vector 3 data type.
Definition DataTypes.h:49
@ DT_Int64
64-bit integer data type
Definition DataTypes.h:46
@ DT_Real2
Vector 2 data type.
Definition DataTypes.h:48
@ DT_Real
Real data type.
Definition DataTypes.h:43
@ Cell
The mesh is AMR by cell.
Definition MeshKind.h:53
std::int32_t Int32
Signed integer type of 32 bits.
ConstArrayView< Real > RealConstArrayView
C equivalent of a 1D array of reals.
Definition UtilsTypes.h:482
Stores info about the data to be saved and the associated offset.
Class to store a pair (hdf_group, dataset_name).
Class to store offset information.
Int64 offset() const
Offset value. (-1) if writing to the end of the array.
Offset information for the part to write associated with a rank.
Int64 m_size
Number of elements on my rank.
Int64 m_total_size
Number of elements across all ranks.