libMesh
exodusII_io_helper.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2019 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 #include "libmesh/exodusII_io_helper.h"
20 
21 
22 #ifdef LIBMESH_HAVE_EXODUS_API
23 
24 #include <algorithm>
25 #include <sstream>
26 #include <cstdlib> // std::strtol
27 #include <unordered_map>
28 
29 #include "libmesh/boundary_info.h"
30 #include "libmesh/enum_elem_type.h"
31 #include "libmesh/elem.h"
32 #include "libmesh/system.h"
33 #include "libmesh/numeric_vector.h"
34 #include "libmesh/enum_to_string.h"
35 #include "libmesh/enum_elem_type.h"
36 #include "libmesh/int_range.h"
37 #include "libmesh/utility.h"
38 
39 #ifdef DEBUG
40 #include "libmesh/mesh_tools.h" // for elem_types warning
41 #endif
42 
43 // Anonymous namespace for file local data
44 namespace
45 {
46 
47 // File scope constant node/edge/face mapping arrays.
48 // 2D inverse face map definitions.
49 // These take a libMesh ID and turn it into an Exodus ID
50 const std::vector<int> trishell3_inverse_edge_map = {3, 4, 5};
51 const std::vector<int> quadshell4_inverse_edge_map = {3, 4, 5, 6};
52 
53 // 3D node map definitions
54 // The hex27 appears to be the only element without an identity mapping between its
55 // node numbering and libmesh's.
56 const std::vector<int> hex27_node_map = {
57  // Vertex and mid-edge nodes
58  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
59  // Mid-face nodes and centroid
60  21, 25, 24, 26, 23, 22, 20};
61 //20 21 22 23 24 25 26 // LibMesh indices
62 
63 const std::vector<int> hex27_inverse_node_map = {
64  // Vertex and mid-edge nodes
65  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
66  // Mid-face nodes and centroid
67  26, 20, 25, 24, 22, 21, 23};
68 //20 21 22 23 24 25 26
69 
70 // 3D face map definitions
71 const std::vector<int> tet_face_map = {1, 2, 3, 0};
72 const std::vector<int> hex_face_map = {1, 2, 3, 4, 0, 5};
73 const std::vector<int> prism_face_map = {1, 2, 3, 0, 4};
74 
75 // These take a libMesh ID and turn it into an Exodus ID
76 const std::vector<int> tet_inverse_face_map = {4, 1, 2, 3};
77 const std::vector<int> hex_inverse_face_map = {5, 1, 2, 3, 4, 6};
78 const std::vector<int> prism_inverse_face_map = {4, 1, 2, 3, 5};
79 
80 // 3D element edge maps. Map 0-based Exodus id -> libMesh id.
81 const std::vector<int> hex_edge_map =
82  {0,1,2,3,8,9,10,11,4,5,7,6};
83 
84 // 3D inverse element edge maps. Map libmesh edge ids to 1-based Exodus edge ids.
85 const std::vector<int> hex_inverse_edge_map =
86  {1,2,3,4,9,10,12,11,5,6,7,8};
87 
88 } // end anonymous namespace
89 
90 
91 
92 namespace libMesh
93 {
94 
95 // ExodusII_IO_Helper::Conversion static data
96 const int ExodusII_IO_Helper::Conversion::invalid_id = std::numeric_limits<int>::max();
97 
99  bool v,
100  bool run_only_on_proc0,
101  bool single_precision) :
102  ParallelObject(parent),
103  ex_id(0),
104  ex_err(0),
105  num_dim(0),
106  num_global_vars(0),
107  num_sideset_vars(0),
108  num_nodes(0),
109  num_elem(0),
110  num_elem_blk(0),
111  num_edge(0),
112  num_edge_blk(0),
113  num_node_sets(0),
114  num_side_sets(0),
115  num_elem_this_blk(0),
116  num_nodes_per_elem(0),
117  num_attr(0),
118  num_elem_all_sidesets(0),
119  num_time_steps(0),
120  num_nodal_vars(0),
121  num_elem_vars(0),
122  verbose(v),
123  opened_for_writing(false),
124  opened_for_reading(false),
125  _run_only_on_proc0(run_only_on_proc0),
126  _elem_vars_initialized(false),
127  _global_vars_initialized(false),
128  _nodal_vars_initialized(false),
129  _use_mesh_dimension_instead_of_spatial_dimension(false),
130  _write_as_dimension(0),
131  _single_precision(single_precision)
132 {
133  title.resize(MAX_LINE_LENGTH+1);
134  elem_type.resize(MAX_STR_LENGTH);
137 }
138 
139 
140 
142 
143 
144 
145 // Initialization function for conversion_map object
147 {
148  {
149  auto & conv = conversion_map[NODEELEM];
150  conv.libmesh_type = NODEELEM;
151  conv.exodus_type = "SPHERE";
152  }
153  {
154  auto & conv = conversion_map[EDGE2];
155  conv.libmesh_type = EDGE2;
156  conv.exodus_type = "EDGE2";
157  }
158  {
159  auto & conv = conversion_map[EDGE3];
160  conv.libmesh_type = EDGE3;
161  conv.exodus_type = "EDGE3";
162  }
163  {
164  auto & conv = conversion_map[QUAD4];
165  conv.libmesh_type = QUAD4;
166  conv.exodus_type = "QUAD4";
167  }
168  {
169  auto & conv = conversion_map[QUADSHELL4];
170  conv.inverse_side_map = &quadshell4_inverse_edge_map;
171  conv.shellface_index_offset = 2;
172  conv.libmesh_type = QUADSHELL4;
173  conv.exodus_type = "SHELL4";
174  }
175  {
176  auto & conv = conversion_map[QUAD8];
177  conv.libmesh_type = QUAD8;
178  conv.exodus_type = "QUAD8";
179  }
180  {
181  auto & conv = conversion_map[QUADSHELL8];
182  conv.inverse_side_map = &quadshell4_inverse_edge_map;
183  conv.shellface_index_offset = 2;
184  conv.libmesh_type = QUADSHELL8;
185  conv.exodus_type = "SHELL8";
186  }
187  {
188  auto & conv = conversion_map[QUAD9];
189  conv.libmesh_type = QUAD9;
190  conv.exodus_type = "QUAD9";
191  }
192  {
193  auto & conv = conversion_map[TRI3];
194  conv.libmesh_type = TRI3;
195  conv.exodus_type = "TRI3";
196  }
197  {
198  auto & conv = conversion_map[TRISHELL3];
199  conv.inverse_side_map = &trishell3_inverse_edge_map;
200  conv.shellface_index_offset = 2;
201  conv.libmesh_type = TRISHELL3;
202  conv.exodus_type = "TRISHELL3";
203  }
204  {
205  auto & conv = conversion_map[TRI3SUBDIVISION];
206  conv.libmesh_type = TRI3SUBDIVISION;
207  conv.exodus_type = "TRI3";
208  }
209  {
210  auto & conv = conversion_map[TRI6];
211  conv.libmesh_type = TRI6;
212  conv.exodus_type = "TRI6";
213  }
214  {
215  auto & conv = conversion_map[HEX8];
216  conv.side_map = &hex_face_map;
217  conv.inverse_side_map = &hex_inverse_face_map;
218  conv.libmesh_type = HEX8;
219  conv.exodus_type = "HEX8";
220  }
221  {
222  auto & conv = conversion_map[HEX20];
223  conv.side_map = &hex_face_map;
224  conv.inverse_side_map = &hex_inverse_face_map;
225  conv.libmesh_type = HEX20;
226  conv.exodus_type = "HEX20";
227  }
228  {
229  auto & conv = conversion_map[HEX27];
230  conv.node_map = &hex27_node_map;
231  conv.inverse_node_map = &hex27_inverse_node_map;
232  conv.side_map = &hex_face_map;
233  conv.inverse_side_map = &hex_inverse_face_map;
234  conv.libmesh_type = HEX27;
235  conv.exodus_type = "HEX27";
236  }
237  {
238  auto & conv = conversion_map[TET4];
239  conv.side_map = &tet_face_map;
240  conv.inverse_side_map = &tet_inverse_face_map;
241  conv.libmesh_type = TET4;
242  conv.exodus_type = "TETRA4";
243  }
244  {
245  auto & conv = conversion_map[TET10];
246  conv.side_map = &tet_face_map;
247  conv.inverse_side_map = &tet_inverse_face_map;
248  conv.libmesh_type = TET10;
249  conv.exodus_type = "TETRA10";
250  }
251  {
252  auto & conv = conversion_map[PRISM6];
253  conv.side_map = &prism_face_map;
254  conv.inverse_side_map = &prism_inverse_face_map;
255  conv.libmesh_type = PRISM6;
256  conv.exodus_type = "WEDGE";
257  }
258  {
259  auto & conv = conversion_map[PRISM15];
260  conv.side_map = &prism_face_map;
261  conv.inverse_side_map = &prism_inverse_face_map;
262  conv.libmesh_type = PRISM15;
263  conv.exodus_type = "WEDGE15";
264  }
265  {
266  auto & conv = conversion_map[PRISM18];
267  conv.side_map = &prism_face_map;
268  conv.inverse_side_map = &prism_inverse_face_map;
269  conv.libmesh_type = PRISM18;
270  conv.exodus_type = "WEDGE18";
271  }
272  {
273  auto & conv = conversion_map[PYRAMID5];
274  conv.libmesh_type = PYRAMID5;
275  conv.exodus_type = "PYRAMID5";
276  }
277  {
278  auto & conv = conversion_map[PYRAMID13];
279  conv.libmesh_type = PYRAMID13;
280  conv.exodus_type = "PYRAMID13";
281  }
282  {
283  auto & conv = conversion_map[PYRAMID14];
284  conv.libmesh_type = PYRAMID14;
285  conv.exodus_type = "PYRAMID14";
286  }
287 }
288 
289 
290 
291 // This function initializes the element_equivalence_map the first time it
292 // is called, and returns early all other times.
294 {
295  // We use an ExodusII SPHERE element to represent a NodeElem
296  element_equivalence_map["SPHERE"] = NODEELEM;
297 
298  // EDGE2 equivalences
299  element_equivalence_map["EDGE"] = EDGE2;
300  element_equivalence_map["EDGE2"] = EDGE2;
301  element_equivalence_map["TRUSS"] = EDGE2;
302  element_equivalence_map["BEAM"] = EDGE2;
304  element_equivalence_map["TRUSS2"] = EDGE2;
305  element_equivalence_map["BEAM2"] = EDGE2;
306  element_equivalence_map["BAR2"] = EDGE2;
307 
308  // EDGE3 equivalences
309  element_equivalence_map["EDGE3"] = EDGE3;
310  element_equivalence_map["TRUSS3"] = EDGE3;
311  element_equivalence_map["BEAM3"] = EDGE3;
312  element_equivalence_map["BAR3"] = EDGE3;
313 
314  // QUAD4 equivalences
315  element_equivalence_map["QUAD"] = QUAD4;
316  element_equivalence_map["QUAD4"] = QUAD4;
317 
318  // QUADSHELL4 equivalences
321 
322  // QUAD8 equivalences
323  element_equivalence_map["QUAD8"] = QUAD8;
324 
325  // QUADSHELL8 equivalences
327 
328  // QUAD9 equivalences
329  element_equivalence_map["QUAD9"] = QUAD9;
330  // element_equivalence_map["SHELL9"] = QUAD9;
331 
332  // TRI3 equivalences
333  element_equivalence_map["TRI"] = TRI3;
334  element_equivalence_map["TRI3"] = TRI3;
335  element_equivalence_map["TRIANGLE"] = TRI3;
336 
337  // TRISHELL3 equivalences
338  element_equivalence_map["TRISHELL"] = TRISHELL3;
339  element_equivalence_map["TRISHELL3"] = TRISHELL3;
340 
341  // TRI6 equivalences
342  element_equivalence_map["TRI6"] = TRI6;
343  // element_equivalence_map["TRISHELL6"] = TRI6;
344 
345  // HEX8 equivalences
346  element_equivalence_map["HEX"] = HEX8;
347  element_equivalence_map["HEX8"] = HEX8;
348 
349  // HEX20 equivalences
350  element_equivalence_map["HEX20"] = HEX20;
351 
352  // HEX27 equivalences
353  element_equivalence_map["HEX27"] = HEX27;
354 
355  // TET4 equivalences
356  element_equivalence_map["TETRA"] = TET4;
357  element_equivalence_map["TETRA4"] = TET4;
358 
359  // TET10 equivalences
360  element_equivalence_map["TETRA10"] = TET10;
361 
362  // PRISM6 equivalences
363  element_equivalence_map["WEDGE"] = PRISM6;
364 
365  // PRISM15 equivalences
366  element_equivalence_map["WEDGE15"] = PRISM15;
367 
368  // PRISM18 equivalences
369  element_equivalence_map["WEDGE18"] = PRISM18;
370 
371  // PYRAMID5 equivalences
372  element_equivalence_map["PYRAMID"] = PYRAMID5;
373  element_equivalence_map["PYRAMID5"] = PYRAMID5;
374 
375  // PYRAMID13 equivalences
376  element_equivalence_map["PYRAMID13"] = PYRAMID13;
377 
378  // PYRAMID14 equivalences
379  element_equivalence_map["PYRAMID14"] = PYRAMID14;
380 }
381 
384 {
385  return libmesh_map_find(conversion_map, type);
386 }
387 
389 ExodusII_IO_Helper::get_conversion(std::string type_str) const
390 {
391  // Do only upper-case comparisons
392  std::transform(type_str.begin(), type_str.end(), type_str.begin(), ::toupper);
393  return get_conversion (libmesh_map_find(element_equivalence_map, type_str));
394 }
395 
397 {
398  return elem_type.data();
399 }
400 
401 
402 
403 void ExodusII_IO_Helper::message(const std::string & msg)
404 {
405  if (verbose) libMesh::out << msg << std::endl;
406 }
407 
408 
409 
410 void ExodusII_IO_Helper::message(const std::string & msg, int i)
411 {
412  if (verbose) libMesh::out << msg << i << "." << std::endl;
413 }
414 
415 
417 MappedOutputVector(const std::vector<Real> & our_data_in,
418  bool single_precision_in)
419  : our_data(our_data_in),
420  single_precision(single_precision_in)
421 {
422  if (single_precision)
423  {
424  if (sizeof(Real) != sizeof(float))
425  float_vec.assign(our_data.begin(), our_data.end());
426  }
427 
428  else if (sizeof(Real) != sizeof(double))
429  double_vec.assign(our_data.begin(), our_data.end());
430 }
431 
432 void *
434 {
435  if (single_precision)
436  {
437  if (sizeof(Real) != sizeof(float))
438  return static_cast<void*>(float_vec.data());
439  }
440 
441  else if (sizeof(Real) != sizeof(double))
442  return static_cast<void*>(double_vec.data());
443 
444  // Otherwise return a (suitably casted) pointer to the original underlying data.
445  return const_cast<void *>(static_cast<const void *>(our_data.data()));
446 }
447 
449 MappedInputVector(std::vector<Real> & our_data_in,
450  bool single_precision_in)
451  : our_data(our_data_in),
452  single_precision(single_precision_in)
453 {
454  // Allocate temporary space to store enough floats/doubles, if required.
455  if (single_precision)
456  {
457  if (sizeof(Real) != sizeof(float))
458  float_vec.resize(our_data.size());
459  }
460  else if (sizeof(Real) != sizeof(double))
461  double_vec.resize(our_data.size());
462 }
463 
466 {
467  if (single_precision)
468  {
469  if (sizeof(Real) != sizeof(float))
470  our_data.assign(float_vec.begin(), float_vec.end());
471  }
472  else if (sizeof(Real) != sizeof(double))
473  our_data.assign(double_vec.begin(), double_vec.end());
474 }
475 
476 void *
478 {
479  if (single_precision)
480  {
481  if (sizeof(Real) != sizeof(float))
482  return static_cast<void*>(float_vec.data());
483  }
484 
485  else if (sizeof(Real) != sizeof(double))
486  return static_cast<void*>(double_vec.data());
487 
488  // Otherwise return a (suitably casted) pointer to the original underlying data.
489  return static_cast<void *>(our_data.data());
490 }
491 
492 void ExodusII_IO_Helper::open(const char * filename, bool read_only)
493 {
494  // Version of Exodus you are using
495  float ex_version = 0.;
496 
497  int comp_ws = 0;
498 
499  if (_single_precision)
500  comp_ws = cast_int<int>(sizeof(float));
501 
502  // Fall back on double precision when necessary since ExodusII
503  // doesn't seem to support long double
504  else
505  comp_ws = cast_int<int>(std::min(sizeof(Real), sizeof(double)));
506 
507  // Word size in bytes of the floating point data as they are stored
508  // in the ExodusII file. "If this argument is 0, the word size of the
509  // floating point data already stored in the file is returned"
510  int io_ws = 0;
511 
512  ex_id = exII::ex_open(filename,
513  read_only ? EX_READ : EX_WRITE,
514  &comp_ws,
515  &io_ws,
516  &ex_version);
517 
518  std::string err_msg = std::string("Error opening ExodusII mesh file: ") + std::string(filename);
519  EX_CHECK_ERR(ex_id, err_msg);
520  if (verbose) libMesh::out << "File opened successfully." << std::endl;
521 
522  if (read_only)
523  opened_for_reading = true;
524  else
525  opened_for_writing = true;
526 
527  current_filename = std::string(filename);
528 }
529 
530 
531 
533 {
534  // Read init params using newer API that reads into a struct. For
535  // backwards compatibility, assign local member values from struct
536  // afterwards. Note: using the new API allows us to automatically
537  // read edge and face block/set information if it's present in the
538  // file.
539  exII::ex_init_params params = {};
540  ex_err = exII::ex_get_init_ext(ex_id, &params);
541  EX_CHECK_ERR(ex_err, "Error retrieving header info.");
542 
543  // "title" has MAX_LINE_LENGTH+1 characters, but the last one is reserved
544  // for null termination so we only copy MAX_LINE_LENGTH chars.
545  title.assign(params.title, params.title + MAX_LINE_LENGTH);
546  num_dim = params.num_dim;
547  num_nodes = params.num_nodes;
548  num_elem = params.num_elem;
549  num_elem_blk = params.num_elem_blk;
550  num_node_sets = params.num_node_sets;
551  num_side_sets = params.num_side_sets;
552  num_edge_blk = params.num_edge_blk;
553  num_edge = params.num_edge;
554 
555  this->read_num_time_steps();
556 
557  ex_err = exII::ex_get_var_param(ex_id, "n", &num_nodal_vars);
558  EX_CHECK_ERR(ex_err, "Error reading number of nodal variables.");
559 
560  ex_err = exII::ex_get_var_param(ex_id, "e", &num_elem_vars);
561  EX_CHECK_ERR(ex_err, "Error reading number of elemental variables.");
562 
563  ex_err = exII::ex_get_var_param(ex_id, "g", &num_global_vars);
564  EX_CHECK_ERR(ex_err, "Error reading number of global variables.");
565 
566  ex_err = exII::ex_get_var_param(ex_id, "s", &num_sideset_vars);
567  EX_CHECK_ERR(ex_err, "Error reading number of sideset variables.");
568 
569  message("Exodus header info retrieved successfully.");
570 }
571 
572 
573 
574 
576 {
577  // The QA records are four MAX_STR_LENGTH-byte character strings.
578  int num_qa_rec =
579  this->inquire(exII::EX_INQ_QA, "Error retrieving number of QA records");
580 
581  if (verbose)
582  libMesh::out << "Found "
583  << num_qa_rec
584  << " QA record(s) in the Exodus file."
585  << std::endl;
586 
587  if (num_qa_rec > 0)
588  {
589  // How to dynamically allocate an array of fixed-size char * arrays in C++.
590  // http://stackoverflow.com/questions/8529359/creating-a-dynamic-sized-array-of-fixed-sized-int-arrays-in-c
591  typedef char * inner_array_t[4];
592  inner_array_t * qa_record = new inner_array_t[num_qa_rec];
593 
594  for (int i=0; i<num_qa_rec; i++)
595  for (int j=0; j<4; j++)
596  qa_record[i][j] = new char[MAX_STR_LENGTH+1];
597 
598  ex_err = exII::ex_get_qa (ex_id, qa_record);
599  EX_CHECK_ERR(ex_err, "Error reading the QA records.");
600 
601  // Print the QA records
602  if (verbose)
603  {
604  for (int i=0; i<num_qa_rec; i++)
605  {
606  libMesh::out << "QA Record: " << i << std::endl;
607  for (int j=0; j<4; j++)
608  libMesh::out << qa_record[i][j] << std::endl;
609  }
610  }
611 
612 
613  // Clean up dynamically-allocated memory
614  for (int i=0; i<num_qa_rec; i++)
615  for (int j=0; j<4; j++)
616  delete [] qa_record[i][j];
617 
618  delete [] qa_record;
619  }
620 }
621 
622 
623 
624 
626 {
627  if (verbose)
628  libMesh::out << "Title: \t" << title.data() << std::endl
629  << "Mesh Dimension: \t" << num_dim << std::endl
630  << "Number of Nodes: \t" << num_nodes << std::endl
631  << "Number of elements: \t" << num_elem << std::endl
632  << "Number of elt blocks: \t" << num_elem_blk << std::endl
633  << "Number of node sets: \t" << num_node_sets << std::endl
634  << "Number of side sets: \t" << num_side_sets << std::endl;
635 }
636 
637 
638 
640 {
641  x.resize(num_nodes);
642  y.resize(num_nodes);
643  z.resize(num_nodes);
644 
645  if (num_nodes)
646  {
647  ex_err = exII::ex_get_coord
648  (ex_id,
652 
653  EX_CHECK_ERR(ex_err, "Error retrieving nodal data.");
654  message("Nodal data retrieved successfully.");
655  }
656 }
657 
658 
659 
661 {
662  node_num_map.resize(num_nodes);
663 
664  // Note: we cannot use the exII::ex_get_num_map() here because it
665  // (apparently) does not behave like ex_get_node_num_map() when
666  // there is no node number map in the file: it throws an error
667  // instead of returning a default identity array (1,2,3,...).
668  ex_err = exII::ex_get_node_num_map
669  (ex_id, node_num_map.empty() ? nullptr : node_num_map.data());
670 
671  EX_CHECK_ERR(ex_err, "Error retrieving nodal number map.");
672  message("Nodal numbering map retrieved successfully.");
673 
674  if (verbose)
675  {
676  libMesh::out << "[" << this->processor_id() << "] node_num_map[i] = ";
677  for (unsigned int i=0; i<static_cast<unsigned int>(std::min(10, num_nodes-1)); ++i)
678  libMesh::out << node_num_map[i] << ", ";
679  libMesh::out << "... " << node_num_map.back() << std::endl;
680  }
681 }
682 
683 
684 void ExodusII_IO_Helper::print_nodes(std::ostream & out_stream)
685 {
686  for (int i=0; i<num_nodes; i++)
687  out_stream << "(" << x[i] << ", " << y[i] << ", " << z[i] << ")" << std::endl;
688 }
689 
690 
691 
693 {
694  if (num_elem_blk)
695  {
696  // Read all element block IDs.
697  block_ids.resize(num_elem_blk);
698  ex_err = exII::ex_get_ids(ex_id,
699  exII::EX_ELEM_BLOCK,
700  block_ids.data());
701 
702  EX_CHECK_ERR(ex_err, "Error getting block IDs.");
703  message("All block IDs retrieved successfully.");
704 
705  char name_buffer[MAX_STR_LENGTH+1];
706  for (int i=0; i<num_elem_blk; ++i)
707  {
708  ex_err = exII::ex_get_name(ex_id, exII::EX_ELEM_BLOCK,
709  block_ids[i], name_buffer);
710  EX_CHECK_ERR(ex_err, "Error getting block name.");
711  id_to_block_names[block_ids[i]] = name_buffer;
712  }
713  message("All block names retrieved successfully.");
714  }
715 
716  if (num_edge_blk)
717  {
718  // Read all edge block IDs.
720  ex_err = exII::ex_get_ids(ex_id,
721  exII::EX_EDGE_BLOCK,
722  edge_block_ids.data());
723 
724  EX_CHECK_ERR(ex_err, "Error getting edge block IDs.");
725  message("All edge block IDs retrieved successfully.");
726 
727  // Read in edge block names
728  char name_buffer[MAX_STR_LENGTH+1];
729  for (int i=0; i<num_edge_blk; ++i)
730  {
731  ex_err = exII::ex_get_name(ex_id, exII::EX_EDGE_BLOCK,
732  edge_block_ids[i], name_buffer);
733  EX_CHECK_ERR(ex_err, "Error getting block name.");
734  id_to_edge_block_names[edge_block_ids[i]] = name_buffer;
735  }
736  message("All edge block names retrieved successfully.");
737  }
738 }
739 
740 
741 
743 {
744  libmesh_assert_less (index, block_ids.size());
745 
746  return block_ids[index];
747 }
748 
749 
750 
752 {
753  libmesh_assert_less (index, block_ids.size());
754 
755  return id_to_block_names[block_ids[index]];
756 }
757 
758 
759 
761 {
762  libmesh_assert_less (index, ss_ids.size());
763 
764  return ss_ids[index];
765 }
766 
767 
768 
770 {
771  libmesh_assert_less (index, ss_ids.size());
772 
773  return id_to_ss_names[ss_ids[index]];
774 }
775 
776 
777 
779 {
780  libmesh_assert_less (index, nodeset_ids.size());
781 
782  return nodeset_ids[index];
783 }
784 
785 
786 
788 {
789  libmesh_assert_less (index, nodeset_ids.size());
790 
791  return id_to_ns_names[nodeset_ids[index]];
792 }
793 
794 
795 
796 
798 {
799  libmesh_assert_less (block, block_ids.size());
800 
801  // Unlike the other "extended" APIs, this one does not use a parameter struct.
802  int num_edges_per_elem = 0;
803  int num_faces_per_elem = 0;
804  ex_err = exII::ex_get_block(ex_id,
805  exII::EX_ELEM_BLOCK,
806  block_ids[block],
807  elem_type.data(),
810  &num_edges_per_elem, // 0 or -1 if no "extended" block info
811  &num_faces_per_elem, // 0 or -1 if no "extended" block info
812  &num_attr);
813 
814  EX_CHECK_ERR(ex_err, "Error getting block info.");
815  message("Info retrieved successfully for block: ", block);
816 
817  // Warn that we don't currently support reading blocks with extended info.
818  // Note: the docs say -1 will be returned for this but I found that it was
819  // actually 0, so not sure which it will be in general.
820  if (!(num_edges_per_elem == 0) && !(num_edges_per_elem == -1))
821  libmesh_warning("Exodus files with extended edge connectivity not currently supported.");
822  if (!(num_faces_per_elem == 0) && !(num_faces_per_elem == -1))
823  libmesh_warning("Exodus files with extended face connectivity not currently supported.");
824 
825  if (verbose)
826  libMesh::out << "Read a block of " << num_elem_this_blk
827  << " " << elem_type.data() << "(s)"
828  << " having " << num_nodes_per_elem
829  << " nodes per element." << std::endl;
830 
831  // Read in the connectivity of the elements of this block,
832  // watching out for the case where we actually have no
833  // elements in this block (possible with parallel files)
835 
836  if (!connect.empty())
837  {
838  ex_err = exII::ex_get_conn(ex_id,
839  exII::EX_ELEM_BLOCK,
840  block_ids[block],
841  connect.data(), // node_conn
842  nullptr, // elem_edge_conn (unused)
843  nullptr); // elem_face_conn (unused)
844 
845  EX_CHECK_ERR(ex_err, "Error reading block connectivity.");
846  message("Connectivity retrieved successfully for block: ", block);
847  }
848 }
849 
850 
851 
853 {
854  // Check for quick return if there are no edge blocks.
855  if (num_edge_blk == 0)
856  return;
857 
858  // Build data structure that we can quickly search for edges
859  // and then add required BoundaryInfo information. This is a
860  // map from edge->key() to a list of (elem_id, edge_id) pairs
861  // for the Edge in question. Since edge->key() is edge orientation
862  // invariant, this map does not distinguish different orientations
863  // of the same Edge.
864  typedef std::pair<dof_id_type, unsigned int> ElemEdgePair;
865  std::unordered_map<dof_id_type, std::vector<ElemEdgePair>> edge_map;
866  for (const auto & elem : mesh.element_ptr_range())
867  for (auto e : elem->edge_index_range())
868  {
869  // TODO: Make various Elem::compute_key() functions
870  // unprotected, this would allow us to avoid calling
871  // build_edge_ptr() repeatedly in case that turns out to be
872  // a bottleneck.
873  std::unique_ptr<Elem> edge = elem->build_edge_ptr(e);
874  dof_id_type edge_key = edge->key();
875 
876  // Creates vector if not already there
877  auto & vec = edge_map[edge_key];
878  vec.push_back(std::make_pair(elem->id(), e));
879  }
880 
881  // Get reference to the mesh's BoundaryInfo object, as we will be
882  // adding edges to this below.
883  BoundaryInfo & bi = mesh.get_boundary_info();
884 
885  for (const auto & edge_block_id : edge_block_ids)
886  {
887  // exII::ex_get_block() output parameters. Unlike the other
888  // "extended" APIs, exII::ex_get_block() does not use a
889  // parameter struct.
890  int num_edge_this_blk = 0;
891  int num_nodes_per_edge = 0;
892  int num_edges_per_edge = 0;
893  int num_faces_per_edge = 0;
894  int num_attr_per_edge = 0;
895  ex_err = exII::ex_get_block(ex_id,
896  exII::EX_EDGE_BLOCK,
897  edge_block_id,
898  elem_type.data(),
899  &num_edge_this_blk,
900  &num_nodes_per_edge,
901  &num_edges_per_edge, // 0 or -1 for edge blocks
902  &num_faces_per_edge, // 0 or -1 for edge blocks
903  &num_attr_per_edge);
904 
905  EX_CHECK_ERR(ex_err, "Error getting edge block info.");
906  message("Info retrieved successfully for block: ", edge_block_id);
907 
908  // Read in the connectivity of the edges of this block,
909  // watching out for the case where we actually have no
910  // elements in this block (possible with parallel files)
911  connect.resize(num_nodes_per_edge * num_edge_this_blk);
912 
913  if (!connect.empty())
914  {
915  ex_err = exII::ex_get_conn(ex_id,
916  exII::EX_EDGE_BLOCK,
917  edge_block_id,
918  connect.data(), // node_conn
919  nullptr, // elem_edge_conn (unused)
920  nullptr); // elem_face_conn (unused)
921 
922  EX_CHECK_ERR(ex_err, "Error reading block connectivity.");
923  message("Connectivity retrieved successfully for block: ", edge_block_id);
924 
925  // All edge types have an identity mapping from the corresponding
926  // Exodus type, so we don't need to bother with mapping ids, but
927  // we do need to know what kind of elements to build.
928  const auto & conv = get_conversion(std::string(elem_type.data()));
929 
930  // Loop over indices in connectivity array, build edge elements,
931  // look them up in the edge_map.
932  for (unsigned int i=0, sz=connect.size(); i<sz; i+=num_nodes_per_edge)
933  {
934  auto edge = Elem::build(conv.libmesh_elem_type());
935  for (int n=0; n<num_nodes_per_edge; ++n)
936  {
937  int exodus_node_id = connect[i+n];
938  int exodus_node_id_zero_based = exodus_node_id - 1;
939  int libmesh_node_id = node_num_map[exodus_node_id_zero_based] - 1;
940 
941  edge->set_node(n) = mesh.node_ptr(libmesh_node_id);
942  }
943 
944  // Compute key for the edge Elem we just built.
945  dof_id_type edge_key = edge->key();
946 
947  // If this key is not found in the edge_map, which is
948  // supposed to include every edge in the Mesh, then we
949  // need to throw an error.
950  auto & elem_edge_pair_vec =
951  libmesh_map_find(edge_map, edge_key);
952 
953  for (const auto & elem_edge_pair : elem_edge_pair_vec)
954  {
955  // We only want to match edges which have the same
956  // orientation (node ordering) to the one in the
957  // Exodus file, otherwise we ignore this elem_edge_pair.
958  //
959  // Note: this also handles the situation where two
960  // edges have the same key (hash collision) as then
961  // this check avoids a false positive.
962 
963  // Build edge indicated by elem_edge_pair
964  auto candidate_edge =
965  mesh.elem_ptr(elem_edge_pair.first)->
966  build_edge_ptr(elem_edge_pair.second);
967 
968  // Determine whether this candidate edge is a "real" match,
969  // i.e. also has the same orientation.
970  bool is_match = true;
971  for (int n=0; n<num_nodes_per_edge; ++n)
972  if (candidate_edge->node_id(n) != edge->node_id(n))
973  {
974  is_match = false;
975  break;
976  }
977 
978  if (is_match)
979  {
980  // Add this (elem, edge, id) combo to the BoundaryInfo object.
981  bi.add_edge(elem_edge_pair.first,
982  elem_edge_pair.second,
983  edge_block_id);
984  }
985  } // end loop over elem_edge_pairs
986  } // end loop over connectivity array
987 
988  // Set edgeset name in the BoundaryInfo object.
989  bi.edgeset_name(edge_block_id) = id_to_edge_block_names[edge_block_id];
990  } // end if !connect.empty()
991  } // end for edge_block_id : edge_block_ids
992 }
993 
994 
995 
997 {
998  elem_num_map.resize(num_elem);
999 
1000  // Note: we cannot use the exII::ex_get_num_map() here because it
1001  // (apparently) does not behave like ex_get_elem_num_map() when
1002  // there is no elem number map in the file: it throws an error
1003  // instead of returning a default identity array (1,2,3,...).
1004  ex_err = exII::ex_get_elem_num_map
1005  (ex_id, elem_num_map.empty() ? nullptr : elem_num_map.data());
1006 
1007  EX_CHECK_ERR(ex_err, "Error retrieving element number map.");
1008  message("Element numbering map retrieved successfully.");
1009 
1010 
1011  if (verbose)
1012  {
1013  libMesh::out << "[" << this->processor_id() << "] elem_num_map[i] = ";
1014  for (unsigned int i=0; i<static_cast<unsigned int>(std::min(10, num_elem-1)); ++i)
1015  libMesh::out << elem_num_map[i] << ", ";
1016  libMesh::out << "... " << elem_num_map.back() << std::endl;
1017  }
1018 }
1019 
1020 
1021 
1023 {
1024  ss_ids.resize(num_side_sets);
1025  if (num_side_sets > 0)
1026  {
1027  ex_err = exII::ex_get_ids(ex_id,
1028  exII::EX_SIDE_SET,
1029  ss_ids.data());
1030  EX_CHECK_ERR(ex_err, "Error retrieving sideset information.");
1031  message("All sideset information retrieved successfully.");
1032 
1033  // Resize appropriate data structures -- only do this once outside the loop
1035  num_df_per_set.resize(num_side_sets);
1036 
1037  // Inquire about the length of the concatenated side sets element list
1038  num_elem_all_sidesets = inquire(exII::EX_INQ_SS_ELEM_LEN, "Error retrieving length of the concatenated side sets element list!");
1039 
1042  id_list.resize (num_elem_all_sidesets);
1043  }
1044 
1045  char name_buffer[MAX_STR_LENGTH+1];
1046  for (int i=0; i<num_side_sets; ++i)
1047  {
1048  ex_err = exII::ex_get_name(ex_id, exII::EX_SIDE_SET,
1049  ss_ids[i], name_buffer);
1050  EX_CHECK_ERR(ex_err, "Error getting side set name.");
1051  id_to_ss_names[ss_ids[i]] = name_buffer;
1052  }
1053  message("All side set names retrieved successfully.");
1054 }
1055 
1056 
1058 {
1059  nodeset_ids.resize(num_node_sets);
1060  if (num_node_sets > 0)
1061  {
1062  ex_err = exII::ex_get_ids(ex_id,
1063  exII::EX_NODE_SET,
1064  nodeset_ids.data());
1065  EX_CHECK_ERR(ex_err, "Error retrieving nodeset information.");
1066  message("All nodeset information retrieved successfully.");
1067 
1068  // Resize appropriate data structures -- only do this once outnode the loop
1071  }
1072 
1073  char name_buffer[MAX_STR_LENGTH+1];
1074  for (int i=0; i<num_node_sets; ++i)
1075  {
1076  ex_err = exII::ex_get_name(ex_id, exII::EX_NODE_SET,
1077  nodeset_ids[i], name_buffer);
1078  EX_CHECK_ERR(ex_err, "Error getting node set name.");
1079  id_to_ns_names[nodeset_ids[i]] = name_buffer;
1080  }
1081  message("All node set names retrieved successfully.");
1082 }
1083 
1084 
1085 
1086 void ExodusII_IO_Helper::read_sideset(int id, int offset)
1087 {
1088  libmesh_assert_less (id, ss_ids.size());
1089  libmesh_assert_less (id, num_sides_per_set.size());
1090  libmesh_assert_less (id, num_df_per_set.size());
1091  libmesh_assert_less_equal (offset, elem_list.size());
1092  libmesh_assert_less_equal (offset, side_list.size());
1093 
1094  ex_err = exII::ex_get_set_param(ex_id,
1095  exII::EX_SIDE_SET,
1096  ss_ids[id],
1097  &num_sides_per_set[id],
1098  &num_df_per_set[id]);
1099  EX_CHECK_ERR(ex_err, "Error retrieving sideset parameters.");
1100  message("Parameters retrieved successfully for sideset: ", id);
1101 
1102 
1103  // It's OK for offset==elem_list.size() as long as num_sides_per_set[id]==0
1104  // because in that case we don't actually read anything...
1105 #ifdef DEBUG
1106  if (static_cast<unsigned int>(offset) == elem_list.size() ||
1107  static_cast<unsigned int>(offset) == side_list.size() )
1108  libmesh_assert_equal_to (num_sides_per_set[id], 0);
1109 #endif
1110 
1111 
1112  // Don't call ex_get_set unless there are actually sides there to get.
1113  // Exodus prints an annoying warning in DEBUG mode otherwise...
1114  if (num_sides_per_set[id] > 0)
1115  {
1116  ex_err = exII::ex_get_set(ex_id,
1117  exII::EX_SIDE_SET,
1118  ss_ids[id],
1119  &elem_list[offset],
1120  &side_list[offset]);
1121  EX_CHECK_ERR(ex_err, "Error retrieving sideset data.");
1122  message("Data retrieved successfully for sideset: ", id);
1123 
1124  for (int i=0; i<num_sides_per_set[id]; i++)
1125  id_list[i+offset] = ss_ids[id];
1126  }
1127 }
1128 
1129 
1130 
1132 {
1133  libmesh_assert_less (id, nodeset_ids.size());
1134  libmesh_assert_less (id, num_nodes_per_set.size());
1135  libmesh_assert_less (id, num_node_df_per_set.size());
1136 
1137  ex_err = exII::ex_get_set_param(ex_id,
1138  exII::EX_NODE_SET,
1139  nodeset_ids[id],
1140  &num_nodes_per_set[id],
1141  &num_node_df_per_set[id]);
1142  EX_CHECK_ERR(ex_err, "Error retrieving nodeset parameters.");
1143  message("Parameters retrieved successfully for nodeset: ", id);
1144 
1145  node_list.resize(num_nodes_per_set[id]);
1146 
1147  // Don't call ex_get_set unless there are actually nodes there to get.
1148  // Exodus prints an annoying warning message in DEBUG mode otherwise...
1149  if (num_nodes_per_set[id] > 0)
1150  {
1151  ex_err = exII::ex_get_set(ex_id,
1152  exII::EX_NODE_SET,
1153  nodeset_ids[id],
1154  node_list.data(),
1155  nullptr); // set_extra_list, ignored for node sets
1156 
1157  EX_CHECK_ERR(ex_err, "Error retrieving nodeset data.");
1158  message("Data retrieved successfully for nodeset: ", id);
1159  }
1160 }
1161 
1162 
1163 
1165 {
1166  // Figure out how many nodesets there are in the file so we can
1167  // properly resize storage as necessary.
1168  num_node_sets =
1169  this->inquire
1170  (exII::EX_INQ_NODE_SETS,
1171  "Error retrieving number of node sets");
1172 
1173  // Figure out how many nodes there are in all the nodesets.
1174  int total_nodes_in_all_sets =
1175  this->inquire
1176  (exII::EX_INQ_NS_NODE_LEN,
1177  "Error retrieving number of nodes in all node sets.");
1178 
1179  // Figure out how many distribution factors there are in all the nodesets.
1180  int total_df_in_all_sets =
1181  this->inquire
1182  (exII::EX_INQ_NS_DF_LEN,
1183  "Error retrieving number of distribution factors in all node sets.");
1184 
1185  // If there are no nodesets, there's nothing to read in.
1186  if (num_node_sets == 0)
1187  return;
1188 
1189  // Allocate space to read all the nodeset data.
1190  // Use existing class members where possible to avoid shadowing
1191  nodeset_ids.clear(); nodeset_ids.resize(num_node_sets);
1196  node_sets_node_list.clear(); node_sets_node_list.resize(total_nodes_in_all_sets);
1197  node_sets_dist_fact.clear(); node_sets_dist_fact.resize(total_df_in_all_sets);
1198 
1199  ex_err = exII::ex_get_concat_node_sets
1200  (ex_id,
1201  nodeset_ids.data(),
1202  num_nodes_per_set.data(),
1203  num_node_df_per_set.data(),
1204  node_sets_node_index.data(),
1205  node_sets_dist_index.data(),
1206  node_sets_node_list.data(),
1207  total_df_in_all_sets ?
1209 
1210  EX_CHECK_ERR(ex_err, "Error reading concatenated nodesets");
1211 
1212  // Read the nodeset names from file!
1213  char name_buffer[MAX_STR_LENGTH+1];
1214  for (int i=0; i<num_node_sets; ++i)
1215  {
1216  ex_err = exII::ex_get_name
1217  (ex_id,
1218  exII::EX_NODE_SET,
1219  nodeset_ids[i],
1220  name_buffer);
1221  EX_CHECK_ERR(ex_err, "Error getting node set name.");
1222  id_to_ns_names[nodeset_ids[i]] = name_buffer;
1223  }
1224 }
1225 
1226 
1227 
1229 {
1230  // Always call close on processor 0.
1231  // If we're running on multiple processors, i.e. as one of several Nemesis files,
1232  // we call close on all processors...
1233  if ((this->processor_id() == 0) || (!_run_only_on_proc0))
1234  {
1235  // Don't close the file if it was never opened, this raises an Exodus error
1237  {
1238  ex_err = exII::ex_close(ex_id);
1239  EX_CHECK_ERR(ex_err, "Error closing Exodus file.");
1240  message("Exodus file closed successfully.");
1241  }
1242  }
1243 }
1244 
1245 
1246 
1247 int ExodusII_IO_Helper::inquire(int req_info_in, std::string error_msg)
1248 {
1249  int ret_int = 0;
1250  char ret_char = 0;
1251  float ret_float = 0.;
1252 
1253  ex_err = exII::ex_inquire(ex_id,
1254  req_info_in,
1255  &ret_int,
1256  &ret_float,
1257  &ret_char);
1258 
1259  EX_CHECK_ERR(ex_err, error_msg);
1260 
1261  return ret_int;
1262 }
1263 
1264 
1265 
1267 {
1268  // Make sure we have an up-to-date count of the number of time steps in the file.
1269  this->read_num_time_steps();
1270 
1271  if (num_time_steps > 0)
1272  {
1273  time_steps.resize(num_time_steps);
1274  ex_err = exII::ex_get_all_times
1275  (ex_id,
1277  EX_CHECK_ERR(ex_err, "Error reading timesteps!");
1278  }
1279 }
1280 
1281 
1282 
1284 {
1285  num_time_steps =
1286  this->inquire(exII::EX_INQ_TIME, "Error retrieving number of time steps");
1287 }
1288 
1289 
1290 
1291 void ExodusII_IO_Helper::read_nodal_var_values(std::string nodal_var_name, int time_step)
1292 {
1293  // Read the nodal variable names from file, so we can see if we have the one we're looking for
1294  this->read_var_names(NODAL);
1295 
1296  // See if we can find the variable we are looking for
1297  unsigned int var_index = 0;
1298  bool found = false;
1299 
1300  // Do a linear search for nodal_var_name in nodal_var_names
1301  for (; var_index<nodal_var_names.size(); ++var_index)
1302  {
1303  found = (nodal_var_names[var_index] == nodal_var_name);
1304  if (found)
1305  break;
1306  }
1307 
1308  if (!found)
1309  {
1310  libMesh::err << "Available variables: " << std::endl;
1311  for (const auto & var_name : nodal_var_names)
1312  libMesh::err << var_name << std::endl;
1313 
1314  libmesh_error_msg("Unable to locate variable named: " << nodal_var_name);
1315  }
1316 
1317  // Allocate enough space to store the nodal variable values
1318  nodal_var_values.resize(num_nodes);
1319 
1320  std::vector<Real> unmapped_nodal_var_values(num_nodes);
1321 
1322  // Call the Exodus API to read the nodal variable values
1323  ex_err = exII::ex_get_nodal_var
1324  (ex_id,
1325  time_step,
1326  var_index+1,
1327  num_nodes,
1328  MappedInputVector(unmapped_nodal_var_values, _single_precision).data());
1329  EX_CHECK_ERR(ex_err, "Error reading nodal variable values!");
1330 
1331  for (unsigned i=0; i<static_cast<unsigned>(num_nodes); i++)
1332  {
1333  // Use the node_num_map to obtain the ID of this node in the Exodus file,
1334  // and remember to subtract 1 since libmesh is zero-based and Exodus is 1-based.
1335  unsigned mapped_node_id = this->node_num_map[i] - 1;
1336 
1337  // Store the nodal value in the map.
1338  nodal_var_values[mapped_node_id] = unmapped_nodal_var_values[i];
1339  }
1340 }
1341 
1342 
1343 
1345 {
1346  switch (type)
1347  {
1348  case NODAL:
1350  break;
1351  case ELEMENTAL:
1353  break;
1354  case GLOBAL:
1356  break;
1357  case SIDESET:
1359  break;
1360  default:
1361  libmesh_error_msg("Unrecognized ExodusVarType " << type);
1362  }
1363 }
1364 
1365 
1366 
1367 void ExodusII_IO_Helper::read_var_names_impl(const char * var_type,
1368  int & count,
1369  std::vector<std::string> & result)
1370 {
1371  // First read and store the number of names we have
1372  ex_err = exII::ex_get_var_param(ex_id, var_type, &count);
1373  EX_CHECK_ERR(ex_err, "Error reading number of variables.");
1374 
1375  // Do nothing if no variables are detected
1376  if (count == 0)
1377  return;
1378 
1379  // Second read the actual names and convert them into a format we can use
1380  NamesData names_table(count, MAX_STR_LENGTH);
1381 
1382  ex_err = exII::ex_get_var_names(ex_id,
1383  var_type,
1384  count,
1385  names_table.get_char_star_star()
1386  );
1387  EX_CHECK_ERR(ex_err, "Error reading variable names!");
1388 
1389  if (verbose)
1390  {
1391  libMesh::out << "Read the variable(s) from the file:" << std::endl;
1392  for (int i=0; i<count; i++)
1393  libMesh::out << names_table.get_char_star(i) << std::endl;
1394  }
1395 
1396  // Allocate enough space for our variable name strings.
1397  result.resize(count);
1398 
1399  // Copy the char buffers into strings.
1400  for (int i=0; i<count; i++)
1401  result[i] = names_table.get_char_star(i); // calls string::op=(const char *)
1402 }
1403 
1404 
1405 
1406 
1407 void
1409  const std::vector<std::string> & names)
1410 {
1411  switch (type)
1412  {
1413  case NODAL:
1414  this->write_var_names_impl("n", num_nodal_vars, names);
1415  break;
1416  case ELEMENTAL:
1417  this->write_var_names_impl("e", num_elem_vars, names);
1418  break;
1419  case GLOBAL:
1420  this->write_var_names_impl("g", num_global_vars, names);
1421  break;
1422  case SIDESET:
1423  {
1424  // Note: calling this function *sets* num_sideset_vars to the
1425  // number of entries in the 'names' vector, num_sideset_vars
1426  // does not already need to be set before calling this.
1427  this->write_var_names_impl("s", num_sideset_vars, names);
1428  break;
1429  }
1430  default:
1431  libmesh_error_msg("Unrecognized ExodusVarType " << type);
1432  }
1433 }
1434 
1435 
1436 
1437 void
1439  int & count,
1440  const std::vector<std::string> & names)
1441 {
1442  // Update the count variable so that it's available to other parts of the class.
1443  count = cast_int<int>(names.size());
1444 
1445  // Write that number of variables to the file.
1446  ex_err = exII::ex_put_var_param(ex_id, var_type, count);
1447  EX_CHECK_ERR(ex_err, "Error setting number of vars.");
1448 
1449  if (count > 0)
1450  {
1451  NamesData names_table(count, MAX_STR_LENGTH);
1452 
1453  // Store the input names in the format required by Exodus.
1454  for (int i=0; i != count; ++i)
1455  names_table.push_back_entry(names[i]);
1456 
1457  if (verbose)
1458  {
1459  libMesh::out << "Writing variable name(s) to file: " << std::endl;
1460  for (int i=0; i != count; ++i)
1461  libMesh::out << names_table.get_char_star(i) << std::endl;
1462  }
1463 
1464  ex_err = exII::ex_put_var_names(ex_id,
1465  var_type,
1466  count,
1467  names_table.get_char_star_star()
1468  );
1469 
1470  EX_CHECK_ERR(ex_err, "Error writing variable names.");
1471  }
1472 }
1473 
1474 
1475 
1476 void ExodusII_IO_Helper::read_elemental_var_values(std::string elemental_var_name,
1477  int time_step,
1478  std::map<dof_id_type, Real> & elem_var_value_map)
1479 {
1480  this->read_var_names(ELEMENTAL);
1481 
1482  // See if we can find the variable we are looking for
1483  unsigned int var_index = 0;
1484  bool found = false;
1485 
1486  // Do a linear search for elem_var_name in elemental_var_names
1487  for (; var_index != elem_var_names.size(); ++var_index)
1488  if (elem_var_names[var_index] == elemental_var_name)
1489  {
1490  found = true;
1491  break;
1492  }
1493 
1494  if (!found)
1495  {
1496  libMesh::err << "Available variables: " << std::endl;
1497  for (const auto & var_name : elem_var_names)
1498  libMesh::err << var_name << std::endl;
1499 
1500  libmesh_error_msg("Unable to locate variable named: " << elemental_var_name);
1501  }
1502 
1503  // Sequential index which we can use to look up the element ID in the elem_num_map.
1504  unsigned ex_el_num = 0;
1505 
1506  // Element variable truth table
1507  std::vector<int> var_table(block_ids.size() * elem_var_names.size());
1508  exII::ex_get_var_tab(ex_id, "e", block_ids.size(), elem_var_names.size(), var_table.data());
1509 
1510  for (unsigned i=0; i<static_cast<unsigned>(num_elem_blk); i++)
1511  {
1512  ex_err = exII::ex_get_elem_block(ex_id,
1513  block_ids[i],
1514  nullptr,
1516  nullptr,
1517  nullptr);
1518  EX_CHECK_ERR(ex_err, "Error getting number of elements in block.");
1519 
1520  // If the current variable isn't active on this subdomain, advance
1521  // the index by the number of elements on this block and go to the
1522  // next loop iteration.
1523  if (!var_table[elem_var_names.size()*i + var_index])
1524  {
1525  ex_el_num += num_elem_this_blk;
1526  continue;
1527  }
1528 
1529  std::vector<Real> block_elem_var_values(num_elem_this_blk);
1530 
1531  ex_err = exII::ex_get_elem_var
1532  (ex_id,
1533  time_step,
1534  var_index+1,
1535  block_ids[i],
1537  MappedInputVector(block_elem_var_values, _single_precision).data());
1538  EX_CHECK_ERR(ex_err, "Error getting elemental values.");
1539 
1540  for (unsigned j=0; j<static_cast<unsigned>(num_elem_this_blk); j++)
1541  {
1542  // Use the elem_num_map to obtain the ID of this element in the Exodus file,
1543  // and remember to subtract 1 since libmesh is zero-based and Exodus is 1-based.
1544  unsigned mapped_elem_id = this->elem_num_map[ex_el_num] - 1;
1545 
1546  // Store the elemental value in the map.
1547  elem_var_value_map[mapped_elem_id] = block_elem_var_values[j];
1548 
1549  // Go to the next sequential element ID.
1550  ex_el_num++;
1551  }
1552  }
1553 }
1554 
1555 
1556 // For Writing Solutions
1557 
1558 void ExodusII_IO_Helper::create(std::string filename)
1559 {
1560  // If we're processor 0, always create the file.
1561  // If we running on all procs, e.g. as one of several Nemesis files, also
1562  // call create there.
1563  if ((this->processor_id() == 0) || (!_run_only_on_proc0))
1564  {
1565  int
1566  comp_ws = 0,
1567  io_ws = 0;
1568 
1569  if (_single_precision)
1570  {
1571  comp_ws = cast_int<int>(sizeof(float));
1572  io_ws = cast_int<int>(sizeof(float));
1573  }
1574  // Fall back on double precision when necessary since ExodusII
1575  // doesn't seem to support long double
1576  else
1577  {
1578  comp_ws = cast_int<int>
1579  (std::min(sizeof(Real), sizeof(double)));
1580  io_ws = cast_int<int>
1581  (std::min(sizeof(Real), sizeof(double)));
1582  }
1583 
1584  // By default we just open the Exodus file in "EX_CLOBBER" mode,
1585  // which, according to "ncdump -k", writes the file in "64-bit
1586  // offset" mode, which is a NETCDF3 file format.
1587  int mode = EX_CLOBBER;
1588 
1589  // If HDF5 is available, by default we will write Exodus files
1590  // in a more modern NETCDF4-compatible format. For this file
1591  // type, "ncdump -k" will report "netCDF-4".
1592 #ifdef LIBMESH_HAVE_HDF5
1593  mode |= EX_NETCDF4;
1594  mode |= EX_NOCLASSIC;
1595 #endif
1596 
1597  ex_id = exII::ex_create(filename.c_str(), mode, &comp_ws, &io_ws);
1598 
1599  EX_CHECK_ERR(ex_id, "Error creating ExodusII mesh file.");
1600 
1601  if (verbose)
1602  libMesh::out << "File created successfully." << std::endl;
1603  }
1604 
1605  opened_for_writing = true;
1606  current_filename = filename;
1607 }
1608 
1609 
1610 
1611 void ExodusII_IO_Helper::initialize(std::string str_title, const MeshBase & mesh, bool use_discontinuous)
1612 {
1613  // The majority of this function only executes on processor 0, so any functions
1614  // which are collective, like n_active_elem() or n_edge_conds() must be called
1615  // before the processors' execution paths diverge.
1616  unsigned int n_active_elem = mesh.n_active_elem();
1617  const BoundaryInfo & bi = mesh.get_boundary_info();
1618  num_edge = bi.n_edge_conds();
1619 
1620  if ((_run_only_on_proc0) && (this->processor_id() != 0))
1621  return;
1622 
1623  // If _write_as_dimension is nonzero, use it to set num_dim in the Exodus file.
1624  if (_write_as_dimension)
1627  num_dim = mesh.mesh_dimension();
1628  else
1629  num_dim = mesh.spatial_dimension();
1630 
1631  num_elem = mesh.n_elem();
1632 
1633  if (!use_discontinuous)
1634  {
1635  // Don't rely on mesh.n_nodes() here. If nodes have been
1636  // deleted recently, it will be incorrect.
1637  num_nodes = cast_int<int>(std::distance(mesh.nodes_begin(),
1638  mesh.nodes_end()));
1639  }
1640  else
1641  {
1642  for (const auto & elem : mesh.active_element_ptr_range())
1643  num_nodes += elem->n_nodes();
1644  }
1645 
1646  std::vector<boundary_id_type> unique_side_boundaries;
1647  std::vector<boundary_id_type> unique_node_boundaries;
1648 
1649  bi.build_side_boundary_ids(unique_side_boundaries);
1650  {
1651  // Add shell face boundaries to the list of side boundaries, since ExodusII
1652  // treats these the same way.
1653  std::vector<boundary_id_type> shellface_boundaries;
1654  bi.build_shellface_boundary_ids(shellface_boundaries);
1655  for (const auto & id : shellface_boundaries)
1656  unique_side_boundaries.push_back(id);
1657  }
1658  bi.build_node_boundary_ids(unique_node_boundaries);
1659 
1660  num_side_sets = cast_int<int>(unique_side_boundaries.size());
1661  num_node_sets = cast_int<int>(unique_node_boundaries.size());
1662 
1663  //loop through element and map between block and element vector
1664  std::map<subdomain_id_type, std::vector<unsigned int>> subdomain_map;
1665 
1666  for (const auto & elem : mesh.active_element_ptr_range())
1667  {
1668  // We skip writing infinite elements to the Exodus file, so
1669  // don't put them in the subdomain_map. That way the number of
1670  // blocks should be correct.
1671 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1672  if (elem->infinite())
1673  continue;
1674 #endif
1675 
1676  subdomain_id_type cur_subdomain = elem->subdomain_id();
1677  subdomain_map[cur_subdomain].push_back(elem->id());
1678  }
1679  num_elem_blk = cast_int<int>(subdomain_map.size());
1680 
1681  if (str_title.size() > MAX_LINE_LENGTH)
1682  {
1683  libMesh::err << "Warning, Exodus files cannot have titles longer than "
1684  << MAX_LINE_LENGTH
1685  << " characters. Your title will be truncated."
1686  << std::endl;
1687  str_title.resize(MAX_LINE_LENGTH);
1688  }
1689 
1690  // Edge BCs are handled a bit differently than sidesets and nodesets.
1691  // They are written as separate "edge blocks", and then edge variables
1692  // can be defined on those blocks. That is, they are not written as
1693  // edge sets, since edge sets must refer to edges stored elsewhere.
1694  // We write a separate edge block for each unique boundary id that
1695  // we have.
1696  num_edge_blk = bi.get_edge_boundary_ids().size();
1697 
1698  // Build an ex_init_params() structure that is to be passed to the
1699  // newer ex_put_init_ext() API. The new API will eventually allow us
1700  // to store edge and face data in the Exodus file.
1701  //
1702  // Notes:
1703  // * We use C++11 zero initialization syntax to make sure that all
1704  // members of the struct (including ones we aren't using) are
1705  // given sensible values.
1706  // * For the "title" field, we manually do a null-terminated string
1707  // copy since std::string does not null-terminate but it does
1708  // return the number of characters successfully copied.
1709  exII::ex_init_params params = {};
1710  params.title[str_title.copy(params.title, MAX_LINE_LENGTH)] = '\0';
1711  params.num_dim = num_dim;
1712  params.num_nodes = num_nodes;
1713  params.num_elem = n_active_elem;
1714  params.num_elem_blk = num_elem_blk;
1715  params.num_node_sets = num_node_sets;
1716  params.num_side_sets = num_side_sets;
1717  params.num_edge_blk = num_edge_blk;
1718  params.num_edge = num_edge;
1719 
1720  ex_err = exII::ex_put_init_ext(ex_id, &params);
1721  EX_CHECK_ERR(ex_err, "Error initializing new Exodus file.");
1722 }
1723 
1724 
1725 
1726 void ExodusII_IO_Helper::write_nodal_coordinates(const MeshBase & mesh, bool use_discontinuous)
1727 {
1728  if ((_run_only_on_proc0) && (this->processor_id() != 0))
1729  return;
1730 
1731  // Clear existing data from any previous calls.
1732  x.clear();
1733  y.clear();
1734  z.clear();
1735  node_num_map.clear();
1736 
1737  // Reserve space in the nodal coordinate vectors. num_nodes is
1738  // exact, this just allows us to do away with one potentially
1739  // error-inducing loop index.
1740  x.reserve(num_nodes);
1741  y.reserve(num_nodes);
1742  z.reserve(num_nodes);
1743 
1744  // And in the node_num_map - since the nodes aren't organized in
1745  // blocks, libmesh will always write out the identity map
1746  // here... unless there has been some refinement and coarsening, or
1747  // node deletion without a corresponding call to contract(). You
1748  // need to write this any time there could be 'holes' in the node
1749  // numbering, so we write it every time.
1750  node_num_map.reserve(num_nodes);
1751 
1752  // Clear out any previously-mapped node IDs.
1754 
1755  if (!use_discontinuous)
1756  {
1757  for (const auto & node_ptr : mesh.node_ptr_range())
1758  {
1759  const Node & node = *node_ptr;
1760 
1761  x.push_back(node(0) + _coordinate_offset(0));
1762 
1763 #if LIBMESH_DIM > 1
1764  y.push_back(node(1) + _coordinate_offset(1));
1765 #else
1766  y.push_back(0.);
1767 #endif
1768 #if LIBMESH_DIM > 2
1769  z.push_back(node(2) + _coordinate_offset(2));
1770 #else
1771  z.push_back(0.);
1772 #endif
1773 
1774  // Fill in node_num_map entry with the proper (1-based) node id
1775  node_num_map.push_back(node.id() + 1);
1776 
1777  // Also map the zero-based libmesh node id to the 1-based
1778  // Exodus ID it will be assigned (this is equivalent to the
1779  // current size of the x vector).
1780  libmesh_node_num_to_exodus[ cast_int<int>(node.id()) ] = cast_int<int>(x.size());
1781  }
1782  }
1783  else
1784  {
1785  for (const auto & elem : mesh.active_element_ptr_range())
1786  for (const Node & node : elem->node_ref_range())
1787  {
1788  x.push_back(node(0));
1789 #if LIBMESH_DIM > 1
1790  y.push_back(node(1));
1791 #else
1792  y.push_back(0.);
1793 #endif
1794 #if LIBMESH_DIM > 2
1795  z.push_back(node(2));
1796 #else
1797  z.push_back(0.);
1798 #endif
1799 
1800  // Let's skip the node_num_map in the discontinuous
1801  // case, since we're effectively duplicating nodes for
1802  // the sake of discontinuous visualization, so it isn't
1803  // clear how to deal with node_num_map here. This means
1804  // that writing discontinuous meshes won't work with
1805  // element numberings that have "holes".
1806  }
1807  }
1808 
1809  ex_err = exII::ex_put_coord
1810  (ex_id,
1811  x.empty() ? nullptr : MappedOutputVector(x, _single_precision).data(),
1812  y.empty() ? nullptr : MappedOutputVector(y, _single_precision).data(),
1813  z.empty() ? nullptr : MappedOutputVector(z, _single_precision).data());
1814 
1815  EX_CHECK_ERR(ex_err, "Error writing coordinates to Exodus file.");
1816 
1817  if (!use_discontinuous)
1818  {
1819  // Also write the (1-based) node_num_map to the file.
1820  ex_err = exII::ex_put_node_num_map(ex_id, node_num_map.data());
1821  EX_CHECK_ERR(ex_err, "Error writing node_num_map");
1822  }
1823 }
1824 
1825 
1826 
1827 void ExodusII_IO_Helper::write_elements(const MeshBase & mesh, bool use_discontinuous)
1828 {
1829  // n_active_elem() is a parallel_only function
1830  unsigned int n_active_elem = mesh.n_active_elem();
1831 
1832  if ((_run_only_on_proc0) && (this->processor_id() != 0))
1833  return;
1834 
1835  // Map from block ID to a vector of element IDs in that block. Element
1836  // IDs are now of type dof_id_type, subdomain IDs are of type subdomain_id_type.
1837  typedef std::map<subdomain_id_type, std::vector<dof_id_type>> subdomain_map_type;
1838  subdomain_map_type subdomain_map;
1839 
1840  // Loop through element and map between block and element vector.
1841  for (const auto & elem : mesh.active_element_ptr_range())
1842  {
1843  // We skip writing infinite elements to the Exodus file, so
1844  // don't put them in the subdomain_map. That way the number of
1845  // blocks should be correct.
1846 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1847  if (elem->infinite())
1848  continue;
1849 #endif
1850 
1851  subdomain_map[ elem->subdomain_id() ].push_back(elem->id());
1852  }
1853 
1854  // element map vector
1855  num_elem_blk = cast_int<int>(subdomain_map.size());
1856  block_ids.resize(num_elem_blk);
1857  elem_num_map.resize(n_active_elem);
1858  std::vector<int>::iterator curr_elem_map_end = elem_num_map.begin();
1859 
1860  std::vector<int> elem_blk_id;
1861  std::vector<int> num_elem_this_blk_vec;
1862  std::vector<int> num_nodes_per_elem_vec;
1863  std::vector<int> num_edges_per_elem_vec;
1864  std::vector<int> num_faces_per_elem_vec;
1865  std::vector<int> num_attr_vec;
1866  NamesData elem_type_table(num_elem_blk, MAX_STR_LENGTH);
1867 
1868  // Note: It appears that there is a bug in exodusII::ex_put_name where
1869  // the index returned from the ex_id_lkup is erroneously used. For now
1870  // the work around is to use the alternative function ex_put_names, but
1871  // this function requires a char ** data structure.
1872  NamesData names_table(num_elem_blk, MAX_STR_LENGTH);
1873 
1874  // counter indexes into the block_ids vector
1875  unsigned int counter = 0;
1876  for (auto & pr : subdomain_map)
1877  {
1878  block_ids[counter] = pr.first;
1879  names_table.push_back_entry(mesh.subdomain_name(pr.first));
1880 
1881  // Get a reference to a vector of element IDs for this subdomain.
1882  subdomain_map_type::mapped_type & tmp_vec = pr.second;
1883 
1884  // Use the first element in this block to get representative information.
1885  // Note that Exodus assumes all elements in a block are of the same type!
1886  // We are using that same assumption here!
1887  const auto & conv = get_conversion(mesh.elem_ref(tmp_vec[0]).type());
1888  num_nodes_per_elem = mesh.elem_ref(tmp_vec[0]).n_nodes();
1889 
1890  elem_blk_id.push_back(pr.first);
1891  elem_type_table.push_back_entry(conv.exodus_elem_type().c_str());
1892  num_elem_this_blk_vec.push_back(cast_int<int>(tmp_vec.size()));
1893  num_nodes_per_elem_vec.push_back(num_nodes_per_elem);
1894  num_attr_vec.push_back(0); // we don't currently use elem block attributes.
1895  num_edges_per_elem_vec.push_back(0); // We don't currently store any edge blocks
1896  num_faces_per_elem_vec.push_back(0); // We don't currently store any face blocks
1897  ++counter;
1898  }
1899 
1900  // In the case of discontinuous plotting we initialize a map from
1901  // (element, node) pairs to the corresponding discontinuous node index.
1902  // This ordering must match the ordering used in write_nodal_coordinates.
1903  //
1904  // Note: This map takes the place of the libmesh_node_num_to_exodus map in
1905  // the discontinuous case.
1906  std::map<std::pair<dof_id_type, unsigned int>, dof_id_type> discontinuous_node_indices;
1907  if (use_discontinuous)
1908  {
1909  dof_id_type node_counter = 1; // Exodus numbering is 1-based
1910  for (const auto & elem : mesh.active_element_ptr_range())
1911  for (auto n : elem->node_index_range())
1912  {
1913  std::pair<dof_id_type,unsigned int> id_pair;
1914  id_pair.first = elem->id();
1915  id_pair.second = n;
1916  discontinuous_node_indices[id_pair] = node_counter;
1917  node_counter++;
1918  }
1919  }
1920 
1921  // Reference to the BoundaryInfo object for convenience.
1922  const BoundaryInfo & bi = mesh.get_boundary_info();
1923 
1924  // Build list of (elem, edge, id) triples
1925  std::vector<BoundaryInfo::BCTuple> edge_tuples = bi.build_edge_list();
1926 
1927  // Build the connectivity array for each edge block. The connectivity array
1928  // is a vector<int> with "num_edges * num_nodes_per_edge" entries. We write
1929  // the Exodus node numbers to the connectivity arrays so that they can
1930  // be used directly in the calls to exII::ex_put_conn() below. We also keep
1931  // track of the ElemType and the number of nodes for each boundary_id. All
1932  // edges with a given boundary_id must be of the same type.
1933  std::map<boundary_id_type, std::vector<int>> edge_id_to_conn;
1934  std::map<boundary_id_type, std::pair<ElemType, unsigned int>> edge_id_to_elem_type;
1935 
1936  for (const auto & t : edge_tuples)
1937  {
1938  dof_id_type elem_id = std::get<0>(t);
1939  unsigned int edge_id = std::get<1>(t);
1940  boundary_id_type b_id = std::get<2>(t);
1941 
1942  // Build the edge in question
1943  std::unique_ptr<const Elem> edge =
1944  mesh.elem_ptr(elem_id)->build_edge_ptr(edge_id);
1945 
1946  // Error checking: make sure that all edges in this block are
1947  // the same geometric type.
1948  auto check_it = edge_id_to_elem_type.find(b_id);
1949 
1950  if (check_it == edge_id_to_elem_type.end())
1951  {
1952  // Keep track of the ElemType and number of nodes in this boundary id.
1953  edge_id_to_elem_type[b_id] = std::make_pair(edge->type(), edge->n_nodes());
1954  }
1955  else
1956  {
1957  // Make sure the existing data is consistent
1958  auto & val_pair = check_it->second;
1959  if (val_pair.first != edge->type() || val_pair.second != edge->n_nodes())
1960  libmesh_error_msg("All edges in a block must have same geometric type.");
1961  }
1962 
1963  // Get reference to the connectivity array for this block
1964  auto & conn = edge_id_to_conn[b_id];
1965 
1966  // For each node on the edge, look up the exodus node id and
1967  // store it in the conn array. Note: all edge types have
1968  // identity node mappings so we don't bother with Conversion
1969  // objects here.
1970  for (auto n : edge->node_index_range())
1971  {
1972  dof_id_type libmesh_node_id = edge->node_ptr(n)->id();
1973 
1974  // We look up Exodus node numbers differently if we are
1975  // writing a discontinuous Exodus file.
1976  int exodus_node_id = -1;
1977 
1978  if (!use_discontinuous)
1979  exodus_node_id = libmesh_map_find
1980  (libmesh_node_num_to_exodus, cast_int<int>(libmesh_node_id));
1981  else
1982  {
1983  // Find the node on the "parent" element containing this edge
1984  // which matches libmesh_node_id. Then use that id to look up
1985  // the exodus_node_id in the discontinuous_node_indices map.
1986  //
1987  // TODO: This should also be given by e.g.:
1988  // Hex8::edge_nodes_map[edge_id][n]. Note: we have something
1989  // like this for sides, Elem::which_node_am_i(), but nothing
1990  // equivalent for edges.
1991  const Elem * parent = mesh.elem_ptr(elem_id);
1992  for (auto pn : parent->node_index_range())
1993  if (parent->node_ptr(pn)->id() == libmesh_node_id)
1994  {
1995  exodus_node_id = libmesh_map_find
1996  (discontinuous_node_indices,
1997  std::make_pair(elem_id, pn));
1998  break;
1999  }
2000  }
2001 
2002  if (exodus_node_id == -1)
2003  libmesh_error_msg("Unable to map edge's libMesh node id to its Exodus node id.");
2004 
2005  conn.push_back(exodus_node_id);
2006  }
2007  }
2008 
2009  // Make sure we have the same number of edge ids that we thought we would.
2010  libmesh_assert(static_cast<int>(edge_id_to_conn.size()) == num_edge_blk);
2011 
2012  // Build data structures describing edge blocks. This information must be
2013  // be passed to exII::ex_put_concat_all_blocks() at the same time as the
2014  // information about elem blocks.
2015  std::vector<int> edge_blk_id;
2016  NamesData edge_type_table(num_edge_blk, MAX_STR_LENGTH);
2017  std::vector<int> num_edge_this_blk_vec;
2018  std::vector<int> num_nodes_per_edge_vec;
2019  std::vector<int> num_attr_edge_vec;
2020 
2021  // We also build a data structure of edge block names which can
2022  // later be passed to exII::ex_put_names().
2023  NamesData edge_block_names_table(num_edge_blk, MAX_STR_LENGTH);
2024 
2025  // Note: We are going to use the edge **boundary** ids as **block** ids.
2026  for (const auto & pr : edge_id_to_conn)
2027  {
2028  // Store the edge block id in the array to be passed to Exodus.
2029  boundary_id_type id = pr.first;
2030  edge_blk_id.push_back(id);
2031 
2032  // Set Exodus element type and number of nodes for this edge block.
2033  const auto & elem_type_node_count = edge_id_to_elem_type[id];
2034  const auto & conv = get_conversion(elem_type_node_count.first);
2035  edge_type_table.push_back_entry(conv.exodus_type.c_str());
2036  num_nodes_per_edge_vec.push_back(elem_type_node_count.second);
2037 
2038  // The number of edges is the number of entries in the connectivity
2039  // array divided by the number of nodes per edge.
2040  num_edge_this_blk_vec.push_back(pr.second.size() / elem_type_node_count.second);
2041 
2042  // We don't store any attributes currently
2043  num_attr_edge_vec.push_back(0);
2044 
2045  // Store the name of this edge block
2046  edge_block_names_table.push_back_entry(bi.get_edgeset_name(id));
2047  }
2048 
2049  // Zero-initialize and then fill in an exII::ex_block_params struct
2050  // with the data we have collected. This new API replaces the old
2051  // exII::ex_put_concat_elem_block() API, and will eventually allow
2052  // us to also allocate space for edge/face blocks if desired.
2053  //
2054  // TODO: It seems like we should be able to take advantage of the
2055  // optimization where you set define_maps==1, but when I tried this
2056  // I got the error: "failed to find node map size". I think the
2057  // problem is that we need to first specify a nonzero number of
2058  // node/elem maps during the call to ex_put_init_ext() in order for
2059  // this to work correctly.
2060  exII::ex_block_params params = {};
2061 
2062  // Set pointers for information about elem blocks.
2063  params.elem_blk_id = elem_blk_id.data();
2064  params.elem_type = elem_type_table.get_char_star_star();
2065  params.num_elem_this_blk = num_elem_this_blk_vec.data();
2066  params.num_nodes_per_elem = num_nodes_per_elem_vec.data();
2067  params.num_edges_per_elem = num_edges_per_elem_vec.data();
2068  params.num_faces_per_elem = num_faces_per_elem_vec.data();
2069  params.num_attr_elem = num_attr_vec.data();
2070  params.define_maps = 0;
2071 
2072  // Set pointers to edge block information only if we actually have some.
2073  if (num_edge_blk)
2074  {
2075  params.edge_blk_id = edge_blk_id.data();
2076  params.edge_type = edge_type_table.get_char_star_star();
2077  params.num_edge_this_blk = num_edge_this_blk_vec.data();
2078  params.num_nodes_per_edge = num_nodes_per_edge_vec.data();
2079  params.num_attr_edge = num_attr_edge_vec.data();
2080  }
2081 
2082  ex_err = exII::ex_put_concat_all_blocks(ex_id, &params);
2083  EX_CHECK_ERR(ex_err, "Error writing element blocks.");
2084 
2085  // This counter is used to fill up the libmesh_elem_num_to_exodus map in the loop below.
2086  unsigned libmesh_elem_num_to_exodus_counter = 0;
2087 
2088  for (auto & pr : subdomain_map)
2089  {
2090  // Get a reference to a vector of element IDs for this subdomain.
2091  subdomain_map_type::mapped_type & tmp_vec = pr.second;
2092 
2093  // Use the first element in this block to get representative information.
2094  // Note that Exodus assumes all elements in a block are of the same type!
2095  // We are using that same assumption here!
2096  const auto & conv = get_conversion(mesh.elem_ref(tmp_vec[0]).type());
2097  num_nodes_per_elem = mesh.elem_ref(tmp_vec[0]).n_nodes();
2098 
2099  connect.resize(tmp_vec.size()*num_nodes_per_elem);
2100 
2101  for (auto i : index_range(tmp_vec))
2102  {
2103  unsigned int elem_id = tmp_vec[i];
2104  libmesh_elem_num_to_exodus[elem_id] = ++libmesh_elem_num_to_exodus_counter; // 1-based indexing for Exodus
2105 
2106  const Elem & elem = mesh.elem_ref(elem_id);
2107 
2108  // We *might* be able to get away with writing mixed element
2109  // types which happen to have the same number of nodes, but
2110  // do we actually *want* to get away with that?
2111  // .) No visualization software would be able to handle it.
2112  // .) There'd be no way for us to read it back in reliably.
2113  // .) Even elements with the same number of nodes may have different connectivities (?)
2114 
2115  // This needs to be more than an assert so we don't fail
2116  // with a mysterious segfault while trying to write mixed
2117  // element meshes in optimized mode.
2118  if (elem.type() != conv.libmesh_elem_type())
2119  libmesh_error_msg("Error: Exodus requires all elements with a given subdomain ID to be the same type.\n" \
2120  << "Can't write both " \
2121  << Utility::enum_to_string(elem.type()) \
2122  << " and " \
2123  << Utility::enum_to_string(conv.libmesh_elem_type()) \
2124  << " in the same block!");
2125 
2126 
2127  for (unsigned int j=0; j<static_cast<unsigned int>(num_nodes_per_elem); ++j)
2128  {
2129  unsigned int connect_index = cast_int<unsigned int>((i*num_nodes_per_elem)+j);
2130  unsigned elem_node_index = conv.get_inverse_node_map(j); // inverse node map is for writing.
2131  if (!use_discontinuous)
2132  {
2133  // The global id for the current node in libmesh.
2134  dof_id_type libmesh_node_id = elem.node_id(elem_node_index);
2135 
2136  // Write the Exodus global node id associated with
2137  // this libmesh node number to the connectivity
2138  // array, or throw an error if it's not found.
2139  connect[connect_index] =
2140  libmesh_map_find(libmesh_node_num_to_exodus,
2141  cast_int<int>(libmesh_node_id));
2142  }
2143  else
2144  {
2145  // Look up the (elem_id, elem_node_index) pair in the map.
2146  connect[connect_index] =
2147  libmesh_map_find(discontinuous_node_indices,
2148  std::make_pair(elem_id, elem_node_index));
2149  }
2150  }
2151  }
2152 
2153  ex_err = exII::ex_put_conn
2154  (ex_id,
2155  exII::EX_ELEM_BLOCK,
2156  pr.first,
2157  connect.data(), // node_conn
2158  nullptr, // elem_edge_conn (unused)
2159  nullptr); // elem_face_conn (unused)
2160  EX_CHECK_ERR(ex_err, "Error writing element connectivities");
2161 
2162  // This transform command stores its result in a range that
2163  // begins at the third argument, so this command is adding
2164  // values to the elem_num_map vector starting from
2165  // curr_elem_map_end. Here we add 1 to each id to make a
2166  // 1-based exodus file.
2167  curr_elem_map_end = std::transform
2168  (tmp_vec.begin(),
2169  tmp_vec.end(),
2170  curr_elem_map_end,
2171  [](dof_id_type id){return id+1;});
2172  }
2173 
2174  // write out the element number map that we created
2175  ex_err = exII::ex_put_elem_num_map(ex_id, elem_num_map.data());
2176  EX_CHECK_ERR(ex_err, "Error writing element map");
2177 
2178  // Write out the block names
2179  if (num_elem_blk > 0)
2180  {
2181  ex_err = exII::ex_put_names(ex_id, exII::EX_ELEM_BLOCK, names_table.get_char_star_star());
2182  EX_CHECK_ERR(ex_err, "Error writing element block names");
2183  }
2184 
2185  // Write out edge blocks if we have any
2186  for (const auto & pr : edge_id_to_conn)
2187  {
2188  ex_err = exII::ex_put_conn
2189  (ex_id,
2190  exII::EX_EDGE_BLOCK,
2191  pr.first,
2192  pr.second.data(), // node_conn
2193  nullptr, // elem_edge_conn (unused)
2194  nullptr); // elem_face_conn (unused)
2195  EX_CHECK_ERR(ex_err, "Error writing element connectivities");
2196  }
2197 
2198  // Write out the edge block names, if any.
2199  if (num_edge_blk > 0)
2200  {
2201  ex_err = exII::ex_put_names
2202  (ex_id,
2203  exII::EX_EDGE_BLOCK,
2204  edge_block_names_table.get_char_star_star());
2205  EX_CHECK_ERR(ex_err, "Error writing edge block names");
2206  }
2207 }
2208 
2209 
2210 
2211 
2213 {
2214  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2215  return;
2216 
2217  // Maps from sideset id to the element and sides
2218  std::map<int, std::vector<int>> elem;
2219  std::map<int, std::vector<int>> side;
2220  std::vector<boundary_id_type> side_boundary_ids;
2221 
2222  {
2223  // Accumulate the vectors to pass into ex_put_side_set
2224  // build_side_list() returns a vector of (elem, side, bc) tuples.
2225  for (const auto & t : mesh.get_boundary_info().build_side_list())
2226  {
2227  std::vector<const Elem *> family;
2228 #ifdef LIBMESH_ENABLE_AMR
2229 
2233  mesh.elem_ref(std::get<0>(t)).active_family_tree_by_side(family, std::get<1>(t), false);
2234 #else
2235  family.push_back(mesh.elem_ptr(std::get<0>(t)));
2236 #endif
2237 
2238  for (const auto & f : family)
2239  {
2240  const auto & conv = get_conversion(mesh.elem_ptr(f->id())->type());
2241 
2242  // Use the libmesh to exodus data structure map to get the proper sideset IDs
2243  // The data structure contains the "collapsed" contiguous ids
2244  elem[std::get<2>(t)].push_back(libmesh_elem_num_to_exodus[f->id()]);
2245  side[std::get<2>(t)].push_back(conv.get_inverse_side_map(std::get<1>(t)));
2246  }
2247  }
2248 
2249  mesh.get_boundary_info().build_side_boundary_ids(side_boundary_ids);
2250  }
2251 
2252  {
2253  // add data for shell faces, if needed
2254 
2255  // Accumulate the vectors to pass into ex_put_side_set
2256  for (const auto & t : mesh.get_boundary_info().build_shellface_list())
2257  {
2258  std::vector<const Elem *> family;
2259 #ifdef LIBMESH_ENABLE_AMR
2260 
2264  mesh.elem_ref(std::get<0>(t)).active_family_tree_by_side(family, std::get<1>(t), false);
2265 #else
2266  family.push_back(mesh.elem_ptr(std::get<0>(t)));
2267 #endif
2268 
2269  for (const auto & f : family)
2270  {
2271  const auto & conv = get_conversion(mesh.elem_ptr(f->id())->type());
2272 
2273  // Use the libmesh to exodus data structure map to get the proper sideset IDs
2274  // The data structure contains the "collapsed" contiguous ids
2275  elem[std::get<2>(t)].push_back(libmesh_elem_num_to_exodus[f->id()]);
2276  side[std::get<2>(t)].push_back(conv.get_inverse_shellface_map(std::get<1>(t)));
2277  }
2278  }
2279 
2280  std::vector<boundary_id_type> shellface_boundary_ids;
2281  mesh.get_boundary_info().build_shellface_boundary_ids(shellface_boundary_ids);
2282  for (const auto & id : shellface_boundary_ids)
2283  side_boundary_ids.push_back(id);
2284  }
2285 
2286  // Write out the sideset names, but only if there is something to write
2287  if (side_boundary_ids.size() > 0)
2288  {
2289  NamesData names_table(side_boundary_ids.size(), MAX_STR_LENGTH);
2290 
2291  std::vector<exII::ex_set> sets(side_boundary_ids.size());
2292 
2293  for (auto i : index_range(side_boundary_ids))
2294  {
2295  boundary_id_type ss_id = side_boundary_ids[i];
2296  names_table.push_back_entry(mesh.get_boundary_info().get_sideset_name(ss_id));
2297 
2298  sets[i].id = ss_id;
2299  sets[i].type = exII::EX_SIDE_SET;
2300  sets[i].num_entry = elem[ss_id].size();
2301  sets[i].num_distribution_factor = 0;
2302  sets[i].entry_list = elem[ss_id].data();
2303  sets[i].extra_list = side[ss_id].data();
2304  sets[i].distribution_factor_list = nullptr;
2305  }
2306 
2307  ex_err = exII::ex_put_sets(ex_id, side_boundary_ids.size(), sets.data());
2308  EX_CHECK_ERR(ex_err, "Error writing sidesets");
2309 
2310  ex_err = exII::ex_put_names(ex_id, exII::EX_SIDE_SET, names_table.get_char_star_star());
2311  EX_CHECK_ERR(ex_err, "Error writing sideset names");
2312  }
2313 }
2314 
2315 
2316 
2318 {
2319  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2320  return;
2321 
2322  // build_node_list() builds a sorted list of (node-id, bc-id) tuples
2323  // that is sorted by node-id, but we actually want it to be sorted
2324  // by bc-id, i.e. the second argument of the tuple.
2325  typedef std::tuple<dof_id_type, boundary_id_type> tuple_t;
2326  std::vector<tuple_t> bc_tuples =
2327  mesh.get_boundary_info().build_node_list();
2328 
2329  // We use std::stable_sort so that the entries within a single
2330  // nodeset remain in whatever order they were previously in.
2331  std::stable_sort(bc_tuples.begin(), bc_tuples.end(),
2332  [](const tuple_t & t1,
2333  const tuple_t & t2)
2334  { return std::get<1>(t1) < std::get<1>(t2); });
2335 
2336  std::vector<boundary_id_type> node_boundary_ids;
2337  mesh.get_boundary_info().build_node_boundary_ids(node_boundary_ids);
2338 
2339  // Write out the nodeset names, but only if there is something to write
2340  if (node_boundary_ids.size() > 0 &&
2341  bc_tuples.size() > 0)
2342  {
2343  NamesData names_table(node_boundary_ids.size(), MAX_STR_LENGTH);
2344 
2345  // Vectors to be filled and passed to exII::ex_put_concat_sets()
2346  // Use existing class members and avoid variable shadowing.
2347  nodeset_ids.clear();
2348  num_nodes_per_set.clear();
2349  num_node_df_per_set.clear();
2350  node_sets_node_index.clear();
2351  node_sets_node_list.clear();
2352 
2353  // Pre-allocate space
2354  nodeset_ids.reserve(node_boundary_ids.size());
2355  num_nodes_per_set.reserve(node_boundary_ids.size());
2356  num_node_df_per_set.resize(node_boundary_ids.size()); // all zeros
2357  node_sets_node_index.reserve(node_boundary_ids.size());
2358  node_sets_node_list.reserve(bc_tuples.size());
2359 
2360  // Assign entries to node_sets_node_list, keeping track of counts as we go.
2361  std::map<boundary_id_type, unsigned int> nodeset_counts;
2362  for (const auto & t : bc_tuples)
2363  {
2364  const dof_id_type & node_id = std::get<0>(t) + 1; // Note: we use 1-based node ids in Exodus!
2365  const boundary_id_type & nodeset_id = std::get<1>(t);
2366  node_sets_node_list.push_back(node_id);
2367  nodeset_counts[nodeset_id] += 1;
2368  }
2369 
2370  // Fill in other indexing vectors needed by Exodus
2371  unsigned int running_sum = 0;
2372  for (const auto & pr : nodeset_counts)
2373  {
2374  nodeset_ids.push_back(pr.first);
2375  num_nodes_per_set.push_back(pr.second);
2376  node_sets_node_index.push_back(running_sum);
2377  names_table.push_back_entry(mesh.get_boundary_info().get_nodeset_name(pr.first));
2378  running_sum += pr.second;
2379  }
2380 
2381  // Fill in an exII::ex_set_specs object which can then be passed to
2382  // the ex_put_concat_sets() function.
2383  exII::ex_set_specs set_data = {};
2384  set_data.sets_ids = nodeset_ids.data();
2385  set_data.num_entries_per_set = num_nodes_per_set.data();
2386  set_data.num_dist_per_set = num_node_df_per_set.data(); // zeros
2387  set_data.sets_entry_index = node_sets_node_index.data();
2388  set_data.sets_dist_index = node_sets_node_index.data(); // dummy value
2389  set_data.sets_entry_list = node_sets_node_list.data();
2390 
2391  // Write all nodesets together.
2392  ex_err = exII::ex_put_concat_sets(ex_id, exII::EX_NODE_SET, &set_data);
2393  EX_CHECK_ERR(ex_err, "Error writing concatenated nodesets");
2394 
2395  // Write out the nodeset names
2396  ex_err = exII::ex_put_names(ex_id, exII::EX_NODE_SET, names_table.get_char_star_star());
2397  EX_CHECK_ERR(ex_err, "Error writing nodeset names");
2398  }
2399 }
2400 
2401 
2402 
2403 void ExodusII_IO_Helper::initialize_element_variables(std::vector<std::string> names,
2404  const std::vector<std::set<subdomain_id_type>> & vars_active_subdomains)
2405 {
2406  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2407  return;
2408 
2409  // Quick return if there are no element variables to write
2410  if (names.size() == 0)
2411  return;
2412 
2413  // Be sure that variables in the file match what we are asking for
2414  if (num_elem_vars > 0)
2415  {
2416  this->check_existing_vars(ELEMENTAL, names, this->elem_var_names);
2417  return;
2418  }
2419 
2420  // Quick return if we have already called this function
2422  return;
2423 
2424  // Set the flag so we can skip this stuff on subsequent calls to
2425  // initialize_element_variables()
2426  _elem_vars_initialized = true;
2427 
2428  this->write_var_names(ELEMENTAL, names);
2429 
2430  // Use the truth table to indicate which subdomain/variable pairs are
2431  // active according to vars_active_subdomains.
2432  std::vector<int> truth_tab(num_elem_blk*num_elem_vars, 0);
2433  for (auto var_num : index_range(vars_active_subdomains))
2434  {
2435  // If the list of active subdomains is empty, it is interpreted as being
2436  // active on *all* subdomains.
2437  std::set<subdomain_id_type> current_set;
2438  if (vars_active_subdomains[var_num].empty())
2439  for (auto block_id : block_ids)
2440  current_set.insert(cast_int<subdomain_id_type>(block_id));
2441  else
2442  current_set = vars_active_subdomains[var_num];
2443 
2444  // Find index into the truth table for each id in current_set.
2445  for (auto block_id : current_set)
2446  {
2447  auto it = std::find(block_ids.begin(), block_ids.end(), block_id);
2448  if (it == block_ids.end())
2449  libmesh_error_msg("ExodusII_IO_Helper: block id " << block_id << " not found in block_ids.");
2450 
2451  std::size_t block_index =
2452  std::distance(block_ids.begin(), it);
2453 
2454  std::size_t truth_tab_index = block_index*num_elem_vars + var_num;
2455  truth_tab[truth_tab_index] = 1;
2456  }
2457  }
2458 
2459  ex_err = exII::ex_put_elem_var_tab(ex_id,
2460  num_elem_blk,
2461  num_elem_vars,
2462  truth_tab.data());
2463  EX_CHECK_ERR(ex_err, "Error writing element truth table.");
2464 }
2465 
2466 
2467 
2468 void ExodusII_IO_Helper::initialize_nodal_variables(std::vector<std::string> names)
2469 {
2470  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2471  return;
2472 
2473  // Quick return if there are no nodal variables to write
2474  if (names.size() == 0)
2475  return;
2476 
2477  // Quick return if we have already called this function
2479  return;
2480 
2481  // Be sure that variables in the file match what we are asking for
2482  if (num_nodal_vars > 0)
2483  {
2484  this->check_existing_vars(NODAL, names, this->nodal_var_names);
2485  return;
2486  }
2487 
2488  // Set the flag so we can skip the rest of this function on subsequent calls.
2489  _nodal_vars_initialized = true;
2490 
2491  this->write_var_names(NODAL, names);
2492 }
2493 
2494 
2495 
2496 void ExodusII_IO_Helper::initialize_global_variables(std::vector<std::string> names)
2497 {
2498  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2499  return;
2500 
2501  // Quick return if there are no global variables to write
2502  if (names.size() == 0)
2503  return;
2504 
2506  return;
2507 
2508  // Be sure that variables in the file match what we are asking for
2509  if (num_global_vars > 0)
2510  {
2511  this->check_existing_vars(GLOBAL, names, this->global_var_names);
2512  return;
2513  }
2514 
2515  _global_vars_initialized = true;
2516 
2517  this->write_var_names(GLOBAL, names);
2518 }
2519 
2520 
2521 
2523  std::vector<std::string> & names,
2524  std::vector<std::string> & names_from_file)
2525 {
2526  // There may already be global variables in the file (for example,
2527  // if we're appending) and in that case, we
2528  // 1.) Cannot initialize them again.
2529  // 2.) Should check to be sure that the global variable names are the same.
2530 
2531  // Fills up names_from_file for us
2532  this->read_var_names(type);
2533 
2534  // Both the number of variables and their names (up to the first
2535  // MAX_STR_LENGTH characters) must match for the names we are
2536  // planning to write and the names already in the file.
2537  bool match =
2538  std::equal(names.begin(), names.end(),
2539  names_from_file.begin(),
2540  [](const std::string & a,
2541  const std::string & b) -> bool
2542  {
2543  return a.compare(/*pos=*/0, /*len=*/MAX_STR_LENGTH, b) == 0;
2544  });
2545 
2546  if (!match)
2547  {
2548  libMesh::err << "Error! The Exodus file already contains the variables:" << std::endl;
2549  for (const auto & name : names_from_file)
2550  libMesh::err << name << std::endl;
2551 
2552  libMesh::err << "And you asked to write:" << std::endl;
2553  for (const auto & name : names)
2554  libMesh::err << name << std::endl;
2555 
2556  libmesh_error_msg("Cannot overwrite existing variables in Exodus II file.");
2557  }
2558 }
2559 
2560 
2561 
2563 {
2564  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2565  return;
2566 
2567  if (_single_precision)
2568  {
2569  float cast_time = float(time);
2570  ex_err = exII::ex_put_time(ex_id, timestep, &cast_time);
2571  }
2572  else
2573  {
2574  double cast_time = double(time);
2575  ex_err = exII::ex_put_time(ex_id, timestep, &cast_time);
2576  }
2577  EX_CHECK_ERR(ex_err, "Error writing timestep.");
2578 
2579  ex_err = exII::ex_update(ex_id);
2580  EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
2581 }
2582 
2583 
2584 
2585 void
2588  int timestep,
2589  const std::vector<std::string> & var_names,
2590  const std::vector<std::set<boundary_id_type>> & side_ids,
2591  const std::vector<std::map<BoundaryInfo::BCTuple, Real>> & bc_vals)
2592 {
2593  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2594  return;
2595 
2596  // Write the sideset variable names to file. This function should
2597  // only be called once for SIDESET variables, repeated calls to
2598  // write_var_names overwrites/changes the order of names that were
2599  // there previously, and will mess up any data that has already been
2600  // written.
2601  this->write_var_names(SIDESET, var_names);
2602 
2603  // I hope that we are allowed to call read_sideset_info() even
2604  // though we are in the middle of writing? It seems to work provided
2605  // that you have already written the mesh itself... read_sideset_info()
2606  // fills in the following data members:
2607  // .) num_side_sets
2608  // .) ss_ids
2609  this->read_sideset_info();
2610 
2611  // Debugging:
2612  // libMesh::out << "File has " << num_side_sets << " side sets." << std::endl;
2613 
2614  // Write "truth" table for sideset variables. The function
2615  // exII::ex_put_var_param() must be called before
2616  // exII::ex_put_sset_var_tab(). For us, this happens during the call
2617  // to ExodusII_IO_Helper::write_var_names(). sset_var_tab is a logically
2618  // (num_side_sets x num_sset_var) integer array of 0s and 1s
2619  // indicating which sidesets a given sideset variable is defined on.
2620  std::vector<int> sset_var_tab(num_side_sets * var_names.size());
2621 
2622  // We now call read_sideset() once per sideset and write any sideset
2623  // variable values which are defined there.
2624  int offset=0;
2625  for (int ss=0; ss<num_side_sets; ++ss)
2626  {
2627  // We don't know num_sides_per_set for each set until we call
2628  // read_sideset(). The values for each sideset are stored (using
2629  // the offsets) into the 'elem_list' and 'side_list' arrays of
2630  // this class.
2631  offset += (ss > 0 ? num_sides_per_set[ss-1] : 0);
2632  this->read_sideset(ss, offset);
2633 
2634  // Debugging:
2635  // libMesh::out << "Sideset " << ss_ids[ss]
2636  // << " has " << num_sides_per_set[ss]
2637  // << " entries."
2638  // << std::endl;
2639 
2640  // For each variable in var_names, write the values for the
2641  // current sideset, if any.
2642  for (auto var : index_range(var_names))
2643  {
2644  // If this var has no values on this sideset, go to the next one.
2645  if (!side_ids[var].count(ss_ids[ss]))
2646  continue;
2647 
2648  // Otherwise, fill in this entry of the sideset truth table.
2649  sset_var_tab[ss*var_names.size() + var] = 1;
2650 
2651  // Data vector that will eventually be passed to exII::ex_put_sset_var().
2652  std::vector<Real> sset_var_vals(num_sides_per_set[ss]);
2653 
2654  // Get reference to the BCTuple -> Real map for this variable.
2655  const auto & data_map = bc_vals[var];
2656 
2657  // Loop over elem_list, side_list entries in current sideset.
2658  for (int i=0; i<num_sides_per_set[ss]; ++i)
2659  {
2660  // Get elem_id and side_id from the respective lists that
2661  // are filled in by calling read_sideset().
2662  //
2663  // Note: these are Exodus-specific ids, so we have to convert them
2664  // to libmesh ids, as that is what will be in the bc_tuples.
2665  //
2666  // TODO: we should probably consult the exodus_elem_num_to_libmesh
2667  // mapping in order to figure out which libmesh element id 'elem_id'
2668  // actually corresponds to here, instead of just assuming it will be
2669  // off by one. Unfortunately that data structure does not seem to
2670  // be used at the moment. If we assume that write_sideset_data() is
2671  // always called following write(), then this should be a fairly safe
2672  // assumption...
2673  dof_id_type elem_id = elem_list[i + offset] - 1;
2674  unsigned int side_id = side_list[i + offset] - 1;
2675 
2676  // Sanity check: make sure that the "off by one"
2677  // assumption we used above to set 'elem_id' is valid.
2678  if (libmesh_map_find(libmesh_elem_num_to_exodus, cast_int<int>(elem_id))
2679  != elem_list[i + offset])
2680  libmesh_error_msg("Error mapping Exodus elem id to libmesh elem id.");
2681 
2682  // Map from Exodus side ids to libmesh side ids.
2683  const auto & conv = get_conversion(mesh.elem_ptr(elem_id)->type());
2684 
2685  // Map from Exodus side ids to libmesh side ids.
2686  unsigned int converted_side_id = conv.get_side_map(side_id);
2687 
2688  // Debugging:
2689  // libMesh::out << "side_id=" << side_id
2690  // << ", converted_side_id=" << converted_side_id
2691  // << std::endl;
2692 
2693  // Construct a key so we can quickly see whether there is any
2694  // data for this variable in the map.
2695  BoundaryInfo::BCTuple key = std::make_tuple
2696  (elem_id,
2697  converted_side_id,
2698  ss_ids[ss]);
2699 
2700  // Find the data for this (elem,side,id) tuple. Throw an
2701  // error if not found. Then store value in vector which
2702  // will be passed to Exodus.
2703  sset_var_vals[i] = libmesh_map_find(data_map, key);
2704  } // end for (i)
2705 
2706  // As far as I can tell, there is no "concat" version of writing
2707  // sideset data, you have to call ex_put_sset_var() once per (variable,
2708  // sideset) pair.
2709  if (sset_var_vals.size() > 0)
2710  {
2711  ex_err = exII::ex_put_sset_var
2712  (ex_id,
2713  timestep,
2714  var + 1, // 1-based variable index of current variable
2715  ss_ids[ss],
2716  num_sides_per_set[ss],
2717  MappedOutputVector(sset_var_vals, _single_precision).data());
2718  EX_CHECK_ERR(ex_err, "Error writing sideset vars.");
2719  }
2720  } // end for (var)
2721  } // end for (ss)
2722 
2723  // Finally, write the sideset truth table.
2724  ex_err =
2725  exII::ex_put_sset_var_tab(ex_id,
2726  num_side_sets,
2727  cast_int<int>(var_names.size()),
2728  sset_var_tab.data());
2729  EX_CHECK_ERR(ex_err, "Error writing sideset var truth table.");
2730 }
2731 
2732 
2733 
2734 void
2737  int timestep,
2738  std::vector<std::string> & var_names,
2739  std::vector<std::set<boundary_id_type>> & side_ids,
2740  std::vector<std::map<BoundaryInfo::BCTuple, Real>> & bc_vals)
2741 {
2742  // This reads the sideset variable names into the local
2743  // sideset_var_names data structure.
2744  this->read_var_names(SIDESET);
2745 
2746  if (num_sideset_vars)
2747  {
2748  // Read the sideset data truth table
2749  std::vector<int> sset_var_tab(num_side_sets * num_sideset_vars);
2750  ex_err = exII::ex_get_sset_var_tab
2751  (ex_id,
2752  num_side_sets,
2754  sset_var_tab.data());
2755  EX_CHECK_ERR(ex_err, "Error reading sideset variable truth table.");
2756 
2757  // Set up/allocate space in incoming data structures.
2758  var_names = sideset_var_names;
2759  side_ids.resize(num_sideset_vars);
2760  bc_vals.resize(num_sideset_vars);
2761 
2762  // Read the sideset data.
2763  //
2764  // Note: we assume that read_sideset() has already been called
2765  // for each sideset, so the required values in elem_list and
2766  // side_list are already present.
2767  //
2768  // TODO: As a future optimization, we could read only the values
2769  // requested by the user by looking at the input parameter
2770  // var_names and checking whether it already has entries in
2771  // it. We could do the same thing with the input side_ids
2772  // container and only read values for requested sidesets.
2773  int offset=0;
2774  for (int ss=0; ss<num_side_sets; ++ss)
2775  {
2776  offset += (ss > 0 ? num_sides_per_set[ss-1] : 0);
2777  for (int var=0; var<num_sideset_vars; ++var)
2778  {
2779  int is_present = sset_var_tab[num_sideset_vars*ss + var];
2780 
2781  if (is_present)
2782  {
2783  // Record the fact that this variable is defined on this sideset.
2784  side_ids[var].insert(ss_ids[ss]);
2785 
2786  // Note: the assumption here is that a previous call
2787  // to this->read_sideset_info() has already set the
2788  // values of num_sides_per_set, so we just use those values here.
2789  std::vector<Real> sset_var_vals(num_sides_per_set[ss]);
2790  ex_err = exII::ex_get_sset_var
2791  (ex_id,
2792  timestep,
2793  var + 1, // 1-based sideset variable index!
2794  ss_ids[ss],
2795  num_sides_per_set[ss],
2796  MappedInputVector(sset_var_vals, _single_precision).data());
2797  EX_CHECK_ERR(ex_err, "Error reading sideset variable.");
2798 
2799  // Debugging:
2800  // libMesh::out << "Variable " << sideset_var_names[var]
2801  // << " is defined on side set " << ss_ids[ss]
2802  // << " and has values: " << std::endl;
2803  // for (int i=0; i<num_sides_per_set[ss]; ++i)
2804  // libMesh::out << sset_var_vals[i] << " ";
2805  // libMesh::out << std::endl;
2806 
2807  for (int i=0; i<num_sides_per_set[ss]; ++i)
2808  {
2809  dof_id_type exodus_elem_id = elem_list[i + offset];
2810  unsigned int exodus_side_id = side_list[i + offset];
2811 
2812  // FIXME: We should use exodus_elem_num_to_libmesh for this,
2813  // but it apparently is never set up, so just
2814  // subtract 1 from the Exodus elem id.
2815  dof_id_type converted_elem_id = exodus_elem_id - 1;
2816 
2817  // Map Exodus side id to libmesh side id.
2818  // Map from Exodus side ids to libmesh side ids.
2819  const auto & conv = get_conversion(mesh.elem_ptr(converted_elem_id)->type());
2820 
2821  // Map from Exodus side id to libmesh side id.
2822  // Note: the mapping is defined on 0-based indices, so subtract
2823  // 1 before doing the mapping.
2824  unsigned int converted_side_id = conv.get_side_map(exodus_side_id - 1);
2825 
2826  // Debugging:
2827  // libMesh::out << "exodus_elem_id = " << exodus_elem_id
2828  // << "\n"
2829  // << "converted_elem_id = " << converted_elem_id
2830  // << "\n\n"
2831  // << "exodus_side_id = " << exodus_side_id
2832  // << "\n"
2833  // << "converted_side_id = " << converted_side_id
2834  // << std::endl;
2835 
2836  // Make a BCTuple key from the converted information.
2837  BoundaryInfo::BCTuple key = std::make_tuple
2838  (converted_elem_id,
2839  converted_side_id,
2840  ss_ids[ss]);
2841 
2842  // Store (elem, side, b_id) tuples in bc_vals[var]
2843  bc_vals[var].insert(std::make_pair(key, sset_var_vals[i]));
2844  } // end for (i)
2845  } // end if (present)
2846  } // end for (var)
2847  } // end for (ss)
2848  } // end if (num_sideset_vars)
2849 }
2850 
2851 
2852 
2854 (const MeshBase & mesh,
2855  const std::vector<Real> & values,
2856  int timestep,
2857  const std::vector<std::set<subdomain_id_type>> & vars_active_subdomains)
2858 {
2859  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2860  return;
2861 
2862  // Ask the file how many element vars it has, store it in the num_elem_vars variable.
2863  ex_err = exII::ex_get_var_param(ex_id, "e", &num_elem_vars);
2864  EX_CHECK_ERR(ex_err, "Error reading number of elemental variables.");
2865 
2866  // We will eventually loop over the element blocks (subdomains) and
2867  // write the data one block at a time. Build a data structure that
2868  // maps each subdomain to a list of element ids it contains.
2869  std::map<subdomain_id_type, std::vector<unsigned int>> subdomain_map;
2870  for (const auto & elem : mesh.active_element_ptr_range())
2871  subdomain_map[elem->subdomain_id()].push_back(elem->id());
2872 
2873  // Use mesh.n_elem() to access into the values vector rather than
2874  // the number of elements the Exodus writer thinks the mesh has,
2875  // which may not include inactive elements.
2876  dof_id_type n_elem = mesh.n_elem();
2877 
2878  // Sanity check: we must have an entry in vars_active_subdomains for
2879  // each variable that we are potentially writing out.
2880  libmesh_assert_equal_to
2881  (vars_active_subdomains.size(),
2882  static_cast<unsigned>(num_elem_vars));
2883 
2884  // For each variable, create a 'data' array which holds all the elemental variable
2885  // values *for a given block* on this processor, then write that data vector to file
2886  // before moving onto the next block.
2887  for (unsigned int var_id=0; var_id<static_cast<unsigned>(num_elem_vars); ++var_id)
2888  {
2889  // The size of the subdomain map is the number of blocks.
2890  auto it = subdomain_map.begin();
2891 
2892  // Reference to the set of active subdomains for the current variable.
2893  const auto & active_subdomains
2894  = vars_active_subdomains[var_id];
2895 
2896  for (unsigned int j=0; it!=subdomain_map.end(); ++it, ++j)
2897  {
2898  // Skip any variable/subdomain pairs that are inactive.
2899  // Note that if active_subdomains is empty, it is interpreted
2900  // as being active on *all* subdomains.
2901  if (!(active_subdomains.empty() || active_subdomains.count(it->first)))
2902  continue;
2903 
2904  // Get reference to list of elem ids which are in the
2905  // current subdomain and count, allocate storage to hold
2906  // data that will be written to file.
2907  const auto & elem_nums = it->second;
2908  const unsigned int num_elems_this_block =
2909  cast_int<unsigned int>(elem_nums.size());
2910  std::vector<Real> data(num_elems_this_block);
2911 
2912  // variable-major ordering is:
2913  // (u1, u2, u3, ..., uN), (v1, v2, v3, ..., vN), ...
2914  // where N is the number of elements.
2915  for (unsigned int k=0; k<num_elems_this_block; ++k)
2916  data[k] = values[var_id*n_elem + elem_nums[k]];
2917 
2918  ex_err = exII::ex_put_elem_var
2919  (ex_id,
2920  timestep,
2921  var_id+1,
2922  this->get_block_id(j),
2923  num_elems_this_block,
2925 
2926  EX_CHECK_ERR(ex_err, "Error writing element values.");
2927  }
2928  }
2929 
2930  ex_err = exII::ex_update(ex_id);
2931  EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
2932 }
2933 
2934 
2935 
2937 (const MeshBase & mesh,
2938  const std::vector<Real> & values,
2939  int timestep,
2940  const std::vector<std::set<subdomain_id_type>> & vars_active_subdomains,
2941  const std::vector<std::string> & derived_var_names,
2942  const std::map<subdomain_id_type, std::vector<std::string>> & subdomain_to_var_names)
2943 {
2944  if ((_run_only_on_proc0) && (this->processor_id() != 0))
2945  return;
2946 
2947  // Ask the file how many element vars it has, store it in the num_elem_vars variable.
2948  ex_err = exII::ex_get_var_param(ex_id, "e", &num_elem_vars);
2949  EX_CHECK_ERR(ex_err, "Error reading number of elemental variables.");
2950 
2951  // We will eventually loop over the element blocks (subdomains) and
2952  // write the data one block (subdomain) at a time. Build a data
2953  // structure that keeps track of how many elements are in each
2954  // subdomain. This will allow us to reserve space in the data vector
2955  // we are going to write.
2956  std::map<subdomain_id_type, unsigned int> subdomain_to_n_elem;
2957  for (const auto & elem : mesh.active_element_ptr_range())
2958  subdomain_to_n_elem[elem->subdomain_id()] += 1;
2959 
2960  // Sanity check: we must have an entry in vars_active_subdomains for
2961  // each variable that we are potentially writing out.
2962  libmesh_assert_equal_to
2963  (vars_active_subdomains.size(),
2964  static_cast<unsigned>(num_elem_vars));
2965 
2966  // The size of the subdomain map is the number of blocks.
2967  auto subdomain_to_n_elem_iter = subdomain_to_n_elem.begin();
2968 
2969  // Store range of active Elem pointers. We are going to loop over
2970  // the elements n_vars * n_subdomains times, so let's make sure
2971  // the predicated iterators aren't slowing us down too much.
2972  ConstElemRange elem_range
2973  (mesh.active_elements_begin(),
2974  mesh.active_elements_end());
2975 
2976  for (unsigned int sbd_idx=0;
2977  subdomain_to_n_elem_iter != subdomain_to_n_elem.end();
2978  ++subdomain_to_n_elem_iter, ++sbd_idx)
2979  for (unsigned int var_id=0; var_id<static_cast<unsigned>(num_elem_vars); ++var_id)
2980  {
2981  // Reference to the set of active subdomains for the current variable.
2982  const auto & active_subdomains
2983  = vars_active_subdomains[var_id];
2984 
2985  // If the vars_active_subdomains container passed to this function
2986  // has an empty entry, it means the variable really is not active on
2987  // _any_ subdomains, not that it is active on _all_ subdomains. This
2988  // is just due to the way that we build the vars_active_subdomains
2989  // container.
2990  if (!active_subdomains.count(subdomain_to_n_elem_iter->first))
2991  continue;
2992 
2993  // Vector to hold values that will be written to Exodus file.
2994  std::vector<Real> data;
2995  data.reserve(subdomain_to_n_elem_iter->second);
2996 
2997  unsigned int values_offset = 0;
2998  for (auto & elem : elem_range)
2999  {
3000  // We'll use the Elem's subdomain id in several places below.
3001  subdomain_id_type sbd_id = elem->subdomain_id();
3002 
3003  // Get reference to the list of variable names defining
3004  // the indexing for the current Elem's subdomain.
3005  auto subdomain_to_var_names_iter =
3006  subdomain_to_var_names.find(sbd_id);
3007 
3008  // It's possible, but unusual, for there to be an Elem
3009  // from a subdomain that has no active variables from the
3010  // set of variables we are currently writing. If that
3011  // happens, we can just go to the next Elem because we
3012  // don't need to advance the offset into the values
3013  // vector, etc.
3014  if (subdomain_to_var_names_iter == subdomain_to_var_names.end())
3015  continue;
3016 
3017  const auto & var_names_this_sbd
3018  = subdomain_to_var_names_iter->second;
3019 
3020  // Only extract values if Elem is in the current subdomain.
3021  if (sbd_id == subdomain_to_n_elem_iter->first)
3022  {
3023  // Location of current var_id in the list of all variables on this
3024  // subdomain. FIXME: linear search but it's over a typically relatively
3025  // short vector of active variable names on this subdomain. We could do
3026  // a nested std::map<string,index> instead of a std::vector where the
3027  // location of the string is implicitly the index..
3028  auto pos =
3029  std::find(var_names_this_sbd.begin(),
3030  var_names_this_sbd.end(),
3031  derived_var_names[var_id]);
3032 
3033  if (pos == var_names_this_sbd.end())
3034  libmesh_error_msg("Derived name " << derived_var_names[var_id] << " not found!");
3035 
3036  // Find the current variable's location in the list of all variable
3037  // names on the current Elem's subdomain.
3038  auto true_index =
3039  std::distance(var_names_this_sbd.begin(), pos);
3040 
3041  data.push_back(values[values_offset + true_index]);
3042  }
3043 
3044  // The "true" offset is how much we have to advance the index for each Elem
3045  // in this subdomain.
3046  auto true_offset = var_names_this_sbd.size();
3047 
3048  // Increment to the next Elem's values
3049  values_offset += true_offset;
3050  } // for elem
3051 
3052  // Now write 'data' to Exodus file, in single precision if requested.
3053  if (!data.empty())
3054  {
3055  ex_err = exII::ex_put_elem_var
3056  (ex_id, timestep, var_id+1, this->get_block_id(sbd_idx), data.size(),
3058 
3059  EX_CHECK_ERR(ex_err, "Error writing element values.");
3060  }
3061  } // for each var_id
3062 
3063  ex_err = exII::ex_update(ex_id);
3064  EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
3065 }
3066 
3067 
3068 
3069 void
3071  const std::vector<Real> & values,
3072  int timestep)
3073 {
3074  if ((_run_only_on_proc0) && (this->processor_id() != 0))
3075  return;
3076 
3077  if (!values.empty())
3078  {
3079  ex_err = exII::ex_put_nodal_var
3080  (ex_id, timestep, var_id, num_nodes,
3082 
3083  EX_CHECK_ERR(ex_err, "Error writing nodal values.");
3084 
3085  ex_err = exII::ex_update(ex_id);
3086  EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
3087  }
3088 }
3089 
3090 
3091 
3092 void ExodusII_IO_Helper::write_information_records(const std::vector<std::string> & records)
3093 {
3094  if ((_run_only_on_proc0) && (this->processor_id() != 0))
3095  return;
3096 
3097  // There may already be information records in the file (for
3098  // example, if we're appending) and in that case, according to the
3099  // Exodus documentation, writing more information records is not
3100  // supported.
3101  int num_info = inquire(exII::EX_INQ_INFO, "Error retrieving the number of information records from file!");
3102  if (num_info > 0)
3103  {
3104  libMesh::err << "Warning! The Exodus file already contains information records.\n"
3105  << "Exodus does not support writing additional records in this situation."
3106  << std::endl;
3107  return;
3108  }
3109 
3110  int num_records = cast_int<int>(records.size());
3111 
3112  if (num_records > 0)
3113  {
3114  NamesData info(num_records, MAX_LINE_LENGTH);
3115 
3116  // If an entry is longer than MAX_LINE_LENGTH characters it's not an error, we just
3117  // write the first MAX_LINE_LENGTH characters to the file.
3118  for (const auto & record : records)
3119  info.push_back_entry(record);
3120 
3121  ex_err = exII::ex_put_info(ex_id, num_records, info.get_char_star_star());
3122  EX_CHECK_ERR(ex_err, "Error writing global values.");
3123 
3124  ex_err = exII::ex_update(ex_id);
3125  EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
3126  }
3127 }
3128 
3129 
3130 
3131 void ExodusII_IO_Helper::write_global_values(const std::vector<Real> & values, int timestep)
3132 {
3133  if ((_run_only_on_proc0) && (this->processor_id() != 0))
3134  return;
3135 
3136  if (!values.empty())
3137  {
3138  ex_err = exII::ex_put_glob_vars
3139  (ex_id, timestep, num_global_vars,
3141 
3142  EX_CHECK_ERR(ex_err, "Error writing global values.");
3143 
3144  ex_err = exII::ex_update(ex_id);
3145  EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
3146  }
3147 }
3148 
3149 
3150 
3151 void ExodusII_IO_Helper::read_global_values(std::vector<Real> & values, int timestep)
3152 {
3153  if ((_run_only_on_proc0) && (this->processor_id() != 0))
3154  return;
3155 
3156  values.clear();
3157  values.resize(num_global_vars);
3158  ex_err = exII::ex_get_glob_vars
3159  (ex_id, timestep, num_global_vars,
3161 
3162  EX_CHECK_ERR(ex_err, "Error reading global values.");
3163 }
3164 
3165 
3166 
3168 {
3170 }
3171 
3172 
3173 
3175 {
3177 }
3178 
3179 
3180 
3182 {
3183  _coordinate_offset = p;
3184 }
3185 
3186 
3187 std::vector<std::string>
3188 ExodusII_IO_Helper::get_complex_names(const std::vector<std::string> & names) const
3189 {
3190  std::vector<std::string> complex_names;
3191 
3192  // This will loop over all names and create new "complex" names
3193  // (i.e. names that start with r_, i_ or a_
3194  for (const auto & name : names)
3195  {
3196  complex_names.push_back("r_" + name);
3197  complex_names.push_back("i_" + name);
3198  complex_names.push_back("a_" + name);
3199  }
3200 
3201  return complex_names;
3202 }
3203 
3204 
3205 
3206 std::vector<std::set<subdomain_id_type>> ExodusII_IO_Helper::get_complex_vars_active_subdomains(
3207  const std::vector<std::set<subdomain_id_type>> & vars_active_subdomains) const
3208 {
3209  std::vector<std::set<subdomain_id_type>> complex_vars_active_subdomains;
3210 
3211  for (auto & s : vars_active_subdomains)
3212  {
3213  // Push back the same data three times to match the tripling of the variables
3214  // for the real, imag, and modulus for the complex-valued solution.
3215  complex_vars_active_subdomains.push_back(s);
3216  complex_vars_active_subdomains.push_back(s);
3217  complex_vars_active_subdomains.push_back(s);
3218  }
3219 
3220  return complex_vars_active_subdomains;
3221 }
3222 
3223 
3224 
3225 std::map<subdomain_id_type, std::vector<std::string>>
3228  const std::map<subdomain_id_type, std::vector<std::string>> & subdomain_to_var_names) const
3229 {
3230  // Eventual return value
3231  std::map<subdomain_id_type, std::vector<std::string>> ret;
3232 
3233  for (const auto & pr : subdomain_to_var_names)
3234  {
3235  // Initialize entry for current subdomain
3236  auto & vec = ret[pr.first];
3237 
3238  // Get list of non-complex variable names active on this subdomain.
3239  const auto & varnames = pr.second;
3240 
3241  // Allocate space for 3x the number of entries
3242  vec.reserve(3 * varnames.size());
3243 
3244  // For each varname in the input map, write three variable names
3245  // to the output formed by prepending "r_", "i_", and "a_",
3246  // respectively.
3247  for (const auto & varname : varnames)
3248  {
3249  vec.push_back("r_" + varname);
3250  vec.push_back("i_" + varname);
3251  vec.push_back("a_" + varname);
3252  }
3253  }
3254  return ret;
3255 }
3256 
3257 
3258 
3260 {
3261  if (!node_map)
3262  return i;
3263 
3264  libmesh_assert_less (i, node_map->size());
3265  return (*node_map)[i];
3266 }
3267 
3268 
3269 
3271 {
3272  if (!inverse_node_map)
3273  return i;
3274 
3275  libmesh_assert_less (i, inverse_node_map->size());
3276  return (*inverse_node_map)[i];
3277 }
3278 
3279 
3280 
3282 {
3283  if (!side_map)
3284  return i;
3285 
3286  // If we asked for a side that doesn't exist, return an invalid_id
3287  // and allow higher-level code to handle it.
3288  if (static_cast<size_t>(i) >= side_map->size())
3289  return invalid_id;
3290 
3291  return (*side_map)[i];
3292 }
3293 
3294 
3295 
3297 {
3298  // For identity side mappings, we our convention is to return a 1-based index.
3299  if (!inverse_side_map)
3300  return i + 1;
3301 
3302  libmesh_assert_less (i, inverse_side_map->size());
3303  return (*inverse_side_map)[i];
3304 }
3305 
3306 
3307 
3313 {
3314  if (!shellface_map)
3315  return i;
3316 
3317  libmesh_assert_less (i, shellface_map->size());
3318  return (*shellface_map)[i];
3319 }
3320 
3321 
3322 
3324 {
3325  if (!inverse_shellface_map)
3326  return i + 1;
3327 
3328  libmesh_assert_less (i, inverse_shellface_map->size());
3329  return (*inverse_shellface_map)[i];
3330 }
3331 
3332 
3333 
3335 {
3336  return libmesh_type;
3337 }
3338 
3339 
3340 
3342 {
3343  return exodus_type;
3344 }
3345 
3346 
3347 
3352 {
3353  return shellface_index_offset;
3354 }
3355 
3356 ExodusII_IO_Helper::NamesData::NamesData(size_t n_strings, size_t string_length) :
3357  data_table(n_strings),
3358  data_table_pointers(n_strings),
3359  counter(0),
3360  table_size(n_strings)
3361 {
3362  for (size_t i=0; i<n_strings; ++i)
3363  {
3364  data_table[i].resize(string_length + 1);
3365 
3366  // Properly terminate these C-style strings, just to be safe.
3367  data_table[i][0] = '\0';
3368 
3369  // Set pointer into the data_table
3370  data_table_pointers[i] = data_table[i].data();
3371  }
3372 }
3373 
3374 
3375 
3377 {
3378  libmesh_assert_less (counter, table_size);
3379 
3380  // 1.) Copy the C++ string into the vector<char>...
3381  size_t num_copied = name.copy(data_table[counter].data(), data_table[counter].size()-1);
3382 
3383  // 2.) ...And null-terminate it.
3384  data_table[counter][num_copied] = '\0';
3385 
3386  // Go to next row
3387  ++counter;
3388 }
3389 
3390 
3391 
3393 {
3394  return data_table_pointers.data();
3395 }
3396 
3397 
3398 
3400 {
3401  if (static_cast<unsigned>(i) >= table_size)
3402  libmesh_error_msg("Requested char * " << i << " but only have " << table_size << "!");
3403 
3404  else
3405  return data_table[i].data();
3406 }
3407 
3408 
3409 } // namespace libMesh
3410 
3411 
3412 
3413 #endif // #ifdef LIBMESH_HAVE_EXODUS_API
libMesh::ExodusII_IO_Helper::ExodusII_IO_Helper
ExodusII_IO_Helper(const ParallelObject &parent, bool v=false, bool run_only_on_proc0=true, bool single_precision=false)
Constructor.
Definition: exodusII_io_helper.C:98
libMesh::ExodusII_IO_Helper::opened_for_writing
bool opened_for_writing
Definition: exodusII_io_helper.h:667
libMesh::ExodusII_IO_Helper::id_to_ss_names
std::map< int, std::string > id_to_ss_names
Definition: exodusII_io_helper.h:659
libMesh::ExodusII_IO_Helper::NamesData::data_table
std::vector< std::vector< char > > data_table
Definition: exodusII_io_helper.h:1012
libMesh::ExodusII_IO_Helper::get_block_id
int get_block_id(int index)
Get the block number for the given block index.
Definition: exodusII_io_helper.C:742
libMesh::HEX20
Definition: enum_elem_type.h:48
libMesh::ExodusII_IO_Helper::global_var_names
std::vector< std::string > global_var_names
Definition: exodusII_io_helper.h:651
libMesh::dof_id_type
uint8_t dof_id_type
Definition: id_types.h:67
libMesh::PRISM6
Definition: enum_elem_type.h:50
libMesh::BoundaryInfo
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
Definition: boundary_info.h:57
libMesh::ExodusII_IO_Helper::get_conversion
const ExodusII_IO_Helper::Conversion & get_conversion(const ElemType type) const
Definition: exodusII_io_helper.C:383
libMesh::ExodusII_IO_Helper::node_num_map
std::vector< int > node_num_map
Definition: exodusII_io_helper.h:596
libMesh::ExodusII_IO_Helper::_single_precision
bool _single_precision
Definition: exodusII_io_helper.h:740
libMesh::ExodusII_IO_Helper::NamesData::data_table_pointers
std::vector< char * > data_table_pointers
Definition: exodusII_io_helper.h:1013
libMesh::ExodusII_IO_Helper::node_sets_dist_index
std::vector< int > node_sets_dist_index
Definition: exodusII_io_helper.h:573
libMesh::ExodusII_IO_Helper::libmesh_node_num_to_exodus
std::map< int, int > libmesh_node_num_to_exodus
Definition: exodusII_io_helper.h:623
libMesh::ExodusII_IO_Helper::MappedInputVector::single_precision
bool single_precision
Definition: exodusII_io_helper.h:790
libMesh::ExodusII_IO_Helper::Conversion::exodus_elem_type
std::string exodus_elem_type() const
Definition: exodusII_io_helper.C:3341
libMesh::ExodusII_IO_Helper::current_filename
std::string current_filename
Definition: exodusII_io_helper.h:681
libMesh::MeshTools::n_elem
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
Count up the number of elements of a specific type (as defined by an iterator range).
Definition: mesh_tools.C:705
libMesh::ExodusII_IO_Helper::Conversion
Definition: exodusII_io_helper.h:834
libMesh::ExodusII_IO_Helper::read_elemental_var_values
void read_elemental_var_values(std::string elemental_var_name, int time_step, std::map< dof_id_type, Real > &elem_var_value_map)
Reads elemental values for the variable 'elemental_var_name' at the specified timestep into the 'elem...
Definition: exodusII_io_helper.C:1476
libMesh::ExodusII_IO_Helper::side_list
std::vector< int > side_list
Definition: exodusII_io_helper.h:587
libMesh::ExodusII_IO_Helper::check_existing_vars
void check_existing_vars(ExodusVarType type, std::vector< std::string > &names, std::vector< std::string > &names_from_file)
When appending: during initialization, check that variable names in the file match those you attempt ...
Definition: exodusII_io_helper.C:2522
libMesh::ExodusII_IO_Helper::read_header
void read_header()
Reads an ExodusII mesh file header.
Definition: exodusII_io_helper.C:532
libMesh::ExodusII_IO_Helper::write_element_values
void write_element_values(const MeshBase &mesh, const std::vector< Real > &values, int timestep, const std::vector< std::set< subdomain_id_type >> &vars_active_subdomains)
Writes the vector of values to the element variables.
Definition: exodusII_io_helper.C:2854
libMesh::ExodusII_IO_Helper::read_sideset_data
void read_sideset_data(const MeshBase &mesh, int timestep, std::vector< std::string > &var_names, std::vector< std::set< boundary_id_type >> &side_ids, std::vector< std::map< BoundaryInfo::BCTuple, Real >> &bc_vals)
Read sideset variables, if any, into the provided data structures.
Definition: exodusII_io_helper.C:2736
libMesh::ExodusII_IO_Helper::y
std::vector< Real > y
Definition: exodusII_io_helper.h:605
libMesh::ExodusII_IO_Helper::write_var_names
void write_var_names(ExodusVarType type, const std::vector< std::string > &names)
Wraps calls to exII::ex_put_var_names() and exII::ex_put_var_param().
Definition: exodusII_io_helper.C:1408
libMesh::ExodusII_IO_Helper::Conversion::get_node_map
int get_node_map(int i) const
Definition: exodusII_io_helper.C:3259
libMesh::ExodusII_IO_Helper::MappedOutputVector::float_vec
std::vector< float > float_vec
Definition: exodusII_io_helper.h:768
libMesh::HEX8
Definition: enum_elem_type.h:47
libMesh::ExodusII_IO_Helper::nodal_var_names
std::vector< std::string > nodal_var_names
Definition: exodusII_io_helper.h:636
libMesh::BoundaryInfo::get_edgeset_name
const std::string & get_edgeset_name(boundary_id_type id) const
Definition: boundary_info.C:2362
libMesh::ExodusII_IO_Helper::num_nodal_vars
int num_nodal_vars
Definition: exodusII_io_helper.h:633
libMesh::ExodusII_IO_Helper::block_ids
std::vector< int > block_ids
Definition: exodusII_io_helper.h:541
libMesh::ExodusII_IO_Helper::print_header
void print_header()
Prints the ExodusII mesh file header, which includes the mesh title, the number of nodes,...
Definition: exodusII_io_helper.C:625
libMesh::ExodusII_IO_Helper::libmesh_elem_num_to_exodus
std::map< int, int > libmesh_elem_num_to_exodus
Definition: exodusII_io_helper.h:618
libMesh::ExodusII_IO_Helper::get_side_set_id
int get_side_set_id(int index)
Get the side set id for the given side set index.
Definition: exodusII_io_helper.C:760
libMesh::ExodusII_IO_Helper::edge_block_ids
std::vector< int > edge_block_ids
Definition: exodusII_io_helper.h:544
libMesh::index_range
IntRange< std::size_t > index_range(const std::vector< T > &vec)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:106
libMesh
The libMesh namespace provides an interface to certain functionality in the library.
Definition: factoryfunction.C:55
libMesh::ExodusII_IO_Helper::num_nodes_per_elem
int num_nodes_per_elem
Definition: exodusII_io_helper.h:532
libMesh::TET10
Definition: enum_elem_type.h:46
libMesh::ExodusII_IO_Helper::num_node_df_per_set
std::vector< int > num_node_df_per_set
Definition: exodusII_io_helper.h:565
libMesh::ExodusII_IO_Helper::num_sides_per_set
std::vector< int > num_sides_per_set
Definition: exodusII_io_helper.h:556
libMesh::ExodusII_IO_Helper::_run_only_on_proc0
bool _run_only_on_proc0
Definition: exodusII_io_helper.h:716
equal
bool equal(const variant_filter_iterator &other) const
Forwards to the equal() function defined for the IterBase pointer.
Definition: variant_filter_iterator.h:470
libMesh::ExodusII_IO_Helper::MappedOutputVector::double_vec
std::vector< double > double_vec
Definition: exodusII_io_helper.h:767
libMesh::ExodusII_IO_Helper::create
virtual void create(std::string filename)
Opens an ExodusII mesh file named filename for writing.
Definition: exodusII_io_helper.C:1558
libMesh::ExodusII_IO_Helper::read_nodal_var_values
void read_nodal_var_values(std::string nodal_var_name, int time_step)
Reads the nodal values for the variable 'nodal_var_name' at the specified time into the 'nodal_var_va...
Definition: exodusII_io_helper.C:1291
libMesh::ExodusII_IO_Helper::opened_for_reading
bool opened_for_reading
Definition: exodusII_io_helper.h:671
libMesh::ExodusII_IO_Helper::num_global_vars
int num_global_vars
Definition: exodusII_io_helper.h:501
libMesh::ExodusII_IO_Helper::Conversion::get_shellface_map
int get_shellface_map(int i) const
Definition: exodusII_io_helper.C:3312
libMesh::ExodusII_IO_Helper::_coordinate_offset
Point _coordinate_offset
Definition: exodusII_io_helper.h:737
libMesh::ExodusII_IO_Helper::write_as_dimension
void write_as_dimension(unsigned dim)
Sets the value of _write_as_dimension.
Definition: exodusII_io_helper.C:3174
libMesh::Elem::node_index_range
IntRange< unsigned short > node_index_range() const
Definition: elem.h:2170
libMesh::ExodusII_IO_Helper::num_nodes
int num_nodes
Definition: exodusII_io_helper.h:507
libMesh::ExodusII_IO_Helper::get_side_set_name
std::string get_side_set_name(int index)
Get the side set name for the given side set index if supplied in the mesh file.
Definition: exodusII_io_helper.C:769
libMesh::ExodusII_IO_Helper::read_nodes
void read_nodes()
Reads the nodal data (x,y,z coordinates) from the ExodusII mesh file.
Definition: exodusII_io_helper.C:639
libMesh::BoundaryInfo::n_edge_conds
std::size_t n_edge_conds() const
Definition: boundary_info.C:1636
mesh
MeshBase & mesh
Definition: mesh_communication.C:1257
libMesh::MeshBase::mesh_dimension
unsigned int mesh_dimension() const
Definition: mesh_base.C:135
libMesh::ExodusII_IO_Helper::num_side_sets
int num_side_sets
Definition: exodusII_io_helper.h:526
libMesh::ExodusII_IO_Helper::id_to_ns_names
std::map< int, std::string > id_to_ns_names
Definition: exodusII_io_helper.h:660
libMesh::ExodusII_IO_Helper::conversion_map
std::map< ElemType, ExodusII_IO_Helper::Conversion > conversion_map
Associates libMesh ElemTypes with node/face/edge/etc.
Definition: exodusII_io_helper.h:823
libMesh::boundary_id_type
int8_t boundary_id_type
Definition: id_types.h:51
libMesh::ExodusII_IO_Helper::_global_vars_initialized
bool _global_vars_initialized
Definition: exodusII_io_helper.h:722
libMesh::BoundaryInfo::edgeset_name
std::string & edgeset_name(boundary_id_type id)
Definition: boundary_info.C:2374
libMesh::ExodusII_IO_Helper::read_sideset
void read_sideset(int id, int offset)
Reads information about sideset id and inserts it into the global sideset array at the position offse...
Definition: exodusII_io_helper.C:1086
dim
unsigned int dim
Definition: adaptivity_ex3.C:113
libMesh::ExodusII_IO_Helper::read_block_info
void read_block_info()
Reads information for all of the blocks in the ExodusII mesh file.
Definition: exodusII_io_helper.C:692
libMesh::ExodusII_IO_Helper::node_sets_node_list
std::vector< int > node_sets_node_list
Definition: exodusII_io_helper.h:577
libMesh::ExodusII_IO_Helper::read_num_time_steps
void read_num_time_steps()
Reads the number of timesteps currently stored in the Exodus file and stores it in the num_time_steps...
Definition: exodusII_io_helper.C:1283
libMesh::TET4
Definition: enum_elem_type.h:45
libMesh::BoundaryInfo::build_node_boundary_ids
void build_node_boundary_ids(std::vector< boundary_id_type > &b_ids) const
Builds the list of unique node boundary ids.
Definition: boundary_info.C:1574
libMesh::PRISM15
Definition: enum_elem_type.h:51
libMesh::ExodusII_IO_Helper::num_dim
int num_dim
Definition: exodusII_io_helper.h:498
libMesh::ExodusII_IO_Helper::read_node_num_map
void read_node_num_map()
Reads the optional node_num_map from the ExodusII mesh file.
Definition: exodusII_io_helper.C:660
libMesh::ExodusII_IO_Helper::MappedOutputVector::single_precision
bool single_precision
Definition: exodusII_io_helper.h:766
libMesh::libmesh_assert
libmesh_assert(ctx)
libMesh::ExodusII_IO_Helper::get_complex_vars_active_subdomains
std::vector< std::set< subdomain_id_type > > get_complex_vars_active_subdomains(const std::vector< std::set< subdomain_id_type >> &vars_active_subdomains) const
returns a "tripled" copy of vars_active_subdomains, which is necessary in the complex-valued case.
Definition: exodusII_io_helper.C:3206
libMesh::ExodusII_IO_Helper::read_time_steps
void read_time_steps()
Reads and stores the timesteps in the 'time_steps' array.
Definition: exodusII_io_helper.C:1266
libMesh::ExodusII_IO_Helper::print_nodes
void print_nodes(std::ostream &out=libMesh::out)
Prints the nodal information, by default to libMesh::out.
Definition: exodusII_io_helper.C:684
libMesh::MeshBase
This is the MeshBase class.
Definition: mesh_base.h:78
libMesh::HEX27
Definition: enum_elem_type.h:49
libMesh::ExodusII_IO_Helper::ss_ids
std::vector< int > ss_ids
Definition: exodusII_io_helper.h:550
libMesh::ExodusII_IO_Helper::init_element_equivalence_map
void init_element_equivalence_map()
Definition: exodusII_io_helper.C:293
libMesh::ExodusII_IO_Helper::elem_var_names
std::vector< std::string > elem_var_names
Definition: exodusII_io_helper.h:645
libMesh::ExodusII_IO_Helper::MappedOutputVector
This class facilitates inline conversion of an input data vector to a different precision level,...
Definition: exodusII_io_helper.h:751
libMesh::BoundaryInfo::build_edge_list
void build_edge_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &edge_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of element numbers, edges, and boundary ids for those edges.
Definition: boundary_info.C:2091
libMesh::ExodusII_IO_Helper::num_edge_blk
int num_edge_blk
Definition: exodusII_io_helper.h:520
libMesh::ExodusII_IO_Helper::num_sideset_vars
int num_sideset_vars
Definition: exodusII_io_helper.h:504
libMesh::ExodusII_IO_Helper::write_element_values_element_major
void write_element_values_element_major(const MeshBase &mesh, const std::vector< Real > &values, int timestep, const std::vector< std::set< subdomain_id_type >> &vars_active_subdomains, const std::vector< std::string > &derived_var_names, const std::map< subdomain_id_type, std::vector< std::string >> &subdomain_to_var_names)
Same as the function above, but assume the input 'values' vector is in element-major order,...
Definition: exodusII_io_helper.C:2937
libMesh::ExodusII_IO_Helper::title
std::vector< char > title
Definition: exodusII_io_helper.h:611
libMesh::ExodusII_IO_Helper::nodeset_ids
std::vector< int > nodeset_ids
Definition: exodusII_io_helper.h:553
libMesh::ExodusII_IO_Helper::initialize_nodal_variables
void initialize_nodal_variables(std::vector< std::string > names)
Sets up the nodal variables.
Definition: exodusII_io_helper.C:2468
libMesh::ExodusII_IO_Helper::MappedOutputVector::our_data
const std::vector< Real > & our_data
Definition: exodusII_io_helper.h:765
libMesh::ExodusII_IO_Helper::_use_mesh_dimension_instead_of_spatial_dimension
bool _use_mesh_dimension_instead_of_spatial_dimension
Definition: exodusII_io_helper.h:730
libMesh::ExodusII_IO_Helper::write_sideset_data
void write_sideset_data(const MeshBase &mesh, int timestep, const std::vector< std::string > &var_names, const std::vector< std::set< boundary_id_type >> &side_ids, const std::vector< std::map< BoundaryInfo::BCTuple, Real >> &bc_vals)
Write sideset data for the requested timestep.
Definition: exodusII_io_helper.C:2587
libMesh::ParallelObject::processor_id
processor_id_type processor_id() const
Definition: parallel_object.h:106
libMesh::ExodusII_IO_Helper::write_var_names_impl
void write_var_names_impl(const char *var_type, int &count, const std::vector< std::string > &names)
write_var_names() dispatches to this function.
Definition: exodusII_io_helper.C:1438
libMesh::ExodusII_IO_Helper::_elem_vars_initialized
bool _elem_vars_initialized
Definition: exodusII_io_helper.h:719
libMesh::ExodusII_IO_Helper::id_to_edge_block_names
std::map< int, std::string > id_to_edge_block_names
Definition: exodusII_io_helper.h:658
libMesh::ExodusII_IO_Helper::MappedOutputVector::MappedOutputVector
MappedOutputVector(const std::vector< Real > &vec_in, bool single_precision_in)
Definition: exodusII_io_helper.C:417
libMesh::ExodusII_IO_Helper::num_df_per_set
std::vector< int > num_df_per_set
Definition: exodusII_io_helper.h:562
libMesh::ExodusII_IO_Helper::MappedInputVector::data
void * data()
Definition: exodusII_io_helper.C:477
libMesh::QUAD4
Definition: enum_elem_type.h:41
libMesh::ExodusII_IO_Helper::Conversion::get_side_map
int get_side_map(int i) const
Definition: exodusII_io_helper.C:3281
libMesh::ExodusII_IO_Helper::Conversion::invalid_id
static const int invalid_id
An invalid_id that can be returned to signal failure in case something goes wrong.
Definition: exodusII_io_helper.h:922
libMesh::Point
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:38
libMesh::ExodusII_IO_Helper::~ExodusII_IO_Helper
virtual ~ExodusII_IO_Helper()
libMesh::BoundaryInfo::build_shellface_boundary_ids
void build_shellface_boundary_ids(std::vector< boundary_id_type > &b_ids) const
Builds the list of unique shellface boundary ids.
Definition: boundary_info.C:1602
libMesh::TRI3
Definition: enum_elem_type.h:39
libMesh::Node
A Node is like a Point, but with more information.
Definition: node.h:52
libMesh::ExodusII_IO_Helper::num_elem_this_blk
int num_elem_this_blk
Definition: exodusII_io_helper.h:529
libMesh::ExodusII_IO_Helper::write_sidesets
virtual void write_sidesets(const MeshBase &mesh)
Writes the sidesets contained in "mesh".
Definition: exodusII_io_helper.C:2212
libMesh::ExodusII_IO_Helper::num_node_sets
int num_node_sets
Definition: exodusII_io_helper.h:523
libMesh::ExodusII_IO_Helper::num_elem
int num_elem
Definition: exodusII_io_helper.h:510
libMesh::ExodusII_IO_Helper::write_nodal_coordinates
virtual void write_nodal_coordinates(const MeshBase &mesh, bool use_discontinuous=false)
Writes the nodal coordinates contained in "mesh".
Definition: exodusII_io_helper.C:1726
libMesh::ExodusII_IO_Helper::_write_as_dimension
unsigned _write_as_dimension
Definition: exodusII_io_helper.h:734
libMesh::ExodusII_IO_Helper::write_timestep
void write_timestep(int timestep, Real time)
Writes the time for the timestep.
Definition: exodusII_io_helper.C:2562
libMesh::ExodusII_IO_Helper::read_global_values
void read_global_values(std::vector< Real > &values, int timestep)
Reads the vector of global variables.
Definition: exodusII_io_helper.C:3151
libMesh::ExodusII_IO_Helper::ExodusVarType
ExodusVarType
Wraps calls to exII::ex_get_var_names() and exII::ex_get_var_param().
Definition: exodusII_io_helper.h:692
libMesh::ExodusII_IO_Helper::num_attr
int num_attr
Definition: exodusII_io_helper.h:535
libMesh::ExodusII_IO_Helper::elem_type
std::vector< char > elem_type
Definition: exodusII_io_helper.h:614
libMesh::ExodusII_IO_Helper::read_edge_blocks
void read_edge_blocks(MeshBase &mesh)
Read in edge blocks, storing information in the BoundaryInfo object.
Definition: exodusII_io_helper.C:852
libMesh::ExodusII_IO_Helper::element_equivalence_map
std::map< std::string, ElemType > element_equivalence_map
Defines equivalence classes of Exodus element types that map to libmesh ElemTypes.
Definition: exodusII_io_helper.h:816
libMesh::ExodusII_IO_Helper::read_sideset_info
void read_sideset_info()
Reads information about all of the sidesets in the ExodusII mesh file.
Definition: exodusII_io_helper.C:1022
libMesh::Utility::enum_to_string
std::string enum_to_string(const T e)
libMesh::ExodusII_IO_Helper::NODAL
Definition: exodusII_io_helper.h:692
libMesh::ExodusII_IO_Helper::open
void open(const char *filename, bool read_only)
Opens an ExodusII mesh file named filename.
Definition: exodusII_io_helper.C:492
libMesh::ExodusII_IO_Helper::id_list
std::vector< int > id_list
Definition: exodusII_io_helper.h:593
libMesh::ExodusII_IO_Helper::write_nodal_values
void write_nodal_values(int var_id, const std::vector< Real > &values, int timestep)
Writes the vector of values to a nodal variable.
Definition: exodusII_io_helper.C:3070
libMesh::ExodusII_IO_Helper::num_time_steps
int num_time_steps
Definition: exodusII_io_helper.h:627
libMesh::ExodusII_IO_Helper::NamesData::get_char_star
char * get_char_star(int i)
Provide access to the i'th underlying char *.
Definition: exodusII_io_helper.C:3399
libMesh::ExodusII_IO_Helper::close
void close()
Closes the ExodusII mesh file.
Definition: exodusII_io_helper.C:1228
libMesh::ExodusII_IO_Helper::get_elem_type
const char * get_elem_type() const
Definition: exodusII_io_helper.C:396
libMesh::ExodusII_IO_Helper::SIDESET
Definition: exodusII_io_helper.h:692
libMesh::ExodusII_IO_Helper::read_nodeset
void read_nodeset(int id)
Reads information about nodeset id and inserts it into the global nodeset array at the position offse...
Definition: exodusII_io_helper.C:1131
libMesh::ExodusII_IO_Helper::MappedInputVector
This class facilitates reading in vectors from Exodus file that may be of a different floating point ...
Definition: exodusII_io_helper.h:778
libMesh::ExodusII_IO_Helper::read_var_names
void read_var_names(ExodusVarType type)
Definition: exodusII_io_helper.C:1344
libMesh::ExodusII_IO_Helper::ELEMENTAL
Definition: exodusII_io_helper.h:692
libMesh::ExodusII_IO_Helper::initialize_global_variables
void initialize_global_variables(std::vector< std::string > names)
Sets up the global variables.
Definition: exodusII_io_helper.C:2496
libMesh::TRI6
Definition: enum_elem_type.h:40
libMesh::ExodusII_IO_Helper::read_elem_num_map
void read_elem_num_map()
Reads the optional node_num_map from the ExodusII mesh file.
Definition: exodusII_io_helper.C:996
libMesh::ExodusII_IO_Helper::Conversion::get_inverse_shellface_map
int get_inverse_shellface_map(int i) const
Definition: exodusII_io_helper.C:3323
libMesh::ExodusII_IO_Helper::Conversion::libmesh_elem_type
ElemType libmesh_elem_type() const
Definition: exodusII_io_helper.C:3334
distance
Real distance(const Point &p)
Definition: subdomains_ex3.C:50
libMesh::ExodusII_IO_Helper::Conversion::get_inverse_node_map
int get_inverse_node_map(int i) const
Definition: exodusII_io_helper.C:3270
libMesh::ExodusII_IO_Helper::z
std::vector< Real > z
Definition: exodusII_io_helper.h:608
libMesh::ExodusII_IO_Helper::NamesData::get_char_star_star
char ** get_char_star_star()
Provide access to the underlying C data table.
Definition: exodusII_io_helper.C:3392
libMesh::ExodusII_IO_Helper::init_conversion_map
void init_conversion_map()
Definition: exodusII_io_helper.C:146
libMesh::ExodusII_IO_Helper::read_elem_in_block
void read_elem_in_block(int block)
Reads all of the element connectivity for block block in the ExodusII mesh file.
Definition: exodusII_io_helper.C:797
libMesh::ExodusII_IO_Helper::num_nodes_per_set
std::vector< int > num_nodes_per_set
Definition: exodusII_io_helper.h:559
libMesh::ExodusII_IO_Helper::id_to_block_names
std::map< int, std::string > id_to_block_names
Definition: exodusII_io_helper.h:657
libMesh::ExodusII_IO_Helper::get_node_set_name
std::string get_node_set_name(int index)
Get the node set name for the given node set index if supplied in the mesh file.
Definition: exodusII_io_helper.C:787
libMesh::ExodusII_IO_Helper::initialize
virtual void initialize(std::string title, const MeshBase &mesh, bool use_discontinuous=false)
Initializes the Exodus file.
Definition: exodusII_io_helper.C:1611
libMesh::ExodusII_IO_Helper::num_elem_blk
int num_elem_blk
Definition: exodusII_io_helper.h:513
libMesh::ExodusII_IO_Helper::write_elements
virtual void write_elements(const MeshBase &mesh, bool use_discontinuous=false)
Writes the elements contained in "mesh".
Definition: exodusII_io_helper.C:1827
libMesh::QUADSHELL8
Definition: enum_elem_type.h:73
libMesh::PYRAMID5
Definition: enum_elem_type.h:53
libMesh::ExodusII_IO_Helper::elem_list
std::vector< int > elem_list
Definition: exodusII_io_helper.h:584
libMesh::DofObject::id
dof_id_type id() const
Definition: dof_object.h:767
libMesh::ExodusII_IO_Helper::verbose
bool verbose
Definition: exodusII_io_helper.h:663
libMesh::ExodusII_IO_Helper::use_mesh_dimension_instead_of_spatial_dimension
void use_mesh_dimension_instead_of_spatial_dimension(bool val)
Sets the underlying value of the boolean flag _use_mesh_dimension_instead_of_spatial_dimension.
Definition: exodusII_io_helper.C:3167
libMesh::BoundaryInfo::add_edge
void add_edge(const dof_id_type elem, const unsigned short int edge, const boundary_id_type id)
Add edge edge of element number elem with boundary id id to the boundary information data structure.
Definition: boundary_info.C:707
libMesh::EDGE3
Definition: enum_elem_type.h:36
libMesh::ExodusII_IO_Helper::set_coordinate_offset
void set_coordinate_offset(Point p)
Allows you to set a vector that is added to the coordinates of all of the nodes.
Definition: exodusII_io_helper.C:3181
libMesh::BoundaryInfo::build_side_boundary_ids
void build_side_boundary_ids(std::vector< boundary_id_type > &b_ids) const
Builds the list of unique side boundary ids.
Definition: boundary_info.C:1588
libMesh::ExodusII_IO_Helper::node_sets_node_index
std::vector< int > node_sets_node_index
Definition: exodusII_io_helper.h:569
libMesh::ExodusII_IO_Helper::MappedOutputVector::data
void * data()
Definition: exodusII_io_helper.C:433
libMesh::ExodusII_IO_Helper::Conversion::get_inverse_side_map
int get_inverse_side_map(int i) const
Definition: exodusII_io_helper.C:3296
libMesh::ExodusII_IO_Helper::NamesData::NamesData
NamesData(size_t n_strings, size_t string_length)
Constructor.
Definition: exodusII_io_helper.C:3356
libMesh::QUADSHELL4
Definition: enum_elem_type.h:72
libMesh::ExodusII_IO_Helper::nodal_var_values
std::vector< Real > nodal_var_values
Definition: exodusII_io_helper.h:639
libMesh::StoredRange
The StoredRange class defines a contiguous, divisible set of objects.
Definition: stored_range.h:52
libMesh::ExodusII_IO_Helper::num_elem_vars
int num_elem_vars
Definition: exodusII_io_helper.h:642
libMesh::BoundaryInfo::get_edge_boundary_ids
const std::set< boundary_id_type > & get_edge_boundary_ids() const
Definition: boundary_info.h:803
libMesh::Elem
This is the base class from which all geometric element types are derived.
Definition: elem.h:100
libMesh::NODEELEM
Definition: enum_elem_type.h:66
data
IterBase * data
Ideally this private member data should have protected access.
Definition: variant_filter_iterator.h:337
libMesh::ExodusII_IO_Helper::MappedInputVector::double_vec
std::vector< double > double_vec
Definition: exodusII_io_helper.h:791
libMesh::ExodusII_IO_Helper::NamesData
This class is useful for managing anything that requires a char ** input/output in ExodusII file.
Definition: exodusII_io_helper.h:984
libMesh::ExodusII_IO_Helper::message
void message(const std::string &msg)
Prints the message defined in msg.
Definition: exodusII_io_helper.C:403
libMesh::ExodusII_IO_Helper::write_nodesets
virtual void write_nodesets(const MeshBase &mesh)
Writes the nodesets contained in "mesh".
Definition: exodusII_io_helper.C:2317
libMesh::QUAD9
Definition: enum_elem_type.h:43
libMesh::ExodusII_IO_Helper::sideset_var_names
std::vector< std::string > sideset_var_names
Definition: exodusII_io_helper.h:654
libMesh::ExodusII_IO_Helper::Conversion::get_shellface_index_offset
std::size_t get_shellface_index_offset() const
Definition: exodusII_io_helper.C:3351
libMesh::ExodusII_IO_Helper::MappedInputVector::MappedInputVector
MappedInputVector(std::vector< Real > &vec_in, bool single_precision_in)
Definition: exodusII_io_helper.C:449
libMesh::ExodusII_IO_Helper::read_qa_records
void read_qa_records()
Reads the QA records from an ExodusII file.
Definition: exodusII_io_helper.C:575
libMesh::err
OStreamProxy err
libMesh::ExodusII_IO_Helper::GLOBAL
Definition: exodusII_io_helper.h:692
libMesh::ExodusII_IO_Helper::node_list
std::vector< int > node_list
Definition: exodusII_io_helper.h:590
libMesh::ExodusII_IO_Helper::NamesData::push_back_entry
void push_back_entry(const std::string &name)
Adds another name to the current data table.
Definition: exodusII_io_helper.C:3376
libMesh::ExodusII_IO_Helper::connect
std::vector< int > connect
Definition: exodusII_io_helper.h:547
libMesh::ExodusII_IO_Helper::get_complex_subdomain_to_var_names
std::map< subdomain_id_type, std::vector< std::string > > get_complex_subdomain_to_var_names(const std::map< subdomain_id_type, std::vector< std::string >> &subdomain_to_var_names) const
Takes a map from subdomain id -> vector of active variable names as input and returns a corresponding...
Definition: exodusII_io_helper.C:3227
libMesh::TestClass
Definition: id_types.h:33
libMesh::Elem::node_id
dof_id_type node_id(const unsigned int i) const
Definition: elem.h:1977
libMesh::TRI3SUBDIVISION
Definition: enum_elem_type.h:69
libMesh::ExodusII_IO_Helper::node_sets_dist_fact
std::vector< Real > node_sets_dist_fact
Definition: exodusII_io_helper.h:581
libMesh::Real
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Definition: libmesh_common.h:121
libMesh::ExodusII_IO_Helper::write_global_values
void write_global_values(const std::vector< Real > &values, int timestep)
Writes the vector of global variables.
Definition: exodusII_io_helper.C:3131
libMesh::ParallelObject
An object whose state is distributed along a set of processors.
Definition: parallel_object.h:55
libMesh::ExodusII_IO_Helper::inquire
int inquire(int req_info, std::string error_msg="")
Definition: exodusII_io_helper.C:1247
libMesh::ExodusII_IO_Helper::read_all_nodesets
void read_all_nodesets()
New API that reads all nodesets simultaneously.
Definition: exodusII_io_helper.C:1164
libMesh::ExodusII_IO_Helper::write_information_records
void write_information_records(const std::vector< std::string > &records)
Writes the vector of information records.
Definition: exodusII_io_helper.C:3092
libMesh::ExodusII_IO_Helper::MappedInputVector::~MappedInputVector
~MappedInputVector()
Definition: exodusII_io_helper.C:465
libMesh::PRISM18
Definition: enum_elem_type.h:52
libMesh::TRISHELL3
Definition: enum_elem_type.h:71
libMesh::Elem::build
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:246
libMesh::ExodusII_IO_Helper::num_edge
int num_edge
Definition: exodusII_io_helper.h:516
libMesh::out
OStreamProxy out
libMesh::ExodusII_IO_Helper::time_steps
std::vector< Real > time_steps
Definition: exodusII_io_helper.h:630
libMesh::Elem::node_ptr
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2009
libMesh::ExodusII_IO_Helper::x
std::vector< Real > x
Definition: exodusII_io_helper.h:602
libMesh::ExodusII_IO_Helper::_nodal_vars_initialized
bool _nodal_vars_initialized
Definition: exodusII_io_helper.h:725
libMesh::ExodusII_IO_Helper::ex_err
int ex_err
Definition: exodusII_io_helper.h:495
libMesh::Elem::type
virtual ElemType type() const =0
libMesh::ExodusII_IO_Helper::num_elem_all_sidesets
int num_elem_all_sidesets
Definition: exodusII_io_helper.h:538
libMesh::ExodusII_IO_Helper::read_nodeset_info
void read_nodeset_info()
Reads information about all of the nodesets in the ExodusII mesh file.
Definition: exodusII_io_helper.C:1057
libMesh::ExodusII_IO_Helper::get_node_set_id
int get_node_set_id(int index)
Get the node set id for the given node set index.
Definition: exodusII_io_helper.C:778
libMesh::ExodusII_IO_Helper::get_complex_names
std::vector< std::string > get_complex_names(const std::vector< std::string > &names) const
Definition: exodusII_io_helper.C:3188
libMesh::ExodusII_IO_Helper::read_var_names_impl
void read_var_names_impl(const char *var_type, int &count, std::vector< std::string > &result)
read_var_names() dispatches to this function.
Definition: exodusII_io_helper.C:1367
libMesh::PYRAMID13
Definition: enum_elem_type.h:54
libMesh::ExodusII_IO_Helper::get_block_name
std::string get_block_name(int index)
Get the block name for the given block index if supplied in the mesh file.
Definition: exodusII_io_helper.C:751
libMesh::ExodusII_IO_Helper::MappedInputVector::our_data
std::vector< Real > & our_data
Definition: exodusII_io_helper.h:789
libMesh::Quality::name
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
Definition: elem_quality.C:42
libMesh::PYRAMID14
Definition: enum_elem_type.h:55
libMesh::EDGE2
Definition: enum_elem_type.h:35
libMesh::BoundaryInfo::BCTuple
std::tuple< dof_id_type, unsigned short int, boundary_id_type > BCTuple
As above, but the library creates and fills in a vector of (elem-id, side-id, bc-id) triplets and ret...
Definition: boundary_info.h:704
libMesh::ExodusII_IO_Helper::ex_id
int ex_id
Definition: exodusII_io_helper.h:492
libMesh::QUAD8
Definition: enum_elem_type.h:42
libMesh::ElemType
ElemType
Defines an enum for geometric element types.
Definition: enum_elem_type.h:33
libMesh::ExodusII_IO_Helper::MappedInputVector::float_vec
std::vector< float > float_vec
Definition: exodusII_io_helper.h:792
libMesh::ExodusII_IO_Helper::elem_num_map
std::vector< int > elem_num_map
Definition: exodusII_io_helper.h:599
libMesh::ExodusII_IO_Helper::initialize_element_variables
virtual void initialize_element_variables(std::vector< std::string > names, const std::vector< std::set< subdomain_id_type >> &vars_active_subdomains)
Sets up the nodal variables.
Definition: exodusII_io_helper.C:2403