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