Nektar++
Loading...
Searching...
No Matches
MeshGraphIOHDF5.cpp
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////////////
2//
3// File: MeshGraphIOHDF5.cpp
4//
5// For more information, please see: http://www.nektar.info/
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: HDF5-based mesh format for Nektar++.
32//
33////////////////////////////////////////////////////////////////////////////////
34
35#include <boost/algorithm/string.hpp>
36#include <tinyxml.h>
37#include <type_traits>
38
45
46#define TIME_RESULT(verb, msg, timer) \
47 if (verb) \
48 { \
49 std::cout << " - " << msg << ": " << timer.TimePerTest(1) << "\n" \
50 << std::endl; \
51 }
52
53using namespace Nektar::LibUtilities;
54
56{
57
58/// Version of the Nektar++ HDF5 geometry format, which is embedded into the
59/// main NEKTAR/GEOMETRY group as an attribute.
60const unsigned int MeshGraphIOHDF5::FORMAT_VERSION = 2;
61
64 "HDF5", MeshGraphIOHDF5::create, "IO with HDF5 geometry");
65
67{
69 ReadDomain();
70
71 m_meshGraph->ReadExpansionInfo(m_session->GetElement("NEKTAR/EXPANSIONS"));
72
73 // Close up shop.
74 m_mesh->Close();
75 m_maps->Close();
76 m_file->Close();
77
78 if (fillGraph)
79 {
80 m_meshGraph->FillGraph();
81 }
82}
83
84/**
85 * @brief Utility function to split a vector equally amongst a number of
86 * processors.
87 *
88 * @param vecsize Size of the total amount of work
89 * @param rank Rank of this process
90 * @param nprocs Number of processors in the group
91 *
92 * @return A pair with the offset this process should occupy, along with the
93 * count for the amount of work.
94 */
95std::pair<size_t, size_t> SplitWork(size_t vecsize, int rank, int nprocs)
96{
97 size_t div = vecsize / nprocs;
98 size_t rem = vecsize % nprocs;
99 if (rank < rem)
100 {
101 return std::make_pair(rank * (div + 1), div + 1);
102 }
103 else
104 {
105 return std::make_pair((rank - rem) * div + rem * (div + 1), div);
106 }
107}
108
109template <class T, typename std::enable_if<T::kDim == 0, int>::type = 0>
110inline int GetGeomDataDim([[maybe_unused]] GeomMapView<T> &geomMap)
111{
112 return 3;
113}
114
115template <class T, typename std::enable_if<T::kDim == 1, int>::type = 0>
116inline int GetGeomDataDim([[maybe_unused]] GeomMapView<T> &geomMap)
117{
118 return T::kNverts;
119}
120
121template <class T, typename std::enable_if<T::kDim == 2, int>::type = 0>
122inline int GetGeomDataDim([[maybe_unused]] GeomMapView<T> &geomMap)
123{
124 return T::kNedges;
125}
126
127template <class T, typename std::enable_if<T::kDim == 3, int>::type = 0>
128inline int GetGeomDataDim([[maybe_unused]] GeomMapView<T> &geomMap)
129{
130 return T::kNfaces;
131}
132
133template <class... T>
134inline void UniqueValues([[maybe_unused]] std::unordered_set<int> &unique)
135{
136}
137
138template <class... T>
139inline void UniqueValues(std::unordered_set<int> &unique,
140 const std::vector<int> &input, T &...args)
141{
142 for (auto i : input)
143 {
144 unique.insert(i);
145 }
146
147 UniqueValues(unique, args...);
148}
149
150std::string MeshGraphIOHDF5::cmdSwitch =
152 "use-hdf5-node-comm", "",
153 "Use a per-node communicator for HDF5 partitioning.");
154
155/**
156 * @brief Partition the mesh
157 */
160
161{
163 all.Start();
164 int err;
165 LibUtilities::CommSharedPtr comm = session->GetComm();
166 LibUtilities::CommSharedPtr commMesh = comm->GetRowComm();
167 const bool isRoot = comm->TreatAsRankZero();
168
169 // By default, only the root process will have read the session file, which
170 // is done to avoid every process needing to read the XML file. For HDF5, we
171 // don't care about this, so just have every process parse the session file.
172 if (!isRoot)
173 {
174 session->InitSession();
175 }
176
177 // We use the XML geometry to find information about the HDF5 file.
178 m_session = session;
179 m_xmlGeom = session->GetElement("NEKTAR/GEOMETRY");
180 TiXmlAttribute *attr = m_xmlGeom->FirstAttribute();
181 int meshDimension = 3;
182 int spaceDimension = 3;
183
184 while (attr)
185 {
186 std::string attrName(attr->Name());
187 if (attrName == "DIM")
188 {
189 err = attr->QueryIntValue(&meshDimension);
190 ASSERTL0(err == TIXML_SUCCESS, "Unable to read mesh dimension.");
191 }
192 else if (attrName == "SPACE")
193 {
194 err = attr->QueryIntValue(&spaceDimension);
195 ASSERTL0(err == TIXML_SUCCESS, "Unable to read space dimension.");
196 }
197 else if (attrName == "PARTITION")
198 {
199 ASSERTL0(false,
200 "PARTITION parameter should only be used in XML meshes");
201 }
202 else if (attrName == "HDF5FILE")
203 {
204 m_hdf5Name = attr->Value();
205 }
206 else if (attrName == "PARTITIONED")
207 {
208 ASSERTL0(false,
209 "PARTITIONED parameter should only be used in XML meshes");
210 }
211 else
212 {
213 std::string errstr("Unknown attribute: ");
214 errstr += attrName;
215 ASSERTL1(false, errstr.c_str());
216 }
217 // Get the next attribute.
218 attr = attr->Next();
219 }
220
221 ASSERTL0(m_hdf5Name.size() > 0, "Unable to obtain mesh file name.");
222 ASSERTL0(meshDimension <= spaceDimension,
223 "Mesh dimension greater than space dimension.");
224
225 m_meshGraph->SetMeshDimension(meshDimension);
226 m_meshGraph->SetSpaceDimension(spaceDimension);
227
228 // Open handle to the HDF5 mesh
231
232#if 0
233#if defined(NEKTAR_USE_MPI) && defined(NEKTAR_HDF5_PARALLEL)
234 if (commMesh->GetSize() > 1)
235 {
236 // Use MPI/O to access the file
237 parallelProps = H5::PList::FileAccess();
238 parallelProps->SetMpio(commMesh);
239 // Use collective IO
241 m_readPL->SetDxMpioCollective();
242 }
243#endif
244#endif
245
246 m_file = H5::File::Open(m_hdf5Name, H5F_ACC_RDONLY, parallelProps);
247
248 auto root = m_file->OpenGroup("NEKTAR");
249 ASSERTL0(root, "Cannot find NEKTAR group in HDF5 file.");
250
251 auto root2 = root->OpenGroup("GEOMETRY");
252 ASSERTL0(root2, "Cannot find NEKTAR/GEOMETRY group in HDF5 file.");
253
254 // Check format version
255 H5::Group::AttrIterator attrIt = root2->attr_begin();
256 H5::Group::AttrIterator attrEnd = root2->attr_end();
257 for (; attrIt != attrEnd; ++attrIt)
258 {
259 if (*attrIt == "FORMAT_VERSION")
260 {
261 break;
262 }
263 }
264 ASSERTL0(attrIt != attrEnd,
265 "Unable to determine Nektar++ geometry HDF5 file version.");
266 root2->GetAttribute("FORMAT_VERSION", m_inFormatVersion);
267
269 "File format in " + m_hdf5Name +
270 " is higher than supported in "
271 "this version of Nektar++");
272
273 m_mesh = root2->OpenGroup("MESH");
274 ASSERTL0(m_mesh, "Cannot find NEKTAR/GEOMETRY/MESH group in HDF5 file.");
275 m_maps = root2->OpenGroup("MAPS");
276 ASSERTL0(m_mesh, "Cannot find NEKTAR/GEOMETRY/MAPS group in HDF5 file.");
277
279 {
280 return;
281 }
282
283 if (m_meshGraph->GetDomainRange() &&
284 m_meshGraph->GetDomainRange()->m_compElmts)
285 {
286 m_meshGraph->GetDomainRange()->m_compElmts = meshDimension;
287 }
288
289 SetupCompositeRange(m_meshGraph->GetDomainRange());
290
291 m_meshPartitioned = true;
292 m_meshGraph->SetMeshPartitioned(true);
293
294 // Depending on dimension, read element IDs.
295 std::map<int,
296 std::vector<std::tuple<std::string, int, LibUtilities::ShapeType>>>
297 dataSets;
298
299 dataSets[1] = {{"SEG", 2, LibUtilities::eSegment}};
300 dataSets[2] = {{"TRI", 3, LibUtilities::eTriangle},
301 {"QUAD", 4, LibUtilities::eQuadrilateral}};
302 dataSets[3] = {{"TET", 4, LibUtilities::eTetrahedron},
303 {"PYR", 5, LibUtilities::ePyramid},
304 {"PRISM", 5, LibUtilities::ePrism},
305 {"HEX", 6, LibUtilities::eHexahedron}};
306
307 const bool verbRoot = isRoot && session->DefinesCmdLineArgument("verbose");
308
309 if (verbRoot)
310 {
311 std::cout << "Reading HDF5 geometry..." << std::endl;
312 }
313
314 // If we want to use an multi-level communicator, then split the
315 // communicator at this point. We set by default the inter-node communicator
316 // to be the normal communicator: this way if the multi-level partitioning
317 // is disabled we proceed as 'normal'.
318 LibUtilities::CommSharedPtr innerComm, interComm = comm;
319 int innerRank = 0, innerSize = 1,
320 interRank = interComm->GetRowComm()->GetRank(),
321 interSize = interComm->GetRowComm()->GetSize();
322
323 if (session->DefinesCmdLineArgument("use-hdf5-node-comm"))
324 {
325 ASSERTL0(comm->GetSize() == commMesh->GetSize(),
326 "--use-hdf5-node-comm not available with Parallel-in-Time")
327
328 auto splitComm = comm->SplitCommNode();
329 innerComm = splitComm.first;
330 interComm = splitComm.second;
331 innerRank = innerComm->GetRank();
332 innerSize = innerComm->GetSize();
333
334 if (innerRank == 0)
335 {
336 interRank = interComm->GetRank();
337 interSize = interComm->GetSize();
338 }
339 }
340
341 // Unordered set of rows of the dataset this process needs to read for the
342 // elements of dimension meshDimension.
343 std::unordered_set<int> toRead;
344
345 // Calculate reasonably even distribution of processors for calling
346 // ptScotch. We'll do this work on only one process per node.
348 t.Start();
349
350 if (verbRoot)
351 {
352 std::cout << " - beginning partitioning" << std::endl;
353 }
354
355 // Perform initial read if either (a) we are on all ranks and multi-level
356 // partitioning is not enabled; or (b) rank 0 of all nodes if it is.
357 if (innerRank == 0)
358 {
360 t2.Start();
361
362 const bool verbRoot2 =
363 isRoot && session->DefinesCmdLineArgument("verbose");
364
365 // Read IDs for partitioning purposes
366 std::vector<int> ids;
367
368 // Map from element ID to 'row' which is a contiguous ordering required
369 // for parallel partitioning.
370 std::vector<MeshEntity> elmts;
371 std::unordered_map<int, int> row2id, id2row;
372
376
377 if (innerComm)
378 {
379 // For per-node partitioning, create a temporary reader (otherwise
380 // communicators are inconsistent).
381 auto parallelProps = H5::PList::FileAccess();
382 parallelProps->SetMpio(interComm);
383
384 // Use collective IO
385#if 0
386#if defined(NEKTAR_USE_MPI) && defined(NEKTAR_HDF5_PARALLEL)
387 readPL = H5::PList::DatasetXfer();
388 readPL->SetDxMpioCollective();
389#endif
390#endif
391 file = H5::File::Open(m_hdf5Name, H5F_ACC_RDONLY, parallelProps);
392
393 auto root = file->OpenGroup("NEKTAR");
394 auto root2 = root->OpenGroup("GEOMETRY");
395 mesh = root2->OpenGroup("MESH");
396 maps = root2->OpenGroup("MAPS");
397 }
398
399 int rowCount = 0;
400 for (auto &it : dataSets[meshDimension])
401 {
402 std::string ds = std::get<0>(it);
403
404 if (!mesh->ContainsDataSet(ds))
405 {
406 continue;
407 }
408
409 // Open metadata dataset
410 H5::DataSetSharedPtr data = mesh->OpenDataSet(ds);
411 H5::DataSpaceSharedPtr space = data->GetSpace();
412 std::vector<hsize_t> dims = space->GetDims();
413
414 H5::DataSetSharedPtr mdata = maps->OpenDataSet(ds);
415 H5::DataSpaceSharedPtr mspace = mdata->GetSpace();
416 std::vector<hsize_t> mdims = mspace->GetDims();
417
418 // TODO: This could perhaps be done more intelligently; reads all
419 // IDs for the top-level elements so that we can construct the dual
420 // graph of the mesh.
421 std::vector<int> tmpElmts, tmpIds;
422 mdata->Read(tmpIds, mspace, readPL);
423 data->Read(tmpElmts, space, readPL);
424
425 const int nGeomData = std::get<1>(it);
426 auto tmpIt = tmpElmts.begin();
427
428 if (m_meshGraph->GetDomainRange() ==
430 {
431 // avoid range checking on larger meshes if not required
432 for (int i = 0; i < tmpIds.size();
433 ++i, ++rowCount, tmpIt += nGeomData)
434 {
435 MeshEntity e;
436 row2id[rowCount] = tmpIds[i];
437 id2row[tmpIds[i]] = row2id[rowCount];
438 e.id = rowCount;
439 e.origId = tmpIds[i];
440 e.ghost = false;
441 e.list =
442 std::vector<unsigned int>(tmpIt, tmpIt + nGeomData);
443 elmts.push_back(e);
444 }
445 }
446 else
447 {
448 // avoid range checking on larger meshes if not required
449 for (int i = 0; i < tmpIds.size();
450 ++i, ++rowCount, tmpIt += nGeomData)
451 {
452 MeshEntity e;
453 row2id[rowCount] = tmpIds[i];
454 id2row[tmpIds[i]] = row2id[rowCount];
455 e.id = rowCount;
456 e.origId = tmpIds[i];
457 e.ghost = false;
458 e.list =
459 std::vector<unsigned int>(tmpIt, tmpIt + nGeomData);
460 if (m_meshGraph->CheckRange(e))
461 {
462 elmts.push_back(e);
463 }
464 }
465 }
466 }
467
468 interComm->GetRowComm()->Block();
469
470 t2.Stop();
471 TIME_RESULT(verbRoot2, " - initial read", t2);
472 t2.Start();
473
474 // Do not partition in serial since postprocessing may not
475 // lead to very suitable element distribution that then leads
476 // to challenges with Scotch
477 if (commMesh->GetSize() > 1)
478 {
479 // Check to see we have at least as many processors as elements.
480 size_t numElmt = elmts.size();
481 ASSERTL0(commMesh->GetSize() <= numElmt,
482 "This mesh has more processors than elements!");
483
484 auto elRange = SplitWork(numElmt, interRank, interSize);
485
486 // Construct map of element entities for partitioner.
487 std::map<int, MeshEntity> partElmts;
488 std::unordered_set<int> facetIDs;
489
490 int vcnt = 0;
491
492 for (int el = elRange.first; el < elRange.first + elRange.second;
493 ++el, ++vcnt)
494 {
495 MeshEntity elmt = elmts[el];
496 elmt.ghost = false;
497 partElmts[elmt.id] = elmt;
498
499 for (auto &facet : elmt.list)
500 {
501 facetIDs.insert(facet);
502 }
503 }
504
505 // Now identify ghost vertices for the graph. This could also
506 // probably be improved.
507 int nLocal = vcnt;
508 for (int i = 0; i < numElmt; ++i)
509 {
510 // Ignore anything we already read.
511 if (i >= elRange.first && i < elRange.first + elRange.second)
512 {
513 continue;
514 }
515
516 MeshEntity elmt = elmts[i];
517 bool insert = false;
518
519 // Check for connections to local elements.
520 for (auto &eId : elmt.list)
521 {
522 if (facetIDs.find(eId) != facetIDs.end())
523 {
524 insert = true;
525 break;
526 }
527 }
528
529 if (insert)
530 {
531 elmt.ghost = true;
532 partElmts[elmt.id] = elmt;
533 }
534 }
535
536 // Create partitioner. Default partitioner to use is PtScotch. Use
537 // ParMetis as default if it is installed. Override default with
538 // command-line flags if they are set.
539 std::string partitionerName =
540 commMesh->GetSize() > 1 ? "PtScotch" : "Scotch";
541 if (GetMeshPartitionFactory().ModuleExists("ParMetis"))
542 {
543 partitionerName = "ParMetis";
544 }
545 if (session->DefinesCmdLineArgument("use-parmetis"))
546 {
547 partitionerName = "ParMetis";
548 }
549 if (session->DefinesCmdLineArgument("use-ptscotch"))
550 {
551 partitionerName = "PtScotch";
552 }
553
554 MeshPartitionSharedPtr partitioner =
556 partitionerName, session, interComm, meshDimension,
557 partElmts, CreateCompositeDescriptor(id2row));
558
559 t2.Stop();
560 TIME_RESULT(verbRoot2, " - partitioner setup", t2);
561 t2.Start();
562
563 partitioner->PartitionMesh(interSize, true, false, nLocal);
564 t2.Stop();
565 TIME_RESULT(verbRoot2, " - partitioning", t2);
566 t2.Start();
567
568 // Now construct a second graph that is partitioned in serial by
569 // this rank.
570 std::vector<unsigned int> nodeElmts;
571 partitioner->GetElementIDs(interRank, nodeElmts);
572
573 if (innerSize > 1)
574 {
575 // Construct map of element entities for partitioner.
576 std::map<int, MeshEntity> partElmts;
577 std::unordered_map<int, int> row2elmtid, elmtid2row;
578
579 int vcnt = 0;
580
581 // We need to keep track of which elements in the new partition
582 // correspond to elemental IDs for later (in a similar manner to
583 // row2id).
584 for (auto &elmtRow : nodeElmts)
585 {
586 row2elmtid[vcnt] = elmts[elmtRow].origId;
587 elmtid2row[elmts[elmtRow].origId] = vcnt;
588 MeshEntity elmt = elmts[elmtRow];
589 elmt.ghost = false;
590 partElmts[vcnt++] = elmt;
591 }
592
593 // Create temporary serial communicator for serial partitioning.
595 "Serial", 0, 0);
596
597 MeshPartitionSharedPtr partitioner =
599 "Scotch", session, tmpComm, meshDimension, partElmts,
600 CreateCompositeDescriptor(elmtid2row));
601
602 t2.Stop();
603 TIME_RESULT(verbRoot2, " - inner partition setup", t2);
604 t2.Start();
605
606 partitioner->PartitionMesh(innerSize, true, false, 0);
607
608 t2.Stop();
609 TIME_RESULT(verbRoot2, " - inner partitioning", t2);
610 t2.Start();
611
612 // Send contributions to remaining processors.
613 for (int i = 1; i < innerSize; ++i)
614 {
615 std::vector<unsigned int> tmp;
616 partitioner->GetElementIDs(i, tmp);
617 size_t tmpsize = tmp.size();
618 for (int j = 0; j < tmpsize; ++j)
619 {
620 tmp[j] = row2elmtid[tmp[j]];
621 }
622 innerComm->Send(i, tmpsize);
623 innerComm->Send(i, tmp);
624 }
625
626 t2.Stop();
627 TIME_RESULT(verbRoot2, " - inner partition scatter", t2);
628
629 std::vector<unsigned int> tmp;
630 partitioner->GetElementIDs(0, tmp);
631
632 for (auto &tmpId : tmp)
633 {
634 toRead.insert(row2elmtid[tmpId]);
635 }
636 }
637 else
638 {
639 for (auto &tmpId : nodeElmts)
640 {
641 toRead.insert(row2id[tmpId]);
642 }
643 }
644 }
645 else // Serial: Fill toRead with Elmt.origId
646 {
647 for (auto &tmpId : elmts)
648 {
649 toRead.insert(tmpId.origId);
650 }
651 }
652 }
653 else
654 {
655 // For multi-level partitioning, the innermost rank receives its
656 // partitions from rank 0 on each node.
657 size_t tmpSize;
658 innerComm->Recv(0, tmpSize);
659 std::vector<unsigned int> tmp(tmpSize);
660 innerComm->Recv(0, tmp);
661
662 for (auto &tmpId : tmp)
663 {
664 toRead.insert(tmpId);
665 }
666 }
667
668 t.Stop();
669 TIME_RESULT(verbRoot, "partitioning total", t);
670
671 // Since objects are going to be constructed starting from vertices, we
672 // now need to recurse down the geometry facet dimensions to figure out
673 // which rows to read from each dataset.
674 std::vector<int> vertIDs, segIDs, triIDs, quadIDs;
675 std::vector<int> tetIDs, prismIDs, pyrIDs, hexIDs;
676 std::vector<int> segData, triData, quadData, tetData;
677 std::vector<int> prismData, pyrData, hexData;
678 std::vector<NekDouble> vertData;
679
680 auto &vertSet = m_meshGraph->GetGeomMap<PointGeom>();
681 auto &segGeoms = m_meshGraph->GetGeomMap<SegGeom>();
682 auto &triGeoms = m_meshGraph->GetGeomMap<TriGeom>();
683 auto &quadGeoms = m_meshGraph->GetGeomMap<QuadGeom>();
684 auto &hexGeoms = m_meshGraph->GetGeomMap<HexGeom>();
685 auto &pyrGeoms = m_meshGraph->GetGeomMap<PyrGeom>();
686 auto &prismGeoms = m_meshGraph->GetGeomMap<PrismGeom>();
687 auto &tetGeoms = m_meshGraph->GetGeomMap<TetGeom>();
688 auto &curvedEdges = m_meshGraph->GetCurvedEdges();
689 auto &curvedFaces = m_meshGraph->GetCurvedFaces();
690
691 if (meshDimension == 3)
692 {
693 t.Start();
694 // Read 3D data
695 ReadGeometryData(hexGeoms, "HEX", toRead, hexIDs, hexData);
696 ReadGeometryData(pyrGeoms, "PYR", toRead, pyrIDs, pyrData);
697 ReadGeometryData(prismGeoms, "PRISM", toRead, prismIDs, prismData);
698 ReadGeometryData(tetGeoms, "TET", toRead, tetIDs, tetData);
699
700 toRead.clear();
701 UniqueValues(toRead, hexData, pyrData, prismData, tetData);
702 t.Stop();
703 TIME_RESULT(verbRoot, "read 3D elements", t);
704 }
705
706 if (meshDimension >= 2)
707 {
708 t.Start();
709 // Read 2D data
710 ReadGeometryData(triGeoms, "TRI", toRead, triIDs, triData);
711 ReadGeometryData(quadGeoms, "QUAD", toRead, quadIDs, quadData);
712
713 toRead.clear();
714 UniqueValues(toRead, triData, quadData);
715 t.Stop();
716 TIME_RESULT(verbRoot, "read 2D elements", t);
717 }
718
719 if (meshDimension >= 1)
720 {
721 t.Start();
722 // Read 1D data
723 ReadGeometryData(segGeoms, "SEG", toRead, segIDs, segData);
724
725 toRead.clear();
726 UniqueValues(toRead, segData);
727 t.Stop();
728 TIME_RESULT(verbRoot, "read 1D elements", t);
729 }
730
731 t.Start();
732 ReadGeometryData(vertSet, "VERT", toRead, vertIDs, vertData);
733 t.Stop();
734 TIME_RESULT(verbRoot, "read 0D elements", t);
735
736 // Now start to construct geometry objects, starting from vertices
737 // upwards.
738 t.Start();
739 FillGeomMap(vertSet, CurveMap(), vertIDs, vertData);
740 t.Stop();
741 TIME_RESULT(verbRoot, "construct 0D elements", t);
742
743 if (meshDimension >= 1)
744 {
745 // Read curves
746 toRead.clear();
747 for (auto &edge : segIDs)
748 {
749 toRead.insert(edge);
750 }
751 ReadCurveMap(curvedEdges, "CURVE_EDGE", toRead);
752
753 t.Start();
754 FillGeomMap(segGeoms, curvedEdges, segIDs, segData);
755 t.Stop();
756 TIME_RESULT(verbRoot, "construct 1D elements", t);
757 }
758
759 if (meshDimension >= 2)
760 {
761 // Read curves
762 toRead.clear();
763 for (auto &face : triIDs)
764 {
765 toRead.insert(face);
766 }
767 for (auto &face : quadIDs)
768 {
769 toRead.insert(face);
770 }
771 ReadCurveMap(curvedFaces, "CURVE_FACE", toRead);
772
773 t.Start();
774 FillGeomMap(triGeoms, curvedFaces, triIDs, triData);
775 FillGeomMap(quadGeoms, curvedFaces, quadIDs, quadData);
776 t.Stop();
777 TIME_RESULT(verbRoot, "construct 2D elements", t);
778 }
779
780 if (meshDimension >= 3)
781 {
782 t.Start();
783 FillGeomMap(hexGeoms, CurveMap(), hexIDs, hexData);
784 FillGeomMap(prismGeoms, CurveMap(), prismIDs, prismData);
785 FillGeomMap(pyrGeoms, CurveMap(), pyrIDs, pyrData);
786 FillGeomMap(tetGeoms, CurveMap(), tetIDs, tetData);
787 t.Stop();
788 TIME_RESULT(verbRoot, "construct 3D elements", t);
789 }
790
791 // Populate m_bndRegOrder.
792 if (session->DefinesElement("NEKTAR/CONDITIONS"))
793 {
794 std::set<int> vBndRegionIdList;
795 TiXmlElement *vConditions =
796 new TiXmlElement(*session->GetElement("Nektar/Conditions"));
797 TiXmlElement *vBndRegions =
798 vConditions->FirstChildElement("BOUNDARYREGIONS");
799 // Use fine-level for mesh partition (Parallel-in-Time)
801 TiXmlElement *vItem;
802
803 if (vBndRegions)
804 {
805 auto &graph_bndRegOrder = m_meshGraph->GetBndRegionOrdering();
806 vItem = vBndRegions->FirstChildElement();
807 while (vItem)
808 {
809 std::string vSeqStr = vItem->FirstChild()->ToText()->Value();
810 std::string::size_type indxBeg = vSeqStr.find_first_of('[') + 1;
811 std::string::size_type indxEnd = vSeqStr.find_last_of(']') - 1;
812 vSeqStr = vSeqStr.substr(indxBeg, indxEnd - indxBeg + 1);
813
814 std::vector<unsigned int> vSeq;
815 ParseUtils::GenerateSeqVector(vSeqStr.c_str(), vSeq);
816
817 int p = atoi(vItem->Attribute("ID"));
818 m_bndRegOrder[p] = vSeq;
819 graph_bndRegOrder[p] = vSeq;
820 vItem = vItem->NextSiblingElement();
821 }
822 }
823 }
824
825 all.Stop();
826 TIME_RESULT(verbRoot, "total time", all);
827}
828
829template <class T, typename DataType>
831 [[maybe_unused]] GeomMapView<T> &geomMap, [[maybe_unused]] int id,
832 [[maybe_unused]] DataType *data, [[maybe_unused]] Curve *curve)
833{
834}
835
836template <>
838 [[maybe_unused]] GeomMapView<PointGeom> &geomMap, int id, NekDouble *data,
839 [[maybe_unused]] Curve *curve)
840{
841 m_meshGraph->CreatePointGeom(m_meshGraph->GetSpaceDimension(), id, data[0],
842 data[1], data[2]);
843}
844
845template <>
847 [[maybe_unused]] GeomMapView<SegGeom> &geomMap, int id, int *data,
848 Curve *curve)
849{
850 std::array<PointGeom *, 2> pts = {m_meshGraph->GetPointGeom(data[0]),
851 m_meshGraph->GetPointGeom(data[1])};
852
853 m_meshGraph->CreateSegGeom(id, m_meshGraph->GetSpaceDimension(), pts,
854 curve);
855}
856
857template <>
859 [[maybe_unused]] GeomMapView<TriGeom> &geomMap, int id, int *data,
860 Curve *curve)
861{
862 std::array<SegGeom *, 3> segs = {m_meshGraph->GetSegGeom(data[0]),
863 m_meshGraph->GetSegGeom(data[1]),
864 m_meshGraph->GetSegGeom(data[2])};
865 m_meshGraph->CreateTriGeom(id, segs, curve);
866}
867
868template <>
870 [[maybe_unused]] GeomMapView<QuadGeom> &geomMap, int id, int *data,
871 Curve *curve)
872{
873 std::array<SegGeom *, 4> segs = {
874 m_meshGraph->GetSegGeom(data[0]), m_meshGraph->GetSegGeom(data[1]),
875 m_meshGraph->GetSegGeom(data[2]), m_meshGraph->GetSegGeom(data[3])};
876 m_meshGraph->CreateQuadGeom(id, segs, curve);
877}
878
879template <>
881 [[maybe_unused]] GeomMapView<TetGeom> &geomMap, int id, int *data,
882 [[maybe_unused]] Curve *curve)
883{
884 std::array<TriGeom *, 4> faces = {
885 m_meshGraph->GetTriGeom(data[0]), m_meshGraph->GetTriGeom(data[1]),
886 m_meshGraph->GetTriGeom(data[2]), m_meshGraph->GetTriGeom(data[3])};
887
888 auto geom = m_meshGraph->CreateTetGeom(id, faces);
889 m_meshGraph->PopulateFaceToElMap(geom, TetGeom::kNfaces);
890}
891
892template <>
894 [[maybe_unused]] GeomMapView<PyrGeom> &geomMap, int id, int *data,
895 [[maybe_unused]] Curve *curve)
896{
897 std::array<Geometry2D *, 5> faces = {m_meshGraph->GetGeometry2D(data[0]),
898 m_meshGraph->GetGeometry2D(data[1]),
899 m_meshGraph->GetGeometry2D(data[2]),
900 m_meshGraph->GetGeometry2D(data[3]),
901 m_meshGraph->GetGeometry2D(data[4])};
902
903 auto geom = m_meshGraph->CreatePyrGeom(id, faces);
904 m_meshGraph->PopulateFaceToElMap(geom, PyrGeom::kNfaces);
905}
906
907template <>
909 [[maybe_unused]] GeomMapView<PrismGeom> &geomMap, int id, int *data,
910 [[maybe_unused]] Curve *curve)
911{
912 std::array<Geometry2D *, 5> faces = {m_meshGraph->GetGeometry2D(data[0]),
913 m_meshGraph->GetGeometry2D(data[1]),
914 m_meshGraph->GetGeometry2D(data[2]),
915 m_meshGraph->GetGeometry2D(data[3]),
916 m_meshGraph->GetGeometry2D(data[4])};
917
918 auto geom = m_meshGraph->CreatePrismGeom(id, faces);
919 m_meshGraph->PopulateFaceToElMap(geom, PrismGeom::kNfaces);
920}
921
922template <>
924 [[maybe_unused]] GeomMapView<HexGeom> &geomMap, int id, int *data,
925 [[maybe_unused]] Curve *curve)
926{
927 std::array<QuadGeom *, 6> faces = {
928 m_meshGraph->GetQuadGeom(data[0]), m_meshGraph->GetQuadGeom(data[1]),
929 m_meshGraph->GetQuadGeom(data[2]), m_meshGraph->GetQuadGeom(data[3]),
930 m_meshGraph->GetQuadGeom(data[4]), m_meshGraph->GetQuadGeom(data[5])};
931
932 auto geom = m_meshGraph->CreateHexGeom(id, faces);
933 m_meshGraph->PopulateFaceToElMap(geom, HexGeom::kNfaces);
934}
935
936template <class T, typename DataType>
938 const CurveMap &curveMap,
939 std::vector<int> &ids,
940 std::vector<DataType> &geomData)
941{
942 const int nGeomData = GetGeomDataDim(geomMap);
943 const int nRows = geomData.size() / nGeomData;
944 Curve *empty = nullptr;
945
946 // Construct geometry object.
947 if (curveMap.size() > 0)
948 {
949 for (int i = 0, cnt = 0; i < nRows; i++, cnt += nGeomData)
950 {
951 auto cIt = curveMap.find(ids[i]);
952 ConstructGeomObject(geomMap, ids[i], &geomData[cnt],
953 cIt == curveMap.end() ? empty
954 : cIt->second.get());
955 }
956 }
957 else
958 {
959 for (int i = 0, cnt = 0; i < nRows; i++, cnt += nGeomData)
960 {
961 ConstructGeomObject(geomMap, ids[i], &geomData[cnt], empty);
962 }
963 }
964}
965
966template <class T, typename DataType>
968 std::string dataSet,
969 const std::unordered_set<int> &readIds,
970 std::vector<int> &ids,
971 std::vector<DataType> &geomData)
972{
973 if (!m_mesh->ContainsDataSet(dataSet))
974 {
975 return;
976 }
977
978 // Open mesh dataset
979 H5::DataSetSharedPtr data = m_mesh->OpenDataSet(dataSet);
980 H5::DataSpaceSharedPtr space = data->GetSpace();
981 std::vector<hsize_t> dims = space->GetDims();
982
983 // Open metadata dataset
984 H5::DataSetSharedPtr mdata = m_maps->OpenDataSet(dataSet);
985 H5::DataSpaceSharedPtr mspace = mdata->GetSpace();
986 std::vector<hsize_t> mdims = mspace->GetDims();
987
988 ASSERTL0(mdims[0] == dims[0], "map and data set lengths do not match");
989
990 const int nGeomData = GetGeomDataDim(geomMap);
991
992 // Read all IDs
993 std::vector<int> allIds;
994 mdata->Read(allIds, mspace);
995
996 // Selective reading; clear data space range so that we can select
997 // certain rows from the datasets.
998 space->ClearRange();
999
1000 int i = 0;
1001 std::vector<hsize_t> coords;
1002 for (auto &id : allIds)
1003 {
1004 if (readIds.find(id) != readIds.end())
1005 {
1006 for (int j = 0; j < nGeomData; ++j)
1007 {
1008 coords.push_back(i);
1009 coords.push_back(j);
1010 }
1011 ids.push_back(id);
1012 }
1013 ++i;
1014 }
1015
1016 space->SetSelection(coords.size() / 2, coords);
1017
1018 // Read selected data.
1019 data->Read(geomData, space, m_readPL);
1020}
1021
1022void MeshGraphIOHDF5::ReadCurveMap(CurveMap &curveMap, std::string dsName,
1023 const std::unordered_set<int> &readIds)
1024{
1025 auto &curveNodes = m_meshGraph->GetAllCurveNodes();
1026
1027 // If dataset does not exist, exit.
1028 if (!m_mesh->ContainsDataSet(dsName))
1029 {
1030 return;
1031 }
1032
1033 // Open up curve map data.
1034 H5::DataSetSharedPtr curveData = m_mesh->OpenDataSet(dsName);
1035 H5::DataSpaceSharedPtr curveSpace = curveData->GetSpace();
1036
1037 // Open up ID data set.
1038 H5::DataSetSharedPtr idData = m_maps->OpenDataSet(dsName);
1039 H5::DataSpaceSharedPtr idSpace = idData->GetSpace();
1040
1041 // Read all IDs and clear data space.
1042 std::vector<int> ids, newIds;
1043 idData->Read(ids, idSpace);
1044 curveSpace->ClearRange();
1045
1046 // Search IDs to figure out which curves to read.
1047 std::vector<hsize_t> curveSel;
1048
1049 int cnt = 0;
1050 for (auto &id : ids)
1051 {
1052 if (readIds.find(id) != readIds.end())
1053 {
1054 curveSel.push_back(cnt);
1055 curveSel.push_back(0);
1056 curveSel.push_back(cnt);
1057 curveSel.push_back(1);
1058 curveSel.push_back(cnt);
1059 curveSel.push_back(2);
1060 newIds.push_back(id);
1061 }
1062
1063 ++cnt;
1064 }
1065
1066 // Check to see whether any processor will read anything
1067 auto toRead = newIds.size();
1068 m_session->GetComm()->GetRowComm()->AllReduce(toRead,
1070
1071 if (toRead == 0)
1072 {
1073 return;
1074 }
1075
1076 // Now read curve map and read data.
1077 std::vector<int> curveInfo;
1078 curveSpace->SetSelection(curveSel.size() / 2, curveSel);
1079 curveData->Read(curveInfo, curveSpace, m_readPL);
1080
1081 curveSel.clear();
1082
1083 std::unordered_map<int, int> curvePtOffset;
1084
1085 // Construct curves. We'll populate nodes in a minute!
1086 for (int i = 0, cnt = 0, cnt2 = 0; i < curveInfo.size() / 3; ++i, cnt += 3)
1087 {
1089 newIds[i], (LibUtilities::PointsType)curveInfo[cnt + 1]);
1090
1091 curve->m_points.resize(curveInfo[cnt]);
1092
1093 const int ptOffset = curveInfo[cnt + 2];
1094
1095 for (int j = 0; j < curveInfo[cnt]; ++j)
1096 {
1097 // ptoffset gives us the row, multiply by 3 for number of
1098 // coordinates.
1099 curveSel.push_back(ptOffset + j);
1100 curveSel.push_back(0);
1101 curveSel.push_back(ptOffset + j);
1102 curveSel.push_back(1);
1103 curveSel.push_back(ptOffset + j);
1104 curveSel.push_back(2);
1105 }
1106
1107 // Store the offset so we know to come back later on to fill in
1108 // these points.
1109 curvePtOffset[newIds[i]] = 3 * cnt2;
1110 cnt2 += curveInfo[cnt];
1111
1112 curveMap[newIds[i]] = std::move(curve);
1113 }
1114
1115 curveInfo.clear();
1116
1117 // Open node data spacee.
1118 H5::DataSetSharedPtr nodeData = m_mesh->OpenDataSet("CURVE_NODES");
1119 H5::DataSpaceSharedPtr nodeSpace = nodeData->GetSpace();
1120
1121 nodeSpace->ClearRange();
1122 nodeSpace->SetSelection(curveSel.size() / 2, curveSel);
1123
1124 std::vector<NekDouble> nodeRawData;
1125 nodeData->Read(nodeRawData, nodeSpace, m_readPL);
1126
1127 // Go back and populate data from nodes.
1128 for (auto &cIt : curvePtOffset)
1129 {
1130 Curve *curve = curveMap[cIt.first].get();
1131
1132 // Create nodes.
1133 int cnt = cIt.second;
1134 for (int i = 0; i < curve->m_points.size(); ++i, cnt += 3)
1135 {
1136 curveNodes.emplace_back(
1138 0, m_meshGraph->GetSpaceDimension(), nodeRawData[cnt],
1139 nodeRawData[cnt + 1], nodeRawData[cnt + 2]));
1140 curve->m_points[i] = curveNodes.back().get();
1141 }
1142 }
1143}
1144
1146{
1147 auto &domain = m_meshGraph->GetDomain();
1148
1149 if (m_inFormatVersion == 1)
1150 {
1151 std::map<int, CompositeSharedPtr> fullDomain;
1152 H5::DataSetSharedPtr dst = m_mesh->OpenDataSet("DOMAIN");
1153 H5::DataSpaceSharedPtr space = dst->GetSpace();
1154
1155 std::vector<std::string> data;
1156 dst->ReadVectorString(data, space, m_readPL);
1157 m_meshGraph->GetCompositeList(data[0], fullDomain);
1158 domain[0] = fullDomain;
1159
1160 return;
1161 }
1162
1163 std::vector<CompositeMap> fullDomain;
1164 H5::DataSetSharedPtr dst = m_mesh->OpenDataSet("DOMAIN");
1165 H5::DataSpaceSharedPtr space = dst->GetSpace();
1166
1167 std::vector<std::string> data;
1168 dst->ReadVectorString(data, space, m_readPL);
1169 for (auto &dIt : data)
1170 {
1171 fullDomain.push_back(CompositeMap());
1172 m_meshGraph->GetCompositeList(dIt, fullDomain.back());
1173 }
1174
1175 H5::DataSetSharedPtr mdata = m_maps->OpenDataSet("DOMAIN");
1176 H5::DataSpaceSharedPtr mspace = mdata->GetSpace();
1177
1178 std::vector<int> ids;
1179 mdata->Read(ids, mspace);
1180
1181 for (int i = 0; i < ids.size(); ++i)
1182 {
1183 domain[ids[i]] = fullDomain[i];
1184 }
1185}
1186
1188{
1189 if (!rng || rng->m_compElmts == false)
1190 {
1191 return; // composite range not being used.
1192 }
1193
1194 std::string nm = "COMPOSITE";
1195
1196 H5::DataSetSharedPtr data = m_mesh->OpenDataSet(nm);
1197 H5::DataSpaceSharedPtr space = data->GetSpace();
1198 std::vector<hsize_t> dims = space->GetDims();
1199
1200 std::vector<std::string> comps;
1201 data->ReadVectorString(comps, space);
1202
1203 H5::DataSetSharedPtr mdata = m_maps->OpenDataSet(nm);
1204 H5::DataSpaceSharedPtr mspace = mdata->GetSpace();
1205 std::vector<hsize_t> mdims = mspace->GetDims();
1206
1207 std::vector<int> ids;
1208 mdata->Read(ids, mspace);
1209
1210 for (int i = 0; i < dims[0]; i++)
1211 {
1212
1213 if (rng->m_comps.count(ids[i]))
1214 {
1215
1216 std::string compStr = comps[i];
1217
1218 char type;
1219 std::istringstream strm(compStr);
1220
1221 strm >> type;
1222
1223 CompositeSharedPtr comp =
1225
1226 std::string::size_type indxBeg = compStr.find_first_of('[') + 1;
1227 std::string::size_type indxEnd = compStr.find_last_of(']') - 1;
1228
1229 std::string indxStr =
1230 compStr.substr(indxBeg, indxEnd - indxBeg + 1);
1231 std::vector<unsigned int> seqVector;
1232
1233 ParseUtils::GenerateSeqVector(indxStr, seqVector);
1234
1235 for (auto it : seqVector) // add ids to Traceid
1236 {
1237 rng->m_traceIDs.insert(it);
1238 }
1239 }
1240 }
1241}
1242
1244{
1245 auto &vertSet = m_meshGraph->GetGeomMap<PointGeom>();
1246 auto &segGeoms = m_meshGraph->GetGeomMap<SegGeom>();
1247 auto &triGeoms = m_meshGraph->GetGeomMap<TriGeom>();
1248 auto &quadGeoms = m_meshGraph->GetGeomMap<QuadGeom>();
1249 auto &tetGeoms = m_meshGraph->GetGeomMap<TetGeom>();
1250 auto &pyrGeoms = m_meshGraph->GetGeomMap<PyrGeom>();
1251 auto &prismGeoms = m_meshGraph->GetGeomMap<PrismGeom>();
1252 auto &hexGeoms = m_meshGraph->GetGeomMap<HexGeom>();
1253 CompositeMap &meshComposites = m_meshGraph->GetComposites();
1254
1255 std::string nm = "COMPOSITE";
1256
1257 H5::DataSetSharedPtr data = m_mesh->OpenDataSet(nm);
1258 H5::DataSpaceSharedPtr space = data->GetSpace();
1259 std::vector<hsize_t> dims = space->GetDims();
1260
1261 std::vector<std::string> comps;
1262 data->ReadVectorString(comps, space);
1263
1264 H5::DataSetSharedPtr mdata = m_maps->OpenDataSet(nm);
1265 H5::DataSpaceSharedPtr mspace = mdata->GetSpace();
1266 std::vector<hsize_t> mdims = mspace->GetDims();
1267
1268 std::vector<int> ids;
1269 mdata->Read(ids, mspace);
1270
1271 auto &graph_compOrder = m_meshGraph->GetCompositeOrdering();
1272 for (int i = 0; i < dims[0]; i++)
1273 {
1274 std::string compStr = comps[i];
1275
1276 char type;
1277 std::istringstream strm(compStr);
1278
1279 strm >> type;
1280
1282
1283 std::string::size_type indxBeg = compStr.find_first_of('[') + 1;
1284 std::string::size_type indxEnd = compStr.find_last_of(']') - 1;
1285
1286 std::string indxStr = compStr.substr(indxBeg, indxEnd - indxBeg + 1);
1287 std::vector<unsigned int> seqVector;
1288
1289 ParseUtils::GenerateSeqVector(indxStr, seqVector);
1290 m_compOrder[ids[i]] = seqVector;
1291 graph_compOrder[ids[i]] = seqVector;
1292
1293 switch (type)
1294 {
1295 case 'V':
1296 for (auto &i : seqVector)
1297 {
1298 auto it = vertSet.find(i);
1299 if (it != vertSet.end())
1300 {
1301 comp->m_geomVec.push_back((*it).second);
1302 }
1303 }
1304 break;
1305 case 'S':
1306 case 'E':
1307 for (auto &i : seqVector)
1308 {
1309 auto it = segGeoms.find(i);
1310 if (it != segGeoms.end())
1311 {
1312 comp->m_geomVec.push_back((*it).second);
1313 }
1314 }
1315 break;
1316 case 'Q':
1317 for (auto &i : seqVector)
1318 {
1319 auto it = quadGeoms.find(i);
1320 if (it != quadGeoms.end())
1321 {
1322 if (m_meshGraph->CheckRange(*(*it).second))
1323 {
1324 comp->m_geomVec.push_back((*it).second);
1325 }
1326 }
1327 }
1328 break;
1329 case 'T':
1330 for (auto &i : seqVector)
1331 {
1332 auto it = triGeoms.find(i);
1333 if (it != triGeoms.end())
1334 {
1335 if (m_meshGraph->CheckRange(*(*it).second))
1336 {
1337 comp->m_geomVec.push_back((*it).second);
1338 }
1339 }
1340 }
1341 break;
1342 case 'F':
1343 for (auto &i : seqVector)
1344 {
1345 auto it1 = quadGeoms.find(i);
1346 if (it1 != quadGeoms.end())
1347 {
1348 if (m_meshGraph->CheckRange(*(*it1).second))
1349 {
1350 comp->m_geomVec.push_back((*it1).second);
1351 }
1352 }
1353 auto it2 = triGeoms.find(i);
1354 if (it2 != triGeoms.end())
1355 {
1356 if (m_meshGraph->CheckRange(*(*it2).second))
1357 {
1358 comp->m_geomVec.push_back((*it2).second);
1359 }
1360 }
1361 }
1362 break;
1363 case 'A':
1364 for (auto &i : seqVector)
1365 {
1366 auto it = tetGeoms.find(i);
1367 if (it != tetGeoms.end())
1368 {
1369 if (m_meshGraph->CheckRange(*(*it).second))
1370 {
1371 comp->m_geomVec.push_back((*it).second);
1372 }
1373 }
1374 }
1375 break;
1376 case 'P':
1377 for (auto &i : seqVector)
1378 {
1379 auto it = pyrGeoms.find(i);
1380 if (it != pyrGeoms.end())
1381 {
1382 if (m_meshGraph->CheckRange(*(*it).second))
1383 {
1384 comp->m_geomVec.push_back((*it).second);
1385 }
1386 }
1387 }
1388 break;
1389 case 'R':
1390 for (auto &i : seqVector)
1391 {
1392 auto it = prismGeoms.find(i);
1393 if (it != prismGeoms.end())
1394 {
1395 if (m_meshGraph->CheckRange(*(*it).second))
1396 {
1397 comp->m_geomVec.push_back((*it).second);
1398 }
1399 }
1400 }
1401 break;
1402 case 'H':
1403 for (auto &i : seqVector)
1404 {
1405 auto it = hexGeoms.find(i);
1406 if (it != hexGeoms.end())
1407 {
1408 if (m_meshGraph->CheckRange(*(*it).second))
1409 {
1410 comp->m_geomVec.push_back((*it).second);
1411 }
1412 }
1413 }
1414 break;
1415 }
1416
1417 if (comp->m_geomVec.size() > 0)
1418 {
1419 meshComposites[ids[i]] = comp;
1420 }
1421 }
1422}
1423
1425 std::unordered_map<int, int> &id2row)
1426{
1428
1429 std::string nm = "COMPOSITE";
1430
1431 H5::DataSetSharedPtr data = m_mesh->OpenDataSet(nm);
1432 H5::DataSpaceSharedPtr space = data->GetSpace();
1433 std::vector<hsize_t> dims = space->GetDims();
1434
1435 std::vector<std::string> comps;
1436 data->ReadVectorString(comps, space);
1437
1438 H5::DataSetSharedPtr mdata = m_maps->OpenDataSet(nm);
1439 H5::DataSpaceSharedPtr mspace = mdata->GetSpace();
1440 std::vector<hsize_t> mdims = mspace->GetDims();
1441
1442 std::vector<int> ids;
1443 mdata->Read(ids, mspace);
1444
1445 for (int i = 0; i < dims[0]; i++)
1446 {
1447 std::string compStr = comps[i];
1448
1449 char type;
1450 std::istringstream strm(compStr);
1451
1452 strm >> type;
1453
1454 std::string::size_type indxBeg = compStr.find_first_of('[') + 1;
1455 std::string::size_type indxEnd = compStr.find_last_of(']') - 1;
1456
1457 std::string indxStr = compStr.substr(indxBeg, indxEnd - indxBeg + 1);
1458 std::vector<unsigned int> seqVector;
1459 ParseUtils::GenerateSeqVector(indxStr, seqVector);
1460
1462
1463 switch (type)
1464 {
1465 case 'V':
1466 shapeType = LibUtilities::ePoint;
1467 break;
1468 case 'S':
1469 case 'E':
1470 shapeType = LibUtilities::eSegment;
1471 break;
1472 case 'Q':
1473 case 'F':
1474 // Note that for HDF5, the composite descriptor is only used
1475 // for partitioning purposes so 'F' tag is not really going
1476 // to be critical in this context.
1477 shapeType = LibUtilities::eQuadrilateral;
1478 break;
1479 case 'T':
1480 shapeType = LibUtilities::eTriangle;
1481 break;
1482 case 'A':
1483 shapeType = LibUtilities::eTetrahedron;
1484 break;
1485 case 'P':
1486 shapeType = LibUtilities::ePyramid;
1487 break;
1488 case 'R':
1489 shapeType = LibUtilities::ePrism;
1490 break;
1491 case 'H':
1492 shapeType = LibUtilities::eHexahedron;
1493 break;
1494 }
1495
1496 ASSERTL0(shapeType != eNoShapeType, "Invalid shape.");
1497
1498 std::vector<int> filteredVector;
1499 for (auto &compElmt : seqVector)
1500 {
1501 if (id2row.find(compElmt) == id2row.end())
1502 {
1503 continue;
1504 }
1505
1506 filteredVector.push_back(compElmt);
1507 }
1508
1509 if (filteredVector.size() == 0)
1510 {
1511 continue;
1512 }
1513
1514 ret[ids[i]] = std::make_pair(shapeType, filteredVector);
1515 }
1516
1517 return ret;
1518}
1519
1520template <class T, typename std::enable_if<T::kDim == 0, int>::type = 0>
1521inline NekDouble GetGeomData(T *geom, int i)
1522{
1523 return (*geom)(i);
1524}
1525
1526template <class T, typename std::enable_if<T::kDim == 1, int>::type = 0>
1527inline int GetGeomData(T *geom, int i)
1528{
1529 return geom->GetVid(i);
1530}
1531
1532template <class T, typename std::enable_if<T::kDim == 2, int>::type = 0>
1533inline int GetGeomData(T *geom, int i)
1534{
1535 return geom->GetEid(i);
1536}
1537
1538template <class T, typename std::enable_if<T::kDim == 3, int>::type = 0>
1539inline int GetGeomData(T *geom, int i)
1540{
1541 return geom->GetFid(i);
1542}
1543
1544template <class T>
1546 std::string datasetName)
1547{
1548 typedef typename std::conditional<std::is_same_v<T, PointGeom>, NekDouble,
1549 int>::type DataType;
1550
1551 const int nGeomData = GetGeomDataDim(geomMap);
1552 const size_t nGeom = geomMap.size();
1553
1554 if (nGeom == 0)
1555 {
1556 return;
1557 }
1558
1559 // Construct a map storing IDs
1560 std::vector<int> idMap(nGeom);
1561 std::vector<DataType> data(nGeom * nGeomData);
1562
1563 int cnt1 = 0, cnt2 = 0;
1564 for (auto [id, geom] : geomMap)
1565 {
1566 idMap[cnt1++] = id;
1567
1568 for (int j = 0; j < nGeomData; ++j)
1569 {
1570 data[cnt2 + j] = GetGeomData(geom, j);
1571 }
1572
1573 cnt2 += nGeomData;
1574 }
1575
1576 std::vector<hsize_t> dims = {static_cast<hsize_t>(nGeom),
1577 static_cast<hsize_t>(nGeomData)};
1580 std::shared_ptr<H5::DataSpace>(new H5::DataSpace(dims));
1581 H5::DataSetSharedPtr dst = m_mesh->CreateDataSet(datasetName, tp, ds);
1582 dst->Write(data, ds);
1583
1584 tp = H5::DataType::OfObject(idMap[0]);
1585 dims = {nGeom};
1586 ds = std::shared_ptr<H5::DataSpace>(new H5::DataSpace(dims));
1587 dst = m_maps->CreateDataSet(datasetName, tp, ds);
1588 dst->Write(idMap, ds);
1589}
1590
1591void MeshGraphIOHDF5::WriteCurveMap(CurveMap &curves, std::string dsName,
1592 MeshCurvedPts &curvedPts, int &ptOffset,
1593 int &newIdx)
1594{
1595 std::vector<int> data, map;
1596
1597 // Compile curve data.
1598 for (auto &c : curves)
1599 {
1600 map.push_back(c.first);
1601 data.push_back(c.second->m_points.size());
1602 data.push_back(c.second->m_ptype);
1603 data.push_back(ptOffset);
1604
1605 ptOffset += c.second->m_points.size();
1606
1607 for (auto &pt : c.second->m_points)
1608 {
1609 MeshVertex v;
1610 v.id = newIdx;
1611 pt->GetCoords(v.x, v.y, v.z);
1612 curvedPts.pts.push_back(v);
1613 curvedPts.index.push_back(newIdx++);
1614 }
1615 }
1616
1617 // Write data.
1618 std::vector<hsize_t> dims = {data.size() / 3, 3};
1621 std::shared_ptr<H5::DataSpace>(new H5::DataSpace(dims));
1622 H5::DataSetSharedPtr dst = m_mesh->CreateDataSet(dsName, tp, ds);
1623 dst->Write(data, ds);
1624
1625 tp = H5::DataType::OfObject(map[0]);
1626 dims = {map.size()};
1627 ds = std::shared_ptr<H5::DataSpace>(new H5::DataSpace(dims));
1628 dst = m_maps->CreateDataSet(dsName, tp, ds);
1629 dst->Write(map, ds);
1630}
1631
1633{
1634 std::vector<double> vertData(curvedPts.pts.size() * 3);
1635
1636 int cnt = 0;
1637 for (auto &pt : curvedPts.pts)
1638 {
1639 vertData[cnt++] = pt.x;
1640 vertData[cnt++] = pt.y;
1641 vertData[cnt++] = pt.z;
1642 }
1643
1644 std::vector<hsize_t> dims = {curvedPts.pts.size(), 3};
1647 std::shared_ptr<H5::DataSpace>(new H5::DataSpace(dims));
1648 H5::DataSetSharedPtr dst = m_mesh->CreateDataSet("CURVE_NODES", tp, ds);
1649 dst->Write(vertData, ds);
1650}
1651
1653{
1654 std::vector<std::string> comps;
1655
1656 // dont need location map only a id map
1657 // will filter the composites per parition on read, its easier
1658 // composites do not need to be written in paralell.
1659 std::vector<int> c_map;
1660
1661 for (auto &cIt : composites)
1662 {
1663 if (cIt.second->m_geomVec.size() == 0)
1664 {
1665 continue;
1666 }
1667
1668 comps.push_back(GetCompositeString(cIt.second));
1669 c_map.push_back(cIt.first);
1670 }
1671
1674 H5::DataSetSharedPtr dst = m_mesh->CreateDataSet("COMPOSITE", tp, ds);
1675 dst->WriteVectorString(comps, ds, tp);
1676
1677 tp = H5::DataType::OfObject(c_map[0]);
1678 ds = H5::DataSpace::OneD(c_map.size());
1679 dst = m_maps->CreateDataSet("COMPOSITE", tp, ds);
1680 dst->Write(c_map, ds);
1681}
1682
1683void MeshGraphIOHDF5::WriteDomain(std::map<int, CompositeMap> &domain)
1684{
1685 // dont need location map only a id map
1686 // will filter the composites per parition on read, its easier
1687 // composites do not need to be written in paralell.
1688 std::vector<int> d_map;
1689 std::vector<std::vector<unsigned int>> idxList;
1690
1691 int cnt = 0;
1692 for (auto &dIt : domain)
1693 {
1694 idxList.push_back(std::vector<unsigned int>());
1695 for (auto cIt = dIt.second.begin(); cIt != dIt.second.end(); ++cIt)
1696 {
1697 idxList[cnt].push_back(cIt->first);
1698 }
1699
1700 ++cnt;
1701 d_map.push_back(dIt.first);
1702 }
1703
1704 std::stringstream domString;
1705 std::vector<std::string> doms;
1706 for (auto &cIt : idxList)
1707 {
1708 doms.push_back(ParseUtils::GenerateSeqString(cIt));
1709 }
1710
1713 H5::DataSetSharedPtr dst = m_mesh->CreateDataSet("DOMAIN", tp, ds);
1714 dst->WriteVectorString(doms, ds, tp);
1715
1716 tp = H5::DataType::OfObject(d_map[0]);
1717 ds = H5::DataSpace::OneD(d_map.size());
1718 dst = m_maps->CreateDataSet("DOMAIN", tp, ds);
1719 dst->Write(d_map, ds);
1720}
1721
1723 const std::string &outfilename, bool defaultExp,
1724 [[maybe_unused]] const LibUtilities::FieldMetaDataMap &metadata)
1725{
1726 auto &vertSet = m_meshGraph->GetGeomMap<PointGeom>();
1727 auto &segGeoms = m_meshGraph->GetGeomMap<SegGeom>();
1728 auto &triGeoms = m_meshGraph->GetGeomMap<TriGeom>();
1729 auto &quadGeoms = m_meshGraph->GetGeomMap<QuadGeom>();
1730 auto &hexGeoms = m_meshGraph->GetGeomMap<HexGeom>();
1731 auto &pyrGeoms = m_meshGraph->GetGeomMap<PyrGeom>();
1732 auto &prismGeoms = m_meshGraph->GetGeomMap<PrismGeom>();
1733 auto &tetGeoms = m_meshGraph->GetGeomMap<TetGeom>();
1734 CurveMap &curvedEdges = m_meshGraph->GetCurvedEdges();
1735 CurveMap &curvedFaces = m_meshGraph->GetCurvedFaces();
1736 CompositeMap &meshComposites = m_meshGraph->GetComposites();
1737 auto domain = m_meshGraph->GetDomain();
1738
1739 std::vector<std::string> tmp;
1740 boost::split(tmp, outfilename, boost::is_any_of("."));
1741 std::string filenameXml = tmp[0] + ".xml";
1742 std::string filenameHdf5 = tmp[0] + ".nekg";
1743
1744 //////////////////
1745 // XML part
1746 //////////////////
1747
1748 // Check to see if a xml of the same name exists
1749 // if might have boundary conditions etc, we will just alter the
1750 // geometry tag if needed
1751 TiXmlDocument *doc = new TiXmlDocument;
1752 TiXmlElement *root;
1753 TiXmlElement *geomTag;
1754
1755 if (fs::exists(filenameXml.c_str()))
1756 {
1757 std::ifstream file(filenameXml.c_str());
1758 file >> (*doc);
1759 TiXmlHandle docHandle(doc);
1760 root = docHandle.FirstChildElement("NEKTAR").Element();
1761 ASSERTL0(root, "Unable to find NEKTAR tag in file.");
1762 geomTag = root->FirstChildElement("GEOMETRY");
1763 defaultExp = false;
1764 }
1765 else
1766 {
1767 TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
1768 doc->LinkEndChild(decl);
1769 root = new TiXmlElement("NEKTAR");
1770 doc->LinkEndChild(root);
1771
1772 geomTag = new TiXmlElement("GEOMETRY");
1773 root->LinkEndChild(geomTag);
1774 }
1775
1776 // Update attributes with dimensions.
1777 geomTag->SetAttribute("DIM", m_meshGraph->GetMeshDimension());
1778 geomTag->SetAttribute("SPACE", m_meshGraph->GetSpaceDimension());
1779 geomTag->SetAttribute("HDF5FILE", filenameHdf5);
1780
1781 geomTag->Clear();
1782
1783 if (defaultExp)
1784 {
1785 TiXmlElement *expTag = new TiXmlElement("EXPANSIONS");
1786
1787 for (auto it = meshComposites.begin(); it != meshComposites.end(); it++)
1788 {
1789 if (it->second->m_geomVec[0]->GetShapeDim() ==
1790 m_meshGraph->GetMeshDimension())
1791 {
1792 TiXmlElement *exp = new TiXmlElement("E");
1793 exp->SetAttribute("COMPOSITE",
1794 "C[" + std::to_string(it->first) + "]");
1795 exp->SetAttribute("NUMMODES", 4);
1796 exp->SetAttribute("TYPE", "MODIFIED");
1797 exp->SetAttribute("FIELDS", "u");
1798
1799 expTag->LinkEndChild(exp);
1800 }
1801 }
1802 root->LinkEndChild(expTag);
1803 }
1804
1805 auto movement = m_meshGraph->GetMovement();
1806 if (movement)
1807 {
1808 movement->WriteMovement(root);
1809 }
1810
1811 doc->SaveFile(filenameXml);
1812
1813 //////////////////
1814 // HDF5 part
1815 //////////////////
1816
1817 // This is serial IO so we will just override any existing file.
1818 m_file = H5::File::Create(filenameHdf5, H5F_ACC_TRUNC);
1819 auto hdfRoot = m_file->CreateGroup("NEKTAR");
1820 auto hdfRoot2 = hdfRoot->CreateGroup("GEOMETRY");
1821
1822 // Write format version.
1823 hdfRoot2->SetAttribute("FORMAT_VERSION", FORMAT_VERSION);
1824
1825 // Create main groups.
1826 m_mesh = hdfRoot2->CreateGroup("MESH");
1827 m_maps = hdfRoot2->CreateGroup("MAPS");
1828
1829 WriteGeometryMap(vertSet, "VERT");
1830 WriteGeometryMap(segGeoms, "SEG");
1831 if (m_meshGraph->GetMeshDimension() > 1)
1832 {
1833 WriteGeometryMap(triGeoms, "TRI");
1834 WriteGeometryMap(quadGeoms, "QUAD");
1835 }
1836 if (m_meshGraph->GetMeshDimension() > 2)
1837 {
1838 WriteGeometryMap(tetGeoms, "TET");
1839 WriteGeometryMap(pyrGeoms, "PYR");
1840 WriteGeometryMap(prismGeoms, "PRISM");
1841 WriteGeometryMap(hexGeoms, "HEX");
1842 }
1843
1844 // Write curves
1845 int ptOffset = 0, newIdx = 0;
1846 MeshCurvedPts curvePts;
1847 WriteCurveMap(curvedEdges, "CURVE_EDGE", curvePts, ptOffset, newIdx);
1848 WriteCurveMap(curvedFaces, "CURVE_FACE", curvePts, ptOffset, newIdx);
1849 WriteCurvePoints(curvePts);
1850
1851 // Write composites and domain.
1852 WriteComposites(meshComposites);
1853 WriteDomain(domain);
1854}
1855
1856} // namespace Nektar::SpatialDomains
#define ASSERTL0(condition, msg)
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
#define TIME_RESULT(verb, msg, timer)
HDF5 DataSpace wrapper.
Definition H5.h:313
static DataSpaceSharedPtr OneD(hsize_t size)
Definition H5.cpp:411
static DataTypeSharedPtr OfObject(const T &obj)
Definition H5.h:404
static DataTypeSharedPtr String(size_t len=0)
Definition H5.cpp:517
static FileSharedPtr Open(const std::string &filename, unsigned mode, PListSharedPtr accessPL=PList::Default())
Definition H5.cpp:647
static FileSharedPtr Create(const std::string &filename, unsigned mode, PListSharedPtr createPL=PList::Default(), PListSharedPtr accessPL=PList::Default())
Definition H5.cpp:638
static PListSharedPtr DatasetXfer()
Properties for raw data transfer.
Definition H5.cpp:109
static PListSharedPtr FileAccess()
Properties for file access.
Definition H5.cpp:91
static PListSharedPtr Default()
Default options.
Definition H5.cpp:73
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
bool ModuleExists(tKey idKey)
Checks if a particular module is available.
static void GetXMLElementTimeLevel(TiXmlElement *&element, const size_t timeLevel, const bool enableCheck=true)
Get XML elment time level (Parallel-in-Time)
static std::string RegisterCmdLineFlag(const std::string &pName, const std::string &pShortName, const std::string &pDescription)
Registers a command-line flag with the session reader.
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
Generic object pool allocator/deallocator.
static std::unique_ptr< DataType, UniquePtrDeleter > AllocateUniquePtr(const Args &...args)
static std::string GenerateSeqString(const std::vector< T > &v)
Generate a compressed comma-separated string representation of a vector of unsigned integers.
Definition ParseUtils.h:72
static bool GenerateSeqVector(const std::string &str, std::vector< unsigned int > &out)
Takes a comma-separated compressed string and converts it to entries in a vector.
static const int kNfaces
Definition HexGeom.h:55
void FillGeomMap(GeomMapView< T > &geomMap, const CurveMap &curveMap, std::vector< int > &ids, std::vector< DataType > &geomData)
void v_PartitionMesh(LibUtilities::SessionReaderSharedPtr session) final
Partition the mesh.
void SetupCompositeRange(LibUtilities::DomainRangeShPtr &rng)
LibUtilities::H5::GroupSharedPtr m_mesh
LibUtilities::H5::GroupSharedPtr m_maps
void v_ReadGeometry(bool fillGraph) final
void WriteDomain(std::map< int, CompositeMap > &domain)
static const unsigned int FORMAT_VERSION
Version of the Nektar++ HDF5 geometry format, which is embedded into the main NEKTAR/GEOMETRY group a...
void WriteCurveMap(CurveMap &curves, std::string dsName, MeshCurvedPts &curvedPts, int &ptOffset, int &newIdx)
void v_WriteGeometry(const std::string &outfilename, bool defaultExp=false, const LibUtilities::FieldMetaDataMap &metadata=LibUtilities::NullFieldMetaDataMap) final
void WriteCurvePoints(MeshCurvedPts &curvedPts)
LibUtilities::H5::FileSharedPtr m_file
void ReadCurveMap(CurveMap &curveMap, std::string dsName, const std::unordered_set< int > &readIds)
void ReadGeometryData(GeomMapView< T > &geomMap, std::string dataSet, const std::unordered_set< int > &readIds, std::vector< int > &ids, std::vector< DataType > &geomData)
void ConstructGeomObject(GeomMapView< T > &geomMap, int id, DataType *data, Curve *curve)
void WriteGeometryMap(GeomMapView< T > &geomMap, std::string datasetName)
LibUtilities::H5::PListSharedPtr m_readPL
static MeshGraphIOSharedPtr create()
LibUtilities::SessionReaderSharedPtr m_session
Definition MeshGraphIO.h:88
std::string GetCompositeString(CompositeSharedPtr comp)
Returns a string representation of a composite.
CompositeDescriptor CreateCompositeDescriptor()
static const int kNfaces
Definition PyrGeom.h:55
static const int kNfaces
Definition TetGeom.h:55
std::shared_ptr< DataSpace > DataSpaceSharedPtr
Definition H5.h:78
std::shared_ptr< PList > PListSharedPtr
Definition H5.h:92
std::shared_ptr< File > FileSharedPtr
Definition H5.h:88
std::shared_ptr< DataType > DataTypeSharedPtr
Definition H5.h:74
std::shared_ptr< Group > GroupSharedPtr
Definition FieldIOHdf5.h:48
std::shared_ptr< DataSet > DataSetSharedPtr
Definition H5.h:90
std::map< std::string, std::string > FieldMetaDataMap
Definition FieldIO.h:50
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::shared_ptr< DomainRange > DomainRangeShPtr
Definition DomainRange.h:69
static DomainRangeShPtr NullDomainRangeShPtr
Definition DomainRange.h:70
CommFactory & GetCommFactory()
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition Comm.h:55
unique_ptr_objpool< Curve > CurveUniquePtr
Definition Geometry.h:70
NekDouble GetGeomData(T *geom, int i)
std::map< int, CurveUniquePtr > CurveMap
Definition Geometry.h:71
std::map< int, std::pair< LibUtilities::ShapeType, std::vector< int > > > CompositeDescriptor
Definition MeshGraph.h:131
MeshPartitionFactory & GetMeshPartitionFactory()
std::pair< size_t, size_t > SplitWork(size_t vecsize, int rank, int nprocs)
Utility function to split a vector equally amongst a number of processors.
std::shared_ptr< Composite > CompositeSharedPtr
Definition MeshGraph.h:185
MeshGraphIOFactory & GetMeshGraphIOFactory()
int GetGeomDataDim(GeomMapView< T > &geomMap)
std::shared_ptr< MeshPartition > MeshPartitionSharedPtr
void UniqueValues(std::unordered_set< int > &unique)
std::map< int, CompositeSharedPtr > CompositeMap
Definition MeshGraph.h:186
std::vector< PointGeom * > m_points
Points along the curve.
Definition Curve.hpp:53
std::vector< int64_t > index
Mapping to access the pts value. Given a 'ptoffset' value the npoints subsquent values provide the in...
std::vector< MeshVertex > pts
mapping to access pts value.
std::vector< unsigned int > list