1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //  This software is distributed WITHOUT ANY WARRANTY; without even
6 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
7 //  PURPOSE.  See the above copyright notice for more information.
8 //
9 //============================================================================
10 
11 #include <fides/DataSetWriter.h>
12 #include <fides/predefined/DataModelFactory.h>
13 #include <fides/predefined/DataModelHelperFunctions.h>
14 #include <fides/predefined/InternalMetadataSource.h>
15 
16 #include <ios>
17 #include <stdexcept>
18 #include <string>
19 #include <vector>
20 
21 #include <fides_rapidjson.h>
22 // clang-format off
23 #include FIDES_RAPIDJSON(rapidjson/error/en.h)
24 #include FIDES_RAPIDJSON(rapidjson/filereadstream.h)
25 #include FIDES_RAPIDJSON(rapidjson/schema.h)
26 #include FIDES_RAPIDJSON(rapidjson/stringbuffer.h)
27 #include FIDES_RAPIDJSON(rapidjson/prettywriter.h)
28 // clang-format on
29 
30 #include <vtkm/cont/ArrayHandleCartesianProduct.h>
31 #include <vtkm/cont/CellSetExplicit.h>
32 #include <vtkm/cont/CellSetSingleType.h>
33 #include <vtkm/cont/CoordinateSystem.h>
34 #include <vtkm/cont/DataSet.h>
35 #include <vtkm/cont/DynamicCellSet.h>
36 
37 #include <fides/CellSet.h>
38 #include <fides/CoordinateSystem.h>
39 #include <fides/DataSource.h>
40 #include <fides/Field.h>
41 
42 #ifdef FIDES_USE_MPI
43 #include <mpi.h>
44 #endif
45 
46 template <typename T>
operator <<(std::ostream & out,const std::vector<T> & v)47 std::ostream& operator<<(std::ostream& out, const std::vector<T>& v)
48 {
49   if (!v.empty())
50   {
51     out << "[";
52     std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
53     out << "]";
54   }
55   return out;
56 }
57 
58 namespace fides
59 {
60 namespace io
61 {
62 
63 class DataSetWriter::GenericWriter
64 {
65 public:
GenericWriter(const vtkm::cont::PartitionedDataSet & dataSets,const std::string & fname,const std::string & outputMode,const bool & appendMode=false)66   GenericWriter(const vtkm::cont::PartitionedDataSet& dataSets,
67                 const std::string& fname,
68                 const std::string& outputMode,
69                 const bool& appendMode = false)
70     : DataSets(dataSets)
71     , OutputFileName(fname)
72 #ifdef FIDES_USE_MPI
73     , Adios(MPI_COMM_WORLD, adios2::DebugON)
74 #else
75     , Adios(true)
76 #endif
77     , FieldsToWriteSet(false)
78   {
79 #ifdef FIDES_USE_MPI
80     MPI_Comm_rank(MPI_COMM_WORLD, &this->Rank);
81     MPI_Comm_size(MPI_COMM_WORLD, &this->NumRanks);
82 #endif
83 
84     this->IO = this->Adios.DeclareIO(outputMode);
85     this->IO.SetEngine(outputMode);
86     this->Engine = this->IO.Open(this->OutputFileName,
87                                  (appendMode ? adios2::Mode::Append : adios2::Mode::Write));
88   }
89 
~GenericWriter()90   virtual ~GenericWriter() { this->Engine.Close(); }
91 
SetWriteFields(std::set<std::string> & writeFields)92   void SetWriteFields(std::set<std::string>& writeFields)
93   {
94     this->FieldsToWriteSet = true;
95     this->FieldsToWrite = writeFields;
96   }
97 
Write()98   void Write()
99   {
100     this->ComputeGlobalBlockInfo();
101 
102     bool firstTime = false;
103     if (!this->VariablesDefined)
104     {
105       this->DefineDataModelVariables();
106       this->DefineFieldVariables();
107       this->VariablesDefined = true;
108       firstTime = true;
109     }
110 
111     this->Engine.BeginStep(adios2::StepMode::Append);
112 
113     if (firstTime)
114     {
115       this->WriteSchema();
116     }
117 
118     this->WriteCoordinates();
119     this->WriteCells();
120     this->WriteFields();
121     this->Engine.PerformPuts();
122 
123     this->Engine.EndStep();
124   }
125 
126   virtual void WriteCoordinates() = 0;
127   virtual void WriteCells() = 0;
128   virtual void DefineDataModelVariables() = 0;
129 
WriteFields()130   virtual void WriteFields()
131   {
132     for (auto& var : this->PointCenteredFieldVars)
133     {
134       std::size_t ptsOffset = this->DataSetPointsOffset;
135       for (vtkm::Id i = 0; i < this->DataSets.GetNumberOfPartitions(); i++)
136       {
137         const auto& ds = this->DataSets.GetPartition(i);
138         std::size_t numPoints = static_cast<std::size_t>(ds.GetNumberOfPoints());
139 
140         if (!ds.HasPointField(var.Name()))
141         {
142           throw std::runtime_error("Variable " + var.Name() + " not in datasset.");
143         }
144         auto field = ds.GetField(var.Name());
145         std::size_t numComponents =
146           static_cast<std::size_t>(field.GetData().GetNumberOfComponents());
147 
148         if (numComponents == 1)
149         {
150           if (i == 0) //Only set the shape for first dataset.
151           {
152             var.SetShape({ this->TotalNumberOfPoints });
153           }
154 
155           adios2::Box<adios2::Dims> sel({ ptsOffset }, { numPoints });
156           var.SetSelection(sel);
157 
158           auto arr =
159             field.GetData().AsArrayHandle<vtkm::cont::ArrayHandleBasic<vtkm::FloatDefault>>();
160           const vtkm::FloatDefault* buff = arr.GetReadPointer();
161           this->Engine.Put<vtkm::FloatDefault>(var, buff);
162         }
163         else if (numComponents == 3)
164         {
165           if (i == 0) //Only set the shape for first dataset.
166           {
167             var.SetShape({ this->TotalNumberOfPoints, numComponents });
168           }
169 
170           adios2::Box<adios2::Dims> sel({ ptsOffset, 0 }, { numPoints, 3 });
171           var.SetSelection(sel);
172 
173           auto arr = field.GetData().AsArrayHandle<vtkm::cont::ArrayHandleBasic<vtkm::Vec3f>>();
174           const vtkm::Vec3f* buffVec = arr.GetReadPointer();
175           const vtkm::FloatDefault* buff = &buffVec[0][0];
176           this->Engine.Put<vtkm::FloatDefault>(var, buff);
177         }
178         else
179         {
180           throw std::runtime_error("Unsupported number of components in " + var.Name());
181         }
182 
183         ptsOffset += numPoints;
184       }
185     }
186 
187     for (auto& var : this->CellCenteredFieldVars)
188     {
189       std::size_t cellsOffset = this->DataSetCellsOffset;
190 
191       for (vtkm::Id i = 0; i < this->DataSets.GetNumberOfPartitions(); i++)
192       {
193         const auto& ds = this->DataSets.GetPartition(i);
194         std::size_t numCells = static_cast<std::size_t>(ds.GetCellSet().GetNumberOfCells());
195 
196         if (!ds.HasCellField(var.Name()))
197         {
198           throw std::runtime_error("Variable " + var.Name() + " not in datasset.");
199         }
200 
201         auto field = ds.GetField(var.Name());
202         std::size_t numComponents =
203           static_cast<std::size_t>(field.GetData().GetNumberOfComponents());
204 
205         if (numComponents == 1)
206         {
207           auto arr =
208             field.GetData().AsArrayHandle<vtkm::cont::ArrayHandleBasic<vtkm::FloatDefault>>();
209           const vtkm::FloatDefault* buff = arr.GetReadPointer();
210 
211           if (i == 0) //Only set the shape for first dataset.
212           {
213             var.SetShape({ this->TotalNumberOfCells });
214           }
215 
216           adios2::Box<adios2::Dims> sel({ cellsOffset }, { numCells });
217           var.SetSelection(sel);
218           this->Engine.Put<vtkm::FloatDefault>(var, buff);
219         }
220         else if (numComponents == 3)
221         {
222           if (i == 0) //Only set the shape for first dataset.
223           {
224             var.SetShape({ this->TotalNumberOfCells, 3 });
225           }
226 
227           adios2::Box<adios2::Dims> sel({ cellsOffset, 0 }, { numCells, 3 });
228           var.SetSelection(sel);
229 
230           auto arr = field.GetData().AsArrayHandle<vtkm::cont::ArrayHandleBasic<vtkm::Vec3f>>();
231           const vtkm::Vec3f* buffVec = arr.GetReadPointer();
232           const vtkm::FloatDefault* buff = &buffVec[0][0];
233           this->Engine.Put<vtkm::FloatDefault>(var, buff);
234         }
235         else
236         {
237           throw std::runtime_error("Unsupported number of components in " + var.Name());
238         }
239 
240         cellsOffset += numCells;
241       }
242     }
243   }
244 
WriteSchema()245   void WriteSchema()
246   {
247     int rankWithDS = 0;
248     for (int i = 0; i < this->NumRanks; i++)
249     {
250       if (this->DataSetsPerRank[static_cast<size_t>(i)] > 0)
251       {
252         rankWithDS = i;
253         break;
254       }
255     }
256 
257     if (this->Rank == rankWithDS)
258     {
259       std::set<std::string> varsToWrite;
260       auto ds = this->DataSets.GetPartition(0);
261       auto dm = fides::predefined::DataModelFactory::GetInstance().CreateDataModel(ds);
262 
263       if (this->FieldsToWriteSet)
264         dm->SetFieldsToWrite(this->FieldsToWrite);
265       auto& doc = dm->GetDOM(false);
266       auto attrMap = dm->GetAttributes();
267       rapidjson::StringBuffer buf;
268       rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buf);
269       doc.Accept(writer);
270       std::string schema = buf.GetString();
271       this->IO.DefineAttribute<std::string>("fides/schema", schema);
272       for (auto& attr : attrMap)
273       {
274         if (attr.second.size() == 1)
275         {
276           this->IO.DefineAttribute<std::string>(attr.first, attr.second[0]);
277         }
278         else
279         {
280           this->IO.DefineAttribute<std::string>(attr.first, attr.second.data(), attr.second.size());
281         }
282       }
283     }
284   }
285 
DefineFieldVariables()286   void DefineFieldVariables()
287   {
288     std::size_t numPoints = 0, numCells = 0;
289     vtkm::IdComponent numFields = 0;
290 
291     vtkm::cont::DataSet ds0;
292     if (this->DataSets.GetNumberOfPartitions() > 0)
293     {
294       ds0 = this->DataSets.GetPartition(0);
295       numFields = ds0.GetNumberOfFields();
296     }
297 
298     // Determine total number of points/cells.
299     for (vtkm::Id i = 0; i < this->DataSets.GetNumberOfPartitions(); i++)
300     {
301       const auto& ds = this->DataSets.GetPartition(i);
302       if (ds.GetNumberOfFields() != numFields)
303       {
304         throw std::runtime_error("DataSets with different number of fields not supported.");
305       }
306 
307       numPoints += static_cast<std::size_t>(ds.GetNumberOfPoints());
308       numCells += static_cast<std::size_t>(ds.GetCellSet().GetNumberOfCells());
309     }
310 
311     for (vtkm::Id i = 0; i < numFields; i++)
312     {
313       const auto& field = ds0.GetField(i);
314       const auto& name = field.GetName();
315       if (!this->ShouldWriteVariable(name))
316         continue;
317 
318       std::size_t numComponents = static_cast<std::size_t>(field.GetData().GetNumberOfComponents());
319       std::vector<std::size_t> shape, offset, size;
320 
321       if (field.GetAssociation() == vtkm::cont::Field::Association::POINTS)
322       {
323         if (numComponents == 1)
324         {
325           shape = { this->TotalNumberOfPoints };
326           offset = { this->DataSetPointsOffset };
327           size = { numPoints };
328         }
329         else
330         {
331           shape = { this->TotalNumberOfPoints, numComponents };
332           offset = { this->DataSetPointsOffset, 0 };
333           size = { numPoints, numComponents };
334         }
335 
336         auto var = this->IO.DefineVariable<vtkm::FloatDefault>(name, shape, offset, size);
337         this->PointCenteredFieldVars.push_back(var);
338       }
339       else if (field.GetAssociation() == vtkm::cont::Field::Association::CELL_SET)
340       {
341         if (numComponents == 1)
342         {
343           shape = { this->TotalNumberOfCells };
344           offset = { this->DataSetCellsOffset };
345           size = { numCells };
346         }
347         else
348         {
349           shape = { this->TotalNumberOfCells, numComponents };
350           offset = { this->DataSetCellsOffset, 0 };
351           size = { numCells, numComponents };
352         }
353         auto var = this->IO.DefineVariable<vtkm::FloatDefault>(name, shape, offset, size);
354         this->CellCenteredFieldVars.push_back(var);
355       }
356     }
357   }
358 
SetDataSets(vtkm::cont::PartitionedDataSet dataSets)359   void SetDataSets(vtkm::cont::PartitionedDataSet dataSets) { this->DataSets = dataSets; }
360 
361 protected:
ShouldWriteVariable(const std::string & var) const362   bool ShouldWriteVariable(const std::string& var) const
363   {
364     bool ret = true;
365     if (this->FieldsToWriteSet)
366     {
367       //See if it's in our set of fields.
368       ret = this->FieldsToWrite.find(var) != this->FieldsToWrite.end();
369     }
370     return ret;
371   }
372 
ComputeGlobalBlockInfo()373   void ComputeGlobalBlockInfo()
374   {
375     this->NumberOfDataSets = static_cast<std::size_t>(this->DataSets.GetNumberOfPartitions());
376 
377     this->DataSetsPerRank.clear();
378     this->DataSetsPerRank.resize(static_cast<size_t>(this->NumRanks), 0);
379     this->DataSetsPerRank[static_cast<size_t>(this->Rank)] =
380       static_cast<int>(this->NumberOfDataSets);
381 
382 #ifdef FIDES_USE_MPI
383     MPI_Allreduce(
384       MPI_IN_PLACE, this->DataSetsPerRank.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
385 #endif
386 
387     int tot = std::accumulate(this->DataSetsPerRank.begin(), this->DataSetsPerRank.end(), 0);
388     this->TotalNumberOfDataSets = static_cast<std::size_t>(tot);
389 
390     this->DataSetOffset = 0;
391     for (size_t i = 0; i < static_cast<size_t>(this->Rank); i++)
392     {
393       this->DataSetOffset += static_cast<size_t>(this->DataSetsPerRank[i]);
394     }
395 
396     // Need to determine the point and cell offsets for each block.
397     std::vector<int> numPoints(static_cast<size_t>(this->NumRanks), 0);
398     std::vector<int> numCells(static_cast<size_t>(this->NumRanks), 0);
399 
400     for (std::size_t i = 0; i < this->NumberOfDataSets; i++)
401     {
402       const auto& ds = this->DataSets.GetPartition(static_cast<vtkm::Id>(i));
403       numPoints[static_cast<size_t>(this->Rank)] += ds.GetNumberOfPoints();
404       numCells[static_cast<size_t>(this->Rank)] += ds.GetCellSet().GetNumberOfCells();
405     }
406 
407 #ifdef FIDES_USE_MPI
408     MPI_Allreduce(MPI_IN_PLACE, numPoints.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
409     MPI_Allreduce(MPI_IN_PLACE, numCells.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
410 #endif
411 
412     tot = std::accumulate(numPoints.begin(), numPoints.end(), 0);
413     this->TotalNumberOfPoints = static_cast<std::size_t>(tot);
414     tot = std::accumulate(numCells.begin(), numCells.end(), 0);
415     this->TotalNumberOfCells = static_cast<std::size_t>(tot);
416 
417     this->DataSetPointsOffset = 0;
418     this->DataSetCellsOffset = 0;
419     for (size_t i = 0; i < static_cast<size_t>(this->Rank); i++)
420     {
421       this->DataSetPointsOffset += static_cast<size_t>(numPoints[i]);
422       this->DataSetCellsOffset += static_cast<size_t>(numCells[i]);
423     }
424 
425     this->ComputeDataModelSpecificGlobalBlockInfo();
426   }
427 
428   virtual void ComputeDataModelSpecificGlobalBlockInfo() = 0;
429 
430   vtkm::cont::PartitionedDataSet DataSets;
431   std::string OutputFileName;
432   adios2::ADIOS Adios;
433   adios2::IO IO;
434   adios2::Engine Engine;
435 
436   std::vector<adios2::Variable<vtkm::FloatDefault>> PointCenteredFieldVars;
437   std::vector<adios2::Variable<vtkm::FloatDefault>> CellCenteredFieldVars;
438   bool FieldsToWriteSet;
439   std::set<std::string> FieldsToWrite;
440 
441   int Rank = 0;
442   int NumRanks = 1;
443   std::vector<int> DataSetsPerRank;
444   std::size_t TotalNumberOfDataSets = 0;
445   std::size_t TotalNumberOfPoints = 0;
446   std::size_t TotalNumberOfCells = 0;
447   std::size_t NumberOfDataSets = 0;
448   std::size_t DataSetOffset = 0;
449   std::size_t DataSetPointsOffset = 0;
450   std::size_t DataSetCellsOffset = 0;
451   bool VariablesDefined = false;
452 };
453 
454 class DataSetWriter::UniformDataSetWriter : public DataSetWriter::GenericWriter
455 {
456   using UniformCoordType = vtkm::cont::ArrayHandleUniformPointCoordinates;
457   using UniformCellType = vtkm::cont::CellSetStructured<3>;
458 
459 public:
UniformDataSetWriter(const vtkm::cont::PartitionedDataSet & dataSets,const std::string & fname,const std::string & outputMode,const bool & appendMode=false)460   UniformDataSetWriter(const vtkm::cont::PartitionedDataSet& dataSets,
461                        const std::string& fname,
462                        const std::string& outputMode,
463                        const bool& appendMode = false)
464     : GenericWriter(dataSets, fname, outputMode, appendMode)
465   {
466   }
467 
DefineDataModelVariables()468   void DefineDataModelVariables() override
469   {
470     std::vector<std::size_t> shape = { 3 * this->TotalNumberOfDataSets };
471     std::vector<std::size_t> offset = { 3 * this->DataSetOffset };
472     std::vector<std::size_t> size = { 3 * this->NumberOfDataSets };
473 
474 
475     this->DimsVar = this->IO.DefineVariable<std::size_t>("dims", shape, offset, size);
476     this->OriginsVar = this->IO.DefineVariable<double>("origin", shape, offset, size);
477     this->SpacingsVar = this->IO.DefineVariable<double>("spacing", shape, offset, size);
478   }
479 
WriteCoordinates()480   void WriteCoordinates() override
481   {
482     this->DimsValues.clear();
483     this->OriginsValues.clear();
484     this->SpacingsValues.clear();
485     this->DimsValues.resize(static_cast<size_t>(this->DataSets.GetNumberOfPartitions() * 3));
486     this->OriginsValues.resize(static_cast<size_t>(this->DataSets.GetNumberOfPartitions() * 3));
487     this->SpacingsValues.resize(static_cast<size_t>(this->DataSets.GetNumberOfPartitions() * 3));
488 
489     std::vector<std::size_t> shape = { 3 * this->TotalNumberOfDataSets };
490     this->DimsVar.SetShape(shape);
491     this->OriginsVar.SetShape(shape);
492     this->SpacingsVar.SetShape(shape);
493 
494     for (std::size_t i = 0; i < static_cast<size_t>(this->DataSets.GetNumberOfPartitions()); i++)
495     {
496       const auto& ds = this->DataSets.GetPartition(static_cast<vtkm::Id>(i));
497       const auto& ucoords = ds.GetCoordinateSystem().GetData().AsArrayHandle<UniformCoordType>();
498       auto origin = ucoords.ReadPortal().GetOrigin();
499       auto spacing = ucoords.ReadPortal().GetSpacing();
500       const auto& cellSet = ds.GetCellSet().Cast<UniformCellType>();
501       auto dim = cellSet.GetPointDimensions();
502 
503       for (int j = 0; j < 3; j++)
504       {
505         this->DimsValues[i * 3 + static_cast<size_t>(j)] = static_cast<std::size_t>(dim[j]);
506         this->OriginsValues[i * 3 + static_cast<size_t>(j)] = origin[j];
507         this->SpacingsValues[i * 3 + static_cast<size_t>(j)] = spacing[j];
508       }
509 
510       adios2::Box<adios2::Dims> sel({ i * 3 + (3 * this->DataSetOffset) }, { 3 });
511       this->DimsVar.SetSelection(sel);
512       this->OriginsVar.SetSelection(sel);
513       this->SpacingsVar.SetSelection(sel);
514       this->Engine.Put<std::size_t>(this->DimsVar, &this->DimsValues[i * 3]);
515       this->Engine.Put<double>(this->OriginsVar, &this->OriginsValues[i * 3]);
516       this->Engine.Put<double>(this->SpacingsVar, &this->SpacingsValues[i * 3]);
517     }
518   }
519 
520   // Nothing to do for structured cells
WriteCells()521   void WriteCells() override {}
522 
523 protected:
524   // Nothing to do for uniform grids.
ComputeDataModelSpecificGlobalBlockInfo()525   void ComputeDataModelSpecificGlobalBlockInfo() override {}
526 
527 private:
528   adios2::Variable<std::size_t> DimsVar;
529   adios2::Variable<double> OriginsVar, SpacingsVar;
530   std::vector<std::size_t> DimsValues;
531   std::vector<double> OriginsValues, SpacingsValues;
532 };
533 
534 class DataSetWriter::RectilinearDataSetWriter : public DataSetWriter::GenericWriter
535 {
536   using RectCoordType =
537     vtkm::cont::ArrayHandleCartesianProduct<vtkm::cont::ArrayHandle<vtkm::FloatDefault>,
538                                             vtkm::cont::ArrayHandle<vtkm::FloatDefault>,
539                                             vtkm::cont::ArrayHandle<vtkm::FloatDefault>>;
540   using RectCellType = vtkm::cont::CellSetStructured<3>;
541 
542 public:
RectilinearDataSetWriter(const vtkm::cont::PartitionedDataSet & dataSets,const std::string & fname,const std::string & outputMode,const bool & appendMode=false)543   RectilinearDataSetWriter(const vtkm::cont::PartitionedDataSet& dataSets,
544                            const std::string& fname,
545                            const std::string& outputMode,
546                            const bool& appendMode = false)
547     : GenericWriter(dataSets, fname, outputMode, appendMode)
548   {
549   }
550 
DefineDataModelVariables()551   void DefineDataModelVariables() override
552   {
553     std::vector<std::size_t> shape, offset, size;
554 
555     shape = { 3 * this->TotalNumberOfDataSets };
556     offset = { 3 * this->DataSetOffset };
557     size = { 3 * this->NumberOfDataSets };
558 
559     this->DimsVar = this->IO.DefineVariable<std::size_t>("dims", shape, offset, size);
560 
561     shape = { this->TotalNumberOfXCoords };
562     offset = { this->XCoordsOffset };
563     size = { this->NumXCoords };
564     this->XCoordsVar = this->IO.DefineVariable<vtkm::FloatDefault>("x_array", shape, offset, size);
565 
566     shape = { this->TotalNumberOfYCoords };
567     offset = { this->YCoordsOffset };
568     size = { this->NumYCoords };
569     this->YCoordsVar = this->IO.DefineVariable<vtkm::FloatDefault>("y_array", shape, offset, size);
570 
571     shape = { this->TotalNumberOfZCoords };
572     offset = { this->ZCoordsOffset };
573     size = { this->NumZCoords };
574     this->ZCoordsVar = this->IO.DefineVariable<vtkm::FloatDefault>("z_array", shape, offset, size);
575   }
576 
WriteCoordinates()577   void WriteCoordinates() override
578   {
579     std::size_t xcOffset = this->XCoordsOffset;
580     std::size_t ycOffset = this->YCoordsOffset;
581     std::size_t zcOffset = this->ZCoordsOffset;
582 
583     this->DimsValues.clear();
584     this->DimsValues.resize(this->DataSets.GetNumberOfPartitions() * 3);
585 
586     this->XCoordsVar.SetShape({ this->TotalNumberOfXCoords });
587     this->YCoordsVar.SetShape({ this->TotalNumberOfYCoords });
588     this->ZCoordsVar.SetShape({ this->TotalNumberOfZCoords });
589     this->DimsVar.SetShape({ 3 * this->TotalNumberOfDataSets });
590 
591     for (vtkm::Id i = 0; i < this->DataSets.GetNumberOfPartitions(); i++)
592     {
593       const auto& ds = this->DataSets.GetPartition(i);
594       const auto& coords = ds.GetCoordinateSystem().GetData().AsArrayHandle<RectCoordType>();
595 
596       vtkm::cont::ArrayHandleBasic<vtkm::FloatDefault> xc(coords.GetFirstArray());
597       vtkm::cont::ArrayHandleBasic<vtkm::FloatDefault> yc(coords.GetSecondArray());
598       vtkm::cont::ArrayHandleBasic<vtkm::FloatDefault> zc(coords.GetThirdArray());
599       std::size_t numXc = static_cast<std::size_t>(xc.GetNumberOfValues());
600       std::size_t numYc = static_cast<std::size_t>(yc.GetNumberOfValues());
601       std::size_t numZc = static_cast<std::size_t>(zc.GetNumberOfValues());
602 
603       const vtkm::FloatDefault* xBuff = xc.GetReadPointer();
604       const vtkm::FloatDefault* yBuff = yc.GetReadPointer();
605       const vtkm::FloatDefault* zBuff = zc.GetReadPointer();
606 
607       adios2::Box<adios2::Dims> xSel({ xcOffset }, { numXc });
608       adios2::Box<adios2::Dims> ySel({ ycOffset }, { numYc });
609       adios2::Box<adios2::Dims> zSel({ zcOffset }, { numZc });
610 
611       this->XCoordsVar.SetSelection(xSel);
612       this->YCoordsVar.SetSelection(ySel);
613       this->ZCoordsVar.SetSelection(zSel);
614 
615       this->Engine.Put<vtkm::FloatDefault>(this->XCoordsVar, xBuff);
616       this->Engine.Put<vtkm::FloatDefault>(this->YCoordsVar, yBuff);
617       this->Engine.Put<vtkm::FloatDefault>(this->ZCoordsVar, zBuff);
618 
619       adios2::Box<adios2::Dims> sel({ i * 3 + (3 * this->DataSetOffset) }, { 3 });
620 
621       this->DimsVar.SetSelection(sel);
622       this->DimsValues[i * 3 + 0] = numXc;
623       this->DimsValues[i * 3 + 1] = numYc;
624       this->DimsValues[i * 3 + 2] = numZc;
625       this->Engine.Put<std::size_t>(this->DimsVar, &this->DimsValues[i * 3]);
626 
627       xcOffset += numXc;
628       ycOffset += numYc;
629       zcOffset += numZc;
630     }
631   }
632 
WriteCells()633   void WriteCells() override {}
634 
635 protected:
ComputeDataModelSpecificGlobalBlockInfo()636   void ComputeDataModelSpecificGlobalBlockInfo() override
637   {
638     std::size_t numDS = static_cast<std::size_t>(this->DataSets.GetNumberOfPartitions());
639 
640     std::vector<int> numCoordinates(this->NumRanks * 3, 0);
641 
642     this->NumXCoords = 0;
643     this->NumYCoords = 0;
644     this->NumZCoords = 0;
645     for (std::size_t i = 0; i < numDS; i++)
646     {
647       const auto& ds = this->DataSets.GetPartition(i);
648       const auto& coords = ds.GetCoordinateSystem().GetData().AsArrayHandle<RectCoordType>();
649 
650       auto coordsPortal = coords.ReadPortal();
651       this->NumXCoords += coordsPortal.GetFirstPortal().GetNumberOfValues();
652       this->NumYCoords += coordsPortal.GetSecondPortal().GetNumberOfValues();
653       this->NumZCoords += coordsPortal.GetThirdPortal().GetNumberOfValues();
654     }
655     numCoordinates[this->Rank * 3 + 0] = this->NumXCoords;
656     numCoordinates[this->Rank * 3 + 1] = this->NumYCoords;
657     numCoordinates[this->Rank * 3 + 2] = this->NumZCoords;
658 
659 #ifdef FIDES_USE_MPI
660     MPI_Allreduce(
661       MPI_IN_PLACE, numCoordinates.data(), this->NumRanks * 3, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
662 #endif
663     this->TotalNumberOfXCoords = 0;
664     this->TotalNumberOfYCoords = 0;
665     this->TotalNumberOfZCoords = 0;
666     for (int i = 0; i < this->NumRanks; i++)
667     {
668       this->TotalNumberOfXCoords += numCoordinates[i * 3 + 0];
669       this->TotalNumberOfYCoords += numCoordinates[i * 3 + 1];
670       this->TotalNumberOfZCoords += numCoordinates[i * 3 + 2];
671     }
672     this->XCoordsOffset = 0;
673     this->YCoordsOffset = 0;
674     this->ZCoordsOffset = 0;
675     for (int i = 0; i < this->Rank; i++)
676     {
677       this->XCoordsOffset += numCoordinates[i * 3 + 0];
678       this->YCoordsOffset += numCoordinates[i * 3 + 1];
679       this->ZCoordsOffset += numCoordinates[i * 3 + 2];
680     }
681   }
682 
683 private:
684   std::vector<std::size_t> DimsValues;
685   adios2::Variable<vtkm::FloatDefault> XCoordsVar, YCoordsVar, ZCoordsVar;
686   adios2::Variable<std::size_t> DimsVar;
687   std::size_t TotalNumberOfXCoords = 0;
688   std::size_t TotalNumberOfYCoords = 0;
689   std::size_t TotalNumberOfZCoords = 0;
690   std::size_t NumXCoords = 0;
691   std::size_t NumYCoords = 0;
692   std::size_t NumZCoords = 0;
693   std::size_t XCoordsOffset = 0;
694   std::size_t YCoordsOffset = 0;
695   std::size_t ZCoordsOffset = 0;
696 };
697 
698 class DataSetWriter::UnstructuredSingleTypeDataSetWriter : public DataSetWriter::GenericWriter
699 {
700   using UnstructuredCoordType = vtkm::cont::ArrayHandle<vtkm::Vec3f>;
701   using UnstructuredSingleType = vtkm::cont::CellSetSingleType<>;
702 
703 public:
UnstructuredSingleTypeDataSetWriter(const vtkm::cont::PartitionedDataSet & dataSets,const std::string & fname,const std::string & outputMode,const bool & appendMode=false)704   UnstructuredSingleTypeDataSetWriter(const vtkm::cont::PartitionedDataSet& dataSets,
705                                       const std::string& fname,
706                                       const std::string& outputMode,
707                                       const bool& appendMode = false)
708     : GenericWriter(dataSets, fname, outputMode, appendMode)
709   {
710   }
711 
DefineDataModelVariables()712   void DefineDataModelVariables() override
713   {
714     std::vector<std::size_t> shape, offset, size;
715 
716     // TotalNumberOfCoords = 3*numpoints; but summed over all datasets you have
717     // on your rank.
718     shape = { this->TotalNumberOfCoords, 3 };
719     offset = { this->CoordOffset, 0 };
720     size = { this->NumCoords, 3 };
721     this->CoordsVar =
722       this->IO.DefineVariable<vtkm::FloatDefault>("coordinates", shape, offset, size);
723 
724     shape = { this->TotalNumberOfConnIds };
725     offset = { this->CellConnOffset };
726     size = { this->NumCells * this->NumPointsInCell };
727     this->ConnVar = this->IO.DefineVariable<vtkm::Id>("connectivity", shape, offset, size);
728   }
729 
WriteCoordinates()730   void WriteCoordinates() override
731   {
732     std::size_t cOffset = this->CoordOffset;
733 
734     this->CoordsVar.SetShape({ this->TotalNumberOfCoords, 3 });
735 
736     for (vtkm::Id i = 0; i < this->DataSets.GetNumberOfPartitions(); i++)
737     {
738       const auto& ds = this->DataSets.GetPartition(i);
739       const auto& coords = ds.GetCoordinateSystem().GetData();
740       const auto& arr = coords.AsArrayHandle<vtkm::cont::ArrayHandleBasic<vtkm::Vec3f>>();
741 
742       const vtkm::Vec3f* buffVec = arr.GetReadPointer();
743       const vtkm::FloatDefault* buff = &buffVec[0][0];
744 
745       std::size_t numCoords = static_cast<std::size_t>(coords.GetNumberOfValues());
746       // This is a way you can write chunks in.
747       // Instead of buffering the entire dataset, and then writing it,
748       // you can buffer subsets, and specify a "Box" offset.
749       adios2::Box<adios2::Dims> sel({ cOffset, 0 }, { numCoords, 3 });
750 
751       this->CoordsVar.SetSelection(sel);
752       this->Engine.Put<vtkm::FloatDefault>(this->CoordsVar, buff);
753 
754       cOffset += numCoords;
755     }
756   }
757 
WriteCells()758   void WriteCells() override
759   {
760     std::size_t offset = this->CellConnOffset;
761     this->ConnVar.SetShape({ this->TotalNumberOfConnIds });
762 
763     for (vtkm::Id i = 0; i < this->DataSets.GetNumberOfPartitions(); i++)
764     {
765       const auto& ds = this->DataSets.GetPartition(i);
766       const auto& cellSet = ds.GetCellSet().Cast<UnstructuredSingleType>();
767       const auto& conn = cellSet.GetConnectivityArray(vtkm::TopologyElementTagCell{},
768                                                       vtkm::TopologyElementTagPoint{});
769 
770       std::size_t numConn = static_cast<std::size_t>(conn.GetNumberOfValues());
771 
772       adios2::Box<adios2::Dims> sel({ offset }, { numConn });
773       this->ConnVar.SetSelection(sel);
774 
775       vtkm::cont::ArrayHandleBasic<vtkm::Id> arr(conn);
776       const vtkm::Id* buff = arr.GetReadPointer();
777       this->Engine.Put<vtkm::Id>(this->ConnVar, buff);
778 
779       offset += numConn;
780     }
781   }
782 
783 protected:
ComputeDataModelSpecificGlobalBlockInfo()784   void ComputeDataModelSpecificGlobalBlockInfo() override
785   {
786     std::size_t numDS = static_cast<std::size_t>(this->DataSets.GetNumberOfPartitions());
787     std::vector<int> numCoordinates(this->NumRanks, 0);
788     std::vector<int> numCells(this->NumRanks, 0);
789     std::vector<int> numPtsInCell(this->NumRanks, 0);
790     std::vector<int> cellShape(this->NumRanks, 0);
791 
792     this->NumCoords = 0;
793     this->NumPointsInCell = 0;
794     this->CellShape = -1;
795     this->TotalNumberOfCoords = 0;
796     this->TotalNumberOfCells = 0;
797 
798     for (std::size_t i = 0; i < numDS; i++)
799     {
800       const auto& ds = this->DataSets.GetPartition(i);
801       const auto& coords =
802         ds.GetCoordinateSystem().GetData().AsArrayHandle<UnstructuredCoordType>();
803       this->NumCoords += coords.GetNumberOfValues();
804 
805       const auto& cellSet = ds.GetCellSet().Cast<UnstructuredSingleType>();
806       this->NumCells += cellSet.GetNumberOfCells();
807       if (i == 0)
808       {
809         this->NumPointsInCell = cellSet.GetNumberOfPointsInCell(0);
810         this->CellShape = cellSet.GetCellShapeAsId();
811       }
812       else
813       {
814         if (static_cast<size_t>(cellSet.GetNumberOfPointsInCell(0)) != this->NumPointsInCell)
815         {
816           throw std::runtime_error("Number of points in cell for "
817                                    "CellSetSingleType is not consistent.");
818         }
819         if (cellSet.GetCellShapeAsId() != this->CellShape)
820         {
821           throw std::runtime_error("Cell shape for CellSetSingleType is not consistent. 00");
822         }
823       }
824     }
825 
826     numCoordinates[this->Rank] = this->NumCoords;
827     numCells[this->Rank] = this->NumCells;
828     numPtsInCell[this->Rank] = this->NumPointsInCell;
829     cellShape[this->Rank] = static_cast<int>(this->CellShape);
830 
831 #ifdef FIDES_USE_MPI
832     MPI_Allreduce(
833       MPI_IN_PLACE, numCoordinates.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
834     MPI_Allreduce(MPI_IN_PLACE, numCells.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
835     MPI_Allreduce(
836       MPI_IN_PLACE, numPtsInCell.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
837     MPI_Allreduce(MPI_IN_PLACE, cellShape.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
838 #endif
839 
840     for (int i = 0; i < this->NumRanks; i++)
841     {
842       this->TotalNumberOfCoords += numCoordinates[i];
843       this->TotalNumberOfCells += numCells[i];
844 
845       if (numCells[i] > 0 && this->NumCells > 0) // if there are cells, they must be consistent.
846       {
847         if (static_cast<size_t>(numPtsInCell[i]) != this->NumPointsInCell)
848         {
849           throw std::runtime_error("Number of points in cell for "
850                                    "CellSetSingleType is not consistent.");
851         }
852         if (cellShape[i] != this->CellShape)
853         {
854           throw std::runtime_error("Cell shape for CellSetSingleType is not consistent.");
855         }
856       }
857     }
858     this->TotalNumberOfConnIds = this->TotalNumberOfCells * this->NumPointsInCell;
859 
860     this->CoordOffset = 0;
861     this->CellConnOffset = 0;
862     for (int i = 0; i < this->Rank; i++)
863     {
864       this->CoordOffset += numCoordinates[i];
865       this->CellConnOffset += (numCells[i] * this->NumPointsInCell);
866     }
867   }
868 
869 private:
870   size_t NumCoords = 0;
871   size_t TotalNumberOfCoords = 0;
872   size_t NumCells = 0;
873   size_t TotalNumberOfCells = 0;
874   size_t TotalNumberOfConnIds = 0;
875   size_t NumPointsInCell = 0;
876   vtkm::Id CellShape = 0;
877   size_t CoordOffset = 0;
878   size_t CellConnOffset = 0;
879 
880   adios2::Variable<vtkm::FloatDefault> CoordsVar;
881   adios2::Variable<vtkm::Id> ConnVar;
882 };
883 
884 class DataSetWriter::UnstructuredExplicitDataSetWriter : public DataSetWriter::GenericWriter
885 {
886   using UnstructuredCoordType = vtkm::cont::ArrayHandle<vtkm::Vec3f>;
887   using UnstructuredExplicitType = vtkm::cont::CellSetExplicit<>;
888 
889 public:
UnstructuredExplicitDataSetWriter(const vtkm::cont::PartitionedDataSet & dataSets,const std::string & fname,const std::string & outputMode,const bool & appendMode=false)890   UnstructuredExplicitDataSetWriter(const vtkm::cont::PartitionedDataSet& dataSets,
891                                     const std::string& fname,
892                                     const std::string& outputMode,
893                                     const bool& appendMode = false)
894     : GenericWriter(dataSets, fname, outputMode, appendMode)
895   {
896     // Validate that every partition has the same type:
897     for (auto const& ds : dataSets)
898     {
899       if (!ds.GetCellSet().IsType<UnstructuredExplicitType>())
900       {
901         std::string err = std::string(__FILE__) + ":" + std::to_string(__LINE__);
902         err += ": The CellSet of each partition of the PartitionedDataSet is "
903                "constrained to be have the type CellSetExplicit.";
904         throw std::runtime_error(err);
905       }
906     }
907   }
908 
DefineDataModelVariables()909   void DefineDataModelVariables() override
910   {
911     // The total number of points in all partitions.
912     // The mental model should be that each partition is a piece of a larger
913     // geometry.
914     std::vector<std::size_t> shape = { static_cast<size_t>(this->TotalNumberOfCoords), 3 };
915     this->CoordsVar = this->IO.DefineVariable<vtkm::FloatDefault>("coordinates", shape);
916 
917     // Now the shapes array.
918     this->ShapesVar = this->IO.DefineVariable<uint8_t>(
919       "cell_types", { static_cast<size_t>(this->TotalNumberOfCells) });
920     // VTK-m stores offsets, but Fides stores the number of vertices/cell.
921     this->VertsVar = this->IO.DefineVariable<vtkm::IdComponent>(
922       "num_verts", { static_cast<size_t>(this->TotalNumberOfCells) });
923     this->ConnVar = this->IO.DefineVariable<vtkm::Id>(
924       "connectivity", { static_cast<size_t>(this->TotalNumberOfConns) });
925   }
926 
WriteCoordinates()927   void WriteCoordinates() override
928   {
929     std::size_t cOffset = this->CoordOffset;
930 
931     this->CoordsVar.SetShape({ static_cast<size_t>(this->TotalNumberOfCoords), 3 });
932 
933     for (auto const& ds : DataSets)
934     {
935       // VTK-m wants to think about this data as [[v0_x, v0_y, v0_z], [v1_x,
936       // v1_y, v1_z], ...] But ADIOS wants to think about it as a contiguous
937       // array. I've asked Norbert to support "array of structs" directly, but
938       // since this bit of hacking gets it done, there's not a huge incentive to
939       // do this.
940       const auto& coords = ds.GetCoordinateSystem().GetData();
941       const auto& arr = coords.AsArrayHandle<vtkm::cont::ArrayHandleBasic<vtkm::Vec3f>>();
942       const vtkm::Vec3f* buffVec = arr.GetReadPointer();
943       // buff will point to v0_x, and buff + 1 points to v0_y, so on.
944       const vtkm::FloatDefault* buff = &buffVec[0][0];
945 
946       std::size_t numCoords = static_cast<std::size_t>(coords.GetNumberOfValues());
947       adios2::Box<adios2::Dims> sel({ cOffset, 0 }, { numCoords, 3 });
948 
949       this->CoordsVar.SetSelection(sel);
950       this->Engine.Put<vtkm::FloatDefault>(this->CoordsVar, buff);
951       cOffset += numCoords;
952     }
953   }
954 
WriteCells()955   void WriteCells() override
956   {
957     this->NumVerts.clear();
958     this->NumVerts.resize(static_cast<size_t>(this->NumCells), -1);
959 
960     //Update the shape size for this step.
961     this->ShapesVar.SetShape({ this->TotalNumberOfCells });
962     this->VertsVar.SetShape({ this->TotalNumberOfCells });
963     this->ConnVar.SetShape({ static_cast<size_t>(this->TotalNumberOfConns) });
964 
965     size_t cellOffset = this->CellOffset;
966     size_t connOffset = this->ConnOffset;
967     size_t numVertsOffset = 0;
968     for (auto const& ds : this->DataSets)
969     {
970       const vtkm::cont::DynamicCellSet& dCellSet = ds.GetCellSet();
971       const auto& cellSet = dCellSet.Cast<vtkm::cont::CellSetExplicit<>>();
972       size_t numCells = static_cast<size_t>(cellSet.GetNumberOfCells());
973 
974       const auto& shapes =
975         cellSet.GetShapesArray(vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{});
976 
977       vtkm::cont::ArrayHandleBasic<uint8_t> shapes_arr(shapes);
978       const uint8_t* buffer = shapes_arr.GetReadPointer();
979       adios2::Box<adios2::Dims> shapesSelection({ cellOffset }, { numCells });
980       this->ShapesVar.SetSelection(shapesSelection);
981       this->Engine.Put<uint8_t>(this->ShapesVar, buffer);
982 
983       // Each offset must be converted to a number of vertices. See
984       // CellSetExplicit::PostRead
985       auto const& offsets =
986         cellSet.GetOffsetsArray(vtkm::TopologyElementTagCell{}, vtkm::TopologyElementTagPoint{});
987       auto rp = offsets.ReadPortal();
988 
989       for (vtkm::Id i = 0; static_cast<size_t>(i) < numCells; i++)
990       {
991         this->NumVerts[numVertsOffset + i] = rp.Get(i + 1) - rp.Get(i);
992       }
993 
994       adios2::Box<adios2::Dims> vertsVarSel({ cellOffset }, { numCells });
995       this->VertsVar.SetSelection(vertsVarSel);
996       this->Engine.Put(this->VertsVar, &(this->NumVerts[numVertsOffset]));
997       cellOffset += numCells;
998       numVertsOffset += numCells;
999 
1000       const auto& conn = cellSet.GetConnectivityArray(vtkm::TopologyElementTagCell{},
1001                                                       vtkm::TopologyElementTagPoint{});
1002       std::size_t numConn = static_cast<std::size_t>(conn.GetNumberOfValues());
1003       adios2::Box<adios2::Dims> connSelection({ connOffset }, { numConn });
1004       this->ConnVar.SetSelection(connSelection);
1005 
1006       // Now get the buffer:
1007       vtkm::cont::ArrayHandleBasic<vtkm::Id> conn_arr(conn);
1008       const vtkm::Id* buff4 = conn_arr.GetReadPointer();
1009       this->Engine.Put<vtkm::Id>(this->ConnVar, buff4);
1010       connOffset += numConn;
1011     }
1012   }
1013 
1014 protected:
ComputeDataModelSpecificGlobalBlockInfo()1015   void ComputeDataModelSpecificGlobalBlockInfo() override
1016   {
1017     std::vector<int> numCoordinates(this->NumRanks, 0);
1018     std::vector<int> numCells(this->NumRanks, 0);
1019     std::vector<int> cellShape(this->NumRanks, 0);
1020     std::vector<int> numConns(this->NumRanks, 0);
1021 
1022     this->NumCoords = 0;
1023     this->NumCells = 0;
1024     this->TotalNumberOfCoords = 0;
1025     this->TotalNumberOfCells = 0;
1026     for (const auto& ds : this->DataSets)
1027     {
1028       const auto& coords = ds.GetCoordinateSystem().GetData();
1029       this->NumCoords += coords.GetNumberOfValues();
1030 
1031       const auto& cellSet = ds.GetCellSet().Cast<UnstructuredExplicitType>();
1032       this->NumCells += cellSet.GetNumberOfCells();
1033     }
1034 
1035     this->NumConns = 0;
1036     this->TotalNumberOfConns = 0;
1037     for (auto const& ds : DataSets)
1038     {
1039       auto const& dCellSet = ds.GetCellSet();
1040       vtkm::cont::CellSetExplicit<>& cellSet = dCellSet.Cast<UnstructuredExplicitType>();
1041       const auto& conn = cellSet.GetConnectivityArray(vtkm::TopologyElementTagCell{},
1042                                                       vtkm::TopologyElementTagPoint{});
1043       std::size_t numConn = static_cast<std::size_t>(conn.GetNumberOfValues());
1044       this->NumConns += numConn;
1045     }
1046 
1047     numCoordinates[this->Rank] = this->NumCoords;
1048     numCells[this->Rank] = this->NumCells;
1049     numConns[this->Rank] = this->NumConns;
1050 #ifdef FIDES_USE_MPI
1051     MPI_Allreduce(
1052       MPI_IN_PLACE, numCoordinates.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
1053     MPI_Allreduce(MPI_IN_PLACE, numCells.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
1054     MPI_Allreduce(MPI_IN_PLACE, numConns.data(), this->NumRanks, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
1055 #endif
1056 
1057     for (int i = 0; i < this->NumRanks; i++)
1058     {
1059       this->TotalNumberOfCoords += numCoordinates[i];
1060       this->TotalNumberOfCells += numCells[i];
1061       this->TotalNumberOfConns += numConns[i];
1062     }
1063 
1064     this->CoordOffset = 0;
1065     this->CellOffset = 0;
1066     this->ConnOffset = 0;
1067     for (int i = 0; i < this->Rank; i++)
1068     {
1069       this->CoordOffset += numCoordinates[i];
1070       this->CellOffset += numCells[i];
1071       this->ConnOffset += numConns[i];
1072     }
1073   }
1074 
1075 private:
1076   adios2::Variable<vtkm::FloatDefault> CoordsVar;
1077   adios2::Variable<uint8_t> ShapesVar;
1078   adios2::Variable<vtkm::Id> ConnVar;
1079   adios2::Variable<vtkm::IdComponent> VertsVar;
1080   vtkm::Id NumCoords = 0;
1081   vtkm::Id NumCells = 0;
1082   vtkm::Id CoordOffset = 0;
1083   vtkm::Id TotalNumberOfCoords = 0;
1084   vtkm::Id CellOffset = 0;
1085   vtkm::Id NumConns = 0;
1086   vtkm::Id ConnOffset = 0;
1087   vtkm::Id TotalNumberOfConns = 0;
1088   std::vector<vtkm::IdComponent> NumVerts;
1089 };
1090 
DataSetWriter(const std::string & outputFile)1091 DataSetWriter::DataSetWriter(const std::string& outputFile)
1092   : OutputFile(outputFile)
1093   , WriteFieldSet(false)
1094 {
1095 }
1096 
GetDataSetType(const vtkm::cont::DataSet & ds)1097 unsigned char DataSetWriter::GetDataSetType(const vtkm::cont::DataSet& ds)
1098 {
1099   using UniformCoordType = vtkm::cont::ArrayHandleUniformPointCoordinates;
1100   using RectilinearCoordType =
1101     vtkm::cont::ArrayHandleCartesianProduct<vtkm::cont::ArrayHandle<vtkm::FloatDefault>,
1102                                             vtkm::cont::ArrayHandle<vtkm::FloatDefault>,
1103                                             vtkm::cont::ArrayHandle<vtkm::FloatDefault>>;
1104   using UnstructuredSingleType = vtkm::cont::CellSetSingleType<>;
1105 
1106   using UnstructuredExplicitType = vtkm::cont::CellSetExplicit<>;
1107 
1108   if (ds.GetCoordinateSystem().GetData().IsType<UniformCoordType>())
1109   {
1110     return DATASET_TYPE_UNIFORM;
1111   }
1112   else if (ds.GetCoordinateSystem().GetData().IsType<RectilinearCoordType>())
1113   {
1114     return DATASET_TYPE_RECTILINEAR;
1115   }
1116   else if (ds.GetCellSet().IsType<UnstructuredSingleType>())
1117   {
1118     return DATASET_TYPE_UNSTRUCTURED_SINGLE;
1119   }
1120   else if (ds.GetCellSet().IsType<UnstructuredExplicitType>())
1121   {
1122     return DATASET_TYPE_UNSTRUCTURED;
1123   }
1124   else
1125   {
1126     return DATASET_TYPE_ERROR;
1127   }
1128 }
1129 
SetDataSetType(const vtkm::cont::PartitionedDataSet & dataSets)1130 void DataSetWriter::SetDataSetType(const vtkm::cont::PartitionedDataSet& dataSets)
1131 {
1132   int rank = 0, numRanks = 1;
1133 #ifdef FIDES_USE_MPI
1134   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
1135   MPI_Comm_size(MPI_COMM_WORLD, &numRanks);
1136 #endif
1137 
1138   //Make sure all the datasets are the same.
1139   std::vector<unsigned char> myDataSetTypes;
1140   for (const auto& ds : dataSets)
1141   {
1142     myDataSetTypes.push_back(this->GetDataSetType(ds));
1143   }
1144 
1145   unsigned char dataSetType = this->DATASET_TYPE_NONE;
1146   for (const auto& v : myDataSetTypes)
1147   {
1148     dataSetType |= v;
1149   }
1150   // if not equal to one of the valid dataset types, it's an error.
1151   if (!(dataSetType == DATASET_TYPE_NONE || dataSetType == DATASET_TYPE_UNIFORM ||
1152         dataSetType == DATASET_TYPE_RECTILINEAR ||
1153         dataSetType == DATASET_TYPE_UNSTRUCTURED_SINGLE ||
1154         dataSetType == DATASET_TYPE_UNSTRUCTURED))
1155   {
1156     dataSetType = DATASET_TYPE_ERROR;
1157   }
1158 
1159   std::vector<unsigned char> allDataSetTypes(numRanks, DATASET_TYPE_NONE);
1160   allDataSetTypes[rank] = dataSetType;
1161 #ifdef FIDES_USE_MPI
1162   MPI_Allreduce(
1163     MPI_IN_PLACE, allDataSetTypes.data(), numRanks, MPI_UNSIGNED_CHAR, MPI_BOR, MPI_COMM_WORLD);
1164 #endif
1165 
1166   //If we OR these values all together, we will get the global dataset type.
1167   //There can be NONE, but all non NONE should be the same. If not, it's an error.
1168   unsigned char globalDataSetType = DATASET_TYPE_NONE;
1169   for (const auto& v : allDataSetTypes)
1170   {
1171     globalDataSetType |= v;
1172   }
1173 
1174   if (!(globalDataSetType == DATASET_TYPE_NONE || globalDataSetType == DATASET_TYPE_UNIFORM ||
1175         globalDataSetType == DATASET_TYPE_RECTILINEAR ||
1176         globalDataSetType == DATASET_TYPE_UNSTRUCTURED_SINGLE ||
1177         globalDataSetType == DATASET_TYPE_UNSTRUCTURED))
1178   {
1179     globalDataSetType = DATASET_TYPE_ERROR;
1180   }
1181 
1182   //Set the dataSet type.
1183   this->DataSetType = globalDataSetType;
1184 }
1185 
Write(const vtkm::cont::PartitionedDataSet & dataSets,const std::string & outputMode)1186 void DataSetWriter::Write(const vtkm::cont::PartitionedDataSet& dataSets,
1187                           const std::string& outputMode)
1188 {
1189   this->SetDataSetType(dataSets);
1190 
1191   if (this->DataSetType == DATASET_TYPE_NONE)
1192   {
1193     //Nobody has anything, so just return.
1194     return;
1195   }
1196   else if (this->DataSetType == DATASET_TYPE_UNIFORM)
1197   {
1198     UniformDataSetWriter writeImpl(dataSets, this->OutputFile, outputMode);
1199     if (this->WriteFieldSet)
1200       writeImpl.SetWriteFields(this->FieldsToWrite);
1201 
1202     writeImpl.Write();
1203   }
1204   else if (this->DataSetType == DATASET_TYPE_RECTILINEAR)
1205   {
1206     RectilinearDataSetWriter writeImpl(dataSets, this->OutputFile, outputMode);
1207     if (this->WriteFieldSet)
1208       writeImpl.SetWriteFields(this->FieldsToWrite);
1209     writeImpl.Write();
1210   }
1211   else if (this->DataSetType == DATASET_TYPE_UNSTRUCTURED_SINGLE)
1212   {
1213     UnstructuredSingleTypeDataSetWriter writeImpl(dataSets, this->OutputFile, outputMode);
1214     if (this->WriteFieldSet)
1215       writeImpl.SetWriteFields(this->FieldsToWrite);
1216     writeImpl.Write();
1217   }
1218   else if (this->DataSetType == DATASET_TYPE_UNSTRUCTURED)
1219   {
1220     UnstructuredExplicitDataSetWriter writeImpl(dataSets, this->OutputFile, outputMode);
1221     if (this->WriteFieldSet)
1222       writeImpl.SetWriteFields(this->FieldsToWrite);
1223     writeImpl.Write();
1224   }
1225   else
1226   {
1227     throw std::runtime_error("Unsupported dataset type");
1228   }
1229 }
1230 
DataSetAppendWriter(const std::string & outputFile)1231 DataSetAppendWriter::DataSetAppendWriter(const std::string& outputFile)
1232   : DataSetWriter(outputFile)
1233   , IsInitialized(false)
1234   , Writer(nullptr)
1235 {
1236 }
1237 
Write(const vtkm::cont::PartitionedDataSet & dataSets,const std::string & outputMode)1238 void DataSetAppendWriter::Write(const vtkm::cont::PartitionedDataSet& dataSets,
1239                                 const std::string& outputMode)
1240 {
1241   if (!this->IsInitialized)
1242     this->Initialize(dataSets, outputMode);
1243 
1244   //Make sure we're being consistent.
1245   unsigned char dsType = DATASET_TYPE_NONE;
1246   for (const auto& ds : dataSets)
1247     dsType |= this->GetDataSetType(ds);
1248 
1249   if (!(dsType == this->DATASET_TYPE_NONE || dsType == this->DataSetType))
1250   {
1251     throw std::runtime_error("Unsupported dataset type");
1252   }
1253 
1254   this->Writer->SetDataSets(dataSets);
1255   this->Writer->Write();
1256 }
1257 
Close()1258 void DataSetAppendWriter::Close()
1259 {
1260   this->IsInitialized = false;
1261   this->Writer.reset();
1262 }
1263 
Initialize(const vtkm::cont::PartitionedDataSet & dataSets,const std::string & outputMode)1264 void DataSetAppendWriter::Initialize(const vtkm::cont::PartitionedDataSet& dataSets,
1265                                      const std::string& outputMode)
1266 {
1267   this->SetDataSetType(dataSets);
1268   if (this->DataSetType == DATASET_TYPE_UNIFORM)
1269   {
1270     this->Writer.reset(
1271       new DataSetWriter::UniformDataSetWriter(dataSets, this->OutputFile, outputMode, true));
1272   }
1273   else if (this->DataSetType == DATASET_TYPE_RECTILINEAR)
1274   {
1275     this->Writer.reset(
1276       new DataSetWriter::RectilinearDataSetWriter(dataSets, this->OutputFile, outputMode, true));
1277   }
1278   else if (this->DataSetType == DATASET_TYPE_UNSTRUCTURED_SINGLE)
1279   {
1280     this->Writer.reset(
1281       new UnstructuredSingleTypeDataSetWriter(dataSets, this->OutputFile, outputMode, true));
1282   }
1283   else if (this->DataSetType == DATASET_TYPE_UNSTRUCTURED)
1284   {
1285     this->Writer.reset(
1286       new UnstructuredExplicitDataSetWriter(dataSets, this->OutputFile, outputMode, true));
1287   }
1288   else
1289   {
1290     throw std::runtime_error("Unsupported dataset type");
1291   }
1292 
1293   if (this->WriteFieldSet)
1294     this->Writer->SetWriteFields(this->FieldsToWrite);
1295 
1296   this->IsInitialized = true;
1297 }
1298 
1299 
1300 } // end namespace io
1301 } // end namespace fides
1302