1 #include "vtkMath.h"
2 #include "vtkStdString.h"
3 #include "vtkIntArray.h"
4 #include "vtkUnstructuredGrid.h"
5 #include "vtkPolyData.h"
6 #include "vtkExodusIIReader.h"
7 #include "vtkExodusIIReaderPrivate.h"
8 #include "vtkExodusIIReaderVariableCheck.h"
9 #include <vtksys/RegularExpression.hxx>
10 #include <vtksys/SystemTools.hxx>
11 #include <string>
12 #include <vector>
13 #include <set>
14 
15 #include <cctype>
16 
Start(std::string name,const int * truth,int numTruth)17 bool vtkExodusIIReaderVariableCheck::Start( std::string name, const int* truth, int numTruth )
18 {
19   this->SeqTruth.clear();
20   this->SeqTruth.insert( this->SeqTruth.begin(), truth, truth + numTruth );
21   this->OriginalNames.clear();
22   bool result = this->StartInternal( name, truth, numTruth );
23   bool atLeastOne = false;
24   for ( int i = 0; i < numTruth; ++ i )
25   {
26     if ( truth[i] )
27       atLeastOne = true;
28   }
29   return result && atLeastOne;
30 }
31 
Length()32 std::vector<std::string>::size_type vtkExodusIIReaderVariableCheck::Length()
33 {
34   return this->OriginalNames.size();
35 }
36 
Accept(std::vector<vtkExodusIIReaderPrivate::ArrayInfoType> & arr,int startIndex,vtkExodusIIReaderPrivate * priv,int objtyp)37 int vtkExodusIIReaderVariableCheck::Accept(
38   std::vector<vtkExodusIIReaderPrivate::ArrayInfoType>& arr,
39   int startIndex, vtkExodusIIReaderPrivate* priv, int objtyp )
40 {
41   std::string::size_type len = this->Length();
42   vtkExodusIIReaderPrivate::ArrayInfoType ainfo;
43   ainfo.Name = this->Prefix;
44   ainfo.Source = vtkExodusIIReaderPrivate::Result;
45   ainfo.Components = static_cast<int>( len );
46   for ( unsigned int i = 0; i < len; ++ i )
47   {
48     ainfo.OriginalIndices.push_back( startIndex + i + 1 /* FORTRAN. Blech. */ );
49     ainfo.OriginalNames.push_back( this->OriginalNames[i] );
50   }
51   ainfo.GlomType = this->GlomType;
52   ainfo.StorageType = VTK_DOUBLE;
53   ainfo.Status = 0;
54   ainfo.ObjectTruth = this->SeqTruth;
55   this->UniquifyName( ainfo, arr );
56   if ( priv )
57   {
58     priv->GetInitialObjectArrayStatus( objtyp, &ainfo );
59   }
60   arr.push_back( ainfo );
61   return static_cast<int>( this->Length() );
62 }
63 
vtkExodusIIReaderVariableCheck()64 vtkExodusIIReaderVariableCheck::vtkExodusIIReaderVariableCheck()
65 {
66   this->GlomType = -1;
67 }
68 
69 vtkExodusIIReaderVariableCheck::~vtkExodusIIReaderVariableCheck() = default;
70 
CheckTruth(const int * truth)71 bool vtkExodusIIReaderVariableCheck::CheckTruth( const int* truth )
72 {
73   if ( ! truth )
74     return false;
75 
76   for ( std::vector<int>::iterator it = this->SeqTruth.begin(); it != this->SeqTruth.end(); ++ it, ++ truth )
77   {
78     if ( *truth != *it )
79     {
80       return false;
81     }
82   }
83 
84   return true;
85 }
86 
UniquifyName(vtkExodusIIReaderPrivate::ArrayInfoType & ainfo,std::vector<vtkExodusIIReaderPrivate::ArrayInfoType> & arrays)87 bool vtkExodusIIReaderVariableCheck::UniquifyName(
88     vtkExodusIIReaderPrivate::ArrayInfoType& ainfo,
89     std::vector<vtkExodusIIReaderPrivate::ArrayInfoType>& arrays )
90 {
91   bool nameChanged = false;
92   std::vector<vtkExodusIIReaderPrivate::ArrayInfoType>::iterator it = arrays.begin();
93   while ( it != arrays.end() )
94   {
95     if ( it->Name == ainfo.Name )
96     {
97       nameChanged = true;
98       ainfo.Name.append( "_" );
99       it = arrays.begin(); // Have to start over now that we've changed the name.
100     }
101     else
102     {
103       ++ it;
104     }
105   }
106   return nameChanged;
107 }
108 
vtkExodusIIReaderScalarCheck()109 vtkExodusIIReaderScalarCheck::vtkExodusIIReaderScalarCheck()
110 {
111   this->GlomType = vtkExodusIIReaderPrivate::Scalar;
112 }
113 
StartInternal(std::string name,const int *,int)114 bool vtkExodusIIReaderScalarCheck::StartInternal( std::string name, const int*, int )
115 {
116   this->Prefix = name;
117   this->OriginalNames.push_back( name );
118   return false;
119 }
120 
Add(std::string,const int *)121 bool vtkExodusIIReaderScalarCheck::Add( std::string, const int* )
122 { // Scalars never have more than 1 name
123   return false;
124 }
125 
vtkExodusIIReaderVectorCheck(const char * seq,int n)126 vtkExodusIIReaderVectorCheck::vtkExodusIIReaderVectorCheck( const char* seq, int n )
127 {
128   this->Endings.clear();
129   this->Endings.insert( this->Endings.begin(), seq, seq + n );
130   this->Endings = vtksys::SystemTools::LowerCase( this->Endings );
131   switch ( n )
132   {
133   case 2:
134     this->GlomType = vtkExodusIIReaderPrivate::Vector2;
135     break;
136   case 3:
137     this->GlomType = vtkExodusIIReaderPrivate::Vector3;
138     break;
139   default:
140     this->GlomType = -1; // Oops. What goes here?
141     break;
142   }
143 }
144 
StartInternal(std::string name,const int *,int)145 bool vtkExodusIIReaderVectorCheck::StartInternal( std::string name, const int*, int )
146 {
147   std::string::size_type len = name.size();
148   if ( len > 1 && tolower( name[len - 1] ) == this->Endings[0] )
149   {
150     this->Prefix = name.substr( 0, len - 1 );
151     this->OriginalNames.push_back( name );
152     this->StillAdding = true;
153     return true;
154   }
155   this->StillAdding = false;
156   this->Prefix = "";
157   return false;
158 }
159 
Add(std::string name,const int * truth)160 bool vtkExodusIIReaderVectorCheck::Add( std::string name, const int* truth )
161 {
162   if (
163     ( ! this->StillAdding ) ||
164     ( this->OriginalNames.size() >= this->Endings.size() ) ||
165     ( ! this->CheckTruth( truth ) ) )
166   {
167     this->StillAdding = false;
168     return false;
169   }
170   std::string::size_type len = name.size();
171   if (
172     ( len != this->Prefix.size() + 1 ) ||
173     ( name.substr( 0, len - 1 ) != this->Prefix ) ||
174     ( tolower( name[len - 1] ) != this->Endings[this->OriginalNames.size()] ) )
175   {
176     this->StillAdding = false;
177     return false;
178   }
179 
180   this->OriginalNames.push_back( name );
181   return true;
182 }
183 
Length()184 std::vector<std::string>::size_type vtkExodusIIReaderVectorCheck::Length()
185 {
186   std::vector<std::string>::size_type len = this->OriginalNames.size();
187   return ( len == this->Endings.size() ) ? len : 0;
188 }
189 
190 /*
191 rank 1:
192 dim 1( 1):   x
193 dim 2( 2):   x   y
194 dim 3( 3):   x   y   z
195 dim 4( 4):   x   y   z   w
196 1
197 1 1
198 1 1 1
199 1 1 1 1
200 rank 2:
201 dim 1( 1):  xx
202 dim 2( 3):  xx  yy  xy
203 dim 3( 6):  xx  yy  zz  xy  xz  yz
204 dim 4(10):  xx  yy  zz  ww  xy  xz  xw  yz  yw  zw
205 1
206 2 1
207 3 2 1
208 4 3 2 1
209 rank 3:
210 dim 1( 1): xxx
211 dim 2( 4): xxx yyy xxy xyy
212 dim 3(10): xxx yyy zzz xxy xxz xyy xyz xzz yyz yzz
213 dim 4(20): xxx yyy zzz www xxy xxz xxw xyy xyz xyw xzz xzw xww yyz yyw yzz yzw yww zzw zww
214 1
215 3 1
216 6 3 1
217 10 6 3 1
218 5!/3!/2 + 4!/2!/2 + 3!/1!/2 + 2!/0!/2 = 20
219 4!/2!/2 + 3!/1!/2 + 2!/0!/2 = 10
220 3!/1!/2 + 2!/0!/2 = 4
221 2!/0!/2 = 1
222 
223 number of endings = nchoosek( rank + dim - 1, rank )
224 */
225 
vtkExodusIIReaderTensorCheck(const char * seq,int n,int rank,int dim)226 vtkExodusIIReaderTensorCheck::vtkExodusIIReaderTensorCheck( const char* seq, int n, int rank, int dim )
227 {
228   this->NumEndings = vtkMath::Binomial( rank + dim - 1, rank );
229   if ( n == (int) this->NumEndings && rank > 0 && dim > 0 )
230   {
231     this->Dimension = dim;
232     this->Rank = rank;
233     this->Endings.insert( this->Endings.begin(), seq, seq + n * rank );
234     this->Endings = vtksys::SystemTools::LowerCase( this->Endings );
235     if ( this->Rank == 1 && this->Dimension == 2 )
236     {
237       this->GlomType = vtkExodusIIReaderPrivate::Vector2;
238     }
239     else if ( this->Rank == 1 && this->Dimension == 3 )
240     {
241       this->GlomType = vtkExodusIIReaderPrivate::Vector3;
242     }
243     else
244     {
245       this->GlomType = vtkExodusIIReaderPrivate::SymmetricTensor;
246     }
247   }
248   else
249   {
250     vtkGenericWarningMacro(
251       "Invalid number of tensor endings " << n << " for "
252       "rank " << rank << " and dimension " << dim << "; expected "
253       "vtkMath::Binomial( " << ( rank + dim - 1 ) << ", " << rank << ")"
254       " = " << this->NumEndings );
255     this->GlomType = -1;
256     this->NumEndings = 0;
257   }
258 }
259 
StartInternal(std::string name,const int *,int)260 bool vtkExodusIIReaderTensorCheck::StartInternal( std::string name, const int*, int )
261 {
262   std::string::size_type len = name.size();
263   if ( ( len > (unsigned) this->Rank ) &&
264     vtksys::SystemTools::LowerCase( name.substr(len - this->Rank ) ) == this->Endings.substr( 0, this->Rank ) )
265   {
266     this->Prefix = name.substr( 0, len - this->Rank );
267     this->OriginalNames.push_back( name );
268     this->StillAdding = true;
269     return true;
270   }
271   this->Prefix = "";
272   this->StillAdding = false;
273   return false;
274 }
275 
Add(std::string name,const int * truth)276 bool vtkExodusIIReaderTensorCheck::Add( std::string name, const int* truth )
277 {
278   if (
279     ( ! this->StillAdding ) ||
280     ( this->OriginalNames.size() >= this->NumEndings ) ||
281     ( ! this->CheckTruth( truth ) ) )
282   {
283     this->StillAdding = false;
284     return false;
285   }
286   std::string::size_type len = name.size();
287   if (
288     ( len != this->Prefix.size() + this->Rank ) ||
289     ( name.substr( 0, len - this->Rank ) != this->Prefix ) )
290   {
291     this->StillAdding = false;
292     return false;
293   }
294   std::string::size_type endingOffset = this->OriginalNames.size() * this->Rank;
295   if ( vtksys::SystemTools::LowerCase( name.substr( len - this->Rank ) ) != this->Endings.substr( endingOffset, this->Rank )  )
296   {
297     this->StillAdding = false;
298     return false;
299   }
300 
301   this->OriginalNames.push_back( name );
302   return true;
303 }
304 
Length()305 std::vector<std::string>::size_type vtkExodusIIReaderTensorCheck::Length()
306 {
307   std::vector<std::string>::size_type len = this->OriginalNames.size();
308   //std::vector<std::string>::size_type expected = this->Rank * this->Dimension;
309   return ( len == this->NumEndings ) ? len : 0;
310 }
311 
vtkExodusIIReaderIntPointCheck()312 vtkExodusIIReaderIntPointCheck::vtkExodusIIReaderIntPointCheck()
313   : RegExp( "(.*)_([^_]*)_GP([0-9,]+)$" )
314 {
315   this->GlomType = vtkExodusIIReaderPrivate::IntegrationPoint;
316 }
317 
StartInternal(std::string name,const int *,int)318 bool vtkExodusIIReaderIntPointCheck::StartInternal( std::string name, const int*, int )
319 {
320   if ( this->RegExp.find( name ) )
321   {
322     this->VarName = this->RegExp.match( 1 );
323     this->CellType = this->RegExp.match( 2 );
324     this->Prefix = this->VarName + "_" + this->CellType;
325     // Can't have 3-D Gauss points on a quad (unless it's a shell) or 2-D Gauss points for a hex,
326     // so verify that the integration domain has a rank appropriate to the cell type.
327     // This also verifies that the cell type is valid and initializes IntPtMin, IntPtMax, and IntPtNames.
328     if ( this->StartIntegrationPoints( this->CellType, this->RegExp.match( 3 ) ) )
329     {
330       this->OriginalNames.push_back( name );
331       this->StillAdding = true;
332       return true;
333     }
334   }
335   this->Prefix = "";
336   this->StillAdding = false;
337   return false;
338 }
339 
Add(std::string name,const int *)340 bool vtkExodusIIReaderIntPointCheck::Add( std::string name, const int* )
341 {
342   if (
343     ( ! this->StillAdding ) ||
344     ( this->Rank == 0 ) )
345   {
346     this->StillAdding = false;
347     return false;
348   }
349   std::string::size_type nlen = name.size();
350   std::string::size_type plen = this->Prefix.size();
351   if (
352     ( nlen != plen + this->Rank + 3 /* for "_GP" */ ) ||
353     ( name.substr( 0, plen ) != this->Prefix ) ||
354     ( ! this->AddIntegrationPoint( name.substr( nlen - this->Rank ) ) ) )
355   {
356     this->StillAdding = false;
357     return false;
358   }
359 
360   this->OriginalNames.push_back( name );
361   return true;
362 }
363 
Length()364 std::vector<std::string>::size_type vtkExodusIIReaderIntPointCheck::Length()
365 {
366   if ( this->IntPtMin.size() != this->IntPtMax.size() )
367     return 0;
368 
369   // Compute the size of the product space of the integration point indices.
370   // FIXME: This assumes that integration points will be placed in a full-tensor
371   //        product arrangement, which may not be true for triangular, tetrahedral
372   //        wedge, or pyramidal elements depending on how they are parameterized.
373   vtkTypeUInt64 numExpected = 1;
374   for ( unsigned int i = 0; i < this->IntPtMax.size(); ++ i )
375   {
376     numExpected *= ( this->IntPtMax[i] - this->IntPtMin[i] + 1 );
377   }
378   if ( numExpected < 1 || numExpected != this->OriginalNames.size() )
379     return 0;
380 
381   return this->OriginalNames.size();
382 }
383 
384 /*
385 int vtkExodusIIReaderIntPointCheck::Accept(
386 std::vector<vtkExodusIIReaderPrivate::ArrayInfoType>& arr, int startIndex, vtkExodusIIReaderPrivate* priv, int objtyp )
387 {
388 }
389 */
390 
StartIntegrationPoints(std::string cellType,std::string iptName)391 bool vtkExodusIIReaderIntPointCheck::StartIntegrationPoints(
392   std::string cellType, std::string iptName )
393 {
394   struct
395   {
396     const char* RE;
397     int Rank;
398   }
399   cellTypes[] =
400     {
401       { "[Qq][Uu][Aa][Dd]", 2 },
402       { "[Hh][Ee][Xx]", 3 },
403       { "[Tt][Ee][Tt]", 3 },
404       { "[Tt][Rr][Ii]", 2 },
405       { "[Ww][Ee][Dd][Gg][Ee]", 3 },
406       { "[Pp][Yy][Rr]", 3 }
407     };
408   vtksys::RegularExpression ctrexp;
409   std::string::size_type expectedRank = static_cast<std::string::size_type>( -1 );
410   for ( unsigned int i = 0; i < sizeof(cellTypes)/sizeof(cellTypes[0]); ++ i )
411   {
412     ctrexp.compile( cellTypes[i].RE );
413     if ( ctrexp.find( cellType ) )
414     {
415       expectedRank = cellTypes[i].Rank;
416       break;
417     }
418   }
419   std::string::size_type rank = iptName.size();
420   if ( expectedRank > 0 && rank != expectedRank )
421   {
422     this->Rank = 0;
423     return false;
424   }
425   this->Rank = rank;
426   this->IntPtMin.clear();
427   this->IntPtMax.clear();
428   for ( std::string::size_type i = 0; i < rank; ++ i )
429   {
430     int ival = iptName[i] - '0';
431     if ( ival < 0 || ival > 9 )
432     {
433       this->Rank = 0;
434       return false;
435     }
436     this->IntPtMin.push_back( ival );
437     this->IntPtMax.push_back( ival );
438   }
439   this->IntPtNames.clear(); // clear out any old values
440   this->IntPtNames.insert( iptName );
441   return true;
442 }
443 
AddIntegrationPoint(std::string iptName)444 bool vtkExodusIIReaderIntPointCheck::AddIntegrationPoint( std::string iptName )
445 {
446   std::string::size_type rank = iptName.size();
447   if ( rank != this->Rank )
448   {
449     this->Rank = 0;
450     return false;
451   }
452   std::pair<std::set<std::string>::iterator,bool> result;
453   result = this->IntPtNames.insert( iptName );
454   if ( ! result.second )
455   { // Oops, this integration point is a duplicate.
456     this->Rank = 0;
457     return false;
458   }
459   for ( std::string::size_type i = 0; i < rank; ++ i )
460   {
461     int ival = iptName[i] - '0';
462     if ( ival < 0 || ival > 9 )
463     {
464       this->Rank = 0;
465       return false;
466     }
467     if ( this->IntPtMin[i] > ival )
468       this->IntPtMin[i] = ival;
469     if ( this->IntPtMax[i] < ival )
470       this->IntPtMax[i] = ival;
471   }
472   return true;
473 }
474