1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkExodusIIWriter.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 /*----------------------------------------------------------------------------
17  Copyright (c) Sandia Corporation
18  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
19 ----------------------------------------------------------------------------*/
20 
21 #include "vtkExodusIIWriter.h"
22 #include "vtkArrayIteratorIncludes.h"
23 #include "vtkCellArray.h"
24 #include "vtkCellData.h"
25 #include "vtkCompositeDataIterator.h"
26 #include "vtkCompositeDataSet.h"
27 #include "vtkDataObject.h"
28 #include "vtkDataObjectTreeIterator.h"
29 #include "vtkDoubleArray.h"
30 #include "vtkFieldData.h"
31 #include "vtkFloatArray.h"
32 #include "vtkIdList.h"
33 #include "vtkInformation.h"
34 #include "vtkInformationVector.h"
35 #include "vtkIntArray.h"
36 #include "vtkModelMetadata.h"
37 #include "vtkMultiBlockDataSet.h"
38 #include "vtkNew.h"
39 #include "vtkObjectFactory.h"
40 #include "vtkPlatform.h" // for VTK_MAXPATH
41 #include "vtkPointData.h"
42 #include "vtkStdString.h"
43 #include "vtkStreamingDemandDrivenPipeline.h"
44 #include "vtkStringArray.h"
45 #include "vtkThreshold.h"
46 #include "vtkUnstructuredGrid.h"
47 
48 #include "vtk_exodusII.h"
49 #include <cctype>
50 #include <ctime>
51 #include <map>
52 #include <sstream>
53 
54 vtkObjectFactoryNewMacro(vtkExodusIIWriter);
55 vtkCxxSetObjectMacro(vtkExodusIIWriter, ModelMetadata, vtkModelMetadata);
56 
57 namespace
58 {
GetNumberOfDigits(unsigned int i)59 unsigned int GetNumberOfDigits(unsigned int i)
60 {
61   if (i < 10)
62   {
63     return 1;
64   }
65   return GetNumberOfDigits(i / 10) + 1;
66 }
67 }
68 
69 //------------------------------------------------------------------------------
70 
vtkExodusIIWriter()71 vtkExodusIIWriter::vtkExodusIIWriter()
72 {
73   this->fid = -1;
74   this->FileName = nullptr;
75 
76   this->StoreDoubles = -1;
77   this->GhostLevel = 0;
78   this->WriteOutBlockIdArray = 0;
79   this->WriteOutGlobalElementIdArray = 0;
80   this->WriteOutGlobalNodeIdArray = 0;
81   this->WriteAllTimeSteps = 0;
82   this->BlockIdArrayName = nullptr;
83   this->ModelMetadata = nullptr;
84 
85   this->NumberOfTimeSteps = 0;
86   this->CurrentTimeIndex = 0;
87   this->FileTimeOffset = 0;
88 
89   this->AtLeastOneGlobalElementIdList = 0;
90   this->AtLeastOneGlobalNodeIdList = 0;
91 
92   this->NumPoints = 0;
93   this->NumCells = 0;
94 
95   this->PassDoubles = 1;
96 
97   this->BlockElementVariableTruthTable = nullptr;
98 
99   this->LocalNodeIdMap = nullptr;
100   this->LocalElementIdMap = nullptr;
101   this->TopologyChanged = false;
102   this->IgnoreMetaDataWarning = false;
103 }
104 
~vtkExodusIIWriter()105 vtkExodusIIWriter::~vtkExodusIIWriter()
106 {
107   this->SetModelMetadata(nullptr); // kill the reference if its there
108 
109   delete[] this->FileName;
110   delete[] this->BlockIdArrayName;
111 
112   delete[] this->BlockElementVariableTruthTable;
113 
114   for (size_t i = 0; i < this->BlockIdList.size(); i++)
115   {
116     this->BlockIdList[i]->UnRegister(this);
117   }
118 }
119 
PrintSelf(ostream & os,vtkIndent indent)120 void vtkExodusIIWriter::PrintSelf(ostream& os, vtkIndent indent)
121 {
122   this->Superclass::PrintSelf(os, indent);
123   os << indent << "FileName " << (this->FileName ? this->FileName : "(none)") << endl;
124   os << indent << "StoreDoubles " << this->StoreDoubles << endl;
125   os << indent << "GhostLevel " << this->GhostLevel << endl;
126   os << indent << "WriteOutBlockIdArray " << this->WriteOutBlockIdArray << endl;
127   os << indent << "WriteOutGlobalNodeIdArray " << this->WriteOutGlobalNodeIdArray << endl;
128   os << indent << "WriteOutGlobalElementIdArray " << this->WriteOutGlobalElementIdArray << endl;
129   os << indent << "WriteAllTimeSteps " << this->WriteAllTimeSteps << endl;
130   os << indent << "BlockIdArrayName "
131      << (this->BlockIdArrayName ? this->BlockIdArrayName : "(none)") << endl;
132   os << indent << "ModelMetadata " << (this->ModelMetadata ? "" : "(none)") << endl;
133   if (this->ModelMetadata)
134   {
135     this->ModelMetadata->PrintSelf(os, indent.GetNextIndent());
136   }
137   os << indent << "IgnoreMetaDataWarning " << this->IgnoreMetaDataWarning << endl;
138 }
139 
140 //------------------------------------------------------------------------------
ProcessRequest(vtkInformation * request,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)141 vtkTypeBool vtkExodusIIWriter::ProcessRequest(
142   vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
143 {
144   if (request->Has(vtkDemandDrivenPipeline::REQUEST_INFORMATION()))
145   {
146     return this->RequestInformation(request, inputVector, outputVector);
147   }
148   else if (request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_UPDATE_EXTENT()))
149   {
150     return this->RequestUpdateExtent(request, inputVector, outputVector);
151   }
152   // generate the data
153   else if (request->Has(vtkDemandDrivenPipeline::REQUEST_DATA()))
154   {
155     return this->RequestData(request, inputVector, outputVector);
156   }
157 
158   return this->Superclass::ProcessRequest(request, inputVector, outputVector);
159 }
160 
161 //------------------------------------------------------------------------------
RequestInformation(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * vtkNotUsed (outputVector))162 int vtkExodusIIWriter::RequestInformation(vtkInformation* vtkNotUsed(request),
163   vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector))
164 {
165   vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
166   if (inInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_STEPS()))
167   {
168     this->NumberOfTimeSteps = inInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
169   }
170   else
171   {
172     this->NumberOfTimeSteps = 0;
173   }
174 
175   return 1;
176 }
177 
178 //------------------------------------------------------------------------------
RequestUpdateExtent(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * vtkNotUsed (outputVector))179 int vtkExodusIIWriter::RequestUpdateExtent(vtkInformation* vtkNotUsed(request),
180   vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector))
181 {
182   vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
183   if (this->WriteAllTimeSteps && inInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_STEPS()))
184   {
185     double* timeSteps = inInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
186     double timeReq = timeSteps[this->CurrentTimeIndex];
187     inputVector[0]->GetInformationObject(0)->Set(
188       vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP(), timeReq);
189   }
190   return 1;
191 }
192 
193 //------------------------------------------------------------------------------
FillInputPortInformation(int vtkNotUsed (port),vtkInformation * info)194 int vtkExodusIIWriter::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info)
195 {
196   info->Remove(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE());
197   info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
198   info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkCompositeDataSet");
199   return 1;
200 }
201 
202 //------------------------------------------------------------------------------
RequestData(vtkInformation * request,vtkInformationVector ** inputVector,vtkInformationVector * vtkNotUsed (outputVector))203 int vtkExodusIIWriter::RequestData(vtkInformation* request, vtkInformationVector** inputVector,
204   vtkInformationVector* vtkNotUsed(outputVector))
205 {
206   if (!this->FileName)
207   {
208     return 1;
209   }
210 
211   vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
212   this->OriginalInput = vtkDataObject::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
213 
214   // is this the first request
215   if (this->CurrentTimeIndex == 0 && this->WriteAllTimeSteps)
216   {
217     // Tell the pipeline to start looping.
218     request->Set(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING(), 1);
219   }
220 
221   this->WriteData();
222 
223   this->CurrentTimeIndex++;
224   if (this->CurrentTimeIndex >= this->NumberOfTimeSteps || this->TopologyChanged)
225   {
226     this->CloseExodusFile();
227     this->CurrentTimeIndex = 0;
228     if (this->WriteAllTimeSteps)
229     {
230       // Tell the pipeline to stop looping.
231       request->Set(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING(), 0);
232     }
233   }
234   // still close out the file after each step written.
235   if (!this->WriteAllTimeSteps)
236   {
237     this->CloseExodusFile();
238   }
239 
240   int localContinue = request->Get(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING());
241   if (this->GlobalContinueExecuting(localContinue) != localContinue)
242   {
243     // Some other node decided to stop the execution.
244     assert(localContinue == 1);
245     request->Set(vtkStreamingDemandDrivenPipeline::CONTINUE_EXECUTING(), 0);
246   }
247   return 1;
248 }
249 
250 //------------------------------------------------------------------------------
GlobalContinueExecuting(int localContinueExecution)251 int vtkExodusIIWriter::GlobalContinueExecuting(int localContinueExecution)
252 {
253   return localContinueExecution;
254 }
255 
256 //------------------------------------------------------------------------------
WriteData()257 void vtkExodusIIWriter::WriteData()
258 {
259   this->NewFlattenedInput.clear();
260   this->NewFlattenedNames.clear();
261   // Is it safe to assume this is the same?
262   bool newHierarchy = false;
263   if (!this->FlattenHierarchy(this->OriginalInput, "", newHierarchy))
264   {
265     vtkErrorMacro("vtkExodusIIWriter::WriteData Unable to flatten hierarchy");
266     return;
267   }
268   if (this->FlattenedInput.size() != this->NewFlattenedInput.size())
269   {
270     newHierarchy = true;
271   }
272 
273   // For now, we don't support changing topology even if the writer supports it.
274   // The reader needs to be updated to support changing topology.
275   if (newHierarchy && !this->FlattenedInput.empty())
276   {
277     this->TopologyChanged = true;
278     vtkErrorMacro("Temporal data with changing topology is not yet supported.");
279     return;
280   }
281 
282   // Copies over the new results data in the new objects
283   this->FlattenedInput = this->NewFlattenedInput;
284   this->FlattenedNames = this->NewFlattenedNames;
285 
286   this->RemoveGhostCells();
287 
288   // move check parameters up here and then if there's a change, new file.
289   if (this->WriteAllTimeSteps && this->CurrentTimeIndex != 0 && !newHierarchy)
290   {
291     if (!this->WriteNextTimeStep())
292     {
293       vtkErrorMacro("vtkExodusIIWriter::WriteData results");
294     }
295     return;
296   }
297   else
298   {
299     // Close out the old file, if we have one
300     if (this->CurrentTimeIndex > 0)
301     {
302       this->CloseExodusFile();
303     }
304 
305     // The file has changed, initialize new file
306     if (!this->CheckParameters())
307     {
308       return;
309     }
310 
311     // TODO this should increment a counter
312     if (!this->CreateNewExodusFile())
313     {
314       vtkErrorMacro("vtkExodusIIWriter: WriteData can't create exodus file");
315       return;
316     }
317 
318     if (!this->WriteInitializationParameters())
319     {
320       vtkErrorMacro(<< "vtkExodusIIWriter::WriteData init params");
321       return;
322     }
323 
324     if (!this->WriteInformationRecords())
325     {
326       vtkErrorMacro("vtkExodusIIWriter::WriteData information records");
327       return;
328     }
329 
330     if (!this->WritePoints())
331     {
332       vtkErrorMacro("vtkExodusIIWriter::WriteData points");
333       return;
334     }
335 
336     if (!this->WriteCoordinateNames())
337     {
338       vtkErrorMacro("vtkExodusIIWriter::WriteData coordinate names");
339       return;
340     }
341 
342     if (!this->WriteGlobalPointIds())
343     {
344       vtkErrorMacro("vtkExodusIIWriter::WriteData global point IDs");
345       return;
346     }
347 
348     if (!this->WriteBlockInformation())
349     {
350       vtkErrorMacro("vtkExodusIIWriter::WriteData block information");
351       return;
352     }
353 
354     if (!this->WriteGlobalElementIds())
355     {
356       vtkErrorMacro("vtkExodusIIWriter::WriteData global element IDs");
357       return;
358     }
359 
360     if (!this->WriteVariableArrayNames())
361     {
362       vtkErrorMacro("vtkExodusIIWriter::WriteData variable array names");
363       return;
364     }
365 
366     if (!this->WriteNodeSetInformation())
367     {
368       vtkErrorMacro("vtkExodusIIWriter::WriteData can't node sets");
369       return;
370     }
371 
372     if (!this->WriteSideSetInformation())
373     {
374       vtkErrorMacro("vtkExodusIIWriter::WriteData can't side sets");
375       return;
376     }
377 
378     if (!this->WriteProperties())
379     {
380       vtkErrorMacro("vtkExodusIIWriter::WriteData can't properties");
381       return;
382     }
383 
384     if (!this->WriteNextTimeStep())
385     {
386       vtkErrorMacro("vtkExodusIIWriter::WriteData results");
387       return;
388     }
389   }
390 }
391 
392 //------------------------------------------------------------------------------
StrDupWithNew(const char * s)393 char* vtkExodusIIWriter::StrDupWithNew(const char* s)
394 {
395   char* newstr = nullptr;
396 
397   if (s)
398   {
399     size_t len = strlen(s);
400     newstr = new char[len + 1];
401     strcpy(newstr, s);
402   }
403 
404   return newstr;
405 }
406 
407 //------------------------------------------------------------------------------
StringUppercase(std::string & str)408 void vtkExodusIIWriter::StringUppercase(std::string& str)
409 {
410   for (size_t i = 0; i < str.size(); i++)
411   {
412     str[i] = toupper(str[i]);
413   }
414 }
415 
416 //------------------------------------------------------------------------------
FlattenHierarchy(vtkDataObject * input,const char * name,bool & changed)417 int vtkExodusIIWriter::FlattenHierarchy(vtkDataObject* input, const char* name, bool& changed)
418 {
419   if (input->IsA("vtkMultiBlockDataSet"))
420   {
421     vtkMultiBlockDataSet* castObj = vtkMultiBlockDataSet::SafeDownCast(input);
422     vtkSmartPointer<vtkDataObjectTreeIterator> iter;
423     iter.TakeReference(castObj->NewTreeIterator());
424     iter->VisitOnlyLeavesOff();
425     iter->TraverseSubTreeOff();
426     iter->SkipEmptyNodesOff();
427     for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
428     {
429       name = iter->GetCurrentMetaData()->Get(vtkCompositeDataSet::NAME());
430       if (name != nullptr && strstr(name, "Sets") != nullptr)
431       {
432         continue;
433       }
434       if (name == nullptr)
435       {
436         // avoid null references in StdString
437         name = "";
438       }
439       if (iter->GetCurrentDataObject() &&
440         !this->FlattenHierarchy(iter->GetCurrentDataObject(), name, changed))
441       {
442         return 0;
443       }
444     }
445   }
446   else if (input->IsA("vtkCompositeDataSet"))
447   {
448     vtkCompositeDataSet* castObj = vtkCompositeDataSet::SafeDownCast(input);
449     vtkSmartPointer<vtkCompositeDataIterator> iter;
450     iter.TakeReference(castObj->NewIterator());
451     vtkDataObjectTreeIterator* treeIter = vtkDataObjectTreeIterator::SafeDownCast(castObj);
452     if (treeIter)
453     {
454       treeIter->VisitOnlyLeavesOff();
455       treeIter->TraverseSubTreeOff();
456       treeIter->SkipEmptyNodesOff();
457     }
458     for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
459     {
460       if (iter->GetCurrentDataObject() &&
461         !this->FlattenHierarchy(iter->GetCurrentDataObject(), name, changed))
462       {
463         return 0;
464       }
465     }
466   }
467   else if (input->IsA("vtkDataSet"))
468   {
469     vtkSmartPointer<vtkUnstructuredGrid> output = vtkSmartPointer<vtkUnstructuredGrid>::New();
470     if (input->IsA("vtkUnstructuredGrid"))
471     {
472       output->ShallowCopy(input);
473     }
474     else
475     {
476       vtkDataSet* castObj = vtkDataSet::SafeDownCast(input);
477 
478       output->GetFieldData()->ShallowCopy(castObj->GetFieldData());
479       output->GetPointData()->ShallowCopy(castObj->GetPointData());
480       output->GetCellData()->ShallowCopy(castObj->GetCellData());
481 
482       vtkIdType numPoints = castObj->GetNumberOfPoints();
483       vtkSmartPointer<vtkPoints> outPoints = vtkSmartPointer<vtkPoints>::New();
484       outPoints->SetNumberOfPoints(numPoints);
485       for (vtkIdType i = 0; i < numPoints; i++)
486       {
487         outPoints->SetPoint(i, castObj->GetPoint(i));
488       }
489       output->SetPoints(outPoints);
490 
491       int numCells = castObj->GetNumberOfCells();
492       output->Allocate(numCells);
493       vtkIdList* ptIds = vtkIdList::New();
494       for (int i = 0; i < numCells; i++)
495       {
496         castObj->GetCellPoints(i, ptIds);
497         output->InsertNextCell(castObj->GetCellType(i), ptIds);
498       }
499       ptIds->Delete();
500     }
501     // check to see if we need a new exodus file
502     // because the element or node count needs to be changed
503     size_t checkIndex = this->NewFlattenedInput.size();
504     if (this->FlattenedInput.size() > checkIndex)
505     {
506       int numPoints = this->FlattenedInput[checkIndex]->GetNumberOfPoints();
507       int numCells = this->FlattenedInput[checkIndex]->GetNumberOfCells();
508       if (numPoints != output->GetNumberOfPoints() || numCells != output->GetNumberOfCells())
509       {
510         changed = true;
511       }
512     }
513     else
514     {
515       changed = true;
516     }
517     this->NewFlattenedInput.push_back(output);
518     if (!name)
519     {
520       // Setting an arbitrary name for datasets that have not been assigned one.
521       name = "block";
522     }
523     this->NewFlattenedNames.emplace_back(name);
524   }
525   else
526   {
527     vtkErrorMacro(<< "Incorrect class type " << input->GetClassName() << " on input");
528     return 0;
529   }
530   return 1;
531 }
532 
533 //------------------------------------------------------------------------------
CreateNewExodusFile()534 int vtkExodusIIWriter::CreateNewExodusFile()
535 {
536   int compWordSize = (this->PassDoubles ? sizeof(double) : sizeof(float));
537   int IOWordSize = (this->StoreDoubles ? sizeof(double) : sizeof(float));
538 
539   if (this->NumberOfProcesses == 1)
540   {
541     if (this->WriteAllTimeSteps == false || this->CurrentTimeIndex == 0)
542     {
543       this->fid = ex_create(this->FileName, EX_CLOBBER, &compWordSize, &IOWordSize);
544       if (fid <= 0)
545       {
546         vtkErrorMacro(<< "vtkExodusIIWriter: CreateNewExodusFile can't create " << this->FileName);
547       }
548     }
549     else
550     {
551       char* myFileName = new char[VTK_MAXPATH];
552       snprintf(myFileName, VTK_MAXPATH, "%s-s.%06d", this->FileName, this->CurrentTimeIndex);
553       this->fid = ex_create(myFileName, EX_CLOBBER, &compWordSize, &IOWordSize);
554       if (fid <= 0)
555       {
556         vtkErrorMacro(<< "vtkExodusIIWriter: CreateNewExodusFile can't create " << myFileName);
557       }
558       delete[] myFileName;
559     }
560   }
561   else
562   {
563     std::ostringstream myFileName;
564     myFileName << this->FileName;
565     if (this->WriteAllTimeSteps == false || this->CurrentTimeIndex == 0)
566     {
567       myFileName << ".";
568     }
569     else
570     {
571       myFileName << "-s." << std::setfill('0') << std::setw(6) << this->CurrentTimeIndex
572                  << std::setw(0) << ".";
573     }
574     unsigned int numDigits =
575       GetNumberOfDigits(static_cast<unsigned int>(this->NumberOfProcesses - 1));
576     myFileName << this->NumberOfProcesses << "." << std::setfill('0') << std::setw(numDigits)
577                << this->MyRank;
578     this->fid = ex_create(myFileName.str().c_str(), EX_CLOBBER, &compWordSize, &IOWordSize);
579     if (this->fid <= 0)
580     {
581       vtkErrorMacro(<< "vtkExodusIIWriter: CreateNewExodusFile can't create " << myFileName.str());
582     }
583   }
584   ex_set_max_name_length(this->fid, static_cast<int>(this->GetMaxNameLength()));
585 
586   // FileTimeOffset makes the time in the file relative
587   // e.g., if the CurrentTimeIndex for this file is 4 and goes through 6, the
588   // file will write them as 0 1 2 instead of 4 5 6
589   this->FileTimeOffset = this->CurrentTimeIndex;
590   return this->fid > 0;
591 }
592 //
593 //------------------------------------------------------------------
CloseExodusFile()594 void vtkExodusIIWriter::CloseExodusFile()
595 {
596   if (this->fid >= 0)
597   {
598     ex_close(this->fid);
599     this->fid = -1;
600     return;
601   }
602 }
603 
604 //------------------------------------------------------------------------------
IsDouble()605 int vtkExodusIIWriter::IsDouble()
606 {
607   // Determine whether we should pass single or double precision
608   // floats to the Exodus Library.  We'll look through the arrays
609   // and points in the input and pick the precision of the
610   // first float we see.
611 
612   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
613   {
614 
615     vtkCellData* cd = this->FlattenedInput[i]->GetCellData();
616     if (cd)
617     {
618       int numCellArrays = cd->GetNumberOfArrays();
619       for (int j = 0; j < numCellArrays; j++)
620       {
621         vtkDataArray* a = cd->GetArray(j);
622         int type = a->GetDataType();
623 
624         if (type == VTK_DOUBLE)
625         {
626           return 1;
627         }
628         else if (type == VTK_FLOAT)
629         {
630           return 0;
631         }
632       }
633     }
634 
635     vtkPointData* pd = this->FlattenedInput[i]->GetPointData();
636     if (pd)
637     {
638       int numPtArrays = pd->GetNumberOfArrays();
639       for (int j = 0; j < numPtArrays; j++)
640       {
641         vtkDataArray* a = pd->GetArray(j);
642         int type = a->GetDataType();
643 
644         if (type == VTK_DOUBLE)
645         {
646           return 1;
647         }
648         else if (type == VTK_FLOAT)
649         {
650           return 0;
651         }
652       }
653     }
654   }
655   return -1;
656 }
657 
658 //------------------------------------------------------------------------------
RemoveGhostCells()659 void vtkExodusIIWriter::RemoveGhostCells()
660 {
661   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
662   {
663     vtkUnsignedCharArray* da = this->FlattenedInput[i]->GetCellGhostArray();
664 
665     if (da)
666     {
667       vtkThreshold* t = vtkThreshold::New();
668       t->SetInputData(this->FlattenedInput[i]);
669       t->SetThresholdFunction(vtkThreshold::THRESHOLD_LOWER);
670       t->SetLowerThreshold(0.0);
671       t->SetInputArrayToProcess(
672         0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_CELLS, vtkDataSetAttributes::GhostArrayName());
673 
674       t->Update();
675 
676       this->FlattenedInput[i] = vtkSmartPointer<vtkUnstructuredGrid>(t->GetOutput());
677       t->Delete();
678 
679       this->FlattenedInput[i]->GetCellData()->RemoveArray(vtkDataSetAttributes::GhostArrayName());
680       this->FlattenedInput[i]->GetPointData()->RemoveArray(vtkDataSetAttributes::GhostArrayName());
681 
682       this->GhostLevel = 1;
683     }
684     else
685     {
686       this->GhostLevel = 0;
687     }
688   }
689 }
690 
691 //------------------------------------------------------------------------------
CheckParametersInternal(int numberOfProcesses,int myRank)692 int vtkExodusIIWriter::CheckParametersInternal(int numberOfProcesses, int myRank)
693 {
694   if (!this->FileName)
695   {
696     vtkErrorMacro("No filename specified.");
697     return 0;
698   }
699 
700   this->PassDoubles = this->IsDouble();
701   if (this->PassDoubles < 0)
702   {
703     // Can't find float types in input, assume doubles
704     this->PassDoubles = 1;
705   }
706 
707   if (this->StoreDoubles < 0)
708   {
709     // The default is to store in the
710     // same precision that appears in the input.
711 
712     this->StoreDoubles = this->PassDoubles;
713   }
714 
715   this->NumberOfProcesses = numberOfProcesses;
716   this->MyRank = myRank;
717 
718   if (!this->CheckInputArrays())
719   {
720     return 0;
721   }
722 
723   if (!this->ConstructBlockInfoMap())
724   {
725     return 0;
726   }
727 
728   if (!this->ConstructVariableInfoMaps())
729   {
730     return 0;
731   }
732 
733   // Data (and possibly the topology) is different for every time step so
734   // always create a new metadata.
735   if (!this->CreateDefaultMetadata())
736   {
737     return 0;
738   }
739 
740   if (!this->ParseMetadata())
741   {
742     return 0;
743   }
744 
745   return 1;
746 }
747 
748 //------------------------------------------------------------------------------
CheckParameters()749 int vtkExodusIIWriter::CheckParameters()
750 {
751   return this->CheckParametersInternal(1, 0);
752 }
753 
CheckInputArrays()754 int vtkExodusIIWriter::CheckInputArrays()
755 {
756   this->BlockIdList.resize(this->FlattenedInput.size());
757   this->GlobalElementIdList.resize(this->FlattenedInput.size());
758   this->AtLeastOneGlobalElementIdList = 0;
759   this->GlobalNodeIdList.resize(this->FlattenedInput.size());
760   this->AtLeastOneGlobalNodeIdList = 0;
761 
762   this->NumPoints = 0;
763   this->NumCells = 0;
764   this->MaxId = 0;
765 
766   size_t i;
767   for (i = 0; i < this->FlattenedInput.size(); i++)
768   {
769     this->NumPoints += this->FlattenedInput[i]->GetNumberOfPoints();
770     int numCells = this->FlattenedInput[i]->GetNumberOfCells();
771     this->NumCells += numCells;
772 
773     vtkCellData* cd = this->FlattenedInput[i]->GetCellData();
774     vtkPointData* pd = this->FlattenedInput[i]->GetPointData();
775 
776     vtkIntArray* bia = this->GetBlockIdArray(this->BlockIdArrayName, this->FlattenedInput[i]);
777     if (bia)
778     {
779       this->BlockIdList[i] = bia;
780       this->BlockIdList[i]->Register(this);
781       // computing the max known id in order to create unique fill in values below
782       for (int j = 0; j < numCells; j++)
783       {
784         if (this->BlockIdList[i]->GetValue(j) > this->MaxId)
785         {
786           this->MaxId = this->BlockIdList[i]->GetValue(j);
787         }
788       }
789     }
790     else
791     {
792       // Will fill in below
793       this->BlockIdList[i] = nullptr;
794     }
795 
796     // Trying to find global element id
797     vtkDataArray* da = cd->GetGlobalIds();
798     if (!da)
799     { // try finding the array explicitly named
800       da = cd->GetArray("GlobalElementId");
801     }
802     if (da)
803     {
804       vtkIdTypeArray* ia = vtkArrayDownCast<vtkIdTypeArray>(da);
805       if (!ia)
806       {
807         vtkWarningMacro(<< "vtkExodusIIWriter, element ID array is not an Id array, ignoring it");
808         this->GlobalElementIdList[i] = nullptr;
809       }
810       else
811       {
812         this->GlobalElementIdList[i] = ia->GetPointer(0);
813         this->AtLeastOneGlobalElementIdList = 1;
814       }
815     }
816 
817     // Trying to find global node id
818     da = pd->GetGlobalIds();
819     if (!da)
820     { // try finding the array explicitly named
821       da = pd->GetArray("GlobalNodeId");
822     }
823     if (da)
824     {
825       vtkIdTypeArray* ia = vtkArrayDownCast<vtkIdTypeArray>(da);
826       if (!ia)
827       {
828         vtkWarningMacro(<< "vtkExodusIIWriter, node ID array is not an Id array, ignoring it");
829         this->GlobalNodeIdList[i] = nullptr;
830       }
831       else
832       {
833         this->GlobalNodeIdList[i] = ia->GetPointer(0);
834         this->AtLeastOneGlobalNodeIdList = 1;
835       }
836     }
837     else
838     {
839       this->GlobalNodeIdList[i] = nullptr;
840     }
841   }
842 
843   return 1;
844 }
845 
ConstructBlockInfoMap()846 int vtkExodusIIWriter::ConstructBlockInfoMap()
847 {
848   // The elements in the input may not be in order by block, but we must
849   // write element IDs and element variables out to the Exodus file in
850   // order by block.  Create a mapping if necessary, for an ordering by
851   // block to the ordering found in the input unstructured grid.
852   this->CellToElementOffset.resize(this->FlattenedInput.size());
853   this->BlockInfoMap.clear();
854   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
855   {
856     // If we weren't explicitly given the block ids, try to extract them from the
857     // block id array embedded in the cell data.
858     vtkIdType ncells = this->FlattenedInput[i]->GetNumberOfCells();
859     if (!this->BlockIdList[i])
860     {
861       vtkIntArray* ia = vtkIntArray::New();
862       ia->SetNumberOfValues(ncells);
863       for (int j = 0; j < ncells; j++)
864       {
865         ia->SetValue(j, this->FlattenedInput[i]->GetCellType(j) + this->MaxId);
866       }
867 
868       // Pretend we had it in the metadata
869       this->BlockIdList[i] = ia;
870       this->BlockIdList[i]->Register(this);
871       ia->Delete();
872 
873       // Also increment the MaxId so we can keep it unique
874       this->MaxId += VTK_NUMBER_OF_CELL_TYPES;
875     }
876 
877     // Compute all the block information mappings.
878     this->CellToElementOffset[i].resize(ncells);
879     for (int j = 0; j < ncells; j++)
880     {
881       std::map<int, Block>::iterator iter =
882         this->BlockInfoMap.find(this->BlockIdList[i]->GetValue(j));
883       // shift by 1 in case there's a 0  This was removed because it changes the user supplied block
884       // ids in the metadata.
885       if (iter == this->BlockInfoMap.end())
886       {
887         this->CellToElementOffset[i][j] = 0;
888         Block& b = this->BlockInfoMap[this->BlockIdList[i]->GetValue(j)]; // CLM
889         b.Name = this->FlattenedNames[i].c_str();
890         b.Type = this->FlattenedInput[i]->GetCellType(j);
891         b.NumElements = 1;
892         b.ElementStartIndex = 0;
893         switch (b.Type)
894         {
895           case VTK_POLY_LINE:
896           case VTK_POLYGON:
897           case VTK_POLYHEDRON:
898             // this block contains variable numbers of nodes per element
899             b.NodesPerElement = 0;
900             b.EntityCounts = std::vector<int>(ncells);
901             b.EntityCounts[0] = this->FlattenedInput[i]->GetCell(j)->GetNumberOfPoints();
902             b.EntityNodeOffsets = std::vector<int>(ncells);
903             b.EntityNodeOffsets[0] = 0;
904             break;
905           default:
906             b.NodesPerElement = this->FlattenedInput[i]->GetCell(j)->GetNumberOfPoints();
907         };
908 
909         // TODO this could be a push if i is different.
910         b.GridIndex = i;
911 
912         // This may get pulled from the meta data below,
913         // but if not, default reasonably to 0
914         b.NumAttributes = 0;
915         b.BlockAttributes = nullptr;
916       }
917       else
918       {
919         // TODO we should be able to deal with this, not just error out
920         if (iter->second.GridIndex != i)
921         {
922           vtkWarningMacro("Block ids are not unique across the hierarchy ");
923         }
924 
925         this->CellToElementOffset[i][j] = iter->second.NumElements;
926         int index = iter->second.NumElements;
927         if (iter->second.NodesPerElement == 0)
928         {
929           iter->second.EntityCounts[index] =
930             this->FlattenedInput[i]->GetCell(j)->GetNumberOfPoints();
931           iter->second.EntityNodeOffsets[index] =
932             iter->second.EntityNodeOffsets[index - 1] + iter->second.EntityCounts[index - 1];
933         }
934         iter->second.NumElements++;
935       }
936     }
937   }
938 
939   this->CheckBlockInfoMap();
940 
941   // Find the ElementStartIndex and the output order
942   std::map<int, Block>::iterator iter;
943   int runningCount = 0;
944   int index = 0;
945   for (iter = this->BlockInfoMap.begin(); iter != this->BlockInfoMap.end(); ++iter)
946   {
947     iter->second.ElementStartIndex = runningCount;
948     runningCount += iter->second.NumElements;
949 
950     iter->second.OutputIndex = index;
951     index++;
952   }
953 
954   return 1;
955 }
956 
ConstructVariableInfoMaps()957 int vtkExodusIIWriter::ConstructVariableInfoMaps()
958 {
959   // Create the variable info maps.
960   if (this->GlobalVariableMap.empty())
961   {
962     this->NumberOfScalarGlobalArrays = 0;
963   }
964   this->NumberOfScalarElementArrays = 0;
965   this->NumberOfScalarNodeArrays = 0;
966   this->BlockVariableMap.clear();
967   this->NodeVariableMap.clear();
968   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
969   {
970     vtkFieldData* fd = this->FlattenedInput[i]->GetFieldData();
971     for (int j = 0; j < fd->GetNumberOfArrays(); j++)
972     {
973       char* name = nullptr;
974       if (fd->GetAbstractArray(j))
975       {
976         name = fd->GetAbstractArray(j)->GetName();
977       }
978       if (name == nullptr)
979       {
980         vtkWarningMacro("Array in input field data has Null name, cannot output it");
981         continue;
982       }
983       std::string upper(name);
984       this->StringUppercase(upper);
985       if (strncmp(upper.c_str(), "TITLE", 5) == 0)
986       {
987         continue;
988       }
989       if (strncmp(upper.c_str(), "QA_RECORD", 9) == 0)
990       {
991         continue;
992       }
993       if (strncmp(upper.c_str(), "INFO_RECORD", 11) == 0)
994       {
995         continue;
996       }
997       if (strncmp(upper.c_str(), "ELEMENTBLOCKIDS", 15) == 0)
998       {
999         continue;
1000       }
1001       int numComp = fd->GetAbstractArray(j)->GetNumberOfComponents();
1002       std::map<std::string, VariableInfo>::const_iterator iter = this->GlobalVariableMap.find(name);
1003       if (iter == this->GlobalVariableMap.end())
1004       {
1005         VariableInfo& info = this->GlobalVariableMap[name];
1006         info.NumComponents = numComp;
1007         info.OutNames.resize(info.NumComponents);
1008         info.ScalarOutOffset = this->NumberOfScalarGlobalArrays;
1009         this->NumberOfScalarGlobalArrays += numComp;
1010       }
1011       else if (iter->second.NumComponents != numComp)
1012       {
1013         vtkErrorMacro("Disagreement in the hierarchy for the number of components in " << name);
1014         return 0;
1015       }
1016     }
1017 
1018     vtkCellData* cd = this->FlattenedInput[i]->GetCellData();
1019     for (int j = 0; j < cd->GetNumberOfArrays(); j++)
1020     {
1021       char* name = nullptr;
1022       if (cd->GetArray(j))
1023       {
1024         name = cd->GetArray(j)->GetName();
1025       }
1026       if (name == nullptr)
1027       {
1028         vtkWarningMacro("Array in input cell data has Null name, cannot output it");
1029         continue;
1030       }
1031       std::string upper(name);
1032       this->StringUppercase(upper);
1033 
1034       if (!this->WriteOutGlobalElementIdArray &&
1035         cd->IsArrayAnAttribute(j) == vtkDataSetAttributes::GLOBALIDS)
1036       {
1037         continue;
1038       }
1039       if (!this->WriteOutBlockIdArray && this->BlockIdArrayName &&
1040         strcmp(name, this->BlockIdArrayName) == 0)
1041       {
1042         continue;
1043       }
1044       if (strncmp(upper.c_str(), "PEDIGREE", 8) == 0)
1045       {
1046         continue;
1047       }
1048 
1049       int numComp = cd->GetArray(j)->GetNumberOfComponents();
1050       std::map<std::string, VariableInfo>::const_iterator iter = this->BlockVariableMap.find(name);
1051       if (iter == this->BlockVariableMap.end())
1052       {
1053         VariableInfo& info = this->BlockVariableMap[name];
1054         info.NumComponents = numComp;
1055         info.OutNames.resize(info.NumComponents);
1056         info.ScalarOutOffset = this->NumberOfScalarElementArrays;
1057         this->NumberOfScalarElementArrays += numComp;
1058       }
1059       else if (iter->second.NumComponents != numComp)
1060       {
1061         vtkErrorMacro("Disagreement in the hierarchy for the number of components in " << name);
1062         return 0;
1063       }
1064     }
1065 
1066     vtkPointData* pd = this->FlattenedInput[i]->GetPointData();
1067     for (int j = 0; j < pd->GetNumberOfArrays(); j++)
1068     {
1069       char* name = nullptr;
1070       if (pd->GetArray(j))
1071       {
1072         name = pd->GetArray(j)->GetName();
1073       }
1074       if (name == nullptr)
1075       {
1076         vtkWarningMacro("Array in input point data has Null name, cannot output it");
1077         continue;
1078       }
1079       std::string upper(name);
1080       this->StringUppercase(upper);
1081 
1082       // Is this array displacement?
1083       // If it is and we are not writing all the timesteps,
1084       // do not write out. It would mess up the geometry the
1085       // next time the file was read in.
1086       if (!this->WriteOutGlobalNodeIdArray &&
1087         pd->IsArrayAnAttribute(j) == vtkDataSetAttributes::GLOBALIDS)
1088       {
1089         continue;
1090       }
1091       if (strncmp(upper.c_str(), "PEDIGREE", 8) == 0)
1092       {
1093         continue;
1094       }
1095       // Is this array displacement?
1096       // If it is and we are not writing all the timesteps,
1097       // do not write out. It would mess up the geometry the
1098       // next time the file was read in.
1099       if (!this->WriteAllTimeSteps && strncmp(upper.c_str(), "DIS", 3) == 0)
1100       {
1101         continue;
1102       }
1103 
1104       int numComp = pd->GetArray(j)->GetNumberOfComponents();
1105       std::map<std::string, VariableInfo>::const_iterator iter = this->NodeVariableMap.find(name);
1106       if (iter == this->NodeVariableMap.end())
1107       {
1108         VariableInfo& info = this->NodeVariableMap[name];
1109         info.NumComponents = numComp;
1110         info.OutNames.resize(info.NumComponents);
1111         info.ScalarOutOffset = this->NumberOfScalarNodeArrays;
1112         this->NumberOfScalarNodeArrays += info.NumComponents;
1113       }
1114       else if (iter->second.NumComponents != numComp)
1115       {
1116         vtkErrorMacro("Disagreement in the hierarchy for the number of components in " << name);
1117         return 0;
1118       }
1119     }
1120   }
1121 
1122   // BLOCK/ELEMENT TRUTH TABLE
1123   size_t ttsize = this->BlockInfoMap.size() * this->NumberOfScalarElementArrays;
1124   if (ttsize > 0)
1125   {
1126     this->BlockElementVariableTruthTable = new int[ttsize];
1127     int index = 0;
1128     std::map<int, Block>::const_iterator blockIter;
1129     for (blockIter = this->BlockInfoMap.begin(); blockIter != this->BlockInfoMap.end(); ++blockIter)
1130     {
1131       std::map<std::string, VariableInfo>::const_iterator varIter;
1132       for (varIter = this->BlockVariableMap.begin(); varIter != this->BlockVariableMap.end();
1133            ++varIter)
1134       {
1135         vtkCellData* cd = this->FlattenedInput[blockIter->second.GridIndex]->GetCellData();
1136         int truth = 0;
1137         if (cd)
1138         {
1139           truth = 1;
1140         }
1141         for (int c = 0; c < varIter->second.NumComponents; c++)
1142         {
1143           this->BlockElementVariableTruthTable[index++] = truth;
1144         }
1145       }
1146     }
1147   }
1148 
1149   return 1;
1150 }
1151 
1152 //------------------------------------------------------------------------------
CreateDefaultMetadata()1153 int vtkExodusIIWriter::CreateDefaultMetadata()
1154 {
1155   // There is no metadata associated with this input.  If we have enough
1156   // information, we create reasonable defaults.
1157 
1158   vtkModelMetadata* em = vtkModelMetadata::New();
1159 
1160   char* title = new char[MAX_LINE_LENGTH + 1];
1161   time_t currentTime = time(nullptr);
1162   char* stime = ctime(&currentTime);
1163 
1164   snprintf(title, MAX_LINE_LENGTH + 1, "Created by vtkExodusIIWriter, %s", stime);
1165 
1166   em->SetTitle(title);
1167 
1168   delete[] title;
1169 
1170   char** dimNames = new char*[3];
1171   dimNames[0] = vtkExodusIIWriter::StrDupWithNew("X");
1172   dimNames[1] = vtkExodusIIWriter::StrDupWithNew("Y");
1173   dimNames[2] = vtkExodusIIWriter::StrDupWithNew("Z");
1174   em->SetCoordinateNames(3, dimNames);
1175 
1176   if (!this->CreateBlockIdMetadata(em))
1177   {
1178     return 0;
1179   }
1180 
1181   if (!this->CreateBlockVariableMetadata(em))
1182   {
1183     return 0;
1184   }
1185 
1186   this->CreateSetsMetadata(em);
1187   this->SetModelMetadata(em);
1188   em->Delete();
1189 
1190   return 1;
1191 }
1192 
1193 //------------------------------------------------------------------------------
GetCellTypeName(int t)1194 char* vtkExodusIIWriter::GetCellTypeName(int t)
1195 {
1196   if (MAX_STR_LENGTH < 32)
1197     return nullptr;
1198   char* nm = new char[MAX_STR_LENGTH + 1];
1199 
1200   switch (t)
1201   {
1202     case VTK_EMPTY_CELL:
1203       strcpy(nm, "empty cell");
1204       break;
1205     case VTK_VERTEX:
1206       strcpy(nm, "sphere");
1207       break;
1208     case VTK_POLY_VERTEX:
1209       strcpy(nm, "sup");
1210       break;
1211     case VTK_LINE:
1212       strcpy(nm, "edge");
1213       break;
1214     case VTK_POLY_LINE:
1215       strcpy(nm, "NSIDED");
1216       break;
1217     case VTK_TRIANGLE:
1218       strcpy(nm, "TRIANGLE");
1219       break;
1220     case VTK_TRIANGLE_STRIP:
1221       strcpy(nm, "TRIANGLE");
1222       break;
1223     case VTK_POLYGON:
1224       strcpy(nm, "NSIDED");
1225       break;
1226     case VTK_POLYHEDRON:
1227       strcpy(nm, "NFACED");
1228       break;
1229     case VTK_PIXEL:
1230       strcpy(nm, "sphere");
1231       break;
1232     case VTK_QUAD:
1233       strcpy(nm, "quad");
1234       break;
1235     case VTK_TETRA:
1236       strcpy(nm, "TETRA");
1237       break;
1238     case VTK_VOXEL:
1239       strcpy(nm, "HEX");
1240       break;
1241     case VTK_HEXAHEDRON:
1242       strcpy(nm, "HEX");
1243       break;
1244     case VTK_WEDGE:
1245       strcpy(nm, "wedge");
1246       break;
1247     case VTK_PYRAMID:
1248       strcpy(nm, "pyramid");
1249       break;
1250     case VTK_PENTAGONAL_PRISM:
1251       strcpy(nm, "pentagonal prism");
1252       break;
1253     case VTK_HEXAGONAL_PRISM:
1254       strcpy(nm, "hexagonal prism");
1255       break;
1256     case VTK_QUADRATIC_EDGE:
1257       strcpy(nm, "edge");
1258       break;
1259     case VTK_QUADRATIC_TRIANGLE:
1260       strcpy(nm, "triangle");
1261       break;
1262     case VTK_QUADRATIC_QUAD:
1263       strcpy(nm, "quad");
1264       break;
1265     case VTK_QUADRATIC_TETRA:
1266       strcpy(nm, "tetra");
1267       break;
1268     case VTK_QUADRATIC_HEXAHEDRON:
1269       strcpy(nm, "hexahedron");
1270       break;
1271     case VTK_QUADRATIC_WEDGE:
1272       strcpy(nm, "wedge");
1273       break;
1274     case VTK_QUADRATIC_PYRAMID:
1275       strcpy(nm, "pyramid");
1276       break;
1277     case VTK_CONVEX_POINT_SET:
1278       strcpy(nm, "convex point set");
1279       break;
1280     case VTK_PARAMETRIC_CURVE:
1281       strcpy(nm, "parametric curve");
1282       break;
1283     case VTK_PARAMETRIC_SURFACE:
1284       strcpy(nm, "parametric surface");
1285       break;
1286     case VTK_PARAMETRIC_TRI_SURFACE:
1287       strcpy(nm, "parametric tri surface");
1288       break;
1289     case VTK_PARAMETRIC_QUAD_SURFACE:
1290       strcpy(nm, "parametric quad surface");
1291       break;
1292     case VTK_PARAMETRIC_TETRA_REGION:
1293       strcpy(nm, "parametric tetra region");
1294       break;
1295     case VTK_PARAMETRIC_HEX_REGION:
1296       strcpy(nm, "paramertric hex region");
1297       break;
1298     default:
1299       strcpy(nm, "unknown cell type");
1300       break;
1301   }
1302 
1303   return nm;
1304 }
1305 
1306 //------------------------------------------------------------------------------
CreateBlockIdMetadata(vtkModelMetadata * em)1307 int vtkExodusIIWriter::CreateBlockIdMetadata(vtkModelMetadata* em)
1308 {
1309   // vtkModelMetadata frees the memory when its done so we need to create a copy
1310   size_t nblocks = this->BlockInfoMap.size();
1311   if (nblocks < 1)
1312     return 1;
1313   em->SetNumberOfBlocks(static_cast<int>(nblocks));
1314 
1315   int* blockIds = new int[nblocks];
1316   char** blockNames = new char*[nblocks];
1317   int* numElements = new int[nblocks];
1318   int* numNodesPerElement = new int[nblocks];
1319   int* numAttributes = new int[nblocks];
1320 
1321   std::map<int, Block>::const_iterator iter;
1322   for (iter = this->BlockInfoMap.begin(); iter != this->BlockInfoMap.end(); ++iter)
1323   {
1324     int index = iter->second.OutputIndex;
1325     blockIds[index] = iter->first;
1326     blockNames[index] = vtkExodusIIWriter::GetCellTypeName(iter->second.Type);
1327     numElements[index] = iter->second.NumElements;
1328     numNodesPerElement[index] = iter->second.NodesPerElement;
1329     numAttributes[index] = 0;
1330   }
1331   em->SetBlockIds(blockIds);
1332   em->SetBlockElementType(blockNames);
1333   em->SetBlockNumberOfElements(numElements);
1334   em->SetBlockNodesPerElement(numNodesPerElement);
1335   em->SetBlockNumberOfAttributesPerElement(numAttributes);
1336   return 1;
1337 }
1338 
1339 //------------------------------------------------------------------------------
CreateBlockVariableMetadata(vtkModelMetadata * em)1340 int vtkExodusIIWriter::CreateBlockVariableMetadata(vtkModelMetadata* em)
1341 {
1342   size_t narrays = this->GlobalVariableMap.size();
1343   char** flattenedNames = nullptr;
1344   if (narrays > 0)
1345   {
1346     flattenedNames = vtkExodusIIWriter::FlattenOutVariableNames(
1347       this->NumberOfScalarGlobalArrays, this->GlobalVariableMap);
1348     em->SetGlobalVariableNames(this->NumberOfScalarGlobalArrays, flattenedNames);
1349   }
1350 
1351   narrays = this->BlockVariableMap.size();
1352   char** nms = nullptr;
1353   if (narrays > 0)
1354   {
1355     nms = new char*[narrays];
1356     int* numComponents = new int[narrays];
1357     int* scalarIndex = new int[narrays];
1358 
1359     int index = 0;
1360     std::map<std::string, VariableInfo>::const_iterator var_iter;
1361     for (var_iter = this->BlockVariableMap.begin(); var_iter != this->BlockVariableMap.end();
1362          ++var_iter)
1363     {
1364       nms[index] = vtkExodusIIWriter::StrDupWithNew(var_iter->first.c_str());
1365       numComponents[index] = var_iter->second.NumComponents;
1366       scalarIndex[index] = var_iter->second.ScalarOutOffset;
1367       index++;
1368     }
1369 
1370     flattenedNames = vtkExodusIIWriter::FlattenOutVariableNames(
1371       this->NumberOfScalarElementArrays, this->BlockVariableMap);
1372 
1373     // these variables are now owned by em.  No deletion
1374     em->SetElementVariableInfo(this->NumberOfScalarElementArrays, flattenedNames,
1375       static_cast<int>(narrays), nms, numComponents, scalarIndex);
1376   }
1377 
1378   narrays = this->NodeVariableMap.size();
1379   if (narrays > 0)
1380   {
1381     nms = new char*[narrays];
1382     int* numComponents = new int[narrays];
1383     int* scalarOutOffset = new int[narrays];
1384 
1385     int index = 0;
1386     std::map<std::string, VariableInfo>::const_iterator iter;
1387     for (iter = this->NodeVariableMap.begin(); iter != this->NodeVariableMap.end(); ++iter)
1388     {
1389       nms[index] = vtkExodusIIWriter::StrDupWithNew(iter->first.c_str());
1390       numComponents[index] = iter->second.NumComponents;
1391       scalarOutOffset[index] = iter->second.ScalarOutOffset;
1392       index++;
1393     }
1394 
1395     flattenedNames = vtkExodusIIWriter::FlattenOutVariableNames(
1396       this->NumberOfScalarNodeArrays, this->NodeVariableMap);
1397 
1398     em->SetNodeVariableInfo(this->NumberOfScalarNodeArrays, flattenedNames,
1399       static_cast<int>(narrays), nms, numComponents, scalarOutOffset);
1400   }
1401   return 1;
1402 }
1403 
CreateSetsMetadata(vtkModelMetadata * em)1404 int vtkExodusIIWriter::CreateSetsMetadata(vtkModelMetadata* em)
1405 {
1406   bool isASideSet = false;
1407   bool isANodeSet = false;
1408 
1409   if (this->OriginalInput->IsA("vtkMultiBlockDataSet"))
1410   {
1411     int numNodeSets = 0;
1412     int sumNodes = 0;
1413     vtkNew<vtkStringArray> nodeSetNames;
1414     vtkSmartPointer<vtkIntArray> nodeSetIds = vtkSmartPointer<vtkIntArray>::New();
1415     vtkSmartPointer<vtkIntArray> nodeSetSizes = vtkSmartPointer<vtkIntArray>::New();
1416     vtkSmartPointer<vtkIntArray> nodeSetNumDF = vtkSmartPointer<vtkIntArray>::New();
1417     vtkSmartPointer<vtkIntArray> nodeIds = vtkSmartPointer<vtkIntArray>::New();
1418     int numSideSets = 0;
1419     int sumSides = 0;
1420     vtkNew<vtkStringArray> sideSetNames;
1421     vtkSmartPointer<vtkIntArray> sideSetIds = vtkSmartPointer<vtkIntArray>::New();
1422     vtkSmartPointer<vtkIntArray> sideSetSizes = vtkSmartPointer<vtkIntArray>::New();
1423     vtkSmartPointer<vtkIntArray> sideSetNumDF = vtkSmartPointer<vtkIntArray>::New();
1424     vtkSmartPointer<vtkIntArray> sideSetElementList = vtkSmartPointer<vtkIntArray>::New();
1425     vtkSmartPointer<vtkIntArray> sideSetSideList = vtkSmartPointer<vtkIntArray>::New();
1426 
1427     vtkMultiBlockDataSet* castObj = vtkMultiBlockDataSet::SafeDownCast(this->OriginalInput);
1428     vtkSmartPointer<vtkDataObjectTreeIterator> iter;
1429     iter.TakeReference(castObj->NewTreeIterator());
1430     iter->VisitOnlyLeavesOff();
1431     iter->TraverseSubTreeOn();
1432     int node_id = 0;
1433     int side_id = 0;
1434     for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
1435     {
1436       const char* name = iter->GetCurrentMetaData()->Get(vtkCompositeDataSet::NAME());
1437       if (iter->GetCurrentDataObject()->IsA("vtkMultiBlockDataSet"))
1438       {
1439         isASideSet = (name != nullptr && strncmp(name, "Side Sets", 9) == 0);
1440         isANodeSet = (name != nullptr && strncmp(name, "Node Sets", 9) == 0);
1441       }
1442       else if (isANodeSet)
1443       {
1444         numNodeSets++;
1445         const char* id_str = name != nullptr ? strstr(name, "ID:") : nullptr;
1446         if (id_str != nullptr)
1447         {
1448           id_str += 3;
1449           node_id = atoi(id_str);
1450         }
1451         nodeSetIds->InsertNextTuple1(node_id);
1452 
1453         if (nodeSetNames->GetNumberOfValues() <= node_id)
1454         {
1455           nodeSetNames->SetNumberOfValues((node_id + 1) * 2);
1456         }
1457         nodeSetNames->SetValue(node_id, name);
1458 
1459         node_id++; // Make sure the node_id is unique if id_str is invalid
1460         vtkUnstructuredGrid* grid = vtkUnstructuredGrid::SafeDownCast(iter->GetCurrentDataObject());
1461         vtkFieldData* field = grid->GetPointData();
1462         vtkIdTypeArray* globalIds =
1463           vtkArrayDownCast<vtkIdTypeArray>(field ? field->GetArray("GlobalNodeId") : nullptr);
1464         if (globalIds)
1465         {
1466           int size = 0;
1467           for (int c = 0; c < grid->GetNumberOfCells(); c++)
1468           {
1469             vtkCell* cell = grid->GetCell(c);
1470             for (int p = 0; p < cell->GetNumberOfPoints(); p++)
1471             {
1472               size++;
1473               vtkIdType pointId = cell->GetPointId(p);
1474               vtkIdType gid = globalIds->GetValue(pointId);
1475               nodeIds->InsertNextTuple1(gid);
1476               // TODO implement distribution factors
1477               nodeSetNumDF->InsertNextTuple1(0);
1478             }
1479           }
1480           nodeSetSizes->InsertNextTuple1(size);
1481           sumNodes += size;
1482         }
1483         // else
1484         // {
1485         // vtkErrorMacro ("We have a node set, but it doesn't have GlobalNodeIDs");
1486         // }
1487       }
1488       else if (isASideSet)
1489       {
1490         int hexSides = 0;
1491         int wedgeSides = 0;
1492         int otherSides = 0;
1493         int badSides = 0;
1494         numSideSets++;
1495         const char* id_str = name != nullptr ? strstr(name, "ID:") : nullptr;
1496         if (id_str != nullptr)
1497         {
1498           id_str += 3;
1499           side_id = atoi(id_str);
1500         }
1501         sideSetIds->InsertNextTuple1(side_id);
1502         if (sideSetNames->GetNumberOfValues() <= side_id)
1503         {
1504           sideSetNames->SetNumberOfValues((side_id + 1) * 2);
1505         }
1506         sideSetNames->SetValue(side_id, name);
1507         side_id++; // Make sure the side_id is unique if id_str is invalid
1508         vtkUnstructuredGrid* grid = vtkUnstructuredGrid::SafeDownCast(iter->GetCurrentDataObject());
1509         vtkFieldData* field = grid->GetCellData();
1510         int cells = grid->GetNumberOfCells();
1511         vtkIdTypeArray* sourceElement =
1512           vtkArrayDownCast<vtkIdTypeArray>(field ? field->GetArray("SourceElementId") : nullptr);
1513         vtkIntArray* sourceSide =
1514           vtkArrayDownCast<vtkIntArray>(field ? field->GetArray("SourceElementSide") : nullptr);
1515         if (sourceElement && sourceSide)
1516         {
1517           for (int c = 0; c < cells; c++)
1518           {
1519             sideSetElementList->InsertNextTuple1(sourceElement->GetValue(c) + 1);
1520             switch (GetElementType(sourceElement->GetValue(c) + 1))
1521             {
1522               case -1:
1523               {
1524                 badSides++;
1525                 break;
1526               }
1527               case VTK_WEDGE:
1528               {
1529                 int wedgeMapping[5] = { 3, 4, 0, 1, 2 };
1530                 int side = wedgeMapping[sourceSide->GetValue(c)] + 1;
1531                 sideSetSideList->InsertNextTuple1(side);
1532                 wedgeSides++;
1533                 break;
1534               }
1535               case VTK_HEXAHEDRON:
1536               {
1537                 int hexMapping[6] = { 3, 1, 0, 2, 4, 5 };
1538                 sideSetSideList->InsertNextTuple1(hexMapping[sourceSide->GetValue(c)] + 1);
1539                 hexSides++;
1540                 break;
1541               }
1542               default:
1543               {
1544                 sideSetSideList->InsertNextTuple1(sourceSide->GetValue(c) + 1);
1545                 otherSides++;
1546                 break;
1547               }
1548             }
1549             // TODO implement distribution factors
1550             sideSetNumDF->InsertNextTuple1(0);
1551           }
1552           sideSetSizes->InsertNextTuple1(cells);
1553           sumSides += cells;
1554         }
1555         // else
1556         // {
1557         // vtkErrorMacro ("We have a side set, but it doesn't have SourceElementId or
1558         // SourceElementSide");
1559         // }
1560       }
1561     }
1562 
1563     em->SetNumberOfNodeSets(numNodeSets);
1564     em->SetSumNodesPerNodeSet(sumNodes);
1565     em->SetNodeSetNames(nodeSetNames);
1566 
1567     int* nodeSetIds_a = new int[nodeSetIds->GetNumberOfTuples()];
1568     if (nodeSetIds->GetPointer(0))
1569     {
1570       memcpy(
1571         nodeSetIds_a, nodeSetIds->GetPointer(0), nodeSetIds->GetNumberOfTuples() * sizeof(int));
1572     }
1573     em->SetNodeSetIds(nodeSetIds_a);
1574 
1575     int* nodeSetSizes_a = new int[nodeSetSizes->GetNumberOfTuples()];
1576     if (nodeSetSizes->GetPointer(0))
1577     {
1578       memcpy(nodeSetSizes_a, nodeSetSizes->GetPointer(0),
1579         nodeSetSizes->GetNumberOfTuples() * sizeof(int));
1580     }
1581     em->SetNodeSetSize(nodeSetSizes_a);
1582 
1583     int* nodeSetNumDF_a = new int[nodeSetNumDF->GetNumberOfTuples()];
1584     if (nodeSetNumDF->GetPointer(0))
1585     {
1586       memcpy(nodeSetNumDF_a, nodeSetNumDF->GetPointer(0),
1587         nodeSetNumDF->GetNumberOfTuples() * sizeof(int));
1588     }
1589     em->SetNodeSetNumberOfDistributionFactors(nodeSetNumDF_a);
1590 
1591     int* nodeIds_a = new int[nodeIds->GetNumberOfTuples()];
1592     if (nodeIds->GetPointer(0))
1593     {
1594       memcpy(nodeIds_a, nodeIds->GetPointer(0), nodeIds->GetNumberOfTuples() * sizeof(int));
1595     }
1596     em->SetNodeSetNodeIdList(nodeIds_a);
1597 
1598     em->SetNumberOfSideSets(numSideSets);
1599     em->SetSumSidesPerSideSet(sumSides);
1600     em->SetSideSetNames(sideSetNames);
1601 
1602     int* sideSetIds_a = new int[sideSetIds->GetNumberOfTuples()];
1603     if (sideSetIds->GetPointer(0))
1604     {
1605       memcpy(
1606         sideSetIds_a, sideSetIds->GetPointer(0), sideSetIds->GetNumberOfTuples() * sizeof(int));
1607     }
1608     em->SetSideSetIds(sideSetIds_a);
1609 
1610     int* sideSetSizes_a = new int[sideSetSizes->GetNumberOfTuples()];
1611     if (sideSetSizes->GetPointer(0))
1612     {
1613       memcpy(sideSetSizes_a, sideSetSizes->GetPointer(0),
1614         sideSetSizes->GetNumberOfTuples() * sizeof(int));
1615     }
1616     em->SetSideSetSize(sideSetSizes_a);
1617 
1618     int* sideSetNumDF_a = new int[sideSetNumDF->GetNumberOfTuples()];
1619     if (sideSetNumDF->GetPointer(0))
1620     {
1621       memcpy(sideSetNumDF_a, sideSetNumDF->GetPointer(0),
1622         sideSetNumDF->GetNumberOfTuples() * sizeof(int));
1623     }
1624     em->SetSideSetNumDFPerSide(sideSetNumDF_a);
1625 
1626     int* sideSetElementList_a = new int[sideSetElementList->GetNumberOfTuples()];
1627     if (sideSetElementList->GetPointer(0))
1628     {
1629       memcpy(sideSetElementList_a, sideSetElementList->GetPointer(0),
1630         sideSetElementList->GetNumberOfTuples() * sizeof(int));
1631     }
1632     em->SetSideSetElementList(sideSetElementList_a);
1633 
1634     int* sideSetSideList_a = new int[sideSetSideList->GetNumberOfTuples()];
1635     if (sideSetSideList->GetPointer(0))
1636     {
1637       memcpy(sideSetSideList_a, sideSetSideList->GetPointer(0),
1638         sideSetSideList->GetNumberOfTuples() * sizeof(int));
1639     }
1640     em->SetSideSetSideList(sideSetSideList_a);
1641   }
1642   return 1;
1643 }
1644 
1645 //------------------------------------------------------------------------------
ParseMetadata()1646 int vtkExodusIIWriter::ParseMetadata()
1647 {
1648   vtkModelMetadata* em = this->GetModelMetadata();
1649   int nblocks = em->GetNumberOfBlocks();
1650   int* ids = em->GetBlockIds();
1651   int* numAttributes = em->GetBlockNumberOfAttributesPerElement();
1652   float* att = em->GetBlockAttributes();
1653   int* attIdx = em->GetBlockAttributesIndex();
1654   // Extract the attribute data from the meta model.
1655   for (int n = 0; n < nblocks; n++)
1656   {
1657     std::map<int, Block>::iterator iter = this->BlockInfoMap.find(ids[n]);
1658     if (iter == this->BlockInfoMap.end())
1659     {
1660       vtkErrorMacro(<< "Unknown id " << ids[n] << " found in meta data");
1661       return 0;
1662     }
1663     iter->second.NumAttributes = numAttributes[n];
1664     iter->second.BlockAttributes = att + attIdx[n];
1665   }
1666 
1667   this->ConvertVariableNames(this->GlobalVariableMap);
1668   this->ConvertVariableNames(this->BlockVariableMap);
1669   this->ConvertVariableNames(this->NodeVariableMap);
1670   return 1;
1671 }
1672 
1673 //------------------------------------------------------------------------------
WriteInitializationParameters()1674 int vtkExodusIIWriter::WriteInitializationParameters()
1675 {
1676   vtkModelMetadata* em = this->GetModelMetadata();
1677 
1678   int dim = em->GetDimension();
1679   int nnsets = em->GetNumberOfNodeSets();
1680   int nssets = em->GetNumberOfSideSets();
1681   const char* title = em->GetTitle();
1682   int numBlocks = em->GetNumberOfBlocks();
1683   int rc =
1684     ex_put_init(this->fid, title, dim, this->NumPoints, this->NumCells, numBlocks, nnsets, nssets);
1685   return rc >= 0;
1686 }
1687 
1688 //------------------------------------------------------------------------------
WriteInformationRecords()1689 int vtkExodusIIWriter::WriteInformationRecords()
1690 {
1691 
1692   vtkModelMetadata* em = this->GetModelMetadata();
1693 
1694   int nlines = em->GetNumberOfInformationLines();
1695 
1696   if (nlines > 0)
1697   {
1698     char** lines = nullptr;
1699 
1700     em->GetInformationLines(&lines);
1701 
1702     ex_put_info(this->fid, nlines, lines);
1703   }
1704 
1705   return 1;
1706 }
1707 
1708 template <typename T>
vtkExodusIIWriterWritePoints(std::vector<vtkSmartPointer<vtkUnstructuredGrid>> input,int numPoints,int fid)1709 int vtkExodusIIWriterWritePoints(
1710   std::vector<vtkSmartPointer<vtkUnstructuredGrid>> input, int numPoints, int fid)
1711 {
1712   T* px = new T[numPoints];
1713   T* py = new T[numPoints];
1714   T* pz = new T[numPoints];
1715 
1716   int array_index = 0;
1717   for (size_t i = 0; i < input.size(); i++)
1718   {
1719     vtkPoints* pts = input[i]->GetPoints();
1720     if (pts)
1721     {
1722       int npts = pts->GetNumberOfPoints();
1723       vtkDataArray* da = pts->GetData();
1724       for (int j = 0; j < npts; j++)
1725       {
1726         px[array_index] = da->GetComponent(j, 0);
1727         py[array_index] = da->GetComponent(j, 1);
1728         pz[array_index] = da->GetComponent(j, 2);
1729         array_index++;
1730       }
1731     }
1732   }
1733 
1734   int rc = ex_put_coord(fid, px, py, pz);
1735 
1736   delete[] px;
1737   delete[] py;
1738   delete[] pz;
1739 
1740   return rc >= 0;
1741 }
1742 
1743 //------------------------------------------------------------------------------
WritePoints()1744 int vtkExodusIIWriter::WritePoints()
1745 {
1746   if (this->PassDoubles)
1747   {
1748     return vtkExodusIIWriterWritePoints<double>(this->FlattenedInput, this->NumPoints, this->fid);
1749   }
1750   else
1751   {
1752     return vtkExodusIIWriterWritePoints<float>(this->FlattenedInput, this->NumPoints, this->fid);
1753   }
1754 }
1755 
1756 //---------------------------------------------------------
1757 // Points and point IDs, element IDs
1758 //---------------------------------------------------------
WriteCoordinateNames()1759 int vtkExodusIIWriter::WriteCoordinateNames()
1760 {
1761   vtkModelMetadata* em = this->GetModelMetadata();
1762 
1763   int rc = ex_put_coord_names(this->fid, em->GetCoordinateNames());
1764 
1765   return rc >= 0;
1766 }
1767 
1768 //------------------------------------------------------------------------------
WriteGlobalPointIds()1769 int vtkExodusIIWriter::WriteGlobalPointIds()
1770 {
1771   if (!this->AtLeastOneGlobalNodeIdList)
1772   {
1773     return 1;
1774   }
1775   int* copyOfIds = new int[this->NumPoints];
1776   int index = 0;
1777   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
1778   {
1779     vtkIdType npoints = this->FlattenedInput[i]->GetNumberOfPoints();
1780 
1781     vtkIdType* ids = this->GlobalNodeIdList[i];
1782     if (ids)
1783     {
1784       for (int j = 0; j < npoints; j++)
1785       {
1786         copyOfIds[index++] = static_cast<int>(ids[j]);
1787       }
1788     }
1789     else
1790     {
1791       for (int j = 0; j < npoints; j++)
1792       {
1793         copyOfIds[index++] = 0;
1794       }
1795     }
1796   }
1797   int rc = ex_put_node_num_map(this->fid, copyOfIds);
1798   delete[] copyOfIds;
1799 
1800   return rc >= 0;
1801 }
1802 
1803 //------------------------------------------------------------------------------
WriteBlockInformation()1804 int vtkExodusIIWriter::WriteBlockInformation()
1805 {
1806   int rc;
1807 
1808   std::map<int, Block>::const_iterator blockIter;
1809   size_t nblocks = this->BlockInfoMap.size();
1810 
1811   std::vector<int*> connectivity(nblocks);
1812 
1813   // Use this to copy the attributes into if we need it.
1814   std::vector<double*> attributesD(nblocks);
1815 
1816   // For each block, a map from element global ID to it's location
1817   // within it's block in the ExodusModel object.
1818   for (blockIter = this->BlockInfoMap.begin(); blockIter != this->BlockInfoMap.end(); ++blockIter)
1819   {
1820     int outputIndex = blockIter->second.OutputIndex;
1821     int numElts = blockIter->second.NumElements;
1822     int numAtts = blockIter->second.NumAttributes;
1823     int numNodes = blockIter->second.NodesPerElement;
1824 
1825     int numPoints;
1826     if (numNodes == 0 &&
1827       // Number of elements is 0 if all data is on one process
1828       numElts > 0)
1829     {
1830       numPoints = blockIter->second.EntityNodeOffsets[numElts - 1] +
1831         blockIter->second.EntityCounts[numElts - 1];
1832     }
1833     else
1834     {
1835       numPoints = numElts * numNodes;
1836     }
1837 
1838     if (numElts > 0)
1839     {
1840       connectivity[outputIndex] = new int[numPoints];
1841 
1842       if (numAtts > 0)
1843       {
1844         if (this->PassDoubles)
1845         {
1846           attributesD[outputIndex] = new double[numElts * numAtts];
1847         }
1848       }
1849     }
1850     else
1851     {
1852       connectivity[outputIndex] = nullptr;
1853       attributesD[outputIndex] = nullptr;
1854     }
1855   }
1856 
1857   // Prepare the pointers as flat arrays for Exodus
1858   // connectivity and attributes, if we need doubles.
1859   int pointOffset = 0;
1860   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
1861   {
1862     vtkCellArray* ca = this->FlattenedInput[i]->GetCells();
1863 
1864     int ncells = this->FlattenedInput[i]->GetNumberOfCells();
1865     for (int j = 0; j < ncells; j++)
1866     {
1867       int blockId = this->BlockIdList[i]->GetValue(j); // CLM
1868       int blockOutIndex = this->BlockInfoMap[blockId].OutputIndex;
1869 
1870       int nodesPerElement = this->BlockInfoMap[blockId].NodesPerElement;
1871       vtkIdType elementOffset = this->CellToElementOffset[i][j];
1872       int offset;
1873       if (nodesPerElement == 0)
1874       {
1875         offset = this->BlockInfoMap[blockId].EntityNodeOffsets[j];
1876       }
1877       else
1878       {
1879         offset = elementOffset * nodesPerElement;
1880       }
1881 
1882       // the block connectivity array
1883       vtkIdType npts;
1884       const vtkIdType* ptIds;
1885       ca->GetCellAtId(j, npts, ptIds);
1886 
1887       switch (this->FlattenedInput[i]->GetCellType(j))
1888       {
1889         case VTK_VOXEL: // reorder to exodus HEX type
1890           connectivity[blockOutIndex][offset + 0] = pointOffset + (int)ptIds[0] + 1;
1891           connectivity[blockOutIndex][offset + 1] = pointOffset + (int)ptIds[1] + 1;
1892           connectivity[blockOutIndex][offset + 3] = pointOffset + (int)ptIds[2] + 1;
1893           connectivity[blockOutIndex][offset + 2] = pointOffset + (int)ptIds[3] + 1;
1894           connectivity[blockOutIndex][offset + 4] = pointOffset + (int)ptIds[4] + 1;
1895           connectivity[blockOutIndex][offset + 5] = pointOffset + (int)ptIds[5] + 1;
1896           connectivity[blockOutIndex][offset + 7] = pointOffset + (int)ptIds[6] + 1;
1897           connectivity[blockOutIndex][offset + 6] = pointOffset + (int)ptIds[7] + 1;
1898           break;
1899         default:
1900           for (vtkIdType p = 0; p < npts; p++)
1901           {
1902             int ExodusPointId = pointOffset + (int)ptIds[p] + 1;
1903             connectivity[blockOutIndex][offset + p] = ExodusPointId;
1904           }
1905       }
1906 
1907       // the block element attributes
1908       float* att = this->BlockInfoMap[blockId].BlockAttributes;
1909 
1910       int numAtts = this->BlockInfoMap[blockId].NumAttributes;
1911 
1912       if ((numAtts == 0) || (att == nullptr))
1913         continue;
1914 
1915       int attOff = (elementOffset * numAtts); // location for the element in the block
1916 
1917       if (this->PassDoubles)
1918       {
1919         for (int k = 0; k < numAtts; k++)
1920         {
1921           int off = attOff + k;
1922           // TODO verify the assumption that ModelMetadata stores
1923           // elements in the same order we do
1924           // Could probably use the global node id? but how global is global.
1925           attributesD[blockOutIndex][off] = static_cast<double>(att[off]);
1926         }
1927       }
1928     }
1929     pointOffset += this->FlattenedInput[i]->GetNumberOfPoints();
1930   }
1931 
1932   // Now, finally, write out the block information
1933   int fail = 0;
1934   for (blockIter = this->BlockInfoMap.begin(); blockIter != this->BlockInfoMap.end(); ++blockIter)
1935   {
1936     char* type_name = vtkExodusIIWriter::GetCellTypeName(blockIter->second.Type);
1937     if (blockIter->second.NodesPerElement == 0 && blockIter->second.NumElements > 0)
1938     {
1939       int numElts = blockIter->second.NumElements;
1940       int numPoints = blockIter->second.EntityNodeOffsets[numElts - 1] +
1941         blockIter->second.EntityCounts[numElts - 1];
1942       rc = ex_put_elem_block(this->fid, blockIter->first, type_name, blockIter->second.NumElements,
1943         numPoints, blockIter->second.NumAttributes);
1944     }
1945     else
1946     {
1947       rc = ex_put_elem_block(this->fid, blockIter->first, type_name, blockIter->second.NumElements,
1948         blockIter->second.NodesPerElement, blockIter->second.NumAttributes);
1949     }
1950     delete[] type_name;
1951 
1952     if (rc < 0)
1953     {
1954       vtkErrorMacro(<< "Problem adding block with id " << blockIter->first);
1955       continue;
1956     }
1957 
1958     ex_put_name(this->fid, EX_ELEM_BLOCK, blockIter->first, blockIter->second.Name);
1959 
1960     if (blockIter->second.NumElements > 0)
1961     {
1962       rc =
1963         ex_put_elem_conn(this->fid, blockIter->first, connectivity[blockIter->second.OutputIndex]);
1964 
1965       if (rc < 0)
1966       {
1967         vtkErrorMacro(<< "Problem writing connectivity " << blockIter->first);
1968         continue;
1969       }
1970 
1971       if (blockIter->second.NumAttributes != 0)
1972       {
1973         if (this->PassDoubles)
1974         {
1975           rc = ex_put_elem_attr(
1976             this->fid, blockIter->first, attributesD[blockIter->second.OutputIndex]);
1977         }
1978         else
1979         {
1980           // TODO verify the assumption that ModelMetadata stores
1981           // elements in the same order we do
1982           rc = ex_put_elem_attr(this->fid, blockIter->first, blockIter->second.BlockAttributes);
1983         }
1984 
1985         if (rc < 0)
1986         {
1987           continue;
1988         }
1989       }
1990 
1991       if (blockIter->second.NodesPerElement == 0)
1992       {
1993         rc = ex_put_entity_count_per_polyhedra(
1994           this->fid, EX_ELEM_BLOCK, blockIter->first, &(blockIter->second.EntityCounts[0]));
1995       }
1996     }
1997   }
1998 
1999   for (size_t n = 0; n < nblocks; n++)
2000   {
2001     delete[] connectivity[n];
2002     if (this->PassDoubles)
2003     {
2004       delete[] attributesD[n];
2005     }
2006   }
2007   return !fail;
2008 }
2009 
2010 //------------------------------------------------------------------------------
WriteGlobalElementIds()2011 int vtkExodusIIWriter::WriteGlobalElementIds()
2012 {
2013   int rc = 0;
2014 
2015   // if (sizeof(vtkIdType) != sizeof(int))
2016   //  {
2017   //  vtkErrorMacro(<<"vtkExodusIIWriter::WriteGlobalElementIds cannot convert vtkIdType to int.");
2018   //  return -1;
2019   //  }
2020 
2021   if (this->AtLeastOneGlobalElementIdList)
2022   {
2023     int* copyOfIds = new int[this->NumCells];
2024     memset(copyOfIds, 0, sizeof(int) * this->NumCells);
2025     for (size_t i = 0; i < this->FlattenedInput.size(); i++)
2026     {
2027       vtkIdType* ids = this->GlobalElementIdList[i];
2028       if (ids)
2029       {
2030         int ncells = this->FlattenedInput[i]->GetNumberOfCells();
2031         for (int j = 0; j < ncells; j++)
2032         {
2033           int blockId = this->BlockIdList[i]->GetValue(j);
2034           int start = this->BlockInfoMap[blockId].ElementStartIndex;
2035           vtkIdType offset = this->CellToElementOffset[i][j];
2036           copyOfIds[start + offset] = static_cast<int>(ids[j]);
2037         }
2038       }
2039     }
2040     rc = ex_put_elem_num_map(this->fid, copyOfIds);
2041     delete[] copyOfIds;
2042   }
2043 
2044   return rc >= 0;
2045 }
2046 
2047 //------------------------------------------------------------------------------
WriteVariableArrayNames()2048 int vtkExodusIIWriter::WriteVariableArrayNames()
2049 {
2050   int rc = 0;
2051 
2052   //  1. We convert vector arrays to individual scalar arrays, using
2053   //     their original names if we have those.
2054   //  2. For the element variables, create the element/block truth table.
2055 
2056   // GLOBAL VARIABLES
2057   const char** outputArrayNames;
2058   if (this->NumberOfScalarGlobalArrays > 0)
2059   {
2060     outputArrayNames = new const char*[this->NumberOfScalarGlobalArrays];
2061     std::map<std::string, VariableInfo>::const_iterator iter;
2062     for (iter = this->GlobalVariableMap.begin(); iter != this->GlobalVariableMap.end(); ++iter)
2063     {
2064       int off = iter->second.ScalarOutOffset;
2065       for (int j = 0; j < iter->second.NumComponents; j++)
2066       {
2067         outputArrayNames[off + j] = iter->second.OutNames[j].c_str();
2068       }
2069     }
2070 
2071     rc = ex_put_var_param(this->fid, "G", this->NumberOfScalarGlobalArrays);
2072     if (rc < 0)
2073     {
2074       vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames cell variables");
2075       delete[] outputArrayNames;
2076       return 0;
2077     }
2078 
2079     rc = ex_put_var_names(
2080       this->fid, "G", this->NumberOfScalarGlobalArrays, const_cast<char**>(outputArrayNames));
2081     // This should be treating this read only... hopefully
2082     if (rc < 0)
2083     {
2084       delete[] outputArrayNames;
2085       vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames cell variables");
2086       return 0;
2087     }
2088 
2089     delete[] outputArrayNames;
2090   }
2091 
2092   // CELL (ELEMENT) VARIABLES
2093   if (this->NumberOfScalarElementArrays > 0 && this->NumCells > 0)
2094   {
2095     outputArrayNames = new const char*[this->NumberOfScalarElementArrays];
2096 
2097     std::map<std::string, VariableInfo>::const_iterator iter;
2098     for (iter = this->BlockVariableMap.begin(); iter != this->BlockVariableMap.end(); ++iter)
2099     {
2100       int off = iter->second.ScalarOutOffset;
2101       for (int j = 0; j < iter->second.NumComponents; j++)
2102       {
2103         outputArrayNames[off + j] = iter->second.OutNames[j].c_str();
2104       }
2105     }
2106 
2107     rc = ex_put_var_param(this->fid, "E", this->NumberOfScalarElementArrays);
2108     if (rc < 0)
2109     {
2110       delete[] outputArrayNames;
2111       vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames cell variables");
2112       return 0;
2113     }
2114 
2115     rc = ex_put_var_names(
2116       this->fid, "E", this->NumberOfScalarElementArrays, const_cast<char**>(outputArrayNames));
2117     // This should be treating this read only... hopefully
2118     if (rc < 0)
2119     {
2120       delete[] outputArrayNames;
2121       vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames cell variables");
2122       return 0;
2123     }
2124 
2125     rc = ex_put_elem_var_tab(this->fid, static_cast<int>(this->BlockInfoMap.size()),
2126       this->NumberOfScalarElementArrays, this->BlockElementVariableTruthTable);
2127     if (rc < 0)
2128     {
2129       delete[] outputArrayNames;
2130       vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames cell variables");
2131       return 0;
2132     }
2133 
2134     delete[] outputArrayNames;
2135   }
2136 
2137   // POINT (NODE) VARIABLES
2138   if (this->NumberOfScalarNodeArrays > 0 && this->NumPoints > 0)
2139   {
2140     outputArrayNames = new const char*[this->NumberOfScalarNodeArrays];
2141 
2142     std::map<std::string, VariableInfo>::const_iterator iter;
2143     for (iter = this->NodeVariableMap.begin(); iter != this->NodeVariableMap.end(); ++iter)
2144     {
2145       int off = iter->second.ScalarOutOffset;
2146       for (int j = 0; j < iter->second.NumComponents; j++)
2147       {
2148         outputArrayNames[off + j] = iter->second.OutNames[j].c_str();
2149       }
2150     }
2151 
2152     rc = ex_put_var_param(this->fid, "N", this->NumberOfScalarNodeArrays);
2153 
2154     if (rc < 0)
2155     {
2156       vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames "
2157                     << "failure to write " << this->NumberOfScalarNodeArrays << " arrays");
2158       delete[] outputArrayNames;
2159       return 0;
2160     }
2161 
2162     rc = ex_put_var_names(
2163       this->fid, "N", this->NumberOfScalarNodeArrays, const_cast<char**>(outputArrayNames));
2164     // This should not save references... hopefully
2165     if (rc < 0)
2166     {
2167       vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames "
2168                     << "failure to write the array names");
2169       delete[] outputArrayNames;
2170       return 0;
2171     }
2172 
2173     delete[] outputArrayNames;
2174   }
2175 
2176   // GLOBAL VARIABLES
2177   /*
2178     int ngvars = mmd->GetNumberOfGlobalVariables();
2179 
2180     if (ngvars > 0)
2181       {
2182       char **names = mmd->GetGlobalVariableNames();
2183 
2184       rc = ex_put_var_param(this->fid, "G", ngvars);
2185 
2186       if (rc == 0)
2187         {
2188         rc = ex_put_var_names(this->fid, "G", ngvars, names);
2189         }
2190 
2191       if (rc < 0)
2192         {
2193         vtkErrorMacro(<<
2194           "vtkExodusIIWriter::WriteVariableArrayNames global variables");
2195         return 0;
2196         }
2197       }
2198   */
2199   return 1;
2200 }
2201 
2202 //------------------------------------------------------------------------------
ConvertVariableNames(std::map<std::string,VariableInfo> & variableMap)2203 void vtkExodusIIWriter::ConvertVariableNames(std::map<std::string, VariableInfo>& variableMap)
2204 {
2205   std::map<std::string, VariableInfo>::iterator varIter;
2206   // Global output variable names
2207   for (varIter = variableMap.begin(); varIter != variableMap.end(); ++varIter)
2208   {
2209     int numComp = varIter->second.NumComponents;
2210     if (numComp == 1)
2211     {
2212       varIter->second.OutNames[0] = std::string(varIter->first);
2213     }
2214     else
2215     {
2216       for (int component = 0; component < numComp; component++)
2217       {
2218         varIter->second.OutNames[component] =
2219           CreateNameForScalarArray(varIter->first.c_str(), component, numComp);
2220       }
2221     }
2222   }
2223 }
2224 
FlattenOutVariableNames(int nScalarArrays,const std::map<std::string,VariableInfo> & variableMap)2225 char** vtkExodusIIWriter::FlattenOutVariableNames(
2226   int nScalarArrays, const std::map<std::string, VariableInfo>& variableMap)
2227 {
2228   char** newNames = new char*[nScalarArrays];
2229 
2230   std::map<std::string, VariableInfo>::const_iterator iter;
2231   for (iter = variableMap.begin(); iter != variableMap.end(); ++iter)
2232   {
2233     for (int component = 0; component < iter->second.NumComponents; component++)
2234     {
2235       int index = iter->second.ScalarOutOffset + component;
2236       newNames[index] = StrDupWithNew(
2237         CreateNameForScalarArray(iter->first.c_str(), component, iter->second.NumComponents)
2238           .c_str());
2239     }
2240   }
2241 
2242   return newNames;
2243 }
2244 
2245 //------------------------------------------------------------------------------
CreateNameForScalarArray(const char * root,int component,int numComponents)2246 std::string vtkExodusIIWriter::CreateNameForScalarArray(
2247   const char* root, int component, int numComponents)
2248 {
2249   // Naming conventions chosen to match ExodusIIReader expectations
2250   if (component >= numComponents)
2251   {
2252     vtkErrorMacro("CreateNameForScalarArray: Component out of range");
2253     return std::string();
2254   }
2255   if (numComponents == 1)
2256   {
2257     return std::string(root);
2258   }
2259   else if (numComponents <= 2)
2260   {
2261     std::string s(root);
2262     switch (component)
2263     {
2264       case 0:
2265         s.append("_R");
2266         break;
2267       case 1:
2268         s.append("_Z");
2269         break;
2270     }
2271     return s;
2272   }
2273   else if (numComponents <= 3)
2274   {
2275     std::string s(root);
2276     switch (component)
2277     {
2278       case 0:
2279         s.append("X");
2280         break;
2281       case 1:
2282         s.append("Y");
2283         break;
2284       case 2:
2285         s.append("Z");
2286         break;
2287     }
2288     return s;
2289   }
2290   else if (numComponents <= 6)
2291   {
2292     std::string s(root);
2293     switch (component)
2294     {
2295       case 0:
2296         s.append("XX");
2297         break;
2298       case 1:
2299         s.append("XY");
2300         break;
2301       case 2:
2302         s.append("XZ");
2303         break;
2304       case 3:
2305         s.append("YY");
2306         break;
2307       case 4:
2308         s.append("YZ");
2309         break;
2310       case 5:
2311         s.append("ZZ");
2312         break;
2313     }
2314     return s;
2315   }
2316   else
2317   {
2318     std::string s(root);
2319     // assume largest for 32 bit decimal representation
2320     char n[11];
2321     snprintf(n, sizeof(n), "%10d", component);
2322     s.append(n);
2323     return s;
2324   }
2325 }
2326 
2327 //------------------------------------------------------------------------------
GetNodeLocalId(vtkIdType id)2328 vtkIdType vtkExodusIIWriter::GetNodeLocalId(vtkIdType id)
2329 {
2330   if (!this->LocalNodeIdMap)
2331   {
2332     this->LocalNodeIdMap = new std::map<vtkIdType, vtkIdType>;
2333     vtkIdType index = 0;
2334     for (size_t i = 0; i < this->FlattenedInput.size(); i++)
2335     {
2336       vtkIdType npoints = this->FlattenedInput[i]->GetNumberOfPoints();
2337       vtkIdType* ids = this->GlobalNodeIdList[i];
2338       if (ids)
2339       {
2340         for (int j = 0; j < npoints; j++)
2341         {
2342           this->LocalNodeIdMap->insert(std::map<vtkIdType, vtkIdType>::value_type(ids[j], index));
2343           index++;
2344         }
2345       }
2346       else
2347       {
2348         index += npoints;
2349       }
2350     }
2351   }
2352 
2353   std::map<vtkIdType, vtkIdType>::iterator mapit = this->LocalNodeIdMap->find(id);
2354 
2355   if (mapit == this->LocalNodeIdMap->end())
2356   {
2357     return -1;
2358   }
2359   else
2360   {
2361     return mapit->second;
2362   }
2363 }
2364 
2365 //------------------------------------------------------------------------------
2366 // Side sets and node sets
2367 //------------------------------------------------------------------------------
WriteNodeSetInformation()2368 int vtkExodusIIWriter::WriteNodeSetInformation()
2369 {
2370   int rc = 0;
2371   int i, j;
2372 
2373   vtkModelMetadata* em = this->GetModelMetadata();
2374 
2375   int nnsets = em->GetNumberOfNodeSets();
2376 
2377   if (nnsets < 1)
2378     return 1;
2379 
2380   int nids = em->GetSumNodesPerNodeSet();
2381 
2382   if (nids < 1 || !this->AtLeastOneGlobalNodeIdList)
2383   {
2384     int* buf = new int[nnsets];
2385 
2386     memset(buf, 0, sizeof(int) * nnsets);
2387 
2388     rc =
2389       ex_put_concat_node_sets(this->fid, em->GetNodeSetIds(), buf, buf, buf, buf, nullptr, nullptr);
2390 
2391     delete[] buf;
2392 
2393     return (rc >= 0);
2394   }
2395 
2396   int* nsSize = new int[nnsets];
2397   int* nsNumDF = new int[nnsets];
2398   int* nsIdIdx = new int[nnsets];
2399   int* nsDFIdx = new int[nnsets];
2400 
2401   int ndf = em->GetSumDistFactPerNodeSet();
2402 
2403   int* idBuf = new int[nids];
2404   float* dfBuf = nullptr;
2405   double* dfBufD = nullptr;
2406 
2407   if (ndf)
2408   {
2409     if (this->PassDoubles)
2410     {
2411       dfBufD = new double[ndf];
2412     }
2413     else
2414     {
2415       dfBuf = new float[ndf];
2416     }
2417   }
2418 
2419   int* emNsSize = em->GetNodeSetSize();
2420   int* emNumDF = em->GetNodeSetNumberOfDistributionFactors();
2421 
2422   int nextId = 0;
2423   int nextDF = 0;
2424 
2425   int* ids = em->GetNodeSetNodeIdList();
2426   float* df = em->GetNodeSetDistributionFactors();
2427 
2428   for (i = 0; i < nnsets; i++)
2429   {
2430     nsSize[i] = 0;
2431     nsNumDF[i] = 0;
2432 
2433     nsIdIdx[i] = nextId;
2434     nsDFIdx[i] = nextDF;
2435 
2436     for (j = 0; j < emNsSize[i]; j++)
2437     {
2438       int lid = this->GetNodeLocalId(*ids);
2439       ids++;
2440 
2441       if (lid < 0)
2442         continue;
2443 
2444       nsSize[i]++;
2445       idBuf[nextId++] = lid + 1;
2446 
2447       if (*emNumDF > 0)
2448       {
2449         nsNumDF[i]++;
2450 
2451         if (this->PassDoubles)
2452         {
2453           dfBufD[nextDF++] = (double)*df;
2454         }
2455         else
2456         {
2457           dfBuf[nextDF++] = *df;
2458         }
2459       }
2460     }
2461     df++;
2462     emNumDF++;
2463   }
2464 
2465   int* node_ids = em->GetNodeSetIds();
2466 
2467   if (this->PassDoubles)
2468   {
2469     rc = ex_put_concat_node_sets(
2470       this->fid, node_ids, nsSize, nsNumDF, nsIdIdx, nsDFIdx, idBuf, dfBufD);
2471   }
2472   else
2473   {
2474     rc =
2475       ex_put_concat_node_sets(this->fid, node_ids, nsSize, nsNumDF, nsIdIdx, nsDFIdx, idBuf, dfBuf);
2476   }
2477 
2478   for (i = 0; i < nnsets; i++)
2479   {
2480     vtkStdString name = em->GetNodeSetNames()->GetValue(node_ids[i]);
2481     ex_put_name(this->fid, EX_NODE_SET, node_ids[i], name.c_str());
2482   }
2483 
2484   delete[] nsSize;
2485   delete[] nsNumDF;
2486   delete[] nsIdIdx;
2487   delete[] nsDFIdx;
2488   delete[] idBuf;
2489   delete[] dfBuf;
2490   delete[] dfBufD;
2491 
2492   return (rc >= 0);
2493 }
2494 
2495 //------------------------------------------------------------------------------
GetElementLocalId(vtkIdType id)2496 vtkIdType vtkExodusIIWriter::GetElementLocalId(vtkIdType id)
2497 {
2498   if (!this->LocalElementIdMap)
2499   {
2500     this->LocalElementIdMap = new std::map<vtkIdType, vtkIdType>;
2501     for (size_t i = 0; i < this->FlattenedInput.size(); i++)
2502     {
2503       if (this->GlobalElementIdList[i])
2504       {
2505         vtkIdType ncells = this->FlattenedInput[i]->GetNumberOfCells();
2506         for (vtkIdType j = 0; j < ncells; j++)
2507         {
2508           vtkIdType gid = this->GlobalElementIdList[i][j];
2509           std::map<vtkIdType, vtkIdType>::iterator mapit = this->LocalElementIdMap->find(gid);
2510           if (mapit != this->LocalElementIdMap->end())
2511           {
2512             vtkErrorMacro("Overlapping gids in the dataset");
2513             continue;
2514           }
2515 
2516           std::map<int, Block>::const_iterator blockIter =
2517             this->BlockInfoMap.find(this->BlockIdList[i]->GetValue(j));
2518           if (blockIter == this->BlockInfoMap.end())
2519           {
2520             vtkWarningMacro("vtkExodusIIWriter: The block id map has come out of sync");
2521             continue;
2522           }
2523 
2524           int index = blockIter->second.ElementStartIndex + CellToElementOffset[i][j];
2525           this->LocalElementIdMap->insert(std::map<vtkIdType, vtkIdType>::value_type(gid, index));
2526         }
2527       }
2528     }
2529   }
2530 
2531   std::map<vtkIdType, vtkIdType>::iterator mapit = this->LocalElementIdMap->find(id);
2532   if (mapit == this->LocalElementIdMap->end())
2533   {
2534     return -1;
2535   }
2536   else
2537   {
2538     return mapit->second;
2539   }
2540 }
2541 
GetElementType(vtkIdType id)2542 int vtkExodusIIWriter::GetElementType(vtkIdType id)
2543 {
2544   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
2545   {
2546     if (this->GlobalElementIdList[i])
2547     {
2548       vtkIdType ncells = this->FlattenedInput[i]->GetNumberOfCells();
2549       for (vtkIdType j = 0; j < ncells; j++)
2550       {
2551         vtkIdType gid = this->GlobalElementIdList[i][j];
2552         if (gid == id)
2553         {
2554           return this->FlattenedInput[i]->GetCellType(j);
2555         }
2556       }
2557     }
2558   }
2559   return -1;
2560 }
2561 
2562 //------------------------------------------------------------------------------
WriteSideSetInformation()2563 int vtkExodusIIWriter::WriteSideSetInformation()
2564 {
2565   int i, j, k;
2566   int rc = 0;
2567 
2568   vtkModelMetadata* em = this->GetModelMetadata();
2569 
2570   int nssets = em->GetNumberOfSideSets();
2571 
2572   if (nssets < 1)
2573     return 1;
2574 
2575   // Cells are written out to file in a different order than
2576   // they appear in the input. We need a mapping from their internal
2577   // id in the input to their internal id in the output.
2578 
2579   int nids = em->GetSumSidesPerSideSet();
2580 
2581   if (nids < 1)
2582   {
2583     int* buf = new int[nssets];
2584 
2585     memset(buf, 0, sizeof(int) * nssets);
2586 
2587     rc = ex_put_concat_side_sets(
2588       this->fid, em->GetSideSetIds(), buf, buf, buf, buf, nullptr, nullptr, nullptr);
2589 
2590     delete[] buf;
2591 
2592     return (rc >= 0);
2593   }
2594 
2595   int* ssSize = new int[nssets];
2596   int* ssNumDF = new int[nssets];
2597   int* ssIdIdx = new int[nssets];
2598   int* ssDFIdx = new int[nssets];
2599 
2600   int ndf = em->GetSumDistFactPerSideSet();
2601 
2602   int* idBuf = new int[nids];
2603   int* sideBuf = new int[nids];
2604   float* dfBuf = nullptr;
2605   double* dfBufD = nullptr;
2606 
2607   if (ndf)
2608   {
2609     if (this->PassDoubles)
2610     {
2611       dfBufD = new double[ndf];
2612     }
2613     else
2614     {
2615       dfBuf = new float[ndf];
2616     }
2617   }
2618 
2619   int* emSsSize = em->GetSideSetSize();
2620   int* emDFIdx = em->GetSideSetDistributionFactorIndex();
2621 
2622   int nextId = 0;
2623   int nextDF = 0;
2624 
2625   int* ids = em->GetSideSetElementList();
2626   int* sides = em->GetSideSetSideList();
2627   int* numDFPerSide = em->GetSideSetNumDFPerSide();
2628 
2629   for (i = 0; i < nssets; i++)
2630   {
2631     ssSize[i] = 0;
2632     ssNumDF[i] = 0;
2633 
2634     ssIdIdx[i] = nextId;
2635     ssDFIdx[i] = nextDF;
2636 
2637     if (emSsSize[i] == 0)
2638       continue;
2639 
2640     float* df = nullptr;
2641 
2642     if (ndf > 0)
2643     {
2644       df = em->GetSideSetDistributionFactors() + emDFIdx[i];
2645     }
2646 
2647     for (j = 0; j < emSsSize[i]; j++)
2648     {
2649       // Have to check if this element is still in the ugrid.
2650       // It may have been deleted since the ExodusModel was created.
2651 
2652       int lid = this->GetElementLocalId(*ids);
2653       ids++;
2654 
2655       if (lid >= 0)
2656       {
2657         ssSize[i]++;
2658 
2659         idBuf[nextId] = lid + 1;
2660         sideBuf[nextId] = *sides;
2661         sides++;
2662 
2663         nextId++;
2664 
2665         if (*numDFPerSide > 0)
2666         {
2667           ssNumDF[i] += *numDFPerSide;
2668 
2669           if (this->PassDoubles)
2670           {
2671             for (k = 0; k < *numDFPerSide; k++)
2672             {
2673               dfBufD[nextDF++] = (double)df[k];
2674             }
2675           }
2676           else
2677           {
2678             for (k = 0; k < *numDFPerSide; k++)
2679             {
2680               dfBuf[nextDF++] = df[k];
2681             }
2682           }
2683         }
2684       }
2685 
2686       if (df)
2687         df += *numDFPerSide;
2688       numDFPerSide++;
2689     }
2690   }
2691 
2692   int* sids = em->GetSideSetIds();
2693 
2694   if (this->PassDoubles)
2695   {
2696     rc = ex_put_concat_side_sets(
2697       this->fid, sids, ssSize, ssNumDF, ssIdIdx, ssDFIdx, idBuf, sideBuf, dfBufD);
2698   }
2699   else
2700   {
2701     rc = ex_put_concat_side_sets(
2702       this->fid, sids, ssSize, ssNumDF, ssIdIdx, ssDFIdx, idBuf, sideBuf, dfBuf);
2703   }
2704 
2705   for (i = 0; i < nssets; i++)
2706   {
2707     vtkStdString name = em->GetSideSetNames()->GetValue(sids[i]);
2708     ex_put_name(this->fid, EX_SIDE_SET, sids[i], name.c_str());
2709   }
2710 
2711   delete[] ssSize;
2712   delete[] ssNumDF;
2713   delete[] ssIdIdx;
2714   delete[] ssDFIdx;
2715   delete[] idBuf;
2716   delete[] sideBuf;
2717   delete[] dfBuf;
2718   delete[] dfBufD;
2719 
2720   return rc >= 0;
2721 }
2722 
2723 //------------------------------------------------------------------------------
BlockVariableTruthValue(int blockIdx,int varIdx)2724 int vtkExodusIIWriter::BlockVariableTruthValue(int blockIdx, int varIdx)
2725 {
2726   int tt = 0;
2727   int nvars = this->NumberOfScalarElementArrays;
2728   int nblocks = static_cast<int>(this->BlockInfoMap.size());
2729 
2730   if ((blockIdx >= 0) && (blockIdx < nblocks) && (varIdx >= 0) && (varIdx < nvars))
2731   {
2732     tt = this->BlockElementVariableTruthTable[(blockIdx * nvars) + varIdx];
2733   }
2734   else
2735   {
2736     vtkWarningMacro(<< "vtkExodusIIWriter::BlockVariableTruthValue invalid index");
2737   }
2738 
2739   return tt;
2740 }
2741 
2742 //------------------------------------------------------------------------------
2743 // Properties
2744 //------------------------------------------------------------------------------
WriteProperties()2745 int vtkExodusIIWriter::WriteProperties()
2746 {
2747   int rc = 0;
2748   int i;
2749 
2750   vtkModelMetadata* em = this->GetModelMetadata();
2751 
2752   int nbprop = em->GetNumberOfBlockProperties();
2753   int nnsprop = em->GetNumberOfNodeSetProperties();
2754   int nssprop = em->GetNumberOfSideSetProperties();
2755 
2756   if (nbprop)
2757   {
2758     char** names = em->GetBlockPropertyNames();
2759 
2760     // Exodus library "feature".  By convention there is a property
2761     // array called "ID", the value of which is the ID of the block,
2762     // node set or side set.  This property is special.  For example,
2763     // if you change the property value for a block, that block's
2764     // block ID is changed.  I had no idea *how* special this property
2765     // was, however.  If you use ex_put_prop_names to tell the library
2766     // what your property names are, and "ID" happens to be one of those
2767     // names, then the library fills out the whole property array for
2768     // you.  Then if you follow this call with ex_put_prop_array for
2769     // each property array, including "ID", you get *two* arrays named
2770     // "ID".  This is not documented, and totally unexpected.
2771     //
2772     // ex_put_prop_names is not required, it's just more efficient to
2773     // call it before all the ex_put_prop_array calls.  So we are
2774     // not going to call it.
2775     //
2776     // rc = ex_put_prop_names(this->fid, EX_ELEM_BLOCK, nbprop, names);
2777 
2778     if (rc >= 0)
2779     {
2780       int* values = em->GetBlockPropertyValue();
2781 
2782       for (i = 0; i < nbprop; i++)
2783       {
2784         rc = ex_put_prop_array(this->fid, EX_ELEM_BLOCK, names[i], values);
2785         if (rc)
2786           break;
2787         // TODO Handle the addition of Blocks not known by the metadata
2788         values += this->BlockInfoMap.size();
2789       }
2790     }
2791   }
2792 
2793   if (!rc && nnsprop)
2794   {
2795     char** names = em->GetNodeSetPropertyNames();
2796     int nnsets = em->GetNumberOfNodeSets();
2797 
2798     // rc = ex_put_prop_names(this->fid, EX_NODE_SET, nnsprop, names);
2799 
2800     if (rc >= 0)
2801     {
2802       int* values = em->GetNodeSetPropertyValue();
2803 
2804       for (i = 0; i < nnsprop; i++)
2805       {
2806         rc = ex_put_prop_array(this->fid, EX_NODE_SET, names[i], values);
2807         if (rc)
2808           break;
2809         values += nnsets;
2810       }
2811     }
2812   }
2813 
2814   if (!rc && nssprop)
2815   {
2816     char** names = em->GetSideSetPropertyNames();
2817     int nssets = em->GetNumberOfSideSets();
2818 
2819     // rc = ex_put_prop_names(this->fid, EX_SIDE_SET, nssprop, names);
2820 
2821     if (rc >= 0)
2822     {
2823       int* values = em->GetSideSetPropertyValue();
2824 
2825       for (i = 0; i < nssprop; i++)
2826       {
2827         rc = ex_put_prop_array(this->fid, EX_SIDE_SET, names[i], values);
2828         if (rc)
2829           break;
2830         values += nssets;
2831       }
2832     }
2833   }
2834 
2835   return (rc >= 0);
2836 }
2837 
2838 //========================================================================
2839 //   VARIABLE ARRAYS:
2840 //========================================================================
2841 template <typename iterT>
vtkExodusIIWriterGetComponent(iterT * it,vtkIdType ind)2842 double vtkExodusIIWriterGetComponent(iterT* it, vtkIdType ind)
2843 {
2844   vtkVariant v(it->GetValue(ind));
2845   return v.ToDouble();
2846 }
2847 
2848 //------------------------------------------------------------------------------
ExtractGlobalData(const char * name,int comp,int ts)2849 double vtkExodusIIWriter::ExtractGlobalData(const char* name, int comp, int ts)
2850 {
2851   double ret = 0.0;
2852   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
2853   {
2854     // find the first block that matches this global data.  Assumes it's global.
2855     vtkDataArray* da = this->FlattenedInput[i]->GetFieldData()->GetArray(name);
2856     if (da)
2857     {
2858       int numTup = da->GetNumberOfTuples();
2859       if (numTup == 1)
2860       {
2861         ret = da->GetComponent(0, comp);
2862       }
2863       // Exodus doesn't support multiple tuples on the global values.
2864       // But the ExodusIIReader reads all timesteps into the field array
2865       // at every time step.  This will assume that if we have multiple tuples
2866       // in the array they are from an exodus file so we'll output them
2867       // back as expected on another read.  Not perfect...
2868       else if (ts < numTup)
2869       {
2870         ret = da->GetComponent(ts, comp);
2871       }
2872     }
2873   }
2874   return ret;
2875 }
2876 
2877 //------------------------------------------------------------------------------
ExtractCellData(const char * name,int comp,vtkDataArray * buffer)2878 void vtkExodusIIWriter::ExtractCellData(const char* name, int comp, vtkDataArray* buffer)
2879 {
2880   buffer->SetNumberOfTuples(this->NumCells);
2881   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
2882   {
2883     vtkDataArray* da = this->FlattenedInput[i]->GetCellData()->GetArray(name);
2884     int ncells = this->FlattenedInput[i]->GetNumberOfCells();
2885     if (da)
2886     {
2887       vtkArrayIterator* arrayIter = da->NewIterator();
2888       vtkIdType ncomp = da->GetNumberOfComponents();
2889       for (vtkIdType j = 0; j < ncells; j++)
2890       {
2891         std::map<int, Block>::const_iterator blockIter =
2892           this->BlockInfoMap.find(this->BlockIdList[i]->GetValue(j));
2893         if (blockIter == this->BlockInfoMap.end())
2894         {
2895           vtkWarningMacro("vtkExodusIIWriter: The block id map has come out of sync");
2896           continue;
2897         }
2898         int index = blockIter->second.ElementStartIndex + CellToElementOffset[i][j];
2899         switch (da->GetDataType())
2900         {
2901           vtkArrayIteratorTemplateMacro(buffer->SetTuple1(index,
2902             vtkExodusIIWriterGetComponent(static_cast<VTK_TT*>(arrayIter), j * ncomp + comp)));
2903         }
2904       }
2905       arrayIter->Delete();
2906     }
2907     else
2908     {
2909       for (vtkIdType j = 0; j < ncells; j++)
2910       {
2911         std::map<int, Block>::const_iterator blockIter =
2912           this->BlockInfoMap.find(this->BlockIdList[i]->GetValue(j));
2913         if (blockIter == this->BlockInfoMap.end())
2914         {
2915           vtkWarningMacro("vtkExodusIIWriter: The block id map has come out of sync");
2916           continue;
2917         }
2918         int index = blockIter->second.ElementStartIndex + CellToElementOffset[i][j];
2919         buffer->SetTuple1(index, 0);
2920       }
2921     }
2922   }
2923 }
2924 
2925 //------------------------------------------------------------------------------
ExtractPointData(const char * name,int comp,vtkDataArray * buffer)2926 void vtkExodusIIWriter::ExtractPointData(const char* name, int comp, vtkDataArray* buffer)
2927 {
2928   buffer->SetNumberOfTuples(this->NumPoints);
2929   int index = 0;
2930   for (size_t i = 0; i < this->FlattenedInput.size(); i++)
2931   {
2932     vtkDataArray* da = this->FlattenedInput[i]->GetPointData()->GetArray(name);
2933     if (da)
2934     {
2935       vtkArrayIterator* iter = da->NewIterator();
2936       vtkIdType ncomp = da->GetNumberOfComponents();
2937       vtkIdType nvals = ncomp * da->GetNumberOfTuples();
2938       for (vtkIdType j = comp; j < nvals; j += ncomp)
2939       {
2940         switch (da->GetDataType())
2941         {
2942           vtkArrayIteratorTemplateMacro(buffer->SetTuple1(
2943             index++, vtkExodusIIWriterGetComponent(static_cast<VTK_TT*>(iter), j)));
2944         }
2945       }
2946       iter->Delete();
2947     }
2948     else
2949     {
2950       vtkIdType nvals = this->FlattenedInput[i]->GetNumberOfPoints();
2951       for (vtkIdType j = 0; j < nvals; j++)
2952       {
2953         buffer->SetTuple1(index++, 0);
2954       }
2955     }
2956   }
2957 }
2958 
2959 //------------------------------------------------------------------------------
WriteGlobalData(int timestep,vtkDataArray * buffer)2960 int vtkExodusIIWriter::WriteGlobalData(int timestep, vtkDataArray* buffer)
2961 {
2962   std::map<std::string, VariableInfo>::const_iterator varIter;
2963   buffer->Initialize();
2964   buffer->SetNumberOfComponents(1);
2965   buffer->SetNumberOfTuples(this->NumberOfScalarGlobalArrays);
2966   for (varIter = this->GlobalVariableMap.begin(); varIter != this->GlobalVariableMap.end();
2967        ++varIter)
2968   {
2969     const char* nameIn = varIter->first.c_str();
2970     int numComp = varIter->second.NumComponents;
2971     for (int component = 0; component < numComp; component++)
2972     {
2973       double val = this->ExtractGlobalData(nameIn, component, timestep);
2974       int varOutIndex = varIter->second.ScalarOutOffset + component;
2975       buffer->SetComponent(varOutIndex, 0, val);
2976     }
2977   }
2978   int rc;
2979   if (buffer->IsA("vtkDoubleArray"))
2980   {
2981     vtkDoubleArray* da = vtkArrayDownCast<vtkDoubleArray>(buffer);
2982     rc = ex_put_glob_vars(
2983       this->fid, timestep + 1, this->NumberOfScalarGlobalArrays, da->GetPointer(0));
2984   }
2985   else /* (buffer->IsA ("vtkFloatArray")) */
2986   {
2987     vtkFloatArray* fa = vtkArrayDownCast<vtkFloatArray>(buffer);
2988     rc = ex_put_glob_vars(
2989       this->fid, timestep + 1, this->NumberOfScalarGlobalArrays, fa->GetPointer(0));
2990   }
2991   if (rc < 0)
2992   {
2993     vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep glob vars");
2994     return 0;
2995   }
2996   return 1;
2997 }
2998 
2999 //------------------------------------------------------------------------------
WriteCellData(int timestep,vtkDataArray * buffer)3000 int vtkExodusIIWriter::WriteCellData(int timestep, vtkDataArray* buffer)
3001 {
3002   std::map<std::string, VariableInfo>::const_iterator varIter;
3003   for (varIter = this->BlockVariableMap.begin(); varIter != this->BlockVariableMap.end(); ++varIter)
3004   {
3005     const char* nameIn = varIter->first.c_str();
3006     int numComp = varIter->second.NumComponents;
3007 
3008     for (int component = 0; component < numComp; component++)
3009     {
3010       buffer->Initialize();
3011       this->ExtractCellData(nameIn, component, buffer);
3012       int varOutIndex = varIter->second.ScalarOutOffset + component;
3013 
3014       std::map<int, Block>::const_iterator blockIter;
3015       for (blockIter = this->BlockInfoMap.begin(); blockIter != this->BlockInfoMap.end();
3016            ++blockIter)
3017       {
3018         int numElts = blockIter->second.NumElements;
3019         if (numElts < 1)
3020           continue; // no cells in this block
3021 
3022         int defined = this->BlockVariableTruthValue(blockIter->second.OutputIndex, varOutIndex);
3023         if (!defined)
3024           continue; // var undefined in this block
3025 
3026         int id = blockIter->first;
3027         int start = blockIter->second.ElementStartIndex;
3028 
3029         int rc;
3030         if (buffer->IsA("vtkDoubleArray"))
3031         {
3032           vtkDoubleArray* da = vtkArrayDownCast<vtkDoubleArray>(buffer);
3033           rc = ex_put_elem_var(
3034             this->fid, timestep + 1, varOutIndex + 1, id, numElts, da->GetPointer(start));
3035         }
3036         else /* (buffer->IsA ("vtkFloatArray")) */
3037         {
3038           vtkFloatArray* fa = vtkArrayDownCast<vtkFloatArray>(buffer);
3039           rc = ex_put_elem_var(
3040             this->fid, timestep + 1, varOutIndex + 1, id, numElts, fa->GetPointer(start));
3041         }
3042 
3043         if (rc < 0)
3044         {
3045           vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep ex_put_elem_var");
3046           return 0;
3047         }
3048       }
3049     }
3050   }
3051   return 1;
3052 }
3053 
3054 //------------------------------------------------------------------------------
WritePointData(int timestep,vtkDataArray * buffer)3055 int vtkExodusIIWriter::WritePointData(int timestep, vtkDataArray* buffer)
3056 {
3057   if (this->NumPoints == 0)
3058   {
3059     return 1;
3060   }
3061   std::map<std::string, VariableInfo>::const_iterator varIter;
3062   for (varIter = this->NodeVariableMap.begin(); varIter != this->NodeVariableMap.end(); ++varIter)
3063   {
3064     const char* nameIn = varIter->first.c_str();
3065     int numComp = varIter->second.NumComponents;
3066     for (int component = 0; component < numComp; component++)
3067     {
3068       buffer->Initialize();
3069       this->ExtractPointData(nameIn, component, buffer);
3070       int varOutIndex = varIter->second.ScalarOutOffset + component;
3071       int rc;
3072       if (buffer->IsA("vtkDoubleArray"))
3073       {
3074         vtkDoubleArray* da = vtkArrayDownCast<vtkDoubleArray>(buffer);
3075         rc = ex_put_nodal_var(
3076           this->fid, timestep + 1, varOutIndex + 1, this->NumPoints, da->GetPointer(0));
3077       }
3078       else /* (buffer->IsA ("vtkFloatArray")) */
3079       {
3080         vtkFloatArray* fa = vtkArrayDownCast<vtkFloatArray>(buffer);
3081         rc = ex_put_nodal_var(
3082           this->fid, timestep + 1, varOutIndex + 1, this->NumPoints, fa->GetPointer(0));
3083       }
3084 
3085       if (rc < 0)
3086       {
3087         vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep ex_put_nodal_var");
3088         return 0;
3089       }
3090     }
3091   }
3092   return 1;
3093 }
3094 
3095 namespace
3096 {
GetLongestFieldDataName(vtkFieldData * fd)3097 unsigned int GetLongestFieldDataName(vtkFieldData* fd)
3098 {
3099   unsigned int maxName = 0;
3100   for (int i = 0; i < fd->GetNumberOfArrays(); i++)
3101   {
3102     unsigned int length = static_cast<unsigned int>(strlen(fd->GetArrayName(i)));
3103     if (length > maxName)
3104     {
3105       maxName = length;
3106     }
3107   }
3108   return maxName;
3109 }
3110 
GetLongestDataSetName(vtkDataSet * ds)3111 unsigned int GetLongestDataSetName(vtkDataSet* ds)
3112 {
3113   unsigned int maxName = 32;
3114   unsigned int maxDataSetName = GetLongestFieldDataName(ds->GetPointData());
3115   if (maxDataSetName > maxName)
3116   {
3117     maxName = maxDataSetName;
3118   }
3119   maxDataSetName = GetLongestFieldDataName(ds->GetCellData());
3120   if (maxDataSetName > maxName)
3121   {
3122     maxName = maxDataSetName;
3123   }
3124   maxDataSetName = GetLongestFieldDataName(ds->GetFieldData());
3125   if (maxDataSetName > maxName)
3126   {
3127     maxName = maxDataSetName;
3128   }
3129   return maxName;
3130 }
3131 }
3132 
3133 //------------------------------------------------------------------------------
GetMaxNameLength()3134 unsigned int vtkExodusIIWriter::GetMaxNameLength()
3135 {
3136   unsigned int maxName = 32;
3137   if (vtkMultiBlockDataSet* mb = vtkMultiBlockDataSet::SafeDownCast(this->OriginalInput))
3138   {
3139     vtkCompositeDataIterator* iter = mb->NewIterator();
3140     iter->SkipEmptyNodesOn();
3141     for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
3142     {
3143       if (vtkDataSet* dataSet = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject()))
3144       {
3145         unsigned int maxDataSetName = GetLongestDataSetName(dataSet);
3146         if (maxDataSetName > maxName)
3147         {
3148           maxName = maxDataSetName;
3149         }
3150         if (vtkInformation* info = iter->GetCurrentMetaData())
3151         {
3152           if (const char* objectName = info->Get(vtkCompositeDataSet::NAME()))
3153           {
3154             maxDataSetName = static_cast<unsigned int>(strlen(objectName));
3155             if (maxDataSetName > maxName)
3156             {
3157               maxName = maxDataSetName;
3158             }
3159           }
3160         }
3161       }
3162     }
3163     iter->Delete();
3164   }
3165   else if (vtkDataSet* dataSet = vtkDataSet::SafeDownCast(this->OriginalInput))
3166   {
3167     maxName = GetLongestDataSetName(dataSet);
3168   }
3169   return maxName;
3170 }
3171 
3172 //------------------------------------------------------------------------------
WriteNextTimeStep()3173 int vtkExodusIIWriter::WriteNextTimeStep()
3174 {
3175   int rc = 0;
3176   int ts = this->CurrentTimeIndex - this->FileTimeOffset;
3177   float tsv = 0.;
3178   if (this->GetInput()->GetInformation()->Has(vtkDataObject::DATA_TIME_STEP()))
3179   {
3180     tsv = this->GetInput()->GetInformation()->Get(vtkDataObject::DATA_TIME_STEP());
3181   }
3182 
3183   vtkSmartPointer<vtkDataArray> buffer;
3184   if (this->PassDoubles)
3185   {
3186     double dtsv = (double)tsv;
3187     rc = ex_put_time(this->fid, ts + 1, &dtsv);
3188     if (rc < 0)
3189     {
3190       vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep time step values"
3191                     << " fid " << this->fid << " ts " << ts + 1 << " tsv " << tsv);
3192       return 0;
3193     }
3194     buffer = vtkSmartPointer<vtkDoubleArray>::New();
3195   }
3196   else
3197   {
3198     rc = ex_put_time(this->fid, ts + 1, &tsv);
3199     if (rc < 0)
3200     {
3201       vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep time step values"
3202                     << " fid " << this->fid << " ts " << ts + 1 << " tsv " << tsv);
3203       return 0;
3204     }
3205     buffer = vtkSmartPointer<vtkFloatArray>::New();
3206   }
3207 
3208   // Buffer is used to help these determine the type of the data to write out
3209   if (!this->WriteGlobalData(ts, buffer))
3210   {
3211     return 0;
3212   }
3213   if (!this->WriteCellData(ts, buffer))
3214   {
3215     return 0;
3216   }
3217   if (!this->WritePointData(ts, buffer))
3218   {
3219     return 0;
3220   }
3221 
3222   return 1;
3223 }
3224 
CheckBlockInfoMap()3225 void vtkExodusIIWriter::CheckBlockInfoMap()
3226 {
3227   // no op for serial version
3228 }
3229 
SameTypeOfCells(vtkIntArray * cellToBlockId,vtkUnstructuredGrid * input)3230 bool vtkExodusIIWriter::SameTypeOfCells(vtkIntArray* cellToBlockId, vtkUnstructuredGrid* input)
3231 {
3232   if (cellToBlockId->GetNumberOfComponents() != 1 ||
3233     cellToBlockId->GetNumberOfTuples() != input->GetNumberOfCells())
3234   {
3235     return false;
3236   }
3237   std::map<int, int> blockIdToCellType;
3238   for (vtkIdType cellId = 0; cellId < cellToBlockId->GetNumberOfTuples(); ++cellId)
3239   {
3240     vtkIdType blockId = cellToBlockId->GetValue(cellId);
3241     std::map<int, int>::iterator it = blockIdToCellType.find(blockId);
3242     if (it == blockIdToCellType.end())
3243     {
3244       blockIdToCellType[blockId] = input->GetCellType(cellId);
3245     }
3246     else
3247     {
3248       if (it->second != input->GetCellType(cellId))
3249       {
3250         return false;
3251       }
3252     }
3253   }
3254   return true;
3255 }
3256 
GetBlockIdArray(const char * name,vtkUnstructuredGrid * input)3257 vtkIntArray* vtkExodusIIWriter::GetBlockIdArray(const char* name, vtkUnstructuredGrid* input)
3258 {
3259   vtkDataArray* da = nullptr;
3260   vtkCellData* cd = input->GetCellData();
3261   if (name)
3262   {
3263     da = cd->GetArray(name);
3264   }
3265   if (!da)
3266   {
3267     name = "ObjectId";
3268     da = cd->GetArray(name);
3269     if (!da)
3270     {
3271       name = "ElementBlockIds";
3272       da = cd->GetArray(name);
3273     }
3274   }
3275   if (da)
3276   {
3277     vtkIntArray* ia = vtkArrayDownCast<vtkIntArray>(da);
3278     if (ia != nullptr && vtkExodusIIWriter::SameTypeOfCells(ia, input))
3279     {
3280       this->SetBlockIdArrayName(name);
3281       return ia;
3282     }
3283   }
3284   this->SetBlockIdArrayName(nullptr);
3285   if ((this->NumberOfProcesses > 1) &&
3286     // you don't have metadata but you have some tuples.
3287     cd->GetNumberOfTuples() > 0 &&
3288     // depending on what we're trying to write out we may not care about missing metadata
3289     this->IgnoreMetaDataWarning == 0)
3290   {
3291     // Parallel apps must have a global list of all block IDs, plus a
3292     // list of block IDs for each cell.
3293     vtkWarningMacro(<< "Attempting to proceed without metadata");
3294   }
3295   return nullptr;
3296 }
3297