/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkExodusIIWriter.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /*---------------------------------------------------------------------------- Copyright (c) Sandia Corporation See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details. ----------------------------------------------------------------------------*/ #include "vtkToolkits.h" #include "vtkObjectFactory.h" #include "vtkExodusIIWriter.h" #include "vtkThreshold.h" #include "vtkModelMetadata.h" #include "vtkUnstructuredGrid.h" #include "vtkDataArray.h" #include "vtkFloatArray.h" #include "vtkDoubleArray.h" #include "vtkUnsignedCharArray.h" #include "vtkCharArray.h" #include "vtkUnsignedIntArray.h" #include "vtkIntArray.h" #include "vtkUnsignedLongArray.h" #include "vtkLongArray.h" #include "vtkUnsignedShortArray.h" #include "vtkShortArray.h" #include "vtkIdTypeArray.h" #include "vtkCellData.h" #include "vtkPointData.h" #include "vtkCellArray.h" #include "vtkInformationVector.h" #include "vtkInformation.h" #ifdef VTK_USE_PARALLEL #include "vtkMultiProcessController.h" #endif #include "vtkstd/map" #include #include #include // TODO - check for errors when there are no element or node variables // TODO - maybe check for ghost cells and omit them, or include them if // the user says to do so // If we are building against a slightly older VTK version, // these cell types are not defined, and won't occur in the input #ifndef VTK_QUADRATIC_WEDGE #define VTK_PENTAGONAL_PRISM 15 #define VTK_HEXAGONAL_PRISM 16 #define VTK_QUADRATIC_WEDGE 26 #define VTK_QUADRATIC_PYRAMID 27 #endif #define FREE(x) if (x) {delete [] x; x = NULL;} #define FREELIST(x, len) \ { \ if (x) \ { \ for (i=0; iDelete(); \ } \ delete [] x; \ } \ x = NULL; \ } vtkCxxRevisionMacro(vtkExodusIIWriter, "$Revision: 1.4 $"); vtkStandardNewMacro(vtkExodusIIWriter); vtkCxxSetObjectMacro(vtkExodusIIWriter, ModelMetadata, vtkModelMetadata); //---------------------------------------------------------------------------- vtkExodusIIWriter::vtkExodusIIWriter() { this->FileName = NULL; this->MyFileName = NULL; this->ModelMetadata = NULL; this->PassDoubles = 0; this->StoreDoubles = -1; // flag that this is not set this->fid = -1; this->InputBlockIdsLength = 0; this->InputBlockIds = NULL; this->InputNumberOfTimeSteps = 0; this->InputCurrentTimeStep = -1; this->InputTimeStepValues = NULL; this->LastTimeStepWritten = -1; this->BlockIdArrayName = NULL; this->GlobalElementIdArrayName = NULL; this->GlobalNodeIdArrayName = NULL; this->BlockIdList = NULL; this->GlobalElementIdList = NULL; this->GlobalNodeIdList = NULL; this->LocalBlockIndexMap = NULL; this->LocalElementIdMap = NULL; this->LocalNodeIdMap = NULL; this->WriteOutBlockIdArray = 0; this->WriteOutGlobalElementIdArray = 0; this->WriteOutGlobalNodeIdArray = 0; this->InitializeBlockLists(); this->InitializeVariableArrayNames(); this->BlockElementVariableTruthTable = NULL; this->AllVariablesDefinedInAllBlocks = 0; this->NumberOfProcesses = 1; this->MyRank = 0; this->MyInput = NULL; this->GhostLevel = 0; this->ErrorStatus = 0; } vtkExodusIIWriter::~vtkExodusIIWriter() { if (!this->BlockIdArrayName && this->BlockIdList) { FREE(this->BlockIdList); // I created this } this->SetFileName(NULL); this->SetMyFileName(NULL); this->SetModelMetadata(NULL); this->SetBlockIdArrayName(NULL); this->SetGlobalElementIdArrayName(NULL); this->SetGlobalNodeIdArrayName(NULL); this->SetTimeStepValues(0, NULL); this->SetAllBlockIds(0, NULL); if (this->LocalElementIdMap) delete this->LocalElementIdMap; if (this->LocalNodeIdMap) delete this->LocalNodeIdMap; this->ClearBlockLists(); this->ClearVariableArrayNames(); FREE(this->BlockElementVariableTruthTable); if (this->MyInput) { this->MyInput->UnRegister(this); this->MyInput->Delete(); } } int vtkExodusIIWriter::FillInputPortInformation( int vtkNotUsed(port), vtkInformation* info ) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid"); return 1; } //---------------------------------------------------------------------------- // Input vtkUnstructuredGrid //---------------------------------------------------------------------------- vtkUnstructuredGrid *vtkExodusIIWriter::GetInput() { vtkUnstructuredGrid *ug = NULL; if (this->MyInput) { ug = this->MyInput; } else { ug = vtkUnstructuredGrid::SafeDownCast(this->Superclass::GetInput()); } return ug; } void vtkExodusIIWriter::SetInput(vtkUnstructuredGrid *ug) { this->Superclass::SetInput(ug); } void vtkExodusIIWriter::SetPassDoubles() { int i; vtkUnstructuredGrid *ug = this->GetInput(); if (!ug) return; // Determine whether we should pass single or double precision // floats to the Exodus Library. We'll look through the arrays // and points in the input and pick the precision of the // first float we see. int da = -1; vtkCellData *cd = ug->GetCellData(); if (cd) { int numCellArrays = cd->GetNumberOfArrays(); for (i=0; iGetArray(i); int type = a->GetDataType(); if (type == VTK_DOUBLE) { da = 1; break; } else if (type == VTK_FLOAT) { da = 0; break; } } } if (da < 0) { vtkPointData *pd = ug->GetPointData(); if (pd) { int numPtArrays = pd->GetNumberOfArrays(); for (i=0; iGetArray(i); int type = a->GetDataType(); if (type == VTK_DOUBLE) { da = 1; break; } else if (type == VTK_FLOAT) { da = 0; break; } } } } if (da < 0) { vtkPoints *pts = ug->GetPoints(); if (pts) { int type = pts->GetDataType(); if (type == VTK_DOUBLE) { da = 1; } else if (type == VTK_FLOAT) { da = 0; } } } this->PassDoubles = 0; if (da == 1) { this->PassDoubles = 1; } if (this->StoreDoubles < 0) { // The default is to store in the // same precision that appears in the input. this->StoreDoubles = this->PassDoubles; } } void vtkExodusIIWriter::RemoveGhostCells() { if (this->MyInput) { this->MyInput->UnRegister(this); this->MyInput->Delete(); this->MyInput = NULL; } vtkUnstructuredGrid *ug = vtkUnstructuredGrid::New(); ug->ShallowCopy(this->GetInput()); vtkDataArray *da = ug->GetCellData()->GetArray("vtkGhostLevels"); if (da) { vtkThreshold *t = vtkThreshold::New(); t->SetInput(ug); t->ThresholdByLower(0); t->SetInputArrayToProcess( 0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_CELLS, "vtkGhostLevels"); t->Update(); this->MyInput = t->GetOutput(); this->MyInput->Register(this); t->Delete(); this->MyInput->GetCellData()->RemoveArray("vtkGhostLevels"); this->MyInput->GetPointData()->RemoveArray("vtkGhostLevels"); this->GhostLevel = 1; } else { this->GhostLevel = 0; } ug->Delete(); } //---------------------------------------------------------------------------- // Values that can be set if there is no metadata //---------------------------------------------------------------------------- void vtkExodusIIWriter::SetTimeStepValues(int n, float *f) { if (this->InputTimeStepValues) { delete [] this->InputTimeStepValues; this->InputTimeStepValues = NULL; } this->InputNumberOfTimeSteps = 0; if (n < 1) return; this->InputNumberOfTimeSteps = n; this->InputTimeStepValues = new float [n]; if (f) { memcpy(this->InputTimeStepValues, f, n * sizeof(float)); } } void vtkExodusIIWriter::SetCurrentTimeStep(int ts) { this->InputCurrentTimeStep = ts; } void vtkExodusIIWriter::SetAllBlockIds(int numEntries, int *blockIds) { if (this->InputBlockIds) { delete [] this->InputBlockIds; this->InputBlockIds = NULL; this->InputBlockIdsLength = 0; } if (numEntries <= 0) return; this->InputBlockIdsLength = numEntries; this->InputBlockIds = new int [numEntries]; if (blockIds) { memcpy(this->InputBlockIds, blockIds, numEntries * sizeof(int)); } } //------------------------------------------------------------------ //------------------------------------------------------------------ void vtkExodusIIWriter::WriteData() { int rc = 0; if ( !this->FileName ) { vtkErrorMacro("No FileName specified."); return; } this->SetPassDoubles(); // does input contain floats or doubles this->RemoveGhostCells(); // Make sure we have all necessary information. If there is no // vtkModelMetadata object, create one with reasonable defaults if // that is possible. rc = this->CheckParameters(); if (rc) { return; } if (this->LastTimeStepWritten >= 0) { this->OpenExodusFile(); rc = this->WriteNextTimeStep(); if (rc) { goto doneError; } goto done; } rc = this->CreateNewExodusFile(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData can't create exodus file"); goto doneError; } rc = this->WriteInitializationParameters(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData init params"); goto doneError; } rc = this->WriteQARecords(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData QA records"); goto doneError; } rc = this->WriteInformationRecords(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData information records"); goto doneError; } rc = this->WritePoints(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData points"); goto doneError; } rc = this->WriteCoordinateNames(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData coordinate names"); goto doneError; } rc = this->WriteGlobalPointIds(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData global point IDs"); goto doneError; } rc = this->WriteBlockInformation(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData block information"); goto doneError; } rc = this->WriteGlobalElementIds(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData global element IDs"); goto doneError; } rc = this->WriteVariableArrayNames(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData variable array names"); goto doneError; } rc = this->WriteNodeSetInformation(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData can't node sets"); goto doneError; } rc = this->WriteSideSetInformation(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData can't side sets"); goto doneError; } rc = this->WriteProperties(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData can't properties"); goto doneError; } rc = this->WriteNextTimeStep(); if (rc) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteData results"); goto doneError; } goto done; doneError: this->SetErrorStatus(1); done: this->SetModelMetadata(NULL); this->CloseExodusFile(); } //------------------------------------------------------------------ // Check input parameters and set reasonable defaults //------------------------------------------------------------------ int vtkExodusIIWriter::CheckParameters() { vtkUnstructuredGrid *input = this->GetInput(); // need input if (input == NULL) { vtkErrorMacro(<< "vtkExodusIIWriter, no input"); return 1; } this->NumberOfProcesses = 1; this->MyRank = 0; #ifdef VTK_USE_PARALLEL vtkMultiProcessController *c = vtkMultiProcessController::GetGlobalController(); if (c) { this->NumberOfProcesses = c->GetNumberOfProcesses(); this->MyRank = c->GetLocalProcessId(); } if (this->GhostLevel > 0) { vtkWarningMacro(<< "ExodusIIWriter ignores ghost level request"); } #endif // What id arrays do we have int HaveGlobalElementIdArray = 0; int HaveGlobalNodeIdArray = 0; int HaveBlockIdArray = 0; vtkDataArray *da = NULL; vtkCellData *cd = input->GetCellData(); if (this->BlockIdArrayName) { da = cd->GetArray(this->BlockIdArrayName); if (da) { HaveBlockIdArray = 1; } else { this->SetBlockIdArrayName(NULL); } } if (!HaveBlockIdArray) { da = cd->GetArray("BlockId"); if (da) { this->SetBlockIdArrayName("BlockId"); HaveBlockIdArray = 1; } } if (HaveBlockIdArray) { da = cd->GetArray(this->BlockIdArrayName); vtkIntArray *ia = vtkIntArray::SafeDownCast(da); if (!ia) { vtkErrorMacro(<< "vtkExodusIIWriter, block ID array is not an integer array"); return 1; } this->BlockIdList = ia->GetPointer(0); } if (this->GlobalElementIdArrayName) { da = cd->GetArray(this->GlobalElementIdArrayName); if (da) { HaveGlobalElementIdArray = 1; } else { this->SetGlobalElementIdArrayName(NULL); } } if (!HaveGlobalElementIdArray) { da = cd->GetArray("GlobalElementId"); if (da) { this->SetGlobalElementIdArrayName("GlobalElementId"); HaveGlobalElementIdArray = 1; } } if (HaveGlobalElementIdArray) { da = cd->GetArray(this->GlobalElementIdArrayName); vtkIntArray *ia = vtkIntArray::SafeDownCast(da); if (!ia) { vtkWarningMacro(<< "vtkExodusIIWriter, element ID array is not an integer array, ignoring it"); this->GlobalElementIdList = NULL; HaveGlobalElementIdArray = 0; } else { this->GlobalElementIdList = ia->GetPointer(0); } } vtkPointData *pd = input->GetPointData(); if (this->GlobalNodeIdArrayName) { da = pd->GetArray(this->GlobalNodeIdArrayName); if (da) { HaveGlobalNodeIdArray = 1; } else { this->SetGlobalNodeIdArrayName(NULL); } } if (!HaveGlobalNodeIdArray) { da = pd->GetArray("GlobalNodeId"); if (da) { this->SetGlobalNodeIdArrayName("GlobalNodeId"); HaveGlobalNodeIdArray = 1; } } if (HaveGlobalNodeIdArray) { da = pd->GetArray(this->GlobalNodeIdArrayName); vtkIntArray *ia = vtkIntArray::SafeDownCast(da); if (!ia) { vtkWarningMacro(<< "vtkExodusIIWriter, node ID array is not an integer array, ignoring it"); this->GlobalNodeIdList = 0; HaveGlobalNodeIdArray = 0; } this->GlobalNodeIdList = ia->GetPointer(0); } if (this->GetModelMetadata()) { // All of the information we'll need is in the ModelMetadata return 0; } int hasPackedMetadata = vtkModelMetadata::HasMetadata(input); if (hasPackedMetadata) { // All the metadata has been packed into field arrays of the ugrid, // probably by the vtkExodusReader or vtkPExodusReader. vtkModelMetadata *mmd = vtkModelMetadata::New(); mmd->Unpack(input, 1); this->SetModelMetadata(mmd); mmd->Delete(); return 0; } int rc = this->CreateExodusModel(); // use sensible defaults if (rc) { return 1; } return 0; } vtkModelMetadata* vtkExodusIIWriter::GetOrCreateModelMetadata() { this->CheckParameters(); return this->GetModelMetadata(); } int vtkExodusIIWriter::CreateExodusModel() { // There is no metadata associated with this input. If we have enough // information, we create reasonable defaults. int i, rc; if ((this->NumberOfProcesses > 1) && ((!this->InputBlockIds) || (!this->BlockIdList))) { // Parallel apps must have a global list of all block IDs, plus a // list of block IDs for each cell. vtkErrorMacro(<< "Can't proceed without metadata. Go back and request metadata from reader."); return 1; } vtkModelMetadata *em = vtkModelMetadata::New(); char *title = new char [MAX_LINE_LENGTH + 1]; time_t currentTime = time(NULL); struct tm *td = localtime(¤tTime); char *stime = asctime(td); sprintf(title, "Created by vtkExodusIIWriter, %s", stime); em->SetTitle(title); char **dimNames = new char * [3]; dimNames[0] = vtkExodusIIWriter::StrDupWithNew("X"); dimNames[1] = vtkExodusIIWriter::StrDupWithNew("Y"); dimNames[2] = vtkExodusIIWriter::StrDupWithNew("Z"); em->SetCoordinateNames(3, dimNames); if (this->InputTimeStepValues) { em->SetTimeSteps(this->InputNumberOfTimeSteps, this->InputTimeStepValues); } if (this->InputBlockIds && this->BlockIdList) { rc = this->CreateBlockIdInformation(em); } else { rc = this->CreateBlockIdInformationFromCellTypes(em); // single process only } if (rc) { return 1; } vtkUnstructuredGrid *ug = this->GetInput(); vtkCellData *cd = ug->GetCellData(); int narrays = cd->GetNumberOfArrays(); int nflattened = 0; char **nms = NULL; char **flattenedNames = NULL; if (narrays > 0) { nms = new char * [narrays]; int *numComponents = new int [narrays]; int *mapToOriginal = new int [narrays]; nflattened = 0; for (i=0; iGetArray(i)->GetName()); numComponents[i] = cd->GetArray(i)->GetNumberOfComponents(); mapToOriginal[i] = nflattened; nflattened += numComponents[i]; } flattenedNames = vtkExodusIIWriter::FlattenOutVariableNames(narrays, nflattened, nms, numComponents); em->SetElementVariableInfo(nflattened, flattenedNames, narrays, nms, numComponents, mapToOriginal); int nblocks = em->GetNumberOfBlocks(); // computed in CreateBlockIdInfo* int *blockSize = em->GetBlockNumberOfElements(); int *tt = new int [nblocks * nflattened]; int index = 0; for (int blockNum=0; blockNum 0) { tt[index++] = 1; // truth table, all ON by default } else { tt[index++] = 0; } } } em->SetElementVariableTruthTable(tt); } vtkPointData *pd = ug->GetPointData(); narrays = pd->GetNumberOfArrays(); nflattened = 0; nms = NULL; flattenedNames = NULL; if (narrays > 0) { nms = new char * [narrays]; int *numComponents = new int [narrays]; int *mapToOriginal = new int [narrays]; nflattened = 0; for (i=0; iGetArray(i)->GetName()); numComponents[i] = pd->GetArray(i)->GetNumberOfComponents(); mapToOriginal[i] = nflattened; nflattened += numComponents[i]; } flattenedNames = vtkExodusIIWriter::FlattenOutVariableNames(narrays, nflattened, nms, numComponents); em->SetNodeVariableInfo(nflattened, flattenedNames, narrays, nms, numComponents, mapToOriginal); } this->SetModelMetadata(em); em->Delete(); return 0; } char **vtkExodusIIWriter::FlattenOutVariableNames( int narrays, int nScalarArrays, char **nms, int *numComponents) { int i; char **newNames = new char * [nScalarArrays]; char **nextName = newNames; for (i=0; i MAX_STR_LENGTH-2) { nms[i][MAX_STR_LENGTH-2] = '\0'; } vtkExodusIIWriter::CreateNamesForScalarArrays(nms[i], nextName, numComponents[i]); nextName += numComponents[i]; } return newNames; } void vtkExodusIIWriter::CreateNamesForScalarArrays(const char *root, char **nms, int numComponents) { int next = 0; if (numComponents == 1) { nms[next++] = vtkExodusIIWriter::StrDupWithNew(root); } else if (numComponents <= 3) { char *n = new char [MAX_STR_LENGTH + 1]; sprintf(n, "%s_X", root); nms[next++] = n; n = new char [MAX_STR_LENGTH + 1]; sprintf(n, "%s_Y", root); nms[next++] = n; if (numComponents == 3) { n = new char [MAX_STR_LENGTH + 1]; sprintf(n, "%s_Z", root); nms[next++] = n; } } else { for (int j=0; jGetInput(); vtkUnsignedCharArray *cellTypes = ug->GetCellTypesArray(); vtkIdType ncells = ug->GetNumberOfCells(); int nblocks = this->InputBlockIdsLength; int i; if (nblocks < 1) return 1; em->SetNumberOfBlocks(nblocks); em->SetBlockIds(this->InputBlockIds); char **blockNames = new char * [nblocks]; int *numElements = new int [nblocks]; int *numNodesPerElement = new int [nblocks]; int *numAttributes = new int [nblocks]; vtkstd::map idxMap; vtkstd::map::iterator it; for (i=0; iInputBlockIds[i]; idxMap.insert(vtkstd::map::value_type(id, i)); int cellType = vtkExodusIIWriter::FindCellType(id, this->BlockIdList, cellTypes->GetPointer(0), ncells); blockNames[i] = vtkExodusIIWriter::GetCellTypeName(cellType); numElements[i] = 0; numNodesPerElement[i] = 0; numAttributes[i] = 0; } int *bids = this->BlockIdList; int err=0; for (i=0; isecond; int numNodes = ug->GetCell(i)->GetNumberOfPoints(); if (numElements[idx] == 0) { numNodesPerElement[idx] = numNodes; } else if (numNodes != numNodesPerElement[idx]) { err=1; vtkErrorMacro(<< "Each cell in a block must have the same number of points"); break; } numElements[idx]++; } if (err) { FREELIST(blockNames, nblocks); delete [] numElements; delete [] numNodesPerElement; delete [] numAttributes; } else { em->SetBlockElementType(blockNames); em->SetBlockNumberOfElements(numElements); em->SetBlockNodesPerElement(numNodesPerElement); em->SetBlockNumberOfAttributesPerElement(numAttributes); } return err; } int vtkExodusIIWriter::CreateBlockIdInformationFromCellTypes(vtkModelMetadata *em) { int i; vtkUnstructuredGrid *ug = this->GetInput(); int ncells = ug->GetNumberOfCells(); vtkUnsignedCharArray *cellTypes = ug->GetCellTypesArray(); unsigned char *ct = cellTypes->GetPointer(0); int nTypes = 0; vtkstd::map idxMap; vtkstd::map::iterator it; for (i=0; i::iterator, bool> inserted = idxMap.insert(vtkstd::map::value_type((int)ct[i], nTypes)); if (inserted.second) { nTypes++; } } int *types = new int [nTypes]; int min = 1; for (it = idxMap.begin(); it != idxMap.end(); ++it) { int typeNum = (int)it->first; int idx = it->second; types[idx] = typeNum; if (typeNum < min) { min = typeNum; } } // slight problem - block IDs must be 1 or greater int offset = 0; if (min < 1) { offset = (min * -1) + 1; } char **blockNames = new char * [nTypes]; int *numElements = new int [nTypes]; int *numNodesPerElement = new int [nTypes]; int *numAttributes = new int [nTypes]; for (i=0; iSetNumberOfBlocks(nTypes); em->SetBlockIds(types); this->BlockIdList = new int [ncells]; int err = 0; for (i=0; isecond; int numNodes = ug->GetCell(i)->GetNumberOfPoints(); if (numElements[idx] == 0) { numNodesPerElement[idx] = numNodes; } else if (numNodes != numNodesPerElement[idx]) { vtkErrorMacro(<< "Exodus writer fails when same cell types have different number of nodes"); err = 1; break; } this->BlockIdList[i] = cellType + offset; numElements[idx]++; } if (err) { FREELIST(blockNames, nTypes); delete [] numElements; delete [] numNodesPerElement; delete [] numAttributes; } else { em->SetBlockElementType(blockNames); em->SetBlockNumberOfElements(numElements); em->SetBlockNodesPerElement(numNodesPerElement); em->SetBlockNumberOfAttributesPerElement(numAttributes); } return err; } char *vtkExodusIIWriter::GetCellTypeName(int t) { if (MAX_STR_LENGTH < 32) return NULL; char *nm = new char [MAX_STR_LENGTH + 1]; switch (t) { case VTK_EMPTY_CELL: strcpy(nm, "empty cell"); break; case VTK_VERTEX: strcpy(nm, "vertex"); break; case VTK_POLY_VERTEX: strcpy(nm, "polyvertex"); break; case VTK_LINE: strcpy(nm, "line"); break; case VTK_POLY_LINE: strcpy(nm, "polyline"); break; case VTK_TRIANGLE: strcpy(nm, "TRIANGLE"); break; case VTK_TRIANGLE_STRIP: strcpy(nm, "triangle strip"); break; case VTK_POLYGON: strcpy(nm, "polygon"); break; case VTK_PIXEL: strcpy(nm, "pixel"); break; case VTK_QUAD: strcpy(nm, "quad"); break; case VTK_TETRA: strcpy(nm, "TETRA"); break; case VTK_VOXEL : strcpy(nm, "voxel"); break; case VTK_HEXAHEDRON: strcpy(nm, "HEX"); break; case VTK_WEDGE: strcpy(nm, "wedge"); break; case VTK_PYRAMID: strcpy(nm, "pyramid"); break; case VTK_PENTAGONAL_PRISM: strcpy(nm, "pentagonal prism"); break; case VTK_HEXAGONAL_PRISM: strcpy(nm, "hexagonal prism"); break; case VTK_QUADRATIC_EDGE: strcpy(nm, "quadratic edge"); break; case VTK_QUADRATIC_TRIANGLE: strcpy(nm, "quadratic triangle"); break; case VTK_QUADRATIC_QUAD: strcpy(nm, "quadratic quad"); break; case VTK_QUADRATIC_TETRA: strcpy(nm, "quadratic tetra"); break; case VTK_QUADRATIC_HEXAHEDRON: strcpy(nm, "quadratic hexahedron"); break; case VTK_QUADRATIC_WEDGE: strcpy(nm, "quadratic wedge"); break; case VTK_QUADRATIC_PYRAMID: strcpy(nm, "quadratic pyramid"); break; case VTK_CONVEX_POINT_SET: strcpy(nm, "convex point set"); break; case VTK_PARAMETRIC_CURVE: strcpy(nm, "parametric curve"); break; case VTK_PARAMETRIC_SURFACE: strcpy(nm, "parametric surface"); break; case VTK_PARAMETRIC_TRI_SURFACE: strcpy(nm, "parametric tri surface"); break; case VTK_PARAMETRIC_QUAD_SURFACE: strcpy(nm, "parametric quad surface"); break; case VTK_PARAMETRIC_TETRA_REGION: strcpy(nm, "parametric tetra region"); break; case VTK_PARAMETRIC_HEX_REGION: strcpy(nm, "paramertric hex region"); break; default: strcpy(nm, "unknown cell type"); break; } return nm; } //------------------------------------------------------------------ // Open or create an Exodus II file //------------------------------------------------------------------ void vtkExodusIIWriter::CloseExodusFile() { if (this->fid >= 0) { ex_close(this->fid); this->fid = -1; return; } } int vtkExodusIIWriter::OpenExodusFile() { this->CloseExodusFile(); int compWordSize= (this->PassDoubles ? sizeof(double) : sizeof(float)); int IOWordSize = (this->StoreDoubles ? sizeof(double) : sizeof(float)); float version = 0.0; this->fid = ex_open(this->MyFileName, EX_WRITE, &compWordSize, &IOWordSize, &version); int fail = (this->fid < 0); return fail; } int vtkExodusIIWriter::CreateNewExodusFile() { if (this->NumberOfProcesses == 1) { if (this->FileName) { this->SetMyFileName(this->GetFileName()); } else { this->SetMyFileName("./ExodusIIWriter.out.exo"); } } else { char *nm = new char [1024]; if (this->FileName == NULL) { sprintf(nm, "./ExodusIIWriter.exo.%04d.%04d", this->NumberOfProcesses, this->MyRank); } else { sprintf(nm, "%s.%04d.%04d", this->FileName, this->NumberOfProcesses, this->MyRank); } this->SetMyFileName(nm); delete [] nm; } int compWordSize= (this->PassDoubles ? sizeof(double) : sizeof(float)); int IOWordSize = (this->StoreDoubles ? sizeof(double) : sizeof(float)); this->fid = ex_create(this->MyFileName, EX_CLOBBER, &compWordSize, &IOWordSize); int fail = (this->fid < 0); return fail; } //======================================================================== // MAPPINGS // Convert local to global IDs and vice versa //======================================================================== int vtkExodusIIWriter::GetBlockLocalIndex(int id) { if (!this->LocalBlockIndexMap) { this->LocalBlockIndexMap = new vtkstd::map; for (int i=0; iNumberOfElementBlocks; i++) { int gid = this->BlockIds[i]; this->LocalBlockIndexMap->insert(vtkstd::map::value_type(gid, i)); } } vtkstd::map::iterator mapit = this->LocalBlockIndexMap->find(id); if (mapit == this->LocalBlockIndexMap->end()) { return -1; } else { return mapit->second; } } int vtkExodusIIWriter::GetElementLocalId(int id) { if (!this->LocalElementIdMap) { vtkUnstructuredGrid *ug = this->GetInput(); int ncells = ug->GetNumberOfCells(); this->LocalElementIdMap = new vtkstd::map; for (int i=0; iGlobalElementIdList[i]; this->LocalElementIdMap->insert(vtkstd::map::value_type(gid, i)); } } vtkstd::map::iterator mapit = this->LocalElementIdMap->find(id); if (mapit == this->LocalElementIdMap->end()) { return -1; } else { return mapit->second; } } int vtkExodusIIWriter::GetNodeLocalId(int id) { if (!this->LocalNodeIdMap) { vtkUnstructuredGrid *ug = this->GetInput(); int npoints = ug->GetNumberOfPoints(); this->LocalNodeIdMap = new vtkstd::map; for (int i=0; iGlobalNodeIdList[i]; this->LocalNodeIdMap->insert(vtkstd::map::value_type(gid, i)); } } vtkstd::map::iterator mapit = this->LocalNodeIdMap->find(id); if (mapit == this->LocalNodeIdMap->end()) { return -1; } else { return mapit->second; } } //======================================================================== // VARIABLE ARRAYS: // CONVERT VECTOR ARRAYS TO APPROPRIATELY NAMED SCALAR ARRAYS //======================================================================== void vtkExodusIIWriter::InitializeVariableArrayNames() { this->InputElementArrayNames = NULL; this->OutputElementArrayNames = NULL; this->InputElementArrayComponent = NULL; this->NumberOfScalarElementArrays = 0; this->InputNodeArrayNames = NULL; this->OutputNodeArrayNames = NULL; this->InputNodeArrayComponent = NULL; this->NumberOfScalarNodeArrays = 0; } void vtkExodusIIWriter::ClearVariableArrayNames() { int i; int n = this->NumberOfScalarElementArrays; FREELIST(this->InputElementArrayNames, n); FREELIST(this->OutputElementArrayNames, n); FREE(this->InputElementArrayComponent); this->NumberOfScalarElementArrays = 0; n = this->NumberOfScalarNodeArrays; FREELIST(this->InputNodeArrayNames, n); FREELIST(this->OutputNodeArrayNames, n); FREE(this->InputNodeArrayComponent); this->NumberOfScalarNodeArrays = 0; } int vtkExodusIIWriter::WriteVariableArrayNames() { int i, j; int rc = 0; this->ClearVariableArrayNames(); vtkUnstructuredGrid *ug = this->GetInput(); vtkModelMetadata *mmd = this->GetModelMetadata(); // 1. We convert vector arrays to individual scalar arrays, using // their original names if we have those. // 2. For the element variables, create the element/block truth table. int checkAndSkipEltIds = 0; int checkAndSkipNodeIds = 0; int checkAndSkipBlockIds = 0; if (this->GlobalElementIdArrayName && !this->WriteOutGlobalElementIdArray) { checkAndSkipEltIds = 1; } if (this->GlobalNodeIdArrayName && !this->WriteOutGlobalNodeIdArray) { checkAndSkipNodeIds = 1; } if (this->BlockIdArrayName && !this->WriteOutBlockIdArray) { checkAndSkipBlockIds = 1; } // CELL (ELEMENT) VARIABLES vtkCellData *cd = ug->GetCellData(); int numCellArrays = cd->GetNumberOfArrays(); int numCellScalars = 0; int *skipCellArray = NULL; if (numCellArrays > 0) { skipCellArray = new int [numCellArrays]; memset(skipCellArray, 0, sizeof(int) * numCellArrays); for (i=0; iGetArray(i)->GetName(); if (!strcmp(nm, this->GlobalElementIdArrayName)) { skipCellArray[i] = 1; continue; } } if (checkAndSkipBlockIds) { const char *nm = cd->GetArray(i)->GetName(); if (!strcmp(nm, this->BlockIdArrayName)) { skipCellArray[i] = 1; continue; } } int numComponents = cd->GetArray(i)->GetNumberOfComponents(); numCellScalars += numComponents; } if (numCellScalars > 0) { this->InputElementArrayNames = new char * [numCellScalars]; this->OutputElementArrayNames = new char * [numCellScalars]; this->InputElementArrayComponent = new int [numCellScalars]; char **tempNames = new char * [numCellScalars]; int nextScalar = 0; for (i=0; iGetArray(i); this->SetNewElementVariableNames(da, tempNames); int numComponents = da->GetNumberOfComponents(); const char *arrayName = da->GetName(); for (j=0; jInputElementArrayComponent[nextScalar] = j; this->InputElementArrayNames[nextScalar] = vtkExodusIIWriter::StrDupWithNew(arrayName); this->OutputElementArrayNames[nextScalar] = tempNames[j]; nextScalar++; } } delete [] tempNames; } delete [] skipCellArray; } this->NumberOfScalarElementArrays = numCellScalars; // BLOCK/ELEMENT TRUTH TABLE int nblocks = mmd->GetNumberOfBlocks(); int nelementVars = this->NumberOfScalarElementArrays; int ttsize = nblocks * nelementVars; int allDefined = 1; int ttValue = 0; if (ttsize > 0) { this->BlockElementVariableTruthTable = new int [ttsize]; int *tt = this->BlockElementVariableTruthTable; for (i=0; iNumberOfElementsPerBlock[i] == 0) { ttValue = 0; } else { ttValue = mmd->ElementVariableIsDefinedInBlock( this->OutputElementArrayNames[j], this->BlockIds[i]); } *tt++ = ttValue; if (allDefined && (ttValue == 0)) { allDefined = 0; } } } } this->AllVariablesDefinedInAllBlocks = allDefined; if (numCellScalars > 0) { rc = ex_put_var_param(this->fid, "E", numCellScalars); if (rc == 0) { rc = ex_put_var_names(this->fid, "E", numCellScalars, this->OutputElementArrayNames); if (rc == 0) { rc = ex_put_elem_var_tab(this->fid, this->NumberOfElementBlocks, numCellScalars, this->BlockElementVariableTruthTable); } } if (rc < 0) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames cell variables"); return 1; } } // POINT (NODE) VARIABLES vtkPointData *pd = ug->GetPointData(); int numPointArrays = pd->GetNumberOfArrays(); int numPointScalars = 0; int *skipPointArray = NULL; if (numPointArrays > 0) { skipPointArray = new int [numPointArrays]; memset(skipPointArray, 0, sizeof(int) * numPointArrays); for (i=0; iGetArray(i)->GetName(); if (!strcmp(nm, this->GlobalNodeIdArrayName)) { skipPointArray[i] = 1; continue; } } int numComponents = pd->GetArray(i)->GetNumberOfComponents(); numPointScalars += numComponents; } if (numPointScalars > 0) { this->InputNodeArrayNames = new char * [numPointScalars]; this->OutputNodeArrayNames = new char * [numPointScalars]; this->InputNodeArrayComponent = new int [numPointScalars]; char **tempNames = new char * [numPointScalars]; int nextScalar = 0; for (i=0; iGetArray(i); this->SetNewNodeVariableNames(da, tempNames); int numComponents = da->GetNumberOfComponents(); const char *arrayName = da->GetName(); for (j=0; jInputNodeArrayComponent[nextScalar] = j; this->InputNodeArrayNames[nextScalar] = vtkExodusIIWriter::StrDupWithNew(arrayName); this->OutputNodeArrayNames[nextScalar] = tempNames[j]; nextScalar++; } } delete [] tempNames; } delete [] skipPointArray; } this->NumberOfScalarNodeArrays = numPointScalars; if (numPointScalars > 0) { rc = ex_put_var_param(this->fid, "N", numPointScalars); if (rc == 0) { rc = ex_put_var_names(this->fid, "N", numPointScalars, this->OutputNodeArrayNames); } if (rc < 0) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames point variables"); return 1; } } // GLOBAL VARIABLES int ngvars = mmd->GetNumberOfGlobalVariables(); if (ngvars > 0) { char **names = mmd->GetGlobalVariableNames(); rc = ex_put_var_param(this->fid, "G", ngvars); if (rc == 0) { rc = ex_put_var_names(this->fid, "G", ngvars, names); } if (rc < 0) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteVariableArrayNames global variables"); return 1; } } return 0; } void vtkExodusIIWriter::SetNewNodeVariableNames(vtkDataArray *da, char **nm) { int i; int numComp = da->GetNumberOfComponents(); vtkModelMetadata *em = this->GetModelMetadata(); const char *arrayName = da->GetName(); if (numComp == 1) { nm[0] = vtkExodusIIWriter::StrDupWithNew(arrayName); return; } char *orig = em->FindOriginalNodeVariableName(arrayName, 0); if (!orig) { // just make up sensible names for the component arrays vtkExodusIIWriter::CreateNamesForScalarArrays(arrayName, nm, numComp); return; } int err = 0; for (i=0; iFindOriginalNodeVariableName(arrayName, comp); if (!orig ) { err = 1; // we'll just have to make up names break; } nm[comp] = vtkExodusIIWriter::StrDupWithNew(orig); } } if (err) { for (i=0; iGetNumberOfComponents(); vtkModelMetadata *em = this->GetModelMetadata(); const char *arrayName = da->GetName(); if (numComp == 1) { nm[0] = vtkExodusIIWriter::StrDupWithNew(arrayName); return; } char *orig = em->FindOriginalElementVariableName(arrayName, 0); if (!orig) { // just make up sensible names for the component arrays vtkExodusIIWriter::CreateNamesForScalarArrays(arrayName, nm, numComp); return; } int err = 0; for (i=0; iFindOriginalElementVariableName(arrayName, comp); if (!orig ) { err = 1; // we'll just have to make up names break; } nm[comp] = vtkExodusIIWriter::StrDupWithNew(orig); } } if (err) { for (i=0; iGetPointer(0); \ if (idx == NULL) \ { \ p += comp; \ for (i=0; iGetNumberOfComponents(); if (numComp <= comp) return NULL; int nvals = da->GetNumberOfTuples(); if (nvals < 1) return NULL; int type = da->GetDataType(); if ((type == VTK_DOUBLE) && (numComp == 1) && (idx == NULL)) { vtkDoubleArray *a = vtkDoubleArray::SafeDownCast(da); return a->GetPointer(0); } // converting to native type is much faster than doing GetTuple double *val = new double [nvals]; switch (type) { case VTK_DOUBLE: COPY_COMPONENT(vtkDoubleArray, double, double) break; case VTK_CHAR: COPY_COMPONENT(vtkCharArray, char, double) break; case VTK_UNSIGNED_CHAR: COPY_COMPONENT(vtkUnsignedCharArray, unsigned char, double) break; case VTK_SHORT: COPY_COMPONENT(vtkShortArray, short, double) break; case VTK_UNSIGNED_SHORT: COPY_COMPONENT(vtkUnsignedShortArray, unsigned short, double) break; case VTK_INT: COPY_COMPONENT(vtkIntArray, int, double) break; case VTK_UNSIGNED_INT: COPY_COMPONENT(vtkUnsignedIntArray, unsigned int, double) break; case VTK_LONG: COPY_COMPONENT(vtkLongArray, long, double) break; case VTK_UNSIGNED_LONG: COPY_COMPONENT(vtkUnsignedLongArray, unsigned long, double) break; case VTK_FLOAT: COPY_COMPONENT(vtkFloatArray, float, double) break; case VTK_ID_TYPE: COPY_COMPONENT(vtkIdTypeArray, vtkIdType, double) break; default: vtkErrorMacro(<< "vtkExodusIIWriter::ExtractComponentD bad type"); break; } return val; } float *vtkExodusIIWriter::ExtractComponentF(vtkDataArray *da, int comp, int *idx) { int i; int numComp = da->GetNumberOfComponents(); if (numComp < comp) return NULL; int nvals = da->GetNumberOfTuples(); if (nvals < 1) return NULL; float *val = new float [nvals]; int type = da->GetDataType(); if ((type == VTK_FLOAT) && (numComp == 1) && (idx == NULL)) { vtkFloatArray *a = vtkFloatArray::SafeDownCast(da); return a->GetPointer(0); } // converting to native type is much faster than doing GetTuple switch (type) { case VTK_FLOAT: COPY_COMPONENT(vtkFloatArray, float, float) break; case VTK_CHAR: COPY_COMPONENT(vtkCharArray, char, float) break; case VTK_UNSIGNED_CHAR: COPY_COMPONENT(vtkUnsignedCharArray, unsigned char, float) break; case VTK_SHORT: COPY_COMPONENT(vtkShortArray, short, float) break; case VTK_UNSIGNED_SHORT: COPY_COMPONENT(vtkUnsignedShortArray, unsigned short, float) break; case VTK_INT: COPY_COMPONENT(vtkIntArray, int, float) break; case VTK_UNSIGNED_INT: COPY_COMPONENT(vtkUnsignedIntArray, unsigned int, float) break; case VTK_LONG: COPY_COMPONENT(vtkLongArray, long, float) break; case VTK_UNSIGNED_LONG: COPY_COMPONENT(vtkUnsignedLongArray, unsigned long, float) break; case VTK_DOUBLE: COPY_COMPONENT(vtkDoubleArray, double, float) break; case VTK_ID_TYPE: COPY_COMPONENT(vtkIdTypeArray, vtkIdType, float) break; default: vtkErrorMacro(<< "vtkExodusIIWriter::ExtractComponentF bad type"); break; } return val; } int vtkExodusIIWriter::GetTimeStepIndex() { int ts = -1; if (this->InputCurrentTimeStep >= 0) { // User told writer which time step to call this results // data when we write it out. ts = this->InputCurrentTimeStep; } else if (this->GetModelMetadata()->GetTimeStepValues()) { // The time step index is in the metadata, the same index as // when the file was read in. ts = this->GetModelMetadata()->GetTimeStepIndex(); } if (ts < 0) { // We don't have metadata and the user didn't specify anything // to the writer. We just start at 1 and go up by 1 for each // write. ts = this->LastTimeStepWritten + 1; } this->LastTimeStepWritten = ts; return ts; } float vtkExodusIIWriter::GetTimeStepValue(int ts) { float val = (float)ts; // default vtkModelMetadata *mmd = this->GetModelMetadata(); float *tsv = mmd->GetTimeStepValues(); int numts = mmd->GetNumberOfTimeSteps(); int realTimeStep = mmd->GetTimeStepIndex(); if (numts > 0) { if (realTimeStep >= 0) { // It doesn't matter what time step we are saying it is // in the output file, this is the actual time stamp // associated with this results data. val = tsv[realTimeStep]; } else if ((ts >= 0) && (ts < numts)) { // We didn't have vtkModelMetadata, but the user of this writer // input a list of time values. Here's the value for this time // step index. val = tsv[ts]; } else if (ts >= numts) { // We didn't have vtkModelMetadata, but the user of this writer // input a list of 1 or more time values. Here's the value for // this time step index if we extrapolate from the last value. int extra = ts - numts + 1; float endOfTime = tsv[numts - 1]; float diff = endOfTime; if (numts >= 2) { diff -= tsv[numts - 2]; } val = endOfTime + (extra * diff); } } return val; } int vtkExodusIIWriter::WriteNextTimeStep() { int i, idIdx; int rc = 0; vtkModelMetadata *mmd = this->GetModelMetadata(); int ts = this->GetTimeStepIndex(); float tsv = this->GetTimeStepValue(ts); if (this->PassDoubles) { double dtsv = (double)tsv; rc = ex_put_time(this->fid, ts + 1, &dtsv); } else { rc = ex_put_time(this->fid, ts + 1, &tsv); } if (rc < 0) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep time step values"); return 1; } vtkUnstructuredGrid *ug = this->GetInput(); int nblocks = this->NumberOfElementBlocks; int npoints = ug->GetNumberOfPoints(); int nCellArrays = this->NumberOfScalarElementArrays; int nPointArrays = this->NumberOfScalarNodeArrays; int nGlobalVariables = mmd->GetNumberOfGlobalVariables(); // CELL VARIABLES for (i=0; iInputElementArrayNames[i]; int component = this->InputElementArrayComponent[i]; vtkDataArray *da = ug->GetCellData()->GetArray(nameIn); int type = da->GetDataType(); int size = da->GetNumberOfComponents(); // We may be able to pass the Exodus library a pointer right into // the input's variable arrays. If not (we had to massage the arrays // in some way) then deleteIt is TRUE, meaning we created a copy to // be deleted. int deleteIt = ((size > 1) || ((type != VTK_DOUBLE) && this->PassDoubles) || ((type != VTK_FLOAT) && !this->PassDoubles) || (this->ElementIndex != NULL)); if (this->PassDoubles) { double *vars = this->ExtractComponentD(da, component, this->ElementIndex); for (idIdx=0; idIdx < nblocks; idIdx++) { int numElts = this->NumberOfElementsPerBlock[idIdx]; if (numElts < 1) continue; // no cells in this block int defined = this->BlockVariableTruthValue(idIdx, i); if (!defined) continue; // var undefined in this block int id = this->BlockIds[idIdx]; int first = this->BlockElementStart[idIdx]; rc = ex_put_elem_var(this->fid, ts + 1, i + 1, id, numElts, vars + first); if (rc < 0) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep ex_put_elem_var"); return 1; } } if (deleteIt) { delete [] vars; } } else { float *vars = this->ExtractComponentF(da, component, this->ElementIndex); for (idIdx=0; idIdx < nblocks; idIdx++) { int numElts = this->NumberOfElementsPerBlock[idIdx]; if (numElts < 1) continue; // no cells in this block int defined = this->BlockVariableTruthValue(idIdx, i); if (!defined) continue; // var undefined in this block int id = this->BlockIds[idIdx]; int first = this->BlockElementStart[idIdx]; rc = ex_put_elem_var(this->fid, ts + 1, i + 1, id, numElts, vars + first); if (rc < 0) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep ex_put_elem_var"); return 1; } } if (deleteIt) { delete [] vars; } } } // POINT VARIABLES for (i=0; iInputNodeArrayNames[i]; int component = this->InputNodeArrayComponent[i]; vtkDataArray *da = ug->GetPointData()->GetArray(nameIn); int type = da->GetDataType(); int size = da->GetNumberOfComponents(); int deleteIt = ((size > 1) || ((type != VTK_DOUBLE) && this->PassDoubles) || ((type != VTK_FLOAT) && !this->PassDoubles)); if (this->PassDoubles) { double *vars = this->ExtractComponentD(da, component, NULL); rc = ex_put_nodal_var(this->fid, ts + 1, i + 1, npoints, vars); if (deleteIt) { delete [] vars; } } else { float *vars = this->ExtractComponentF(da, component, NULL); rc = ex_put_nodal_var(this->fid, ts + 1, i + 1, npoints, vars); if (deleteIt) { delete [] vars; } } if (rc < 0) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep ex_put_nodal_var"); return 1; } } // GLOBAL VARIABLES if (nGlobalVariables > 0) { float *vals = mmd->GetGlobalVariableValue(); if (this->PassDoubles) { double *dvals = new double [nGlobalVariables]; for (int ii=0; iifid, ts + 1, nGlobalVariables, dvals); delete [] dvals; } else { rc = ex_put_glob_vars(this->fid, ts + 1, nGlobalVariables, vals); } if (rc < 0) { vtkErrorMacro(<< "vtkExodusIIWriter::WriteNextTimeStep ex_put_glob_vars"); return 1; } } return 0; } int vtkExodusIIWriter::BlockVariableTruthValue(int blockIdx, int varIdx) { int tt=0; int nvars = this->NumberOfScalarElementArrays; int nblocks = this->NumberOfElementBlocks; if (this->AllVariablesDefinedInAllBlocks) { tt = 1; } else if ( (blockIdx >= 0) && (blockIdx < nblocks) && (varIdx >= 0) && (varIdx < nvars)) { tt = this->BlockElementVariableTruthTable[(blockIdx * nvars) + varIdx]; } else { vtkErrorMacro(<< "vtkExodusIIWriter::BlockVariableTruthValue invalid index"); } return tt; } //----------------------------------------------------------------------- // Properties //----------------------------------------------------------------------- int vtkExodusIIWriter::WriteProperties() { int rc = 0; int i; vtkModelMetadata *em = this->GetModelMetadata(); int nbprop = em->GetNumberOfBlockProperties(); int nnsprop = em->GetNumberOfNodeSetProperties(); int nssprop = em->GetNumberOfSideSetProperties(); if (nbprop) { char **names = em->GetBlockPropertyNames(); // Exodus library "feature". By convention there is a property // array called "ID", the value of which is the ID of the block, // node set or side set. This property is special. For example, // if you change the property value for a block, that block's // block ID is changed. I had no idea *how* special this property // was, however. If you use ex_put_prop_names to tell the library // what your property names are, and "ID" happens to be one of those // names, then the library fills out the whole property array for // you. Then if you follow this call with ex_put_prop_array for // each property array, including "ID", you get *two* arrays named // "ID". This is not documented, and totally unexpected. // // ex_put_prop_names is not required, it's just more efficient to // call it before all the ex_put_prop_array calls. So we are // not going to call it. // // rc = ex_put_prop_names(this->fid, EX_ELEM_BLOCK, nbprop, names); if (rc >= 0) { int *values = em->GetBlockPropertyValue(); for (i=0; ifid, EX_ELEM_BLOCK, names[i], values); if (rc) break; values += this->NumberOfElementBlocks; } } } if (!rc && nnsprop) { char **names = em->GetNodeSetPropertyNames(); int nnsets = em->GetNumberOfNodeSets(); // rc = ex_put_prop_names(this->fid, EX_NODE_SET, nnsprop, names); if (rc >= 0) { int *values = em->GetNodeSetPropertyValue(); for (i=0; ifid, EX_NODE_SET, names[i], values); if (rc) break; values += nnsets; } } } if (!rc && nssprop) { char **names = em->GetSideSetPropertyNames(); int nssets = em->GetNumberOfSideSets(); // rc = ex_put_prop_names(this->fid, EX_SIDE_SET, nssprop, names); if (rc >= 0) { int *values = em->GetSideSetPropertyValue(); for (i=0; ifid, EX_SIDE_SET, names[i], values); if (rc) break; values += nssets; } } } int fail = (rc < 0); return fail; } //----------------------------------------------------------------------- // Side sets and node sets //----------------------------------------------------------------------- int vtkExodusIIWriter::WriteSideSetInformation() { int rc= 0; int i, j, k; vtkModelMetadata *em = this->GetModelMetadata(); int nssets = em->GetNumberOfSideSets(); if (nssets < 1) return 0; // If cells are written out to file in a different order than // they appear in the input, we need a mapping from their internal // id in the input to their internal id in the output. vtkstd::map newElementId; vtkstd::map::iterator idIt; if (this->ElementIndex) { vtkUnstructuredGrid *ug = this->GetInput(); int ncells = ug->GetNumberOfCells(); for (i=0; i::value_type(this->ElementIndex[i], i)); } } int nids = em->GetSumSidesPerSideSet(); if (nids < 1) { int *buf = new int [nssets]; memset(buf, 0, sizeof(int) * nssets); rc = ex_put_concat_side_sets(this->fid, em->GetSideSetIds(), buf, buf, buf, buf, NULL, NULL, NULL); delete [] buf; return (rc < 0); } int *ssSize = new int [nssets]; int *ssNumDF = new int [nssets]; int *ssIdIdx = new int [nssets]; int *ssDFIdx = new int [nssets]; int ndf = em->GetSumDistFactPerSideSet(); int *idBuf = new int [nids]; int *sideBuf = new int [nids]; float *dfBuf = NULL; double *dfBufD = NULL; if (ndf) { if (this->PassDoubles) { dfBufD = new double [ndf]; } else { dfBuf = new float [ndf]; } } int *emSsSize = em->GetSideSetSize(); int *emIdIdx = em->GetSideSetListIndex(); int *emDFIdx = em->GetSideSetDistributionFactorIndex(); int nextId = 0; int nextDF = 0; int outputInternalId = 0; for (i=0; iGetSideSetElementList() + emIdIdx[i]; int *sides = em->GetSideSetSideList() + emIdIdx[i]; int *numDFPerSide = em->GetSideSetNumDFPerSide() + emIdIdx[i]; float *df = NULL; if (ndf > 0) { df = em->GetSideSetDistributionFactors() + emDFIdx[i]; } for (j=0; j< emSsSize[i]; j++) { // Have to check if this element is still in the ugrid. // It may have been deleted since the ExodusModel was created. int lid = this->GetElementLocalId(ids[j]); if (lid >= 0) { ssSize[i]++; if (this->ElementIndex) { idIt = newElementId.find(lid); outputInternalId = idIt->second + 1; } else { outputInternalId = lid + 1; } idBuf[nextId] = outputInternalId; sideBuf[nextId] = sides[j]; nextId++; if (numDFPerSide[j] > 0) { ssNumDF[i] += numDFPerSide[j]; if (this->PassDoubles) { for (k=0; k < numDFPerSide[j]; k++) { dfBufD[nextDF++] = (double)df[k]; } } else { for (k=0; k < numDFPerSide[j]; k++) { dfBuf[nextDF++] = df[k]; } } } } if (df) df += numDFPerSide[j]; } } if (this->PassDoubles) { rc = ex_put_concat_side_sets(this->fid, em->GetSideSetIds(), ssSize, ssNumDF, ssIdIdx, ssDFIdx, idBuf, sideBuf, dfBufD); } else { rc = ex_put_concat_side_sets(this->fid, em->GetSideSetIds(), ssSize, ssNumDF, ssIdIdx, ssDFIdx, idBuf, sideBuf, dfBuf); } delete [] ssSize; delete [] ssNumDF; delete [] ssIdIdx; delete [] ssDFIdx; delete [] idBuf; delete [] sideBuf; if (dfBuf) delete [] dfBuf; else if (dfBufD) delete [] dfBufD; int fail = (rc < 0); return fail; } int vtkExodusIIWriter::WriteNodeSetInformation() { int rc = 0; int i, j; vtkModelMetadata *em = this->GetModelMetadata(); int nnsets = em->GetNumberOfNodeSets(); if (nnsets < 1) return 0; int nids = em->GetSumNodesPerNodeSet(); if (nids < 1) { int *buf = new int [nnsets]; memset(buf, 0, sizeof(int) * nnsets); rc = ex_put_concat_node_sets(this->fid, em->GetNodeSetIds(), buf, buf, buf, buf, NULL, NULL); delete [] buf; return (rc < 0); } int *nsSize = new int [nnsets]; int *nsNumDF = new int [nnsets]; int *nsIdIdx = new int [nnsets]; int *nsDFIdx = new int [nnsets]; int ndf = em->GetSumDistFactPerNodeSet(); int *idBuf = new int [nids]; float *dfBuf = NULL; double *dfBufD = NULL; if (ndf) { if (this->PassDoubles) { dfBufD = new double [ndf]; } else { dfBuf = new float [ndf]; } } int *emNsSize = em->GetNodeSetSize(); int *emNumDF = em->GetNodeSetNumberOfDistributionFactors(); int *emIdIdx = em->GetNodeSetNodeIdListIndex(); int *emDFIdx = em->GetNodeSetDistributionFactorIndex(); int nextId = 0; int nextDF = 0; for (i=0; iGetNodeSetNodeIdList() + emIdIdx[i]; float *df = em->GetNodeSetDistributionFactors() + emDFIdx[i]; for (j=0; j< emNsSize[i]; j++) { // Have to check if this node is still in the ugrid. // It may have been deleted since the ExodusModel was created. int lid = this->GetNodeLocalId(ids[j]); if (lid < 0) continue; nsSize[i]++; idBuf[nextId++] = lid + 1; if (emNumDF[i] > 0) { nsNumDF[i]++; if (this->PassDoubles) { dfBufD[nextDF++] = (double)df[j]; } else { dfBuf[nextDF++] = df[j]; } } } } if (this->PassDoubles) { rc = ex_put_concat_node_sets(this->fid, em->GetNodeSetIds(), nsSize, nsNumDF, nsIdIdx, nsDFIdx, idBuf, dfBufD); } else { rc = ex_put_concat_node_sets(this->fid, em->GetNodeSetIds(), nsSize, nsNumDF, nsIdIdx, nsDFIdx, idBuf, dfBuf); } delete [] nsSize; delete [] nsNumDF; delete [] nsIdIdx; delete [] nsDFIdx; delete [] idBuf; if (dfBuf) delete [] dfBuf; else if (dfBufD) delete [] dfBufD; int fail = (rc < 0); return fail; } //--------------------------------------------------------- // Points and point IDs, element IDs //--------------------------------------------------------- int vtkExodusIIWriter::WriteCoordinateNames() { vtkModelMetadata *em = this->GetModelMetadata(); int rc = ex_put_coord_names(this->fid, em->GetCoordinateNames()); int fail = (rc < 0); return fail; } int vtkExodusIIWriter::WriteGlobalPointIds() { int rc = 0; if (this->GlobalNodeIdList) { rc = ex_put_node_num_map(this->fid, this->GlobalNodeIdList); } int fail = ( rc < 0); return fail; } int vtkExodusIIWriter::WriteGlobalElementIds() { int rc = 0; int *ids = this->GlobalElementIdList; if (ids) { if (this->ElementIndex) { vtkUnstructuredGrid *ug = this->GetInput(); int ncells = ug->GetNumberOfCells(); int *newIds = new int [ncells]; for (int i=0; iElementIndex[i]]; } ids = newIds; } rc = ex_put_elem_num_map(this->fid, ids); if (ids != this->GlobalElementIdList) { delete [] ids; } } int fail = ( rc < 0); return fail; } int vtkExodusIIWriter::WritePoints() { vtkUnstructuredGrid *ug = this->GetInput(); if (!ug) return 1; vtkPoints *pts = ug->GetPoints(); int npts = pts->GetNumberOfPoints(); vtkDataArray *da = pts->GetData(); int type = pts->GetDataType(); int i = 0; int fail = 0; if (this->PassDoubles) { double *px = new double [npts]; double *py = new double [npts]; double *pz = new double [npts]; if (type == VTK_DOUBLE) { vtkDoubleArray *a = vtkDoubleArray::SafeDownCast(da); double *p = a->GetPointer(0); for (i=0; iGetComponent(i, 0); py[i] = da->GetComponent(i, 1); pz[i] = da->GetComponent(i, 2); } } int rc = ex_put_coord(this->fid, px, py, pz); fail = (rc < 0); delete [] px; delete [] py; delete [] pz; } else { float *px = new float [npts]; float *py = new float [npts]; float *pz = new float [npts]; if (type == VTK_FLOAT) { vtkFloatArray *a = vtkFloatArray::SafeDownCast(da); float *p = a->GetPointer(0); for (i=0; iGetPointer(0); for (i=0; ifid, px, py, pz); fail = (rc < 0); delete [] px; delete [] py; delete [] pz; } return fail; } //--------------------------------------------------------- // Initialization, QA, Title, information records //--------------------------------------------------------- int vtkExodusIIWriter::WriteQARecords() { vtkModelMetadata *em = this->GetModelMetadata(); int nrecs = em->GetNumberOfQARecords(); if (nrecs > 0) { typedef char *p4[4]; p4 *qarecs = new p4 [nrecs]; for (int i=0; iGetQARecord(i, &qarecs[i][0], &qarecs[i][1], &qarecs[i][2], &qarecs[i][3]); } ex_put_qa(this->fid, nrecs, qarecs); delete [] qarecs; } return 0; } int vtkExodusIIWriter::WriteInformationRecords() { vtkModelMetadata *em = this->GetModelMetadata(); int nlines = em->GetNumberOfInformationLines(); if (nlines > 0) { char **lines = NULL; em->GetInformationLines(&lines); ex_put_info(this->fid, nlines, lines); } return 0; } int vtkExodusIIWriter::WriteInitializationParameters() { vtkUnstructuredGrid *ug = this->GetInput(); if (!ug) return 1; vtkModelMetadata *em = this->GetModelMetadata(); int dim = em->GetDimension(); int nnodes = ug->GetNumberOfPoints(); int ncells = ug->GetNumberOfCells(); int nnsets = em->GetNumberOfNodeSets(); int nssets = em->GetNumberOfSideSets(); char *title = em->GetTitle(); int numBlocks = em->GetNumberOfBlocks(); int rc = ex_put_init(this->fid, title, dim, nnodes, ncells, numBlocks, nnsets, nssets); int fail = (rc < 0); return fail; } //======================================================================== // BLOCKS //======================================================================== void vtkExodusIIWriter::InitializeBlockLists() { this->NumberOfElementBlocks = 0; this->BlockIds = NULL; this->BlockElementType = NULL; this->BlockElementStart = NULL; this->ElementIndex = NULL; this->NumberOfElementsPerBlock = NULL; this->NumberOfNodesPerElementInBlock = NULL; this->NumberOfAttributesPerElementInBlock = NULL; this->BlockElementAttributesF = NULL; this->BlockElementAttributesD = NULL; this->BlockElementConnectivity = NULL; } void vtkExodusIIWriter::ClearBlockLists() { int i; int len = this->NumberOfElementBlocks; if (len == 0) return; FREELIST(this->BlockElementType, len); FREELIST(this->BlockElementAttributesF, len); FREELIST(this->BlockElementAttributesD, len); FREELIST(this->BlockElementConnectivity, len); FREE(this->BlockIds); FREE(this->BlockElementStart); FREE(this->ElementIndex); FREE(this->NumberOfElementsPerBlock); FREE(this->NumberOfNodesPerElementInBlock); FREE(this->NumberOfAttributesPerElementInBlock); if (this->LocalBlockIndexMap) { delete this->LocalBlockIndexMap; this->LocalBlockIndexMap = NULL; } this->NumberOfElementBlocks = 0; } int vtkExodusIIWriter::WriteBlockInformation() { int i, j, rc; vtkUnstructuredGrid *ug = this->GetInput(); vtkModelMetadata *em = this->GetModelMetadata(); this->ClearBlockLists(); int nblocks = em->GetNumberOfBlocks(); int ncells = ug->GetNumberOfCells(); int *ids = em->GetBlockIds(); char **blockNames = em->GetBlockElementType(); int *nodesPerElement = em->GetBlockNodesPerElement(); int *numAttributes = em->GetBlockNumberOfAttributesPerElement(); this->NumberOfElementBlocks = nblocks; this->BlockIds = new int [nblocks]; this->BlockElementType = new char * [nblocks]; this->BlockElementStart = new int [nblocks]; this->NumberOfElementsPerBlock = new int [nblocks]; this->NumberOfNodesPerElementInBlock = new int [nblocks]; this->NumberOfAttributesPerElementInBlock = new int [nblocks]; this->BlockElementConnectivity = new int * [nblocks]; if (this->PassDoubles) { this->BlockElementAttributesD = new double * [nblocks]; } else { this->BlockElementAttributesF = new float * [nblocks]; } for (i=0; iNumberOfElementsPerBlock[i] = 0; this->BlockElementStart[i] = -1; this->BlockIds[i] = (int)ids[i]; this->BlockElementType[i] = vtkExodusIIWriter::StrDupWithNew(blockNames[i]); this->NumberOfNodesPerElementInBlock[i] = nodesPerElement[i]; this->NumberOfAttributesPerElementInBlock[i] = numAttributes[i]; this->BlockElementConnectivity[i] = NULL; if (this->PassDoubles) { this->BlockElementAttributesD[i] = NULL; } else { this->BlockElementAttributesF[i] = NULL; } } // Count the number of elements in each block - it's not necessarily // the number in the ExodusModel because some cells may have been // deleted. // // The elements in the input may not be in order by block, but we must // write element IDs and element variables out to the Exodus file in // order by block. Create a mapping if necessary, for an ordering by // block to the ordering found in the input unstructured grid. int lastId = -1; int idx = -1; int needMapping = 0; for (i=0; iBlockIdList[i]; if (blockId != lastId) { idx = this->GetBlockLocalIndex(blockId); } this->NumberOfElementsPerBlock[idx]++; if (!needMapping) { if (blockId != lastId) { int start = this->BlockElementStart[idx]; if (start == -1) { this->BlockElementStart[idx] = i; } else { needMapping = 1; } } } lastId = blockId; } if (needMapping) { // Element variables and global IDs are mixed up, not grouped // by block as they must be when written out. this->ElementIndex = new int [ncells]; this->BlockElementStart[0] = 0; for (i=1; iBlockElementStart[i] = this->BlockElementStart[i-1] + this->NumberOfElementsPerBlock[i-1]; } int *blockCount = new int [nblocks]; memset(blockCount, 0, sizeof(int) * nblocks); lastId = -1; idx = -1; for (i=0; iBlockIdList[i]; if (blockId != lastId) { idx = this->GetBlockLocalIndex(blockId); } int which = this->BlockElementStart[idx] + blockCount[idx]++; this->ElementIndex[which] = i; lastId = blockId; } delete [] blockCount; } // Write out the connectivity array and the attribute array. int *nodeCount = new int [nblocks]; int *attCount = new int [nblocks]; // For each block, a map from element global ID to it's location // within it's block in the ExodusModel object. vtkstd::map **eltIdx = new vtkstd::map * [nblocks]; vtkstd::map::iterator eltIt; for (i=0; iNumberOfElementsPerBlock[i]; int numAtts = this->NumberOfAttributesPerElementInBlock[i]; int numNodes = this->NumberOfNodesPerElementInBlock[i]; if (numElts > 0) { this->BlockElementConnectivity[i] = new int [numElts * numNodes]; if (numAtts > 0) { if (this->PassDoubles) { this->BlockElementAttributesD[i] = new double [numElts * numAtts]; } else { this->BlockElementAttributesF[i] = new float [numElts * numAtts]; } eltIdx[i] = this->BuildBlockElementSearchStructure(i); } } } vtkCellArray *ca = ug->GetCells(); vtkIdType *ptIds = ca->GetPointer(); vtkIdTypeArray *loca = ug->GetCellLocationsArray(); vtkIdType *loc = loca->GetPointer(0); float *att = em->GetBlockAttributes(); int *attIdx = em->GetBlockAttributesIndex(); int skipAttributes=0; lastId = -1; idx = -1; for (i=0; iElementIndex ? this->ElementIndex[i] : i); int blockId = this->BlockIdList[nextIdx]; if (blockId != lastId) { idx = this->GetBlockLocalIndex(blockId); lastId = blockId; } // the block connectivity array vtkIdType ptListIdx = loc[nextIdx]; vtkIdType npts = ptIds[ptListIdx++]; for (vtkIdType p=0; pBlockElementConnectivity[idx][nodeCount[idx]++] = ExodusPointId; } // the block element attributes int numAtts = this->NumberOfAttributesPerElementInBlock[idx]; if ((numAtts == 0) || skipAttributes) continue; if (!this->GlobalElementIdList) { vtkWarningMacro(<< "Exodus writer must omit element block attributes, because there are no global element IDs"); skipAttributes = 1; break; } int globalId = this->GlobalElementIdList[nextIdx]; eltIt = eltIdx[idx]->find(globalId); if (eltIt == eltIdx[idx]->end()) { vtkWarningMacro(<< "Exodus writer must omit element block attributes, because new elements were added"); skipAttributes = 1; break; } int where = eltIt->second; float *eltAtt = att + // list of all blocks attribute values attIdx[idx] + // location for this block (where * numAtts); // location for the element in the block if (this->PassDoubles) { for (j=0; jBlockElementAttributesD[idx][attCount[idx]++] = (double)eltAtt[j]; } } else { for (j=0; jBlockElementAttributesF[idx][attCount[idx]++] = eltAtt[j]; } } } for (i=0; iNumberOfAttributesPerElementInBlock[i] = 0; FREELIST(this->BlockElementAttributesD, nblocks); FREELIST(this->BlockElementAttributesF, nblocks); } } // Now, finally, write out the block information int fail = 0; for (i=0; ifid, this->BlockIds[i], this->BlockElementType[i], this->NumberOfElementsPerBlock[i], this->NumberOfNodesPerElementInBlock[i], this->NumberOfAttributesPerElementInBlock[i]); if (rc < 0) { fail = 1; break; } } if (fail) { return 1; } for (i=0; iNumberOfElementsPerBlock[i] > 0) { rc = ex_put_elem_conn(this->fid, this->BlockIds[i], this->BlockElementConnectivity[i]); if (rc < 0) { fail = 1; break; } if (this->NumberOfAttributesPerElementInBlock[i] > 0) { if (this->PassDoubles) { rc = ex_put_elem_attr(this->fid, this->BlockIds[i], this->BlockElementAttributesD[i]); } else { rc = ex_put_elem_attr(this->fid, this->BlockIds[i], this->BlockElementAttributesF[i]); } if (rc < 0) { fail = 1; break; } } } } return fail; } vtkstd::map *vtkExodusIIWriter::BuildBlockElementSearchStructure(int block) { // Create a map from an element global ID to it's location in // the block array in the ExodusModel object. vtkModelMetadata *em = this->GetModelMetadata(); int *blockElts = em->GetBlockElementIdList(); int *numElts = em->GetBlockNumberOfElements(); int *listIdx = em->GetBlockElementIdListIndex(); vtkstd::map *eltMap = new vtkstd::map; int blockSize = numElts[block]; int *eltIds = blockElts + listIdx[block]; for (int i=0; iinsert(vtkstd::map::value_type(eltIds[i], i)); } return eltMap; } //---------------------------------------------------------------------------- char *vtkExodusIIWriter::StrDupWithNew(const char *s) { char *newstr = NULL; if (s) { int len = strlen(s); if (len == 0) { newstr = new char [1]; newstr[0] = '\0'; } else { newstr = new char [len + 1]; strcpy(newstr, s); } } return newstr; } //---------------------------------------------------------------------------- void vtkExodusIIWriter::PrintSelf(ostream& os, vtkIndent indent) { vtkWriter::PrintSelf(os,indent); if (this->FileName) { os << indent << "FileName " << this->FileName << "\n"; } if (this->MyFileName) { os << indent << "MyFileName " << this->MyFileName << "\n"; } os << indent << "ErrorStatus " << this->ErrorStatus << endl; os << indent << "StoreDoubles " << this->StoreDoubles << endl; os << indent << "GhostLevel " << this->GhostLevel << endl; if (this->BlockIdArrayName) { os << indent << "BlockIdArrayName " << this->BlockIdArrayName << endl; } if (this->GlobalNodeIdArrayName) { os << indent << "GlobalNodeIdArrayName " << this->GlobalNodeIdArrayName << endl; } if (this->GlobalElementIdArrayName) { os << indent << "GlobalNodeIdArrayName " << this->GlobalNodeIdArrayName << endl; } os << indent << "WriteOutBlockIdArray " << this->WriteOutBlockIdArray << endl; os << indent << "WriteOutGlobalNodeIdArray " << this->WriteOutGlobalNodeIdArray << endl; os << indent << "WriteOutGlobalElementIdArray " << this->WriteOutGlobalElementIdArray << endl; os << indent << "ModelMetadata " << this->ModelMetadata << endl; }