Arcane  v4.1.7.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
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/* Pos-traitement au format VTK HDF. */
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/VtkCellTypes.h"
32
33#include "arcane/core/materials/IMeshMaterialMng.h"
34#include "arcane/core/materials/IMeshEnvironment.h"
35
36#include "arcane/hdf5/Hdf5Utils.h"
37#include "arcane/hdf5/VtkHdfV2PostProcessor_axl.h"
38
39#include <map>
40
41// Ce format est décrit sur la page web suivante :
42//
43// https://kitware.github.io/vtk-examples/site/VTKFileFormats/#hdf-file-formats
44//
45// Le format 2.0 avec le support intégré de l'évolution temporelle n'est
46// disponible que dans la branche master de VTK à partir d'avril 2023.
47
48/*---------------------------------------------------------------------------*/
49/*---------------------------------------------------------------------------*/
50
51// TODO: Ajouter test de vérifcation des valeurs sauvegardées
52
53// TODO: Regarder la sauvegarde des uniqueId() (via vtkOriginalCellIds)
54
55// TODO: Regarder comment éviter de sauver le maillage à chaque itération s'il
56// ne change pas.
57
58// TODO: Regarder la compression
59
60// TODO: gérer les variables 2D
61
62// TODO: hors HDF5, faire un mécanisme qui regroupe plusieurs parties
63// du maillage en une seule. Cela permettra de réduire le nombre de mailles
64// fantômes et d'utiliser MPI/IO en mode hybride.
65
66/*---------------------------------------------------------------------------*/
67/*---------------------------------------------------------------------------*/
68
69namespace Arcane
70{
71using namespace Hdf5Utils;
72using namespace Materials;
73
74namespace
75{
76 template <typename T> Span<const T>
77 asConstSpan(const T* v)
78 {
79 return Span<const T>(v, 1);
80 }
81} // namespace
82
83/*---------------------------------------------------------------------------*/
84/*---------------------------------------------------------------------------*/
85
86class VtkHdfV2DataWriter
87: public TraceAccessor
88, public IDataWriter
89{
90 public:
91
92 /*!
93 * \brief Classe pour conserver un couple (hdf_group,nom_du_dataset).
94 *
95 * Les instances de cette classe utilisent une référence sur un groupe HDF5
96 * et ce dernier doit donc vivre plus longtemps que l'instance.
97 */
98 struct DatasetGroupAndName
99 {
100 public:
101
102 DatasetGroupAndName(HGroup& group_, const String& name_)
103 : group(group_)
104 , name(name_)
105 {}
106
107 public:
108
109 HGroup& group;
110 String name;
111 };
112
113 /*!
114 * \brief Classe pour conserver les information d'un offset.
115 *
116 * Il s'agit d'un couple (hdf_group,nom_du_dataset).
117 *
118 * Le groupe peut être nul auquel cas il s'agit d'un offset qui est
119 * uniquement calculé et qui ne sera pas sauvegardé.
120 *
121 * Les instances de cette classe utilisent une référence sur un groupe HDF5
122 * et ce dernier doit donc vivre plus longtemps que l'instance.
123 */
124 struct DatasetInfo
125 {
126 DatasetInfo() = default;
127 explicit DatasetInfo(const String& name)
128 : m_name(name)
129 {}
130 DatasetInfo(HGroup& _group, const String& name)
131 : m_group(&_group)
132 , m_name(name)
133 {}
134 bool isNull() const { return m_name.null(); }
135
136 HGroup* group() const { return m_group; }
137 const String& name() const { return m_name; }
138 //! Valeur de l'offset. (-1) si on écrit à la fin du tableau
139 Int64 offset() const { return m_offset; }
140 void setOffset(Int64 v) { m_offset = v; }
141 friend bool operator<(const DatasetInfo& s1, const DatasetInfo& s2)
142 {
143 return (s1.m_name < s2.m_name);
144 }
145
146 private:
147
148 HGroup* m_group = nullptr;
149 String m_name;
150 Int64 m_offset = -1;
151 };
152
153 //! Informations sur l'offset de la partie à écrire associée à un rang
155 {
156 public:
157
158 void setTotalSize(Int64 v) { m_total_size = v; }
159 void setSize(Int64 v) { m_size = v; }
160 void setOffset(Int64 v) { m_offset = v; }
161
162 Int64 totalSize() const { return m_total_size; }
163 Int64 size() const { return m_size; }
164 Int64 offset() const { return m_offset; }
165
166 private:
167
168 //! Nombre d'éléments sur tous les rangs
169 Int64 m_total_size = 0;
170 //! Nombre d'éléments de mon rang
171 Int64 m_size = 0;
172 //! Offset de mon rang
173 Int64 m_offset = -1;
174 };
175
176 //! Informations collectives sur un ItemGroup;
177 struct ItemGroupCollectiveInfo
178 {
179 public:
180
181 explicit ItemGroupCollectiveInfo(const ItemGroup& g)
182 : m_item_group(g)
183 {}
184
185 public:
186
187 void setWritePartInfo(const WritePartInfo& part_info) { m_write_part_info = part_info; }
188 const WritePartInfo& writePartInfo() const { return m_write_part_info; }
189
190 public:
191
192 //! Groupe associé
194 //! Informations sur l'écriture.
196 };
197
198 /*!
199 * \brief Conserve les infos sur les données à sauver et l'offset associé.
200 */
201 struct DataInfo
202 {
203 public:
204
205 DataInfo(const DatasetGroupAndName& dname, const DatasetInfo& dataset_info)
206 : dataset(dname)
207 , m_dataset_info(dataset_info)
208 {
209 }
210 DataInfo(const DatasetGroupAndName& dname, const DatasetInfo& dataset_info,
211 ItemGroupCollectiveInfo* group_info)
212 : dataset(dname)
213 , m_dataset_info(dataset_info)
214 , m_group_info(group_info)
215 {
216 }
217
218 public:
219
220 DatasetInfo datasetInfo() const { return m_dataset_info; }
221
222 public:
223
224 DatasetGroupAndName dataset;
225 DatasetInfo m_dataset_info;
226 ItemGroupCollectiveInfo* m_group_info = nullptr;
227 };
228
229 public:
230
231 VtkHdfV2DataWriter(IMesh* mesh, const ItemGroupCollection& groups, bool is_collective_io);
232
233 public:
234
235 void beginWrite(const VariableCollection& vars) override;
236 void endWrite() override;
237 void setMetaData(const String& meta_data) override;
238 void write(IVariable* var, IData* data) override;
239
240 public:
241
242 void setTimes(RealConstArrayView times) { m_times = times; }
243 void setDirectoryName(const String& dir_name) { m_directory_name = dir_name; }
244 void setMaxWriteSize(Int64 v) { m_max_write_size = v; }
245
246 private:
247
248 //! Maillage associé
249 IMesh* m_mesh = nullptr;
250
251 //! Gestionnaire de matériaux associé (peut-être nul)
252 IMeshMaterialMng* m_material_mng = nullptr;
253
254 //! Liste des groupes à sauver
255 ItemGroupCollection m_groups;
256
257 //! Liste des temps
258 UniqueArray<Real> m_times;
259
260 //! Nom du fichier HDF courant
261 String m_full_filename;
262
263 //! Répertoire de sortie.
264 String m_directory_name;
265
266 //! Identifiant HDF du fichier
267 HFile m_file_id;
268
269 HGroup m_top_group;
270 HGroup m_cell_data_group;
271 HGroup m_node_data_group;
272
273 HGroup m_steps_group;
274 HGroup m_point_data_offsets_group;
275 HGroup m_cell_data_offsets_group;
276 HGroup m_field_data_offsets_group;
277
278 bool m_is_parallel = false;
279 bool m_is_master_io = false;
280 bool m_is_collective_io = false;
281 bool m_is_first_call = false;
282 bool m_is_writer = false;
283
284 DatasetInfo m_cell_offset_info;
285 DatasetInfo m_point_offset_info;
286 DatasetInfo m_connectivity_offset_info;
287 DatasetInfo m_offset_for_cell_offset_info;
288 DatasetInfo m_part_offset_info;
289 DatasetInfo m_time_offset_info;
290 std::map<DatasetInfo, Int64> m_offset_info_list;
291
292 StandardTypes m_standard_types{ false };
293
294 ItemGroupCollectiveInfo m_all_cells_info;
295 ItemGroupCollectiveInfo m_all_nodes_info;
296 UniqueArray<Ref<ItemGroupCollectiveInfo>> m_materials_groups;
297
298 /*!
299 * \brief Taille maximale (en kilo-octet) pour une écriture.
300 *
301 * Si l'écriture dépasse cette taille, elle est scindée en plusieurs écriture.
302 * Cela peut être nécessaire avec MPI-IO pour les gros volumes.
303 */
304 Int64 m_max_write_size = 0;
305
306 private:
307
308 void _addInt64ArrayAttribute(Hid& hid, const char* name, Span<const Int64> values);
309 void _addStringAttribute(Hid& hid, const char* name, const String& value);
310
311 template <typename DataType> void
312 _writeDataSet1D(const DataInfo& data_info, Span<const DataType> values);
313 template <typename DataType> void
314 _writeDataSet1DUsingCollectiveIO(const DataInfo& data_info, Span<const DataType> values);
315 template <typename DataType> void
316 _writeDataSet1DCollective(const DataInfo& data_info, Span<const DataType> values);
317 template <typename DataType> void
318 _writeDataSet2D(const DataInfo& data_info, Span2<const DataType> values);
319 template <typename DataType> void
320 _writeDataSet2DUsingCollectiveIO(const DataInfo& data_info, Span2<const DataType> values);
321 template <typename DataType> void
322 _writeDataSet2DCollective(const DataInfo& data_info, Span2<const DataType> values);
323 template <typename DataType> void
324 _writeBasicTypeDataset(const DataInfo& data_info, IData* data);
325 void _writeReal3Dataset(const DataInfo& data_info, IData* data);
326 void _writeReal2Dataset(const DataInfo& data_info, IData* data);
327
328 String _getFileName()
329 {
330 StringBuilder sb(m_mesh->name());
331 sb += ".hdf";
332 return sb.toString();
333 }
334 template <typename DataType> void
335 _writeDataSetGeneric(const DataInfo& data_info, Int32 nb_dim,
336 Int64 dim1_size, Int64 dim2_size, const DataType* values_data,
337 bool is_collective);
338 void _writeDataSetGeneric(const DataInfo& data_info, Int32 nb_dim,
339 Int64 dim1_size, Int64 dim2_size, ConstMemoryView values_data,
340 const hid_t hdf_datatype_type, bool is_collective);
341 void _addInt64Attribute(Hid& hid, const char* name, Int64 value);
342 Int64 _readInt64Attribute(Hid& hid, const char* name);
343 void _openOrCreateGroups();
344 void _closeGroups();
345 void _readAndSetOffset(DatasetInfo& offset_info, Int32 wanted_step);
346 void _initializeOffsets();
347 void _initializeItemGroupCollectiveInfos(ItemGroupCollectiveInfo& group_info);
348 WritePartInfo _computeWritePartInfo(Int64 local_size);
349 void _writeConstituentsGroups();
350};
351
352/*---------------------------------------------------------------------------*/
353/*---------------------------------------------------------------------------*/
354
355VtkHdfV2DataWriter::
356VtkHdfV2DataWriter(IMesh* mesh, const ItemGroupCollection& groups, bool is_collective_io)
357: TraceAccessor(mesh->traceMng())
358, m_mesh(mesh)
359, m_groups(groups)
360, m_is_collective_io(is_collective_io)
361, m_all_cells_info(mesh->allCells())
362, m_all_nodes_info(mesh->allNodes())
363{
364}
365
366/*---------------------------------------------------------------------------*/
367/*---------------------------------------------------------------------------*/
368
369void VtkHdfV2DataWriter::
370beginWrite(const VariableCollection& vars)
371{
372 ARCANE_UNUSED(vars);
373
374 // Récupère le gestionnaire de matériaux s'il existe
375 m_material_mng = IMeshMaterialMng::getReference(m_mesh, false);
376
377 IParallelMng* pm = m_mesh->parallelMng();
378 const Int32 nb_rank = pm->commSize();
379 m_is_parallel = nb_rank > 1;
380 m_is_master_io = pm->isMasterIO();
381
382 Int32 time_index = m_times.size();
383 const bool is_first_call = (time_index < 2);
384 m_is_first_call = is_first_call;
385 if (is_first_call)
386 info() << "WARNING: L'implémentation au format 'VtkHdfV2' est expérimentale";
387
388 String filename = _getFileName();
389
390 Directory dir(m_directory_name);
391
392 m_full_filename = dir.file(filename);
393 info(4) << "VtkHdfV2DataWriter::beginWrite() file=" << m_full_filename;
394
395 HInit();
396 HInit::useMutex(pm->isThreadImplementation(), pm);
397
398 // Il est possible d'utiliser le mode collectif de HDF5 via MPI-IO dans les cas suivants :
399 // * Hdf5 a été compilé avec MPI,
400 // * on est en mode MPI pure (ni mode mémoire partagé, ni mode hybride).
401 m_is_collective_io = m_is_collective_io && (pm->isParallel() && HInit::hasParallelHdf5());
402 if (pm->isHybridImplementation() || pm->isThreadImplementation())
403 m_is_collective_io = false;
404
405 if (is_first_call) {
406 info() << "VtkHdfV2DataWriter: using collective MPI/IO ?=" << m_is_collective_io;
407 info() << "VtkHdfV2DataWriter: max_write_size (kB) =" << m_max_write_size;
408 info() << "VtkHdfV2DataWriter: has_material?=" << (m_material_mng != nullptr);
409 }
410
411 // Vrai si on doit participer aux écritures
412 // Si on utilise MPI/IO avec HDF5, il faut tout de même que tous
413 // les rangs fassent toutes les opérations d'écriture pour garantir
414 // la cohérence des méta-données.
415 m_is_writer = m_is_master_io || m_is_collective_io;
416
417 // Indique qu'on utilise MPI/IO si demandé
418 HProperty plist_id;
419 if (m_is_collective_io)
420 plist_id.createFilePropertyMPIIO(pm);
421
422 if (is_first_call && m_is_master_io)
423 dir.createDirectory();
424
425 if (m_is_collective_io)
426 pm->barrier();
427
428 if (m_is_writer) {
429 m_standard_types.initialize();
430
431 if (is_first_call)
432 m_file_id.openTruncate(m_full_filename, plist_id.id());
433 else
434 m_file_id.openAppend(m_full_filename, plist_id.id());
435
436 _openOrCreateGroups();
437
438 if (is_first_call) {
439 std::array<Int64, 2> version = { 2, 0 };
440 _addInt64ArrayAttribute(m_top_group, "Version", version);
441 _addStringAttribute(m_top_group, "Type", "UnstructuredGrid");
442 }
443 }
444
445 // Initialise les informations collectives sur les groupes de mailles et noeuds
446 _initializeItemGroupCollectiveInfos(m_all_cells_info);
447 _initializeItemGroupCollectiveInfos(m_all_nodes_info);
448
449 CellGroup all_cells = m_mesh->allCells();
450 NodeGroup all_nodes = m_mesh->allNodes();
451
452 const Int32 nb_cell = all_cells.size();
453 const Int32 nb_node = all_nodes.size();
454
455 Int32 total_nb_connected_node = 0;
456 ENUMERATE_ (Cell, icell, all_cells) {
457 Cell cell = *icell;
458 total_nb_connected_node += cell.nodeIds().size();
459 }
460
461 // Pour les offsets, la taille du tableau est égal
462 // au nombre de mailles plus 1.
463 UniqueArray<Int64> cells_connectivity(total_nb_connected_node);
464 UniqueArray<Int64> cells_offset(nb_cell + 1);
465 UniqueArray<unsigned char> cells_ghost_type(nb_cell);
466 UniqueArray<unsigned char> cells_type(nb_cell);
467 UniqueArray<Int64> cells_uid(nb_cell);
468 cells_offset[0] = 0;
469 {
470 Int32 connected_node_index = 0;
471 ENUMERATE_CELL (icell, all_cells) {
472 Int32 index = icell.index();
473 Cell cell = *icell;
474
475 cells_uid[index] = cell.uniqueId();
476
477 Byte ghost_type = 0;
478 bool is_ghost = !cell.isOwn();
479 if (is_ghost)
480 ghost_type = VtkUtils::CellGhostTypes::DUPLICATECELL;
481 cells_ghost_type[index] = ghost_type;
482
483 unsigned char vtk_type = VtkUtils::arcaneToVtkCellType(cell.type());
484 cells_type[index] = vtk_type;
485 for (NodeLocalId node : cell.nodeIds()) {
486 cells_connectivity[connected_node_index] = node;
487 ++connected_node_index;
488 }
489 cells_offset[index + 1] = connected_node_index;
490 }
491 }
492
493 _initializeOffsets();
494
495 // TODO: faire un offset pour cet objet (ou regarder comment le calculer automatiquement
496 _writeDataSet1DCollective<Int64>({ { m_top_group, "Offsets" }, m_offset_for_cell_offset_info }, cells_offset);
497
498 _writeDataSet1DCollective<Int64>({ { m_top_group, "Connectivity" }, m_connectivity_offset_info },
499 cells_connectivity);
500 _writeDataSet1DCollective<unsigned char>({ { m_top_group, "Types" }, m_cell_offset_info }, cells_type);
501
502 {
503 Int64 nb_cell_int64 = nb_cell;
504 _writeDataSet1DCollective<Int64>({ { m_top_group, "NumberOfCells" }, m_part_offset_info },
505 asConstSpan(&nb_cell_int64));
506 Int64 nb_node_int64 = nb_node;
507 _writeDataSet1DCollective<Int64>({ { m_top_group, "NumberOfPoints" }, m_part_offset_info },
508 asConstSpan(&nb_node_int64));
509 Int64 number_of_connectivity_ids = cells_connectivity.size();
510 _writeDataSet1DCollective<Int64>({ { m_top_group, "NumberOfConnectivityIds" }, m_part_offset_info },
511 asConstSpan(&number_of_connectivity_ids));
512 }
513
514 // Sauve les uniqueIds, les types et les coordonnées des noeuds.
515 {
516 UniqueArray<Int64> nodes_uid(nb_node);
517 UniqueArray<unsigned char> nodes_ghost_type(nb_node);
518 VariableNodeReal3& nodes_coordinates(m_mesh->nodesCoordinates());
519 UniqueArray2<Real> points;
520 points.resize(nb_node, 3);
521 ENUMERATE_ (Node, inode, all_nodes) {
522 Int32 index = inode.index();
523 Node node = *inode;
524
525 nodes_uid[index] = node.uniqueId();
526
527 Byte ghost_type = 0;
528 bool is_ghost = !node.isOwn();
529 if (is_ghost)
530 ghost_type = VtkUtils::PointGhostTypes::DUPLICATEPOINT;
531 nodes_ghost_type[index] = ghost_type;
532
533 Real3 pos = nodes_coordinates[inode];
534 points[index][0] = pos.x;
535 points[index][1] = pos.y;
536 points[index][2] = pos.z;
537 }
538
539 // Sauve l'uniqueId de chaque nœud dans le dataset "GlobalNodeId".
540 _writeDataSet1DCollective<Int64>({ { m_node_data_group, "GlobalNodeId" }, m_cell_offset_info }, nodes_uid);
541
542 // Sauve les informations sur le type de nœud (réel ou fantôme).
543 _writeDataSet1DCollective<unsigned char>({ { m_node_data_group, "vtkGhostType" }, m_cell_offset_info }, nodes_ghost_type);
544
545 // Sauve les coordonnées des noeuds.
546 _writeDataSet2DCollective<Real>({ { m_top_group, "Points" }, m_point_offset_info }, points);
547 }
548
549 // Sauve les informations sur le type de maille (réel ou fantôme)
550 _writeDataSet1DCollective<unsigned char>({ { m_cell_data_group, "vtkGhostType" }, m_cell_offset_info }, cells_ghost_type);
551
552 // Sauve l'uniqueId de chaque maille dans le dataset "GlobalCellId".
553 // L'utilisation du dataset "vtkOriginalCellIds" ne fonctionne pas dans Paraview.
554 _writeDataSet1DCollective<Int64>({ { m_cell_data_group, "GlobalCellId" }, m_cell_offset_info }, cells_uid);
555
556 if (m_is_writer) {
557 // Liste des temps.
558 Real current_time = m_times[time_index - 1];
559 _writeDataSet1D<Real>({ { m_steps_group, "Values" }, m_time_offset_info }, asConstSpan(&current_time));
560
561 // Offset de la partie.
562 Int64 comm_size = pm->commSize();
563 Int64 part_offset = (time_index - 1) * comm_size;
564 _writeDataSet1D<Int64>({ { m_steps_group, "PartOffsets" }, m_time_offset_info }, asConstSpan(&part_offset));
565
566 // Nombre de temps
567 _addInt64Attribute(m_steps_group, "NSteps", time_index);
568 }
569
570 _writeConstituentsGroups();
571}
572
573/*---------------------------------------------------------------------------*/
574/*---------------------------------------------------------------------------*/
575
576void VtkHdfV2DataWriter::
577_writeConstituentsGroups()
578{
579 if (!m_material_mng)
580 return;
581
582 // Remplit les informations des groupes liés aux constituents
583 // NOTE : Pour l'instant, on ne traite que les milieux.
584 for (IMeshEnvironment* env : m_material_mng->environments()) {
585 CellGroup cells = env->cells();
586 Ref<ItemGroupCollectiveInfo> group_info_ref = createRef<ItemGroupCollectiveInfo>(cells);
587 m_materials_groups.add(group_info_ref);
588 ItemGroupCollectiveInfo& group_info = *group_info_ref.get();
589 _initializeItemGroupCollectiveInfos(group_info);
590 ConstArrayView<Int32> groups_ids = cells.view().localIds();
591 DatasetGroupAndName dataset_group_name(m_top_group, String("Constituent_") + cells.name());
592 if (m_is_first_call)
593 info() << "Writing infos for group '" << cells.name() << "'";
594 _writeDataSet1DCollective<Int32>({ dataset_group_name, m_cell_offset_info }, groups_ids);
595 }
596}
597
598/*---------------------------------------------------------------------------*/
599/*---------------------------------------------------------------------------*/
600/*!
601 * \brief Calcule l'offset de notre partie et le nombre total d'éléments.
602 */
603VtkHdfV2DataWriter::WritePartInfo VtkHdfV2DataWriter::
604_computeWritePartInfo(Int64 local_size)
605{
606 // TODO: regarder pour utiliser un scan.
607 IParallelMng* pm = m_mesh->parallelMng();
608 Int32 nb_rank = pm->commSize();
609 Int32 my_rank = pm->commRank();
610
611 UniqueArray<Int64> ranks_size(nb_rank);
612 ArrayView<Int64> all_sizes(ranks_size);
613 Int64 dim1_size = local_size;
614 pm->allGather(ConstArrayView<Int64>(1, &dim1_size), all_sizes);
615
616 Int64 total_size = 0;
617 for (Integer i = 0; i < nb_rank; ++i)
618 total_size += all_sizes[i];
619
620 Int64 my_index = 0;
621 for (Integer i = 0; i < my_rank; ++i)
622 my_index += all_sizes[i];
623
624 WritePartInfo part_info;
625 part_info.setTotalSize(total_size);
626 part_info.setSize(local_size);
627 part_info.setOffset(my_index);
628 return part_info;
629}
630
631/*---------------------------------------------------------------------------*/
632/*---------------------------------------------------------------------------*/
633
634void VtkHdfV2DataWriter::
635_initializeItemGroupCollectiveInfos(ItemGroupCollectiveInfo& group_info)
636{
637 Int64 dim1_size = group_info.m_item_group.size();
638 group_info.setWritePartInfo(_computeWritePartInfo(dim1_size));
639}
640
641namespace
642{
643 std::pair<Int64, Int64> _getInterval(Int64 index, Int64 nb_interval, Int64 total_size)
644 {
645 Int64 n = total_size;
646 Int64 isize = n / nb_interval;
647 Int64 ibegin = index * isize;
648 // Pour le dernier interval, prend les elements restants
649 if ((index + 1) == nb_interval)
650 isize = n - ibegin;
651 return { ibegin, isize };
652 }
653} // namespace
654
655/*---------------------------------------------------------------------------*/
656/*---------------------------------------------------------------------------*/
657/*!
658 * \brief Écrit une donnée 1D ou 2D.
659 *
660 * Pour chaque temps ajouté, la donnée est écrite à la fin des valeurs précédentes
661 * sauf en cas de retour arrière où l'offset est dans data_info.
662 */
663void VtkHdfV2DataWriter::
664_writeDataSetGeneric(const DataInfo& data_info, Int32 nb_dim,
665 Int64 dim1_size, Int64 dim2_size,
666 ConstMemoryView values_data,
667 const hid_t hdf_type, bool is_collective)
668{
669 if (nb_dim == 1)
670 dim2_size = 1;
671
672 HGroup& group = data_info.dataset.group;
673 const String& name = data_info.dataset.name;
674
675 // Si positif ou nul, indique l'offset d'écriture.
676 // Sinon, on écrit à la fin du dataset actuel.
677 Int64 wanted_offset = data_info.datasetInfo().offset();
678
679 static constexpr int MAX_DIM = 2;
680 HDataset dataset;
681
682 // En cas d'opération collective, local_dims et global_dims sont
683 // différents sur la première dimension. La deuxième dimension est toujours
684 // identique pour local_dims et global_dims et ne doit pas être modifiée durant
685 // tout le calcul.
686
687 // Dimensions du dataset que le rang courant va écrire.
688 FixedArray<hsize_t, MAX_DIM> local_dims;
689 local_dims[0] = dim1_size;
690 local_dims[1] = dim2_size;
691
692 // Dimensions cumulées de tous les rangs pour l'écriture.
693 FixedArray<hsize_t, MAX_DIM> global_dims;
694
695 // Dimensions maximales du DataSet
696 // Pour la deuxième dimension, on suppose qu'elle est constante au cours du temps.
697 FixedArray<hsize_t, MAX_DIM> max_dims;
698 max_dims[0] = H5S_UNLIMITED;
699 max_dims[1] = dim2_size;
700
701 herr_t herror = 0;
702 Int64 write_offset = 0;
703
704 Int64 my_index = 0;
705 Int64 global_dim1_size = dim1_size;
706 Int32 nb_participating_rank = 1;
707
708 if (is_collective) {
709 nb_participating_rank = m_mesh->parallelMng()->commSize();
710 WritePartInfo part_info;
711 if (data_info.m_group_info) {
712 // Si la donnée est associée à un groupe, alors les informations
713 // sur l'offset ont déjà été calculées
714 part_info = data_info.m_group_info->writePartInfo();
715 }
716 else {
717 part_info = _computeWritePartInfo(dim1_size);
718 }
719 global_dim1_size = part_info.totalSize();
720 my_index = part_info.offset();
721 }
722
723 HProperty write_plist_id;
724 if (is_collective)
725 write_plist_id.createDatasetTransfertCollectiveMPIIO();
726
727 HSpace file_space;
728 FixedArray<hsize_t, MAX_DIM> hyperslab_offsets;
729
730 if (m_is_first_call) {
731 // TODO: regarder comment mieux calculer le chunk
732 FixedArray<hsize_t, MAX_DIM> chunk_dims;
733 global_dims[0] = global_dim1_size;
734 global_dims[1] = dim2_size;
735 // Il est important que tout le monde ait la même taille de chunk.
736 Int64 chunk_size = global_dim1_size / nb_participating_rank;
737 if (chunk_size < 1024)
738 chunk_size = 1024;
739 const Int64 max_chunk_size = 1024 * 1024 * 10;
740 chunk_size = math::min(chunk_size, max_chunk_size);
741 chunk_dims[0] = chunk_size;
742 chunk_dims[1] = dim2_size;
743 info() << "CHUNK nb_dim=" << nb_dim
744 << " global_dim1_size=" << global_dim1_size
745 << " chunk0=" << chunk_dims[0]
746 << " chunk1=" << chunk_dims[1]
747 << " name=" << name;
748 file_space.createSimple(nb_dim, global_dims.data(), max_dims.data());
749 HProperty plist_id;
750 plist_id.create(H5P_DATASET_CREATE);
751 H5Pset_chunk(plist_id.id(), nb_dim, chunk_dims.data());
752 dataset.create(group, name.localstr(), hdf_type, file_space, HProperty{}, plist_id, HProperty{});
753
754 if (is_collective) {
755 hyperslab_offsets[0] = my_index;
756 hyperslab_offsets[1] = 0;
757 }
758 }
759 else {
760 // Agrandit la première dimension du dataset.
761 // On va ajouter 'global_dim1_size' à cette dimension.
762 dataset.open(group, name.localstr());
763 file_space = dataset.getSpace();
764 int nb_dimension = file_space.nbDimension();
765 if (nb_dimension != nb_dim)
766 ARCANE_THROW(IOException, "Bad dimension '{0}' for dataset '{1}' (should be 1)",
767 nb_dimension, name);
768 // TODO: Vérifier que la deuxième dimension est la même que celle sauvée.
769 FixedArray<hsize_t, MAX_DIM> original_dims;
770 file_space.getDimensions(original_dims.data(), nullptr);
771 hsize_t offset0 = original_dims[0];
772 // Si on a un offset positif issu de DatasetInfo alors on le prend.
773 // Cela signifie qu'on a fait un retour arrière.
774 if (wanted_offset >= 0) {
775 offset0 = wanted_offset;
776 info() << "Forcing offset to " << wanted_offset;
777 }
778 global_dims[0] = offset0 + global_dim1_size;
779 global_dims[1] = dim2_size;
780 write_offset = offset0;
781 // Agrandit le dataset.
782 // ATTENTION cela invalide file_space. Il faut donc le relire juste après.
783 if ((herror = dataset.setExtent(global_dims.data())) < 0)
784 ARCANE_THROW(IOException, "Can not extent dataset '{0}' (err={1})", name, herror);
785 file_space = dataset.getSpace();
786
787 hyperslab_offsets[0] = offset0 + my_index;
788 hyperslab_offsets[1] = 0;
789 info(4) << "APPEND nb_dim=" << nb_dim
790 << " dim0=" << global_dims[0]
791 << " count0=" << local_dims[0]
792 << " offsets0=" << hyperslab_offsets[0] << " name=" << name;
793 }
794
795 Int64 nb_write_byte = global_dim1_size * dim2_size * values_data.datatypeSize();
796
797 // Effectue l'écriture en plusieurs parties si demandé.
798 // Cela n'est possible que pour l'écriture collective.
799 Int64 nb_interval = 1;
800 if (is_collective && m_max_write_size > 0) {
801 nb_interval = 1 + nb_write_byte / (m_max_write_size * 1024);
802 }
803 info(4) << "WRITE global_size=" << nb_write_byte << " max_size=" << m_max_write_size << " nb_interval=" << nb_interval;
804
805 for (Int64 i = 0; i < nb_interval; ++i) {
806 auto [index, nb_element] = _getInterval(i, nb_interval, dim1_size);
807 // Sélectionne la partie de la donnée à écrire
808 FixedArray<hsize_t, 2> dims;
809 dims[0] = nb_element;
810 dims[1] = dim2_size;
811 FixedArray<hsize_t, 2> offsets;
812 offsets[0] = hyperslab_offsets[0] + index;
813 offsets[1] = 0;
814 if ((herror = H5Sselect_hyperslab(file_space.id(), H5S_SELECT_SET, offsets.data(), nullptr, dims.data(), nullptr)) < 0)
815 ARCANE_THROW(IOException, "Can not select hyperslab '{0}' (err={1})", name, herror);
816
817 HSpace memory_space;
818 memory_space.createSimple(nb_dim, dims.data());
819 Int64 data_offset = index * values_data.datatypeSize() * dim2_size;
820 // Effectue l'écriture
821 if ((herror = dataset.write(hdf_type, values_data.data() + data_offset, memory_space, file_space, write_plist_id)) < 0)
822 ARCANE_THROW(IOException, "Can not write dataset '{0}' (err={1})", name, herror);
823
824 if (dataset.isBad())
825 ARCANE_THROW(IOException, "Can not write dataset '{0}'", name);
826 }
827
828 if (!data_info.datasetInfo().isNull())
829 m_offset_info_list.insert(std::make_pair(data_info.datasetInfo(), write_offset));
830}
831
832/*---------------------------------------------------------------------------*/
833/*---------------------------------------------------------------------------*/
834
835template <typename DataType> void VtkHdfV2DataWriter::
836_writeDataSetGeneric(const DataInfo& data_info, Int32 nb_dim,
837 Int64 dim1_size, Int64 dim2_size, const DataType* values_data,
838 bool is_collective)
839{
840 const hid_t hdf_type = m_standard_types.nativeType(DataType{});
841 ConstMemoryView mem_view = makeConstMemoryView(values_data, sizeof(DataType), dim1_size * dim2_size);
842 _writeDataSetGeneric(data_info, nb_dim, dim1_size, dim2_size, mem_view, hdf_type, is_collective);
843}
844
845/*---------------------------------------------------------------------------*/
846/*---------------------------------------------------------------------------*/
847
848template <typename DataType> void VtkHdfV2DataWriter::
849_writeDataSet1D(const DataInfo& data_info, Span<const DataType> values)
850{
851 _writeDataSetGeneric(data_info, 1, values.size(), 1, values.data(), false);
852}
853
854/*---------------------------------------------------------------------------*/
855/*---------------------------------------------------------------------------*/
856
857template <typename DataType> void VtkHdfV2DataWriter::
858_writeDataSet1DUsingCollectiveIO(const DataInfo& data_info, Span<const DataType> values)
859{
860 _writeDataSetGeneric(data_info, 1, values.size(), 1, values.data(), true);
861}
862
863/*---------------------------------------------------------------------------*/
864/*---------------------------------------------------------------------------*/
865
866template <typename DataType> void VtkHdfV2DataWriter::
867_writeDataSet1DCollective(const DataInfo& data_info, Span<const DataType> values)
868{
869 if (!m_is_parallel)
870 return _writeDataSet1D(data_info, values);
871 if (m_is_collective_io)
872 return _writeDataSet1DUsingCollectiveIO(data_info, values);
873 UniqueArray<DataType> all_values;
874 IParallelMng* pm = m_mesh->parallelMng();
875 pm->gatherVariable(values.smallView(), all_values, pm->masterIORank());
876 if (m_is_master_io)
877 _writeDataSet1D<DataType>(data_info, all_values);
878}
879
880/*---------------------------------------------------------------------------*/
881/*---------------------------------------------------------------------------*/
882
883template <typename DataType> void VtkHdfV2DataWriter::
884_writeDataSet2D(const DataInfo& data_info, Span2<const DataType> values)
885{
886 _writeDataSetGeneric(data_info, 2, values.dim1Size(), values.dim2Size(), values.data(), false);
887}
888
889/*---------------------------------------------------------------------------*/
890/*---------------------------------------------------------------------------*/
891
892template <typename DataType> void VtkHdfV2DataWriter::
893_writeDataSet2DUsingCollectiveIO(const DataInfo& data_info, Span2<const DataType> values)
894{
895 _writeDataSetGeneric(data_info, 2, values.dim1Size(), values.dim2Size(), values.data(), true);
896}
897
898/*---------------------------------------------------------------------------*/
899/*---------------------------------------------------------------------------*/
900
901template <typename DataType> void VtkHdfV2DataWriter::
902_writeDataSet2DCollective(const DataInfo& data_info, Span2<const DataType> values)
903{
904 if (!m_is_parallel)
905 return _writeDataSet2D(data_info, values);
906 if (m_is_collective_io)
907 return _writeDataSet2DUsingCollectiveIO(data_info, values);
908
909 Int64 dim2_size = values.dim2Size();
910 UniqueArray<DataType> all_values;
911 IParallelMng* pm = m_mesh->parallelMng();
912 Span<const DataType> values_1d(values.data(), values.totalNbElement());
913 pm->gatherVariable(values_1d.smallView(), all_values, pm->masterIORank());
914 if (m_is_master_io) {
915 Int64 dim1_size = all_values.size();
916 if (dim2_size != 0)
917 dim1_size = dim1_size / dim2_size;
918 Span2<const DataType> span2(all_values.data(), dim1_size, dim2_size);
919 return _writeDataSet2D<DataType>(data_info, span2);
920 }
921}
922
923/*---------------------------------------------------------------------------*/
924/*---------------------------------------------------------------------------*/
925
926void VtkHdfV2DataWriter::
927_addInt64ArrayAttribute(Hid& hid, const char* name, Span<const Int64> values)
928{
929 hsize_t len = values.size();
930 hid_t aid = H5Screate_simple(1, &len, nullptr);
931 hid_t attr = H5Acreate2(hid.id(), name, H5T_NATIVE_INT64, aid, H5P_DEFAULT, H5P_DEFAULT);
932 if (attr < 0)
933 ARCANE_FATAL("Can not create attribute '{0}'", name);
934 int ret = H5Awrite(attr, H5T_NATIVE_INT64, values.data());
935 if (ret < 0)
936 ARCANE_FATAL("Can not write attribute '{0}'", name);
937 H5Aclose(attr);
938 H5Sclose(aid);
939}
940
941/*---------------------------------------------------------------------------*/
942/*---------------------------------------------------------------------------*/
943
944void VtkHdfV2DataWriter::
945_addInt64Attribute(Hid& hid, const char* name, Int64 value)
946{
947 HSpace aid(H5Screate(H5S_SCALAR));
948 HAttribute attr;
949 if (m_is_first_call)
950 attr.create(hid, name, H5T_NATIVE_INT64, aid);
951 else
952 attr.open(hid, name);
953 if (attr.isBad())
954 ARCANE_FATAL("Can not create attribute '{0}'", name);
955 herr_t ret = attr.write(H5T_NATIVE_INT64, &value);
956 if (ret < 0)
957 ARCANE_FATAL("Can not write attribute '{0}'", name);
958}
959
960/*---------------------------------------------------------------------------*/
961/*---------------------------------------------------------------------------*/
962
963Int64 VtkHdfV2DataWriter::
964_readInt64Attribute(Hid& hid, const char* name)
965{
966 HAttribute attr;
967 attr.open(hid, name);
968 if (attr.isBad())
969 ARCANE_FATAL("Can not open attribute '{0}'", name);
970 Int64 value;
971 herr_t ret = attr.read(H5T_NATIVE_INT64, &value);
972 if (ret < 0)
973 ARCANE_FATAL("Can not read attribute '{0}'", name);
974 return value;
975}
976
977/*---------------------------------------------------------------------------*/
978/*---------------------------------------------------------------------------*/
979
980void VtkHdfV2DataWriter::
981_addStringAttribute(Hid& hid, const char* name, const String& value)
982{
983 hid_t aid = H5Screate(H5S_SCALAR);
984 hid_t attr_type = H5Tcopy(H5T_C_S1);
985 H5Tset_size(attr_type, value.length());
986 hid_t attr = H5Acreate2(hid.id(), name, attr_type, aid, H5P_DEFAULT, H5P_DEFAULT);
987 if (attr < 0)
988 ARCANE_FATAL("Can not create attribute {0}", name);
989 int ret = H5Awrite(attr, attr_type, value.localstr());
990 ret = H5Tclose(attr_type);
991 if (ret < 0)
992 ARCANE_FATAL("Can not write attribute '{0}'", name);
993 H5Aclose(attr);
994 H5Sclose(aid);
995}
996
997/*---------------------------------------------------------------------------*/
998/*---------------------------------------------------------------------------*/
999
1000void VtkHdfV2DataWriter::
1001endWrite()
1002{
1003 // Sauvegarde les offsets enregistrés
1004
1005 if (m_is_writer) {
1006 for (const auto& i : m_offset_info_list) {
1007 Int64 offset = i.second;
1008 const DatasetInfo& offset_info = i.first;
1009 HGroup* hdf_group = offset_info.group();
1010 //info() << "OFFSET_INFO name=" << offset_info.name() << " offset=" << offset;
1011 if (hdf_group)
1012 _writeDataSet1D<Int64>({ { *hdf_group, offset_info.name() }, m_time_offset_info }, asConstSpan(&offset));
1013 }
1014 }
1015 _closeGroups();
1016 m_file_id.close();
1017}
1018
1019/*---------------------------------------------------------------------------*/
1020/*---------------------------------------------------------------------------*/
1021
1022void VtkHdfV2DataWriter::
1023_openOrCreateGroups()
1024{
1025 // Tout groupe ouvert ici doit être fermé dans closeGroups().
1026 m_top_group.openOrCreate(m_file_id, "VTKHDF");
1027 m_cell_data_group.openOrCreate(m_top_group, "CellData");
1028 m_node_data_group.openOrCreate(m_top_group, "PointData");
1029 m_steps_group.openOrCreate(m_top_group, "Steps");
1030 m_point_data_offsets_group.openOrCreate(m_steps_group, "PointDataOffsets");
1031 m_cell_data_offsets_group.openOrCreate(m_steps_group, "CellDataOffsets");
1032 m_field_data_offsets_group.openOrCreate(m_steps_group, "FieldDataOffsets");
1033}
1034
1035/*---------------------------------------------------------------------------*/
1036/*---------------------------------------------------------------------------*/
1037
1038void VtkHdfV2DataWriter::
1039_closeGroups()
1040{
1041 m_cell_data_group.close();
1042 m_node_data_group.close();
1043 m_point_data_offsets_group.close();
1044 m_cell_data_offsets_group.close();
1045 m_field_data_offsets_group.close();
1046 m_steps_group.close();
1047 m_top_group.close();
1048}
1049
1050/*---------------------------------------------------------------------------*/
1051/*---------------------------------------------------------------------------*/
1052
1054setMetaData(const String& meta_data)
1055{
1056 ARCANE_UNUSED(meta_data);
1057}
1058
1059/*---------------------------------------------------------------------------*/
1060/*---------------------------------------------------------------------------*/
1061
1063write(IVariable* var, IData* data)
1064{
1065 info(4) << "Write VtkHdfV2 var=" << var->name();
1066
1067 eItemKind item_kind = var->itemKind();
1068
1069 if (var->dimension() != 1)
1070 ARCANE_FATAL("Only export of scalar item variable is implemented (name={0})", var->name());
1071 if (var->isPartial())
1072 ARCANE_FATAL("Export of partial variable is not implemented");
1073
1074 HGroup* group = nullptr;
1075 DatasetInfo offset_info;
1076 ItemGroupCollectiveInfo* group_info = nullptr;
1077 switch (item_kind) {
1078 case IK_Cell:
1079 group = &m_cell_data_group;
1080 offset_info = m_cell_offset_info;
1081 group_info = &m_all_cells_info;
1082 break;
1083 case IK_Node:
1084 group = &m_node_data_group;
1085 offset_info = m_point_offset_info;
1086 group_info = &m_all_nodes_info;
1087 break;
1088 default:
1089 ARCANE_FATAL("Only export of 'Cell' or 'Node' variable is implemented (name={0})", var->name());
1090 }
1091
1092 ARCANE_CHECK_POINTER(group);
1093
1094 DataInfo data_info(DatasetGroupAndName{ *group, var->name() }, offset_info, group_info);
1095 eDataType data_type = var->dataType();
1096 switch (data_type) {
1097 case DT_Real:
1098 _writeBasicTypeDataset<Real>(data_info, data);
1099 break;
1100 case DT_Int64:
1101 _writeBasicTypeDataset<Int64>(data_info, data);
1102 break;
1103 case DT_Int32:
1104 _writeBasicTypeDataset<Int32>(data_info, data);
1105 break;
1106 case DT_Real3:
1107 _writeReal3Dataset(data_info, data);
1108 break;
1109 case DT_Real2:
1110 _writeReal2Dataset(data_info, data);
1111 break;
1112 default:
1113 warning() << String::format("Export for datatype '{0}' is not supported (var_name={1})", data_type, var->name());
1114 }
1115}
1116
1117/*---------------------------------------------------------------------------*/
1118/*---------------------------------------------------------------------------*/
1119
1120template <typename DataType> void VtkHdfV2DataWriter::
1121_writeBasicTypeDataset(const DataInfo& data_info, IData* data)
1122{
1123 auto* true_data = dynamic_cast<IArrayDataT<DataType>*>(data);
1124 ARCANE_CHECK_POINTER(true_data);
1125 _writeDataSet1DCollective(data_info, Span<const DataType>(true_data->view()));
1126}
1127
1128/*---------------------------------------------------------------------------*/
1129/*---------------------------------------------------------------------------*/
1130
1131void VtkHdfV2DataWriter::
1132_writeReal3Dataset(const DataInfo& data_info, IData* data)
1133{
1134 auto* true_data = dynamic_cast<IArrayDataT<Real3>*>(data);
1135 ARCANE_CHECK_POINTER(true_data);
1136 SmallSpan<const Real3> values(true_data->view());
1137 Int32 nb_value = values.size();
1138 // TODO: optimiser cela sans passer par un tableau temporaire
1139 UniqueArray2<Real> scalar_values;
1140 scalar_values.resize(nb_value, 3);
1141 for (Int32 i = 0; i < nb_value; ++i) {
1142 Real3 v = values[i];
1143 scalar_values[i][0] = v.x;
1144 scalar_values[i][1] = v.y;
1145 scalar_values[i][2] = v.z;
1146 }
1147 _writeDataSet2DCollective<Real>(data_info, scalar_values);
1148}
1149
1150/*---------------------------------------------------------------------------*/
1151/*---------------------------------------------------------------------------*/
1152
1153void VtkHdfV2DataWriter::
1154_writeReal2Dataset(const DataInfo& data_info, IData* data)
1155{
1156 // Converti en un tableau de 3 composantes dont la dernière vaudra 0.
1157 auto* true_data = dynamic_cast<IArrayDataT<Real2>*>(data);
1158 ARCANE_CHECK_POINTER(true_data);
1159 SmallSpan<const Real2> values(true_data->view());
1160 Int32 nb_value = values.size();
1161 UniqueArray2<Real> scalar_values;
1162 scalar_values.resize(nb_value, 3);
1163 for (Int32 i = 0; i < nb_value; ++i) {
1164 Real2 v = values[i];
1165 scalar_values[i][0] = v.x;
1166 scalar_values[i][1] = v.y;
1167 scalar_values[i][2] = 0.0;
1168 }
1169 _writeDataSet2DCollective<Real>(data_info, scalar_values);
1170}
1171
1172/*---------------------------------------------------------------------------*/
1173/*---------------------------------------------------------------------------*/
1174
1175void VtkHdfV2DataWriter::
1176_readAndSetOffset(DatasetInfo& offset_info, Int32 wanted_step)
1177{
1178 HGroup* hgroup = offset_info.group();
1179 ARCANE_CHECK_POINTER(hgroup);
1180 StandardArrayT<Int64> a(hgroup->id(), offset_info.name());
1181 UniqueArray<Int64> values;
1182 a.directRead(m_standard_types, values);
1183 Int64 offset_value = values[wanted_step];
1184 offset_info.setOffset(offset_value);
1185 info() << "VALUES name=" << offset_info.name() << " values=" << values
1186 << " wanted_step=" << wanted_step << " v=" << offset_value;
1187}
1188
1189/*---------------------------------------------------------------------------*/
1190/*---------------------------------------------------------------------------*/
1191
1192void VtkHdfV2DataWriter::
1193_initializeOffsets()
1194{
1195 // Il y a 5 valeurs d'offset utilisées :
1196 //
1197 // - offset sur le nombre de mailles (CellOffsets). Cet offset a pour nombre d'éléments
1198 // le nombre de temps sauvés et est augmenté à chaque sortie du nombre de mailles. Cet offset
1199 // est aussi utilisé pour les variables aux mailles
1200 // - offset sur le nombre de noeuds (PointOffsets). Il est équivalent à 'CellOffsets' mais
1201 // pour les noeuds.
1202 // - offset pour "NumberOfCells", "NumberOfPoints" et "NumberOfConnectivityIds". Pour chacun
1203 // de ces champs il y a NbPart valeurs par temps, avec 'NbPart' le nombre de parties (donc
1204 // le nombre de sous-domaines si on ne fait pas de regroupement). Il y a ainsi au total
1205 // NbPart * NbTimeStep dans ce champ d'offset.
1206 // - offset pour le champ "Connectivity" qui s'appelle "ConnectivityIdOffsets".
1207 // Cet offset a pour nombre d'éléments le nombre de temps sauvés.
1208 // - offset pour le champ "Offsets". "Offset" contient pour chaque maille l'offset dans
1209 // "Connectivity" de la connectivité des noeuds de la maille. Cet offset n'est pas sauvés,
1210 // mais comme ce champ à un nombre de valeurs égal au nombre de mailles plus un il est possible
1211 // de le déduire de "CellOffsets" (il vaut "CellOffsets" plus l'index du temps courant).
1212
1213 m_cell_offset_info = DatasetInfo(m_steps_group, "CellOffsets");
1214 m_point_offset_info = DatasetInfo(m_steps_group, "PointOffsets");
1215 m_connectivity_offset_info = DatasetInfo(m_steps_group, "ConnectivityIdOffsets");
1216 // Ces trois offsets ne sont pas sauvegardés dans le format VTK
1217 m_offset_for_cell_offset_info = DatasetInfo("_OffsetForCellOffsetInfo");
1218 m_part_offset_info = DatasetInfo("_PartOffsetInfo");
1219 m_time_offset_info = DatasetInfo("_TimeOffsetInfo");
1220
1221 // Regarde si on n'a pas fait de retour-arrière.
1222 // C'est le cas si le nombre de temps sauvés est supérieur au nombre
1223 // de valeurs de \a m_times.
1224 if (m_is_writer && !m_is_first_call) {
1225 IParallelMng* pm = m_mesh->parallelMng();
1226 const Int32 nb_rank = pm->commSize();
1227 Int64 nb_current_step = _readInt64Attribute(m_steps_group, "NSteps");
1228 Int32 time_index = m_times.size();
1229 info(4) << "NB_STEP=" << nb_current_step << " time_index=" << time_index
1230 << " current_time=" << m_times.back();
1231 const bool debug_times = false;
1232 if (debug_times) {
1233 StandardArrayT<Real> a1(m_steps_group.id(), "Values");
1234 UniqueArray<Real> times;
1235 a1.directRead(m_standard_types, times);
1236 info() << "TIMES=" << times;
1237 }
1238 if ((nb_current_step + 1) != time_index) {
1239 info() << "[VtkHdf] go_backward detected";
1240 Int32 wanted_step = time_index - 1;
1241 // Signifie qu'on a fait un retour arrière.
1242 // Dans ce cas, il faut relire les offsets
1243 _readAndSetOffset(m_cell_offset_info, wanted_step);
1244 _readAndSetOffset(m_point_offset_info, wanted_step);
1245 _readAndSetOffset(m_connectivity_offset_info, wanted_step);
1246 m_part_offset_info.setOffset(wanted_step * nb_rank);
1247 m_time_offset_info.setOffset(wanted_step);
1248 m_offset_for_cell_offset_info.setOffset(m_cell_offset_info.offset() + wanted_step * nb_rank);
1249 }
1250 }
1251}
1252
1253/*---------------------------------------------------------------------------*/
1254/*---------------------------------------------------------------------------*/
1255
1256/*---------------------------------------------------------------------------*/
1257/*---------------------------------------------------------------------------*/
1258/*!
1259 * \brief Post-traitement au format VtkHdf V2.
1260 */
1261class VtkHdfV2PostProcessor
1262: public ArcaneVtkHdfV2PostProcessorObject
1263{
1264 public:
1265
1266 explicit VtkHdfV2PostProcessor(const ServiceBuildInfo& sbi)
1267 : ArcaneVtkHdfV2PostProcessorObject(sbi)
1268 {
1269 }
1270
1271 IDataWriter* dataWriter() override { return m_writer.get(); }
1272 void notifyBeginWrite() override
1273 {
1274 bool use_collective_io = true;
1275 Int64 max_write_size = 0;
1276 if (options()) {
1277 use_collective_io = options()->useCollectiveWrite();
1278 max_write_size = options()->maxWriteSize();
1279 }
1280 auto w = std::make_unique<VtkHdfV2DataWriter>(mesh(), groups(), use_collective_io);
1281 w->setMaxWriteSize(max_write_size);
1282 w->setTimes(times());
1283 Directory dir(baseDirectoryName());
1284 w->setDirectoryName(dir.file("vtkhdfv2"));
1285 m_writer = std::move(w);
1286 }
1287 void notifyEndWrite() override
1288 {
1289 m_writer = nullptr;
1290 }
1291 void close() override {}
1292
1293 private:
1294
1295 std::unique_ptr<IDataWriter> m_writer;
1296};
1297
1298/*---------------------------------------------------------------------------*/
1299/*---------------------------------------------------------------------------*/
1300
1301ARCANE_REGISTER_SERVICE_VTKHDFV2POSTPROCESSOR(VtkHdfV2PostProcessor,
1303
1304/*---------------------------------------------------------------------------*/
1305/*---------------------------------------------------------------------------*/
1306
1307} // namespace Arcane
1308
1309/*---------------------------------------------------------------------------*/
1310/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
#define ARCANE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ENUMERATE_(type, name, group)
Enumérateur générique d'un groupe d'entité
#define ENUMERATE_CELL(name, group)
Enumérateur générique d'un groupe de mailles.
Classe gérant un répertoire.
Definition Directory.h:35
String file(const String &file_name) const override
Retourne le chemin complet du fichier file_name dans le répertoire.
Definition Directory.cc:120
Encapsule un hid_t pour un groupe.
Definition Hdf5Utils.h:327
static void useMutex(bool is_active, IParallelMng *pm)
Fonction permettant d'activer ou de désactiver les verrous à chaque appel à HDF5.
Definition Hdf5Utils.cc:118
static bool hasParallelHdf5()
Vrai HDF5 est compilé avec le support de MPI.
Definition Hdf5Utils.cc:105
Interface d'écriture des données d'une variable.
Definition IDataWriter.h:44
Interface d'une donnée.
Definition IData.h:33
Interface d'une variable.
Definition IVariable.h:39
virtual eDataType dataType() const =0
Type de la donnée gérée par la variable (Real, Integer, ...)
virtual eItemKind itemKind() const =0
Genre des entités du maillage sur lequel repose la variable.
virtual bool isPartial() const =0
Indique si la variable est partielle.
virtual Integer dimension() const =0
Dimension de la variable.
virtual String name() const =0
Nom de la variable.
Groupe d'entités de maillage.
Definition ItemGroup.h:49
Integer size() const
Nombre d'éléments du groupe.
Definition ItemGroup.h:88
static IMeshMaterialMng * getReference(const MeshHandleOrMesh &mesh_handle, bool create=true)
Récupère ou créé la référence associée à mesh.
Structure contenant les informations pour créer un service.
Vue d'un tableau d'éléments de type T.
Definition Span.h:633
Chaîne de caractères unicode.
TraceAccessor(ITraceMng *m)
Construit un accesseur via le gestionnaire de trace m.
TraceMessage info() const
Flot pour un message d'information.
TraceMessage warning() const
Flot pour un message d'avertissement.
Collection de variables.
void write(IVariable *var, IData *data) override
Ecrit les données data de la variable var.
void setMetaData(const String &meta_data) override
Positionne les infos des méta-données.
Post-traitement au format VtkHdf V2.
__host__ __device__ Real2 min(Real2 a, Real2 b)
Retourne le minimum de deux Real2.
Definition MathUtils.h:336
ItemGroupT< Cell > CellGroup
Groupe de mailles.
Definition ItemTypes.h:183
ItemGroupT< Node > NodeGroup
Groupe de noeuds.
Definition ItemTypes.h:167
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Grandeur au noeud de type coordonnées.
Integer len(const char *s)
Retourne la longueur de la chaîne s.
Fonctions utilitaires pour Hdf5.
Definition Hdf5Utils.cc:37
Active toujours les traces dans les parties Arcane concernant les matériaux.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
Ref< TrueType > createRef(Args &&... args)
Créé une instance de type TrueType avec les arguments Args et retourne une référence dessus.
Collection< ItemGroup > ItemGroupCollection
Collection de groupes d'éléments du maillage.
std::int64_t Int64
Type entier signé sur 64 bits.
Int32 Integer
Type représentant un entier.
bool operator<(const Item &item1, const Item &item2)
Compare deux entités.
Definition Item.h:551
ConstMemoryView makeConstMemoryView(const void *ptr, Int32 datatype_size, Int64 nb_element)
Créé une vue mémoire en lecture seule.
Definition MemoryView.cc:36
eItemKind
Genre d'entité de maillage.
@ IK_Node
Entité de maillage de genre noeud.
@ IK_Cell
Entité de maillage de genre maille.
double Real
Type représentant un réel.
unsigned char Byte
Type d'un octet.
Definition BaseTypes.h:43
eDataType
Type d'une donnée.
Definition DataTypes.h:39
@ DT_Int32
Donnée de type entier 32 bits.
Definition DataTypes.h:43
@ DT_Real3
Donnée de type vecteur 3.
Definition DataTypes.h:47
@ DT_Int64
Donnée de type entier 64 bits.
Definition DataTypes.h:44
@ DT_Real2
Donnée de type vecteur 2.
Definition DataTypes.h:46
@ DT_Real
Donnée de type réel.
Definition DataTypes.h:41
@ Cell
Le maillage est AMR par maille.
Definition MeshKind.h:52
std::int32_t Int32
Type entier signé sur 32 bits.
ConstArrayView< Real > RealConstArrayView
Equivalent C d'un tableau à une dimension de réels.
Definition UtilsTypes.h:488
Conserve les infos sur les données à sauver et l'offset associé.
Classe pour conserver un couple (hdf_group,nom_du_dataset).
Classe pour conserver les information d'un offset.
Int64 offset() const
Valeur de l'offset. (-1) si on écrit à la fin du tableau.
Informations collectives sur un ItemGroup;.
WritePartInfo m_write_part_info
Informations sur l'écriture.
Informations sur l'offset de la partie à écrire associée à un rang.