Arcane  v4.1.10.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/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// Ce format est décrit sur la page web suivante :
44//
45// https://kitware.github.io/vtk-examples/site/VTKFileFormats/#hdf-file-formats
46//
47// Le format 2.0 avec le support intégré de l'évolution temporelle n'est
48// disponible que dans la branche master de VTK à partir d'avril 2023.
49
50/*---------------------------------------------------------------------------*/
51/*---------------------------------------------------------------------------*/
52
53// TODO: Ajouter test de vérifcation des valeurs sauvegardées
54
55// TODO: Regarder la sauvegarde des uniqueId() (via vtkOriginalCellIds)
56
57// TODO: Regarder comment éviter de sauver le maillage à chaque itération s'il
58// ne change pas.
59
60// TODO: Regarder la compression
61
62// TODO: gérer les variables 2D
63
64// TODO: hors HDF5, faire un mécanisme qui regroupe plusieurs parties
65// du maillage en une seule. Cela permettra de réduire le nombre de mailles
66// fantômes et d'utiliser MPI/IO en mode hybride.
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
94 /*!
95 * \brief Classe pour conserver un couple (hdf_group,nom_du_dataset).
96 *
97 * Les instances de cette classe utilisent une référence sur un groupe HDF5
98 * et ce dernier doit donc vivre plus longtemps que l'instance.
99 */
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
115 /*!
116 * \brief Classe pour conserver les information d'un offset.
117 *
118 * Il s'agit d'un couple (hdf_group,nom_du_dataset).
119 *
120 * Le groupe peut être nul auquel cas il s'agit d'un offset qui est
121 * uniquement calculé et qui ne sera pas sauvegardé.
122 *
123 * Les instances de cette classe utilisent une référence sur un groupe HDF5
124 * et ce dernier doit donc vivre plus longtemps que l'instance.
125 */
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; }
140 //! Valeur de l'offset. (-1) si on écrit à la fin du tableau
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
155 //! Informations sur l'offset de la partie à écrire associée à un rang
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
170 //! Nombre d'éléments sur tous les rangs
171 Int64 m_total_size = 0;
172 //! Nombre d'éléments de mon rang
173 Int64 m_size = 0;
174 //! Offset de mon rang
175 Int64 m_offset = -1;
176 };
177
178 //! Informations collectives sur un ItemGroup;
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
194 //! Groupe associé
196 //! Informations sur l'écriture.
198 };
199
200 /*!
201 * \brief Conserve les infos sur les données à sauver et l'offset associé.
202 */
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
250 //! Maillage associé
251 IMesh* m_mesh = nullptr;
252
253 //! Gestionnaire de matériaux associé (peut-être nul)
254 IMeshMaterialMng* m_material_mng = nullptr;
255
256 //! Liste des groupes à sauver
257 ItemGroupCollection m_groups;
258
259 //! Liste des temps
260 UniqueArray<Real> m_times;
261
262 //! Nom du fichier HDF courant
263 String m_full_filename;
264
265 //! Répertoire de sortie.
266 String m_directory_name;
267
268 //! Identifiant HDF du fichier
269 HFile m_file_id;
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;
298 UniqueArray<Ref<ItemGroupCollectiveInfo>> m_materials_groups;
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
304 /*!
305 * \brief Taille maximale (en kilo-octet) pour une écriture.
306 *
307 * Si l'écriture dépasse cette taille, elle est scindée en plusieurs écriture.
308 * Cela peut être nécessaire avec MPI-IO pour les gros volumes.
309 */
310 Int64 m_max_write_size = 0;
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 // Récupère le gestionnaire de matériaux s'il existe
383 m_material_mng = IMeshMaterialMng::getReference(m_mesh, false);
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: L'implémentation au format 'VtkHdfV2' est expérimentale";
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 // Il est possible d'utiliser le mode collectif de HDF5 via MPI-IO dans les cas suivants :
406 // * Hdf5 a été compilé avec MPI,
407 // * on est en mode MPI pure (ni mode mémoire partagé, ni mode hybride).
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 // Vrai si on doit participer aux écritures
421 // Si on utilise MPI/IO avec HDF5, il faut tout de même que tous
422 // les rangs fassent toutes les opérations d'écriture pour garantir
423 // la cohérence des méta-données.
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 // Indique qu'on utilise MPI/IO si demandé
434 HProperty plist_id;
435 if (m_is_collective_io && m_is_writer)
436 plist_id.createFilePropertyMPIIO(pm);
437
438 // Même avec MPI-IO, un seul proc doit créer le dossier.
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 // Initialise les informations collectives sur les groupes de mailles et noeuds
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 // Pour les offsets, la taille du tableau est égal
479 // au nombre de mailles 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 est un GatherGroupInfo pour les tableaux qui ne sont pas de taille nb_cell ou 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: faire un offset pour cet objet (ou regarder comment le calculer automatiquement
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 // Sauve les uniqueIds, les types et les coordonnées des noeuds.
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 // Sauve l'uniqueId de chaque nœud dans le dataset "GlobalNodeId".
566 _writeDataSet1DCollective<Int64>({ { m_node_data_group, "GlobalIds" }, m_cell_offset_info }, &m_all_nodes_gather_group_info, nodes_uid);
567
568 // Sauve les informations sur le type de nœud (réel ou fantôme).
569 _writeDataSet1DCollective<unsigned char>({ { m_node_data_group, "vtkGhostType" }, m_cell_offset_info }, &m_all_nodes_gather_group_info, nodes_ghost_type);
570
571 // Sauve les coordonnées des noeuds.
572 _writeDataSet2DCollective<Real>({ { m_top_group, "Points" }, m_point_offset_info }, ggi, points);
573 ggi->setNeedRecompute();
574 }
575
576 // Sauve les informations sur le type de maille (réel ou fantôme)
577 _writeDataSet1DCollective<unsigned char>({ { m_cell_data_group, "vtkGhostType" }, m_cell_offset_info }, &m_all_cells_gather_group_info, cells_ghost_type);
578
579 // Sauve l'uniqueId de chaque maille dans le dataset "GlobalCellId".
580 // L'utilisation du dataset "vtkOriginalCellIds" ne fonctionne pas dans 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 // Liste des temps.
585 Real current_time = m_times[time_index - 1];
586 // TODO : Remplacer ggi par nullptr en non-collective ?
587 _writeDataSet1D<Real>({ { m_steps_group, "Values" }, m_time_offset_info }, ggi, asConstSpan(&current_time));
588
589 // Offset de la partie.
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 // Nombre de temps
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 // Remplit les informations des groupes liés aux constituents
612 // NOTE : Pour l'instant, on ne traite que les milieux.
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/*---------------------------------------------------------------------------*/
636/*!
637 * \brief Calcule l'offset de notre partie et le nombre total d'éléments.
638 */
639VtkHdfV2DataWriter::WritePartInfo VtkHdfV2DataWriter::
640_computeWritePartInfo(Int64 local_size)
641{
642 // TODO: regarder pour utiliser un 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 // Pour le dernier interval, prend les elements restants
691 if ((index + 1) == nb_interval)
692 isize = n - ibegin;
693 return { ibegin, isize };
694 }
695} // namespace
696
697/*---------------------------------------------------------------------------*/
698/*---------------------------------------------------------------------------*/
699/*!
700 * \brief Écrit une donnée 1D ou 2D.
701 *
702 * Pour chaque temps ajouté, la donnée est écrite à la fin des valeurs précédentes
703 * sauf en cas de retour arrière où l'offset est dans data_info.
704 */
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 // Si positif ou nul, indique l'offset d'écriture.
719 // Sinon, on écrit à la fin du dataset actuel.
720 Int64 wanted_offset = data_info.datasetInfo().offset();
721
722 static constexpr int MAX_DIM = 2;
723 HDataset dataset;
724
725 // En cas d'opération collective, local_dims et global_dims sont
726 // différents sur la première dimension. La deuxième dimension est toujours
727 // identique pour local_dims et global_dims et ne doit pas être modifiée durant
728 // tout le calcul.
729
730 // Dimensions du dataset que le rang courant va écrire.
731 FixedArray<hsize_t, MAX_DIM> local_dims;
732 local_dims[0] = dim1_size;
733 local_dims[1] = dim2_size;
734
735 // Dimensions cumulées de tous les rangs pour l'écriture.
736 FixedArray<hsize_t, MAX_DIM> global_dims;
737
738 // Dimensions maximales du DataSet
739 // Pour la deuxième dimension, on suppose qu'elle est constante au cours du temps.
740 FixedArray<hsize_t, MAX_DIM> max_dims;
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 // Si la donnée est associée à un groupe, alors les informations
756 // sur l'offset ont déjà été calculées
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 // La seule opération collective était _computeWritePartInfo().
767 if (!m_is_writer) {
768 return;
769 }
770
771 HProperty write_plist_id;
772 if constexpr (HInit::hasParallelHdf5()) {
773 if (is_collective)
774 write_plist_id.createDatasetTransfertCollectiveMPIIO();
775 else
776 write_plist_id.createDatasetTransfertIndependentMPIIO();
777 }
778
779 HSpace file_space;
780 FixedArray<hsize_t, MAX_DIM> hyperslab_offsets;
781
782 if (m_is_first_call) {
783 // TODO: regarder comment mieux calculer le chunk
784 FixedArray<hsize_t, MAX_DIM> chunk_dims;
785 global_dims[0] = global_dim1_size;
786 global_dims[1] = dim2_size;
787 // Il est important que tout le monde ait la même taille de chunk.
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 // Agrandit la première dimension du dataset.
813 // On va ajouter 'global_dim1_size' à cette 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: Vérifier que la deuxième dimension est la même que celle sauvée.
821 FixedArray<hsize_t, MAX_DIM> original_dims;
822 file_space.getDimensions(original_dims.data(), nullptr);
823 hsize_t offset0 = original_dims[0];
824 // Si on a un offset positif issu de DatasetInfo alors on le prend.
825 // Cela signifie qu'on a fait un retour arrière.
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 // Agrandit le dataset.
834 // ATTENTION cela invalide file_space. Il faut donc le relire juste après.
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 // Effectue l'écriture en plusieurs parties si demandé.
850 // Cela n'est possible que pour l'écriture collective.
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 // Sélectionne la partie de la donnée à écrire
860 FixedArray<hsize_t, 2> dims;
861 dims[0] = nb_element;
862 dims[1] = dim2_size;
863 FixedArray<hsize_t, 2> offsets;
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 // Effectue l'écriture
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 // Sauvegarde les offsets enregistrés
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 // Tout groupe ouvert ici doit être fermé dans 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: optimiser cela sans passer par un tableau temporaire
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 // Converti en un tableau de 3 composantes dont la dernière vaudra 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 // Il y a 5 valeurs d'offset utilisées :
1271 //
1272 // - offset sur le nombre de mailles (CellOffsets). Cet offset a pour nombre d'éléments
1273 // le nombre de temps sauvés et est augmenté à chaque sortie du nombre de mailles. Cet offset
1274 // est aussi utilisé pour les variables aux mailles
1275 // - offset sur le nombre de noeuds (PointOffsets). Il est équivalent à 'CellOffsets' mais
1276 // pour les noeuds.
1277 // - offset pour "NumberOfCells", "NumberOfPoints" et "NumberOfConnectivityIds". Pour chacun
1278 // de ces champs il y a NbPart valeurs par temps, avec 'NbPart' le nombre de parties (donc
1279 // le nombre de sous-domaines si on ne fait pas de regroupement). Il y a ainsi au total
1280 // NbPart * NbTimeStep dans ce champ d'offset.
1281 // - offset pour le champ "Connectivity" qui s'appelle "ConnectivityIdOffsets".
1282 // Cet offset a pour nombre d'éléments le nombre de temps sauvés.
1283 // - offset pour le champ "Offsets". "Offset" contient pour chaque maille l'offset dans
1284 // "Connectivity" de la connectivité des noeuds de la maille. Cet offset n'est pas sauvés,
1285 // mais comme ce champ à un nombre de valeurs égal au nombre de mailles plus un il est possible
1286 // de le déduire de "CellOffsets" (il vaut "CellOffsets" plus l'index du temps courant).
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 // Ces trois offsets ne sont pas sauvegardés dans le format VTK
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 // Regarde si on n'a pas fait de retour-arrière.
1297 // C'est le cas si le nombre de temps sauvés est supérieur au nombre
1298 // de valeurs de \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 // Signifie qu'on a fait un retour arrière.
1317 // Dans ce cas, il faut relire les offsets
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/*---------------------------------------------------------------------------*/
1333/*!
1334 * \brief Post-traitement au format VtkHdf V2.
1335 */
1336class VtkHdfV2PostProcessor
1337: public ArcaneVtkHdfV2PostProcessorObject
1338{
1339 public:
1340
1341 explicit VtkHdfV2PostProcessor(const ServiceBuildInfo& sbi)
1342 : ArcaneVtkHdfV2PostProcessorObject(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());
1358 Directory dir(baseDirectoryName());
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,
1378
1379/*---------------------------------------------------------------------------*/
1380/*---------------------------------------------------------------------------*/
1381
1382} // namespace Arcane
1383
1384/*---------------------------------------------------------------------------*/
1385/*---------------------------------------------------------------------------*/
#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:349
static void useMutex(bool is_active, IParallelMng *pm)
Fonction permettant d'activer ou de désactiver les verrous à chaque appel à HDF5.
Definition Hdf5Utils.cc:90
static constexpr bool hasParallelHdf5()
Vrai HDF5 est compilé avec le support de MPI.
Definition Hdf5Utils.h:122
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:34
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.