1 /*=========================================================================
2 
3   Program:   ParaView
4   Module:    vtkExodusIIReaderParser.cxx
5 
6   Copyright (c) Kitware, Inc.
7   All rights reserved.
8   See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 #include "vtkExodusIIReaderParser.h"
16 
17 #include "vtkDataSetAttributes.h"
18 #include "vtkMutableDirectedGraph.h"
19 #include "vtkObjectFactory.h"
20 #include "vtkStringArray.h"
21 #include "vtkUnsignedCharArray.h"
22 
23 #include <cassert>
24 #include <sstream>
25 
26 vtkStandardNewMacro(vtkExodusIIReaderParser);
27 //------------------------------------------------------------------------------
vtkExodusIIReaderParser()28 vtkExodusIIReaderParser::vtkExodusIIReaderParser()
29 {
30   this->SIL = vtkMutableDirectedGraph::New();
31   this->InBlocks = false;
32   this->InMaterialAssignments = false;
33 }
34 
35 //------------------------------------------------------------------------------
~vtkExodusIIReaderParser()36 vtkExodusIIReaderParser::~vtkExodusIIReaderParser()
37 {
38   this->SIL->Delete();
39   this->SIL = nullptr;
40 }
41 
42 //------------------------------------------------------------------------------
StartElement(const char * tagName,const char ** attrs)43 void vtkExodusIIReaderParser::StartElement(const char* tagName, const char** attrs)
44 {
45   const char* name = strrchr(tagName, ':');
46 
47   // If tag name has xml namespace separator, get rid of namespace:
48   name = name ? name + 1 : tagName;
49   std::string tName(name);
50 
51   if (tName == "solid-model")
52   {
53     // Move down to the Assemblies branch.
54     this->CurrentVertex.push_back(this->AssembliesVertex);
55   }
56   else if (tName == "assembly")
57   {
58     // Starting a new "assembly" node. Get paratmeters for this assembly.
59     const char* assemblyNumber = this->GetValue("number", attrs);
60     const char* assemblyDescription = this->GetValue("description", attrs);
61 
62     // Setup the name for this node.
63     std::string node_name = std::string("Assembly: ") + assemblyDescription + std::string(" (") +
64       assemblyNumber + std::string(")");
65 
66     // Now add a vertex in the SIL for this assembly node.
67     vtkIdType vertexID = this->AddVertexToSIL(node_name.c_str());
68     this->AddChildEdgeToSIL(this->CurrentVertex.back(), vertexID);
69     this->CurrentVertex.push_back(vertexID);
70   }
71   else if (tName == "part")
72   {
73     const char* instance = this->GetValue("instance", attrs);
74     std::string instanceString = instance ? instance : "";
75     const char* partNumber = this->GetValue("number", attrs);
76     std::string partNumberBasicString;
77     std::string partNumberString;
78     if (partNumber)
79     {
80       partNumberBasicString = std::string(partNumber);
81       partNumberString = std::string(partNumber) + std::string(" Instance: ") + instanceString;
82     }
83 
84     const char* partDesc = this->GetValue("description", attrs);
85     std::string partDescString;
86     if (partDesc)
87     {
88       partDescString = std::string(partDesc);
89     }
90 
91     // This will create a new vertex if none already present.
92     vtkIdType partVertex = this->GetPartVertex(partNumberString.c_str());
93 
94     // Now fix the part vertex name.
95     std::string result = std::string("Part: ") + partDescString + std::string(" (") +
96       partNumberBasicString + std::string(")") + std::string(" Instance: ") + instanceString;
97     this->NamesArray->InsertValue(partVertex, result.c_str());
98 
99     // Insert the part vertex info the assemblies hierarchy.
100     this->AddChildEdgeToSIL(this->CurrentVertex.back(), partVertex);
101     // The cross link between the part at the blocks it refers is added when the
102     // <blocks/> are parsed.
103 
104     // Save the description for this part, this description is used later to
105     // name the block appropriately.
106     this->PartVertexID_To_Descriptions[partVertex] = partDescString;
107 
108     // Add a "part" vertex in the "Assemblies" hierarchy.
109     this->CurrentVertex.push_back(partVertex);
110   }
111   else if (tName == "material-specification")
112   {
113     // The <part /> element may contain material-specification for each part.
114     // These are used only if <material-assignments/> are not present.
115     vtkIdType partVertex = this->CurrentVertex.back();
116 
117     const char* materialDescriptionString = this->GetValue("description", attrs);
118     std::string material = materialDescriptionString ? materialDescriptionString : "";
119     material += " : ";
120 
121     const char* materialSpecificationString = this->GetValue("specification", attrs);
122     material += materialSpecificationString ? materialSpecificationString : "";
123 
124     this->MaterialSpecifications[partVertex] = material;
125   }
126   else if (tName == "mesh")
127   {
128     assert(this->CurrentVertex.empty());
129     this->CurrentVertex.push_back(this->BlocksVertex);
130   }
131   else if (tName == "blocks")
132   {
133     const char* instance = this->GetValue("part-instance", attrs);
134     std::string instanceString = instance ? instance : "";
135     const char* partNumber = this->GetValue("part-number", attrs);
136     std::string partNumberString;
137     if (partNumber)
138     {
139       partNumberString = std::string(partNumber) + std::string(" Instance: ") + instanceString;
140     }
141 
142     this->InBlocks = true;
143     this->BlockPartNumberString = partNumberString;
144   }
145   else if (tName == "block")
146   {
147     const char* blockString = this->GetValue("id", attrs);
148     int id = -1;
149     if (blockString)
150     {
151       id = atoi(blockString);
152     }
153 
154     if (id >= 0)
155     {
156       if (this->InBlocks && !this->BlockPartNumberString.empty())
157       {
158         // the name for the block is re-generated at the end.
159         vtkIdType blockVertex = this->AddVertexToSIL(blockString);
160         this->AddChildEdgeToSIL(this->BlocksVertex, blockVertex);
161 
162         // This <block /> element was encountered while reading the <mesh />.
163         this->BlockID_To_VertexID[id] = blockVertex;
164 
165         this->BlockID_To_Part[id] = this->BlockPartNumberString;
166         // // Add cross edge linking the assembly part to the block.
167         // vtkIdType partVertex = this->CurrentVertex.back();
168         // this->AddCrossEdgeToSIL(partVertex, blockVertex);
169         // this->BlockID_To_PartVertexID[id] = partVertex;
170       }
171       else if (this->InMaterialAssignments)
172       {
173         // This <block /> element was encountered while reading the
174         // <material-assignments />
175         const char* tmaterialName = this->GetValue("material-name", attrs);
176         if (tmaterialName)
177         {
178           // Save the material information for later since we may not have
179           // seen the <blocks /> yet, consequently we have no mapping from
180           // vertex to block id.
181           this->BlockID_To_MaterialName[id] = tmaterialName;
182         }
183       }
184     }
185   }
186   else if (tName == "material-assignments")
187   {
188     this->CurrentVertex.push_back(this->MaterialsVertex);
189     this->InMaterialAssignments = true;
190   }
191   else if (tName == "material")
192   {
193     const char* material = this->GetValue("name", attrs);
194     if (material)
195     {
196       const char* spec = this->GetValue("specification", attrs);
197       const char* desc = this->GetValue("description", attrs);
198       std::string node_name;
199       if (desc)
200       {
201         node_name = desc;
202       }
203       else
204       {
205         node_name = material;
206       }
207       if (spec)
208       {
209         node_name += " : ";
210         node_name += spec;
211       }
212 
213       vtkIdType vertex = this->AddVertexToSIL(node_name.c_str());
214       this->AddChildEdgeToSIL(this->MaterialsVertex, vertex);
215       this->MaterialName_To_VertexID[material] = vertex;
216     }
217   }
218 }
219 
220 //------------------------------------------------------------------------------
EndElement(const char * tagName)221 void vtkExodusIIReaderParser::EndElement(const char* tagName)
222 {
223   const char* name = strrchr(tagName, ':');
224   // If tag name has xml namespace separator, get rid of namespace:
225   name = name ? name + 1 : tagName;
226   std::string tName(name);
227   if (tName == "solid-model")
228   {
229     this->CurrentVertex.pop_back();
230   }
231   else if (tName == "assembly")
232   {
233     this->CurrentVertex.pop_back();
234   }
235   else if (tName == "part")
236   {
237     this->CurrentVertex.pop_back();
238   }
239   else if (tName == "mesh")
240   {
241     this->CurrentVertex.pop_back();
242   }
243   else if (tName == "blocks")
244   {
245     this->InBlocks = false;
246     this->BlockPartNumberString = "";
247   }
248   else if (tName == "material-assignments")
249   {
250     this->InMaterialAssignments = false;
251     this->CurrentVertex.pop_back();
252   }
253 }
254 
255 //------------------------------------------------------------------------------
FinishedParsing()256 void vtkExodusIIReaderParser::FinishedParsing()
257 {
258   std::map<int, vtkIdType> blockID_to_partVertexID;
259 
260   // * Is assembly was parsed, add cross links between assembly parts and blocks
261   //   belonging to that part.
262   if (!this->Part_To_VertexID.empty())
263   {
264     std::map<int, std::string>::iterator iterIS;
265     for (iterIS = this->BlockID_To_Part.begin(); iterIS != this->BlockID_To_Part.end(); ++iterIS)
266     {
267       if (this->Part_To_VertexID.find(iterIS->second) == this->Part_To_VertexID.end())
268       {
269         // This block blongs to a part not present in the assembly.
270         continue;
271       }
272       vtkIdType partVertex = this->Part_To_VertexID[iterIS->second];
273       vtkIdType blockVertex = this->BlockID_To_VertexID[iterIS->first];
274       this->AddCrossEdgeToSIL(partVertex, blockVertex);
275       blockID_to_partVertexID[iterIS->first] = partVertex;
276     }
277   }
278 
279   // * Assign correct names for all the "block" vertices.
280   std::map<int, vtkIdType>::iterator iter;
281   for (iter = this->BlockID_To_VertexID.begin(); iter != this->BlockID_To_VertexID.end(); ++iter)
282   {
283     // To locate the part description for this block, first locate the part to
284     // which this block belongs.
285     std::string desc = "None";
286     if (blockID_to_partVertexID.find(iter->first) != blockID_to_partVertexID.end())
287     {
288       vtkIdType partVertex = blockID_to_partVertexID[iter->first];
289       desc = this->PartVertexID_To_Descriptions[partVertex];
290     }
291 
292     std::ostringstream stream;
293     stream << "Block: " << iter->first << " (" << desc.c_str() << ") "
294            << this->BlockID_To_Part[iter->first].c_str();
295     this->NamesArray->SetValue(iter->second, stream.str().c_str());
296   }
297 
298   //// * If <material-assignments /> are not present use
299   //// <material-specification /> to construct material assignments.
300   if (this->BlockID_To_MaterialName.empty())
301   {
302     std::map<int, vtkIdType>::iterator iterII;
303     for (iterII = blockID_to_partVertexID.begin(); iterII != blockID_to_partVertexID.end();
304          ++iterII)
305     {
306       int blockID = iterII->first;
307       vtkIdType partVertex = iterII->second;
308 
309       std::string node_name = this->MaterialSpecifications[partVertex];
310       vtkIdType materialVertex;
311       if (this->MaterialName_To_VertexID.find(node_name) == this->MaterialName_To_VertexID.end())
312       {
313         materialVertex = this->AddVertexToSIL(node_name.c_str());
314         this->AddChildEdgeToSIL(this->MaterialsVertex, materialVertex);
315         this->MaterialName_To_VertexID[node_name] = materialVertex;
316       }
317       else
318       {
319         materialVertex = this->MaterialName_To_VertexID[node_name];
320       }
321       this->BlockID_To_MaterialName[blockID] = node_name;
322     }
323   }
324 
325   //// * Add cross-links between "block" vertices and "material" vertices.
326   std::map<int, std::string>::iterator iter2;
327   for (iter2 = this->BlockID_To_MaterialName.begin(); iter2 != this->BlockID_To_MaterialName.end();
328        ++iter2)
329   {
330     vtkIdType blockVertex = this->BlockID_To_VertexID[iter2->first];
331     if (this->MaterialName_To_VertexID.find(iter2->second) != this->MaterialName_To_VertexID.end())
332     {
333       vtkIdType materialVertex = this->MaterialName_To_VertexID[iter2->second];
334       this->AddCrossEdgeToSIL(materialVertex, blockVertex);
335     }
336   }
337 }
338 
339 //------------------------------------------------------------------------------
AddVertexToSIL(const char * name)340 vtkIdType vtkExodusIIReaderParser::AddVertexToSIL(const char* name)
341 {
342   vtkIdType vertex = this->SIL->AddVertex();
343   this->NamesArray->InsertValue(vertex, name);
344   return vertex;
345 }
346 
347 //------------------------------------------------------------------------------
AddChildEdgeToSIL(vtkIdType src,vtkIdType dst)348 vtkIdType vtkExodusIIReaderParser::AddChildEdgeToSIL(vtkIdType src, vtkIdType dst)
349 {
350   vtkIdType id = this->SIL->AddEdge(src, dst).Id;
351   this->CrossEdgesArray->InsertValue(id, 0);
352   return id;
353 }
354 
355 //------------------------------------------------------------------------------
AddCrossEdgeToSIL(vtkIdType src,vtkIdType dst)356 vtkIdType vtkExodusIIReaderParser::AddCrossEdgeToSIL(vtkIdType src, vtkIdType dst)
357 {
358   vtkIdType id = this->SIL->AddEdge(src, dst).Id;
359   this->CrossEdgesArray->InsertValue(id, 1);
360   return id;
361 }
362 
363 //------------------------------------------------------------------------------
GetPartVertex(const char * part_number_instance_string)364 vtkIdType vtkExodusIIReaderParser::GetPartVertex(const char* part_number_instance_string)
365 {
366   std::map<std::string, vtkIdType>::iterator iter =
367     this->Part_To_VertexID.find(part_number_instance_string);
368   if (iter != this->Part_To_VertexID.end())
369   {
370     return iter->second;
371   }
372 
373   // The name here is temporary. The full name for a the "part" nodes is
374   // determined when the assembly is parsed.
375   vtkIdType vertex = this->AddVertexToSIL(part_number_instance_string);
376   // Save the vertex for later use.
377   this->Part_To_VertexID[part_number_instance_string] = vertex;
378   return vertex;
379 }
380 
381 //------------------------------------------------------------------------------
Go(const char * filename)382 void vtkExodusIIReaderParser::Go(const char* filename)
383 {
384   this->SIL->Initialize();
385   this->CurrentVertex.clear();
386   this->BlockID_To_VertexID.clear();
387   this->BlockID_To_MaterialName.clear();
388   this->MaterialName_To_VertexID.clear();
389   this->PartVertexID_To_Descriptions.clear();
390   this->Part_To_VertexID.clear();
391   this->MaterialSpecifications.clear();
392   this->BlockID_To_Part.clear();
393   this->InBlocks = false;
394   this->InMaterialAssignments = false;
395 
396   this->NamesArray = vtkSmartPointer<vtkStringArray>::New();
397   this->NamesArray->SetName("Names");
398   this->CrossEdgesArray = vtkSmartPointer<vtkUnsignedCharArray>::New();
399   this->CrossEdgesArray->SetName("CrossEdges");
400   this->SIL->GetVertexData()->AddArray(this->NamesArray);
401   this->SIL->GetEdgeData()->AddArray(this->CrossEdgesArray);
402 
403   this->RootVertex = this->AddVertexToSIL("SIL");
404   this->BlocksVertex = this->AddVertexToSIL("Blocks");
405   this->AssembliesVertex = this->AddVertexToSIL("Assemblies");
406   this->MaterialsVertex = this->AddVertexToSIL("Materials");
407   this->AddChildEdgeToSIL(this->RootVertex, this->BlocksVertex);
408   this->AddChildEdgeToSIL(this->RootVertex, this->AssembliesVertex);
409   this->AddChildEdgeToSIL(this->RootVertex, this->MaterialsVertex);
410 
411   this->SetFileName(filename);
412   this->Parse();
413   this->FinishedParsing();
414 }
415 
416 //------------------------------------------------------------------------------
GetBlockName(int id)417 std::string vtkExodusIIReaderParser::GetBlockName(int id)
418 {
419   if (this->BlockID_To_VertexID.find(id) != this->BlockID_To_VertexID.end())
420   {
421     vtkIdType vertex = this->BlockID_To_VertexID[id];
422     return this->NamesArray->GetValue(vertex);
423   }
424   return "";
425 }
426 
427 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)428 void vtkExodusIIReaderParser::PrintSelf(ostream& os, vtkIndent indent)
429 {
430   this->Superclass::PrintSelf(os, indent);
431   os << indent << "SIL: " << this->SIL << endl;
432 }
433