1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkLSDynaReader.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  Copyright (c) Sandia Corporation
17  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
18 ----------------------------------------------------------------------------*/
19 
20 // NOTE TO DEVELOPERS: ========================================================
21 //
22 // This is a really big reader.
23 // It uses several classes defined in Utilities/LSDyna:
24 // - LSDynaFamily:
25 //    A class to abstract away I/O from families of output files.
26 //    This performs the actual reads and writes plus any required byte swapping.
27 //    Also contains a subclass, LSDynaFamilyAdaptLevel, used to store
28 //    file+offset
29 //    information for each mesh adaptation's state info.
30 // - LSDynaMetaData:
31 //    A class to hold metadata about a particular file (such as time steps,
32 //    the start of state information for each time step, the number of
33 //    adaptive remeshes, and the large collection of constants that determine
34 //    the available attributes). It contains an LSDynaFamily instance.
35 
36 //It also uses a helper vtk class
37 // - vtkLSDynaSummaryParser:
38 //    A class to parse XML summary files containing part names and their IDs.
39 //    This class is used by vtkLSDynaReader::ReadInputDeckXML().
40 
41 // This class is preceded by some file-static constants and utility routines.
42 
43 #include "vtkLSDynaReader.h"
44 #include "vtkLSDynaSummaryParser.h"
45 #include "vtkLSDynaPartCollection.h"
46 #include "LSDynaFamily.h"
47 #include "LSDynaMetaData.h"
48 
49 #include "vtksys/SystemTools.hxx"
50 
51 #include <string>
52 #include <vector>
53 #include <algorithm>
54 #include <map>
55 #include <cassert>
56 
57 #include "vtkCellType.h"
58 #include "vtkDataObject.h"
59 #include "vtkDoubleArray.h"
60 #include "vtkFloatArray.h"
61 #include "vtkIdTypeArray.h"
62 #include "vtkInformation.h"
63 #include "vtkInformationDoubleVectorKey.h"
64 #include "vtkInformationVector.h"
65 #include "vtkMultiBlockDataSet.h"
66 #include "vtkObjectFactory.h"
67 #include "vtkPointData.h"
68 #include "vtkPoints.h"
69 #include "vtkSmartPointer.h"
70 #include "vtkStreamingDemandDrivenPipeline.h"
71 #include "vtkUnsignedCharArray.h"
72 #include "vtkUnstructuredGrid.h"
73 #include "vtkVectorOperators.h"
74 
75 vtkStandardNewMacro(vtkLSDynaReader);
76 
77 // Names of vtkDataArrays provided with grid:
78 #define LS_ARRAYNAME_DEATH              "Death"
79 #define LS_ARRAYNAME_USERID             "UserID"
80 #define LS_ARRAYNAME_SPECIES_BLNK       "SpeciesXX"
81 #define LS_ARRAYNAME_SPECIES_FMT        "Species%02d"
82 #define LS_ARRAYNAME_SPECIES_01         "Species01"
83 #define LS_ARRAYNAME_SPECIES_02         "Species02"
84 #define LS_ARRAYNAME_SPECIES_03         "Species03"
85 #define LS_ARRAYNAME_SPECIES_04         "Species04"
86 #define LS_ARRAYNAME_SPECIES_05         "Species05"
87 #define LS_ARRAYNAME_SPECIES_06         "Species06"
88 #define LS_ARRAYNAME_SPECIES_07         "Species07"
89 #define LS_ARRAYNAME_SPECIES_08         "Species08"
90 #define LS_ARRAYNAME_SPECIES_09         "Species09"
91 #define LS_ARRAYNAME_SPECIES_10         "Species10"
92 #define LS_ARRAYNAME_TEMPERATURE        "Temperature"
93 #define LS_ARRAYNAME_DEFLECTION         "Deflected Coordinates"
94 #define LS_ARRAYNAME_VELOCITY           "Velocity"
95 #define LS_ARRAYNAME_ACCELERATION       "Acceleration"
96 #define LS_ARRAYNAME_PRESSURE           "Pressure"
97 #define LS_ARRAYNAME_VORTICITY          "Vorticity"
98 #define LS_ARRAYNAME_RESULTANTVORTICITY "ResVorticity"
99 #define LS_ARRAYNAME_ENSTROPHY          "Enstrophy"
100 #define LS_ARRAYNAME_HELICITY           "Helicity"
101 #define LS_ARRAYNAME_STREAMFUNCTION     "StreamFunc"
102 #define LS_ARRAYNAME_ENTHALPY           "Enthalpy"
103 #define LS_ARRAYNAME_DENSITY            "Density"
104 #define LS_ARRAYNAME_TURBULENTKE        "TurbulentKE"
105 #define LS_ARRAYNAME_DISSIPATION        "Dissipation"
106 #define LS_ARRAYNAME_EDDYVISCOSITY      "EddyVisc"
107 #define LS_ARRAYNAME_RADIUSOFINFLUENCE  "InfluenceRadius"
108 #define LS_ARRAYNAME_NUMNEIGHBORS       "NumberOfNeighbors"
109 #define LS_ARRAYNAME_SEGMENTID          "SegmentID"
110 #define LS_ARRAYNAME_STRAIN             "Strain"
111 #define LS_ARRAYNAME_STRESS             "Stress"
112 #define LS_ARRAYNAME_EPSTRAIN           "EffPlastStrn"
113 #define LS_ARRAYNAME_INTEGRATIONPOINT   "IntPtData"
114 #define LS_ARRAYNAME_RESULTANTS         "Resultants"
115 #define LS_ARRAYNAME_ELEMENTMISC        "ElementMisc"
116 #define LS_ARRAYNAME_INTERNALENERGY     "InternalEnergy"
117 #define LS_ARRAYNAME_AXIALFORCE         "AxialForce"
118 #define LS_ARRAYNAME_SHEARRESULTANT     "ShearResultant"
119 #define LS_ARRAYNAME_BENDINGRESULTANT   "BendingResultant"
120 #define LS_ARRAYNAME_TORSIONRESULTANT   "TorsionResultant"
121 #define LS_ARRAYNAME_NORMALRESULTANT    "NormalResultant"
122 #define LS_ARRAYNAME_AXIALSTRAIN        "AxialStrain"
123 #define LS_ARRAYNAME_AXIALSTRESS        "AxialStress"
124 #define LS_ARRAYNAME_SHEARSTRAIN        "ShearStrain"
125 #define LS_ARRAYNAME_SHEARSTRESS        "ShearStress"
126 #define LS_ARRAYNAME_PLASTICSTRAIN      "PlasticStrain"
127 #define LS_ARRAYNAME_THICKNESS          "Thickness"
128 #define LS_ARRAYNAME_MASS               "Mass"
129 #define LS_ARRAYNAME_VOLUME_FRACTION_FMT "VolumeFraction%02d"
130 #define LS_ARRAYNAME_DOMINANT_GROUP     "DominantGroup"
131 #define LS_ARRAYNAME_SPECIES_MASS_FMT   "SpeciesMass%02d"
132 #define LS_ARRAYNAME_MATERIAL           "Material"
133 
134 // Possible material options
135 #define LS_MDLOPT_NONE 0
136 #define LS_MDLOPT_POINT 1
137 #define LS_MDLOPT_CELL 2
138 
139 #ifdef VTK_LSDYNA_DBG_MULTIBLOCK
140 static void vtkDebugMultiBlockStructure( vtkIndent indent, vtkMultiGroupDataSet* mbds );
141 #endif // VTK_LSDYNA_DBG_MULTIBLOCK
142 
143 namespace
144 {
145 static const char* vtkLSDynaCellTypes[] =
146 {
147   "Point",
148   "Beam",
149   "Shell",
150   "Thick Shell",
151   "Solid",
152   "Rigid Body",
153   "Road Surface"
154 };
155 
156 // Read in lines until one that's
157 // - not empty, and
158 // - not a comment
159 // is encountered. Return with that text stored in \a line.
160 // If an error or EOF is hit, return 0. Otherwise, return 1.
vtkLSNextSignificantLine(ifstream & deck,std::string & line)161 static int vtkLSNextSignificantLine( ifstream& deck, std::string& line )
162 {
163   while ( deck.good() )
164   {
165     std::getline( deck, line, '\n' );
166     if ( ! line.empty() && line[0] != '$' )
167     {
168       return 1;
169     }
170   }
171   return 0;
172 }
173 
vtkLSTrimWhitespace(std::string & line)174 static void vtkLSTrimWhitespace( std::string& line )
175 {
176   std::string::size_type llen = line.length();
177   while ( llen &&
178     ( line[llen - 1] == ' ' ||
179       line[llen - 1] == '\t' ||
180       line[llen - 1] == '\r' ||
181       line[llen - 1] == '\n' ) )
182   {
183     --llen;
184   }
185 
186   std::string::size_type nameStart = 0;
187   while ( nameStart < llen &&
188     ( line[nameStart] == ' ' ||
189       line[nameStart] == '\t' ) )
190   {
191     ++nameStart;
192   }
193 
194   line = line.substr( nameStart, llen - nameStart );
195 }
196 
vtkLSDowncaseFirstWord(std::string & downcased,const std::string & line)197 static void vtkLSDowncaseFirstWord( std::string& downcased, const std::string& line )
198 {
199   std::string::size_type i;
200   std::string::value_type chr;
201   int leadingSpace = 0;
202   downcased = "";
203   for ( i = 0; i < line.length(); ++i )
204   {
205     chr = tolower( line[i] );
206     if ( chr == ' ' || chr == '\t' )
207     {
208       if ( leadingSpace )
209       { // We've trimmed leading whitespace already, so we're done with the word.
210         return;
211       }
212     }
213     else
214     {
215       leadingSpace = 1;
216       if ( chr == ',' )
217       { // We're at a separator (other than whitespace). No need to continue.
218         return;
219       }
220     }
221     downcased += chr;
222   }
223 }
224 
vtkLSSplitString(std::string & input,std::vector<std::string> & splits,const char * separators)225 void vtkLSSplitString( std::string& input, std::vector<std::string>& splits, const char* separators )
226 {
227   std::string::size_type posBeg = 0;
228   std::string::size_type posEnd;
229   do {
230     posEnd = input.find_first_of( separators, posBeg );
231     if ( posEnd > posBeg )
232     {
233       // don't include empty entries in splits.
234       // NOTE: This means ",comp,1, ,3" with separators ", " yields "comp","1","3", not "","comp","1","","","3".
235       splits.push_back( input.substr( posBeg, posEnd - posBeg ) );
236     }
237     posBeg = input.find_first_not_of( separators, posEnd );
238   } while ( posBeg != std::string::npos );
239 }
240 
241 template<int hostBitSize, int fileBitSize, int cellLength> struct Converter
242 {
243   //general use case that the host
244   //bit size and file bit size are the same
convert__anon4070b03b0111::Converter245   vtkIdType* convert(vtkIdType* buff, const vtkIdType&)
246   {
247     return buff;
248   }
249 };
250 
251 template<int cellLength> struct Converter<8,4,cellLength>
252 {
253   //specilization of 64bit machine and 32bit file
254   //so we have to copy each item individually
convert__anon4070b03b0111::Converter255   vtkIdType* convert(int* buff, const vtkIdType& size)
256   {
257     for(vtkIdType i=0;i<size;++i)
258     {
259       this->Conn[i]=static_cast<vtkIdType>(buff[i]);
260     }
261     return Conn;
262   }
263   vtkIdType Conn[cellLength];
264 };
265 
266 template<int cellLength> struct Converter<4,8,cellLength>
267 {
268   //specilization for reading 64 bit files on a 32 bit machine
269   //which means reading the bottom half of the long long
convert__anon4070b03b0111::Converter270   vtkIdType* convert(int* buff, const vtkIdType& size)
271   {
272     vtkIdType idx=0;
273     for(vtkIdType i=0;i<size;i+=2,++idx)
274     {
275       this->Conn[idx]=static_cast<vtkIdType>(buff[i]);
276     }
277     return Conn;
278   }
279   vtkIdType Conn[cellLength];
280 };
281 
282 template<int type,int wordSize,int cellLength>
283   struct FillBlock
284 {
285   Converter<sizeof(vtkIdType),wordSize,cellLength> BC;
286 
287   template<typename T>
FillBlock__anon4070b03b0111::FillBlock288   FillBlock(T* buff, vtkLSDynaPartCollection *parts,LSDynaMetaData *p,
289     const vtkIdType& numWordsPerCell, const int& cellType)
290   {
291     //determine the relationship between the file bit size and
292     //the host machine bit size. This allows us to read 64 bit files on a
293     //32 bit machine. The Converter allows us to easily convert 32bit
294     //arrays to 64bit arrays
295     const int numWordsPerIdType (p->Fam.GetWordSize() / sizeof(T));
296     const vtkIdType numFileWordsPerCell(numWordsPerCell * numWordsPerIdType);
297     const vtkIdType offsetToMatId(numWordsPerIdType *(numWordsPerCell-1));
298     vtkIdType *conn;
299 
300     vtkIdType nc=0,j=0,matlId=0;
301     vtkIdType numCellsToSkip=0, numCellsToSkipEnd=0, chunkSize=0;
302 
303 
304     //get from the part the read information for this lsdyna block type
305     parts->GetPartReadInfo(type,nc,numCellsToSkip,numCellsToSkipEnd);
306 
307     p->Fam.SkipWords(numFileWordsPerCell * numCellsToSkip ); //skip to the right start id
308 
309     //buffer the amount in small chunks so we don't create a massive buffer
310     vtkIdType numChunks = p->Fam.InitPartialChunkBuffering(nc,numWordsPerCell);
311     for(vtkIdType i=0; i < numChunks; ++i)
312     {
313       chunkSize = p->Fam.GetNextChunk( LSDynaFamily::Int);
314       buff = p->Fam.GetBufferAs<T>();
315       for (j=0; j<chunkSize;j+=numWordsPerCell)
316       {
317         conn = BC.convert(buff,offsetToMatId);
318         buff+=offsetToMatId;
319         matlId = static_cast<vtkIdType>(*buff);
320         buff+=numWordsPerIdType;
321         parts->InsertCell(type,matlId,cellType,cellLength,conn);
322       }
323     }
324     p->Fam.SkipWords(numFileWordsPerCell * numCellsToSkipEnd);
325   }
326 };
327 
328 template<int wordSize,int cellLength>
329   struct FillBlock<LSDynaMetaData::SOLID,wordSize,cellLength>
330 {
331   Converter<sizeof(vtkIdType),wordSize,cellLength> BC;
332 
333   template<typename T>
FillBlock__anon4070b03b0111::FillBlock334   FillBlock(T* buff, vtkLSDynaPartCollection *parts,LSDynaMetaData *p,
335     const vtkIdType& numWordsPerCell, const int& vtkNotUsed(cellType) )
336   {
337     //determine the relationship between the file bit size and
338     //the host machine bit size. This allows us to read 64 bit files on a
339     //32 bit machine. The Converter allows us to easily convert 32bit
340     //arrays to 64bit arrays
341     const int numWordsPerIdType (p->Fam.GetWordSize() / sizeof(T));
342     const vtkIdType numFileWordsPerCell(numWordsPerCell * numWordsPerIdType);
343     const vtkIdType offsetToMatId(numWordsPerIdType * cellLength);
344     vtkIdType *conn;
345 
346     //This is a read solids template specialization since it has a special use
347     //case for cell length based on the connectivity mapping
348     vtkIdType nc=0,j=0,matlId=0;
349     vtkIdType numCellsToSkip=0, numCellsToSkipEnd=0, chunkSize=0;
350 
351     //get from the part the read information for this lsdyna block type
352     parts->GetPartReadInfo(LSDynaMetaData::SOLID,nc,numCellsToSkip,numCellsToSkipEnd);
353 
354     p->Fam.SkipWords(numFileWordsPerCell * numCellsToSkip); //skip to the right start id
355 
356     //buffer the amount in small chunks so we don't create a massive buffer
357     vtkIdType numChunks = p->Fam.InitPartialChunkBuffering(nc,numWordsPerCell);
358     vtkIdType npts = 0;
359     int ctype = 0;
360     for(vtkIdType i=0; i < numChunks; ++i)
361     {
362       chunkSize = p->Fam.GetNextChunk( LSDynaFamily::Int);
363       buff = p->Fam.GetBufferAs<T>();
364       for (j=0; j<chunkSize;j+=numWordsPerCell)
365       {
366         conn = BC.convert(buff,offsetToMatId);
367         buff+=offsetToMatId;
368         matlId = static_cast<vtkIdType>(*buff);
369         buff+=numWordsPerIdType;
370 
371         //Detect repeated connectivity entries to determine element type
372         if (conn[3] == conn[7])
373         {
374           ctype = VTK_TETRA;
375           npts = 4;
376         }
377         else if (conn[4] == conn[7])
378         {
379           ctype = VTK_PYRAMID;
380           npts = 5;
381         }
382         else if (conn[5] == conn[7])
383         {
384           ctype = VTK_WEDGE;
385           npts = 6;
386         }
387         else
388         {
389           ctype = VTK_HEXAHEDRON;
390           npts = 8;
391         }
392 
393         //push this cell back into the unstructured grid for this part(if the part is active)
394         parts->InsertCell(LSDynaMetaData::SOLID,matlId,ctype,npts,conn);
395       }
396     }
397     p->Fam.SkipWords(numFileWordsPerCell * numCellsToSkipEnd);
398   }
399 };
400 
401 template<int wordSize,int cellLength>
402   struct FillBlock<LSDynaMetaData::SHELL,wordSize,cellLength>
403 {
404   Converter<sizeof(vtkIdType),wordSize,cellLength> BC;
405 
406   template<typename T>
FillBlock__anon4070b03b0111::FillBlock407   FillBlock(T* buff, vtkLSDynaPartCollection *parts,LSDynaMetaData *p,
408     const vtkIdType& numWordsPerCell, const int& cellType)
409   {
410     //determine the relationship between the file bit size and
411     //the host machine bit size. This allows us to read 64 bit files on a
412     //32 bit machine. The Converter allows us to easily convert 32bit
413     //arrays to 64bit arrays
414     const int numWordsPerIdType (p->Fam.GetWordSize() / sizeof(T));
415     const vtkIdType numFileWordsPerCell(numWordsPerCell * numWordsPerIdType);
416     const vtkIdType offsetToMatId(numWordsPerIdType * cellLength);
417     vtkIdType *conn;
418 
419     //This is a read RIGID_BODY and SHELL template specialization since it
420     //has a weird weaving of cell types
421     bool haveRigidMaterials = (p->Dict["MATTYP"] != 0) &&
422                               !p->RigidMaterials.empty();
423 
424     vtkIdType nc=0, j=0,matlId=0;
425     vtkIdType numCellsToSkip=0, numCellsToSkipEnd=0, chunkSize=0;
426 
427     //get from the part the read information for this lsdyna block type
428     parts->GetPartReadInfo(LSDynaMetaData::SHELL,nc,numCellsToSkip,numCellsToSkipEnd);
429 
430     p->Fam.SkipWords(numFileWordsPerCell * numCellsToSkip); //skip to the right start id
431 
432     //buffer the amount in small chunks so we don't create a massive buffer
433     vtkIdType numChunks = p->Fam.InitPartialChunkBuffering(nc,numWordsPerCell);
434     int pType = 0;
435     for(vtkIdType i=0; i < numChunks; ++i)
436     {
437       chunkSize = p->Fam.GetNextChunk( LSDynaFamily::Int);
438       buff = p->Fam.GetBufferAs<T>();
439       for (j=0; j<chunkSize;j+=numWordsPerCell)
440       {
441         conn = BC.convert(buff,offsetToMatId);
442         buff+=offsetToMatId;
443         matlId = static_cast<vtkIdType>(*buff);
444         buff+=numWordsPerIdType;
445 
446         if ( haveRigidMaterials &&
447           p->RigidMaterials.find( matlId ) == p->RigidMaterials.end())
448         {
449           pType = LSDynaMetaData::RIGID_BODY;
450         }
451         else
452         {
453           pType = LSDynaMetaData::SHELL;
454         }
455         parts->InsertCell(pType,matlId,cellType,cellLength,conn);
456       }
457     }
458     p->Fam.SkipWords(numFileWordsPerCell * numCellsToSkipEnd);
459   }
460 };
461 
462 template<int wordSize,int cellLength>
463   struct FillBlock<LSDynaMetaData::ROAD_SURFACE,wordSize,cellLength>
464 {
465   template<typename T>
FillBlock__anon4070b03b0111::FillBlock466   FillBlock(T*, vtkLSDynaPartCollection *parts,LSDynaMetaData *p,
467     const vtkIdType&, const int& cellType)
468   {
469     //This is a ROAD_SURFACE specialization
470     //has a weird weaving of cell types
471     vtkIdType nc=0,segId=0,segSz=0;
472     vtkIdType numCellsToSkip=0, numCellsToSkipEnd=0;
473 
474     //get from the part the read information for this lsdyna block type
475     parts->GetPartReadInfo(LSDynaMetaData::SHELL,nc,numCellsToSkip,numCellsToSkipEnd);
476 
477     //the road surface format is horrible for parallel reading.
478     //we don't know the number of cells in each road surface.
479     //only the total number of cells. So we have to do some fun stuff to correctly skip
480     //this is unoptimized since I don't have any road surface data
481     vtkIdType currentCell=0;
482     vtkIdType conn[4];
483     for (vtkIdType i=0; i<p->Dict["NSURF"]; ++i)
484     {
485       p->Fam.BufferChunk( LSDynaFamily::Int, 2 );
486       segId = p->Fam.GetNextWordAsInt();
487       segSz = p->Fam.GetNextWordAsInt();
488       p->Fam.BufferChunk( LSDynaFamily::Int, 4*segSz );
489       for (vtkIdType t=0; t<segSz; ++t, ++currentCell)
490       {
491         if(currentCell >= numCellsToSkip)
492         {
493           for (int j=0; j<4; ++j )
494           {
495             conn[j] = p->Fam.GetNextWordAsInt() - 1;
496           }
497           parts->InsertCell(LSDynaMetaData::ROAD_SURFACE,segId,cellType,4,conn);
498         }
499       }
500     }
501   }
502 };
503 
504 }
505 // =================================================== Start of public interface
vtkLSDynaReader()506 vtkLSDynaReader::vtkLSDynaReader()
507 {
508   this->P = new LSDynaMetaData;
509   this->SetNumberOfInputPorts(0);
510   this->SetNumberOfOutputPorts(1);
511   this->TimeStepRange[0] = 0;
512   this->TimeStepRange[1] = 0;
513   this->DeformedMesh = 1;
514   this->RemoveDeletedCells = 1;
515   this->DeletedCellsAsGhostArray = 0;
516   this->InputDeck = nullptr;
517   this->Parts = nullptr;
518 }
519 
~vtkLSDynaReader()520 vtkLSDynaReader::~vtkLSDynaReader()
521 {
522   this->ResetPartsCache();
523   this->SetInputDeck(nullptr);
524   delete this->P;
525   this->P = nullptr;
526 }
527 
PrintSelf(ostream & os,vtkIndent indent)528 void vtkLSDynaReader::PrintSelf( ostream &os, vtkIndent indent )
529 {
530   this->Superclass::PrintSelf( os, indent );
531 
532   os << indent << "Title: \"" << this->GetTitle() << "\"" << endl;
533   os << indent << "InputDeck: " << (this->InputDeck ? this->InputDeck : "(null)") << endl;
534   os << indent << "DeformedMesh: " << (this->DeformedMesh ? "On" : "Off") << endl;
535   os << indent << "RemoveDeletedCells: " << (this->RemoveDeletedCells ? "On" : "Off") << endl;
536   os << indent << "TimeStepRange: " << this->TimeStepRange[0] << ", " << this->TimeStepRange[1] << endl;
537 
538   if (this->P)
539   {
540     os << indent << "PrivateData: " << this->P << endl;
541   }
542   else
543   {
544     os << indent << "PrivateData: (none)" << endl;
545   }
546   os << indent << "Show Deleted Cells as Ghost Cells: "<<
547         (this->DeletedCellsAsGhostArray ? "On" : "Off") << endl;
548 
549   os << indent << "Dimensionality: " << this->GetDimensionality() << endl;
550   os << indent << "Nodes: " << this->GetNumberOfNodes() << endl;
551   os << indent << "Cells: " << this->GetNumberOfCells() << endl;
552 
553   os << indent << "PointArrays: ";
554   for ( int i=0; i<this->GetNumberOfPointArrays(); ++i )
555   {
556     os << this->GetPointArrayName( i ) << " ";
557   }
558   os << endl;
559 
560   os << "CellArrays: " << endl;
561   for ( int ct = 0; ct < LSDynaMetaData::NUM_CELL_TYPES; ++ct )
562   {
563     os << vtkLSDynaCellTypes[ct] << ":" << endl;
564     for ( int i = 0; i < this->GetNumberOfCellArrays( ct ); ++i )
565     {
566       os << this->GetCellArrayName( ct, i ) << " ";
567     }
568     os << endl;
569   }
570   os << endl;
571 
572   os << indent << "Time Steps: " << this->GetNumberOfTimeSteps() << endl;
573   for ( int j=0; j<this->GetNumberOfTimeSteps(); ++j )
574   {
575     os.precision(5);
576     os.width(12);
577     os << this->GetTimeValue(j) ;
578     if ( (j+1) % 8 == 0 && j != this->GetNumberOfTimeSteps()-1 )
579     {
580       os << endl << indent;
581     }
582     else
583     {
584       os << " ";
585     }
586   }
587   os << endl;
588 }
589 
Dump(ostream & os)590 void vtkLSDynaReader::Dump( ostream& os )
591 {
592   vtkIndent indent;
593   os << indent << "Title: \"" << this->GetTitle() << "\"" << endl
594      << indent << "DeformedMesh: " << (this->DeformedMesh ? "On" : "Off") << endl
595      << indent << "RemoveDeletedCells: " << (this->RemoveDeletedCells ? "On" : "Off") << endl
596      << indent << "TimeStepRange: " << this->TimeStepRange[0] << ", " << this->TimeStepRange[1] << endl
597      << indent << "PrivateData: " << this->P << endl
598      << indent << "Dimensionality: " << this->GetDimensionality() << endl
599      << indent << "Nodes: " << this->GetNumberOfNodes() << endl
600      << indent << "Cells: " << this->GetNumberOfCells() << endl
601      << indent << "PointArrays:    ";
602   for ( int i=0; i<this->GetNumberOfPointArrays(); ++i )
603   {
604     os << this->GetPointArrayName( i ) << " ";
605   }
606   os << endl
607      << "CellArrays:" << endl;
608   for ( int ct = 0; ct < LSDynaMetaData::NUM_CELL_TYPES; ++ct )
609   {
610     os << vtkLSDynaCellTypes[ct] << ":" << endl;
611     for ( int i = 0; i < this->GetNumberOfCellArrays( ct ); ++i )
612     {
613       os << this->GetCellArrayName( ct, i ) << " ";
614     }
615     os << endl;
616   }
617   os << endl;
618 
619   os << indent << "Time Steps:       " << this->GetNumberOfTimeSteps() << endl;
620   for ( int j=0; j<this->GetNumberOfTimeSteps(); ++j )
621   {
622     os.precision(5);
623     os.width(12);
624     os << this->GetTimeValue(j) ;
625     if ( (j+1) % 8 == 0 && j != this->GetNumberOfTimeSteps()-1 )
626     {
627       os << endl << indent;
628     }
629     else
630     {
631       os << " ";
632     }
633   }
634   os << endl;
635 }
636 
DebugDump()637 void vtkLSDynaReader::DebugDump()
638 {
639   this->Dump( cout );
640 }
641 
CanReadFile(const char * fname)642 int vtkLSDynaReader::CanReadFile( const char* fname )
643 {
644   if ( ! fname )
645     return 0;
646 
647   std::string dbDir = vtksys::SystemTools::GetFilenamePath( fname );
648   std::string dbName = vtksys::SystemTools::GetFilenameName( fname );
649   std::string dbExt;
650   std::string::size_type dot;
651   LSDynaMetaData* p = new LSDynaMetaData;
652   int result = 0;
653 
654   // GetFilenameExtension doesn't look for the rightmost "." ... do it ourselves.
655   dot = dbName.rfind( '.' );
656   if ( dot != std::string::npos )
657   {
658     dbExt = dbName.substr( dot );
659   }
660   else
661   {
662     dbExt = "";
663   }
664 
665   p->Fam.SetDatabaseDirectory( dbDir );
666 
667   if ( dbExt == ".k" || dbExt == ".lsdyna" )
668   {
669     p->Fam.SetDatabaseBaseName( "/d3plot" );
670   }
671   else
672   {
673     vtksys::SystemTools::Stat_t st;
674     if ( vtksys::SystemTools::Stat( fname, &st ) == 0 )
675     {
676       dbName.insert( 0, "/" );
677       p->Fam.SetDatabaseBaseName( dbName );
678     }
679     else
680     {
681       p->Fam.SetDatabaseBaseName( "/d3plot" );
682     }
683   }
684   // If the time step is set before RequestInformation is called, we must
685   // read the header information immediately in order to determine whether
686   // the timestep that's been passed is valid. If it's not, we ignore it.
687   if ( ! p->FileIsValid )
688   {
689     if ( p->Fam.GetDatabaseDirectory().empty() )
690     {
691       result = -1;
692     }
693     else
694     {
695       if ( p->Fam.GetDatabaseBaseName().empty() )
696       {
697         p->Fam.SetDatabaseBaseName( "/d3plot" ); // not a bad assumption.
698       }
699       p->Fam.ScanDatabaseDirectory();
700       if ( p->Fam.GetNumberOfFiles() < 1 )
701       {
702         result = -1;
703       }
704       else
705       {
706         if ( p->Fam.DetermineStorageModel() != 0 )
707           result = 0;
708         else
709           result = 1;
710       }
711     }
712   }
713   delete p;
714 
715   return result > 0; // -1 and 0 are both problems, 1 indicates success.
716 }
717 
SetDatabaseDirectory(const char * f)718 void vtkLSDynaReader::SetDatabaseDirectory( const char* f )
719 {
720   vtkDebugMacro(<< this->GetClassName() << " (" << this << "): setting DatabaseDirectory to " << f );
721   if ( ! f )
722   {
723     if ( ! this->P->Fam.GetDatabaseDirectory().empty() )
724     { // no string => no database directory
725       this->P->Reset();
726       this->SetInputDeck( nullptr );
727       this->ResetPartsCache();
728       this->Modified();
729     }
730     return;
731   }
732   if ( strcmp(this->P->Fam.GetDatabaseDirectory().c_str(), f) )
733   {
734     this->P->Reset();
735     this->SetInputDeck( nullptr );
736     this->P->Fam.SetDatabaseDirectory( std::string(f) );
737     this->ResetPartsCache();
738     this->Modified();
739   }
740 }
741 
GetDatabaseDirectory()742 const char* vtkLSDynaReader::GetDatabaseDirectory()
743 {
744   return this->P->Fam.GetDatabaseDirectory().c_str();
745 }
746 
IsDatabaseValid()747 int vtkLSDynaReader::IsDatabaseValid()
748 {
749   return this->P->FileIsValid;
750 }
751 
SetFileName(const char * f)752 void vtkLSDynaReader::SetFileName( const char* f )
753 {
754   std::string dbDir = vtksys::SystemTools::GetFilenamePath( f );
755   std::string dbName = vtksys::SystemTools::GetFilenameName( f );
756   std::string dbExt;
757   std::string::size_type dot;
758 
759   // GetFilenameExtension doesn't look for the rightmost "." ... do it ourselves.
760   dot = dbName.rfind( '.' );
761   if ( dot != std::string::npos )
762   {
763     dbExt = dbName.substr( dot );
764   }
765   else
766   {
767     dbExt = "";
768   }
769 
770   this->SetDatabaseDirectory( dbDir.c_str() );
771 
772   if ( dbExt == ".k" || dbExt == ".lsdyna" )
773   {
774     this->SetInputDeck( f );
775     this->P->Fam.SetDatabaseBaseName( "/d3plot" );
776   }
777   else
778   {
779     vtksys::SystemTools::Stat_t st;
780     if ( vtksys::SystemTools::Stat( f, &st ) == 0 )
781     {
782       dbName.insert( 0, "/" );
783       this->P->Fam.SetDatabaseBaseName( dbName );
784     }
785     else
786     {
787       this->P->Fam.SetDatabaseBaseName( "/d3plot" );
788     }
789   }
790 }
791 
GetFileName()792 const char* vtkLSDynaReader::GetFileName()
793 {
794   // This is completely thread UNsafe. But what to do?
795   static std::string filenameSurrogate;
796   filenameSurrogate = this->P->Fam.GetDatabaseDirectory() + "/d3plot";
797   return filenameSurrogate.c_str();
798 }
799 
GetTitle()800 char* vtkLSDynaReader::GetTitle()
801 {
802   return this->P->Title;
803 }
804 
GetDimensionality()805 int vtkLSDynaReader::GetDimensionality()
806 {
807   return this->P->Dimensionality;
808 }
809 
SetTimeStep(vtkIdType t)810 void vtkLSDynaReader::SetTimeStep( vtkIdType t )
811 {
812   LSDynaMetaData* p = this->P;
813   if ( p->CurrentState == t )
814   {
815     return;
816   }
817 
818   // If the time step is set before RequestInformation is called, we must
819   // read the header information immediately in order to determine whether
820   // the timestep that's been passed is valid. If it's not, we ignore it.
821   if ( ! p->FileIsValid )
822   {
823     if ( p->Fam.GetDatabaseDirectory().empty() )
824     {
825       vtkErrorMacro( "You haven't set the LS-Dyna database directory!" );
826       return;
827     }
828 
829     p->Fam.SetDatabaseBaseName( "/d3plot" ); // force this for now.
830     p->Fam.ScanDatabaseDirectory();
831     if ( p->Fam.GetNumberOfFiles() < 1 )
832     {
833       p->FileIsValid = 0;
834       return;
835     }
836     p->Fam.DetermineStorageModel();
837     p->MaxFileLength = p->FileSizeFactor*512*512*p->Fam.GetWordSize();
838     p->FileIsValid = 1;
839 
840     // OK, now we have a list of files. Next, determine the length of the
841     // state vector (#bytes of data stored per time step):
842     this->ReadHeaderInformation( 0 );
843 
844     // Finally, we can loop through and determine where all the state
845     // vectors start for each time step.
846     this->ScanDatabaseTimeSteps();
847   }
848 
849   // Now, make sure we update the dictionary to contain information
850   // relevant to the adaptation level that matches the requested timestep.
851   if ( t >= 0 && t < (int) p->TimeValues.size() )
852   {
853     if ( p->Fam.GetCurrentAdaptLevel() != p->Fam.TimeAdaptLevel( t ) )
854     {
855       if ( this->ReadHeaderInformation( p->Fam.TimeAdaptLevel( t ) ) == 0 )
856       {
857         // unable to read the header information for the adaptation level corresponding
858         // to the requested time step
859         return;
860       }
861     }
862   }
863 
864   p->CurrentState = t;
865   this->Modified();
866 }
867 
GetTimeStep()868 vtkIdType vtkLSDynaReader::GetTimeStep()
869 {
870   return this->P->CurrentState;
871 }
872 
GetNumberOfTimeSteps()873 vtkIdType vtkLSDynaReader::GetNumberOfTimeSteps()
874 {
875   return (vtkIdType) this->P->TimeValues.size();
876 }
877 
GetTimeValue(vtkIdType s)878 double vtkLSDynaReader::GetTimeValue( vtkIdType s )
879 {
880   if ( s < 0 || s >= (vtkIdType) this->P->TimeValues.size() )
881   {
882     return -1.0;
883   }
884 
885   return this->P->TimeValues[s];
886 }
887 
GetNumberOfNodes()888 vtkIdType vtkLSDynaReader::GetNumberOfNodes()
889 {
890   return this->P->NumberOfNodes;
891 }
892 
GetNumberOfCells()893 vtkIdType vtkLSDynaReader::GetNumberOfCells()
894 {
895   vtkIdType tmp=0;
896   for ( int c=0; c<LSDynaMetaData::NUM_CELL_TYPES; ++c )
897   {
898     tmp += this->P->NumberOfCells[c];
899   }
900   return tmp;
901 }
902 
GetNumberOfSolidCells()903 vtkIdType vtkLSDynaReader::GetNumberOfSolidCells()
904 {
905   return this->P->NumberOfCells[LSDynaMetaData::SOLID];
906 }
907 
GetNumberOfThickShellCells()908 vtkIdType vtkLSDynaReader::GetNumberOfThickShellCells()
909 {
910   return this->P->NumberOfCells[LSDynaMetaData::THICK_SHELL];
911 }
912 
GetNumberOfShellCells()913 vtkIdType vtkLSDynaReader::GetNumberOfShellCells()
914 {
915   return this->P->NumberOfCells[LSDynaMetaData::SHELL];
916 }
917 
GetNumberOfRigidBodyCells()918 vtkIdType vtkLSDynaReader::GetNumberOfRigidBodyCells()
919 {
920   return this->P->NumberOfCells[LSDynaMetaData::RIGID_BODY];
921 }
922 
GetNumberOfRoadSurfaceCells()923 vtkIdType vtkLSDynaReader::GetNumberOfRoadSurfaceCells()
924 {
925   return this->P->NumberOfCells[LSDynaMetaData::ROAD_SURFACE];
926 }
927 
GetNumberOfBeamCells()928 vtkIdType vtkLSDynaReader::GetNumberOfBeamCells()
929 {
930   return this->P->NumberOfCells[LSDynaMetaData::BEAM];
931 }
932 
GetNumberOfParticleCells()933 vtkIdType vtkLSDynaReader::GetNumberOfParticleCells()
934 {
935   return this->P->NumberOfCells[LSDynaMetaData::PARTICLE];
936 }
937 
GetNumberOfContinuumCells()938 vtkIdType vtkLSDynaReader::GetNumberOfContinuumCells()
939 {
940   vtkIdType tmp=0;
941   for ( int c=LSDynaMetaData::PARTICLE+1; c<LSDynaMetaData::NUM_CELL_TYPES; ++c )
942   {
943     tmp += this->P->NumberOfCells[c];
944   }
945   return tmp;
946 }
947 
948 // =================================== Point array queries
GetNumberOfPointArrays()949 int vtkLSDynaReader::GetNumberOfPointArrays()
950 {
951   return (int) this->P->PointArrayNames.size();
952 }
953 
GetPointArrayName(int a)954 const char* vtkLSDynaReader::GetPointArrayName( int a )
955 {
956   if ( a < 0 || a >= (int) this->P->PointArrayNames.size() )
957     return nullptr;
958 
959   return this->P->PointArrayNames[a].c_str();
960 }
961 
GetPointArrayStatus(int a)962 int vtkLSDynaReader::GetPointArrayStatus( int a )
963 {
964   if ( a < 0 || a >= (int) this->P->PointArrayStatus.size() )
965     return 0;
966 
967   return this->P->PointArrayStatus[a];
968 }
969 
SetPointArrayStatus(int a,int stat)970 void vtkLSDynaReader::SetPointArrayStatus( int a, int stat )
971 {
972   if ( a < 0 || a >= (int) this->P->PointArrayStatus.size() )
973   {
974     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
975     return;
976   }
977 
978   if ( stat == this->P->PointArrayStatus[a] )
979     return;
980 
981   this->P->PointArrayStatus[a] = stat;
982   this->ResetPartsCache();
983   this->Modified();
984 }
985 
GetNumberOfComponentsInPointArray(int a)986 int vtkLSDynaReader::GetNumberOfComponentsInPointArray( int a )
987 {
988   if ( a < 0 || a >= (int) this->P->PointArrayStatus.size() )
989     return 0;
990 
991   return this->P->PointArrayComponents[a];
992 }
993 
994 // =================================== Cell array queries
GetNumberOfCellArrays(int ct)995 int vtkLSDynaReader::GetNumberOfCellArrays( int ct )
996 {
997   return (int) this->P->CellArrayNames[ct].size();
998 }
999 
GetCellArrayName(int ct,int a)1000 const char* vtkLSDynaReader::GetCellArrayName( int ct, int a )
1001 {
1002   if ( a < 0 || a >= (int) this->P->CellArrayNames[ct].size() )
1003     return nullptr;
1004 
1005   return this->P->CellArrayNames[ct][a].c_str();
1006 }
1007 
GetCellArrayStatus(int ct,int a)1008 int vtkLSDynaReader::GetCellArrayStatus( int ct, int a )
1009 {
1010   if ( a < 0 || a >= (int) this->P->CellArrayStatus[ct].size() )
1011     return 0;
1012 
1013   return this->P->CellArrayStatus[ct][a];
1014 }
1015 
GetNumberOfComponentsInCellArray(int ct,int a)1016 int vtkLSDynaReader::GetNumberOfComponentsInCellArray( int ct, int a )
1017 {
1018   if ( a < 0 || a >= (int) this->P->CellArrayStatus[ct].size() )
1019     return 0;
1020 
1021   return this->P->CellArrayComponents[ct][a];
1022 }
1023 
SetCellArrayStatus(int ct,int a,int stat)1024 void vtkLSDynaReader::SetCellArrayStatus( int ct, int a, int stat )
1025 {
1026   if ( a < 0 || a >= (int) this->P->CellArrayStatus[ct].size() )
1027   {
1028     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1029     return;
1030   }
1031 
1032   if ( stat == this->P->CellArrayStatus[ct][a] )
1033     return;
1034 
1035   this->P->CellArrayStatus[ct][a] = stat;
1036   this->ResetPartsCache();
1037   this->Modified();
1038 }
1039 
1040 // =================================== Cell array queries
GetNumberOfSolidArrays()1041 int vtkLSDynaReader::GetNumberOfSolidArrays()
1042 {
1043   return (int) this->P->CellArrayNames[LSDynaMetaData::SOLID].size();
1044 }
1045 
GetSolidArrayName(int a)1046 const char* vtkLSDynaReader::GetSolidArrayName( int a )
1047 {
1048   if ( a < 0 || a >= (int) this->P->CellArrayNames[LSDynaMetaData::SOLID].size() )
1049     return nullptr;
1050 
1051   return this->P->CellArrayNames[LSDynaMetaData::SOLID][a].c_str();
1052 }
1053 
GetSolidArrayStatus(int a)1054 int vtkLSDynaReader::GetSolidArrayStatus( int a )
1055 {
1056   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::SOLID].size() )
1057     return 0;
1058 
1059   return this->P->CellArrayStatus[LSDynaMetaData::SOLID][a];
1060 }
1061 
GetNumberOfComponentsInSolidArray(int a)1062 int vtkLSDynaReader::GetNumberOfComponentsInSolidArray( int a )
1063 {
1064   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::SOLID].size() )
1065     return 0;
1066 
1067   return this->P->CellArrayComponents[LSDynaMetaData::SOLID][a];
1068 }
1069 
SetSolidArrayStatus(int a,int stat)1070 void vtkLSDynaReader::SetSolidArrayStatus( int a, int stat )
1071 {
1072   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::SOLID].size() )
1073   {
1074     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1075     return;
1076   }
1077 
1078   if ( stat == this->P->CellArrayStatus[LSDynaMetaData::SOLID][a] )
1079     return;
1080 
1081   this->P->CellArrayStatus[LSDynaMetaData::SOLID][a] = stat;
1082   this->ResetPartsCache();
1083   this->Modified();
1084 }
1085 
1086 // =================================== Cell array queries
GetNumberOfThickShellArrays()1087 int vtkLSDynaReader::GetNumberOfThickShellArrays()
1088 {
1089   return (int) this->P->CellArrayNames[LSDynaMetaData::THICK_SHELL].size();
1090 }
1091 
GetThickShellArrayName(int a)1092 const char* vtkLSDynaReader::GetThickShellArrayName( int a )
1093 {
1094   if ( a < 0 || a >= (int) this->P->CellArrayNames[LSDynaMetaData::THICK_SHELL].size() )
1095     return nullptr;
1096 
1097   return this->P->CellArrayNames[LSDynaMetaData::THICK_SHELL][a].c_str();
1098 }
1099 
GetThickShellArrayStatus(int a)1100 int vtkLSDynaReader::GetThickShellArrayStatus( int a )
1101 {
1102   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::THICK_SHELL].size() )
1103     return 0;
1104 
1105   return this->P->CellArrayStatus[LSDynaMetaData::THICK_SHELL][a];
1106 }
1107 
GetNumberOfComponentsInThickShellArray(int a)1108 int vtkLSDynaReader::GetNumberOfComponentsInThickShellArray( int a )
1109 {
1110   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::THICK_SHELL].size() )
1111     return 0;
1112 
1113   return this->P->CellArrayComponents[LSDynaMetaData::THICK_SHELL][a];
1114 }
1115 
SetThickShellArrayStatus(int a,int stat)1116 void vtkLSDynaReader::SetThickShellArrayStatus( int a, int stat )
1117 {
1118   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::THICK_SHELL].size() )
1119   {
1120     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1121     return;
1122   }
1123 
1124   if ( stat == this->P->CellArrayStatus[LSDynaMetaData::THICK_SHELL][a] )
1125     return;
1126 
1127   this->P->CellArrayStatus[LSDynaMetaData::THICK_SHELL][a] = stat;
1128   this->ResetPartsCache();
1129   this->Modified();
1130 }
1131 
1132 // =================================== Cell array queries
GetNumberOfShellArrays()1133 int vtkLSDynaReader::GetNumberOfShellArrays()
1134 {
1135   return (int) this->P->CellArrayNames[LSDynaMetaData::SHELL].size();
1136 }
1137 
GetShellArrayName(int a)1138 const char* vtkLSDynaReader::GetShellArrayName( int a )
1139 {
1140   if ( a < 0 || a >= (int) this->P->CellArrayNames[LSDynaMetaData::SHELL].size() )
1141     return nullptr;
1142 
1143   return this->P->CellArrayNames[LSDynaMetaData::SHELL][a].c_str();
1144 }
1145 
GetShellArrayStatus(int a)1146 int vtkLSDynaReader::GetShellArrayStatus( int a )
1147 {
1148   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::SHELL].size() )
1149     return 0;
1150 
1151   return this->P->CellArrayStatus[LSDynaMetaData::SHELL][a];
1152 }
1153 
GetNumberOfComponentsInShellArray(int a)1154 int vtkLSDynaReader::GetNumberOfComponentsInShellArray( int a )
1155 {
1156   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::SHELL].size() )
1157     return 0;
1158 
1159   return this->P->CellArrayComponents[LSDynaMetaData::SHELL][a];
1160 }
1161 
SetShellArrayStatus(int a,int stat)1162 void vtkLSDynaReader::SetShellArrayStatus( int a, int stat )
1163 {
1164   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::SHELL].size() )
1165   {
1166     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1167     return;
1168   }
1169 
1170   if ( stat == this->P->CellArrayStatus[LSDynaMetaData::SHELL][a] )
1171     return;
1172 
1173   this->P->CellArrayStatus[LSDynaMetaData::SHELL][a] = stat;
1174   this->ResetPartsCache();
1175   this->Modified();
1176 }
1177 
1178 // =================================== Cell array queries
GetNumberOfRigidBodyArrays()1179 int vtkLSDynaReader::GetNumberOfRigidBodyArrays()
1180 {
1181   return (int) this->P->CellArrayNames[LSDynaMetaData::RIGID_BODY].size();
1182 }
1183 
GetRigidBodyArrayName(int a)1184 const char* vtkLSDynaReader::GetRigidBodyArrayName( int a )
1185 {
1186   if ( a < 0 || a >= (int) this->P->CellArrayNames[LSDynaMetaData::RIGID_BODY].size() )
1187     return nullptr;
1188 
1189   return this->P->CellArrayNames[LSDynaMetaData::RIGID_BODY][a].c_str();
1190 }
1191 
GetRigidBodyArrayStatus(int a)1192 int vtkLSDynaReader::GetRigidBodyArrayStatus( int a )
1193 {
1194   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::RIGID_BODY].size() )
1195     return 0;
1196 
1197   return this->P->CellArrayStatus[LSDynaMetaData::RIGID_BODY][a];
1198 }
1199 
GetNumberOfComponentsInRigidBodyArray(int a)1200 int vtkLSDynaReader::GetNumberOfComponentsInRigidBodyArray( int a )
1201 {
1202   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::RIGID_BODY].size() )
1203     return 0;
1204 
1205   return this->P->CellArrayComponents[LSDynaMetaData::RIGID_BODY][a];
1206 }
1207 
SetRigidBodyArrayStatus(int a,int stat)1208 void vtkLSDynaReader::SetRigidBodyArrayStatus( int a, int stat )
1209 {
1210   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::RIGID_BODY].size() )
1211   {
1212     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1213     return;
1214   }
1215 
1216   if ( stat == this->P->CellArrayStatus[LSDynaMetaData::RIGID_BODY][a] )
1217     return;
1218 
1219   this->P->CellArrayStatus[LSDynaMetaData::RIGID_BODY][a] = stat;
1220   this->ResetPartsCache();
1221   this->Modified();
1222 }
1223 
1224 // =================================== Cell array queries
GetNumberOfRoadSurfaceArrays()1225 int vtkLSDynaReader::GetNumberOfRoadSurfaceArrays()
1226 {
1227   return (int) this->P->CellArrayNames[LSDynaMetaData::ROAD_SURFACE].size();
1228 }
1229 
GetRoadSurfaceArrayName(int a)1230 const char* vtkLSDynaReader::GetRoadSurfaceArrayName( int a )
1231 {
1232   if ( a < 0 || a >= (int) this->P->CellArrayNames[LSDynaMetaData::ROAD_SURFACE].size() )
1233     return nullptr;
1234 
1235   return this->P->CellArrayNames[LSDynaMetaData::ROAD_SURFACE][a].c_str();
1236 }
1237 
GetRoadSurfaceArrayStatus(int a)1238 int vtkLSDynaReader::GetRoadSurfaceArrayStatus( int a )
1239 {
1240   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::ROAD_SURFACE].size() )
1241     return 0;
1242 
1243   return this->P->CellArrayStatus[LSDynaMetaData::ROAD_SURFACE][a];
1244 }
1245 
GetNumberOfComponentsInRoadSurfaceArray(int a)1246 int vtkLSDynaReader::GetNumberOfComponentsInRoadSurfaceArray( int a )
1247 {
1248   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::ROAD_SURFACE].size() )
1249     return 0;
1250 
1251   return this->P->CellArrayComponents[LSDynaMetaData::ROAD_SURFACE][a];
1252 }
1253 
SetRoadSurfaceArrayStatus(int a,int stat)1254 void vtkLSDynaReader::SetRoadSurfaceArrayStatus( int a, int stat )
1255 {
1256   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::ROAD_SURFACE].size() )
1257   {
1258     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1259     return;
1260   }
1261 
1262   if ( stat == this->P->CellArrayStatus[LSDynaMetaData::ROAD_SURFACE][a] )
1263     return;
1264 
1265   this->P->CellArrayStatus[LSDynaMetaData::ROAD_SURFACE][a] = stat;
1266   this->ResetPartsCache();
1267   this->Modified();
1268 }
1269 
1270 // =================================== Cell array queries
GetNumberOfBeamArrays()1271 int vtkLSDynaReader::GetNumberOfBeamArrays()
1272 {
1273   return (int) this->P->CellArrayNames[LSDynaMetaData::BEAM].size();
1274 }
1275 
GetBeamArrayName(int a)1276 const char* vtkLSDynaReader::GetBeamArrayName( int a )
1277 {
1278   if ( a < 0 || a >= (int) this->P->CellArrayNames[LSDynaMetaData::BEAM].size() )
1279     return nullptr;
1280 
1281   return this->P->CellArrayNames[LSDynaMetaData::BEAM][a].c_str();
1282 }
1283 
GetBeamArrayStatus(int a)1284 int vtkLSDynaReader::GetBeamArrayStatus( int a )
1285 {
1286   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::BEAM].size() )
1287     return 0;
1288 
1289   return this->P->CellArrayStatus[LSDynaMetaData::BEAM][a];
1290 }
1291 
GetNumberOfComponentsInBeamArray(int a)1292 int vtkLSDynaReader::GetNumberOfComponentsInBeamArray( int a )
1293 {
1294   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::BEAM].size() )
1295     return 0;
1296 
1297   return this->P->CellArrayComponents[LSDynaMetaData::BEAM][a];
1298 }
1299 
SetBeamArrayStatus(int a,int stat)1300 void vtkLSDynaReader::SetBeamArrayStatus( int a, int stat )
1301 {
1302   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::BEAM].size() )
1303   {
1304     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1305     return;
1306   }
1307 
1308   if ( stat == this->P->CellArrayStatus[LSDynaMetaData::BEAM][a] )
1309     return;
1310 
1311   this->P->CellArrayStatus[LSDynaMetaData::BEAM][a] = stat;
1312   this->ResetPartsCache();
1313   this->Modified();
1314 }
1315 
1316 // =================================== Cell array queries
GetNumberOfParticleArrays()1317 int vtkLSDynaReader::GetNumberOfParticleArrays()
1318 {
1319   return (int) this->P->CellArrayNames[LSDynaMetaData::PARTICLE].size();
1320 }
1321 
GetParticleArrayName(int a)1322 const char* vtkLSDynaReader::GetParticleArrayName( int a )
1323 {
1324   if ( a < 0 || a >= (int) this->P->CellArrayNames[LSDynaMetaData::PARTICLE].size() )
1325     return nullptr;
1326 
1327   return this->P->CellArrayNames[LSDynaMetaData::PARTICLE][a].c_str();
1328 }
1329 
GetParticleArrayStatus(int a)1330 int vtkLSDynaReader::GetParticleArrayStatus( int a )
1331 {
1332   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::PARTICLE].size() )
1333     return 0;
1334 
1335   return this->P->CellArrayStatus[LSDynaMetaData::PARTICLE][a];
1336 }
1337 
GetNumberOfComponentsInParticleArray(int a)1338 int vtkLSDynaReader::GetNumberOfComponentsInParticleArray( int a )
1339 {
1340   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::PARTICLE].size() )
1341     return 0;
1342 
1343   return this->P->CellArrayComponents[LSDynaMetaData::PARTICLE][a];
1344 }
1345 
SetParticleArrayStatus(int a,int stat)1346 void vtkLSDynaReader::SetParticleArrayStatus( int a, int stat )
1347 {
1348   if ( a < 0 || a >= (int) this->P->CellArrayStatus[LSDynaMetaData::PARTICLE].size() )
1349   {
1350     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1351     return;
1352   }
1353 
1354   if ( stat == this->P->CellArrayStatus[LSDynaMetaData::PARTICLE][a] )
1355     return;
1356 
1357   this->P->CellArrayStatus[LSDynaMetaData::PARTICLE][a] = stat;
1358   this->ResetPartsCache();
1359   this->Modified();
1360 }
1361 
1362 // =================================== Part queries
GetNumberOfPartArrays()1363 int vtkLSDynaReader::GetNumberOfPartArrays()
1364 {
1365   return (int) this->P->PartNames.size();
1366 }
1367 
GetPartArrayName(int a)1368 const char* vtkLSDynaReader::GetPartArrayName( int a )
1369 {
1370   if ( a < 0 || a >= (int) this->P->PartNames.size() )
1371     return nullptr;
1372 
1373   return this->P->PartNames[a].c_str();
1374 }
1375 
GetPartArrayStatus(int a)1376 int vtkLSDynaReader::GetPartArrayStatus( int a )
1377 {
1378   if ( a < 0 || a >= (int) this->P->PartStatus.size() )
1379     return 0;
1380 
1381   return this->P->PartStatus[a];
1382 }
1383 
SetPartArrayStatus(int a,int stat)1384 void vtkLSDynaReader::SetPartArrayStatus( int a, int stat )
1385 {
1386   if ( a < 0 || a >= (int) this->P->PartStatus.size() )
1387   {
1388     vtkWarningMacro( "Cannot set status of non-existent point array " << a );
1389     return;
1390   }
1391 
1392   if ( stat == this->P->PartStatus[a] )
1393     return;
1394 
1395   this->P->PartStatus[a] = stat;
1396   this->ResetPartsCache();
1397   this->Modified();
1398 }
1399 
1400 // ===================================
ResetPartsCache()1401 void vtkLSDynaReader::ResetPartsCache()
1402 {
1403   if(this->Parts)
1404   {
1405     this->Parts->Delete();
1406     this->Parts=nullptr;
1407   }
1408 }
1409 
1410 // =================================== Read the control word header for the current adaptation level
ReadHeaderInformation(int curAdapt)1411 int vtkLSDynaReader::ReadHeaderInformation( int curAdapt )
1412 {
1413   LSDynaMetaData* p = this->P;
1414 
1415   // =================================== Control Word Section
1416   p->Fam.SkipToWord( LSDynaFamily::ControlSection, curAdapt /*timestep*/, 0 );
1417   p->Fam.BufferChunk( LSDynaFamily::Char, 10 );
1418   memcpy( p->Title, p->Fam.GetNextWordAsChars(), 40*sizeof(char) );
1419   p->Title[40] = '\0';
1420 
1421   p->Fam.SkipToWord( LSDynaFamily::ControlSection, curAdapt /*timestep*/, 13 );
1422 
1423   // Release number: Release number in character*4 form: 50 for R5.0, 511c for R5.1.1c.
1424   p->Fam.BufferChunk( LSDynaFamily::Char, 1 );
1425   memcpy( p->ReleaseNumber, p->Fam.GetNextWordAsChars(), 4*sizeof(char));
1426   p->ReleaseNumber[4] = '\0';
1427 
1428   // Version: Code version, floating number, eg 960.0 it is used to distinguish the
1429   // floating point format, like cray, ieee, and dpieee.
1430   p->Fam.BufferChunk( LSDynaFamily::Float, 1 );
1431   p->CodeVersion = p->Fam.GetNextWordAsFloat();
1432 
1433   p->Fam.BufferChunk( LSDynaFamily::Int, 49 );
1434   p->Dict[    "NDIM"] = p->Fam.GetNextWordAsInt();
1435   p->Dict[   "NUMNP"] = p->Fam.GetNextWordAsInt();
1436   p->Dict[   "ICODE"] = p->Fam.GetNextWordAsInt();
1437   p->Dict[   "NGLBV"] = p->Fam.GetNextWordAsInt();
1438   p->Dict[      "IT"] = p->Fam.GetNextWordAsInt();
1439   p->Dict[      "IU"] = p->Fam.GetNextWordAsInt();
1440   p->Dict[      "IV"] = p->Fam.GetNextWordAsInt();
1441   p->Dict[      "IA"] = p->Fam.GetNextWordAsInt();
1442   p->Dict[    "NEL8"] = p->Fam.GetNextWordAsInt();
1443   p->Dict[ "NUMMAT8"] = p->Fam.GetNextWordAsInt();
1444   p->Fam.GetNextWordAsInt(); // BLANK
1445   p->Fam.GetNextWordAsInt(); // BLANK
1446   p->Dict[    "NV3D"] = p->Fam.GetNextWordAsInt();
1447   p->Dict[    "NEL2"] = p->Fam.GetNextWordAsInt();
1448   p->Dict[ "NUMMAT2"] = p->Fam.GetNextWordAsInt();
1449   p->Dict[    "NV1D"] = p->Fam.GetNextWordAsInt();
1450   p->Dict[    "NEL4"] = p->Fam.GetNextWordAsInt();
1451   p->Dict[ "NUMMAT4"] = p->Fam.GetNextWordAsInt();
1452   p->Dict[    "NV2D"] = p->Fam.GetNextWordAsInt();
1453   p->Dict[   "NEIPH"] = p->Fam.GetNextWordAsInt();
1454   p->Dict[   "NEIPS"] = p->Fam.GetNextWordAsInt();
1455   p->Dict[  "MAXINT"] = p->Fam.GetNextWordAsInt();
1456   // do MDLOPT here?
1457   p->Dict[   "NMSPH"] = p->Fam.GetNextWordAsInt();
1458   p->Dict[  "EDLOPT"] = p->Dict["NMSPH"]; // EDLOPT is not standard
1459   p->Dict[  "NGPSPH"] = p->Fam.GetNextWordAsInt();
1460   p->Dict[   "NARBS"] = p->Fam.GetNextWordAsInt();
1461   p->Dict[    "NELT"] = p->Fam.GetNextWordAsInt();
1462   p->Dict[ "NUMMATT"] = p->Fam.GetNextWordAsInt();
1463   p->Dict[   "NV3DT"] = p->Fam.GetNextWordAsInt();
1464   p->Dict["IOSHL(1)"] = p->Fam.GetNextWordAsInt() == 1000 ? 1 : 0;
1465   p->Dict["IOSHL(2)"] = p->Fam.GetNextWordAsInt() == 1000 ? 1 : 0;
1466   p->Dict["IOSHL(3)"] = p->Fam.GetNextWordAsInt() == 1000 ? 1 : 0;
1467   p->Dict["IOSHL(4)"] = p->Fam.GetNextWordAsInt() == 1000 ? 1 : 0;
1468   p->Dict[ "IALEMAT"] = p->Fam.GetNextWordAsInt();
1469   p->Dict[  "NCFDV1"] = p->Fam.GetNextWordAsInt();
1470   p->Dict[  "NCFDV2"] = p->Fam.GetNextWordAsInt();
1471   p->Dict[  "NADAPT"] = p->Fam.GetNextWordAsInt();
1472   p->Dict[   "NMMAT"] = p->Fam.GetNextWordAsInt();
1473 
1474   // NUMFLUID: Total number of ALE fluid groups. Fluid density and
1475   // volume fractions output as history variables, and a flag
1476   // for the dominant group. If negative multi-material
1477   // species mass for each group is also output. Order is: rho,
1478   // vf1, ... vfn, dvf flag, m1, ... mn. Density is at position 8
1479   // after the location for plastic strain. Any element material
1480   // history variables are written before the Ale variables, and
1481   // the six element strains components after these if
1482   // ISTRN=1.
1483   p->Dict["NUMFLUID"] = p->Fam.GetNextWordAsInt();
1484 
1485   // Compute the derived values in this->P
1486   // =========================================== Control Word Section Processing
1487   int itmp;
1488   vtkIdType iddtmp;
1489   char ctmp[128]; // temp space for generating keywords (i.e. isphfg) and attribute names (i.e., StressIntPt3)
1490 
1491   // --- Initialize some values
1492   p->ReadRigidRoadMvmt = 0;
1493   p->PreStateSize = 64*p->Fam.GetWordSize();
1494   p->StateSize = p->Fam.GetWordSize(); // Account for "time word"
1495   p->Dimensionality = p->Dict["NDIM"];
1496   switch (p->Dimensionality)
1497   {
1498   case 2:
1499   case 3:
1500     p->Dict["MATTYP"] = 0;
1501     p->ConnectivityUnpacked = 0;
1502     break;
1503   case 7:
1504     p->ReadRigidRoadMvmt = 1;
1505     VTK_FALLTHROUGH;
1506   case 5:
1507     p->Dict["MATTYP"] = 1;
1508     p->ConnectivityUnpacked = 1;
1509     p->Dimensionality = 3;
1510     break;
1511   case 4:
1512     p->ConnectivityUnpacked = 1;
1513     p->Dict["MATTYP"] = 0;
1514     p->Dimensionality = 3;
1515     break;
1516   default:
1517     vtkErrorMacro("Unknown Dimensionality " << p->Dimensionality << " encountered" );
1518     p->FileIsValid = 0;
1519     return 0;
1520   }
1521 
1522   // FIXME Are these marks valid since we are marking the word past the end of the chunk?
1523   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::StaticSection );
1524   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::MaterialTypeData );
1525   if ( p->Dict["MATTYP"] != 0 )
1526   {
1527     p->Fam.BufferChunk( LSDynaFamily::Int, 2 );
1528     p->Dict["NUMRBE"] = p->Fam.GetNextWordAsInt();
1529     p->Dict["NUMMAT"] = p->Fam.GetNextWordAsInt();
1530   }
1531   else
1532   {
1533     p->Dict["NUMRBE"] = 0;
1534     p->Dict["NUMMAT"] = 0;
1535   }
1536   p->NumberOfNodes = p->Dict["NUMNP"];
1537 
1538   p->NumberOfCells[LSDynaMetaData::RIGID_BODY] = p->Dict["NUMRBE"];
1539   p->NumberOfCells[LSDynaMetaData::SOLID] = p->Dict["NEL8"];
1540   p->NumberOfCells[LSDynaMetaData::THICK_SHELL] = p->Dict["NELT"];
1541   p->NumberOfCells[LSDynaMetaData::SHELL] = p->Dict["NEL4"];
1542   p->NumberOfCells[LSDynaMetaData::BEAM] = p->Dict["NEL2"];
1543   p->NumberOfCells[LSDynaMetaData::PARTICLE] = p->Dict["NMSPH"];
1544 
1545   p->StateSize += p->Dict["NGLBV"]*p->Fam.GetWordSize();
1546 
1547   if ( p->Dict["IT"] != 0 )
1548   {
1549     p->AddPointArray( LS_ARRAYNAME_TEMPERATURE, 1, 1 );
1550     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1551   }
1552   if ( p->Dict["IU"] != 0 )
1553   {
1554     p->AddPointArray( LS_ARRAYNAME_DEFLECTION, p->Dimensionality, 1 );
1555     p->StateSize += p->NumberOfNodes * p->Dimensionality * p->Fam.GetWordSize();
1556   }
1557   if ( p->Dict["IV"] != 0 )
1558   {
1559     p->AddPointArray( LS_ARRAYNAME_VELOCITY, p->Dimensionality, 1 );
1560     p->StateSize += p->NumberOfNodes * p->Dimensionality * p->Fam.GetWordSize();
1561   }
1562   if ( p->Dict["IA"] != 0 )
1563   {
1564     p->AddPointArray( LS_ARRAYNAME_ACCELERATION, p->Dimensionality, 1 );
1565     p->StateSize += p->NumberOfNodes * p->Dimensionality * p->Fam.GetWordSize();
1566   }
1567   p->Dict["cfdPressure"] = 0;
1568   p->Dict["cfdVort"] = 0;
1569   p->Dict["cfdXVort"] = 0;
1570   p->Dict["cfdYVort"] = 0;
1571   p->Dict["cfdZVort"] = 0;
1572   p->Dict["cfdRVort"] = 0;
1573   p->Dict["cfdEnstrophy"] = 0;
1574   p->Dict["cfdHelicity"] = 0;
1575   p->Dict["cfdStream"] = 0;
1576   p->Dict["cfdEnthalpy"] = 0;
1577   p->Dict["cfdDensity"] = 0;
1578   p->Dict["cfdTurbKE"] = 0;
1579   p->Dict["cfdDiss"] = 0;
1580   p->Dict["cfdEddyVisc"] = 0;
1581   itmp = p->Dict["NCFDV1"];
1582   if ( itmp & 2 )
1583   {
1584     p->AddPointArray( LS_ARRAYNAME_PRESSURE, 1, 1 );
1585     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1586     p->Dict["cfdPressure"] = 1;
1587   }
1588   if ( (itmp & 28) == 28 )
1589   {
1590     p->AddPointArray( LS_ARRAYNAME_VORTICITY, 3, 1 );
1591     p->StateSize += p->NumberOfNodes * 3 * p->Fam.GetWordSize();
1592     p->Dict["cfdVort"] = 1;
1593     p->Dict["cfdXVort"] = 1;
1594     p->Dict["cfdYVort"] = 1;
1595     p->Dict["cfdZVort"] = 1;
1596   }
1597   else
1598   { // OK, we don't have all the vector components... maybe we have some of them?
1599     if ( itmp & 4 )
1600     {
1601       p->AddPointArray( LS_ARRAYNAME_VORTICITY "_X", 1, 1 );
1602       p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1603       p->Dict["cfdXVort"] = 1;
1604     }
1605     if ( itmp & 8 )
1606     {
1607       p->AddPointArray( LS_ARRAYNAME_VORTICITY "_Y", 1, 1 );
1608       p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1609       p->Dict["cfdYVort"] = 1;
1610     }
1611     if ( itmp & 16 )
1612     {
1613       p->AddPointArray( LS_ARRAYNAME_VORTICITY "_Z", 1, 1 );
1614       p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1615       p->Dict["cfdZVort"] = 1;
1616     }
1617   }
1618   if ( itmp & 32 )
1619   {
1620     p->AddPointArray( LS_ARRAYNAME_RESULTANTVORTICITY, 1, 1 );
1621     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1622     p->Dict["cfdRVort"] = 1;
1623   if ( itmp & 64 )
1624   {
1625     p->AddPointArray( LS_ARRAYNAME_ENSTROPHY, 1, 1 );
1626     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1627     p->Dict["cfdEnstrophy"] = 1;
1628   }
1629   if ( itmp & 128 )
1630   {
1631     p->AddPointArray( LS_ARRAYNAME_HELICITY, 1, 1 );
1632     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1633     p->Dict["cfdHelicity"] = 1;
1634   }
1635   if ( itmp & 256 )
1636   {
1637     p->AddPointArray( LS_ARRAYNAME_STREAMFUNCTION, 1, 1 );
1638     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1639     p->Dict["cfdStream"] = 1;
1640   }
1641   if ( itmp & 512 )
1642   {
1643     p->AddPointArray( LS_ARRAYNAME_ENTHALPY, 1, 1 );
1644     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1645     p->Dict["cfdEnthalpy"] = 1;
1646   }
1647   if ( itmp & 1024 )
1648   {
1649     p->AddPointArray( LS_ARRAYNAME_DENSITY, 1, 1 );
1650     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1651     p->Dict["cfdDensity"] = 1;
1652   }
1653   if ( itmp & 2048 )
1654   {
1655     p->AddPointArray( LS_ARRAYNAME_TURBULENTKE, 1, 1 );
1656     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1657     p->Dict["cfdTurbKE"] = 1;
1658   }
1659   if ( itmp & 4096 )
1660   {
1661     p->AddPointArray( LS_ARRAYNAME_DISSIPATION, 1, 1 );
1662     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1663     p->Dict["cfdDiss"] = 1;
1664   }
1665   if ( itmp & 1040384 )
1666   {
1667     p->AddPointArray( LS_ARRAYNAME_EDDYVISCOSITY, 1, 1 );
1668     p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1669     p->Dict["cfdEddyVisc"] = 1;
1670   }
1671   }
1672 
1673   char sname[]=LS_ARRAYNAME_SPECIES_BLNK;
1674   iddtmp = p->Dict["NCFDV2"];
1675   for ( itmp=1; itmp<11; ++itmp )
1676   {
1677     if ( iddtmp & (static_cast<vtkIdType>(1)<<itmp) )
1678     {
1679       snprintf( sname, sizeof(sname), LS_ARRAYNAME_SPECIES_FMT, itmp );
1680       p->AddPointArray( sname, 1, 1 );
1681       p->StateSize += p->NumberOfNodes * p->Fam.GetWordSize();
1682       snprintf( sname, sizeof(sname), "cfdSpec%02d", itmp );
1683       p->Dict[ sname ] = 1;
1684     }
1685     else
1686     {
1687       snprintf( sname, sizeof(sname), "cfdSpec%02d", itmp );
1688       p->Dict[ sname ] = 0;
1689     }
1690   }
1691 
1692   // Solid element state size   FIXME: 7 + NEIPH should really be NV3D (in case things change)
1693   p->StateSize += (7+p->Dict["NEIPH"])*p->NumberOfCells[LSDynaMetaData::SOLID]*p->Fam.GetWordSize();
1694   // Thick shell state size
1695   p->StateSize += p->Dict["NV3DT"]*p->NumberOfCells[LSDynaMetaData::THICK_SHELL]*p->Fam.GetWordSize();
1696   // (Thin) shell state size (we remove rigid body shell element state below)
1697   p->StateSize += p->Dict["NV2D"]*p->NumberOfCells[LSDynaMetaData::SHELL]*p->Fam.GetWordSize();
1698   // Beam state size
1699   p->StateSize += p->Dict["NV1D"]*p->NumberOfCells[LSDynaMetaData::BEAM]*p->Fam.GetWordSize();
1700 
1701   // ================================================ Static Information Section
1702   // This is marked above so we can read NUMRBE in time to do StateSize calculations
1703   // ================================================ Material Type Data Section
1704   // This is marked above so we can read NUMRBE in time to do StateSize calculations
1705   if ( p->Dict["MATTYP"] != 0 )
1706   {
1707     // If there are rigid body elements, they won't have state data and we must
1708     // reduce the state size
1709     p->StateSize -= p->Dict["NV2D"] * p->NumberOfCells[ LSDynaMetaData::RIGID_BODY ];
1710 
1711     p->Fam.BufferChunk( LSDynaFamily::Int, p->Dict["NUMMAT"] );
1712     for ( itmp = 0; itmp < p->Dict["NUMMAT"]; ++itmp )
1713     {
1714       p->RigidMaterials.insert( p->Fam.GetNextWordAsInt() );
1715     }
1716     p->PreStateSize += (2 + p->Dict["NUMMAT"])*p->Fam.GetWordSize();
1717   }
1718 
1719   //process the deletion array
1720   //save the position we currently have as it is the offset to jump to
1721   //when reading deletion
1722   p->ElementDeletionOffset = p->StateSize/p->Fam.GetWordSize();
1723 
1724   int mdlopt;
1725   int intpts2;
1726   mdlopt = p->Dict["MAXINT"];
1727   if ( mdlopt >= 0 && mdlopt <= 10000)
1728   {
1729     intpts2 = mdlopt;
1730     mdlopt = LS_MDLOPT_NONE;
1731   }
1732   else if ( mdlopt < -10000 )
1733   {
1734     intpts2 = -mdlopt -10000;
1735     mdlopt = LS_MDLOPT_CELL;
1736 
1737     // WARNING: This needs NumberOfCells[LSDynaMetaData::RIGID_BODY] set, which relies on NUMRBE
1738     p->StateSize += this->GetNumberOfContinuumCells() * p->Fam.GetWordSize();
1739   }
1740   else if ( mdlopt > 10000)
1741   {
1742     intpts2 = mdlopt -10000;
1743     mdlopt = LS_MDLOPT_CELL;
1744 
1745     // WARNING: This needs NumberOfCells[LSDynaMetaData::RIGID_BODY] set, which relies on NUMRBE
1746     p->StateSize += this->GetNumberOfContinuumCells() * p->Fam.GetWordSize();
1747   }
1748   else
1749   {
1750     intpts2 = -mdlopt;
1751     mdlopt = LS_MDLOPT_POINT;
1752     //p->AddPointArray( LS_ARRAYNAME_DEATH, 1, 1 );
1753     p->StateSize += this->GetNumberOfNodes() * p->Fam.GetWordSize();
1754   }
1755   p->Dict["MDLOPT"] = mdlopt;
1756   p->Dict["_MAXINT_"] = intpts2;
1757   if ( p->Dict["NV2D"] > 0 )
1758   {
1759     if ( p->Dict["NV2D"]-(p->Dict["_MAXINT_"]*(6*p->Dict["IOSHL(1)"]+p->Dict["IOSHL(2)"]+p->Dict["NEIPS"])+8*p->Dict["IOSHL(3)"]+4*p->Dict["IOSHL(4)"]) > 1 )
1760     {
1761       p->Dict["ISTRN"] = 1;
1762     }
1763     else
1764     {
1765       p->Dict["ISTRN"] = 0;
1766     }
1767   }
1768   else if ( p->Dict["NELT"] > 0 )
1769   {
1770     if ( p->Dict["NV3D"] - p->Dict["_MAXINT_"]*(6*p->Dict["IOSHL(1)"]+p->Dict["IOSHL(2)"]+p->Dict["NEIPS"]) > 1 )
1771     {
1772       p->Dict["ISTRN"] = 1;
1773     }
1774     else
1775     {
1776       p->Dict["ISTRN"] = 0;
1777     }
1778   }
1779   else
1780   {
1781     p->Dict["ISTRN"] = 0;
1782   }
1783 
1784 // OK, we are done processing the header (control) section.
1785 
1786 
1787   p->SPHStateOffset = p->StateSize/p->Fam.GetWordSize();
1788   // ============================================ Fluid Material ID Data Section
1789   // IALEMAT offset
1790   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::FluidMaterialIdData );
1791   p->PreStateSize += p->Dict["IALEMAT"];
1792   p->Fam.BufferChunk( LSDynaFamily::Int, p->Dict["IALEMAT"] );
1793   for ( itmp = 0; itmp < p->Dict["IALEMAT"]; ++itmp )
1794   {
1795     p->FluidMaterials.insert( p->Fam.GetNextWordAsInt() );
1796   }
1797   //p->Fam.SkipToWord( LSDynaFamily::FluidMaterialIdData, curAdapt, p->Dict["IALEMAT"] );
1798 
1799   // =======================  Smooth Particle Hydrodynamics Element Data Section
1800   // Only when NMSPH > 0
1801   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::SPHElementData );
1802   if ( p->NumberOfCells[LSDynaMetaData::PARTICLE] > 0 )
1803   {
1804     p->Fam.BufferChunk( LSDynaFamily::Int, 1 );
1805     vtkIdType sphAttributes = p->Fam.GetNextWordAsInt();
1806     p->Dict["isphfg(1)"] = sphAttributes;
1807     if ( sphAttributes >= 9 )
1808     {
1809       p->Fam.BufferChunk( LSDynaFamily::Int, sphAttributes - 1 ); // should be 9 or 10
1810       // Dyna docs call statePerParticle "NUM_SPH_DATA":
1811       int statePerParticle = 1; // start at 1 because we always have material ID of particle.
1812       for ( itmp = 2; itmp <= sphAttributes; ++itmp )
1813       {
1814         int numComponents = p->Fam.GetNextWordAsInt();
1815         snprintf( ctmp, sizeof(ctmp), "isphfg(%d)", itmp );
1816         p->Dict[ ctmp ] = numComponents;
1817         statePerParticle += numComponents;
1818       }
1819       p->Dict["NUM_SPH_DATA"] = statePerParticle;
1820       p->StateSize += statePerParticle * p->NumberOfCells[LSDynaMetaData::PARTICLE] * p->Fam.GetWordSize();
1821     }
1822     else
1823     {
1824       p->FileIsValid = 0;
1825       return 0;
1826     }
1827     p->Fam.SkipToWord( LSDynaFamily::SPHElementData, curAdapt, p->Dict["isphfg(1)"] );
1828     p->PreStateSize += p->Dict["isphfg(1)"]*p->Fam.GetWordSize();
1829   }
1830 
1831   // ===================================================== Geometry Data Section
1832   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::GeometryData );
1833   iddtmp = this->GetNumberOfNodes()*p->Dimensionality*p->Fam.GetWordSize(); // Size of nodes on disk
1834   iddtmp += p->NumberOfCells[LSDynaMetaData::SOLID]*9*p->Fam.GetWordSize(); // Size of hexes on disk
1835   iddtmp += p->NumberOfCells[LSDynaMetaData::THICK_SHELL]*9*p->Fam.GetWordSize(); // Size of thick shells on disk
1836   iddtmp += p->NumberOfCells[LSDynaMetaData::SHELL]*5*p->Fam.GetWordSize(); // Size of quads on disk
1837   iddtmp += p->NumberOfCells[LSDynaMetaData::BEAM]*6*p->Fam.GetWordSize(); // Size of beams on disk
1838   p->PreStateSize += iddtmp;
1839   p->Fam.SkipToWord( LSDynaFamily::GeometryData, curAdapt, iddtmp/p->Fam.GetWordSize() ); // Skip to end of geometry
1840 
1841   // =========== User Material, Node, And Element Identification Numbers Section
1842   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::UserIdData );
1843   if ( p->Dict["NARBS"] != 0 )
1844   {
1845     // For sequential material numbering
1846     // NARBS = 10+NUMNP+NEL8+NEL2+NEL4+NELT+3*NMMAT   (even though the 3*NMMAT numbers are unused)
1847     // For arbitrary material numbering (NSORT<0)
1848     // NARBS = 16+NUMNP+NEL8+NEL2+NEL4+NELT+3*NMMAT   (Note 6 extra params)
1849 
1850     p->Fam.BufferChunk( LSDynaFamily::Int, 10 );
1851     p->Dict["NSORT"] = p->Fam.GetNextWordAsInt();
1852     p->Dict["NSRH"] = p->Fam.GetNextWordAsInt();
1853     p->Dict["NSRB"] = p->Fam.GetNextWordAsInt();
1854     p->Dict["NSRS"] = p->Fam.GetNextWordAsInt();
1855     p->Dict["NSRT"] = p->Fam.GetNextWordAsInt();
1856     p->Dict["NSORTD"] = p->Fam.GetNextWordAsInt();
1857     p->Dict["NSRHD"] = p->Fam.GetNextWordAsInt();
1858     p->Dict["NSRBD"] = p->Fam.GetNextWordAsInt();
1859     p->Dict["NSRSD"] = p->Fam.GetNextWordAsInt();
1860     p->Dict["NSRTD"] = p->Fam.GetNextWordAsInt();
1861 
1862     if ( p->Dict["NSORT"] < 0 )
1863     {
1864       p->Fam.BufferChunk( LSDynaFamily::Int, 6 );
1865       p->Dict["NSRMA"] = p->Fam.GetNextWordAsInt();
1866       p->Dict["NSRMU"] = p->Fam.GetNextWordAsInt();
1867       p->Dict["NSRMP"] = p->Fam.GetNextWordAsInt();
1868       p->Dict["NSRTM"] = p->Fam.GetNextWordAsInt();
1869       p->Dict["NUMRBS"] = p->Fam.GetNextWordAsInt();
1870       p->Dict["NMMAT"] = p->Fam.GetNextWordAsInt();
1871     }
1872 
1873     iddtmp = p->Dict["NARBS"] * p->Fam.GetWordSize();
1874     p->PreStateSize += iddtmp;
1875     p->Fam.SkipToWord( LSDynaFamily::UserIdData, curAdapt, p->Dict["NARBS"] );
1876   }
1877   else
1878   {
1879     p->Dict["NSORT"] = 0;
1880   }
1881   // Break from convention and read in actual array values (as opposed to just summary information)
1882   // about the material IDs. This is required because the reader must present part names after
1883   // RequestInformation is called and that cannot be done without a list of material IDs.
1884   this->ReadUserMaterialIds();
1885 
1886   // ======================================= Adapted Element Parent List Section
1887   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::AdaptedParentData );
1888   p->Fam.SkipToWord( LSDynaFamily::AdaptedParentData, curAdapt, 2*p->Dict["NADAPT"] );
1889   iddtmp = 2*p->Dict["NADAPT"]*p->Fam.GetWordSize();
1890   p->PreStateSize += iddtmp;
1891 
1892   // ============== Smooth Particle Hydrodynamics Node And Material List Section
1893   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::SPHNodeData );
1894   iddtmp = 2*p->NumberOfCells[LSDynaMetaData::PARTICLE]*p->Fam.GetWordSize();
1895   p->PreStateSize += iddtmp;
1896   p->Fam.SkipToWord( LSDynaFamily::SPHNodeData, curAdapt, 2*p->NumberOfCells[LSDynaMetaData::PARTICLE] );
1897 
1898   // =========================================== Rigid Road Surface Data Section
1899   p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::RigidSurfaceData );
1900   if ( p->Dict["NDIM"] > 5 )
1901   {
1902     p->Fam.BufferChunk( LSDynaFamily::Int, 4 );
1903     p->PreStateSize += 4*p->Fam.GetWordSize();
1904     p->Dict["NNODE"]  = p->Fam.GetNextWordAsInt();
1905     p->Dict["NSEG"]   = p->Fam.GetNextWordAsInt();
1906     p->Dict["NSURF"]  = p->Fam.GetNextWordAsInt();
1907     p->Dict["MOTION"] = p->Fam.GetNextWordAsInt();
1908     iddtmp = 4*p->Dict["NNODE"]*p->Fam.GetWordSize();
1909     p->PreStateSize += iddtmp;
1910     p->Fam.SkipWords( 4*p->Dict["NNODE"] );
1911     p->NumberOfCells[LSDynaMetaData::ROAD_SURFACE] = p->Dict["NSEG"];
1912 
1913     for ( itmp = 0; itmp < p->Dict["NSURF"]; ++itmp )
1914     {
1915       p->Fam.BufferChunk( LSDynaFamily::Int, 2 );
1916       p->Fam.GetNextWordAsInt(); // Skip SURFID
1917       iddtmp = p->Fam.GetNextWordAsInt(); // Read SURFNSEG[SURFID]
1918       p->RigidSurfaceSegmentSizes.push_back( iddtmp );
1919       p->PreStateSize += (2 + 4*iddtmp)*p->Fam.GetWordSize();
1920       p->Fam.SkipWords( 4*iddtmp );
1921     }
1922 
1923     if ( p->Dict["NSEG"] > 0 )
1924     {
1925       p->AddCellArray( LSDynaMetaData::ROAD_SURFACE, LS_ARRAYNAME_SEGMENTID, 1, 1 );
1926       //FIXME: p->AddRoadPointArray( LSDynaMetaData::ROAD_SURFACE, LS_ARRAYNAME_USERID, 1, 1 );
1927     }
1928 
1929     if ( p->Dict["MOTION"] )
1930     {
1931       p->StateSize += 6*p->Dict["NSURF"]*p->Fam.GetWordSize();
1932     }
1933   }
1934 
1935   //if ( curAdapt == 0 )
1936   {
1937     p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::EndOfStaticSection );
1938     p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::TimeStepSection );
1939   }
1940   p->Fam.SetStateSize( p->StateSize / p->Fam.GetWordSize() );
1941 
1942 
1943   // ==========================================================================
1944   // Now that we've read the header, create a list of the cell arrays for
1945   // each output mesh.
1946 
1947   // Currently, the LS-Dyna reader only gives users a few knobs to control which cell variables are loaded.
1948   // It is a difficult problem since many attributes only occur on some element types, there are many
1949   // dyna flags that conditionally omit results, and some quantities are repeated over differing numbers
1950   // of points for different types of cells.
1951   // Given the complexity, we punt by defining some knobs for "types" of data and define related fields.
1952   // In a perfect world, finer-grained control would be available.
1953   //
1954   // As an example: if there are any
1955   // - 3-D cells, OR
1956   // - non-rigid 2-D cells with IOSHL(1)==1, OR
1957   // - beam cells with NV1D > 6, OR
1958   // - SPH cells with isphfg(4)==6
1959   // then there will be stresses present
1960 
1961   // Every cell always has a material type
1962   // FIXME: Is this true? Rigid bodies may be an exception, in which
1963   // case we need to check that the number of cells in the other 5 meshes sum to >0
1964 
1965   // Total number of ALE fluid groups. Fluid density and
1966   // volume fractions output as history variables, and a flag
1967   // for the dominant group. If negative multi-material
1968   // species mass for each group is also output. Order is: rho,
1969   // vf1, … vfn, dvf flag, m1, … mn. Density is at position 8
1970   // after the location for plastic strain. Any element material
1971   // history variables are written before the Ale variables, and
1972   // the six element strains components after these if
1973   // ISTRN=1.
1974   int numGroups = std::abs(static_cast<int>(p->Dict["NUMFLUID"]));
1975   bool hasMass = (p->Dict["NUMFLUID"] < 0);
1976   int numALEvalues = (numGroups > 0) ? 1 + numGroups + 1 + (hasMass? numGroups : 0) : 0;
1977 
1978   if ( p->Dict["NARBS"] )
1979   {
1980     p->AddPointArray( LS_ARRAYNAME_USERID, 1, 1 );
1981   }
1982 
1983   if ( p->NumberOfCells[ LSDynaMetaData::PARTICLE ] )
1984   {
1985     // One value is always output which is the material number as a floating point number for each particle.
1986     // If this value is negative then the particle has been deleted from the model.
1987     p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_MATERIAL, 1, 1 );
1988     //p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_DEATH, 1, 1 );  // There is no mention of this in manual... Was this the -ve Material case?
1989     if ( p->Dict["isphfg(2)"] == 1 )
1990     {
1991       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_RADIUSOFINFLUENCE, 1, 1 );
1992     }
1993     if ( p->Dict["isphfg(3)"] == 1 )
1994     {
1995       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_PRESSURE, 1, 1 );
1996     }
1997     if ( p->Dict["isphfg(4)"] == 6 )
1998     {
1999       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_STRESS, 6, 1 );
2000     }
2001     if ( p->Dict["isphfg(5)"] == 1 )
2002     {
2003       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_EPSTRAIN, 1, 1 );
2004     }
2005     if ( p->Dict["isphfg(6)"] == 1 )
2006     {
2007       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_DENSITY, 1, 1 );
2008     }
2009     if ( p->Dict["isphfg(7)"] == 1 )
2010     {
2011       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_INTERNALENERGY, 1, 1 );
2012     }
2013     if ( p->Dict["isphfg(8)"] == 1 )
2014     {
2015       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_NUMNEIGHBORS, 1, 1 );
2016     }
2017     if ( p->Dict["isphfg(9)"] == 6 )
2018     {
2019       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_STRAIN, 6, 1 );
2020     }
2021     if ( p->Dict["isphfg(10)"] == 1 )
2022     {
2023       p->AddCellArray( LSDynaMetaData::PARTICLE, LS_ARRAYNAME_MASS, 1, 1 );
2024     }
2025   }
2026 
2027   if ( p->NumberOfCells[ LSDynaMetaData::BEAM ] )
2028   {
2029 //    p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_MATERIAL, 1, 1 );
2030 //    if ( p->Dict["MDLOPT"] == LS_MDLOPT_CELL )
2031 //      {
2032 //      p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_DEATH, 1, 1 );
2033 //      }
2034 
2035     if ( p->Dict["NARBS"] != 0 )
2036     {
2037       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_USERID, 1, 1 );
2038     }
2039     if ( p->Dict["NV1D"] >= 6 )
2040     {
2041       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_AXIALFORCE, 1, 1 );
2042       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_SHEARRESULTANT, 2, 1 );
2043       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_BENDINGRESULTANT, 2, 1 );
2044       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_TORSIONRESULTANT, 1, 1 );
2045     }
2046     if ( p->Dict["NV1D"] > 6 )
2047     {
2048       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_SHEARSTRESS, 2, 1 );
2049       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_AXIALSTRESS, 1, 1 );
2050       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_PLASTICSTRAIN, 1, 1 );
2051       p->AddCellArray( LSDynaMetaData::BEAM, LS_ARRAYNAME_AXIALSTRAIN, 1, 1 );
2052     }
2053   }
2054 
2055   if ( p->NumberOfCells[ LSDynaMetaData::SHELL ] )
2056   {
2057 //    p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_MATERIAL, 1, 1 );
2058 //    if ( p->Dict["MDLOPT"] == LS_MDLOPT_CELL )
2059 //      {
2060 //      p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_DEATH, 1, 1 );
2061 //      }
2062     if ( p->Dict["NARBS"] != 0 )
2063     {
2064       p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_USERID, 1, 1 );
2065     }
2066     if ( p->Dict["IOSHL(1)"] )
2067       p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_STRESS, 6, 1 );
2068     if ( p->Dict["IOSHL(2)"] )
2069       p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_EPSTRAIN, 1, 1 );
2070 
2071     int neips = p->Dict["NEIPS"];
2072     int extraValues = neips;
2073     if (extraValues>0)
2074     {
2075       // Any element material history variables are written before the Ale variables, and the six element strains components after these if ISTRN=1
2076       int materialValues = extraValues - numALEvalues;
2077       if (materialValues > 0)
2078       {
2079         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_INTEGRATIONPOINT, materialValues, 1 );
2080         extraValues -= materialValues;
2081       }
2082 
2083       if ( (numALEvalues > 0) && (extraValues>=numALEvalues) )
2084       {
2085         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_DENSITY, 1, 1 );
2086         extraValues--;
2087 
2088         for (int g=0; g < numGroups; ++g)
2089         {
2090           snprintf( ctmp, sizeof(ctmp), LS_ARRAYNAME_VOLUME_FRACTION_FMT, static_cast<int>(g+1) );
2091           p->AddCellArray( LSDynaMetaData::SHELL, ctmp, 1, 1 );
2092           extraValues--;
2093         }
2094 
2095         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_DOMINANT_GROUP, 1, 1 );
2096         extraValues--;
2097 
2098         for (int g=0; hasMass && (g < numGroups); ++g)
2099         {
2100           snprintf( ctmp, sizeof(ctmp), LS_ARRAYNAME_SPECIES_MASS_FMT, static_cast<int>(g+1) );
2101           p->AddCellArray( LSDynaMetaData::SHELL, ctmp, 1, 1 );
2102           extraValues--;
2103         }
2104       }
2105     }
2106 
2107     if ( p->Dict["_MAXINT_"] >= 2 )
2108     {
2109       if ( p->Dict["IOSHL(1)"] )
2110         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_STRESS "InnerSurf", 6, 1 );
2111       if ( p->Dict["IOSHL(2)"] )
2112         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_EPSTRAIN "InnerSurf", 1, 1 );
2113       if ( neips )
2114         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_INTEGRATIONPOINT "InnerSurf", neips, 1 );
2115     }
2116     if ( p->Dict["_MAXINT_"] >= 3 )
2117     {
2118       if ( p->Dict["IOSHL(1)"] )
2119         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_STRESS "OuterSurf", 6, 1 );
2120       if ( p->Dict["IOSHL(2)"] )
2121         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_EPSTRAIN "OuterSurf", 1, 1 );
2122       if ( neips )
2123         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_INTEGRATIONPOINT "OuterSurf", neips, 1 );
2124     }
2125 
2126     for ( itmp = 3; itmp < p->Dict["_MAXINT_"]; ++itmp )
2127     {
2128       if ( p->Dict["IOSHL(1)"] )
2129       {
2130         snprintf( ctmp, sizeof(ctmp), "%sIntPt%d", LS_ARRAYNAME_STRESS, itmp + 1 );
2131         p->AddCellArray( LSDynaMetaData::SHELL, ctmp, 6, 1 );
2132       }
2133       if ( p->Dict["IOSHL(2)"] )
2134       {
2135         snprintf( ctmp, sizeof(ctmp), "%sIntPt%d", LS_ARRAYNAME_EPSTRAIN, itmp + 1 );
2136         p->AddCellArray( LSDynaMetaData::SHELL, ctmp, 1, 1 );
2137       }
2138       if ( neips )
2139       {
2140         snprintf( ctmp, sizeof(ctmp), "%sIntPt%d", LS_ARRAYNAME_INTEGRATIONPOINT, itmp + 1 );
2141         p->AddCellArray( LSDynaMetaData::SHELL, ctmp, neips, 1 );
2142       }
2143     }
2144     if ( p->Dict["IOSHL(3)"] )
2145     {
2146       p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_NORMALRESULTANT, 3, 1 );
2147       p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_SHEARRESULTANT, 2, 1 );
2148       p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_BENDINGRESULTANT, 3, 1 );
2149     }
2150     if ( p->Dict["IOSHL(4)"] )
2151     {
2152       p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_THICKNESS, 1, 1 );
2153       p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_ELEMENTMISC, 2, 1 );
2154     }
2155 
2156     if ( p->Dict["ISTRN"] )
2157     {
2158         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_STRAIN "InnerSurf", 6, 1 );
2159         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_STRAIN "OuterSurf", 6, 1 );
2160     }
2161     if ( ! p->Dict["ISTRN"] || (p->Dict["NV2D"] >= 45) )
2162     {
2163         p->AddCellArray( LSDynaMetaData::SHELL, LS_ARRAYNAME_INTERNALENERGY, 1, 1 );
2164     }
2165   }
2166 
2167   if ( p->NumberOfCells[ LSDynaMetaData::THICK_SHELL ] )
2168   {
2169 //    p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_MATERIAL, 1, 1 );
2170 //    if ( p->Dict["MDLOPT"] == LS_MDLOPT_CELL )
2171 //      {
2172 //      p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_DEATH, 1, 1 );
2173 //      }
2174     if ( p->Dict["NARBS"] != 0 )
2175     {
2176       p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_USERID, 1, 1 );
2177     }
2178     if ( p->Dict["IOSHL(1)"] )
2179     {
2180       if ( p->Dict["_MAXINT_"] >= 3 )
2181       {
2182         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_STRESS, 6, 1 );
2183         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_STRESS "InnerSurf", 6, 1 );
2184         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_STRESS "OuterSurf", 6, 1 );
2185       }
2186       for ( itmp = 3; itmp < p->Dict["_MAXINT_"]; ++itmp )
2187       {
2188         snprintf( ctmp, sizeof(ctmp), "%sIntPt%d", LS_ARRAYNAME_STRESS, itmp + 1 );
2189         p->AddCellArray( LSDynaMetaData::THICK_SHELL, ctmp, 6, 1 );
2190       }
2191     }
2192     if ( p->Dict["IOSHL(2)"] )
2193     {
2194       if ( p->Dict["_MAXINT_"] >= 3 )
2195       {
2196         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_EPSTRAIN, 1, 1 );
2197         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_EPSTRAIN "InnerSurf", 1, 1 );
2198         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_EPSTRAIN "OuterSurf", 1, 1 );
2199       }
2200       for ( itmp = 3; itmp < p->Dict["_MAXINT_"]; ++itmp )
2201       {
2202         snprintf( ctmp, sizeof(ctmp), "%sIntPt%d", LS_ARRAYNAME_EPSTRAIN, itmp + 1 );
2203         p->AddCellArray( LSDynaMetaData::THICK_SHELL, ctmp, 1, 1 );
2204       }
2205     }
2206     if ( p->Dict["NEIPS"] )
2207     {
2208       int neips = p->Dict["NEIPS"];
2209       if ( p->Dict["_MAXINT_"] >= 3 )
2210       {
2211         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_INTEGRATIONPOINT, neips, 1 );
2212         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_INTEGRATIONPOINT, neips, 1 );
2213         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_INTEGRATIONPOINT "InnerSurf", neips, 1 );
2214         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_INTEGRATIONPOINT "OuterSurf", neips, 1 );
2215       }
2216       for ( itmp = 3; itmp < p->Dict["_MAXINT_"]; ++itmp )
2217       {
2218         snprintf( ctmp, sizeof(ctmp), "%sIntPt%d", LS_ARRAYNAME_INTEGRATIONPOINT, itmp + 1 );
2219         p->AddCellArray( LSDynaMetaData::THICK_SHELL, ctmp, 6, 1 );
2220       }
2221     }
2222     if ( p->Dict["ISTRN"] )
2223     {
2224         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_STRAIN "InnerSurf", 6, 1 );
2225         p->AddCellArray( LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_STRAIN "OuterSurf", 6, 1 );
2226     }
2227   }
2228 
2229   if ( p->NumberOfCells[ LSDynaMetaData::SOLID ] )
2230   {
2231 //    p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_MATERIAL, 1, 1 );
2232 //    if ( p->Dict["MDLOPT"] == LS_MDLOPT_CELL )
2233 //      {
2234 //      p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_DEATH, 1, 1 );
2235 //      }
2236     if ( p->Dict["NARBS"] != 0 )
2237     {
2238       p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_USERID, 1, 1 );
2239     }
2240     p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_STRESS, 6, 1 );
2241     p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_EPSTRAIN, 1, 1 );
2242 
2243     int extraValues = p->Dict["NEIPH"];
2244     if (extraValues>0)
2245     {
2246       int strainValues = ( p->Dict["ISTRN"]==1 ) ? 6 : 0; // last six values are strain.
2247 
2248       // Any element material history variables are written before the Ale variables, and the six element strains components after these if ISTRN=1
2249       int materialValues = extraValues - (numALEvalues+strainValues);
2250       if (materialValues > 0)
2251       {
2252         p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_INTEGRATIONPOINT, materialValues, 1 );
2253         extraValues -= materialValues;
2254       }
2255 
2256       if ( (numALEvalues > 0) && (extraValues>=numALEvalues) )
2257       {
2258         p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_DENSITY, 1, 1 );
2259         extraValues--;
2260 
2261         for (int g=0; g < numGroups; ++g)
2262         {
2263           snprintf( ctmp, sizeof(ctmp), LS_ARRAYNAME_VOLUME_FRACTION_FMT, static_cast<int>(g+1) );
2264           p->AddCellArray( LSDynaMetaData::SOLID, ctmp, 1, 1 );
2265           extraValues--;
2266         }
2267 
2268         p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_DOMINANT_GROUP, 1, 1 );
2269         extraValues--;
2270 
2271         for (int g=0; hasMass && (g < numGroups); ++g)
2272         {
2273           snprintf( ctmp, sizeof(ctmp), LS_ARRAYNAME_SPECIES_MASS_FMT, static_cast<int>(g+1) );
2274           p->AddCellArray( LSDynaMetaData::SOLID, ctmp, 1, 1 );
2275           extraValues--;
2276         }
2277       }
2278       if ( (strainValues > 0) && (extraValues>=strainValues))
2279       {
2280         p->AddCellArray( LSDynaMetaData::SOLID, LS_ARRAYNAME_STRAIN, strainValues, 1 );
2281       }
2282     }
2283   }
2284 
2285   // Only try reading the keyword file if we don't have part names.
2286   if ( curAdapt == 0 && p->PartNames.empty() )
2287   {
2288     this->ResetPartInfo();
2289 
2290     int result = this->ReadInputDeck();
2291 
2292     if (result == 0)
2293     {
2294       //we failed to read the input deck so we are going to read the first binary file for part names
2295       this->ReadPartTitlesFromRootFile();
2296     }
2297   }
2298 
2299   return -1;
2300 }
2301 
ScanDatabaseTimeSteps()2302 int vtkLSDynaReader::ScanDatabaseTimeSteps()
2303 {
2304   LSDynaMetaData* p = this->P;
2305 
2306   // ======================================================= State Data Sections
2307   // The 2 lines below are now in ReadHeaderInformation:
2308   // p->Fam.MarkSectionStart( curAdapt, LSDynaFamily::TimeStepSection );
2309   // p->Fam.SetStateSize( p->StateSize / p->Fam.GetWordSize() );
2310   // It may be useful to call
2311   // p->JumpToMark( LSDynaFamily::TimeStepSection );
2312   // here.
2313   if ( p->Fam.GetStateSize() <= 0 )
2314   {
2315     vtkErrorMacro( "Database has bad state size (" << p->Fam.GetStateSize() << ")." );
2316     return 1;
2317   }
2318 
2319   // Discover the number of states and record the time value for each.
2320   int ntimesteps = 0;
2321   double time;
2322   int itmp = 1;
2323   int lastAdapt = 0;
2324   do {
2325     if ( p->Fam.BufferChunk( LSDynaFamily::Float, 1 ) == 0 )
2326     {
2327       time = p->Fam.GetNextWordAsFloat();
2328       if ( time != LSDynaFamily::EOFMarker )
2329       {
2330         p->Fam.MarkTimeStep();
2331         p->TimeValues.push_back( time );
2332         //fprintf( stderr, "%d %f\n", (int) p->TimeValues.size() - 1, time ); fflush(stderr);
2333         if ( p->Fam.SkipToWord( LSDynaFamily::TimeStepSection, ntimesteps++, p->Fam.GetStateSize() ) )
2334         {
2335           itmp = 0;
2336         }
2337       }
2338       else
2339       {
2340         if ( p->Fam.AdvanceFile() )
2341         {
2342           itmp = 0;
2343         }
2344         else
2345         {
2346           if ( ntimesteps == 0 )
2347           {
2348             // First time step was an EOF marker... move the marker to the
2349             // beginning of the first real time step...
2350             p->Fam.MarkSectionStart( lastAdapt, LSDynaFamily::TimeStepSection );
2351           }
2352         }
2353         int nextAdapt = p->Fam.GetCurrentAdaptLevel();
2354         if ( nextAdapt != lastAdapt )
2355         {
2356           // Read the next static header section... state size has changed.
2357           p->Fam.MarkSectionStart( nextAdapt, LSDynaFamily::ControlSection );
2358           this->ReadHeaderInformation( nextAdapt );
2359           //p->Fam.SkipToWord( LSDynaFamily::EndOfStaticSection, nextAdapt, 0 );
2360           lastAdapt = nextAdapt;
2361         }
2362       }
2363     }
2364     else
2365     {
2366       itmp = 0;
2367     }
2368   } while (itmp);
2369 
2370   this->TimeStepRange[0] = 0;
2371   this->TimeStepRange[1] = ntimesteps ? ntimesteps - 1 : 0;
2372 
2373   return -1;
2374 }
2375 
2376 // =================================== Provide information about the database to the pipeline
RequestInformation(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (iinfo),vtkInformationVector * oinfo)2377 int vtkLSDynaReader::RequestInformation( vtkInformation* vtkNotUsed(request),
2378                                          vtkInformationVector** vtkNotUsed(iinfo),
2379                                          vtkInformationVector* oinfo )
2380 {
2381   LSDynaMetaData* p = this->P;
2382   // If the time step is set before RequestInformation is called, we must
2383   // read the header information immediately in order to determine whether
2384   // the timestep that's been passed is valid. If it's not, we ignore it.
2385   if ( ! p->FileIsValid )
2386   {
2387     if ( p->Fam.GetDatabaseDirectory().empty() )
2388     {
2389       // fail silently for CanReadFile()'s sake.
2390       //vtkErrorMacro( "You haven't set the LS-Dyna database directory!" );
2391       return 1;
2392     }
2393 
2394     if ( p->Fam.GetDatabaseBaseName().empty() )
2395     {
2396       p->Fam.SetDatabaseBaseName( "/d3plot" ); // not a bad assumption.
2397     }
2398     p->Fam.ScanDatabaseDirectory();
2399     if ( p->Fam.GetNumberOfFiles() < 1 )
2400     {
2401       p->FileIsValid = 0;
2402       return 1;
2403     }
2404     p->Fam.DetermineStorageModel();
2405     p->MaxFileLength = p->FileSizeFactor*512*512*p->Fam.GetWordSize();
2406     p->FileIsValid = 1;
2407 
2408     // OK, now we have a list of files. Next, determine the length of the
2409     // state vector (#bytes of data stored per time step):
2410     this->ReadHeaderInformation( 0 );
2411 
2412     // Finally, we can loop through and determine where all the state
2413     // vectors start for each time step.
2414     // This will call ReadHeaderInformation when it encounters any
2415     // mesh adaptations (so that it can get the new state vector size).
2416     this->ScanDatabaseTimeSteps();
2417   }
2418 
2419   if ( p->TimeValues.empty() )
2420   {
2421     vtkErrorMacro( "No valid time steps in the LS-Dyna database" );
2422     return 0;
2423   }
2424 
2425   // Clamp timestep to be valid here.
2426   if ( p->CurrentState < 0 )
2427   {
2428     p->CurrentState = 0;
2429   }
2430   else if ( p->CurrentState >= static_cast<vtkIdType>(p->TimeValues.size()) )
2431   {
2432     p->CurrentState = static_cast<vtkIdType>(p->TimeValues.size() - 1);
2433   }
2434 
2435   int newAdaptLevel = p->Fam.TimeAdaptLevel( p->CurrentState );
2436   if ( p->Fam.GetCurrentAdaptLevel() !=  newAdaptLevel )
2437   {
2438     // Requested time step has a different mesh adaptation than current one.
2439     // Update the header information so that queries like GetNumberOfCells() work properly.
2440     int result;
2441     result = this->ReadHeaderInformation( newAdaptLevel );
2442     if ( result >= 0 )
2443     {
2444       this->ResetPartsCache();
2445       return result;
2446     }
2447   }
2448 
2449   // Every output object has all the time steps.
2450   vtkInformation* outInfo = oinfo->GetInformationObject(0);
2451   outInfo->Set( vtkStreamingDemandDrivenPipeline::TIME_STEPS(), &p->TimeValues[0], (int)p->TimeValues.size() );
2452   double timeRange[2];
2453   timeRange[0] = p->TimeValues[0];
2454   timeRange[1] = p->TimeValues[p->TimeValues.size() - 1];
2455   outInfo->Set( vtkStreamingDemandDrivenPipeline::TIME_RANGE(), timeRange, 2 );
2456 
2457   return 1;
2458 }
2459 
2460 //----------------------------------------------------------------------------
ReadTopology()2461 int vtkLSDynaReader::ReadTopology()
2462 {
2463   bool readTopology=false;
2464   if(!this->Parts)
2465   {
2466     readTopology=true;
2467     this->Parts = vtkLSDynaPartCollection::New();
2468     this->Parts->InitCollection(this->P,nullptr,nullptr);
2469   }
2470   if(!readTopology)
2471   {
2472     return 0;
2473   }
2474 
2475   if( this->ReadPartSizes())
2476   {
2477     vtkErrorMacro( "Could not read cell sizes." );
2478     return 1;
2479   }
2480 
2481   if ( this->ReadConnectivityAndMaterial() )
2482   {
2483     vtkErrorMacro( "Could not read connectivity." );
2484     return 1;
2485   }
2486 
2487   this->Parts->FinalizeTopology();
2488 
2489   if(this->ReadNodes())
2490   {
2491     vtkErrorMacro("Could not read static node values.");
2492     return 1;
2493   }
2494 
2495 
2496   // we need to read the user ids after we have read the topology
2497   // so we know how many cells are in each part
2498   if ( this->ReadUserIds() )
2499   {
2500     vtkErrorMacro( "Could not read user node/element IDs." );
2501     return 1;
2502   }
2503 
2504   return 0;
2505 }
2506 
2507 // ============================================================= Section parsing
ReadNodes()2508 int vtkLSDynaReader::ReadNodes()
2509 {
2510   LSDynaMetaData* p = this->P;
2511 
2512   // Always read geometry, even if the mesh will be deformed using deflected coordinates
2513   // (LS_ARRAYNAME_DEFLECTION). That way, we can compute deflection array later on.
2514   p->Fam.SkipToWord(LSDynaFamily::GeometryData, p->Fam.GetCurrentAdaptLevel(), 0);
2515   this->Parts->ReadPointProperty(p->NumberOfNodes, p->Dimensionality, nullptr, false, true, false);
2516 
2517   // Note that in any event we still have to read the rigid road coordinates.
2518   // If the mesh is deformed each state will have the points so see ReadState
2519   if ( p->ReadRigidRoadMvmt )
2520   {
2521     vtkIdType nnode = p->Dict["NNODE"];
2522     p->Fam.SkipToWord( LSDynaFamily::RigidSurfaceData, p->Fam.GetCurrentAdaptLevel(), 4 + nnode );
2523     this->Parts->ReadPointProperty(nnode,3,nullptr,false,false,true);
2524   }
2525 
2526   return 0;
2527 }
2528 
2529 //-----------------------------------------------------------------------------
ReadUserIds()2530 int vtkLSDynaReader::ReadUserIds()
2531 {
2532   // Below here is code that runs when user node or element numbers are present
2533   int arbitraryMaterials = this->P->Dict["NSORT"] < 0 ? 1 : 0;
2534   vtkIdType isz = this->GetNumberOfNodes();
2535   if ( arbitraryMaterials )
2536   {
2537     this->P->Fam.SkipToWord( LSDynaFamily::UserIdData,
2538                              this->P->Fam.GetCurrentAdaptLevel(), 16 );
2539   }
2540   else
2541   {
2542     this->P->Fam.SkipToWord( LSDynaFamily::UserIdData,
2543                              this->P->Fam.GetCurrentAdaptLevel(), 10 );
2544   }
2545 
2546   // Node numbers
2547   bool nodeIdStatus = this->GetPointArrayStatus( LS_ARRAYNAME_USERID ) == 1;
2548   if(nodeIdStatus)
2549   {
2550     this->Parts->ReadPointUserIds(isz,LS_ARRAYNAME_USERID);
2551   }
2552 
2553   // FIXME: This won't work if Rigid Body and Shell elements are interleaved (which I now believe they are)
2554   this->Parts->ReadCellUserIds(LSDynaMetaData::BEAM,
2555               this->GetCellArrayStatus(LSDynaMetaData::BEAM, LS_ARRAYNAME_USERID));
2556   this->Parts->ReadCellUserIds(LSDynaMetaData::SHELL,
2557               this->GetCellArrayStatus(LSDynaMetaData::SHELL, LS_ARRAYNAME_USERID));
2558   this->Parts->ReadCellUserIds(LSDynaMetaData::THICK_SHELL,
2559               this->GetCellArrayStatus(LSDynaMetaData::THICK_SHELL, LS_ARRAYNAME_USERID));
2560   this->Parts->ReadCellUserIds(LSDynaMetaData::SOLID,
2561               this->GetCellArrayStatus(LSDynaMetaData::SOLID, LS_ARRAYNAME_USERID));
2562   this->Parts->ReadCellUserIds(LSDynaMetaData::RIGID_BODY,
2563               this->GetCellArrayStatus(LSDynaMetaData::RIGID_BODY, LS_ARRAYNAME_USERID));
2564   return 0;
2565 }
2566 
2567 //-----------------------------------------------------------------------------
ReadDeletion()2568 int vtkLSDynaReader::ReadDeletion()
2569 {
2570   enum LSDynaMetaData::LSDYNA_TYPES validCellTypes[4] = {
2571         LSDynaMetaData::SOLID,
2572         LSDynaMetaData::THICK_SHELL,
2573         LSDynaMetaData::SHELL,
2574         LSDynaMetaData::BEAM};
2575 
2576   if(!this->RemoveDeletedCells)
2577   {
2578     //this functions doesn't have to lead the reader to a certain
2579     //position in the files
2580     this->Parts->DisbleDeadCells();
2581     return 0;
2582   }
2583 
2584   LSDynaMetaData* p = this->P;
2585   vtkUnsignedCharArray* death;
2586   switch ( p->Dict["MDLOPT"] )
2587   {
2588   case LS_MDLOPT_POINT:
2589     vtkErrorMacro("We currently only support cell death");
2590     break;
2591   case LS_MDLOPT_CELL:
2592     for(int i=0; i < 4; ++i)
2593     {
2594       const LSDynaMetaData::LSDYNA_TYPES type = validCellTypes[i];
2595       vtkIdType numCells,numSkipStart,numSkipEnd;
2596       this->Parts->GetPartReadInfo(type,numCells,numSkipStart,numSkipEnd);
2597 
2598       death = vtkUnsignedCharArray::New();
2599       death->SetName( LS_ARRAYNAME_DEATH );
2600       death->SetNumberOfComponents( 1 );
2601       death->SetNumberOfTuples(numCells);
2602 
2603       p->Fam.SkipWords(numSkipStart);
2604       this->ReadDeletionArray(death,0,1);
2605       p->Fam.SkipWords(numSkipEnd);
2606       this->Parts->SetCellDeadFlags(type,death,this->DeletedCellsAsGhostArray);
2607       death->Delete();
2608     }
2609 
2610     //we are now at the position to read the SPH deletion info from the sph state info
2611     if(p->NumberOfCells[LSDynaMetaData::PARTICLE]>0)
2612     {
2613       const LSDynaMetaData::LSDYNA_TYPES type = LSDynaMetaData::PARTICLE;
2614       vtkIdType numCells,numSkipStart,numSkipEnd;
2615       this->Parts->GetPartReadInfo(type,numCells,numSkipStart,numSkipEnd);
2616 
2617       death = vtkUnsignedCharArray::New();
2618       death->SetName( LS_ARRAYNAME_DEATH );
2619       death->SetNumberOfComponents( 1 );
2620       death->SetNumberOfTuples(numCells);
2621 
2622       p->Fam.SkipWords(numSkipStart);
2623       //we are really reading the material id as the death flag
2624       //and each particle has twenty words of info, so we have to skip 19
2625       //since luckily material id is first
2626       this->ReadDeletionArray(death,0,20);
2627       p->Fam.SkipWords(numSkipEnd);
2628       this->Parts->SetCellDeadFlags(type,death,this->DeletedCellsAsGhostArray);
2629       death->Delete();
2630     }
2631     break;
2632   case LS_MDLOPT_NONE:
2633   default:
2634     // do nothing.
2635     break;
2636   }
2637   return 0;
2638 }
2639 
2640 //-----------------------------------------------------------------------------
ReadDeletionArray(vtkUnsignedCharArray * arr,const int & pos,const int & size)2641 void vtkLSDynaReader::ReadDeletionArray(vtkUnsignedCharArray* arr, const int& pos, const int& size)
2642 {
2643   //setup to do a block read, way faster than converting each
2644   //float/double individually
2645   LSDynaMetaData *p = this->P;
2646   vtkIdType startId = 0;
2647   vtkIdType numChunks = p->Fam.InitPartialChunkBuffering(arr->GetNumberOfTuples(),size);
2648   if(p->Fam.GetWordSize() == 8)
2649   {
2650     for(vtkIdType i=0;i<numChunks;++i)
2651     {
2652       vtkIdType chunkSize = p->Fam.GetNextChunk(LSDynaFamily::Float );
2653       vtkIdType numCellsInChunk = chunkSize/size;
2654       double *dbuf = p->Fam.GetBufferAs<double>();
2655       this->FillDeletionArray(dbuf,arr,startId,numCellsInChunk,pos,size);
2656       startId+=numCellsInChunk;
2657     }
2658   }
2659   else
2660   {
2661     for(vtkIdType i=0;i<numChunks;++i)
2662     {
2663       vtkIdType chunkSize = p->Fam.GetNextChunk(LSDynaFamily::Float );
2664       vtkIdType numCellsInChunk = chunkSize/size;
2665       float *fbuf = p->Fam.GetBufferAs<float>();
2666       this->FillDeletionArray(fbuf,arr,startId,numCellsInChunk,pos,size);
2667       startId+=numCellsInChunk;
2668     }
2669   }
2670 }
2671 
2672 //-----------------------------------------------------------------------------
ReadState(vtkIdType step)2673 int vtkLSDynaReader::ReadState( vtkIdType step )
2674 {
2675   //remember C style return so zero is pass
2676   if(this->ReadNodeStateInfo(step))
2677   {
2678     vtkErrorMacro( "Problem reading state point information." );
2679     return 1;
2680   }
2681   if(this->ReadCellStateInfo( step ))
2682   {
2683     vtkErrorMacro( "Problem reading state cell information." );
2684     return 1;
2685   }
2686   if(this->ReadDeletion())
2687   {
2688     vtkErrorMacro( "Problem reading state deletion information." );
2689     return 1;
2690   }
2691   return 0;
2692 }
2693 
2694 //-----------------------------------------------------------------------------
ReadNodeStateInfo(vtkIdType step)2695 int vtkLSDynaReader::ReadNodeStateInfo( vtkIdType step )
2696 {
2697   LSDynaMetaData* p = this->P;
2698 
2699   // Skip global variables for now
2700   p->Fam.SkipToWord( LSDynaFamily::TimeStepSection, step, 1 + p->Dict["NGLBV"] );
2701 
2702   // Read nodal data ===========================================================
2703   std::vector<std::string> names;
2704   std::vector<int> cmps;
2705   // Important: push_back in the order these are interleaved on disk
2706   // Note that temperature and deflection are swapped relative to the order they
2707   // are specified in the header section.
2708   const char * aNames[] = {
2709     LS_ARRAYNAME_DEFLECTION,
2710     LS_ARRAYNAME_TEMPERATURE,
2711     LS_ARRAYNAME_VELOCITY,
2712     LS_ARRAYNAME_ACCELERATION,
2713     LS_ARRAYNAME_PRESSURE,
2714     LS_ARRAYNAME_VORTICITY "_X",
2715     LS_ARRAYNAME_VORTICITY "_Y",
2716     LS_ARRAYNAME_VORTICITY "_Z",
2717     LS_ARRAYNAME_RESULTANTVORTICITY,
2718     LS_ARRAYNAME_ENSTROPHY,
2719     LS_ARRAYNAME_HELICITY,
2720     LS_ARRAYNAME_STREAMFUNCTION,
2721     LS_ARRAYNAME_ENTHALPY,
2722     LS_ARRAYNAME_DENSITY,
2723     LS_ARRAYNAME_TURBULENTKE,
2724     LS_ARRAYNAME_DISSIPATION,
2725     LS_ARRAYNAME_EDDYVISCOSITY,
2726     LS_ARRAYNAME_SPECIES_01,
2727     LS_ARRAYNAME_SPECIES_02,
2728     LS_ARRAYNAME_SPECIES_03,
2729     LS_ARRAYNAME_SPECIES_04,
2730     LS_ARRAYNAME_SPECIES_05,
2731     LS_ARRAYNAME_SPECIES_06,
2732     LS_ARRAYNAME_SPECIES_07,
2733     LS_ARRAYNAME_SPECIES_08,
2734     LS_ARRAYNAME_SPECIES_09,
2735     LS_ARRAYNAME_SPECIES_10
2736   };
2737 
2738   const char* aDictNames[] = {
2739     "IU",
2740     "IT",
2741     "IV",
2742     "IA",
2743     "cfdPressure",
2744     "cfdXVort",
2745     "cfdYVort",
2746     "cfdZVort",
2747     "cfdRVort",
2748     "cfdEnstrophy",
2749     "cfdHelicity",
2750     "cfdStream",
2751     "cfdEnthalpy",
2752     "cfdDensity",
2753     "cfdTurbKE",
2754     "cfdDiss",
2755     "cfdEddyVisc",
2756     "cfdSpec01",
2757     "cfdSpec02",
2758     "cfdSpec03",
2759     "cfdSpec04",
2760     "cfdSpec05",
2761     "cfdSpec06",
2762     "cfdSpec07",
2763     "cfdSpec08",
2764     "cfdSpec09",
2765     "cfdSpec10"
2766   };
2767   int aComponents[] = {
2768     -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
2769   };
2770   int vppt = 0; // values per point
2771   int allVortPresent = p->Dict["cfdXVort"] && p->Dict["cfdYVort"] && p->Dict["cfdZVort"];
2772 
2773   for ( int nvnum = 0; nvnum < (int) (sizeof(aComponents)/sizeof(aComponents[0])); ++nvnum )
2774   {
2775     if ( p->Dict[ aDictNames[nvnum] ] )
2776     {
2777       if ( allVortPresent && ! strncmp( LS_ARRAYNAME_VORTICITY, aNames[ nvnum ], sizeof(LS_ARRAYNAME_VORTICITY) ) )
2778       {
2779         // turn the vorticity components from individual scalars into one vector (with a hack)
2780         if ( nvnum < 7 )
2781           continue;
2782         aComponents[ nvnum ] = 3;
2783         aNames[ nvnum ] = LS_ARRAYNAME_VORTICITY;
2784       }
2785       names.push_back(aNames[ nvnum ]);
2786       cmps.push_back( aComponents[ nvnum ] == -1 ? p->Dimensionality : aComponents[ nvnum ] );
2787       vppt += cmps.back();
2788     }
2789   }
2790 
2791   if ( vppt != 0 )
2792   {
2793     for(size_t i=0; i < cmps.size(); i++)
2794     {
2795       // Note, we don't do anything special when reading deflected coordinates here.
2796       // See `ComputeDeflectionAndUpdateGeometry` for computing of deflection and
2797       // updating for geometry if requested to be deformed.
2798       bool valid = this->GetPointArrayStatus( names[i].c_str() ) != 0;
2799       this->Parts->ReadPointProperty(p->NumberOfNodes, cmps[i], names[i].c_str(), valid);
2800     }
2801 
2802     //clear the buffer as it will be very large and not needed
2803     p->Fam.ClearBuffer();
2804   }
2805   return 0;
2806 }
2807 
2808 //-----------------------------------------------------------------------------
ReadCellStateInfo(vtkIdType vtkNotUsed (step))2809 int vtkLSDynaReader::ReadCellStateInfo( vtkIdType vtkNotUsed(step) )
2810 {
2811 
2812   LSDynaMetaData* p = this->P;
2813 
2814   // ENN (Total element data for state) = Sum of
2815   //      NEL8  (# 8 node solid elems)           * NV3D
2816   //      NELT  (# 8 node thick shell elems)     * NV3DT
2817   //      NEL2  (# 2 node one-dimensional elems) * NV1D
2818   //      NEL4  (# 4 node shells elems)          * NV2D
2819   //      NMSPH (#of SPH Nodes)                  * NUM_SPH_VARS
2820   // where:
2821   //      NV3D = 7 + NEIPH  ( NEIPH = # ALE Data + # Strain Data)
2822   //      NV2D = MAXINT* (6*IOSHL(1) + 1*IOSHL(2) + NEIPS) +8*IOSHL(3) + 4*IOSHL(4) + 12*ISTRN
2823   //
2824 
2825   // Instead of repeating the specification of what array values are read for each cell type here
2826   // we can use the Property lists which we generated in ReadHeaderInformation().
2827 
2828   LSDynaMetaData::LSDYNA_TYPES celltypes[] = {LSDynaMetaData::SOLID, LSDynaMetaData::THICK_SHELL, LSDynaMetaData::BEAM, LSDynaMetaData::SHELL};
2829   vtkIdType cells[]    = {p->Dict["NEL8"], p->Dict["NELT"], p->Dict["NEL2"], p->Dict["NEL4"]};
2830   vtkIdType cellVals[] = {p->Dict["NV3D"], p->Dict["NV3DT"], p->Dict["NV1D"], p->Dict["NV2D"]};
2831 
2832   // Be careful to exclude arrays which are not part State data
2833   unsigned int firstStateArrayNdx = (p->Dict["NARBS"]>0) ? 1 : 0;  // Skip first Array if it is UserIds
2834 
2835   for (int i=0; i<4; i++)
2836   {
2837     if (cells[i]>0)
2838     {
2839       LSDynaMetaData::LSDYNA_TYPES celltype = celltypes[i];
2840       int startPos = 0;
2841       for (unsigned int a = firstStateArrayNdx; a < p->CellArrayNames[celltype].size(); a++ )
2842       {
2843         int numComps = this->GetNumberOfComponentsInCellArray(celltype, a);
2844         //std::cout << setw(3) << numComps << " " << this->GetCellArrayName(celltype,a) << std::endl;
2845         if (this->GetCellArrayStatus(celltype, a))
2846           this->Parts->AddProperty(celltype,this->GetCellArrayName(celltype,a), startPos, numComps);
2847          startPos += numComps;
2848       }
2849       this->ReadCellProperties(celltype, cellVals[i]);
2850     }
2851   }
2852 
2853 #undef VTK_LS_CELLARRAY
2854   return 0;
2855 }
2856 
2857 //-----------------------------------------------------------------------------
ReadCellProperties(const int & type,const int & numTuples)2858 void vtkLSDynaReader::ReadCellProperties(const int& type,const int& numTuples)
2859 {
2860   LSDynaMetaData::LSDYNA_TYPES t =
2861     static_cast<LSDynaMetaData::LSDYNA_TYPES>(type);
2862   vtkIdType numCells,numSkipStart,numSkipEnd;
2863   this->Parts->GetPartReadInfo(type,numCells,numSkipStart,numSkipEnd);
2864 
2865   this->P->Fam.SkipWords(numSkipStart * numTuples);
2866   vtkIdType numChunks = this->P->Fam.InitPartialChunkBuffering(numCells,numTuples);
2867   vtkIdType startId = 0;
2868   if(this->P->Fam.GetWordSize() == 8 && numCells > 0)
2869   {
2870     for(vtkIdType i=0; i < numChunks; ++i)
2871     {
2872       //we need offsets!
2873       vtkIdType chunkSize = this->P->Fam.GetNextChunk( LSDynaFamily::Float);
2874       vtkIdType numCellsInChunk = chunkSize/numTuples;
2875       double *dbuf = this->P->Fam.GetBufferAs<double>();
2876       this->Parts->FillCellProperties(dbuf,t,startId,numCellsInChunk,numTuples);
2877       startId += numCellsInChunk;
2878     }
2879   }
2880   else if (numCells > 0)
2881   {
2882     for(vtkIdType i=0; i < numChunks; ++i)
2883     {
2884       vtkIdType chunkSize = this->P->Fam.GetNextChunk( LSDynaFamily::Float);
2885       vtkIdType numCellsInChunk = chunkSize/numTuples;
2886       float *fbuf = this->P->Fam.GetBufferAs<float>();
2887       this->Parts->FillCellProperties(fbuf,t,startId,numCellsInChunk,numTuples);
2888       startId += numCellsInChunk;
2889     }
2890   }
2891   this->P->Fam.SkipWords(numSkipEnd * numTuples);
2892 
2893   //clear the buffer as it will be very large and not needed
2894   this->P->Fam.ClearBuffer();
2895 }
2896 
2897 
ReadSPHState(vtkIdType vtkNotUsed (step))2898 int vtkLSDynaReader::ReadSPHState( vtkIdType vtkNotUsed(step) )
2899 {
2900   LSDynaMetaData* p = this->P;
2901 
2902   //Make sure we are at the start of the SPH state data
2903   p->Fam.SkipToWord(LSDynaFamily::TimeStepSection, p->CurrentState,0);
2904   p->Fam.SkipWords(p->SPHStateOffset);
2905 
2906 #define VTK_LS_SPHARRAY(cond,celltype,arrayname,numComps)\
2907   if ( (cond) && this->GetCellArrayStatus( celltype, arrayname ) ) \
2908   { \
2909     this->Parts->AddProperty(celltype,arrayname,startPos,numComps); \
2910   } \
2911   if (cond) startPos+=(numComps);
2912 
2913   // Smooth Particle ========================================================
2914 
2915   // currently have a bug when reading SPH properties disabling for now
2916   int startPos=0; //used to keep track of the startpos between calls to VTK_LS_CELLARRAY
2917   VTK_LS_SPHARRAY(               true ,LSDynaMetaData::PARTICLE,LS_ARRAYNAME_MATERIAL,1); //always keep material id / death
2918   VTK_LS_SPHARRAY(p->Dict["isphfg(2)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_RADIUSOFINFLUENCE,1);
2919   VTK_LS_SPHARRAY(p->Dict["isphfg(3)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_PRESSURE,1);
2920   VTK_LS_SPHARRAY(p->Dict["isphfg(4)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_STRESS,6);
2921   VTK_LS_SPHARRAY(p->Dict["isphfg(5)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_EPSTRAIN,1);
2922   VTK_LS_SPHARRAY(p->Dict["isphfg(6)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_DENSITY,1);
2923   VTK_LS_SPHARRAY(p->Dict["isphfg(7)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_INTERNALENERGY,1);
2924   VTK_LS_SPHARRAY(p->Dict["isphfg(8)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_NUMNEIGHBORS,1);
2925   VTK_LS_SPHARRAY(p->Dict["isphfg(9)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_STRAIN,6);
2926   VTK_LS_SPHARRAY(p->Dict["isphfg(10)"],LSDynaMetaData::PARTICLE,LS_ARRAYNAME_MASS,1);
2927 
2928 //  std::cout << "NUM_SPH_DATA: " << p->Dict["NUM_SPH_DATA"] << "start Pos is " << startPos << std::endl;
2929   this->ReadCellProperties(LSDynaMetaData::PARTICLE,p->Dict["NUM_SPH_DATA"]);
2930 
2931 
2932 #undef VTK_LS_SPHARRAY
2933   return 0;
2934 }
2935 
ReadUserMaterialIds()2936 int vtkLSDynaReader::ReadUserMaterialIds()
2937 {
2938   LSDynaMetaData* p = this->P;
2939   vtkIdType m, numMats;
2940 
2941   p->MaterialsOrdered.clear();
2942   p->MaterialsUnordered.clear();
2943   p->MaterialsLookup.clear();
2944   // Does the file contain arbitrary material IDs?
2945 
2946   if ( (p->Dict["NARBS"] > 0) && (p->Dict["NSORT"] < 0))
2947   { // Yes, it does. Read them.
2948 
2949 
2950     // Skip over arbitrary node and element IDs:
2951     vtkIdType skipIds = p->Dict["NUMNP"] + p->Dict["NEL8"] + p->Dict["NEL2"] + p->Dict["NEL4"] + p->Dict["NELT"];
2952     p->Fam.SkipToWord( LSDynaFamily::UserIdData, p->Fam.GetCurrentAdaptLevel(), 16 + skipIds );
2953 
2954     //in some cases the number of materials in NMMAT is incorrect since we are loading
2955     //SPH materials.
2956     numMats = p->Dict["NMMAT"];
2957 
2958     // Read in material ID lists:
2959     p->Fam.BufferChunk( LSDynaFamily::Int, numMats*3 );
2960     for ( m=0; m<numMats; ++m )
2961     {
2962       p->MaterialsOrdered.push_back( p->Fam.GetNextWordAsInt() );
2963     }
2964     for ( m=0; m<numMats; ++m )
2965     {
2966       p->MaterialsUnordered.push_back( p->Fam.GetNextWordAsInt() );
2967     }
2968     for ( m=0; m<numMats; ++m )
2969     {
2970       p->MaterialsLookup.push_back( p->Fam.GetNextWordAsInt() );
2971     }
2972 
2973   }
2974   else
2975   {
2976     numMats = p->Dict["NUMMAT8"] + p->Dict["NUMMATT"] + p->Dict["NUMMAT4"] + p->Dict["NUMMAT2"] + p->Dict["NGPSPH"];
2977     // No, it doesn't. Fabricate a list of sequential IDs
2978     // construct the (trivial) material lookup tables
2979     for ( m = 1; m <= numMats; ++m )
2980     {
2981       p->MaterialsOrdered.push_back( m );
2982       p->MaterialsUnordered.push_back( m );
2983       p->MaterialsLookup.push_back( m );
2984     }
2985   }
2986   return 0;
2987 }
2988 
ReadPartTitlesFromRootFile()2989 int vtkLSDynaReader::ReadPartTitlesFromRootFile()
2990 {
2991   /*
2992   The extra data is written at the end of the following files:
2993   d3plot, d3part and intfor files, and the header and part titles are written directly after the
2994   EOF (= -999999.0) marker.
2995   Value Length Description
2996   -------------------------------
2997 
2998   NTYPE 1 entity type = 90001
2999   NUMPROP 1 number of parts
3000 
3001   For NUMPROP parts:
3002   IDP 1 part id
3003   PTITLE 18 Part title (72 characters)
3004 
3005   For NUMPROP parts:
3006   NTYPE 1 entity type = 90000
3007   HEAD 18 Header title (72 characters)
3008   */
3009 
3010   LSDynaMetaData* p = this->P;
3011   if ( p->PreStateSize <= 0 )
3012   {
3013     vtkErrorMacro( "Database has bad pre state size(" << p->PreStateSize << ")." );
3014     return 1;
3015   }
3016 
3017  //when called this method is at the right spot to read the part names
3018   vtkIdType currentFileLoc = p->Fam.GetCurrentFWord();
3019   vtkIdType currentAdaptLevel = p->Fam.GetCurrentAdaptLevel();
3020 
3021   p->Fam.BufferChunk( LSDynaFamily::Float, 1 );
3022   double eofM = p->Fam.GetNextWordAsFloat();
3023   if(eofM !=LSDynaFamily::EOFMarker)
3024   {
3025     //we failed to find a marker stop on the part names
3026     p->Fam.SkipToWord(LSDynaFamily::ControlSection,currentAdaptLevel,currentFileLoc);
3027     return 1;
3028   }
3029 
3030   //make sure that the root files has room left for the amount of data we are going to request
3031   //if it doesn't we know it can't have part names
3032   vtkIdType numParts = static_cast<vtkIdType>(p->PartIds.size());
3033   vtkIdType partTitlesByteSize = p->Fam.GetWordSize() * (2 + numParts); //NType + NUMPRop + (header part ids)
3034   partTitlesByteSize += (numParts * 72); //names are constant at 72 bytes each independent of word size
3035 
3036   vtkIdType fileSize = p->Fam.GetFileSize(0);
3037   if ( fileSize < partTitlesByteSize + p->Fam.GetCurrentFWord())
3038   {
3039     //this root file doesn't part names
3040     p->Fam.SkipToWord(LSDynaFamily::ControlSection,currentAdaptLevel,currentFileLoc);
3041     return 1;
3042   }
3043 
3044   //we can now safely read the part titles
3045   p->Fam.SkipWords(2); //skip types and num of parts
3046   vtkIdType nameWordSize = 72 / p->Fam.GetWordSize();
3047   for(vtkIdType i=0; i < numParts; ++i)
3048   {
3049     p->Fam.BufferChunk(LSDynaFamily::Int, 1);
3050     p->Fam.GetNextWordAsInt(); //vtkIdType partId
3051 
3052     p->Fam.BufferChunk( LSDynaFamily::Char, nameWordSize);
3053     std::string name(p->Fam.GetNextWordAsChars(),72);
3054     if(!name.empty() && name[0]!=' ')
3055     {
3056       //strip the name to the subset that
3057       size_t found = name.find_last_not_of(' ');
3058       if(found != std::string::npos)
3059       {
3060         name = name.substr(0,found+1);
3061       }
3062       //get the right part id
3063       p->PartNames[i] = name;
3064     }
3065   }
3066   p->Fam.SkipToWord(LSDynaFamily::ControlSection,currentAdaptLevel,currentFileLoc);
3067   return 0;
3068 }
3069 
ResetPartInfo()3070 void vtkLSDynaReader::ResetPartInfo()
3071 {
3072   LSDynaMetaData* p = this->P;
3073   p->PartNames.clear();
3074   p->PartIds.clear();
3075   p->PartMaterials.clear();
3076   p->PartStatus.clear();
3077 
3078   // Create simple part names as place holders
3079   int mat = 1, realMat = 1;
3080   int i;
3081   int N;
3082   char partLabel[64];
3083   int arbitraryMaterials = p->Dict["NSORT"] < 0 ? 1 : 0;
3084 
3085 #define VTK_LSDYNA_PARTLABEL(dict,fmt) \
3086   N = p->Dict[dict]; \
3087   for ( i = 0; i < N; ++i, ++mat ) \
3088   { \
3089     if(arbitraryMaterials) \
3090     { \
3091       if(mat < static_cast<int>(p->MaterialsOrdered.size())) \
3092       { \
3093         realMat = p->MaterialsOrdered[mat - 1]; \
3094       } \
3095       else \
3096       { \
3097         realMat = mat; \
3098       } \
3099       snprintf( partLabel, sizeof(partLabel), fmt " (Matl%d)", mat, realMat ); \
3100     } \
3101     else{ \
3102       realMat = mat; \
3103       snprintf( partLabel, sizeof(partLabel), fmt, mat );  \
3104     } \
3105     p->PartNames.push_back( partLabel ); \
3106     p->PartIds.push_back( realMat ); \
3107     p->PartMaterials.push_back( mat ); \
3108     p->PartStatus.push_back( 1 ); \
3109   }
3110 
3111   VTK_LSDYNA_PARTLABEL("NUMMAT8","Part%d"); // was "PartSolid%d
3112   VTK_LSDYNA_PARTLABEL("NUMMATT","Part%d"); // was "PartThickShell%d
3113   VTK_LSDYNA_PARTLABEL("NUMMAT4","Part%d"); // was "PartShell%d
3114   VTK_LSDYNA_PARTLABEL("NUMMAT2","Part%d"); // was "PartBeam%d
3115   VTK_LSDYNA_PARTLABEL("NGPSPH", "Part%d"); // was "PartParticle%d
3116   VTK_LSDYNA_PARTLABEL("NSURF",  "Part%d"); // was "PartRoadSurface%d
3117   VTK_LSDYNA_PARTLABEL("NUMMAT", "Part%d"); // was "PartRigidBody%d
3118 
3119 #undef VTK_LSDYNA_PARTLABEL
3120 }
3121 
ReadInputDeck()3122 int vtkLSDynaReader::ReadInputDeck()
3123 {
3124   if ( ! this->InputDeck )
3125   {
3126     // nothing more we can do
3127     return 0;
3128   }
3129 
3130   ifstream deck( this->InputDeck, ios::in );
3131   if ( ! deck.good() )
3132   {
3133     return 0;
3134   }
3135 
3136   std::string header;
3137   std::getline( deck, header, '\n' );
3138   deck.seekg( 0, ios::beg );
3139   int retval;
3140   if ( vtksys::SystemTools::StringStartsWith( header.c_str(), "<?xml" ) )
3141   {
3142     retval = this->ReadInputDeckXML( deck );
3143   }
3144   else
3145   {
3146     retval = this->ReadInputDeckKeywords( deck );
3147   }
3148 
3149   return retval;
3150 }
3151 
ReadInputDeckXML(ifstream & deck)3152 int vtkLSDynaReader::ReadInputDeckXML( ifstream& deck )
3153 {
3154   vtkLSDynaSummaryParser* parser = vtkLSDynaSummaryParser::New();
3155   parser->MetaData = this->P;
3156   parser->SetStream( &deck );
3157   // We must be able to parse the file and end up with 1 part per material ID
3158   if ( ! parser->Parse() || this->P->GetTotalMaterialCount() != (int)this->P->PartNames.size() )
3159   {
3160     // We had a problem identifying a part, give up and start over by resetting the parts
3161     this->ResetPartInfo();
3162   }
3163   parser->Delete();
3164 
3165   return 0;
3166 }
3167 
ReadInputDeckKeywords(ifstream & deck)3168 int vtkLSDynaReader::ReadInputDeckKeywords( ifstream& deck )
3169 {
3170   int success = 1;
3171   std::map<std::string,int> parameters;
3172   std::string line;
3173   std::string lineLowercase;
3174   std::string partName;
3175   int partMaterial;
3176   int partId;
3177   int curPart = 0;
3178 
3179   while ( deck.good() && vtkLSNextSignificantLine( deck, line ) && curPart < (int)this->P->PartNames.size() )
3180   {
3181     if ( line[0] == '*' )
3182     {
3183       vtkLSDowncaseFirstWord( lineLowercase, line.substr( 1 ) );
3184       if ( vtksys::SystemTools::StringStartsWith( lineLowercase.c_str(), "part" ) )
3185       {
3186         // found a part
3187         // ... read the next non-comment line as the part name
3188         if ( vtkLSNextSignificantLine( deck, line ) )
3189         {
3190           // Get rid of leading and trailing newlines, whitespace, etc.
3191           vtkLSTrimWhitespace( line );
3192           partName = line;
3193         }
3194         else
3195         {
3196           partName = "";
3197         }
3198         // ... read the next non-comment line as the part id or a reference to it.
3199         if ( vtkLSNextSignificantLine( deck, line ) )
3200         {
3201           std::vector<std::string> splits;
3202           vtkLSSplitString( line, splits, "& ,\t\n\r" );
3203           if ( line[0] == '&' )
3204           {
3205             // found a reference. look it up.
3206             partId = !splits.empty() ? parameters[splits[0]] : -1;
3207           }
3208           else
3209           {
3210             if ( splits.empty() || sscanf( splits[0].c_str(), "%d", &partId ) <= 0 )
3211             {
3212               partId = -1;
3213             }
3214           }
3215           if ( splits.size() < 3 )
3216           {
3217             partMaterial = -1;
3218           }
3219           else
3220           {
3221             if ( splits[2][0] == '&' )
3222             {
3223               partMaterial = parameters[splits[2]];
3224             }
3225             else
3226             {
3227               if ( sscanf( splits[2].c_str(), "%d", &partMaterial ) <= 0 )
3228               {
3229                 partMaterial = -1;
3230               }
3231             }
3232           }
3233         } // read the part id or reference
3234         else
3235         {
3236           partId = -1;
3237           partMaterial = -1;
3238         }
3239         // Comment on next line: partId values need not be consecutive. FIXME: ... or even positive?
3240         if ( ! partName.empty() && partId >= 0 )
3241         {
3242           this->P->PartNames[curPart] = partName;
3243           this->P->PartIds[curPart] = partId;
3244           this->P->PartMaterials[curPart] = partMaterial;
3245           this->P->PartStatus[curPart] = 1;
3246           fprintf( stderr, "%2d: Part: \"%s\" Id: %d\n", curPart, partName.c_str(), partId );
3247           ++curPart;
3248         }
3249         else
3250         {
3251           success = 0;
3252         }
3253       }
3254       else if ( vtksys::SystemTools::StringStartsWith( lineLowercase.c_str(), "parameter" ) )
3255       {
3256         // found a reference
3257         // ... read the next non-comment line to decode the reference
3258         if ( vtkLSNextSignificantLine( deck, line ) )
3259         {
3260           std::string paramName;
3261           int paramIntVal;
3262           // Look for "^[IiRr]\s*(\w+)\s+([\w\.-]+)" and set parameters[\2]=\1
3263           if ( line[0] == 'I' || line[0] == 'i' )
3264           { // We found an integer parameter. Those are the only ones we care about.
3265             line = line.substr( 1 );
3266             std::string::size_type paramStart = line.find_first_not_of( " \t," );
3267             if ( paramStart == std::string::npos )
3268             { // ignore a bad parameter line
3269               continue;
3270             }
3271             std::string::size_type paramEnd = line.find_first_of( " \t,", paramStart );
3272             if ( paramEnd == std::string::npos )
3273             { // found the parameter name, but no value after it
3274               continue;
3275             }
3276             paramName = line.substr( paramStart, paramEnd - paramStart );
3277             if ( sscanf( line.substr( paramEnd + 1 ).c_str(), "%d", &paramIntVal ) <= 0 )
3278             { // unable to read id
3279               continue;
3280             }
3281             parameters[ paramName ] = paramIntVal;
3282           }
3283         }
3284         else
3285         {
3286           // no valid line after "*parameter" keyword. Silently ignore it.
3287         }
3288       } // "parameter line"
3289     } // line starts with "*"
3290   } // while ( deck.good() )
3291 
3292   if ( success )
3293   {
3294     // Save a summary file if possible. The user can open the summary file next
3295     // time and not be forced to parse the entire input deck to get part IDs.
3296     std::string deckDir = vtksys::SystemTools::GetFilenamePath( this->InputDeck );
3297     std::string deckName = vtksys::SystemTools::GetFilenameName( this->InputDeck );
3298     std::string deckExt;
3299     std::string::size_type dot;
3300     std::string xmlSummary;
3301 
3302     // GetFilenameExtension doesn't look for the rightmost "." ... do it ourselves.
3303     dot = deckName.rfind( '.' );
3304     if ( dot != std::string::npos )
3305     {
3306       deckExt = deckName.substr( dot );
3307       deckName = deckName.substr( 0, dot );
3308     }
3309     else
3310     {
3311       deckExt = "";
3312     }
3313 #ifndef _WIN32
3314     xmlSummary = deckDir + "/" + deckName + ".lsdyna";
3315 #else
3316     xmlSummary = deckDir + "\\" + deckName + ".lsdyna";
3317 #endif // _WIN32
3318     // As long as we don't kill the input deck, write the summary XML:
3319     if ( xmlSummary != this->InputDeck )
3320     {
3321       this->WriteInputDeckSummary( xmlSummary.c_str() );
3322     }
3323   }
3324   else
3325   {
3326     // We had a problem identifying a part, give up and reset part info
3327     this->ResetPartInfo();
3328   }
3329 
3330   return ! success;
3331 }
3332 
WriteInputDeckSummary(const char * fname)3333 int vtkLSDynaReader::WriteInputDeckSummary( const char* fname )
3334 {
3335   ofstream xmlSummary( fname, ios::out | ios::trunc );
3336   if ( ! xmlSummary.good() )
3337   {
3338     return 1;
3339   }
3340 
3341   xmlSummary
3342     << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl
3343     << "<lsdyna>" << endl;
3344 
3345   std::string dbDir = this->P->Fam.GetDatabaseDirectory();
3346   std::string dbName = this->P->Fam.GetDatabaseBaseName();
3347   if ( this->IsDatabaseValid() && ! dbDir.empty() && ! dbName.empty() )
3348   {
3349 #ifndef _WIN32
3350     if ( dbDir[0] == '/' )
3351 #else
3352     if ( dbDir[0] == '\\' )
3353 #endif // _WIN32
3354     {
3355       // OK, we have an absolute path, so it should be safe to write it out.
3356       xmlSummary
3357         << "  <database path=\"" << dbDir.c_str()
3358         << "\" name=\"" << dbName.c_str() << "\"/>" << endl;
3359     }
3360   }
3361 
3362   for ( unsigned p = 0; p < this->P->PartNames.size(); ++p )
3363   {
3364     xmlSummary
3365       << "  <part id=\"" << this->P->PartIds[p]
3366       << "\" material_id=\"" << this->P->PartMaterials[p]
3367       << "\" status=\"" << this->P->PartStatus[p]
3368       << "\"><name>" << this->P->PartNames[p].c_str()
3369       << "</name></part>" << endl;
3370   }
3371 
3372   xmlSummary
3373     << "</lsdyna>" << endl;
3374 
3375   return 0;
3376 }
3377 
3378 // ================================================== OK Already! Read the file!
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (iinfo),vtkInformationVector * oinfo)3379 int vtkLSDynaReader::RequestData(
3380   vtkInformation* vtkNotUsed(request),
3381   vtkInformationVector** vtkNotUsed(iinfo),
3382   vtkInformationVector* oinfo )
3383 {
3384   LSDynaMetaData* p = this->P;
3385 
3386   if ( ! p->FileIsValid )
3387   {
3388     // This should have been set in RequestInformation()
3389     return 0;
3390   }
3391   p->Fam.ClearBuffer();
3392   p->Fam.OpenFileHandles();
3393 
3394   vtkMultiBlockDataSet* mbds = nullptr;
3395   vtkInformation* oi = oinfo->GetInformationObject(0);
3396   if ( ! oi )
3397   {
3398     return 0;
3399   }
3400 
3401   if ( oi->Has( vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP() ) )
3402   {
3403     // Only return single time steps for now.
3404     double requestedTimeStep = oi->Get( vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP());
3405     int timeStepLen = oi->Length( vtkStreamingDemandDrivenPipeline::TIME_STEPS() );
3406     double* timeSteps = oi->Get( vtkStreamingDemandDrivenPipeline::TIME_STEPS() );
3407 
3408     int cnt = 0;
3409     while ( cnt < timeStepLen - 1 && timeSteps[cnt] < requestedTimeStep )
3410     {
3411       ++cnt;
3412     }
3413     this->SetTimeStep( cnt );
3414 
3415     oi->Set( vtkDataObject::DATA_TIME_STEP(), p->TimeValues[ p->CurrentState ] );
3416   }
3417 
3418   mbds = vtkMultiBlockDataSet::SafeDownCast( oi->Get(vtkDataObject::DATA_OBJECT()) );
3419   if ( ! mbds )
3420   {
3421     return 0;
3422   }
3423   this->UpdateProgress( 0.01 );
3424 
3425   if ( p->Dict["MATTYP"] )
3426   {
3427     // Do something with material type data
3428   }
3429   this->UpdateProgress( 0.05 );
3430 
3431   if ( p->Dict["IALEMAT"] )
3432   {
3433     // Do something with fluid material ID data
3434   }
3435   this->UpdateProgress( 0.10 );
3436 
3437   if ( p->Dict["NMSPH"] )
3438   {
3439     // Do something with smooth partical hydrodynamics element data
3440   }
3441   this->UpdateProgress( 0.15 );
3442 
3443   //Read in the topology information for caching
3444   this->ReadTopology();
3445 
3446   // Adapted element parent list
3447   // This isn't even implemented by LS-Dyna yet
3448 
3449   // Smooth Particle Hydrodynamics Node and Material List are handled in ReadConnectivityAndMaterial()
3450 
3451   // Start of state data ===================
3452   // I. Node and Cell State
3453   this->UpdateProgress( 0.6 );
3454   if ( this->ReadState( p->CurrentState ) )
3455   {
3456     vtkErrorMacro( "Problem reading state data for time step " << p->CurrentState );
3457     return 1;
3458   }
3459 
3460   // III. SPH Node State
3461   this->UpdateProgress( 0.7 );
3462   if ( this->GetNumberOfParticleCells() )
3463   {
3464     if ( this->ReadSPHState( p->CurrentState ) )
3465     {
3466       vtkErrorMacro( "Problem reading smooth particle hydrodynamics state." );
3467       return 1;
3468     }
3469   }
3470 
3471   this->UpdateProgress( 0.8 );
3472   //add all the parts as child blocks to the output
3473   int size = this->Parts->GetNumberOfParts();
3474   for(int i=0; i < size;++i)
3475   {
3476     if (this->Parts->IsActivePart(i))
3477     {
3478       vtkUnstructuredGrid *ug = this->Parts->GetGridForPart(i);
3479       this->ComputeDeflectionAndUpdateGeometry(ug);
3480 
3481       mbds->SetBlock(i,ug);
3482       mbds->GetMetaData(i)->Set(vtkCompositeDataSet::NAME(),
3483         this->P->PartNames[i].c_str());
3484     }
3485     else
3486     {
3487       mbds->SetBlock(i,nullptr);
3488     }
3489   }
3490 
3491   this->P->Fam.ClearBuffer();
3492   this->UpdateProgress( 1.0 );
3493   return 1;
3494 }
3495 
3496 //-----------------------------------------------------------------------------
3497 template<typename T>
FillDeletionArray(T * buffer,vtkUnsignedCharArray * arr,const vtkIdType & start,const vtkIdType & numCells,const int & deathPos,const int & cellSize)3498 void vtkLSDynaReader::FillDeletionArray(T *buffer, vtkUnsignedCharArray* arr,
3499   const vtkIdType& start, const vtkIdType& numCells,
3500   const int& deathPos, const int& cellSize)
3501 {
3502   unsigned char val;
3503   for ( vtkIdType i=0; i<numCells; ++i )
3504   {
3505     //Quote from LSDyna Manual:
3506     //"each value is set to the element material number or =0,
3507     //if the element is deleted"
3508     val = (buffer[deathPos] == 0.0) ? 1 : 0;
3509     buffer+=cellSize;
3510     arr->SetTuple1(start+i, val);
3511   }
3512 }
3513 
3514 //-----------------------------------------------------------------------------
3515 template <int wordSize,typename T>
FillTopology(T * buff)3516 int vtkLSDynaReader::FillTopology(T *buff)
3517 {
3518   //the passed in buffer is null, and only used to specialze the method
3519   //as pure method specialization isn't support by some compilers
3520   //READ PARTICLES
3521   this->P->Fam.SkipToWord(LSDynaFamily::SPHNodeData,
3522                           this->P->Fam.GetCurrentAdaptLevel(), 0 );
3523   FillBlock<LSDynaMetaData::PARTICLE,wordSize,1>(buff,this->Parts,this->P,2,
3524                                       VTK_VERTEX);
3525 
3526   //READ SOLIDS
3527   this->P->Fam.SkipToWord(LSDynaFamily::GeometryData,
3528                           this->P->Fam.GetCurrentAdaptLevel(),
3529                           this->P->NumberOfNodes*this->P->Dimensionality );
3530 
3531   //other than buff, these parameters are changed by the template specialization
3532   //as SOLID is a unique case
3533   FillBlock<LSDynaMetaData::SOLID,wordSize,8>(buff,this->Parts,this->P,9,
3534                                     VTK_HEXAHEDRON);
3535 
3536   //READ THICK_SHELL
3537   FillBlock<LSDynaMetaData::THICK_SHELL,wordSize,8>(buff,this->Parts,this->P,9,
3538                                           VTK_QUADRATIC_QUAD);
3539 
3540   //READ BEAM
3541   FillBlock<LSDynaMetaData::BEAM,wordSize,2>(buff,this->Parts,this->P,6,VTK_LINE);
3542 
3543   //READ SHELL and RIGID_BODY
3544   //uses a specialization to weave SHELL and RIGID BODY cells together
3545   FillBlock<LSDynaMetaData::SHELL,wordSize,4>(buff,this->Parts,this->P,5,VTK_QUAD);
3546 
3547   //Read Road Surface
3548   if ( this->P->ReadRigidRoadMvmt )
3549   {
3550     this->P->Fam.SkipToWord( LSDynaFamily::RigidSurfaceData,
3551                              this->P->Fam.GetCurrentAdaptLevel(),
3552                              4 + 4*this->P->Dict["NNODE"] );
3553     FillBlock<LSDynaMetaData::ROAD_SURFACE,wordSize,4>(buff,this->Parts,this->P,5,
3554                                             VTK_QUAD);
3555   }
3556   return 0;
3557 }
3558 
3559 //-----------------------------------------------------------------------------
ReadConnectivityAndMaterial()3560 int vtkLSDynaReader::ReadConnectivityAndMaterial()
3561 {
3562   LSDynaMetaData* p = this->P;
3563   if ( p->ConnectivityUnpacked == 0 )
3564   {
3565     // FIXME
3566     vtkErrorMacro( "Packed connectivity isn't supported yet." );
3567     return 1;
3568   }
3569 
3570   this->Parts->InitCellInsertion();
3571   if(p->Fam.GetWordSize() == 8)
3572   {
3573     vtkIdType *buf=nullptr;
3574     return this->FillTopology<8>(buf);
3575   }
3576   else
3577   {
3578     int *buf=nullptr;
3579     return this->FillTopology<4>(buf);
3580   }
3581 }
3582 
3583 //-----------------------------------------------------------------------------
3584 template<typename T, int blockType, vtkIdType numWordsPerCell, vtkIdType cellLength>
ReadBlockCellSizes()3585 void vtkLSDynaReader::ReadBlockCellSizes()
3586 {
3587   //determine the relationship between the file bit size and
3588   //the host machine bit size. This allows us to read 64 bit files on a
3589   //32 bit machine
3590   const int numWordsPerIdType (this->P->Fam.GetWordSize() / sizeof(T));
3591 
3592   vtkIdType nc=0, t=0,j=0,matlId=0;
3593   vtkIdType numCellsToSkip=0, numCellsToSkipEnd=0, chunkSize=0;
3594   const T fileNumWordsPerCell(numWordsPerCell * numWordsPerIdType);
3595   const T offsetToMatId(numWordsPerIdType * (numWordsPerCell-1));
3596   T* buff = nullptr;
3597 
3598   //get from the part the read information for this lsdyna block type
3599   this->Parts->GetPartReadInfo(blockType,nc,numCellsToSkip,numCellsToSkipEnd);
3600 
3601   this->P->Fam.SkipWords(fileNumWordsPerCell * numCellsToSkip ); //skip to the right start id
3602 
3603   //buffer the amount in small chunks so we don't create a massive buffer
3604   vtkIdType numChunks = this->P->Fam.InitPartialChunkBuffering(nc,numWordsPerCell);
3605   for(vtkIdType i=0; i < numChunks; ++i)
3606   {
3607     chunkSize = this->P->Fam.GetNextChunk( LSDynaFamily::Int);
3608     buff = this->P->Fam.GetBufferAs<T>();
3609 
3610     for (j=0; j<chunkSize;j+=numWordsPerCell)
3611     {
3612       buff+=offsetToMatId;
3613       matlId = static_cast<vtkIdType>(*buff);
3614       buff+=numWordsPerIdType;
3615       this->Parts->RegisterCellIndexToPart(blockType,matlId,t++,cellLength);
3616     }
3617   }
3618   this->P->Fam.SkipWords(fileNumWordsPerCell * numCellsToSkipEnd);
3619 }
3620 
3621 //-----------------------------------------------------------------------------
3622 template <typename T>
FillPartSizes()3623 int vtkLSDynaReader::FillPartSizes()
3624 {
3625   //READ PARTICLES
3626   this->P->Fam.SkipToWord(LSDynaFamily::SPHNodeData,
3627                           this->P->Fam.GetCurrentAdaptLevel(), 0 );
3628   this->ReadBlockCellSizes<T,LSDynaMetaData::PARTICLE,2,1>();
3629 
3630   //READ SOLIDS
3631   this->P->Fam.SkipToWord(LSDynaFamily::GeometryData,
3632                           this->P->Fam.GetCurrentAdaptLevel(),
3633                           this->P->NumberOfNodes*this->P->Dimensionality );
3634 
3635   this->ReadBlockCellSizes<T,LSDynaMetaData::SOLID,9,8>();
3636 
3637   //READ THICK_SHELL
3638   this->ReadBlockCellSizes<T,LSDynaMetaData::THICK_SHELL,9,8>();
3639 
3640   //READ BEAM
3641   this->ReadBlockCellSizes<T,LSDynaMetaData::BEAM,6,2>();
3642 
3643   //READ SHELL and RIGID_BODY
3644   //uses a specialization to weave SHELL and RIGID BODY cells together
3645   this->ReadBlockCellSizes<T,LSDynaMetaData::SHELL,5,4>();
3646 
3647   //Read Road Surface
3648   if ( this->P->ReadRigidRoadMvmt )
3649   {
3650     this->P->Fam.SkipToWord( LSDynaFamily::RigidSurfaceData,
3651                              this->P->Fam.GetCurrentAdaptLevel(),
3652                              4 + 4*this->P->Dict["NNODE"] );
3653     this->ReadBlockCellSizes<T,LSDynaMetaData::ROAD_SURFACE,5,4>();
3654   }
3655 
3656   //now that all the registering is done tell the collection
3657   //it can allocate the necessary space for each part
3658   this->Parts->AllocateParts();
3659   return 0;
3660 }
3661 
3662 //-----------------------------------------------------------------------------
ReadPartSizes()3663 int vtkLSDynaReader::ReadPartSizes()
3664 {
3665   LSDynaMetaData* p = this->P;
3666   if ( p->ConnectivityUnpacked == 0 )
3667   {
3668     // FIXME
3669     vtkErrorMacro( "Packed connectivity isn't supported yet." );
3670     return 1;
3671   }
3672 
3673   if(p->Fam.GetWordSize() == 8)
3674   {
3675     return this->FillPartSizes<vtkIdType>();
3676   }
3677   else
3678   {
3679     return this->FillPartSizes<int>();
3680   }
3681 }
3682 
3683 //-----------------------------------------------------------------------------
SetDeformedMesh(vtkTypeBool deformed)3684 void vtkLSDynaReader::SetDeformedMesh(vtkTypeBool deformed)
3685 {
3686   if (this->DeformedMesh != deformed)
3687   {
3688     this->DeformedMesh = deformed;
3689     this->ResetPartsCache();
3690     this->Modified();
3691   }
3692 }
3693 
3694 namespace
3695 {
3696 template <class T, int NumberOfComponents>
vtkComputeDifference(T * aArray,T * bArray)3697 vtkSmartPointer<T> vtkComputeDifference(T* aArray, T* bArray)
3698 {
3699   if (aArray == nullptr || bArray == nullptr)
3700   {
3701     return nullptr;
3702   }
3703 
3704   const vtkIdType numTuples = aArray->GetNumberOfTuples();
3705   const int numComps = aArray->GetNumberOfComponents();
3706   if (bArray->GetNumberOfTuples() != numTuples || bArray->GetNumberOfComponents() != numComps ||
3707     numComps != NumberOfComponents)
3708   {
3709     return nullptr;
3710   }
3711 
3712   vtkVector<typename T::ValueType, NumberOfComponents> tupleA, tupleB;
3713   vtkSmartPointer<T> result = vtkSmartPointer<T>::New();
3714   result->SetNumberOfComponents(NumberOfComponents);
3715   result->SetNumberOfTuples(numTuples);
3716   for (vtkIdType cc = 0, max = numTuples; cc < max; ++cc)
3717   {
3718     aArray->GetTypedTuple(cc, tupleA.GetData());
3719     bArray->GetTypedTuple(cc, tupleB.GetData());
3720     result->SetTypedTuple(cc, (tupleA - tupleB).GetData());
3721   }
3722   return result;
3723 }
3724 }
3725 
3726 //-----------------------------------------------------------------------------
ComputeDeflectionAndUpdateGeometry(vtkUnstructuredGrid * ug)3727 int vtkLSDynaReader::ComputeDeflectionAndUpdateGeometry(vtkUnstructuredGrid* ug)
3728 {
3729   // If LS_ARRAYNAME_DEFLECTION is preset then this computes the deflection.
3730   // If this->DeformedMesh is true, this additionally swaps the output geometry points
3731   // to be the LS_ARRAYNAME_DEFLECTION (which is the deflected coordinates).
3732   LSDynaMetaData* p = this->P;
3733   vtkDataArray* deflectedCoords =
3734     ug ? ug->GetPointData()->GetArray(LS_ARRAYNAME_DEFLECTION) : nullptr;
3735   if (deflectedCoords)
3736   {
3737     vtkSmartPointer<vtkDataArray> deflection;
3738     if (p->Fam.GetWordSize() == 8)
3739     {
3740       deflection =
3741         vtkComputeDifference<vtkDoubleArray, 3>(vtkDoubleArray::SafeDownCast(deflectedCoords),
3742           vtkDoubleArray::SafeDownCast(ug->GetPoints()->GetData()));
3743     }
3744     else
3745     {
3746       deflection =
3747         vtkComputeDifference<vtkFloatArray, 3>(vtkFloatArray::SafeDownCast(deflectedCoords),
3748           vtkFloatArray::SafeDownCast(ug->GetPoints()->GetData()));
3749     }
3750 
3751     if (deflection)
3752     {
3753       deflection->SetName("Deflection");
3754       ug->GetPointData()->AddArray(deflection);
3755     }
3756 
3757     if (this->DeformedMesh)
3758     {
3759       ug->GetPoints()->SetData(deflectedCoords);
3760     }
3761   }
3762   return EXIT_SUCCESS;
3763 }
3764