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(¤tTime);
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