Nektar++
FieldIOXml.cpp
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////////////
2//
3// File: FieldIOXml.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: I/O routines relating to Fields into XML
32//
33////////////////////////////////////////////////////////////////////////////////
34
38
39#include <boost/format.hpp>
40
41#ifdef NEKTAR_USE_MPI
42#include <mpi.h>
43#endif
44
46{
47
49 "Xml", FieldIOXml::create, "XML-based output of field data.");
50
51/**
52 * @brief Default constructor.
53 *
54 * @param pComm Communicator.
55 * @param sharedFilesystem True if the underlying filesystem is shared by the
56 * compute nodes.
57 */
59 : FieldIO(pComm, sharedFilesystem)
60{
61}
62
63/**
64 * @brief Write an XML file to @p outFile given the field definitions @p
65 * fielddefs, field data @p fielddata and metadata @p fieldmetadatamap.
66 *
67 * The writing strategy is as follows:
68 *
69 * - Use FieldIO::SetUpOutput to construct the directory to contain each
70 * partition.
71 * - The root processor writes an `Info.xml` file containing the field
72 * metadata and an index that describes which elements lie in which XML
73 * file.
74 * - Each processor then writes an XML file containing the field definitions
75 * for that processor and output data in base64-encoded zlib-compressed
76 * format.
77 *
78 * @param outFile Output filename.
79 * @param fielddefs Input field definitions.
80 * @param fielddata Input field data.
81 * @param fieldmetadatamap Field metadata.
82 */
83void FieldIOXml::v_Write(const std::string &outFile,
84 std::vector<FieldDefinitionsSharedPtr> &fielddefs,
85 std::vector<std::vector<NekDouble>> &fielddata,
86 const FieldMetaDataMap &fieldmetadatamap,
87 const bool backup)
88{
89 double tm0 = 0.0, tm1 = 0.0;
90 if (m_comm->TreatAsRankZero())
91 {
92 tm0 = m_comm->Wtime();
93 }
94
95 // Check everything seems sensible
96 ASSERTL1(fielddefs.size() == fielddata.size(),
97 "Length of fielddefs and fielddata incompatible");
98 for (int f = 0; f < fielddefs.size(); ++f)
99 {
100 ASSERTL1(fielddata[f].size() > 0,
101 "Fielddata vector must contain at least one value.");
102
103 ASSERTL1(fielddata[f].size() == fielddefs[f]->m_fields.size() *
104 CheckFieldDefinition(fielddefs[f]),
105 "Invalid size of fielddata vector.");
106 }
107
108 // Prepare to write out data. In parallel, we must create directory and
109 // determine the full pathname to the file to write out. Any existing
110 // file/directory which is in the way is removed.
111 std::string filename = SetUpOutput(outFile, true, backup);
112 SetUpFieldMetaData(outFile, fielddefs, fieldmetadatamap);
113
114 // Create the file (partition)
115 TiXmlDocument doc;
116 TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
117 doc.LinkEndChild(decl);
118
119 TiXmlElement *root = new TiXmlElement("NEKTAR");
120 doc.LinkEndChild(root);
121
122 AddInfoTag(XmlTagWriterSharedPtr(new XmlTagWriter(root)), fieldmetadatamap);
123
124 for (int f = 0; f < fielddefs.size(); ++f)
125 {
126 //---------------------------------------------
127 // Write ELEMENTS
128 TiXmlElement *elemTag = new TiXmlElement("ELEMENTS");
129 root->LinkEndChild(elemTag);
130
131 // Write FIELDS
132 std::string fieldsString;
133 {
134 std::stringstream fieldsStringStream;
135 bool first = true;
136 for (size_t i = 0; i < fielddefs[f]->m_fields.size(); i++)
137 {
138 if (!first)
139 {
140 fieldsStringStream << ",";
141 }
142 fieldsStringStream << fielddefs[f]->m_fields[i];
143 first = false;
144 }
145 fieldsString = fieldsStringStream.str();
146 }
147 elemTag->SetAttribute("FIELDS", fieldsString);
148
149 // Write SHAPE
150 std::string shapeString;
151 {
152 std::stringstream shapeStringStream;
153 shapeStringStream << ShapeTypeMap[fielddefs[f]->m_shapeType];
154 if (fielddefs[f]->m_numHomogeneousDir == 1)
155 {
156 shapeStringStream << "-HomogenousExp1D";
157 }
158 else if (fielddefs[f]->m_numHomogeneousDir == 2)
159 {
160 shapeStringStream << "-HomogenousExp2D";
161 }
162
163 if (fielddefs[f]->m_homoStrips)
164 {
165 shapeStringStream << "-Strips";
166 }
167
168 shapeString = shapeStringStream.str();
169 }
170 elemTag->SetAttribute("SHAPE", shapeString);
171
172 // Write BASIS
173 std::string basisString;
174 {
175 std::stringstream basisStringStream;
176 bool first = true;
177 for (size_t i = 0; i < fielddefs[f]->m_basis.size(); i++)
178 {
179 if (!first)
180 {
181 basisStringStream << ",";
182 }
183 basisStringStream << BasisTypeMap[fielddefs[f]->m_basis[i]];
184 first = false;
185 }
186 basisString = basisStringStream.str();
187 }
188 elemTag->SetAttribute("BASIS", basisString);
189
190 // Write homogeneuous length details
191 if (fielddefs[f]->m_numHomogeneousDir)
192 {
193 std::string homoLenString;
194 {
195 std::stringstream homoLenStringStream;
196 bool first = true;
197 for (int i = 0; i < fielddefs[f]->m_numHomogeneousDir; ++i)
198 {
199 if (!first)
200 {
201 homoLenStringStream << ",";
202 }
203 homoLenStringStream
204 << fielddefs[f]->m_homogeneousLengths[i];
205 first = false;
206 }
207 homoLenString = homoLenStringStream.str();
208 }
209 elemTag->SetAttribute("HOMOGENEOUSLENGTHS", homoLenString);
210 }
211
212 // Write homogeneuous planes/lines details
213 if (fielddefs[f]->m_numHomogeneousDir)
214 {
215 if (fielddefs[f]->m_homogeneousYIDs.size() > 0)
216 {
217 std::string homoYIDsString;
218 {
219 std::stringstream homoYIDsStringStream;
220 bool first = true;
221 for (int i = 0; i < fielddefs[f]->m_homogeneousYIDs.size();
222 i++)
223 {
224 if (!first)
225 {
226 homoYIDsStringStream << ",";
227 }
228 homoYIDsStringStream
229 << fielddefs[f]->m_homogeneousYIDs[i];
230 first = false;
231 }
232 homoYIDsString = homoYIDsStringStream.str();
233 }
234 elemTag->SetAttribute("HOMOGENEOUSYIDS", homoYIDsString);
235 }
236
237 if (fielddefs[f]->m_homogeneousZIDs.size() > 0)
238 {
239 std::string homoZIDsString;
240 {
241 std::stringstream homoZIDsStringStream;
242 bool first = true;
243 for (int i = 0; i < fielddefs[f]->m_homogeneousZIDs.size();
244 i++)
245 {
246 if (!first)
247 {
248 homoZIDsStringStream << ",";
249 }
250 homoZIDsStringStream
251 << fielddefs[f]->m_homogeneousZIDs[i];
252 first = false;
253 }
254 homoZIDsString = homoZIDsStringStream.str();
255 }
256 elemTag->SetAttribute("HOMOGENEOUSZIDS", homoZIDsString);
257 }
258
259 if (fielddefs[f]->m_homogeneousSIDs.size() > 0)
260 {
261 std::string homoSIDsString;
262 {
263 std::stringstream homoSIDsStringStream;
264 bool first = true;
265 for (int i = 0; i < fielddefs[f]->m_homogeneousSIDs.size();
266 i++)
267 {
268 if (!first)
269 {
270 homoSIDsStringStream << ",";
271 }
272 homoSIDsStringStream
273 << fielddefs[f]->m_homogeneousSIDs[i];
274 first = false;
275 }
276 homoSIDsString = homoSIDsStringStream.str();
277 }
278 elemTag->SetAttribute("HOMOGENEOUSSIDS", homoSIDsString);
279 }
280 }
281
282 // Write NUMMODESPERDIR
283 std::string numModesString;
284 {
285 std::stringstream numModesStringStream;
286
287 if (fielddefs[f]->m_uniOrder)
288 {
289 numModesStringStream << "UNIORDER:";
290 // Just dump single definition
291 bool first = true;
292 for (size_t i = 0; i < fielddefs[f]->m_basis.size(); i++)
293 {
294 if (!first)
295 {
296 numModesStringStream << ",";
297 }
298 numModesStringStream << fielddefs[f]->m_numModes[i];
299 first = false;
300 }
301 }
302 else
303 {
304 numModesStringStream << "MIXORDER:";
305 bool first = true;
306 for (size_t i = 0; i < fielddefs[f]->m_numModes.size(); i++)
307 {
308 if (!first)
309 {
310 numModesStringStream << ",";
311 }
312 numModesStringStream << fielddefs[f]->m_numModes[i];
313 first = false;
314 }
315 }
316
317 numModesString = numModesStringStream.str();
318 }
319 elemTag->SetAttribute("NUMMODESPERDIR", numModesString);
320
321 // Write ID
322 // Should ideally look at ways of compressing this stream
323 // if just sequential;
324 std::string idString;
325 {
326 std::stringstream idStringStream;
327 idString =
328 ParseUtils::GenerateSeqString(fielddefs[f]->m_elementIDs);
329 }
330 elemTag->SetAttribute("ID", idString);
331 elemTag->SetAttribute("COMPRESSED",
333
334 // Add this information for future compatibility
335 // issues, for exmaple in case we end up using a 128
336 // bit machine.
337 elemTag->SetAttribute("BITSIZE",
339
340 // Write field data.
341 std::string base64string;
343 base64string),
344 "Failed to compress field data.");
345
346 elemTag->LinkEndChild(new TiXmlText(base64string));
347 }
348 doc.SaveFile(filename);
349
350 m_comm->GetSpaceComm()->Block();
351
352 // All data has been written
353 if (m_comm->TreatAsRankZero())
354 {
355 tm1 = m_comm->Wtime();
356 std::cout << " (" << tm1 - tm0 << "s, XML)" << std::endl;
357 }
358}
359
360/**
361 * @brief Write out a file containing element ID to partition mapping.
362 *
363 * This function writes out an XML file (usually called `Info.xml`) that
364 * contains the element IDs that are contained within each partition, as well as
365 * the field metadata map.
366 *
367 * @param outFile Output multi-field file name.
368 * @param fileNames List of partition filenames.
369 * @param elementList Vector of element IDs that lie on each process.
370 * @param fieldmetadatamap Field metadata map that is written into @p outFile.
371 */
373 const std::string &outFile, const std::vector<std::string> fileNames,
374 std::vector<std::vector<unsigned int>> &elementList,
375 const FieldMetaDataMap &fieldmetadatamap)
376{
377 TiXmlDocument doc;
378 TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
379 doc.LinkEndChild(decl);
380
381 ASSERTL0(fileNames.size() == elementList.size(),
382 "Outfile names and list of elements ids does not match");
383
384 TiXmlElement *root = new TiXmlElement("NEKTAR");
385 doc.LinkEndChild(root);
386
387 AddInfoTag(XmlTagWriterSharedPtr(new XmlTagWriter(root)), fieldmetadatamap);
388
389 for (int t = 0; t < fileNames.size(); ++t)
390 {
391 if (elementList[t].size())
392 {
393 TiXmlElement *elemIDs = new TiXmlElement("Partition");
394 root->LinkEndChild(elemIDs);
395
396 elemIDs->SetAttribute("FileName", fileNames[t]);
397
398 std::string IDstr = ParseUtils::GenerateSeqString(elementList[t]);
399
400 elemIDs->LinkEndChild(new TiXmlText(IDstr));
401 }
402 }
403
404 doc.SaveFile(outFile);
405}
406
407/**
408 * @brief Read file containing element ID to partition mapping.
409 *
410 * This function reads an XML file (usually called `Info.xml`) that contains the
411 * element IDs that are contained within each partition, as well as the field
412 * metadata map.
413 *
414 * @param inFile Input multi-field file name.
415 * @param fileNames List of partition filenames.
416 * @param elementList Vector of element IDs that lie on each process.
417 * @param fieldmetadatamap Field metadata map that is read from @p inFile.
418 */
420 const std::string &inFile, std::vector<std::string> &fileNames,
421 std::vector<std::vector<unsigned int>> &elementList,
422 [[maybe_unused]] FieldMetaDataMap &fieldmetadatamap)
423{
424 TiXmlDocument doc(inFile);
425 bool loadOkay = doc.LoadFile();
426
427 std::stringstream errstr;
428 errstr << "Unable to load file: " << inFile << std::endl;
429 errstr << "Reason: " << doc.ErrorDesc() << std::endl;
430 errstr << "Position: Line " << doc.ErrorRow() << ", Column "
431 << doc.ErrorCol() << std::endl;
432 ASSERTL0(loadOkay, errstr.str());
433
434 // Handle on XML document
435 TiXmlHandle docHandle(&doc);
436
437 // Retrieve main NEKTAR tag - XML specification states one
438 // top-level element tag per file.
439 TiXmlElement *master = doc.FirstChildElement("NEKTAR");
440 ASSERTL0(master, "Unable to find NEKTAR tag in file.");
441
442 // Partition element tag name
443 std::string strPartition = "Partition";
444
445 // First attempt to get the first Partition element
446 TiXmlElement *fldfileIDs = master->FirstChildElement(strPartition.c_str());
447 if (!fldfileIDs)
448 {
449 // If this files try previous name
450 strPartition = "MultipleFldFiles";
451 fldfileIDs = master->FirstChildElement("MultipleFldFiles");
452 }
453 ASSERTL0(fldfileIDs, "Unable to find 'Partition' or 'MultipleFldFiles' tag "
454 "within nektar tag.");
455
456 while (fldfileIDs)
457 {
458 // Read file name of partition file
459 const char *attr = fldfileIDs->Attribute("FileName");
460 ASSERTL0(attr, "'FileName' not provided as an attribute of '" +
461 strPartition + "' tag.");
462 fileNames.push_back(std::string(attr));
463
464 const char *elementIDs = fldfileIDs->GetText();
465 ASSERTL0(elementIDs, "Element IDs not specified.");
466
467 std::string elementIDsStr(elementIDs);
468
469 std::vector<unsigned int> idvec;
470 ParseUtils::GenerateSeqVector(elementIDsStr, idvec);
471
472 elementList.push_back(idvec);
473
474 fldfileIDs = fldfileIDs->NextSiblingElement(strPartition.c_str());
475 }
476}
477
478/**
479 * @brief Import an XML format file.
480 *
481 * @param finfilename Input filename
482 * @param fielddefs Field definitions of resulting field
483 * @param fielddata Field data of resulting field
484 * @param fieldinfomap Field metadata of resulting field
485 * @param ElementIDs If specified, contains the list of element IDs on
486 * this rank. The resulting field definitions will only
487 * contain data for the element IDs specified in this
488 * array.
489 */
490void FieldIOXml::v_Import(const std::string &infilename,
491 std::vector<FieldDefinitionsSharedPtr> &fielddefs,
492 std::vector<std::vector<NekDouble>> &fielddata,
493 FieldMetaDataMap &fieldinfomap,
494 const Array<OneD, int> &ElementIDs)
495{
496 std::string infile = infilename;
497
498 fs::path pinfilename(infilename);
499
500 // Check to see whether infile is a directory and therefore read in parallel
501 // or serial.
502 if (fs::is_directory(pinfilename))
503 {
504 fs::path infofile("Info.xml");
505 fs::path fullpath = pinfilename / infofile;
506 infile = PortablePath(fullpath);
507
508 std::vector<std::string> filenames;
509 std::vector<std::vector<unsigned int>> elementIDs_OnPartitions;
510
511 ImportMultiFldFileIDs(infile, filenames, elementIDs_OnPartitions,
512 fieldinfomap);
513
514 // Load metadata
515 ImportFieldMetaData(infile, fieldinfomap);
516
517 if (ElementIDs == NullInt1DArray) // load all elements
518 {
519 for (int i = 0; i < filenames.size(); ++i)
520 {
521 fs::path pfilename(filenames[i]);
522 fullpath = pinfilename / pfilename;
523 std::string fname = PortablePath(fullpath);
524 DataSourceSharedPtr dataSource = XmlDataSource::create(fname);
525 ImportFieldDefs(dataSource, fielddefs, false);
526 if (fielddata != NullVectorNekDoubleVector)
527 {
528 ImportFieldData(dataSource, fielddefs, fielddata);
529 }
530 }
531 }
532 else // only load relevant elements from partitions
533 {
534 int i, j;
535 std::map<int, std::vector<int>> FileIDs;
536 std::set<int> LoadFile;
537
538 for (i = 0; i < elementIDs_OnPartitions.size(); ++i)
539 {
540 for (j = 0; j < elementIDs_OnPartitions[i].size(); ++j)
541 {
542 FileIDs[elementIDs_OnPartitions[i][j]].push_back(i);
543 }
544 }
545
546 for (i = 0; i < ElementIDs.size(); ++i)
547 {
548 auto it = FileIDs.find(ElementIDs[i]);
549 if (it != FileIDs.end())
550 {
551 for (j = 0; j < it->second.size(); ++j)
552 {
553 LoadFile.insert(it->second[j]);
554 }
555 }
556 }
557
558 for (auto &iter : LoadFile)
559 {
560 fs::path pfilename(filenames[iter]);
561 fullpath = pinfilename / pfilename;
562 std::string fname = PortablePath(fullpath);
563 DataSourceSharedPtr dataSource = XmlDataSource::create(fname);
564 ImportFieldDefs(dataSource, fielddefs, false);
565 if (fielddata != NullVectorNekDoubleVector)
566 {
567 ImportFieldData(dataSource, fielddefs, fielddata);
568 }
569 }
570 }
571 }
572 else
573 {
574 // Serial format case.
575 DataSourceSharedPtr doc = ImportFieldMetaData(infilename, fieldinfomap);
576 ImportFieldDefs(doc, fielddefs, false);
577 if (fielddata != NullVectorNekDoubleVector)
578 {
579 ImportFieldData(doc, fielddefs, fielddata);
580 }
581 }
582}
583
584/**
585 * @brief Import field metadata from @p filename and return the data source
586 * which wraps @p filename.
587 *
588 * @param filename Input filename.
589 * @param fieldmetadatamap Resulting field metadata from @p dataSource.
590 */
592 const std::string &filename, FieldMetaDataMap &fieldmetadatamap)
593{
595 XmlDataSourceSharedPtr xml = std::static_pointer_cast<XmlDataSource>(doc);
596 TiXmlElement *metadata = nullptr;
597 TiXmlElement *master = nullptr; // Master tag within which all data is
598 // contained.
599
600 master = xml->Get().FirstChildElement("NEKTAR");
601 ASSERTL0(master, "Unable to find NEKTAR tag in file.");
602 std::string strLoop = "NEKTAR";
603
604 metadata = master->FirstChildElement("Metadata");
605 if (metadata)
606 {
607 TiXmlElement *param = metadata->FirstChildElement();
608
609 while (param)
610 {
611 std::string paramString = param->Value();
612 if (paramString != "Provenance")
613 {
614 // Now read body of param
615 if (param->NoChildren())
616 {
617 fieldmetadatamap[paramString] = "";
618 }
619 else
620 {
621 TiXmlNode *paramBody = param->FirstChild();
622 std::string paramBodyStr = paramBody->ToText()->Value();
623 fieldmetadatamap[paramString] = paramBodyStr;
624 }
625 }
626 param = param->NextSiblingElement();
627 }
628 }
629
630 return doc;
631}
632
633/// Returns the class name.
634const std::string &FieldIOXml::v_GetClassName() const
635{
636 return className;
637}
638
639/**
640 * @brief Set up field meta data map.
641 *
642 * This routine sets up the necessary information for the field metadata map
643 * before calling FieldIOXml::WriteMultiFldFileIDs, which involves each process
644 * sending its element ID list to the root processor. The root processor writes
645 * the `Info.xml` file.
646 *
647 * @param outname Output directory.
648 * @param fielddefs Field definitions, needed to grab element IDs.
649 * @param fieldmetadatamap Field metadata map that is also written to the
650 * `Info.xml` file.
651 */
653 const std::string &outname,
654 const std::vector<FieldDefinitionsSharedPtr> &fielddefs,
655 const FieldMetaDataMap &fieldmetadatamap)
656{
657 ASSERTL0(!outname.empty(), "Empty path given to SetUpFieldMetaData()");
658
659 unsigned int nprocs = m_comm->GetSpaceComm()->GetSize();
660 unsigned int rank = m_comm->GetSpaceComm()->GetRank();
661
662 fs::path specPath(outname);
663
664 // Compute number of elements on this process and share with other
665 // processes. Also construct list of elements on this process from
666 // available vector of field definitions.
667 std::vector<size_t> elmtnums(nprocs, 0);
668 std::vector<unsigned int> idlist;
669 for (size_t i = 0; i < fielddefs.size(); ++i)
670 {
671 elmtnums[rank] += fielddefs[i]->m_elementIDs.size();
672 idlist.insert(idlist.end(), fielddefs[i]->m_elementIDs.begin(),
673 fielddefs[i]->m_elementIDs.end());
674 }
675 m_comm->GetSpaceComm()->AllReduce(elmtnums, LibUtilities::ReduceMax);
676
677 // Collate per-process element lists on root process to generate
678 // the info file.
679 if (rank == 0)
680 {
681 std::vector<std::vector<unsigned int>> ElementIDs(nprocs);
682
683 // Populate the list of element ID lists from all processes
684 ElementIDs[0] = idlist;
685 for (size_t i = 1; i < nprocs; ++i)
686 {
687 if (elmtnums[i] > 0)
688 {
689 std::vector<unsigned int> tmp(elmtnums[i]);
690 m_comm->GetSpaceComm()->Recv(i, tmp);
691 ElementIDs[i] = tmp;
692 }
693 }
694
695 // Set up output names
696 std::vector<std::string> filenames;
697 for (unsigned int i = 0; i < nprocs; ++i)
698 {
699 boost::format pad("P%1$07d.%2$s");
700 pad % i % GetFileEnding();
701 filenames.push_back(pad.str());
702 }
703
704 // Write the Info.xml file
705 std::string infofile =
706 LibUtilities::PortablePath(specPath / fs::path("Info.xml"));
707
708 WriteMultiFldFileIDs(infofile, filenames, ElementIDs, fieldmetadatamap);
709 }
710 else
711 {
712 // Send this process's ID list to the root process
713 if (elmtnums[rank] > 0)
714 {
715 m_comm->GetSpaceComm()->Send(0, idlist);
716 }
717 }
718}
719
720/**
721 * @brief Import field definitions from the target file.
722 *
723 * @param dataSource Target XML file
724 * @param fielddefs Output vector that will contain read field definitions.
725 * @param expChild Determines if the field definitions are defined by
726 * `<EXPANSIONS>` or in `<NEKTAR>`.
727 */
729 DataSourceSharedPtr dataSource,
730 std::vector<FieldDefinitionsSharedPtr> &fielddefs, bool expChild)
731{
733 std::static_pointer_cast<XmlDataSource>(dataSource);
734 TiXmlElement *master =
735 nullptr; // Master tag within which all data is contained.
736
737 master = xml->Get().FirstChildElement("NEKTAR");
738 ASSERTL0(master, "Unable to find NEKTAR tag in file.");
739 std::string strLoop = "NEKTAR";
740 TiXmlElement *loopXml = master;
741
742 TiXmlElement *expansionTypes;
743 if (expChild)
744 {
745 expansionTypes = master->FirstChildElement("EXPANSIONS");
746 ASSERTL0(expansionTypes, "Unable to find EXPANSIONS tag in file.");
747 loopXml = expansionTypes;
748 strLoop = "EXPANSIONS";
749 }
750
751 // Loop through all nektar tags, finding all of the element tags.
752 while (loopXml)
753 {
754 TiXmlElement *element = loopXml->FirstChildElement("ELEMENTS");
755
756 while (element)
757 {
758 // Extract the attributes.
759 std::string idString;
760 std::string shapeString;
761 std::string basisString;
762 std::string homoLengthsString;
763 std::string homoSIDsString;
764 std::string homoZIDsString;
765 std::string homoYIDsString;
766 std::string numModesString;
767 std::string numPointsString;
768 std::string fieldsString;
769 std::string pointsString;
770 bool pointDef = false;
771 bool numPointDef = false;
772 TiXmlAttribute *attr = element->FirstAttribute();
773 while (attr)
774 {
775 std::string attrName(attr->Name());
776 if (attrName == "FIELDS")
777 {
778 fieldsString.insert(0, attr->Value());
779 }
780 else if (attrName == "SHAPE")
781 {
782 shapeString.insert(0, attr->Value());
783 }
784 else if (attrName == "BASIS")
785 {
786 basisString.insert(0, attr->Value());
787 }
788 else if (attrName == "HOMOGENEOUSLENGTHS")
789 {
790 homoLengthsString.insert(0, attr->Value());
791 }
792 else if (attrName == "HOMOGENEOUSSIDS")
793 {
794 homoSIDsString.insert(0, attr->Value());
795 }
796 else if (attrName == "HOMOGENEOUSZIDS")
797 {
798 homoZIDsString.insert(0, attr->Value());
799 }
800 else if (attrName == "HOMOGENEOUSYIDS")
801 {
802 homoYIDsString.insert(0, attr->Value());
803 }
804 else if (attrName == "NUMMODESPERDIR")
805 {
806 numModesString.insert(0, attr->Value());
807 }
808 else if (attrName == "ID")
809 {
810 idString.insert(0, attr->Value());
811 }
812 else if (attrName == "POINTSTYPE")
813 {
814 pointsString.insert(0, attr->Value());
815 pointDef = true;
816 }
817 else if (attrName == "NUMPOINTSPERDIR")
818 {
819 numPointsString.insert(0, attr->Value());
820 numPointDef = true;
821 }
822 else if (attrName == "COMPRESSED")
823 {
824 WARNINGL0(boost::iequals(attr->Value(),
826 "Compressed formats do not "
827 "match. Expected: " +
829 " but got " + std::string(attr->Value()));
830 }
831 else if (attrName == "BITSIZE")
832 {
833 // This information is for future compatibility
834 // issues, for example in case we end up using a 128
835 // bit machine. Currently just do nothing.
836 }
837 else
838 {
839 std::string errstr("Unknown attribute: ");
840 errstr += attrName;
841 NEKERROR(ErrorUtil::ewarning, errstr.c_str());
842 }
843
844 // Get the next attribute.
845 attr = attr->Next();
846 }
847
848 // Check to see if using strips formulation
849 bool strips = false;
850 if (shapeString.find("Strips") != std::string::npos)
851 {
852 strips = true;
853 }
854
855 // Check to see if homogeneous expansion and if so
856 // strip down the shapeString definition
857 int numHomoDir = 0;
858 size_t loc;
859 //---> This finds the first location of 'n'!
860 if ((loc = shapeString.find_first_of("-")) != std::string::npos)
861 {
862 if (shapeString.find("Exp1D") != std::string::npos)
863 {
864 numHomoDir = 1;
865 }
866 else // HomogeneousExp1D
867 {
868 numHomoDir = 2;
869 }
870
871 shapeString.erase(loc, shapeString.length());
872 }
873
874 // Reconstruct the fielddefs.
875 std::vector<unsigned int> elementIds;
876 {
877 bool valid =
878 ParseUtils::GenerateSeqVector(idString, elementIds);
879 ASSERTL0(valid, "Unable to correctly parse the element ids.");
880 }
881
882 // Get the geometrical shape
883 ShapeType shape = (ShapeType)0;
884 bool valid = false;
885 for (unsigned int j = 0; j < SIZE_ShapeType; j++)
886 {
887 if (ShapeTypeMap[j] == shapeString)
888 {
889 shape = (ShapeType)j;
890 valid = true;
891 break;
892 }
893 }
894
895 ASSERTL0(valid,
896 std::string("Unable to correctly parse the shape type: ")
897 .append(shapeString)
898 .c_str());
899
900 // Get the basis
901 std::vector<std::string> basisStrings;
902 std::vector<BasisType> basis;
903 valid = ParseUtils::GenerateVector(basisString, basisStrings);
904 ASSERTL0(valid, "Unable to correctly parse the basis types.");
905 for (std::vector<std::string>::size_type i = 0;
906 i < basisStrings.size(); i++)
907 {
908 valid = false;
909 for (unsigned int j = 0; j < SIZE_BasisType; j++)
910 {
911 if (BasisTypeMap[j] == basisStrings[i])
912 {
913 basis.push_back((BasisType)j);
914 valid = true;
915 break;
916 }
917 }
918 ASSERTL0(
919 valid,
920 std::string("Unable to correctly parse the basis type: ")
921 .append(basisStrings[i])
922 .c_str());
923 }
924
925 // Get homoLengths
926 std::vector<NekDouble> homoLengths;
927 if (numHomoDir)
928 {
929 valid =
930 ParseUtils::GenerateVector(homoLengthsString, homoLengths);
931 ASSERTL0(valid, "Unable to correctly parse the number of "
932 "homogeneous lengths.");
933 }
934
935 // Get Homogeneous strips IDs
936 std::vector<unsigned int> homoSIDs;
937 if (strips)
938 {
939 valid = ParseUtils::GenerateVector(homoSIDsString, homoSIDs);
940 ASSERTL0(valid,
941 "Unable to correctly parse homogeneous strips IDs.");
942 }
943
944 // Get Homogeneous points IDs
945 std::vector<unsigned int> homoZIDs;
946 std::vector<unsigned int> homoYIDs;
947
948 if (numHomoDir == 1)
949 {
950 valid = ParseUtils::GenerateSeqVector(homoZIDsString, homoZIDs);
951 ASSERTL0(valid,
952 "Unable to correctly parse homogeneous planes IDs.");
953 }
954
955 if (numHomoDir == 2)
956 {
957 valid = ParseUtils::GenerateSeqVector(homoZIDsString, homoZIDs);
958 ASSERTL0(valid, "Unable to correctly parse homogeneous lines "
959 "IDs in z-direction.");
960 valid = ParseUtils::GenerateSeqVector(homoYIDsString, homoYIDs);
961 ASSERTL0(valid, "Unable to correctly parse homogeneous lines "
962 "IDs in y-direction.");
963 }
964
965 // Get points type
966 std::vector<PointsType> points;
967
968 if (pointDef)
969 {
970 std::vector<std::string> pointsStrings;
971 valid = ParseUtils::GenerateVector(pointsString, pointsStrings);
972 ASSERTL0(valid, "Unable to correctly parse the points types.");
973 for (std::vector<std::string>::size_type i = 0;
974 i < pointsStrings.size(); i++)
975 {
976 valid = false;
977 for (unsigned int j = 0; j < SIZE_PointsType; j++)
978 {
979 if (kPointsTypeStr[j] == pointsStrings[i])
980 {
981 points.push_back((PointsType)j);
982 valid = true;
983 break;
984 }
985 }
986
987 ASSERTL0(valid,
988 std::string(
989 "Unable to correctly parse the points type: ")
990 .append(pointsStrings[i])
991 .c_str());
992 }
993 }
994
995 // Get numModes
996 std::vector<unsigned int> numModes;
997 bool UniOrder = false;
998
999 if (strstr(numModesString.c_str(), "UNIORDER:"))
1000 {
1001 UniOrder = true;
1002 }
1003
1004 valid =
1005 ParseUtils::GenerateVector(numModesString.substr(9), numModes);
1006 ASSERTL0(valid, "Unable to correctly parse the number of modes.");
1007
1008 // Get numPoints
1009 std::vector<unsigned int> numPoints;
1010 if (numPointDef)
1011 {
1012 valid = ParseUtils::GenerateVector(numPointsString, numPoints);
1013 ASSERTL0(valid,
1014 "Unable to correctly parse the number of points.");
1015 }
1016
1017 // Get fields names
1018 std::vector<std::string> Fields;
1019 valid = ParseUtils::GenerateVector(fieldsString, Fields);
1020 ASSERTL0(valid, "Unable to correctly parse the number of fields.");
1021
1022 FieldDefinitionsSharedPtr fielddef =
1024 shape, elementIds, basis, UniOrder, numModes, Fields,
1025 numHomoDir, homoLengths, strips, homoSIDs, homoZIDs,
1026 homoYIDs, points, pointDef, numPoints, numPointDef);
1027
1028 fielddefs.push_back(fielddef);
1029
1030 element = element->NextSiblingElement("ELEMENTS");
1031 }
1032 loopXml = loopXml->NextSiblingElement(strLoop);
1033 }
1034}
1035
1036/**
1037 * @brief Import field data from a target file.
1038 *
1039 * @param dataSource Target XML file
1040 * @param fielddefs Field definitions for file
1041 * @param fielddata On return, contains field data for each field.
1042 */
1044 DataSourceSharedPtr dataSource,
1045 const std::vector<FieldDefinitionsSharedPtr> &fielddefs,
1046 std::vector<std::vector<NekDouble>> &fielddata)
1047{
1048 int cntdumps = 0;
1050 std::static_pointer_cast<XmlDataSource>(dataSource);
1051
1052 TiXmlElement *master =
1053 nullptr; // Master tag within which all data is contained.
1054
1055 master = xml->Get().FirstChildElement("NEKTAR");
1056 ASSERTL0(master, "Unable to find NEKTAR tag in file.");
1057
1058 // Loop through all nektar tags, finding all of the element tags.
1059 while (master)
1060 {
1061 TiXmlElement *element = master->FirstChildElement("ELEMENTS");
1062 ASSERTL0(element, "Unable to find ELEMENTS tag within nektar tag.");
1063 while (element)
1064 {
1065 // Extract the body, which is the "data".
1066 TiXmlNode *elementChild = element->FirstChild();
1067 ASSERTL0(elementChild,
1068 "Unable to extract the data from the element tag.");
1069 std::string elementStr;
1070 while (elementChild)
1071 {
1072 if (elementChild->Type() == TiXmlNode::TINYXML_TEXT)
1073 {
1074 elementStr += elementChild->ToText()->ValueStr();
1075 }
1076 elementChild = elementChild->NextSibling();
1077 }
1078
1079 std::vector<NekDouble> elementFieldData;
1080
1081 // Convert from base64 to binary.
1082 const char *CompressStr = element->Attribute("COMPRESSED");
1083 if (CompressStr)
1084 {
1085 WARNINGL0(boost::iequals(CompressStr,
1087 "Compressed formats do not match. "
1088 "Expected: " +
1089 CompressData::GetCompressString() + " but got " +
1090 std::string(CompressStr));
1091 }
1092
1094 elementStr, elementFieldData),
1095 "Failed to decompress field data.");
1096 fielddata.push_back(elementFieldData);
1097
1098 int datasize = CheckFieldDefinition(fielddefs[cntdumps]);
1099 ASSERTL0(
1100 fielddata[cntdumps].size() ==
1101 datasize * fielddefs[cntdumps]->m_fields.size(),
1102 "Input data is not the same length as header infoarmation");
1103
1104 cntdumps++;
1105
1106 element = element->NextSiblingElement("ELEMENTS");
1107 }
1108 master = master->NextSiblingElement("NEKTAR");
1109 }
1110}
1111} // namespace Nektar::LibUtilities
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:202
#define WARNINGL0(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:242
Class for operating on Nektar++ input/output files.
Definition: FieldIO.h:227
int CheckFieldDefinition(const FieldDefinitionsSharedPtr &fielddefs)
Check field definitions for correctness and return storage size.
Definition: FieldIO.cpp:690
DataSourceSharedPtr ImportFieldMetaData(const std::string &filename, FieldMetaDataMap &fieldmetadatamap)
Import the metadata from a field file.
Definition: FieldIO.h:371
std::string SetUpOutput(const std::string outname, bool perRank, bool backup=false)
Set up the filesystem ready for output.
Definition: FieldIO.cpp:404
LibUtilities::CommSharedPtr m_comm
Communicator to use when writing parallel format.
Definition: FieldIO.h:280
std::string GetFileEnding() const
Helper function that determines default file extension.
Definition: FieldIO.h:273
static void AddInfoTag(TagWriterSharedPtr root, const FieldMetaDataMap &fieldmetadatamap)
Add provenance information to the field metadata map.
Definition: FieldIO.cpp:342
void ImportMultiFldFileIDs(const std::string &inFile, std::vector< std::string > &fileNames, std::vector< std::vector< unsigned int > > &elementList, FieldMetaDataMap &fieldmetadatamap)
Read file containing element ID to partition mapping.
Definition: FieldIOXml.cpp:419
void ImportFieldData(DataSourceSharedPtr dataSource, const std::vector< FieldDefinitionsSharedPtr > &fielddefs, std::vector< std::vector< NekDouble > > &fielddata)
Import field data from a target file.
const std::string & v_GetClassName() const override
Returns the class name.
Definition: FieldIOXml.cpp:634
void ImportFieldDefs(DataSourceSharedPtr dataSource, std::vector< FieldDefinitionsSharedPtr > &fielddefs, bool expChild)
Import field definitions from the target file.
Definition: FieldIOXml.cpp:728
void WriteMultiFldFileIDs(const std::string &outfile, const std::vector< std::string > fileNames, std::vector< std::vector< unsigned int > > &elementList, const FieldMetaDataMap &fieldinfomap=NullFieldMetaDataMap)
Write out a file containing element ID to partition mapping.
Definition: FieldIOXml.cpp:372
static FieldIOSharedPtr create(LibUtilities::CommSharedPtr pComm, bool sharedFilesystem)
Creates an instance of this class.
Definition: FieldIOXml.h:208
DataSourceSharedPtr v_ImportFieldMetaData(const std::string &filename, FieldMetaDataMap &fieldmetadatamap) override
Import field metadata from filename and return the data source which wraps filename.
Definition: FieldIOXml.cpp:591
void v_Import(const std::string &infilename, std::vector< FieldDefinitionsSharedPtr > &fielddefs, std::vector< std::vector< NekDouble > > &fielddata=NullVectorNekDoubleVector, FieldMetaDataMap &fieldinfomap=NullFieldMetaDataMap, const Array< OneD, int > &ElementIDs=NullInt1DArray) override
Import an XML format file.
Definition: FieldIOXml.cpp:490
void v_Write(const std::string &outFile, std::vector< FieldDefinitionsSharedPtr > &fielddefs, std::vector< std::vector< NekDouble > > &fielddata, const FieldMetaDataMap &fieldinfomap=NullFieldMetaDataMap, const bool backup=false) override
Write an XML file to outFile given the field definitions fielddefs, field data fielddata and metadata...
Definition: FieldIOXml.cpp:83
FieldIOXml(LibUtilities::CommSharedPtr pComm, bool sharedFilesystem)
Default constructor.
Definition: FieldIOXml.cpp:58
void SetUpFieldMetaData(const std::string &outname, const std::vector< FieldDefinitionsSharedPtr > &fielddefs, const FieldMetaDataMap &fieldmetadatamap)
Set up field meta data map.
Definition: FieldIOXml.cpp:652
static std::string className
Name of class.
Definition: FieldIOXml.h:216
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:197
static DataSourceSharedPtr create(const std::string &fn)
Create a new XML data source based on the filename.
Definition: FieldIOXml.h:92
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 GenerateVector(const std::string &str, std::vector< T > &out)
Takes a comma-separated string and converts it to entries in a vector.
Definition: ParseUtils.cpp:130
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
int ZlibDecodeFromBase64Str(std::string &in64, std::vector< T > &out)
Definition: CompressData.h:218
int ZlibEncodeToBase64Str(std::vector< T > &in, std::string &out64)
Definition: CompressData.h:130
const char *const ShapeTypeMap[SIZE_ShapeType]
Definition: ShapeType.hpp:75
const char *const BasisTypeMap[]
Definition: Foundations.hpp:44
std::shared_ptr< DataSource > DataSourceSharedPtr
Definition: FieldIO.h:88
std::map< std::string, std::string > FieldMetaDataMap
Definition: FieldIO.h:50
const std::string kPointsTypeStr[]
Definition: Foundations.hpp:52
static std::string PortablePath(const fs::path &path)
create portable path on different platforms for std::filesystem path.
Definition: Filesystem.hpp:56
std::shared_ptr< XmlDataSource > XmlDataSourceSharedPtr
Definition: FieldIOXml.h:109
static std::vector< std::vector< NekDouble > > NullVectorNekDoubleVector
std::shared_ptr< FieldDefinitions > FieldDefinitionsSharedPtr
Definition: FieldIO.h:184
FieldIOFactory & GetFieldIOFactory()
Returns the FieldIO factory.
Definition: FieldIO.cpp:70
std::shared_ptr< XmlTagWriter > XmlTagWriterSharedPtr
Definition: FieldIOXml.h:166
@ SIZE_PointsType
Length of enum list.
Definition: PointsType.h:95
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:55
@ SIZE_BasisType
Length of enum list.
Definition: BasisType.h:70
static Array< OneD, int > NullInt1DArray