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