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