Arcane  4.1.12.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/VariableCollection.h"
29#include "arcane/core/IParallelMng.h"
30#include "arcane/core/IMesh.h"
31#include "arcane/core/internal/IParallelMngInternal.h"
32#include "arcane/core/internal/VtkCellTypes.h"
33#include "arcane/core/internal/GatherGroup.h"
34
35#include "arcane/core/materials/IMeshMaterialMng.h"
36#include "arcane/core/materials/IMeshEnvironment.h"
37
38#include "arcane/hdf5/Hdf5Utils.h"
39#include "arcane/hdf5/VtkHdfV2PostProcessor_axl.h"
40
41#include <map>
42
43// This format is described on the following web page:
44//
45// https://kitware.github.io/vtk-examples/site/VTKFileFormats/#hdf-file-formats
46//
47// Format 2.0 with integrated temporal evolution support is only available
48// in the VTK master branch starting from April 2023.
49
50/*---------------------------------------------------------------------------*/
51/*---------------------------------------------------------------------------*/
52
53// TODO: Add verification test for saved values
54
55// TODO: Look into saving uniqueId() (via vtkOriginalCellIds)
56
57// TODO: Look into how to avoid saving the mesh at every iteration if it
58// does not change.
59
60// TODO: Look into compression
61
62// TODO: handle 2D variables
63
64// TODO: outside of HDF5, implement a mechanism that groups several parts
65// of the cell into one. This will allow reducing the number of ghost cells
66// and using MPI/IO in hybrid mode.
67
68/*---------------------------------------------------------------------------*/
69/*---------------------------------------------------------------------------*/
70
71namespace Arcane
72{
73using namespace Hdf5Utils;
74using namespace Materials;
75
76namespace
77{
78 template <typename T> Span<const T>
79 asConstSpan(const T* v)
80 {
81 return Span<const T>(v, 1);
82 }
83} // namespace
84
85/*---------------------------------------------------------------------------*/
86/*---------------------------------------------------------------------------*/
87
88class VtkHdfV2DataWriter
89: public TraceAccessor
90, public IDataWriter
91{
92 public:
93
100 struct DatasetGroupAndName
101 {
102 public:
103
104 DatasetGroupAndName(HGroup& group_, const String& name_)
105 : group(group_)
106 , name(name_)
107 {}
108
109 public:
110
111 HGroup& group;
112 String name;
113 };
114
126 struct DatasetInfo
127 {
128 DatasetInfo() = default;
129 explicit DatasetInfo(const String& name)
130 : m_name(name)
131 {}
132 DatasetInfo(HGroup& _group, const String& name)
133 : m_group(&_group)
134 , m_name(name)
135 {}
136 bool isNull() const { return m_name.null(); }
137
138 HGroup* group() const { return m_group; }
139 const String& name() const { return m_name; }
141 Int64 offset() const { return m_offset; }
142 void setOffset(Int64 v) { m_offset = v; }
143 friend bool operator<(const DatasetInfo& s1, const DatasetInfo& s2)
144 {
145 return (s1.m_name < s2.m_name);
146 }
147
148 private:
149
150 HGroup* m_group = nullptr;
151 String m_name;
152 Int64 m_offset = -1;
153 };
154
157 {
158 public:
159
160 void setTotalSize(Int64 v) { m_total_size = v; }
161 void setSize(Int64 v) { m_size = v; }
162 void setOffset(Int64 v) { m_offset = v; }
163
164 Int64 totalSize() const { return m_total_size; }
165 Int64 size() const { return m_size; }
166 Int64 offset() const { return m_offset; }
167
168 private:
169
176 };
177
179 struct ItemGroupCollectiveInfo
180 {
181 public:
182
183 explicit ItemGroupCollectiveInfo(const ItemGroup& g)
184 : m_item_group(g)
185 {}
186
187 public:
188
189 void setWritePartInfo(const WritePartInfo& part_info) { m_write_part_info = part_info; }
190 const WritePartInfo& writePartInfo() const { return m_write_part_info; }
191
192 public:
193
198 };
199
203 struct DataInfo
204 {
205 public:
206
207 DataInfo(const DatasetGroupAndName& dname, const DatasetInfo& dataset_info)
208 : dataset(dname)
209 , m_dataset_info(dataset_info)
210 {
211 }
212 DataInfo(const DatasetGroupAndName& dname, const DatasetInfo& dataset_info,
213 ItemGroupCollectiveInfo* group_info)
214 : dataset(dname)
215 , m_dataset_info(dataset_info)
216 , m_group_info(group_info)
217 {
218 }
219
220 public:
221
222 DatasetInfo datasetInfo() const { return m_dataset_info; }
223
224 public:
225
226 DatasetGroupAndName dataset;
227 DatasetInfo m_dataset_info;
228 ItemGroupCollectiveInfo* m_group_info = nullptr;
229 };
230
231 public:
232
233 VtkHdfV2DataWriter(IMesh* mesh, const ItemGroupCollection& groups, bool is_collective_io);
234
235 public:
236
237 void beginWrite(const VariableCollection& vars) override;
238 void endWrite() override;
239 void setMetaData(const String& meta_data) override;
240 void write(IVariable* var, IData* data) override;
241
242 public:
243
244 void setTimes(RealConstArrayView times) { m_times = times; }
245 void setDirectoryName(const String& dir_name) { m_directory_name = dir_name; }
246 void setMaxWriteSize(Int64 v) { m_max_write_size = v; }
247
248 private:
249
251 IMesh* m_mesh = nullptr;
252
255
258
261
264
267
270
271 HGroup m_top_group;
272 HGroup m_cell_data_group;
273 HGroup m_node_data_group;
274
275 HGroup m_steps_group;
276 HGroup m_point_data_offsets_group;
277 HGroup m_cell_data_offsets_group;
278 HGroup m_field_data_offsets_group;
279
280 bool m_is_parallel = false;
281 bool m_is_collective_io = false;
282 bool m_is_first_call = false;
283 bool m_is_writer = false;
284 Int32 m_writer = 0;
285
286 DatasetInfo m_cell_offset_info;
287 DatasetInfo m_point_offset_info;
288 DatasetInfo m_connectivity_offset_info;
289 DatasetInfo m_offset_for_cell_offset_info;
290 DatasetInfo m_part_offset_info;
291 DatasetInfo m_time_offset_info;
292 std::map<DatasetInfo, Int64> m_offset_info_list;
293
294 StandardTypes m_standard_types{ false };
295
296 ItemGroupCollectiveInfo m_all_cells_info;
297 ItemGroupCollectiveInfo m_all_nodes_info;
299
300 GatherGroupInfo m_all_cells_gather_group_info;
301 GatherGroupInfo m_all_nodes_gather_group_info;
302 UniqueArray<Ref<GatherGroupInfo>> m_gather_info_materials_groups;
303
311
312 private:
313
314 void _addInt64ArrayAttribute(Hid& hid, const char* name, Span<const Int64> values);
315 void _addStringAttribute(Hid& hid, const char* name, const String& value);
316
317 template <typename DataType> void
318 _writeDataSet1D(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values);
319 template <typename DataType> void
320 _writeDataSet1DUsingCollectiveIO(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values);
321 template <typename DataType> void
322 _writeDataSet1DCollective(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values);
323 template <typename DataType> void
324 _writeDataSet2D(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values);
325 template <typename DataType> void
326 _writeDataSet2DUsingCollectiveIO(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values);
327 template <typename DataType> void
328 _writeDataSet2DCollective(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values);
329 template <typename DataType> void
330 _writeBasicTypeDataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data);
331 void _writeReal3Dataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data);
332 void _writeReal2Dataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data);
333
334 String _getFileName()
335 {
336 StringBuilder sb(m_mesh->name());
337 sb += ".hdf";
338 return sb.toString();
339 }
340 template <typename DataType> void
341 _writeDataSetGeneric(const DataInfo& data_info, GatherGroupInfo* gather_info, Int32 nb_dim,
342 Int64 dim1_size, Int64 dim2_size, const DataType* values_data,
343 bool is_collective);
344 void _writeDataSetGeneric(const DataInfo& data_info, GatherGroupInfo* gather_info, Int32 nb_dim,
345 Int64 dim1_size, Int64 dim2_size, ConstMemoryView values_data,
346 const hid_t hdf_datatype_type, bool is_collective);
347 void _addInt64Attribute(Hid& hid, const char* name, Int64 value);
348 Int64 _readInt64Attribute(Hid& hid, const char* name);
349 void _openOrCreateGroups();
350 void _closeGroups();
351 void _readAndSetOffset(DatasetInfo& offset_info, Int32 wanted_step);
352 void _initializeOffsets();
353 void _initializeItemGroupCollectiveInfos(ItemGroupCollectiveInfo& group_info, GatherGroupInfo& gather_info);
354 WritePartInfo _computeWritePartInfo(Int64 local_size);
355 void _writeConstituentsGroups();
356};
357
358/*---------------------------------------------------------------------------*/
359/*---------------------------------------------------------------------------*/
360
361VtkHdfV2DataWriter::
362VtkHdfV2DataWriter(IMesh* mesh, const ItemGroupCollection& groups, bool is_collective_io)
363: TraceAccessor(mesh->traceMng())
364, m_mesh(mesh)
365, m_groups(groups)
366, m_is_collective_io(is_collective_io)
367, m_all_cells_info(mesh->allCells())
368, m_all_nodes_info(mesh->allNodes())
369, m_all_cells_gather_group_info(mesh->parallelMng(), is_collective_io)
370, m_all_nodes_gather_group_info(mesh->parallelMng(), is_collective_io)
371{
372}
373
374/*---------------------------------------------------------------------------*/
375/*---------------------------------------------------------------------------*/
376
377void VtkHdfV2DataWriter::
378beginWrite(const VariableCollection& vars)
379{
380 ARCANE_UNUSED(vars);
381
382 // Retrieve the material manager if it exists
384
385 IParallelMng* pm = m_mesh->parallelMng();
386 const Int32 nb_rank = pm->commSize();
387 m_is_parallel = nb_rank > 1;
388
389 Int32 time_index = m_times.size();
390 const bool is_first_call = (time_index < 2);
391 m_is_first_call = is_first_call;
392 if (is_first_call)
393 info() << "WARNING: The 'VtkHdfV2' implementation is experimental";
394
395 String filename = _getFileName();
396
397 Directory dir(m_directory_name);
398
399 m_full_filename = dir.file(filename);
400 info(4) << "VtkHdfV2DataWriter::beginWrite() file=" << m_full_filename;
401
402 HInit();
403 HInit::useMutex(pm->isThreadImplementation(), pm);
404
405 // It is possible to use the collective mode of HDF5 via MPI-IO in the following cases:
406 // * Hdf5 was compiled with MPI,
407 // * we are in pure MPI mode (neither shared memory mode nor hybrid mode).
408 m_is_collective_io = m_is_collective_io && (pm->isParallel() && HInit::hasParallelHdf5());
409 if (pm->isThreadImplementation() && !pm->isHybridImplementation())
410 m_is_collective_io = false;
411
412 if (is_first_call) {
413 info() << "VtkHdfV2DataWriter: using collective MPI/IO ?=" << m_is_collective_io;
414 info() << "VtkHdfV2DataWriter: max_write_size (kB) =" << m_max_write_size;
415 info() << "VtkHdfV2DataWriter: has_material?=" << (m_material_mng != nullptr);
416 }
417
418 bool is_master_io = pm->isMasterIO();
419
420 // True if we must participate in the writes
421 // If we use MPI/IO with HDF5, all
422 // ranks must perform all write operations to guarantee
423 // metadata consistency.
424 if (m_is_collective_io) {
425 m_writer = pm->_internalApi()->masterParallelIORank();
426 m_is_writer = (m_writer == pm->commRank());
427 }
428 else {
429 m_writer = pm->masterIORank();
430 m_is_writer = is_master_io;
431 }
432
433 // Indicates that we are using MPI/IO if requested
434 HProperty plist_id;
435 if (m_is_collective_io && m_is_writer)
436 plist_id.createFilePropertyMPIIO(pm);
437
438 // Even with MPI-IO, only one proc must create the directory.
439 if (is_first_call && is_master_io)
440 dir.createDirectory();
441
442 if (m_is_collective_io)
443 pm->barrier();
444
445 if (m_is_writer) {
446 m_standard_types.initialize();
447
448 if (is_first_call)
449 m_file_id.openTruncate(m_full_filename, plist_id.id());
450 else
451 m_file_id.openAppend(m_full_filename, plist_id.id());
452
453 _openOrCreateGroups();
454
455 if (is_first_call) {
456 std::array<Int64, 2> version = { 2, 0 };
457 _addInt64ArrayAttribute(m_top_group, "Version", version);
458 _addStringAttribute(m_top_group, "Type", "UnstructuredGrid");
459 }
460 }
461
462 // Initializes collective information on cell and node groups
463 _initializeItemGroupCollectiveInfos(m_all_cells_info, m_all_cells_gather_group_info);
464 _initializeItemGroupCollectiveInfos(m_all_nodes_info, m_all_nodes_gather_group_info);
465
466 CellGroup all_cells = m_mesh->allCells();
467 NodeGroup all_nodes = m_mesh->allNodes();
468
469 const Int32 nb_cell = all_cells.size();
470 const Int32 nb_node = all_nodes.size();
471
472 Int32 total_nb_connected_node = 0;
473 ENUMERATE_ (Cell, icell, all_cells) {
474 Cell cell = *icell;
475 total_nb_connected_node += cell.nodeIds().size();
476 }
477
478 // For the offsets, the array size is equal
479 // to the number of cells plus 1.
480 UniqueArray<Int64> cells_connectivity(total_nb_connected_node);
481 UniqueArray<Int64> cells_offset(nb_cell + 1);
482 UniqueArray<unsigned char> cells_ghost_type(nb_cell);
483 UniqueArray<unsigned char> cells_type(nb_cell);
484 UniqueArray<Int64> cells_uid(nb_cell);
485 cells_offset[0] = 0;
486 {
487 Int32 connected_node_index = 0;
488 ENUMERATE_CELL (icell, all_cells) {
489 Int32 index = icell.index();
490 Cell cell = *icell;
491
492 cells_uid[index] = cell.uniqueId();
493
494 Byte ghost_type = 0;
495 bool is_ghost = !cell.isOwn();
496 if (is_ghost)
497 ghost_type = VtkUtils::CellGhostTypes::DUPLICATECELL;
498 cells_ghost_type[index] = ghost_type;
499
500 unsigned char vtk_type = VtkUtils::arcaneToVtkCellType(cell.type());
501 cells_type[index] = vtk_type;
502 for (NodeLocalId node : cell.nodeIds()) {
503 cells_connectivity[connected_node_index] = node;
504 ++connected_node_index;
505 }
506 cells_offset[index + 1] = connected_node_index;
507 }
508 }
509
510 _initializeOffsets();
511
512 // ggi is a GatherGroupInfo for arrays that are not of size nb_cell or nb_node.
513 Ref<GatherGroupInfo> ggi_ref = createRef<GatherGroupInfo>(m_mesh->parallelMng(), m_is_collective_io);
514 GatherGroupInfo* ggi = ggi_ref.get();
515
516 // TODO: create an offset for this object (or look into how to calculate it automatically
517 _writeDataSet1DCollective<Int64>({ { m_top_group, "Offsets" }, m_offset_for_cell_offset_info }, ggi, cells_offset);
518 ggi->setNeedRecompute();
519
520 _writeDataSet1DCollective<Int64>({ { m_top_group, "Connectivity" }, m_connectivity_offset_info }, ggi, cells_connectivity);
521 ggi->setNeedRecompute();
522
523 _writeDataSet1DCollective<unsigned char>({ { m_top_group, "Types" }, m_cell_offset_info }, &m_all_cells_gather_group_info, cells_type);
524
525 {
526 Int64 nb_cell_int64 = nb_cell;
527 _writeDataSet1DCollective<Int64>({ { m_top_group, "NumberOfCells" }, m_part_offset_info }, ggi,
528 asConstSpan(&nb_cell_int64));
529
530 Int64 nb_node_int64 = nb_node;
531 _writeDataSet1DCollective<Int64>({ { m_top_group, "NumberOfPoints" }, m_part_offset_info }, ggi,
532 asConstSpan(&nb_node_int64));
533
534 Int64 number_of_connectivity_ids = cells_connectivity.size();
535 _writeDataSet1DCollective<Int64>({ { m_top_group, "NumberOfConnectivityIds" }, m_part_offset_info }, ggi,
536 asConstSpan(&number_of_connectivity_ids));
537 }
538 ggi->setNeedRecompute();
539
540 // Saves the unique IDs, types, and coordinates of the nodes.
541 {
542 UniqueArray<Int64> nodes_uid(nb_node);
543 UniqueArray<unsigned char> nodes_ghost_type(nb_node);
544 VariableNodeReal3& nodes_coordinates(m_mesh->nodesCoordinates());
545 UniqueArray2<Real> points;
546 points.resize(nb_node, 3);
547 ENUMERATE_ (Node, inode, all_nodes) {
548 Int32 index = inode.index();
549 Node node = *inode;
550
551 nodes_uid[index] = node.uniqueId();
552
553 Byte ghost_type = 0;
554 bool is_ghost = !node.isOwn();
555 if (is_ghost)
556 ghost_type = VtkUtils::PointGhostTypes::DUPLICATEPOINT;
557 nodes_ghost_type[index] = ghost_type;
558
559 Real3 pos = nodes_coordinates[inode];
560 points[index][0] = pos.x;
561 points[index][1] = pos.y;
562 points[index][2] = pos.z;
563 }
564
565 // Saves the unique ID of each node in the dataset "GlobalNodeId".
566 _writeDataSet1DCollective<Int64>({ { m_node_data_group, "GlobalIds" }, m_cell_offset_info }, &m_all_nodes_gather_group_info, nodes_uid);
567
568 // Saves the information about the node type (real or ghost).
569 _writeDataSet1DCollective<unsigned char>({ { m_node_data_group, "vtkGhostType" }, m_cell_offset_info }, &m_all_nodes_gather_group_info, nodes_ghost_type);
570
571 // Saves the coordinates of the nodes.
572 _writeDataSet2DCollective<Real>({ { m_top_group, "Points" }, m_point_offset_info }, ggi, points);
573 ggi->setNeedRecompute();
574 }
575
576 // Saves the information about the cell type (real or ghost)
577 _writeDataSet1DCollective<unsigned char>({ { m_cell_data_group, "vtkGhostType" }, m_cell_offset_info }, &m_all_cells_gather_group_info, cells_ghost_type);
578
579 // Saves the unique ID of each cell in the dataset "GlobalCellId".
580 // Using the dataset "vtkOriginalCellIds" does not work in Paraview.
581 _writeDataSet1DCollective<Int64>({ { m_cell_data_group, "GlobalIds" }, m_cell_offset_info }, &m_all_cells_gather_group_info, cells_uid);
582
583 if (m_is_writer) {
584 // List of times.
585 Real current_time = m_times[time_index - 1];
586 // TODO: Replace ggi with nullptr in non-collective mode?
587 _writeDataSet1D<Real>({ { m_steps_group, "Values" }, m_time_offset_info }, ggi, asConstSpan(&current_time));
588
589 // Part offset.
590 Int64 comm_size = pm->commSize();
591 Int64 part_offset = (time_index - 1) * comm_size;
592 _writeDataSet1D<Int64>({ { m_steps_group, "PartOffsets" }, m_time_offset_info }, ggi, asConstSpan(&part_offset));
593
594 // Number of times
595 _addInt64Attribute(m_steps_group, "NSteps", time_index);
596 }
597 //ggi->needRecompute();
598
599 _writeConstituentsGroups();
600}
601
602/*---------------------------------------------------------------------------*/
603/*---------------------------------------------------------------------------*/
604
605void VtkHdfV2DataWriter::
606_writeConstituentsGroups()
607{
608 if (!m_material_mng)
609 return;
610
611 // Fills the information for groups related to constituents
612 // NOTE: For now, we only process the media.
613 for (IMeshEnvironment* env : m_material_mng->environments()) {
614 CellGroup cells = env->cells();
615
616 Ref<ItemGroupCollectiveInfo> group_info_ref = createRef<ItemGroupCollectiveInfo>(cells);
617 m_materials_groups.add(group_info_ref);
618
619 Ref<GatherGroupInfo> gather_info_ref = createRef<GatherGroupInfo>(m_material_mng->mesh()->parallelMng(), m_is_collective_io);
620 m_gather_info_materials_groups.add(gather_info_ref);
621
622 ItemGroupCollectiveInfo& group_info = *group_info_ref.get();
623 GatherGroupInfo& gather_info = *gather_info_ref.get();
624 _initializeItemGroupCollectiveInfos(group_info, gather_info);
625
626 ConstArrayView<Int32> groups_ids = cells.view().localIds();
627 DatasetGroupAndName dataset_group_name(m_top_group, String("Constituent_") + cells.name());
628 if (m_is_first_call)
629 info() << "Writing infos for group '" << cells.name() << "'";
630 _writeDataSet1DCollective<Int32>({ dataset_group_name, m_cell_offset_info, group_info_ref.get() }, gather_info_ref.get(), groups_ids);
631 }
632}
633
634/*---------------------------------------------------------------------------*/
635/*---------------------------------------------------------------------------*/
641{
642 // TODO: look into using a scan.
643 IParallelMng* pm = m_mesh->parallelMng();
644 Int32 nb_rank = pm->commSize();
645 Int32 my_rank = pm->commRank();
646
647 UniqueArray<Int64> ranks_size(nb_rank);
648 ArrayView<Int64> all_sizes(ranks_size);
649 Int64 dim1_size = local_size;
650 pm->allGather(ConstArrayView<Int64>(1, &dim1_size), all_sizes);
651
652 Int64 total_size = 0;
653 for (Integer i = 0; i < nb_rank; ++i)
654 total_size += all_sizes[i];
655
656 Int64 my_index = 0;
657 for (Integer i = 0; i < my_rank; ++i)
658 my_index += all_sizes[i];
659
660 WritePartInfo part_info;
661 part_info.setTotalSize(total_size);
662 part_info.setSize(local_size);
663 part_info.setOffset(my_index);
664 return part_info;
665}
666
667/*---------------------------------------------------------------------------*/
668/*---------------------------------------------------------------------------*/
669
670void VtkHdfV2DataWriter::
671_initializeItemGroupCollectiveInfos(ItemGroupCollectiveInfo& group_info, GatherGroupInfo& gather_info)
672{
673 Int32 dim1_size = group_info.m_item_group.size();
674
675 gather_info.setCollectiveIO(m_is_collective_io);
676 gather_info.computeSize(dim1_size);
677
678 Int32 computed_nb_elem = gather_info.nbElemOutput();
679
680 group_info.setWritePartInfo(_computeWritePartInfo(computed_nb_elem));
681}
682
683namespace
684{
685 std::pair<Int64, Int64> _getInterval(Int64 index, Int64 nb_interval, Int64 total_size)
686 {
687 Int64 n = total_size;
688 Int64 isize = n / nb_interval;
689 Int64 ibegin = index * isize;
690 // For the last interval, take the remaining elements
691 if ((index + 1) == nb_interval)
692 isize = n - ibegin;
693 return { ibegin, isize };
694 }
695} // namespace
696
697/*---------------------------------------------------------------------------*/
698/*---------------------------------------------------------------------------*/
705void VtkHdfV2DataWriter::
706_writeDataSetGeneric(const DataInfo& data_info, GatherGroupInfo* gather_info, Int32 nb_dim,
707 Int64 dim1_size, Int64 dim2_size,
708 ConstMemoryView values_data,
709 const hid_t hdf_type, bool is_collective)
710{
711 ARCANE_CHECK_POINTER(gather_info);
712 if (nb_dim == 1)
713 dim2_size = 1;
714
715 HGroup& group = data_info.dataset.group;
716 const String& name = data_info.dataset.name;
717
718 // If positive or zero, indicates the write offset.
719 // Otherwise, we write to the end of the current dataset.
720 Int64 wanted_offset = data_info.datasetInfo().offset();
721
722 static constexpr int MAX_DIM = 2;
723 HDataset dataset;
724
725 // In case of collective operation, local_dims and global_dims are
726 // different on the first dimension. The second dimension is always
727 // identical for local_dims and global_dims and should not be modified during
728 // the entire calculation.
729
730 // Dimensions of the dataset that the current rank will write.
732 local_dims[0] = dim1_size;
733 local_dims[1] = dim2_size;
734
735 // Cumulative dimensions of all ranks for writing.
737
738 // Maximum dimensions of the DataSet
739 // For the second dimension, we assume it is constant over time.
741 max_dims[0] = H5S_UNLIMITED;
742 max_dims[1] = dim2_size;
743
744 herr_t herror = 0;
745 Int64 write_offset = 0;
746
747 Int64 my_index = 0;
748 Int64 global_dim1_size = dim1_size;
749 Int32 nb_participating_rank = 1;
750
751 if (is_collective) {
752 nb_participating_rank = gather_info->nbWriterGlobal();
753 WritePartInfo part_info;
754 if (data_info.m_group_info) {
755 // If the data is associated with a group, then the information
756 // about the offset has already been calculated
757 part_info = data_info.m_group_info->writePartInfo();
758 }
759 else {
760 part_info = _computeWritePartInfo(dim1_size);
761 }
762 global_dim1_size = part_info.totalSize();
763 my_index = part_info.offset();
764 }
765
766 // The only collective operation was _computeWritePartInfo().
767 if (!m_is_writer) {
768 return;
769 }
770
771 HProperty write_plist_id;
772 if constexpr (HInit::hasParallelHdf5()) {
773 if (is_collective)
775 else
777 }
778
779 HSpace file_space;
780 FixedArray<hsize_t, MAX_DIM> hyperslab_offsets;
781
782 if (m_is_first_call) {
783 // TODO: look into how to better calculate the chunk
785 global_dims[0] = global_dim1_size;
786 global_dims[1] = dim2_size;
787 // It is important that everyone has the same chunk size.
788 Int64 chunk_size = global_dim1_size / nb_participating_rank;
789 if (chunk_size < 1024)
790 chunk_size = 1024;
791 const Int64 max_chunk_size = 1024 * 1024 * 10;
792 chunk_size = math::min(chunk_size, max_chunk_size);
793 chunk_dims[0] = chunk_size;
794 chunk_dims[1] = dim2_size;
795 info() << "CHUNK nb_dim=" << nb_dim
796 << " global_dim1_size=" << global_dim1_size
797 << " chunk0=" << chunk_dims[0]
798 << " chunk1=" << chunk_dims[1]
799 << " name=" << name;
800 file_space.createSimple(nb_dim, global_dims.data(), max_dims.data());
801 HProperty plist_id;
802 plist_id.create(H5P_DATASET_CREATE);
803 H5Pset_chunk(plist_id.id(), nb_dim, chunk_dims.data());
804 dataset.create(group, name.localstr(), hdf_type, file_space, HProperty{}, plist_id, HProperty{});
805
806 if (is_collective) {
807 hyperslab_offsets[0] = my_index;
808 hyperslab_offsets[1] = 0;
809 }
810 }
811 else {
812 // Expands the first dimension of the dataset.
813 // We are going to add 'global_dim1_size' to this dimension.
814 dataset.open(group, name.localstr());
815 file_space = dataset.getSpace();
816 int nb_dimension = file_space.nbDimension();
817 if (nb_dimension != nb_dim)
818 ARCANE_THROW(IOException, "Bad dimension '{0}' for dataset '{1}' (should be 1)",
819 nb_dimension, name);
820 // TODO: Check that the second dimension is the same as the one saved.
821 FixedArray<hsize_t, MAX_DIM> original_dims;
822 file_space.getDimensions(original_dims.data(), nullptr);
823 hsize_t offset0 = original_dims[0];
824 // If we have a positive offset from DatasetInfo, we take it.
825 // This means we have performed a rollback.
826 if (wanted_offset >= 0) {
827 offset0 = wanted_offset;
828 info() << "Forcing offset to " << wanted_offset;
829 }
830 global_dims[0] = offset0 + global_dim1_size;
831 global_dims[1] = dim2_size;
832 write_offset = offset0;
833 // Expands the dataset.
834 // WARNING this invalidates file_space. It must therefore be re-read immediately after.
835 if ((herror = dataset.setExtent(global_dims.data())) < 0)
836 ARCANE_THROW(IOException, "Can not extent dataset '{0}' (err={1})", name, herror);
837 file_space = dataset.getSpace();
838
839 hyperslab_offsets[0] = offset0 + my_index;
840 hyperslab_offsets[1] = 0;
841 info(4) << "APPEND nb_dim=" << nb_dim
842 << " dim0=" << global_dims[0]
843 << " count0=" << local_dims[0]
844 << " offsets0=" << hyperslab_offsets[0] << " name=" << name;
845 }
846
847 Int64 nb_write_byte = global_dim1_size * dim2_size * values_data.datatypeSize();
848
849 // Performs the writing in multiple parts if requested.
850 // This is only possible for collective writing.
851 Int64 nb_interval = 1;
852 if (is_collective && m_max_write_size > 0) {
853 nb_interval = 1 + nb_write_byte / (m_max_write_size * 1024);
854 }
855 info(4) << "WRITE global_size=" << nb_write_byte << " max_size=" << m_max_write_size << " nb_interval=" << nb_interval;
856
857 for (Int64 i = 0; i < nb_interval; ++i) {
858 auto [index, nb_element] = _getInterval(i, nb_interval, dim1_size);
859 // Selects the part of the data to write
861 dims[0] = nb_element;
862 dims[1] = dim2_size;
864 offsets[0] = hyperslab_offsets[0] + index;
865 offsets[1] = 0;
866 if ((herror = H5Sselect_hyperslab(file_space.id(), H5S_SELECT_SET, offsets.data(), nullptr, dims.data(), nullptr)) < 0)
867 ARCANE_THROW(IOException, "Can not select hyperslab '{0}' (err={1})", name, herror);
868
869 HSpace memory_space;
870 memory_space.createSimple(nb_dim, dims.data());
871 Int64 data_offset = index * values_data.datatypeSize() * dim2_size;
872 // Performs the writing
873 if ((herror = dataset.write(hdf_type, values_data.data() + data_offset, memory_space, file_space, write_plist_id)) < 0)
874 ARCANE_THROW(IOException, "Can not write dataset '{0}' (err={1})", name, herror);
875
876 if (dataset.isBad())
877 ARCANE_THROW(IOException, "Can not write dataset '{0}'", name);
878 }
879
880 if (!data_info.datasetInfo().isNull())
881 m_offset_info_list.insert(std::make_pair(data_info.datasetInfo(), write_offset));
882}
883
884/*---------------------------------------------------------------------------*/
885/*---------------------------------------------------------------------------*/
886
887template <typename DataType> void VtkHdfV2DataWriter::
888_writeDataSetGeneric(const DataInfo& data_info, GatherGroupInfo* gather_info, Int32 nb_dim,
889 Int64 dim1_size, Int64 dim2_size, const DataType* values_data,
890 bool is_collective)
891{
892 const hid_t hdf_type = m_standard_types.nativeType(DataType{});
893 ConstMemoryView mem_view = makeConstMemoryView(values_data, sizeof(DataType), dim1_size * dim2_size);
894 _writeDataSetGeneric(data_info, gather_info, nb_dim, dim1_size, dim2_size, mem_view, hdf_type, is_collective);
895}
896
897/*---------------------------------------------------------------------------*/
898/*---------------------------------------------------------------------------*/
899
900template <typename DataType> void VtkHdfV2DataWriter::
901_writeDataSet1D(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values)
902{
903 _writeDataSetGeneric(data_info, gather_info, 1, values.size(), 1, values.data(), false);
904}
905
906/*---------------------------------------------------------------------------*/
907/*---------------------------------------------------------------------------*/
908
909template <typename DataType> void VtkHdfV2DataWriter::
910_writeDataSet1DUsingCollectiveIO(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values)
911{
912 _writeDataSetGeneric(data_info, gather_info, 1, values.size(), 1, values.data(), true);
913}
914
915/*---------------------------------------------------------------------------*/
916/*---------------------------------------------------------------------------*/
917
918template <typename DataType> void VtkHdfV2DataWriter::
919_writeDataSet1DCollective(const DataInfo& data_info, GatherGroupInfo* gather_info, Span<const DataType> values)
920{
921 ARCANE_CHECK_POINTER(gather_info);
922
923 GatherGroup gg;
924
925 gather_info->computeSizeT(values);
926 gg.setGatherGroupInfo(gather_info);
927
928 if (gg.isNeedGather()) {
929 UniqueArray<DataType> all_values;
930 gg.gatherToMasterIOT(values, all_values);
931
932 if (m_is_collective_io)
933 _writeDataSet1DUsingCollectiveIO(data_info, gather_info, all_values.constSpan());
934 else
935 _writeDataSet1D(data_info, gather_info, all_values.constSpan());
936 }
937 else {
938 if (m_is_collective_io)
939 _writeDataSet1DUsingCollectiveIO(data_info, gather_info, values);
940 else
941 _writeDataSet1D(data_info, gather_info, values);
942 }
943}
944
945/*---------------------------------------------------------------------------*/
946/*---------------------------------------------------------------------------*/
947
948template <typename DataType> void VtkHdfV2DataWriter::
949_writeDataSet2D(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values)
950{
951 _writeDataSetGeneric(data_info, gather_info, 2, values.dim1Size(), values.dim2Size(), values.data(), false);
952}
953
954/*---------------------------------------------------------------------------*/
955/*---------------------------------------------------------------------------*/
956
957template <typename DataType> void VtkHdfV2DataWriter::
958_writeDataSet2DUsingCollectiveIO(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values)
959{
960 _writeDataSetGeneric(data_info, gather_info, 2, values.dim1Size(), values.dim2Size(), values.data(), true);
961}
962
963/*---------------------------------------------------------------------------*/
964/*---------------------------------------------------------------------------*/
965
966template <typename DataType> void VtkHdfV2DataWriter::
967_writeDataSet2DCollective(const DataInfo& data_info, GatherGroupInfo* gather_info, Span2<const DataType> values)
968{
969 ARCANE_CHECK_POINTER(gather_info);
970
971 GatherGroup gg;
972
973 gather_info->computeSizeT(values);
974 gg.setGatherGroupInfo(gather_info);
975
976 if (gg.isNeedGather()) {
977 UniqueArray2<DataType> all_values;
978 gg.gatherToMasterIOT(values, all_values);
979
980 if (m_is_collective_io)
981 _writeDataSet2DUsingCollectiveIO(data_info, gather_info, all_values.constSpan());
982 else
983 _writeDataSet2D(data_info, gather_info, all_values.constSpan());
984 }
985 else {
986 if (m_is_collective_io)
987 _writeDataSet2DUsingCollectiveIO(data_info, gather_info, values);
988 else
989 _writeDataSet2D(data_info, gather_info, values);
990 }
991}
992
993/*---------------------------------------------------------------------------*/
994/*---------------------------------------------------------------------------*/
995
996void VtkHdfV2DataWriter::
997_addInt64ArrayAttribute(Hid& hid, const char* name, Span<const Int64> values)
998{
999 hsize_t len = values.size();
1000 hid_t aid = H5Screate_simple(1, &len, nullptr);
1001 hid_t attr = H5Acreate2(hid.id(), name, H5T_NATIVE_INT64, aid, H5P_DEFAULT, H5P_DEFAULT);
1002 if (attr < 0)
1003 ARCANE_FATAL("Can not create attribute '{0}'", name);
1004 int ret = H5Awrite(attr, H5T_NATIVE_INT64, values.data());
1005 if (ret < 0)
1006 ARCANE_FATAL("Can not write attribute '{0}'", name);
1007 H5Aclose(attr);
1008 H5Sclose(aid);
1009}
1010
1011/*---------------------------------------------------------------------------*/
1012/*---------------------------------------------------------------------------*/
1013
1014void VtkHdfV2DataWriter::
1015_addInt64Attribute(Hid& hid, const char* name, Int64 value)
1016{
1017 HSpace aid(H5Screate(H5S_SCALAR));
1018 HAttribute attr;
1019 if (m_is_first_call)
1020 attr.create(hid, name, H5T_NATIVE_INT64, aid);
1021 else
1022 attr.open(hid, name);
1023 if (attr.isBad())
1024 ARCANE_FATAL("Can not create attribute '{0}'", name);
1025 herr_t ret = attr.write(H5T_NATIVE_INT64, &value);
1026 if (ret < 0)
1027 ARCANE_FATAL("Can not write attribute '{0}'", name);
1028}
1029
1030/*---------------------------------------------------------------------------*/
1031/*---------------------------------------------------------------------------*/
1032
1033Int64 VtkHdfV2DataWriter::
1034_readInt64Attribute(Hid& hid, const char* name)
1035{
1036 HAttribute attr;
1037 attr.open(hid, name);
1038 if (attr.isBad())
1039 ARCANE_FATAL("Can not open attribute '{0}'", name);
1040 Int64 value;
1041 herr_t ret = attr.read(H5T_NATIVE_INT64, &value);
1042 if (ret < 0)
1043 ARCANE_FATAL("Can not read attribute '{0}'", name);
1044 return value;
1045}
1046
1047/*---------------------------------------------------------------------------*/
1048/*---------------------------------------------------------------------------*/
1049
1050void VtkHdfV2DataWriter::
1051_addStringAttribute(Hid& hid, const char* name, const String& value)
1052{
1053 hid_t aid = H5Screate(H5S_SCALAR);
1054 hid_t attr_type = H5Tcopy(H5T_C_S1);
1055 H5Tset_size(attr_type, value.length());
1056 hid_t attr = H5Acreate2(hid.id(), name, attr_type, aid, H5P_DEFAULT, H5P_DEFAULT);
1057 if (attr < 0)
1058 ARCANE_FATAL("Can not create attribute {0}", name);
1059 int ret = H5Awrite(attr, attr_type, value.localstr());
1060 ret = H5Tclose(attr_type);
1061 if (ret < 0)
1062 ARCANE_FATAL("Can not write attribute '{0}'", name);
1063 H5Aclose(attr);
1064 H5Sclose(aid);
1065}
1066
1067/*---------------------------------------------------------------------------*/
1068/*---------------------------------------------------------------------------*/
1069
1070void VtkHdfV2DataWriter::
1071endWrite()
1072{
1073 // Save the recorded offsets
1074
1075 if (m_is_writer) {
1076 for (const auto& i : m_offset_info_list) {
1077 Int64 offset = i.second;
1078 const DatasetInfo& offset_info = i.first;
1079 HGroup* hdf_group = offset_info.group();
1080 //info() << "OFFSET_INFO name=" << offset_info.name() << " offset=" << offset;
1081 if (hdf_group) {
1082 Ref<GatherGroupInfo> ggi = createRef<GatherGroupInfo>(m_mesh->parallelMng(), m_is_collective_io);
1083 _writeDataSet1D<Int64>({ { *hdf_group, offset_info.name() }, m_time_offset_info }, ggi.get(), asConstSpan(&offset));
1084 }
1085 }
1086 }
1087 _closeGroups();
1088 m_file_id.close();
1089}
1090
1091/*---------------------------------------------------------------------------*/
1092/*---------------------------------------------------------------------------*/
1093
1094void VtkHdfV2DataWriter::
1095_openOrCreateGroups()
1096{
1097 // Any group opened here must be closed in closeGroups().
1098 m_top_group.openOrCreate(m_file_id, "VTKHDF");
1099 m_cell_data_group.openOrCreate(m_top_group, "CellData");
1100 m_node_data_group.openOrCreate(m_top_group, "PointData");
1101 m_steps_group.openOrCreate(m_top_group, "Steps");
1102 m_point_data_offsets_group.openOrCreate(m_steps_group, "PointDataOffsets");
1103 m_cell_data_offsets_group.openOrCreate(m_steps_group, "CellDataOffsets");
1104 m_field_data_offsets_group.openOrCreate(m_steps_group, "FieldDataOffsets");
1105}
1106
1107/*---------------------------------------------------------------------------*/
1108/*---------------------------------------------------------------------------*/
1109
1110void VtkHdfV2DataWriter::
1111_closeGroups()
1112{
1113 m_cell_data_group.close();
1114 m_node_data_group.close();
1115 m_point_data_offsets_group.close();
1116 m_cell_data_offsets_group.close();
1117 m_field_data_offsets_group.close();
1118 m_steps_group.close();
1119 m_top_group.close();
1120}
1121
1122/*---------------------------------------------------------------------------*/
1123/*---------------------------------------------------------------------------*/
1124
1126setMetaData(const String& meta_data)
1127{
1128 ARCANE_UNUSED(meta_data);
1129}
1130
1131/*---------------------------------------------------------------------------*/
1132/*---------------------------------------------------------------------------*/
1133
1135write(IVariable* var, IData* data)
1136{
1137 info(4) << "Write VtkHdfV2 var=" << var->name();
1138
1139 eItemKind item_kind = var->itemKind();
1140
1141 if (var->dimension() != 1)
1142 ARCANE_FATAL("Only export of scalar item variable is implemented (name={0})", var->name());
1143 if (var->isPartial())
1144 ARCANE_FATAL("Export of partial variable is not implemented");
1145
1146 HGroup* group = nullptr;
1147 DatasetInfo offset_info;
1148 ItemGroupCollectiveInfo* group_info = nullptr;
1149 GatherGroupInfo* gather_info = nullptr;
1150 switch (item_kind) {
1151 case IK_Cell:
1152 group = &m_cell_data_group;
1153 offset_info = m_cell_offset_info;
1154 group_info = &m_all_cells_info;
1155 gather_info = &m_all_cells_gather_group_info;
1156 break;
1157 case IK_Node:
1158 group = &m_node_data_group;
1159 offset_info = m_point_offset_info;
1160 group_info = &m_all_nodes_info;
1161 gather_info = &m_all_nodes_gather_group_info;
1162 break;
1163 default:
1164 ARCANE_FATAL("Only export of 'Cell' or 'Node' variable is implemented (name={0})", var->name());
1165 }
1166
1167 ARCANE_CHECK_POINTER(group);
1168
1169 DataInfo data_info(DatasetGroupAndName{ *group, var->name() }, offset_info, group_info);
1170 eDataType data_type = var->dataType();
1171 switch (data_type) {
1172 case DT_Real:
1173 _writeBasicTypeDataset<Real>(data_info, gather_info, data);
1174 break;
1175 case DT_Int64:
1176 _writeBasicTypeDataset<Int64>(data_info, gather_info, data);
1177 break;
1178 case DT_Int32:
1179 _writeBasicTypeDataset<Int32>(data_info, gather_info, data);
1180 break;
1181 case DT_Real3:
1182 _writeReal3Dataset(data_info, gather_info, data);
1183 break;
1184 case DT_Real2:
1185 _writeReal2Dataset(data_info, gather_info, data);
1186 break;
1187 default:
1188 warning() << String::format("Export for datatype '{0}' is not supported (var_name={1})", data_type, var->name());
1189 }
1190}
1191
1192/*---------------------------------------------------------------------------*/
1193/*---------------------------------------------------------------------------*/
1194
1195template <typename DataType> void VtkHdfV2DataWriter::
1196_writeBasicTypeDataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data)
1197{
1198 auto* true_data = dynamic_cast<IArrayDataT<DataType>*>(data);
1199 ARCANE_CHECK_POINTER(true_data);
1200 _writeDataSet1DCollective(data_info, gather_info, Span<const DataType>(true_data->view()));
1201}
1202
1203/*---------------------------------------------------------------------------*/
1204/*---------------------------------------------------------------------------*/
1205
1206void VtkHdfV2DataWriter::
1207_writeReal3Dataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data)
1208{
1209 auto* true_data = dynamic_cast<IArrayDataT<Real3>*>(data);
1210 ARCANE_CHECK_POINTER(true_data);
1211 SmallSpan<const Real3> values(true_data->view());
1212 Int32 nb_value = values.size();
1213 // TODO: optimize this without passing through a temporary array
1214 UniqueArray2<Real> scalar_values;
1215 scalar_values.resize(nb_value, 3);
1216 for (Int32 i = 0; i < nb_value; ++i) {
1217 Real3 v = values[i];
1218 scalar_values[i][0] = v.x;
1219 scalar_values[i][1] = v.y;
1220 scalar_values[i][2] = v.z;
1221 }
1222 _writeDataSet2DCollective<Real>(data_info, gather_info, scalar_values);
1223}
1224
1225/*---------------------------------------------------------------------------*/
1226/*---------------------------------------------------------------------------*/
1227
1228void VtkHdfV2DataWriter::
1229_writeReal2Dataset(const DataInfo& data_info, GatherGroupInfo* gather_info, IData* data)
1230{
1231 // Convert to an array of 3 components where the last one will be 0.
1232 auto* true_data = dynamic_cast<IArrayDataT<Real2>*>(data);
1233 ARCANE_CHECK_POINTER(true_data);
1234 SmallSpan<const Real2> values(true_data->view());
1235 Int32 nb_value = values.size();
1236 UniqueArray2<Real> scalar_values;
1237 scalar_values.resize(nb_value, 3);
1238 for (Int32 i = 0; i < nb_value; ++i) {
1239 Real2 v = values[i];
1240 scalar_values[i][0] = v.x;
1241 scalar_values[i][1] = v.y;
1242 scalar_values[i][2] = 0.0;
1243 }
1244 _writeDataSet2DCollective<Real>(data_info, gather_info, scalar_values);
1245}
1246
1247/*---------------------------------------------------------------------------*/
1248/*---------------------------------------------------------------------------*/
1249
1250void VtkHdfV2DataWriter::
1251_readAndSetOffset(DatasetInfo& offset_info, Int32 wanted_step)
1252{
1253 HGroup* hgroup = offset_info.group();
1254 ARCANE_CHECK_POINTER(hgroup);
1255 StandardArrayT<Int64> a(hgroup->id(), offset_info.name());
1256 UniqueArray<Int64> values;
1257 a.directRead(m_standard_types, values);
1258 Int64 offset_value = values[wanted_step];
1259 offset_info.setOffset(offset_value);
1260 info() << "VALUES name=" << offset_info.name() << " values=" << values
1261 << " wanted_step=" << wanted_step << " v=" << offset_value;
1262}
1263
1264/*---------------------------------------------------------------------------*/
1265/*---------------------------------------------------------------------------*/
1266
1267void VtkHdfV2DataWriter::
1268_initializeOffsets()
1269{
1270 // There are 5 offset values used:
1271 //
1272 // - offset on the number of cells (CellOffsets). This offset has a number of elements
1273 // equal to the number of saved time steps and is increased at each number of cells output. This offset
1274 // is also used for cell variables
1275 // - offset on the number of nodes (PointOffsets). It is equivalent to 'CellOffsets' but
1276 // for nodes.
1277 // - offset for "NumberOfCells", "NumberOfPoints" and "NumberOfConnectivityIds". For each
1278 // of these fields there are NbPart values per time step, with 'NbPart' being the number of parts (and
1279 // thus the number of sub-domains if no grouping is done). There are thus a total of
1280 // NbPart * NbTimeStep in this offset field.
1281 // - offset for the "Connectivity" field, which is called "ConnectivityIdOffsets".
1282 // This offset has a number of elements equal to the number of saved time steps.
1283 // - offset for the "Offsets" field. "Offset" contains for each cell the offset in
1284 // "Connectivity" of the cell node connectivity. This offset is not saved,
1285 // but since this field has a number of values equal to the number of cells plus one it is possible
1286 // to deduce it from "CellOffsets" (it equals "CellOffsets" plus the current time index).
1287
1288 m_cell_offset_info = DatasetInfo(m_steps_group, "CellOffsets");
1289 m_point_offset_info = DatasetInfo(m_steps_group, "PointOffsets");
1290 m_connectivity_offset_info = DatasetInfo(m_steps_group, "ConnectivityIdOffsets");
1291 // These three offsets are not saved in the VTK format
1292 m_offset_for_cell_offset_info = DatasetInfo("_OffsetForCellOffsetInfo");
1293 m_part_offset_info = DatasetInfo("_PartOffsetInfo");
1294 m_time_offset_info = DatasetInfo("_TimeOffsetInfo");
1295
1296 // Check if we haven't done a backward run.
1297 // This is the case if the number of saved time steps is greater than the number
1298 // of values in \a m_times.
1299 if (m_is_writer && !m_is_first_call) {
1300 IParallelMng* pm = m_mesh->parallelMng();
1301 const Int32 nb_rank = pm->commSize();
1302 Int64 nb_current_step = _readInt64Attribute(m_steps_group, "NSteps");
1303 Int32 time_index = m_times.size();
1304 info(4) << "NB_STEP=" << nb_current_step << " time_index=" << time_index
1305 << " current_time=" << m_times.back();
1306 const bool debug_times = false;
1307 if (debug_times) {
1308 StandardArrayT<Real> a1(m_steps_group.id(), "Values");
1309 UniqueArray<Real> times;
1310 a1.directRead(m_standard_types, times);
1311 info() << "TIMES=" << times;
1312 }
1313 if ((nb_current_step + 1) != time_index) {
1314 info() << "[VtkHdf] go_backward detected";
1315 Int32 wanted_step = time_index - 1;
1316 // This means a backward run has been performed.
1317 // In this case, the offsets must be reread
1318 _readAndSetOffset(m_cell_offset_info, wanted_step);
1319 _readAndSetOffset(m_point_offset_info, wanted_step);
1320 _readAndSetOffset(m_connectivity_offset_info, wanted_step);
1321 m_part_offset_info.setOffset(wanted_step * nb_rank);
1322 m_time_offset_info.setOffset(wanted_step);
1323 m_offset_for_cell_offset_info.setOffset(m_cell_offset_info.offset() + wanted_step * nb_rank);
1324 }
1325 }
1326}
1327
1328/*---------------------------------------------------------------------------*/
1329/*---------------------------------------------------------------------------*/
1330
1331/*---------------------------------------------------------------------------*/
1332/*---------------------------------------------------------------------------*/
1336class VtkHdfV2PostProcessor
1338{
1339 public:
1340
1341 explicit VtkHdfV2PostProcessor(const ServiceBuildInfo& sbi)
1343 {
1344 }
1345
1346 IDataWriter* dataWriter() override { return m_writer.get(); }
1347 void notifyBeginWrite() override
1348 {
1349 bool use_collective_io = true;
1350 Int64 max_write_size = 0;
1351 if (options()) {
1352 use_collective_io = options()->useCollectiveWrite();
1353 max_write_size = options()->maxWriteSize();
1354 }
1355 auto w = std::make_unique<VtkHdfV2DataWriter>(mesh(), groups(), use_collective_io);
1356 w->setMaxWriteSize(max_write_size);
1357 w->setTimes(times());
1359 w->setDirectoryName(dir.file("vtkhdfv2"));
1360 m_writer = std::move(w);
1361 }
1362 void notifyEndWrite() override
1363 {
1364 m_writer = nullptr;
1365 }
1366 void close() override {}
1367
1368 private:
1369
1370 std::unique_ptr<IDataWriter> m_writer;
1371};
1372
1373/*---------------------------------------------------------------------------*/
1374/*---------------------------------------------------------------------------*/
1375
1376ARCANE_REGISTER_SERVICE_VTKHDFV2POSTPROCESSOR(VtkHdfV2PostProcessor,
1377 VtkHdfV2PostProcessor);
1378
1379/*---------------------------------------------------------------------------*/
1380/*---------------------------------------------------------------------------*/
1381
1382} // namespace Arcane
1383
1384/*---------------------------------------------------------------------------*/
1385/*---------------------------------------------------------------------------*/
#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.
Class managing a directory.
Definition Directory.h:36
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:89
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:896
void createDatasetTransfertIndependentMPIIO()
Creates a dataset property for MPIIO.
Definition Hdf5Utils.cc:915
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:324
View of an array of elements of type T.
Definition Span.h:635
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.
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
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:34
Always enables tracing in Arcane parts concerning materials.
-- 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.
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:43
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:488
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.