Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
PreProcessing/MeshConvert/Module.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: Module.cpp
4 //
5 // For more information, please see: http://www.nektar.info/
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // License for the specific language governing rights and limitations under
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 //
32 // Description: Abstract input/output modules.
33 //
34 ////////////////////////////////////////////////////////////////////////////////
35 
36 #include <string>
37 #include "Module.h"
38 
39 using namespace std;
40 
41 namespace Nektar
42 {
43  namespace Utilities
44  {
45  /**
46  * Returns an instance of the module factory, held as a singleton.
47  */
49  {
50  typedef Loki::SingletonHolder<ModuleFactory,
51  Loki::CreateUsingNew,
52  Loki::NoDestroy > Type;
53  return Type::Instance();
54  }
55 
56  /**
57  * Prints a given module key to a stream.
58  */
59  std::ostream& operator<<(std::ostream& os, const ModuleKey& rhs)
60  {
61  return os << ModuleTypeMap[rhs.first] << ": " << rhs.second;
62  }
63 
64  InputModule::InputModule(MeshSharedPtr m) : Module(m)
65  {
66  m_config["infile"] = ConfigOption(false, "", "Input filename.");
67  }
68 
70  {
71  m_config["outfile"] = ConfigOption(false, "", "Output filename.");
72  }
73 
74  /**
75  * @brief Open a file for input.
76  */
78  {
79  string fname = m_config["infile"].as<string>();
80  m_mshFile.open(fname.c_str());
81  if (!m_mshFile.good())
82  {
83  cerr << "Error opening file: " << fname << endl;
84  abort();
85  }
86  }
87 
88  /**
89  * @brief Open a file for output.
90  */
92  {
93  string fname = m_config["outfile"].as<string>();
94  m_mshFile.open(fname.c_str());
95  if (!m_mshFile.good())
96  {
97  cerr << "Error opening file: " << fname << endl;
98  abort();
99  }
100  }
101 
102  /**
103  * @brief Create a unique set of mesh vertices from elements stored in
104  * Mesh::element.
105  *
106  * Each element is processed in turn and the vertices extracted and
107  * inserted into #m_vertexSet, which at the end of the routine
108  * contains all unique vertices in the mesh.
109  */
111  {
112  vector<ElementSharedPtr> &elmt = m_mesh->m_element[m_mesh->m_expDim];
113 
114  m_mesh->m_vertexSet.clear();
115 
116  for (int i = 0, vid = 0; i < elmt.size(); ++i)
117  {
118  for (int j = 0; j < elmt[i]->GetVertexCount(); ++j)
119  {
120  pair<NodeSet::iterator,bool> testIns =
121  m_mesh->m_vertexSet.insert(elmt[i]->GetVertex(j));
122 
123  if (testIns.second)
124  {
125  (*(testIns.first))->m_id = vid++;
126  }
127  else
128  {
129  elmt[i]->SetVertex(j,*testIns.first);
130  }
131  }
132  }
133  }
134 
135  /**
136  * @brief Create a unique set of mesh edges from elements stored in
137  * Mesh::element.
138  *
139  * All elements are first scanned and a list of unique, enumerated
140  * edges produced in #m_edgeSet. Since each element generated its
141  * edges independently, we must now ensure that each element only uses
142  * edge objects from the #m_edgeSet set This ensures there are no
143  * duplicate edge objects. Finally, we scan the list of elements for
144  * 1-D boundary elements which correspond to an edge in
145  * #m_edgeSet. For such elements, we set its edgeLink to reference the
146  * corresponding edge in #m_edgeSet.
147  *
148  * This routine only proceeds if the expansion dimension is 2 or 3.
149  */
150  void Module::ProcessEdges(bool ReprocessEdges)
151  {
152  if (m_mesh->m_expDim < 2) return;
153 
154  if(ReprocessEdges)
155  {
156  vector<ElementSharedPtr> &elmt = m_mesh->m_element[m_mesh->m_expDim];
157 
158  m_mesh->m_edgeSet.clear();
159 
160  // Scan all elements and generate list of unique edges
161  for (int i = 0, eid = 0; i < elmt.size(); ++i)
162  {
163  for (int j = 0; j < elmt[i]->GetEdgeCount(); ++j)
164  {
165  pair<EdgeSet::iterator,bool> testIns;
166  EdgeSharedPtr ed = elmt[i]->GetEdge(j);
167  testIns = m_mesh->m_edgeSet.insert(ed);
168 
169  if (testIns.second)
170  {
171  (*(testIns.first))->m_id = eid++;
172  }
173  else
174  {
175  EdgeSharedPtr e2 = *(testIns.first);
176  elmt[i]->SetEdge(j, e2);
177  if (e2->m_edgeNodes.size() == 0 &&
178  ed->m_edgeNodes.size() > 0)
179  {
180  e2->m_curveType = ed->m_curveType;
181  e2->m_edgeNodes = ed->m_edgeNodes;
182 
183  // Reverse nodes if appropriate.
184  if (e2->m_n1->m_id != ed->m_n1->m_id)
185  {
186  reverse(e2->m_edgeNodes.begin(),
187  e2->m_edgeNodes.end());
188  }
189  }
190 
191  // Update edge to element map.
192  (*(testIns.first))->m_elLink.push_back(
193  pair<ElementSharedPtr,int>(elmt[i],j));
194  }
195  }
196  }
197  }
198 
199  // Create links for 1D elements
200  for (int i = 0; i < m_mesh->m_element[1].size(); ++i)
201  {
202  NodeSharedPtr v0 = m_mesh->m_element[1][i]->GetVertex(0);
203  NodeSharedPtr v1 = m_mesh->m_element[1][i]->GetVertex(1);
204  vector<NodeSharedPtr> edgeNodes;
205  EdgeSharedPtr E = boost::shared_ptr<Edge>(
206  new Edge(v0, v1, edgeNodes,
207  m_mesh->m_element[1][i]->GetConf().m_edgeCurveType));
208  EdgeSet::iterator it = m_mesh->m_edgeSet.find(E);
209  if (it == m_mesh->m_edgeSet.end())
210  {
211  cerr << "Cannot find corresponding element edge for "
212  << "1D element " << i << endl;
213  abort();
214  }
215  m_mesh->m_element[1][i]->SetEdgeLink(*it);
216 
217  // Update 2D element boundary map.
218  ASSERTL0((*it)->m_elLink.size() != 0,
219  "Empty boundary map!");
220  ASSERTL0((*it)->m_elLink.size() == 1,
221  "Too many elements in boundary map!");
222  pair<ElementSharedPtr, int> eMap = (*it)->m_elLink.at(0);
223  eMap.first->SetBoundaryLink(eMap.second, i);
224  }
225  }
226 
227 
228  /**
229  * @brief Create a unique set of mesh faces from elements stored in
230  * Mesh::element.
231  *
232  * All elements are scanned and a unique list of enumerated faces is
233  * produced in #m_faceSet. Since elements created their own faces
234  * independently, we examine each element only uses face objects from
235  * #m_faceSet. Duplicate faces of those in #m_face are replaced with
236  * the corresponding entry in #m_faceSet. Finally, we scan the list of
237  * elements for 2-D boundary faces which correspond to faces in
238  * #m_faceSet. For such elements, we set its faceLink to reference the
239  * corresponding face in #m_faceSet.
240  *
241  * This routine only proceeds if the expansion dimension is 3.
242  */
243  void Module::ProcessFaces(bool ReprocessFaces)
244  {
245  if (m_mesh->m_expDim < 3) return;
246 
247  if(ReprocessFaces)
248  {
249  vector<ElementSharedPtr> &elmt = m_mesh->m_element[m_mesh->m_expDim];
250 
251  m_mesh->m_faceSet.clear();
252 
253  // Scan all elements and generate list of unique faces
254  for (int i = 0, fid = 0; i < elmt.size(); ++i)
255  {
256  for (int j = 0; j < elmt[i]->GetFaceCount(); ++j)
257  {
258  pair<FaceSet::iterator,bool> testIns;
259  testIns = m_mesh->m_faceSet.insert(elmt[i]->GetFace(j));
260 
261  if (testIns.second)
262  {
263  (*(testIns.first))->m_id = fid++;
264  }
265  else
266  {
267  elmt[i]->SetFace(j,*testIns.first);
268  // Update face to element map.
269  (*(testIns.first))->m_elLink.push_back(
270  pair<ElementSharedPtr,int>(elmt[i],j));
271  }
272  }
273  }
274  }
275 
276  // Create links for 2D elements
277  for (int i = 0; i < m_mesh->m_element[2].size(); ++i)
278  {
279  vector<NodeSharedPtr> vertices = m_mesh->m_element[2][i]->GetVertexList();
280  vector<NodeSharedPtr> faceNodes;
281  vector<EdgeSharedPtr> edgeList = m_mesh->m_element[2][i]->GetEdgeList();
282  FaceSharedPtr F = boost::shared_ptr<Face>(
283  new Face(vertices, faceNodes, edgeList,
284  m_mesh->m_element[2][i]->GetConf().m_faceCurveType));
285  FaceSet::iterator it = m_mesh->m_faceSet.find(F);
286  if (it == m_mesh->m_faceSet.end())
287  {
288  cout << "Cannot find corresponding element face for 2D "
289  << "element " << i << endl;
290  abort();
291  }
292  m_mesh->m_element[2][i]->SetFaceLink(*it);
293 
294  // Update 3D element boundary map.
295  ASSERTL0((*it)->m_elLink.size() != 0,
296  "Empty element link map!");
297  ASSERTL0((*it)->m_elLink.size() == 1,
298  "Too many elements in element link map!");
299  pair<ElementSharedPtr, int> eMap = (*it)->m_elLink.at(0);
300  eMap.first->SetBoundaryLink(eMap.second, i);
301  }
302  }
303 
304  /**
305  * @brief Enumerate elements stored in Mesh::element.
306  *
307  * For all elements of equal dimension to the mesh dimension, we
308  * enumerate sequentially. All other elements in the list should be of
309  * lower dimension and have ID set by a corresponding edgeLink or
310  * faceLink (as set in #ProcessEdges or #ProcessFaces).
311  */
313  {
314  int cnt = 0;
315  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim].size(); ++i)
316  {
317  m_mesh->m_element[m_mesh->m_expDim][i]->SetId(cnt++);
318  }
319  }
320 
321  /**
322  * @brief Generate a list of composites (groups of elements) from tag
323  * IDs stored in mesh vertices/edges/faces/elements.
324  *
325  * Each element is assigned to a composite ID by an input module. First
326  * we scan the element list and generate a list of composite IDs. We
327  * then generate the composite objects and populate them with a second
328  * scan through the element list.
329  */
331  {
332  m_mesh->m_composite.clear();
333 
334  // For each element, check to see if a composite has been
335  // created. If not, create a new composite. Otherwise, add the
336  // element to the composite.
337  for (int d = 0; d <= m_mesh->m_expDim; ++d)
338  {
339  vector<ElementSharedPtr> &elmt = m_mesh->m_element[d];
340 
341  for (int i = 0; i < elmt.size(); ++i)
342  {
344  unsigned int tagid = elmt[i]->GetTagList()[0];
345 
346  it = m_mesh->m_composite.find(tagid);
347 
348  if (it == m_mesh->m_composite.end())
349  {
350  CompositeSharedPtr tmp = boost::shared_ptr<Composite>(
351  new Composite());
352  pair<CompositeMap::iterator, bool> testIns;
353  tmp->m_id = tagid;
354  tmp->m_tag = elmt[i]->GetTag();
355  testIns = m_mesh->m_composite.insert(
356  pair<unsigned int, CompositeSharedPtr>(tagid,tmp));
357  it = testIns.first;
358  }
359 
360  if (elmt[i]->GetTag() != it->second->m_tag)
361  {
362  cout << "Different types of elements in same composite!" << endl;
363  cout << " -> Composite uses " << it->second->m_tag << endl;
364  cout << " -> Element uses " << elmt[i]->GetTag() << endl;
365  cout << "Have you specified physical volumes and surfaces?" << endl;
366  }
367  it->second->m_items.push_back(elmt[i]);
368  }
369  }
370  }
371 
372  /**
373  * @brief Reorder node IDs so that prisms and tetrahedra are aligned
374  * correctly.
375  *
376  * Orientation of prism lines (i.e. a large prism which has been split
377  * into subprisms) cannot be guaranteed when elements are created
378  * one-by-one, or when periodic boundary conditions are used. This
379  * routine uses the following strategy:
380  *
381  * - Destroy the existing node numbering.
382  * - Detect a line of prisms using the PrismLines routine.
383  * - For each line, renumber node IDs consistently so that highest ID
384  * per-element corresponds to the line of collapsed coordinate
385  * points.
386  * - Recreate each prism in the line using the new ordering, and apply
387  * the existing OrientPrism routine to orient nodes accordingly.
388  * - When all prism lines are processed, recreate all tetrahedra using
389  * the existing orientation code.
390  * - Finally renumber any other nodes (i.e. those belonging to
391  * hexahedra).
392  *
393  * The last step is to eliminate duplicate edges/faces and reenumerate.
394  *
395  * NOTE: This routine does not copy high-order information yet!
396  */
398  {
399  // Loop over elements and extract any that are prisms.
400  int i, j, k;
401 
402  if (m_mesh->m_expDim < 3)
403  {
404  return;
405  }
406 
407  map<int, int> lines;
408  set<int> prismsDone, tetsDone;
409  PerMap::iterator pIt;
410 
411  // Compile list of prisms and tets.
412  for (i = 0; i < m_mesh->m_element[3].size(); ++i)
413  {
414  ElementSharedPtr el = m_mesh->m_element[3][i];
415 
416  if (el->GetConf().m_e == LibUtilities::ePrism)
417  {
418  prismsDone.insert(i);
419  }
420  else if (el->GetConf().m_e == LibUtilities::eTetrahedron)
421  {
422  tetsDone.insert(i);
423  }
424  }
425 
426  // Destroy existing node numbering.
428  for (it = m_mesh->m_vertexSet.begin(); it != m_mesh->m_vertexSet.end(); ++it)
429  {
430  (*it)->m_id = -1;
431  }
432 
433  // Counter for new node IDs.
434  int nodeId = 0;
435  int prismTris[2][3] = {{0,1,4}, {3,2,5}};
436 
437  // facesDone tracks face IDs inside prisms which have already been
438  // aligned.
439  boost::unordered_set<int> facesDone;
441 
442  // Loop over prisms until we've found all lines of prisms.
443  while (prismsDone.size() > 0)
444  {
445  vector<ElementSharedPtr> line;
446 
447  // Call PrismLines to identify all prisms connected to
448  // prismDone.begin() and place them in line[].
449  PrismLines(*prismsDone.begin(), perFaces, prismsDone, line);
450 
451  // Loop over each prism, figure out which line of vertices
452  // contains the vertex with highest ID.
453  for (i = 0; i < line.size(); ++i)
454  {
455  // Copy tags and nodes from existing element.
456  vector<int> tags = line[i]->GetTagList();
457  vector<NodeSharedPtr> nodes = line[i]->GetVertexList();
458 
459  // See if either face of this prism has been renumbered
460  // already.
461  FaceSharedPtr f[2] = {
462  line[i]->GetFace(1), line[i]->GetFace(3)
463  };
464 
465  fIt[0] = facesDone.find(f[0]->m_id);
466  fIt[1] = facesDone.find(f[1]->m_id);
467 
468  // See if either of these faces is periodic. If it is, then
469  // assign ids accordingly.
470  for (j = 0; j < 2; ++j)
471  {
472  pIt = perFaces.find(f[j]->m_id);
473 
474  if (pIt == perFaces.end())
475  {
476  continue;
477  }
478 
479  fIt2 = facesDone.find(pIt->second.first->m_id);
480 
481  if (fIt[j] == facesDone.end() &&
482  fIt2 != facesDone.end())
483  {
484  fIt[j] = fIt2;
485  }
486  }
487 
488  if (fIt[0] != facesDone.end() &&
489  fIt[1] != facesDone.end())
490  {
491  // Should not be the case that both faces have already
492  // been renumbered.
493  ASSERTL0(false, "Renumbering error!");
494  }
495  else if (fIt[0] == facesDone.end() &&
496  fIt[1] == facesDone.end())
497  {
498  // Renumber both faces.
499  for (j = 0; j < 2; ++j)
500  {
501  for (k = 0; k < 3; ++k)
502  {
503  NodeSharedPtr n = nodes[prismTris[j][k]];
504  if (n->m_id == -1)
505  {
506  n->m_id = nodeId++;
507  }
508  }
509  }
510 
511  facesDone.insert(f[0]->m_id);
512  facesDone.insert(f[1]->m_id);
513  }
514  else
515  {
516  // Renumber face. t identifies the face not yet
517  // numbered, o identifies the other face.
518  int t = fIt[0] == facesDone.end() ? 0 : 1;
519  int o = (t+1) % 2;
520  ASSERTL1(fIt[o] != facesDone.end(),"Renumbering error");
521 
522  // Determine which of the three vertices on the 'other'
523  // face corresponds to the highest ID - this signifies
524  // the singular point of the line of prisms.
525  int tmp1[3] = {
526  nodes[prismTris[o][0]]->m_id,
527  nodes[prismTris[o][1]]->m_id,
528  nodes[prismTris[o][2]]->m_id
529  };
530  int tmp2[3] = {0,1,2};
531 
532  if (tmp1[0] > tmp1[1])
533  {
534  swap(tmp1[0], tmp1[1]);
535  swap(tmp2[0], tmp2[1]);
536  }
537 
538  if (tmp1[1] > tmp1[2])
539  {
540  swap(tmp1[1], tmp1[2]);
541  swap(tmp2[1], tmp2[2]);
542  }
543 
544  if (tmp1[0] > tmp1[2])
545  {
546  swap(tmp1[0], tmp1[2]);
547  swap(tmp2[0], tmp2[2]);
548  }
549 
550  // Renumber this face so that highest ID matches.
551  for (j = 0; j < 3; ++j)
552  {
553  NodeSharedPtr n = nodes[prismTris[t][tmp2[j]]];
554  if (n->m_id == -1)
555  {
556  n->m_id = nodeId++;
557  }
558  }
559 
560  facesDone.insert(f[t]->m_id);
561  }
562 
563  for (j = 0; j < 6; ++j)
564  {
565  ASSERTL1(nodes[j]->m_id != -1, "Renumbering error");
566  }
567 
568  // Recreate prism with the new ordering.
569  ElmtConfig conf(LibUtilities::ePrism, 1, false, false, true);
571  LibUtilities::ePrism, conf, nodes, tags);
572 
573  // Replace old prism.
574  m_mesh->m_element[3][line[i]->GetId()] = el;
575  }
576  }
577 
578  // Loop over periodic faces, enumerate vertices.
579  for (pIt = perFaces.begin(); pIt != perFaces.end(); ++pIt)
580  {
581  FaceSharedPtr f2 = pIt->second.first;
582  FaceSharedPtr f1 = perFaces[f2->m_id].first;
583  vector<int> perVerts = pIt->second.second;
584  int nVerts = perVerts.size();
585 
586  // Number periodic vertices first.
587  for (j = 0; j < nVerts; ++j)
588  {
589  NodeSharedPtr n1 = f1->m_vertexList[j];
590  NodeSharedPtr n2 = f2->m_vertexList[perVerts[j]];
591 
592  if (n1->m_id == -1 && n2->m_id == -1)
593  {
594  n1->m_id = nodeId++;
595  n2->m_id = nodeId++;
596  }
597  else if (n1->m_id != -1 && n2->m_id != -1)
598  {
599  continue;
600  }
601  else
602  {
603  ASSERTL0(false, "Periodic face renumbering error");
604  }
605  }
606  }
607 
608  // Recreate tets.
609  set<int>::iterator it2;
610  for (it2 = tetsDone.begin(); it2 != tetsDone.end(); ++it2)
611  {
612  ElementSharedPtr el = m_mesh->m_element[3][*it2];
613  vector<NodeSharedPtr> nodes = el->GetVertexList();
614  vector<int> tags = el->GetTagList();
615 
616  for (i = 0; i < 4; ++i)
617  {
618  if (nodes[i]->m_id == -1)
619  {
620  nodes[i]->m_id = nodeId++;
621  }
622  }
623 
624  // Recreate tet.
625  ElmtConfig conf(LibUtilities::eTetrahedron, 1, false, false, true);
626  m_mesh->m_element[3][*it2] = GetElementFactory().CreateInstance(
627  LibUtilities::eTetrahedron, conf, nodes, tags);
628  }
629 
630  // Enumerate rest of vertices.
631  for (it = m_mesh->m_vertexSet.begin(); it != m_mesh->m_vertexSet.end(); ++it)
632  {
633  if ((*it)->m_id == -1)
634  {
635  (*it)->m_id = nodeId++;
636  }
637  }
638 
639  ProcessEdges ();
640  ProcessFaces ();
641  ProcessElements();
642  }
643 
644  void Module::PrismLines(int prism,
645  PerMap &perFaces,
646  set<int> &prismsDone,
647  vector<ElementSharedPtr> &line)
648  {
649  int i;
650  set<int>::iterator it = prismsDone.find(prism);
651  PerMap::iterator it2;
652 
653  if (it == prismsDone.end())
654  {
655  return;
656  }
657 
658  // Remove this prism from the list.
659  prismsDone.erase(it);
660  line.push_back(m_mesh->m_element[3][prism]);
661 
662  // Now find prisms connected to this one through a triangular face.
663  for (i = 1; i <= 3; i += 2)
664  {
665  FaceSharedPtr f = m_mesh->m_element[3][prism]->GetFace(i);
666  int nextId;
667 
668  // See if this face is periodic.
669  it2 = perFaces.find(f->m_id);
670 
671  if (it2 != perFaces.end())
672  {
673  int id2 = it2->second.first->m_id;
674  nextId = it2->second.first->m_elLink[0].first->GetId();
675  perFaces.erase(it2);
676  perFaces.erase(id2);
677  PrismLines(nextId, perFaces, prismsDone, line);
678  }
679 
680  // Nothing else connected to this face.
681  if (f->m_elLink.size() == 1)
682  {
683  continue;
684  }
685 
686  nextId = f->m_elLink[0].first->GetId();
687  if (nextId == m_mesh->m_element[3][prism]->GetId())
688  {
689  nextId = f->m_elLink[1].first->GetId();
690  }
691 
692  PrismLines(nextId, perFaces, prismsDone, line);
693  }
694  }
695 
696  /**
697  * @brief Register a configuration option with a module.
698  */
699  void Module::RegisterConfig(string key, string val)
700  {
702  if (it == m_config.end())
703  {
704  cerr << "WARNING: Unrecognised config option " << key
705  << ", proceeding anyway." << endl;
706  }
707 
708  it->second.beenSet = true;
709 
710  if (it->second.isBool)
711  {
712  it->second.value = "1";
713  }
714  else
715  {
716  it->second.value = val;
717  }
718  }
719 
720  /**
721  * @brief Print out all configuration options for a module.
722  */
723  void Module::PrintConfig()
724  {
726 
727  if (m_config.size() == 0)
728  {
729  cerr << "No configuration options for this module." << endl;
730  return;
731  }
732 
733  for (it = m_config.begin(); it != m_config.end(); ++it)
734  {
735  cerr << setw(10) << it->first << ": " << it->second.desc
736  << endl;
737  }
738  }
739 
740  /**
741  * @brief Sets default configuration options for those which have not
742  * been set.
743  */
744  void Module::SetDefaults()
745  {
747 
748  for (it = m_config.begin(); it != m_config.end(); ++it)
749  {
750  if (!it->second.beenSet)
751  {
752  it->second.value = it->second.defValue;
753  }
754  }
755  }
756 
757  /**
758  * @brief Print a brief summary of information.
759  */
761  {
762  // Compute the number of full-dimensional elements and boundary
763  // elements.
764  cerr << "Expansion dimension is " << m_mesh->m_expDim << endl;
765  cerr << "Space dimension is " << m_mesh->m_spaceDim << endl;
766  cerr << "Read " << m_mesh->m_node.size() << " nodes" << endl;
767  cerr << "Read " << m_mesh->GetNumElements() << " "
768  << m_mesh->m_expDim << "-D elements" << endl;
769  cerr << "Read " << m_mesh->GetNumBndryElements()
770  << " boundary elements" << endl;
771  }
772  }
773 }