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