$extrastylesheet
exodusII_io_helper.C
Go to the documentation of this file.
00001 // The libMesh Finite Element Library.
00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
00003 
00004 // This library is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public
00006 // License as published by the Free Software Foundation; either
00007 // version 2.1 of the License, or (at your option) any later version.
00008 
00009 // This library is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // Lesser General Public License for more details.
00013 
00014 // You should have received a copy of the GNU Lesser General Public
00015 // License along with this library; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 
00019 #include "libmesh/exodusII_io_helper.h"
00020 
00021 
00022 #ifdef LIBMESH_HAVE_EXODUS_API
00023 
00024 #include <algorithm>
00025 #include <functional>
00026 #include <sstream>
00027 
00028 #include "libmesh/boundary_info.h"
00029 #include "libmesh/enum_elem_type.h"
00030 #include "libmesh/elem.h"
00031 #include "libmesh/system.h"
00032 #include "libmesh/numeric_vector.h"
00033 #include "libmesh/string_to_enum.h"
00034 
00035 #ifdef DEBUG
00036 #include "libmesh/mesh_tools.h"  // for elem_types warning
00037 #endif
00038 
00039 // This macro returns the length of the array a.  Don't
00040 // try using it on empty arrays, since it accesses the
00041 // zero'th element.
00042 #define ARRAY_LENGTH(a) (sizeof((a))/sizeof((a)[0]))
00043 
00044 // Anonymous namespace for file local data
00045 namespace
00046 {
00047 using namespace libMesh;
00048 
00049 // Define equivalence classes of Cubit/Exodus element types that map to
00050 // libmesh ElemTypes
00051 std::map<std::string, ElemType> element_equivalence_map;
00052 
00053 // This function initializes the element_equivalence_map the first time it
00054 // is called, and returns early all other times.
00055 void init_element_equivalence_map()
00056 {
00057   if (element_equivalence_map.empty())
00058     {
00059       // EDGE2 equivalences
00060       element_equivalence_map["EDGE2"]  = EDGE2;
00061       element_equivalence_map["TRUSS"]  = EDGE2;
00062       element_equivalence_map["BEAM"]   = EDGE2;
00063       element_equivalence_map["BAR"]    = EDGE2;
00064       element_equivalence_map["TRUSS2"] = EDGE2;
00065       element_equivalence_map["BEAM2"]  = EDGE2;
00066       element_equivalence_map["BAR2"]   = EDGE2;
00067 
00068       // EDGE3 equivalences
00069       element_equivalence_map["EDGE3"]  = EDGE3;
00070       element_equivalence_map["TRUSS3"] = EDGE3;
00071       element_equivalence_map["BEAM3"]  = EDGE3;
00072       element_equivalence_map["BAR3"]   = EDGE3;
00073 
00074       // QUAD4 equivalences
00075       element_equivalence_map["QUAD"]   = QUAD4;
00076       element_equivalence_map["QUAD4"]  = QUAD4;
00077       // element_equivalence_map["SHELL"]  = QUAD4;
00078       // element_equivalence_map["SHELL4"] = QUAD4;
00079 
00080       // QUAD8 equivalences
00081       element_equivalence_map["QUAD8"]  = QUAD8;
00082       // element_equivalence_map["SHELL8"] = QUAD8;
00083 
00084       // QUAD9 equivalences
00085       element_equivalence_map["QUAD9"]  = QUAD9;
00086       // element_equivalence_map["SHELL9"] = QUAD9;
00087 
00088       // TRI3 equivalences
00089       element_equivalence_map["TRI"]       = TRI3;
00090       element_equivalence_map["TRI3"]      = TRI3;
00091       element_equivalence_map["TRIANGLE"]  = TRI3;
00092       // element_equivalence_map["TRISHELL"]  = TRI3;
00093       // element_equivalence_map["TRISHELL3"] = TRI3;
00094 
00095       // TRI6 equivalences
00096       element_equivalence_map["TRI6"]      = TRI6;
00097       // element_equivalence_map["TRISHELL6"] = TRI6;
00098 
00099       // HEX8 equivalences
00100       element_equivalence_map["HEX"]  = HEX8;
00101       element_equivalence_map["HEX8"] = HEX8;
00102 
00103       // HEX20 equivalences
00104       element_equivalence_map["HEX20"] = HEX20;
00105 
00106       // HEX27 equivalences
00107       element_equivalence_map["HEX27"] = HEX27;
00108 
00109       // TET4 equivalences
00110       element_equivalence_map["TETRA"]  = TET4;
00111       element_equivalence_map["TETRA4"] = TET4;
00112 
00113       // TET10 equivalences
00114       element_equivalence_map["TETRA10"] = TET10;
00115 
00116       // PRISM6 equivalences
00117       element_equivalence_map["WEDGE"] = PRISM6;
00118 
00119       // PRISM15 equivalences
00120       element_equivalence_map["WEDGE15"] = PRISM15;
00121 
00122       // PRISM18 equivalences
00123       element_equivalence_map["WEDGE18"] = PRISM18;
00124 
00125       // PYRAMID5 equivalences
00126       element_equivalence_map["PYRAMID"]  = PYRAMID5;
00127       element_equivalence_map["PYRAMID5"] = PYRAMID5;
00128 
00129       // PYRAMID13 equivalences
00130       element_equivalence_map["PYRAMID13"] = PYRAMID13;
00131 
00132       // PYRAMID14 equivalences
00133       element_equivalence_map["PYRAMID14"] = PYRAMID14;
00134     }
00135 }
00136 }
00137 
00138 
00139 
00140 namespace libMesh
00141 {
00142 
00143 // ------------------------------------------------------------
00144 // ExodusII_IO_Helper::ElementMaps static data
00145 
00146 // 1D node map definitions
00147 const int ExodusII_IO_Helper::ElementMaps::edge2_node_map[2] = {0, 1};
00148 const int ExodusII_IO_Helper::ElementMaps::edge3_node_map[3] = {0, 1, 2};
00149 
00150 // 1D edge maps
00151 // FIXME: This notion may or may not be defined in ExodusII
00152 const int ExodusII_IO_Helper::ElementMaps::edge_edge_map[2] = {0, 1};
00153 const int ExodusII_IO_Helper::ElementMaps::edge_inverse_edge_map[2] = {1, 2};
00154 
00155 // 2D node map definitions
00156 const int ExodusII_IO_Helper::ElementMaps::quad4_node_map[4] = {0, 1, 2, 3};
00157 const int ExodusII_IO_Helper::ElementMaps::quad8_node_map[8] = {0, 1, 2, 3, 4, 5, 6, 7};
00158 const int ExodusII_IO_Helper::ElementMaps::quad9_node_map[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
00159 const int ExodusII_IO_Helper::ElementMaps::tri3_node_map[3]  = {0, 1, 2};
00160 const int ExodusII_IO_Helper::ElementMaps::tri6_node_map[6]  = {0, 1, 2, 3, 4, 5};
00161 
00162 // 2D edge map definitions
00163 const int ExodusII_IO_Helper::ElementMaps::tri_edge_map[3] = {0, 1, 2};
00164 const int ExodusII_IO_Helper::ElementMaps::quad_edge_map[4] = {0, 1, 2, 3};
00165 
00166 //These take a libMesh ID and turn it into an Exodus ID
00167 const int ExodusII_IO_Helper::ElementMaps::tri_inverse_edge_map[3] = {1, 2, 3};
00168 const int ExodusII_IO_Helper::ElementMaps::quad_inverse_edge_map[4] = {1, 2, 3, 4};
00169 
00170 // 3D node map definitions
00171 const int ExodusII_IO_Helper::ElementMaps::hex8_node_map[8]   = {0, 1, 2, 3, 4, 5, 6, 7};
00172 const int ExodusII_IO_Helper::ElementMaps::hex20_node_map[20] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
00173                                                                   10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
00174 
00175 // Perhaps an older Hex27 node numbering?  This no longer works.
00176 //const int ExodusII_IO_Helper::ElementMaps::hex27_node_map[27] = { 1,  5,  6,  2,  0,  4,  7,  3, 13, 17, 14,  9,  8, 16,
00177 //  18, 10, 12, 19, 15, 11, 24, 25, 22, 26, 21, 23, 20};
00178 
00179 //DRG: Using the newest exodus documentation available on sourceforge and using Table 2 to see
00180 // where all of the nodes over 21 are supposed to go... we come up with:
00181 const int ExodusII_IO_Helper::ElementMaps::hex27_node_map[27] = {
00182   // Vertex and mid-edge nodes
00183   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
00184   // Mid-face nodes and centroid
00185   21, 25, 24, 26, 23, 22, 20};
00186 //20  21  22  23  24  25  26 // LibMesh indices
00187 
00188 // The hex27 appears to be the only element without a 1:1 map between its
00189 // node numbering and libmesh's.  Therefore when writing out hex27's we need
00190 // to invert this map...
00191 const int ExodusII_IO_Helper::ElementMaps::hex27_inverse_node_map[27] = {
00192   // Vertex and mid-edge nodes
00193   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
00194   // Mid-face nodes and centroid
00195   26, 20, 25, 24, 22, 21, 23};
00196 //20  21  22  23  24  25  26
00197 
00198 
00199 const int ExodusII_IO_Helper::ElementMaps::tet4_node_map[4]   = {0, 1, 2, 3};
00200 const int ExodusII_IO_Helper::ElementMaps::tet10_node_map[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
00201 
00202 const int ExodusII_IO_Helper::ElementMaps::prism6_node_map[6]   = {0, 1, 2, 3, 4, 5};
00203 const int ExodusII_IO_Helper::ElementMaps::prism15_node_map[15]   = {0, 1, 2, 3, 4, 5, 6,  7,  8,  9,
00204                                                                      10, 11, 12, 13, 14};
00205 const int ExodusII_IO_Helper::ElementMaps::prism18_node_map[18]   = {0, 1, 2, 3, 4, 5, 6,  7,  8,  9,
00206                                                                      10, 11, 12, 13, 14, 15, 16, 17};
00207 const int ExodusII_IO_Helper::ElementMaps::pyramid5_node_map[5] = {0, 1, 2, 3, 4};
00208 const int ExodusII_IO_Helper::ElementMaps::pyramid13_node_map[13] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
00209 const int ExodusII_IO_Helper::ElementMaps::pyramid14_node_map[14] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
00210 
00211 // 3D face map definitions
00212 const int ExodusII_IO_Helper::ElementMaps::tet_face_map[4]     = {1, 2, 3, 0};
00213 
00214 const int ExodusII_IO_Helper::ElementMaps::hex_face_map[6]     = {1, 2, 3, 4, 0, 5};
00215 const int ExodusII_IO_Helper::ElementMaps::hex27_face_map[6]   = {1, 2, 3, 4, 0, 5};
00216 //const int ExodusII_IO_Helper::ElementMaps::hex27_face_map[6]   = {1, 0, 3, 5, 4, 2};
00217 const int ExodusII_IO_Helper::ElementMaps::prism_face_map[5]   = {1, 2, 3, 0, 4};
00218 const int ExodusII_IO_Helper::ElementMaps::pyramid_face_map[5] = {-1,-1,-1,-1,-1}; // Not Implemented!
00219 
00220 //These take a libMesh ID and turn it into an Exodus ID
00221 const int ExodusII_IO_Helper::ElementMaps::tet_inverse_face_map[4]     = {4, 1, 2, 3};
00222 const int ExodusII_IO_Helper::ElementMaps::hex_inverse_face_map[6]     = {5, 1, 2, 3, 4, 6};
00223 const int ExodusII_IO_Helper::ElementMaps::hex27_inverse_face_map[6]   = {5, 1, 2, 3, 4, 6};
00224 //const int ExodusII_IO_Helper::ElementMaps::hex27_inverse_face_map[6]   = {2, 1, 6, 3, 5, 4};
00225 const int ExodusII_IO_Helper::ElementMaps::prism_inverse_face_map[5]   = {4, 1, 2, 3, 5};
00226 const int ExodusII_IO_Helper::ElementMaps::pyramid_inverse_face_map[5] = {-1,-1,-1,-1,-1}; // Not Implemented!
00227 
00228 
00229 // ------------------------------------------------------------
00230 // ExodusII_IO_Helper class members
00231 
00232 ExodusII_IO_Helper::ExodusII_IO_Helper(const ParallelObject &parent,
00233                                        bool v,
00234                                        bool run_only_on_proc0,
00235                                        bool single_precision) :
00236   ParallelObject(parent),
00237   ex_id(0),
00238   ex_err(0),
00239   num_dim(0),
00240   num_global_vars(0),
00241   num_nodes(0),
00242   num_elem(0),
00243   num_elem_blk(0),
00244   num_node_sets(0),
00245   num_side_sets(0),
00246   num_elem_this_blk(0),
00247   num_nodes_per_elem(0),
00248   num_attr(0),
00249   num_elem_all_sidesets(0),
00250   num_time_steps(0),
00251   num_nodal_vars(0),
00252   num_elem_vars(0),
00253   verbose(v),
00254   opened_for_writing(false),
00255   opened_for_reading(false),
00256   _run_only_on_proc0(run_only_on_proc0),
00257   _elem_vars_initialized(false),
00258   _global_vars_initialized(false),
00259   _nodal_vars_initialized(false),
00260   _use_mesh_dimension_instead_of_spatial_dimension(false),
00261   _single_precision(single_precision)
00262 {
00263   title.resize(MAX_LINE_LENGTH+1);
00264   elem_type.resize(MAX_STR_LENGTH);
00265 }
00266 
00267 
00268 
00269 ExodusII_IO_Helper::~ExodusII_IO_Helper()
00270 {
00271 }
00272 
00273 
00274 
00275 const char* ExodusII_IO_Helper::get_elem_type() const
00276 {
00277   return &elem_type[0];
00278 }
00279 
00280 
00281 
00282 void ExodusII_IO_Helper::message(const std::string& msg)
00283 {
00284   if (verbose) libMesh::out << msg << std::endl;
00285 }
00286 
00287 
00288 
00289 void ExodusII_IO_Helper::message(const std::string& msg, int i)
00290 {
00291   if (verbose) libMesh::out << msg << i << "." << std::endl;
00292 }
00293 
00294 
00295 
00296 void ExodusII_IO_Helper::open(const char* filename, bool read_only)
00297 {
00298   // Version of Exodus you are using
00299   float ex_version = 0.;
00300 
00301   // Word size in bytes of the floating point variables used in the
00302   // application program (0, 4, or 8)
00303   int comp_ws = sizeof(Real);
00304 
00305   // Word size in bytes of the floating point data as they are stored
00306   // in the ExodusII file.  "If this argument is 0, the word size of the
00307   // floating point data already stored in the file is returned"
00308   int io_ws = 0;
00309 
00310   ex_id = exII::ex_open(filename,
00311                         read_only ? EX_READ : EX_WRITE,
00312                         &comp_ws,
00313                         &io_ws,
00314                         &ex_version);
00315 
00316   std::string err_msg = std::string("Error opening ExodusII mesh file: ") + std::string(filename);
00317   EX_CHECK_ERR(ex_id, err_msg);
00318   if (verbose) libMesh::out << "File opened successfully." << std::endl;
00319 
00320   if (read_only)
00321     opened_for_reading = true;
00322   else
00323     opened_for_writing = true;
00324 
00325   current_filename = std::string(filename);
00326 }
00327 
00328 
00329 
00330 void ExodusII_IO_Helper::read_header()
00331 {
00332   ex_err = exII::ex_get_init(ex_id,
00333                              &title[0],
00334                              &num_dim,
00335                              &num_nodes,
00336                              &num_elem,
00337                              &num_elem_blk,
00338                              &num_node_sets,
00339                              &num_side_sets);
00340 
00341   EX_CHECK_ERR(ex_err, "Error retrieving header info.");
00342 
00343   this->read_num_time_steps();
00344 
00345   ex_err = exII::ex_get_var_param(ex_id, "n", &num_nodal_vars);
00346   EX_CHECK_ERR(ex_err, "Error reading number of nodal variables.");
00347 
00348   ex_err = exII::ex_get_var_param(ex_id, "e", &num_elem_vars);
00349   EX_CHECK_ERR(ex_err, "Error reading number of elemental variables.");
00350 
00351   ex_err = exII::ex_get_var_param(ex_id, "g", &num_global_vars);
00352   EX_CHECK_ERR(ex_err, "Error reading number of global variables.");
00353 
00354   message("Exodus header info retrieved successfully.");
00355 }
00356 
00357 
00358 
00359 
00360 void ExodusII_IO_Helper::print_header()
00361 {
00362   if (verbose)
00363     libMesh::out << "Title: \t" << &title[0] << std::endl
00364                  << "Mesh Dimension: \t"   << num_dim << std::endl
00365                  << "Number of Nodes: \t" << num_nodes << std::endl
00366                  << "Number of elements: \t" << num_elem << std::endl
00367                  << "Number of elt blocks: \t" << num_elem_blk << std::endl
00368                  << "Number of node sets: \t" << num_node_sets << std::endl
00369                  << "Number of side sets: \t" << num_side_sets << std::endl;
00370 }
00371 
00372 
00373 
00374 void ExodusII_IO_Helper::read_nodes()
00375 {
00376   x.resize(num_nodes);
00377   y.resize(num_nodes);
00378   z.resize(num_nodes);
00379 
00380   ex_err = exII::ex_get_coord(ex_id,
00381                               static_cast<void*>(&x[0]),
00382                               static_cast<void*>(&y[0]),
00383                               static_cast<void*>(&z[0]));
00384 
00385   EX_CHECK_ERR(ex_err, "Error retrieving nodal data.");
00386   message("Nodal data retrieved successfully.");
00387 }
00388 
00389 
00390 
00391 void ExodusII_IO_Helper::read_node_num_map ()
00392 {
00393   node_num_map.resize(num_nodes);
00394 
00395   ex_err = exII::ex_get_node_num_map (ex_id,
00396                                       node_num_map.empty() ? NULL : &node_num_map[0]);
00397 
00398   EX_CHECK_ERR(ex_err, "Error retrieving nodal number map.");
00399   message("Nodal numbering map retrieved successfully.");
00400 
00401   if (verbose)
00402     {
00403       libMesh::out << "[" << this->processor_id() << "] node_num_map[i] = ";
00404       for (unsigned int i=0; i<static_cast<unsigned int>(std::min(10, num_nodes-1)); ++i)
00405         libMesh::out << node_num_map[i] << ", ";
00406       libMesh::out << "... " << node_num_map.back() << std::endl;
00407     }
00408 }
00409 
00410 
00411 void ExodusII_IO_Helper::print_nodes(std::ostream &out_stream)
00412 {
00413   for (int i=0; i<num_nodes; i++)
00414     out_stream << "(" << x[i] << ", " << y[i] << ", " << z[i] << ")" << std::endl;
00415 }
00416 
00417 
00418 
00419 void ExodusII_IO_Helper::read_block_info()
00420 {
00421   block_ids.resize(num_elem_blk);
00422   // Get all element block IDs.
00423   ex_err = exII::ex_get_elem_blk_ids(ex_id,
00424                                      block_ids.empty() ? NULL : &block_ids[0]);
00425   // Usually, there is only one
00426   // block since there is only
00427   // one type of element.
00428   // However, there could be more.
00429 
00430   EX_CHECK_ERR(ex_err, "Error getting block IDs.");
00431   message("All block IDs retrieved successfully.");
00432 
00433   char name_buffer[MAX_STR_LENGTH+1];
00434   for (int i=0; i<num_elem_blk; ++i)
00435     {
00436       ex_err = exII::ex_get_name(ex_id, exII::EX_ELEM_BLOCK,
00437                                  block_ids[i], name_buffer);
00438       EX_CHECK_ERR(ex_err, "Error getting block name.");
00439       id_to_block_names[block_ids[i]] = name_buffer;
00440     }
00441   message("All block names retrieved successfully.");
00442 }
00443 
00444 
00445 
00446 int ExodusII_IO_Helper::get_block_id(int index)
00447 {
00448   libmesh_assert_less (static_cast<unsigned int>(index), block_ids.size());
00449 
00450   return block_ids[index];
00451 }
00452 
00453 
00454 
00455 std::string ExodusII_IO_Helper::get_block_name(int index)
00456 {
00457   libmesh_assert_less (static_cast<unsigned int>(index), block_ids.size());
00458 
00459   return id_to_block_names[block_ids[index]];
00460 }
00461 
00462 
00463 
00464 int ExodusII_IO_Helper::get_side_set_id(int index)
00465 {
00466   libmesh_assert_less (static_cast<unsigned int>(index), ss_ids.size());
00467 
00468   return ss_ids[index];
00469 }
00470 
00471 
00472 
00473 std::string ExodusII_IO_Helper::get_side_set_name(int index)
00474 {
00475   libmesh_assert_less (static_cast<unsigned int>(index), ss_ids.size());
00476 
00477   return id_to_ss_names[ss_ids[index]];
00478 }
00479 
00480 
00481 
00482 int ExodusII_IO_Helper::get_node_set_id(int index)
00483 {
00484   libmesh_assert_less (static_cast<unsigned int>(index), nodeset_ids.size());
00485 
00486   return nodeset_ids[index];
00487 }
00488 
00489 
00490 
00491 std::string ExodusII_IO_Helper::get_node_set_name(int index)
00492 {
00493   libmesh_assert_less (static_cast<unsigned int>(index), nodeset_ids.size());
00494 
00495   return id_to_ns_names[nodeset_ids[index]];
00496 }
00497 
00498 
00499 
00500 
00501 void ExodusII_IO_Helper::read_elem_in_block(int block)
00502 {
00503   libmesh_assert_less (static_cast<unsigned int>(block), block_ids.size());
00504 
00505   ex_err = exII::ex_get_elem_block(ex_id,
00506                                    block_ids[block],
00507                                    &elem_type[0],
00508                                    &num_elem_this_blk,
00509                                    &num_nodes_per_elem,
00510                                    &num_attr);
00511   if (verbose)
00512     libMesh::out << "Reading a block of " << num_elem_this_blk
00513                  << " " << &elem_type[0] << "(s)"
00514                  << " having " << num_nodes_per_elem
00515                  << " nodes per element." << std::endl;
00516 
00517   EX_CHECK_ERR(ex_err, "Error getting block info.");
00518   message("Info retrieved successfully for block: ", block);
00519 
00520 
00521 
00522   // Read in the connectivity of the elements of this block,
00523   // watching out for the case where we actually have no
00524   // elements in this block (possible with parallel files)
00525   connect.resize(num_nodes_per_elem*num_elem_this_blk);
00526 
00527   if (!connect.empty())
00528     {
00529       ex_err = exII::ex_get_elem_conn(ex_id,
00530                                       block_ids[block],
00531                                       &connect[0]);
00532 
00533       EX_CHECK_ERR(ex_err, "Error reading block connectivity.");
00534       message("Connectivity retrieved successfully for block: ", block);
00535     }
00536 }
00537 
00538 
00539 
00540 
00541 void ExodusII_IO_Helper::read_elem_num_map ()
00542 {
00543   elem_num_map.resize(num_elem);
00544 
00545   ex_err = exII::ex_get_elem_num_map (ex_id,
00546                                       elem_num_map.empty() ? NULL : &elem_num_map[0]);
00547 
00548   EX_CHECK_ERR(ex_err, "Error retrieving element number map.");
00549   message("Element numbering map retrieved successfully.");
00550 
00551 
00552   if (verbose)
00553     {
00554       libMesh::out << "[" << this->processor_id() << "] elem_num_map[i] = ";
00555       for (unsigned int i=0; i<static_cast<unsigned int>(std::min(10, num_elem-1)); ++i)
00556         libMesh::out << elem_num_map[i] << ", ";
00557       libMesh::out << "... " << elem_num_map.back() << std::endl;
00558     }
00559 }
00560 
00561 
00562 
00563 void ExodusII_IO_Helper::read_sideset_info()
00564 {
00565   ss_ids.resize(num_side_sets);
00566   if (num_side_sets > 0)
00567     {
00568       ex_err = exII::ex_get_side_set_ids(ex_id,
00569                                          &ss_ids[0]);
00570       EX_CHECK_ERR(ex_err, "Error retrieving sideset information.");
00571       message("All sideset information retrieved successfully.");
00572 
00573       // Resize appropriate data structures -- only do this once outside the loop
00574       num_sides_per_set.resize(num_side_sets);
00575       num_df_per_set.resize(num_side_sets);
00576 
00577       // Inquire about the length of the concatenated side sets element list
00578       num_elem_all_sidesets = inquire(exII::EX_INQ_SS_ELEM_LEN, "Error retrieving length of the concatenated side sets element list!");
00579 
00580       elem_list.resize (num_elem_all_sidesets);
00581       side_list.resize (num_elem_all_sidesets);
00582       id_list.resize   (num_elem_all_sidesets);
00583     }
00584 
00585   char name_buffer[MAX_STR_LENGTH+1];
00586   for (int i=0; i<num_side_sets; ++i)
00587     {
00588       ex_err = exII::ex_get_name(ex_id, exII::EX_SIDE_SET,
00589                                  ss_ids[i], name_buffer);
00590       EX_CHECK_ERR(ex_err, "Error getting side set name.");
00591       id_to_ss_names[ss_ids[i]] = name_buffer;
00592     }
00593   message("All side set names retrieved successfully.");
00594 }
00595 
00596 void ExodusII_IO_Helper::read_nodeset_info()
00597 {
00598   nodeset_ids.resize(num_node_sets);
00599   if (num_node_sets > 0)
00600     {
00601       ex_err = exII::ex_get_node_set_ids(ex_id,
00602                                          &nodeset_ids[0]);
00603       EX_CHECK_ERR(ex_err, "Error retrieving nodeset information.");
00604       message("All nodeset information retrieved successfully.");
00605 
00606       // Resize appropriate data structures -- only do this once outnode the loop
00607       num_nodes_per_set.resize(num_node_sets);
00608       num_node_df_per_set.resize(num_node_sets);
00609     }
00610 
00611   char name_buffer[MAX_STR_LENGTH+1];
00612   for (int i=0; i<num_node_sets; ++i)
00613     {
00614       ex_err = exII::ex_get_name(ex_id, exII::EX_NODE_SET,
00615                                  nodeset_ids[i], name_buffer);
00616       EX_CHECK_ERR(ex_err, "Error getting node set name.");
00617       id_to_ns_names[nodeset_ids[i]] = name_buffer;
00618     }
00619   message("All node set names retrieved successfully.");
00620 }
00621 
00622 
00623 
00624 void ExodusII_IO_Helper::read_sideset(int id, int offset)
00625 {
00626   libmesh_assert_less (static_cast<unsigned int>(id), ss_ids.size());
00627   libmesh_assert_less (static_cast<unsigned int>(id), num_sides_per_set.size());
00628   libmesh_assert_less (static_cast<unsigned int>(id), num_df_per_set.size());
00629   libmesh_assert_less_equal (static_cast<unsigned int>(offset), elem_list.size());
00630   libmesh_assert_less_equal (static_cast<unsigned int>(offset), side_list.size());
00631 
00632   ex_err = exII::ex_get_side_set_param(ex_id,
00633                                        ss_ids[id],
00634                                        &num_sides_per_set[id],
00635                                        &num_df_per_set[id]);
00636   EX_CHECK_ERR(ex_err, "Error retrieving sideset parameters.");
00637   message("Parameters retrieved successfully for sideset: ", id);
00638 
00639 
00640   // It's OK for offset==elem_list.size() as long as num_sides_per_set[id]==0
00641   // because in that case we don't actually read anything...
00642 #ifdef DEBUG
00643   if (static_cast<unsigned int>(offset) == elem_list.size() ||
00644       static_cast<unsigned int>(offset) == side_list.size() )
00645     libmesh_assert_equal_to (num_sides_per_set[id], 0);
00646 #endif
00647 
00648 
00649   // Don't call ex_get_side_set unless there are actually sides there to get.
00650   // Exodus prints an annoying warning in DEBUG mode otherwise...
00651   if (num_sides_per_set[id] > 0)
00652     {
00653       ex_err = exII::ex_get_side_set(ex_id,
00654                                      ss_ids[id],
00655                                      &elem_list[offset],
00656                                      &side_list[offset]);
00657       EX_CHECK_ERR(ex_err, "Error retrieving sideset data.");
00658       message("Data retrieved successfully for sideset: ", id);
00659 
00660       for (int i=0; i<num_sides_per_set[id]; i++)
00661         id_list[i+offset] = ss_ids[id];
00662     }
00663 }
00664 
00665 
00666 
00667 void ExodusII_IO_Helper::read_nodeset(int id)
00668 {
00669   libmesh_assert_less (static_cast<unsigned int>(id), nodeset_ids.size());
00670   libmesh_assert_less (static_cast<unsigned int>(id), num_nodes_per_set.size());
00671   libmesh_assert_less (static_cast<unsigned int>(id), num_node_df_per_set.size());
00672 
00673   ex_err = exII::ex_get_node_set_param(ex_id,
00674                                        nodeset_ids[id],
00675                                        &num_nodes_per_set[id],
00676                                        &num_node_df_per_set[id]);
00677   EX_CHECK_ERR(ex_err, "Error retrieving nodeset parameters.");
00678   message("Parameters retrieved successfully for nodeset: ", id);
00679 
00680   node_list.resize(num_nodes_per_set[id]);
00681 
00682   // Don't call ex_get_node_set unless there are actually nodes there to get.
00683   // Exodus prints an annoying warning message in DEBUG mode otherwise...
00684   if (num_nodes_per_set[id] > 0)
00685     {
00686       ex_err = exII::ex_get_node_set(ex_id,
00687                                      nodeset_ids[id],
00688                                      &node_list[0]);
00689 
00690       EX_CHECK_ERR(ex_err, "Error retrieving nodeset data.");
00691       message("Data retrieved successfully for nodeset: ", id);
00692     }
00693 }
00694 
00695 
00696 
00697 void ExodusII_IO_Helper::close()
00698 {
00699   // Always call close on processor 0.
00700   // If we're running on multiple processors, i.e. as one of several Nemesis files,
00701   // we call close on all processors...
00702   if ((this->processor_id() == 0) || (!_run_only_on_proc0))
00703     {
00704       ex_err = exII::ex_close(ex_id);
00705       EX_CHECK_ERR(ex_err, "Error closing Exodus file.");
00706       message("Exodus file closed successfully.");
00707     }
00708 }
00709 
00710 
00711 
00712 int ExodusII_IO_Helper::inquire(int req_info_in, std::string error_msg)
00713 {
00714   int ret_int = 0;
00715   char ret_char = 0;
00716   float ret_float = 0.;
00717 
00718   ex_err = exII::ex_inquire(ex_id,
00719                             req_info_in,
00720                             &ret_int,
00721                             &ret_float,
00722                             &ret_char);
00723 
00724   EX_CHECK_ERR(ex_err, error_msg);
00725 
00726   return ret_int;
00727 }
00728 
00729 
00730 
00731 void ExodusII_IO_Helper::read_time_steps()
00732 {
00733   // Make sure we have an up-to-date count of the number of time steps in the file.
00734   this->read_num_time_steps();
00735 
00736   if (num_time_steps > 0)
00737     {
00738       time_steps.resize(num_time_steps);
00739       ex_err = exII::ex_get_all_times(ex_id, &time_steps[0]);
00740       EX_CHECK_ERR(ex_err, "Error reading timesteps!");
00741     }
00742 }
00743 
00744 
00745 
00746 void ExodusII_IO_Helper::read_num_time_steps()
00747 {
00748   num_time_steps =
00749     this->inquire(exII::EX_INQ_TIME, "Error retrieving number of time steps");
00750 }
00751 
00752 
00753 
00754 void ExodusII_IO_Helper::read_nodal_var_values(std::string nodal_var_name, int time_step)
00755 {
00756   // Read the nodal variable names from file, so we can see if we have the one we're looking for
00757   this->read_var_names(NODAL);
00758 
00759   // See if we can find the variable we are looking for
00760   unsigned int var_index = 0;
00761   bool found = false;
00762 
00763   // Do a linear search for nodal_var_name in nodal_var_names
00764   for (; var_index<nodal_var_names.size(); ++var_index)
00765     {
00766       found = (nodal_var_names[var_index] == nodal_var_name);
00767       if (found)
00768         break;
00769     }
00770 
00771   if (!found)
00772     {
00773       libMesh::err << "Available variables: " << std::endl;
00774       for (unsigned int i=0; i<nodal_var_names.size(); ++i)
00775         libMesh::err << nodal_var_names[i] << std::endl;
00776 
00777       libmesh_error_msg("Unable to locate variable named: " << nodal_var_name);
00778     }
00779 
00780   // Allocate enough space to store the nodal variable values
00781   nodal_var_values.resize(num_nodes);
00782 
00783   // Call the Exodus API to read the nodal variable values
00784   ex_err = exII::ex_get_nodal_var(ex_id,
00785                                   time_step,
00786                                   var_index+1,
00787                                   num_nodes,
00788                                   &nodal_var_values[0]);
00789   EX_CHECK_ERR(ex_err, "Error reading nodal variable values!");
00790 }
00791 
00792 
00793 
00794 void ExodusII_IO_Helper::read_var_names(ExodusVarType type)
00795 {
00796   switch (type)
00797     {
00798     case NODAL:
00799       this->read_var_names_impl("n", num_nodal_vars, nodal_var_names);
00800       break;
00801     case ELEMENTAL:
00802       this->read_var_names_impl("e", num_elem_vars, elem_var_names);
00803       break;
00804     case GLOBAL:
00805       this->read_var_names_impl("g", num_global_vars, global_var_names);
00806       break;
00807     default:
00808       libmesh_error_msg("Unrecognized ExodusVarType " << type);
00809     }
00810 }
00811 
00812 
00813 
00814 void ExodusII_IO_Helper::read_var_names_impl(const char* var_type,
00815                                              int& count,
00816                                              std::vector<std::string>& result)
00817 {
00818   // First read and store the number of names we have
00819   ex_err = exII::ex_get_var_param(ex_id, var_type, &count);
00820   EX_CHECK_ERR(ex_err, "Error reading number of variables.");
00821 
00822   // Do nothing if no variables are detected
00823   if (count == 0)
00824     return;
00825 
00826   // Second read the actual names and convert them into a format we can use
00827   NamesData names_table(count, MAX_STR_LENGTH);
00828 
00829   ex_err = exII::ex_get_var_names(ex_id,
00830                                   var_type,
00831                                   count,
00832                                   names_table.get_char_star_star()
00833                                   );
00834   EX_CHECK_ERR(ex_err, "Error reading variable names!");
00835 
00836   if (verbose)
00837     {
00838       libMesh::out << "Read the variable(s) from the file:" << std::endl;
00839       for (int i=0; i<count; i++)
00840         libMesh::out << names_table.get_char_star(i) << std::endl;
00841     }
00842 
00843   // Allocate enough space for our variable name strings.
00844   result.resize(count);
00845 
00846   // Copy the char buffers into strings.
00847   for (int i=0; i<count; i++)
00848     result[i] = names_table.get_char_star(i); // calls string::op=(const char*)
00849 }
00850 
00851 
00852 
00853 
00854 void ExodusII_IO_Helper::write_var_names(ExodusVarType type, std::vector<std::string>& names)
00855 {
00856   switch (type)
00857     {
00858     case NODAL:
00859       this->write_var_names_impl("n", num_nodal_vars, names);
00860       break;
00861     case ELEMENTAL:
00862       this->write_var_names_impl("e", num_elem_vars, names);
00863       break;
00864     case GLOBAL:
00865       this->write_var_names_impl("g", num_global_vars, names);
00866       break;
00867     default:
00868       libmesh_error_msg("Unrecognized ExodusVarType " << type);
00869     }
00870 }
00871 
00872 
00873 
00874 void ExodusII_IO_Helper::write_var_names_impl(const char* var_type, int& count, std::vector<std::string>& names)
00875 {
00876   // Update the count variable so that it's available to other parts of the class.
00877   count = cast_int<int>(names.size());
00878 
00879   // Write that number of variables to the file.
00880   ex_err = exII::ex_put_var_param(ex_id, var_type, count);
00881   EX_CHECK_ERR(ex_err, "Error setting number of vars.");
00882 
00883   if (names.size() > 0)
00884     {
00885       NamesData names_table(names.size(), MAX_STR_LENGTH);
00886 
00887       // Store the input names in the format required by Exodus.
00888       for (unsigned i=0; i<names.size(); ++i)
00889         names_table.push_back_entry(names[i]);
00890 
00891       if (verbose)
00892         {
00893           libMesh::out << "Writing variable name(s) to file: " << std::endl;
00894           for (unsigned i=0; i<names.size(); ++i)
00895             libMesh::out << names_table.get_char_star(i) << std::endl;
00896         }
00897 
00898       ex_err = exII::ex_put_var_names(ex_id,
00899                                       var_type,
00900                                       cast_int<int>(names.size()),
00901                                       names_table.get_char_star_star()
00902                                       );
00903 
00904       EX_CHECK_ERR(ex_err, "Error writing variable names.");
00905     }
00906 }
00907 
00908 
00909 
00910 void ExodusII_IO_Helper::read_elemental_var_values(std::string elemental_var_name, int time_step)
00911 {
00912   // There is no way to get the whole elemental field from the
00913   // exodus file, so we have to go block by block.
00914   elem_var_values.resize(num_elem);
00915 
00916   this->read_var_names(ELEMENTAL);
00917 
00918   // See if we can find the variable we are looking for
00919   unsigned var_index = 0;
00920   bool found = false;
00921 
00922   // Do a linear search for nodal_var_name in nodal_var_names
00923   for (; var_index<elem_var_names.size(); ++var_index)
00924     {
00925       found = (elem_var_names[var_index] == elemental_var_name);
00926       if (found)
00927         break;
00928     }
00929 
00930   if (!found)
00931     {
00932       libMesh::err << "Available variables: " << std::endl;
00933       for (unsigned i=0; i<elem_var_names.size(); ++i)
00934         libMesh::err << elem_var_names[i] << std::endl;
00935 
00936       libmesh_error_msg("Unable to locate variable named: " << elemental_var_name);
00937     }
00938 
00939   // Sequential index which we can use to look up the element ID in the elem_num_map.
00940   unsigned ex_el_num = 0;
00941 
00942   for (unsigned i=0; i<static_cast<unsigned>(num_elem_blk); i++)
00943     {
00944       ex_err = exII::ex_get_elem_block(ex_id,
00945                                        block_ids[i],
00946                                        NULL,
00947                                        &num_elem_this_blk,
00948                                        NULL,
00949                                        NULL);
00950       EX_CHECK_ERR(ex_err, "Error getting number of elements in block.");
00951 
00952       std::vector<Real> block_elem_var_values(num_elem);
00953       ex_err = exII::ex_get_elem_var(ex_id,
00954                                      time_step,
00955                                      var_index+1,
00956                                      block_ids[i],
00957                                      num_elem_this_blk,
00958                                      &block_elem_var_values[0]);
00959       EX_CHECK_ERR(ex_err, "Error getting elemental values.");
00960 
00961       for (unsigned j=0; j<static_cast<unsigned>(num_elem_this_blk); j++)
00962         {
00963           // Use the elem_num_map to obtain the ID of this element in the Exodus file,
00964           // and remember to subtract 1 since libmesh is zero-based and Exodus is 1-based.
00965           unsigned mapped_elem_id = this->elem_num_map[ex_el_num] - 1;
00966 
00967           // Make sure we can actually write into this location in the elem_var_values vector
00968           if (mapped_elem_id >= elem_var_values.size())
00969             libmesh_error_msg("Error reading elemental variable values in Exodus!");
00970 
00971           // Write into the mapped_elem_id entry of the
00972           // elem_var_values vector.
00973           elem_var_values[mapped_elem_id] = block_elem_var_values[j];
00974 
00975           // Go to the next sequential element ID.
00976           ex_el_num++;
00977         }
00978     }
00979 }
00980 
00981 
00982 // For Writing Solutions
00983 
00984 void ExodusII_IO_Helper::create(std::string filename)
00985 {
00986   // If we're processor 0, always create the file.
00987   // If we running on all procs, e.g. as one of several Nemesis files, also
00988   // call create there.
00989   if ((this->processor_id() == 0) || (!_run_only_on_proc0))
00990     {
00991       int
00992         comp_ws = 0,
00993         io_ws = 0;
00994 
00995       if(_single_precision)
00996         {
00997           comp_ws = cast_int<int>(sizeof(float));
00998           io_ws = cast_int<int>(sizeof(float));
00999         }
01000       // Fall back on double precision when necessary since ExodusII
01001       // doesn't seem to support long double
01002       else
01003         {
01004           comp_ws = cast_int<int>
01005             (std::min(sizeof(Real), sizeof(double)));
01006           io_ws = cast_int<int>
01007             (std::min(sizeof(Real), sizeof(double)));
01008         }
01009 
01010       ex_id = exII::ex_create(filename.c_str(), EX_CLOBBER, &comp_ws, &io_ws);
01011 
01012       EX_CHECK_ERR(ex_id, "Error creating ExodusII mesh file.");
01013 
01014       if (verbose)
01015         libMesh::out << "File created successfully." << std::endl;
01016     }
01017 
01018   opened_for_writing = true;
01019   current_filename = filename;
01020 }
01021 
01022 
01023 
01024 void ExodusII_IO_Helper::initialize(std::string str_title, const MeshBase & mesh, bool use_discontinuous)
01025 {
01026   // n_active_elem() is a parallel_only function
01027   unsigned int n_active_elem = mesh.n_active_elem();
01028 
01029   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01030     return;
01031 
01032   if (_use_mesh_dimension_instead_of_spatial_dimension)
01033     num_dim = mesh.mesh_dimension();
01034   else
01035     num_dim = mesh.spatial_dimension();
01036 
01037   num_elem = mesh.n_elem();
01038 
01039   if (!use_discontinuous)
01040     num_nodes = mesh.n_nodes();
01041   else
01042     {
01043       MeshBase::const_element_iterator       it  = mesh.active_elements_begin();
01044       const MeshBase::const_element_iterator end = mesh.active_elements_end();
01045       for (; it!=end; ++it)
01046         num_nodes += (*it)->n_nodes();
01047     }
01048 
01049   std::vector<boundary_id_type> unique_side_boundaries;
01050   std::vector<boundary_id_type> unique_node_boundaries;
01051 
01052   mesh.get_boundary_info().build_side_boundary_ids(unique_side_boundaries);
01053   mesh.get_boundary_info().build_node_boundary_ids(unique_node_boundaries);
01054 
01055   num_side_sets = cast_int<int>(unique_side_boundaries.size());
01056   num_node_sets = cast_int<int>(unique_node_boundaries.size());
01057 
01058   //loop through element and map between block and element vector
01059   std::map<subdomain_id_type, std::vector<unsigned int>  > subdomain_map;
01060 
01061   MeshBase::const_element_iterator it = mesh.active_elements_begin();
01062   const MeshBase::const_element_iterator end = mesh.active_elements_end();
01063   for (; it!=end; ++it)
01064     {
01065       const Elem * elem = *it;
01066       subdomain_id_type cur_subdomain = elem->subdomain_id();
01067 
01068       subdomain_map[cur_subdomain].push_back(elem->id());
01069     }
01070   num_elem_blk = cast_int<int>(subdomain_map.size());
01071 
01072   if (str_title.size() > MAX_LINE_LENGTH)
01073     {
01074       libMesh::err << "Warning, Exodus files cannot have titles longer than "
01075                    << MAX_LINE_LENGTH
01076                    << " characters.  Your title will be truncated."
01077                    << std::endl;
01078       str_title.resize(MAX_LINE_LENGTH);
01079     }
01080 
01081   ex_err = exII::ex_put_init(ex_id,
01082                              str_title.c_str(),
01083                              num_dim,
01084                              num_nodes,
01085                              n_active_elem,
01086                              num_elem_blk,
01087                              num_node_sets,
01088                              num_side_sets);
01089 
01090   EX_CHECK_ERR(ex_err, "Error initializing new Exodus file.");
01091 }
01092 
01093 
01094 
01095 void ExodusII_IO_Helper::write_nodal_coordinates(const MeshBase & mesh, bool use_discontinuous)
01096 {
01097   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01098     return;
01099 
01100   // Make room in the coordinate vectors
01101   x.resize(num_nodes);
01102   y.resize(num_nodes);
01103   z.resize(num_nodes);
01104 
01105   // And in the node_num_map - since the nodes aren't organized in
01106   // blocks, libmesh will always write out the identity map
01107   // here... unless there has been some refinement and coarsening. If
01108   // the nodes aren't renumbered after refinement and coarsening,
01109   // there may be 'holes' in the numbering, so we write out the node
01110   // map just to be on the safe side.
01111   node_num_map.resize(num_nodes);
01112 
01113   if (!use_discontinuous)
01114     {
01115       MeshBase::const_node_iterator it = mesh.nodes_begin();
01116       const MeshBase::const_node_iterator end = mesh.nodes_end();
01117       for (unsigned i = 0; it != end; ++it, ++i)
01118         {
01119           const Node* node = *it;
01120 
01121           x[i] = (*node)(0) + _coordinate_offset(0);
01122 
01123 #if LIBMESH_DIM > 1
01124           y[i]=(*node)(1) + _coordinate_offset(1);
01125 #else
01126           y[i]=0.;
01127 #endif
01128 #if LIBMESH_DIM > 2
01129           z[i]=(*node)(2) + _coordinate_offset(2);
01130 #else
01131           z[i]=0.;
01132 #endif
01133 
01134           // Fill in node_num_map entry with the proper (1-based) node id
01135           node_num_map[i] = node->id() + 1;
01136         }
01137     }
01138   else
01139     {
01140       MeshBase::const_element_iterator       it  = mesh.active_elements_begin();
01141       const MeshBase::const_element_iterator end = mesh.active_elements_end();
01142 
01143       unsigned int i = 0;
01144       for (; it!=end; ++it)
01145         for (unsigned int n=0; n<(*it)->n_nodes(); n++)
01146           {
01147             x[i]=(*it)->point(n)(0);
01148 #if LIBMESH_DIM > 1
01149             y[i]=(*it)->point(n)(1);
01150 #else
01151             y[i]=0.;
01152 #endif
01153 #if LIBMESH_DIM > 2
01154             z[i]=(*it)->point(n)(2);
01155 #else
01156             z[i]=0.;
01157 #endif
01158 
01159             // Let's skip the node_num_map in the discontinuous case,
01160             // since we're effectively duplicating nodes for the
01161             // sake of discontinuous visualization, so it isn't clear
01162             // how to deal with node_num_map here. It's only optional
01163             // anyway, so no big deal.
01164 
01165             i++;
01166           }
01167     }
01168 
01169   if (_single_precision)
01170     {
01171       std::vector<float>
01172         x_single(x.begin(), x.end()),
01173         y_single(y.begin(), y.end()),
01174         z_single(z.begin(), z.end());
01175 
01176       ex_err = exII::ex_put_coord(ex_id,
01177                                   x_single.empty() ? NULL : &x_single[0],
01178                                   y_single.empty() ? NULL : &y_single[0],
01179                                   z_single.empty() ? NULL : &z_single[0]);
01180     }
01181   else
01182     {
01183       ex_err = exII::ex_put_coord(ex_id,
01184                                   x.empty() ? NULL : &x[0],
01185                                   y.empty() ? NULL : &y[0],
01186                                   z.empty() ? NULL : &z[0]);
01187     }
01188 
01189 
01190   EX_CHECK_ERR(ex_err, "Error writing coordinates to Exodus file.");
01191 
01192   if (!use_discontinuous)
01193     {
01194       // Also write the (1-based) node_num_map to the file.
01195       ex_err = exII::ex_put_node_num_map(ex_id, &node_num_map[0]);
01196       EX_CHECK_ERR(ex_err, "Error writing node_num_map");
01197     }
01198 }
01199 
01200 
01201 
01202 void ExodusII_IO_Helper::write_elements(const MeshBase & mesh, bool use_discontinuous)
01203 {
01204   // n_active_elem() is a parallel_only function
01205   unsigned int n_active_elem = mesh.n_active_elem();
01206 
01207   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01208     return;
01209 
01210   // Map from block ID to a vector of element IDs in that block.  Element
01211   // IDs are now of type dof_id_type, subdomain IDs are of type subdomain_id_type.
01212   typedef std::map<subdomain_id_type, std::vector<dof_id_type> > subdomain_map_type;
01213   subdomain_map_type subdomain_map;
01214 
01215   MeshBase::const_element_iterator mesh_it = mesh.active_elements_begin();
01216   const MeshBase::const_element_iterator end = mesh.active_elements_end();
01217   //loop through element and map between block and element vector
01218   for (; mesh_it!=end; ++mesh_it)
01219     {
01220       const Elem * elem = *mesh_it;
01221       subdomain_map[ elem->subdomain_id() ].push_back(elem->id());
01222     }
01223 
01224   // element map vector
01225   num_elem_blk = cast_int<int>(subdomain_map.size());
01226   block_ids.resize(num_elem_blk);
01227   elem_num_map.resize(n_active_elem);
01228   std::vector<int>::iterator curr_elem_map_end = elem_num_map.begin();
01229 
01230   // Note: It appears that there is a bug in exodusII::ex_put_name where
01231   // the index returned from the ex_id_lkup is erronously used.  For now
01232   // the work around is to use the alternative function ex_put_names, but
01233   // this function requires a char** datastructure.
01234   NamesData names_table(num_elem_blk, MAX_STR_LENGTH);
01235 
01236   // This counter is used to fill up the libmesh_elem_num_to_exodus map in the loop below.
01237   unsigned libmesh_elem_num_to_exodus_counter = 0;
01238 
01239   // counter indexes into the block_ids vector
01240   unsigned int counter = 0;
01241 
01242   // node counter for discontinuous plotting
01243   unsigned int node_counter = 0;
01244 
01245   for (subdomain_map_type::iterator it=subdomain_map.begin(); it!=subdomain_map.end(); ++it)
01246     {
01247       block_ids[counter] = (*it).first;
01248       names_table.push_back_entry(mesh.subdomain_name((*it).first));
01249 
01250       // Get a reference to a vector of element IDs for this subdomain.
01251       subdomain_map_type::mapped_type& tmp_vec = (*it).second;
01252 
01253       ExodusII_IO_Helper::ElementMaps em;
01254 
01255       //Use the first element in this block to get representative information.
01256       //Note that Exodus assumes all elements in a block are of the same type!
01257       //We are using that same assumption here!
01258       const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(mesh.elem(tmp_vec[0])->type());
01259       num_nodes_per_elem = mesh.elem(tmp_vec[0])->n_nodes();
01260 
01261       ex_err = exII::ex_put_elem_block(ex_id,
01262                                        (*it).first,
01263                                        conv.exodus_elem_type().c_str(),
01264                                        tmp_vec.size(),
01265                                        num_nodes_per_elem,
01266                                        /*num_attr=*/0);
01267 
01268       EX_CHECK_ERR(ex_err, "Error writing element block.");
01269 
01270       connect.resize(tmp_vec.size()*num_nodes_per_elem);
01271 
01272       for (unsigned int i=0; i<tmp_vec.size(); i++)
01273         {
01274           unsigned int elem_id = tmp_vec[i];
01275           libmesh_elem_num_to_exodus[elem_id] = ++libmesh_elem_num_to_exodus_counter; // 1-based indexing for Exodus
01276 
01277           const Elem* elem = mesh.elem(elem_id);
01278 
01279           // We *might* be able to get away with writing mixed element
01280           // types which happen to have the same number of nodes, but
01281           // do we actually *want* to get away with that?
01282           // .) No visualization software would be able to handle it.
01283           // .) There'd be no way for us to read it back in reliably.
01284           // .) Even elements with the same number of nodes may have different connectivities (?)
01285 
01286           // This needs to be more than an assert so we don't fail
01287           // with a mysterious segfault while trying to write mixed
01288           // element meshes in optimized mode.
01289           if (elem->type() != conv.get_canonical_type())
01290             libmesh_error_msg("Error: Exodus requires all elements with a given subdomain ID to be the same type.\n" \
01291                               << "Can't write both "                  \
01292                               << Utility::enum_to_string(elem->type()) \
01293                               << " and "                              \
01294                               << Utility::enum_to_string(conv.get_canonical_type()) \
01295                               << " in the same block!");
01296 
01297 
01298           for (unsigned int j=0; j<static_cast<unsigned int>(num_nodes_per_elem); ++j)
01299             {
01300               unsigned connect_index   = (i*num_nodes_per_elem)+j;
01301               unsigned elem_node_index = conv.get_inverse_node_map(j); // inverse node map is for writing.
01302               if (verbose)
01303                 {
01304                   libMesh::out << "Exodus node index: " << j
01305                                << "=LibMesh node index " << elem_node_index << std::endl;
01306                 }
01307 
01308               // FIXME: We are hard-coding the 1-based node numbering assumption here.
01309               if (!use_discontinuous)
01310                 connect[connect_index] = elem->node(elem_node_index)+1;
01311               else
01312                 connect[connect_index] = node_counter*num_nodes_per_elem+elem_node_index+1;
01313             }
01314 
01315           node_counter++;
01316         }
01317 
01318       ex_err = exII::ex_put_elem_conn(ex_id, (*it).first, &connect[0]);
01319       EX_CHECK_ERR(ex_err, "Error writing element connectivities");
01320 
01321       // This transform command stores its result in a range that begins at the third argument,
01322       // so this command is adding values to the elem_num_map vector starting from curr_elem_map_end.
01323       curr_elem_map_end = std::transform(tmp_vec.begin(),
01324                                          tmp_vec.end(),
01325                                          curr_elem_map_end,
01326                                          std::bind2nd(std::plus<subdomain_map_type::mapped_type::value_type>(), 1));  // Adds one to each id to make a 1-based exodus file!
01327 
01328       // But if we don't want to add one, we just want to put the values
01329       // of tmp_vec into elem_map in the right location, we can use
01330       // std::copy().
01331       // curr_elem_map_end = std::copy(tmp_vec.begin(), tmp_vec.end(), curr_elem_map_end);
01332 
01333       counter++;
01334     }
01335 
01336   // write out the element number map that we created
01337   ex_err = exII::ex_put_elem_num_map(ex_id, &elem_num_map[0]);
01338   EX_CHECK_ERR(ex_err, "Error writing element map");
01339 
01340   // Write out the block names
01341   if (num_elem_blk > 0)
01342     {
01343       ex_err = exII::ex_put_names(ex_id, exII::EX_ELEM_BLOCK, names_table.get_char_star_star());
01344       EX_CHECK_ERR(ex_err, "Error writing element names");
01345     }
01346 }
01347 
01348 
01349 
01350 
01351 void ExodusII_IO_Helper::write_sidesets(const MeshBase & mesh)
01352 {
01353   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01354     return;
01355 
01356   ExodusII_IO_Helper::ElementMaps em;
01357 
01358   std::vector< dof_id_type > el;
01359   std::vector< unsigned short int > sl;
01360   std::vector< boundary_id_type > il;
01361 
01362   mesh.get_boundary_info().build_side_list(el, sl, il);
01363 
01364   // Maps from sideset id to the element and sides
01365   std::map<int, std::vector<int> > elem;
01366   std::map<int, std::vector<int> > side;
01367 
01368   // Accumulate the vectors to pass into ex_put_side_set
01369   for (unsigned int i=0; i<el.size(); i++)
01370     {
01371       std::vector<const Elem *> family;
01372 #ifdef LIBMESH_ENABLE_AMR
01373 
01377       mesh.elem(el[i])->active_family_tree_by_side(family, sl[i], false);
01378 #else
01379       family.push_back(mesh.elem(el[i]));
01380 #endif
01381 
01382       for (unsigned int j=0; j<family.size(); ++j)
01383         {
01384           const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(mesh.elem(family[j]->id())->type());
01385 
01386           // Use the libmesh to exodus datastructure map to get the proper sideset IDs
01387           // The datastructure contains the "collapsed" contiguous ids
01388           elem[il[i]].push_back(libmesh_elem_num_to_exodus[family[j]->id()]);
01389           side[il[i]].push_back(conv.get_inverse_side_map(sl[i]));
01390         }
01391     }
01392 
01393   std::vector<boundary_id_type> side_boundary_ids;
01394   mesh.get_boundary_info().build_side_boundary_ids(side_boundary_ids);
01395 
01396   // Write out the sideset names, but only if there is something to write
01397   if (side_boundary_ids.size() > 0)
01398     {
01399       NamesData names_table(side_boundary_ids.size(), MAX_STR_LENGTH);
01400 
01401       for (unsigned int i=0; i<side_boundary_ids.size(); i++)
01402         {
01403           boundary_id_type ss_id = side_boundary_ids[i];
01404 
01405           int actual_id = ss_id;
01406 
01407           names_table.push_back_entry(mesh.get_boundary_info().get_sideset_name(ss_id));
01408 
01409           ex_err = exII::ex_put_side_set_param(ex_id, actual_id, elem[ss_id].size(), 0);
01410           EX_CHECK_ERR(ex_err, "Error writing sideset parameters");
01411 
01412           ex_err = exII::ex_put_side_set(ex_id, actual_id, &elem[ss_id][0], &side[ss_id][0]);
01413           EX_CHECK_ERR(ex_err, "Error writing sidesets");
01414         }
01415 
01416       ex_err = exII::ex_put_names(ex_id, exII::EX_SIDE_SET, names_table.get_char_star_star());
01417       EX_CHECK_ERR(ex_err, "Error writing sideset names");
01418     }
01419 }
01420 
01421 
01422 
01423 void ExodusII_IO_Helper::write_nodesets(const MeshBase & mesh)
01424 {
01425   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01426     return;
01427 
01428   std::vector< dof_id_type > nl;
01429   std::vector< boundary_id_type > il;
01430 
01431   mesh.get_boundary_info().build_node_list(nl, il);
01432 
01433   // Maps from nodeset id to the nodes
01434   std::map<boundary_id_type, std::vector<int> > node;
01435 
01436   // Accumulate the vectors to pass into ex_put_node_set
01437   for (unsigned int i=0; i<nl.size(); i++)
01438     node[il[i]].push_back(nl[i]+1);
01439 
01440   std::vector<boundary_id_type> node_boundary_ids;
01441   mesh.get_boundary_info().build_node_boundary_ids(node_boundary_ids);
01442 
01443   // Write out the nodeset names, but only if there is something to write
01444   if (node_boundary_ids.size() > 0)
01445     {
01446       NamesData names_table(node_boundary_ids.size(), MAX_STR_LENGTH);
01447 
01448       for (unsigned int i=0; i<node_boundary_ids.size(); i++)
01449         {
01450           boundary_id_type nodeset_id = node_boundary_ids[i];
01451 
01452           int actual_id = nodeset_id;
01453 
01454           names_table.push_back_entry(mesh.get_boundary_info().get_nodeset_name(nodeset_id));
01455 
01456           ex_err = exII::ex_put_node_set_param(ex_id, actual_id, node[nodeset_id].size(), 0);
01457           EX_CHECK_ERR(ex_err, "Error writing nodeset parameters");
01458 
01459           ex_err = exII::ex_put_node_set(ex_id, actual_id, &node[nodeset_id][0]);
01460           EX_CHECK_ERR(ex_err, "Error writing nodesets");
01461         }
01462 
01463       // Write out the nodeset names
01464       ex_err = exII::ex_put_names(ex_id, exII::EX_NODE_SET, names_table.get_char_star_star());
01465       EX_CHECK_ERR(ex_err, "Error writing nodeset names");
01466     }
01467 }
01468 
01469 
01470 
01471 void ExodusII_IO_Helper::initialize_element_variables(std::vector<std::string> names)
01472 {
01473   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01474     return;
01475 
01476   // Quick return if there are no element variables to write
01477   if (names.size() == 0)
01478     return;
01479 
01480   // Quick return if we have already called this function
01481   if (_elem_vars_initialized)
01482     return;
01483 
01484   // Be sure that variables in the file match what we are asking for
01485   if (num_elem_vars > 0)
01486     {
01487       this->check_existing_vars(ELEMENTAL, names, this->elem_var_names);
01488       return;
01489     }
01490 
01491   // Set the flag so we can skip this stuff on subsequent calls to
01492   // initialize_element_variables()
01493   _elem_vars_initialized = true;
01494 
01495   this->write_var_names(ELEMENTAL, names);
01496 
01497   // Form the element variable truth table and send to Exodus.
01498   // This tells which variables are written to which blocks,
01499   // and can dramatically speed up writing element variables
01500   //
01501   // We really should initialize all entries in the truth table to 0
01502   // and then loop over all subdomains, setting their entries to 1
01503   // if a given variable exists on that subdomain.  However,
01504   // we don't have that information, and the element variables
01505   // passed to us are padded with zeroes for the blocks where
01506   // they aren't defined.  To be consistent with that, fill
01507   // the truth table with ones.
01508   std::vector<int> truth_tab(num_elem_blk*num_elem_vars, 1);
01509   ex_err = exII::ex_put_elem_var_tab(ex_id,
01510                                      num_elem_blk,
01511                                      num_elem_vars,
01512                                      &truth_tab[0]);
01513   EX_CHECK_ERR(ex_err, "Error writing element truth table.");
01514 }
01515 
01516 
01517 
01518 void ExodusII_IO_Helper::initialize_nodal_variables(std::vector<std::string> names)
01519 {
01520   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01521     return;
01522 
01523   // Quick return if there are no nodal variables to write
01524   if (names.size() == 0)
01525     return;
01526 
01527   // Quick return if we have already called this function
01528   if (_nodal_vars_initialized)
01529     return;
01530 
01531   // Be sure that variables in the file match what we are asking for
01532   if (num_nodal_vars > 0)
01533     {
01534       this->check_existing_vars(NODAL, names, this->nodal_var_names);
01535       return;
01536     }
01537 
01538   // Set the flag so we can skip the rest of this function on subsequent calls.
01539   _nodal_vars_initialized = true;
01540 
01541   this->write_var_names(NODAL, names);
01542 }
01543 
01544 
01545 
01546 void ExodusII_IO_Helper::initialize_global_variables(std::vector<std::string> names)
01547 {
01548   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01549     return;
01550 
01551   // Quick return if there are no global variables to write
01552   if (names.size() == 0)
01553     return;
01554 
01555   if (_global_vars_initialized)
01556     return;
01557 
01558   // Be sure that variables in the file match what we are asking for
01559   if (num_global_vars > 0)
01560     {
01561       this->check_existing_vars(GLOBAL, names, this->global_var_names);
01562       return;
01563     }
01564 
01565   _global_vars_initialized = true;
01566 
01567   this->write_var_names(GLOBAL, names);
01568 }
01569 
01570 
01571 
01572 void ExodusII_IO_Helper::check_existing_vars(ExodusVarType type,
01573                                              std::vector<std::string>& names,
01574                                              std::vector<std::string>& names_from_file)
01575 {
01576   // There may already be global variables in the file (for example,
01577   // if we're appending) and in that case, we
01578   // 1.) Cannot initialize them again.
01579   // 2.) Should check to be sure that the global variable names are the same.
01580 
01581   // Fills up names_from_file for us
01582   this->read_var_names(type);
01583 
01584   // Both the names of the global variables and their order must match
01585   if (names_from_file != names)
01586     {
01587       libMesh::err << "Error! The Exodus file already contains the variables:" << std::endl;
01588       for (unsigned i=0; i<names_from_file.size(); ++i)
01589         libMesh::out << names_from_file[i] << std::endl;
01590 
01591       libMesh::err << "And you asked to write:" << std::endl;
01592       for (unsigned i=0; i<names.size(); ++i)
01593         libMesh::out << names[i] << std::endl;
01594 
01595       libmesh_error_msg("Cannot overwrite existing variables in Exodus II file.");
01596     }
01597 }
01598 
01599 
01600 
01601 void ExodusII_IO_Helper::write_timestep(int timestep, Real time)
01602 {
01603   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01604     return;
01605 
01606   ex_err = exII::ex_put_time(ex_id, timestep, &time);
01607   EX_CHECK_ERR(ex_err, "Error writing timestep.");
01608 
01609   ex_err = exII::ex_update(ex_id);
01610   EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
01611 }
01612 
01613 
01614 
01615 void ExodusII_IO_Helper::write_element_values(const MeshBase & mesh, const std::vector<Real> & values, int timestep)
01616 {
01617   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01618     return;
01619 
01620   // Loop over the element blocks and write the data one block at a time
01621   std::map<unsigned int, std::vector<unsigned int> > subdomain_map;
01622 
01623   // Ask the file how many element vars it has, store it in the num_elem_vars variable.
01624   ex_err = exII::ex_get_var_param(ex_id, "e", &num_elem_vars);
01625   EX_CHECK_ERR(ex_err, "Error reading number of elemental variables.");
01626 
01627   MeshBase::const_element_iterator mesh_it = mesh.active_elements_begin();
01628   const MeshBase::const_element_iterator end = mesh.active_elements_end();
01629 
01630   // loop through element and map between block and element vector
01631   for (; mesh_it!=end; ++mesh_it)
01632     {
01633       const Elem * elem = *mesh_it;
01634       subdomain_map[elem->subdomain_id()].push_back(elem->id());
01635     }
01636 
01637   // Use mesh.n_elem() to access into the values vector rather than
01638   // the number of elements the Exodus writer thinks the mesh has,
01639   // which may not include inactive elements.
01640   dof_id_type n_elem = mesh.n_elem();
01641 
01642   // For each variable, create a 'data' array which holds all the elemental variable
01643   // values *for a given block* on this processor, then write that data vector to file
01644   // before moving onto the next block.
01645   for (unsigned int i=0; i<static_cast<unsigned>(num_elem_vars); ++i)
01646     {
01647       // The size of the subdomain map is the number of blocks.
01648       std::map<unsigned int, std::vector<unsigned int> >::iterator it = subdomain_map.begin();
01649 
01650       for (unsigned int j=0; it!=subdomain_map.end(); ++it, ++j)
01651         {
01652           const std::vector<unsigned int> & elem_nums = (*it).second;
01653           const unsigned int num_elems_this_block =
01654             cast_int<unsigned int>(elem_nums.size());
01655           std::vector<Real> data(num_elems_this_block);
01656 
01657           for (unsigned int k=0; k<num_elems_this_block; ++k)
01658             data[k] = values[i*n_elem + elem_nums[k]];
01659 
01660           if (_single_precision)
01661             {
01662               std::vector<float> cast_data(data.begin(), data.end());
01663 
01664               ex_err = exII::ex_put_elem_var(ex_id,
01665                                              timestep,
01666                                              i+1,
01667                                              this->get_block_id(j),
01668                                              num_elems_this_block,
01669                                              &cast_data[0]);
01670             }
01671           else
01672             {
01673               ex_err = exII::ex_put_elem_var(ex_id,
01674                                              timestep,
01675                                              i+1,
01676                                              this->get_block_id(j),
01677                                              num_elems_this_block,
01678                                              &data[0]);
01679             }
01680           EX_CHECK_ERR(ex_err, "Error writing element values.");
01681         }
01682     }
01683 
01684   ex_err = exII::ex_update(ex_id);
01685   EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
01686 }
01687 
01688 
01689 
01690 void ExodusII_IO_Helper::write_nodal_values(int var_id, const std::vector<Real> & values, int timestep)
01691 {
01692   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01693     return;
01694 
01695   if (_single_precision)
01696     {
01697       std::vector<float> cast_values(values.begin(), values.end());
01698       ex_err = exII::ex_put_nodal_var(ex_id, timestep, var_id, num_nodes, &cast_values[0]);
01699     }
01700   else
01701     {
01702       ex_err = exII::ex_put_nodal_var(ex_id, timestep, var_id, num_nodes, &values[0]);
01703     }
01704   EX_CHECK_ERR(ex_err, "Error writing nodal values.");
01705 
01706   ex_err = exII::ex_update(ex_id);
01707   EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
01708 }
01709 
01710 
01711 
01712 void ExodusII_IO_Helper::write_information_records(const std::vector<std::string>& records)
01713 {
01714   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01715     return;
01716 
01717   // There may already be information records in the file (for
01718   // example, if we're appending) and in that case, according to the
01719   // Exodus documentation, writing more information records is not
01720   // supported.
01721   int num_info = inquire(exII::EX_INQ_INFO, "Error retrieving the number of information records from file!");
01722   if (num_info > 0)
01723     {
01724       libMesh::err << "Warning! The Exodus file already contains information records.\n"
01725                    << "Exodus does not support writing additional records in this situation."
01726                    << std::endl;
01727       return;
01728     }
01729 
01730   int num_records = cast_int<int>(records.size());
01731 
01732   if (num_records > 0)
01733     {
01734       NamesData info(num_records, MAX_LINE_LENGTH);
01735 
01736       // If an entry is longer than MAX_LINE_LENGTH characters it's not an error, we just
01737       // write the first MAX_LINE_LENGTH characters to the file.
01738       for (unsigned i=0; i<records.size(); ++i)
01739         info.push_back_entry(records[i]);
01740 
01741       ex_err = exII::ex_put_info(ex_id, num_records, info.get_char_star_star());
01742       EX_CHECK_ERR(ex_err, "Error writing global values.");
01743 
01744       ex_err = exII::ex_update(ex_id);
01745       EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
01746     }
01747 }
01748 
01749 
01750 
01751 void ExodusII_IO_Helper::write_global_values(const std::vector<Real> & values, int timestep)
01752 {
01753   if ((_run_only_on_proc0) && (this->processor_id() != 0))
01754     return;
01755 
01756   if (_single_precision)
01757     {
01758       std::vector<float> cast_values(values.begin(), values.end());
01759       ex_err = exII::ex_put_glob_vars(ex_id, timestep, num_global_vars, &cast_values[0]);
01760     }
01761   else
01762     {
01763       ex_err = exII::ex_put_glob_vars(ex_id, timestep, num_global_vars, &values[0]);
01764     }
01765   EX_CHECK_ERR(ex_err, "Error writing global values.");
01766 
01767   ex_err = exII::ex_update(ex_id);
01768   EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
01769 }
01770 
01771 
01772 
01773 void ExodusII_IO_Helper::use_mesh_dimension_instead_of_spatial_dimension(bool val)
01774 {
01775   _use_mesh_dimension_instead_of_spatial_dimension = val;
01776 }
01777 
01778 void ExodusII_IO_Helper::set_coordinate_offset(Point p)
01779 {
01780   _coordinate_offset = p;
01781 }
01782 
01783 
01784 std::vector<std::string> ExodusII_IO_Helper::get_complex_names(const std::vector<std::string>& names) const
01785 {
01786   std::vector<std::string>::const_iterator names_it = names.begin();
01787   std::vector<std::string>::const_iterator names_end = names.end();
01788 
01789   std::vector<std::string> complex_names;
01790 
01791   // This will loop over all names and create new "complex" names
01792   // (i.e. names that start with r_, i_ or a_
01793   for (; names_it != names_end; ++names_it)
01794     {
01795 //      std::cout << "VARIABLE: " << *names_it << std::endl;
01796       std::stringstream name_real, name_imag, name_abs;
01797       name_real << "r_" << *names_it;
01798       name_imag << "i_" << *names_it;
01799       name_abs << "a_" << *names_it;
01800 
01801       complex_names.push_back(name_real.str());
01802       complex_names.push_back(name_imag.str());
01803       complex_names.push_back(name_abs.str());
01804     }
01805 
01806   return complex_names;
01807 }
01808 
01809 
01810 
01811 // ------------------------------------------------------------
01812 // ExodusII_IO_Helper::Conversion class members
01813 ExodusII_IO_Helper::Conversion ExodusII_IO_Helper::ElementMaps::assign_conversion(std::string type_str)
01814 {
01815   init_element_equivalence_map();
01816 
01817   // Do only upper-case comparisons
01818   std::transform(type_str.begin(), type_str.end(), type_str.begin(), ::toupper);
01819 
01820   std::map<std::string, ElemType>::iterator it =
01821     element_equivalence_map.find(type_str);
01822 
01823   if (it != element_equivalence_map.end())
01824     return assign_conversion( it->second );
01825   else
01826     libmesh_error_msg("ERROR! Unrecognized element type_str: " << type_str);
01827 
01828   libmesh_error_msg("We'll never get here!");
01829   return assign_conversion (EDGE2);
01830 }
01831 
01832 
01833 
01834 ExodusII_IO_Helper::Conversion ExodusII_IO_Helper::ElementMaps::assign_conversion(const ElemType type)
01835 {
01836   switch (type)
01837     {
01838     case EDGE2:
01839       {
01840         const Conversion conv(edge2_node_map,
01841                               ARRAY_LENGTH(edge2_node_map),
01842                               edge2_node_map, // inverse node map same as forward node map
01843                               ARRAY_LENGTH(edge2_node_map),
01844                               edge_edge_map,
01845                               ARRAY_LENGTH(edge_edge_map),
01846                               edge_inverse_edge_map,
01847                               ARRAY_LENGTH(edge_inverse_edge_map),
01848                               EDGE2, "EDGE2");
01849         return conv;
01850       }
01851     case EDGE3:
01852       {
01853         const Conversion conv(edge3_node_map,
01854                               ARRAY_LENGTH(edge3_node_map),
01855                               edge3_node_map, // inverse node map same as forward node map
01856                               ARRAY_LENGTH(edge3_node_map),
01857                               edge_edge_map,
01858                               ARRAY_LENGTH(edge_edge_map),
01859                               edge_inverse_edge_map,
01860                               ARRAY_LENGTH(edge_inverse_edge_map),
01861                               EDGE3, "EDGE3");
01862         return conv;
01863       }
01864     case QUAD4:
01865       {
01866         const Conversion conv(quad4_node_map,
01867                               ARRAY_LENGTH(quad4_node_map),
01868                               quad4_node_map, // inverse node map same as forward node map
01869                               ARRAY_LENGTH(quad4_node_map),
01870                               quad_edge_map,
01871                               ARRAY_LENGTH(quad_edge_map),
01872                               quad_inverse_edge_map,
01873                               ARRAY_LENGTH(quad_inverse_edge_map),
01874                               QUAD4,
01875                               "QUAD4");
01876         return conv;
01877       }
01878 
01879     case QUAD8:
01880       {
01881         const Conversion conv(quad8_node_map,
01882                               ARRAY_LENGTH(quad8_node_map),
01883                               quad8_node_map, // inverse node map same as forward node map
01884                               ARRAY_LENGTH(quad8_node_map),
01885                               quad_edge_map,
01886                               ARRAY_LENGTH(quad_edge_map),
01887                               quad_inverse_edge_map,
01888                               ARRAY_LENGTH(quad_inverse_edge_map),
01889                               QUAD8,
01890                               "QUAD8");
01891         return conv;
01892       }
01893 
01894     case QUAD9:
01895       {
01896         const Conversion conv(quad9_node_map,
01897                               ARRAY_LENGTH(quad9_node_map),
01898                               quad9_node_map, // inverse node map same as forward node map
01899                               ARRAY_LENGTH(quad9_node_map),
01900                               quad_edge_map,
01901                               ARRAY_LENGTH(quad_edge_map),
01902                               quad_inverse_edge_map,
01903                               ARRAY_LENGTH(quad_inverse_edge_map),
01904                               QUAD9,
01905                               "QUAD9");
01906         return conv;
01907       }
01908 
01909     case TRI3:
01910       {
01911         const Conversion conv(tri3_node_map,
01912                               ARRAY_LENGTH(tri3_node_map),
01913                               tri3_node_map, // inverse node map same as forward node map
01914                               ARRAY_LENGTH(tri3_node_map),
01915                               tri_edge_map,
01916                               ARRAY_LENGTH(tri_edge_map),
01917                               tri_inverse_edge_map,
01918                               ARRAY_LENGTH(tri_inverse_edge_map),
01919                               TRI3,
01920                               "TRI3");
01921         return conv;
01922       }
01923 
01924     case TRI3SUBDIVISION:
01925       {
01926         const Conversion conv(tri3_node_map,
01927                               ARRAY_LENGTH(tri3_node_map),
01928                               tri3_node_map, // inverse node map same as forward node map
01929                               ARRAY_LENGTH(tri3_node_map),
01930                               tri_edge_map,
01931                               ARRAY_LENGTH(tri_edge_map),
01932                               tri_inverse_edge_map,
01933                               ARRAY_LENGTH(tri_inverse_edge_map),
01934                               TRI3SUBDIVISION,
01935                               "TRI3");
01936         return conv;
01937       }
01938 
01939     case TRI6:
01940       {
01941         const Conversion conv(tri6_node_map,
01942                               ARRAY_LENGTH(tri6_node_map),
01943                               tri6_node_map, // inverse node map same as forward node map
01944                               ARRAY_LENGTH(tri6_node_map),
01945                               tri_edge_map,
01946                               ARRAY_LENGTH(tri_edge_map),
01947                               tri_inverse_edge_map,
01948                               ARRAY_LENGTH(tri_inverse_edge_map),
01949                               TRI6,
01950                               "TRI6");
01951         return conv;
01952       }
01953 
01954     case HEX8:
01955       {
01956         const Conversion conv(hex8_node_map,
01957                               ARRAY_LENGTH(hex8_node_map),
01958                               hex8_node_map, // inverse node map same as forward node map
01959                               ARRAY_LENGTH(hex8_node_map),
01960                               hex_face_map,
01961                               ARRAY_LENGTH(hex_face_map),
01962                               hex_inverse_face_map,
01963                               ARRAY_LENGTH(hex_inverse_face_map),
01964                               HEX8,
01965                               "HEX8");
01966         return conv;
01967       }
01968 
01969     case HEX20:
01970       {
01971         const Conversion conv(hex20_node_map,
01972                               ARRAY_LENGTH(hex20_node_map),
01973                               hex20_node_map, // inverse node map same as forward node map
01974                               ARRAY_LENGTH(hex20_node_map),
01975                               hex_face_map,
01976                               ARRAY_LENGTH(hex_face_map),
01977                               hex_inverse_face_map,
01978                               ARRAY_LENGTH(hex_inverse_face_map),
01979                               HEX20,
01980                               "HEX20");
01981         return conv;
01982       }
01983 
01984     case HEX27:
01985       {
01986         const Conversion conv(hex27_node_map,
01987                               ARRAY_LENGTH(hex27_node_map),
01988                               hex27_inverse_node_map, // different inverse node map for Hex27!
01989                               ARRAY_LENGTH(hex27_inverse_node_map),
01990                               hex27_face_map,
01991                               ARRAY_LENGTH(hex27_face_map),
01992                               hex27_inverse_face_map,
01993                               ARRAY_LENGTH(hex27_inverse_face_map),
01994                               HEX27,
01995                               "HEX27");
01996         return conv;
01997       }
01998 
01999     case TET4:
02000       {
02001         const Conversion conv(tet4_node_map,
02002                               ARRAY_LENGTH(tet4_node_map),
02003                               tet4_node_map, // inverse node map same as forward node map
02004                               ARRAY_LENGTH(tet4_node_map),
02005                               tet_face_map,
02006                               ARRAY_LENGTH(tet_face_map),
02007                               tet_inverse_face_map,
02008                               ARRAY_LENGTH(tet_inverse_face_map),
02009                               TET4,
02010                               "TETRA4");
02011         return conv;
02012       }
02013 
02014     case TET10:
02015       {
02016         const Conversion conv(tet10_node_map,
02017                               ARRAY_LENGTH(tet10_node_map),
02018                               tet10_node_map, // inverse node map same as forward node map
02019                               ARRAY_LENGTH(tet10_node_map),
02020                               tet_face_map,
02021                               ARRAY_LENGTH(tet_face_map),
02022                               tet_inverse_face_map,
02023                               ARRAY_LENGTH(tet_inverse_face_map),
02024                               TET10,
02025                               "TETRA10");
02026         return conv;
02027       }
02028 
02029     case PRISM6:
02030       {
02031         const Conversion conv(prism6_node_map,
02032                               ARRAY_LENGTH(prism6_node_map),
02033                               prism6_node_map, // inverse node map same as forward node map
02034                               ARRAY_LENGTH(prism6_node_map),
02035                               prism_face_map,
02036                               ARRAY_LENGTH(prism_face_map),
02037                               prism_inverse_face_map,
02038                               ARRAY_LENGTH(prism_inverse_face_map),
02039                               PRISM6,
02040                               "WEDGE");
02041         return conv;
02042       }
02043 
02044     case PRISM15:
02045       {
02046         const Conversion conv(prism15_node_map,
02047                               ARRAY_LENGTH(prism15_node_map),
02048                               prism15_node_map, // inverse node map same as forward node map
02049                               ARRAY_LENGTH(prism15_node_map),
02050                               prism_face_map,
02051                               ARRAY_LENGTH(prism_face_map),
02052                               prism_inverse_face_map,
02053                               ARRAY_LENGTH(prism_inverse_face_map),
02054                               PRISM15,
02055                               "WEDGE15");
02056         return conv;
02057       }
02058 
02059     case PRISM18:
02060       {
02061         const Conversion conv(prism18_node_map,
02062                               ARRAY_LENGTH(prism18_node_map),
02063                               prism18_node_map, // inverse node map same as forward node map
02064                               ARRAY_LENGTH(prism18_node_map),
02065                               prism_face_map,
02066                               ARRAY_LENGTH(prism_face_map),
02067                               prism_inverse_face_map,
02068                               ARRAY_LENGTH(prism_inverse_face_map),
02069                               PRISM18,
02070                               "WEDGE18");
02071         return conv;
02072       }
02073 
02074     case PYRAMID5:
02075       {
02076         const Conversion conv(pyramid5_node_map,
02077                               ARRAY_LENGTH(pyramid5_node_map),
02078                               pyramid5_node_map, // inverse node map same as forward node map
02079                               ARRAY_LENGTH(pyramid5_node_map),
02080                               pyramid_face_map,
02081                               ARRAY_LENGTH(pyramid_face_map),
02082                               pyramid_inverse_face_map,
02083                               ARRAY_LENGTH(pyramid_inverse_face_map),
02084                               PYRAMID5,
02085                               "PYRAMID5");
02086         return conv;
02087       }
02088 
02089     case PYRAMID13:
02090       {
02091         const Conversion conv(pyramid13_node_map,
02092                               ARRAY_LENGTH(pyramid13_node_map),
02093                               pyramid13_node_map, // inverse node map same as forward node map
02094                               ARRAY_LENGTH(pyramid13_node_map),
02095                               pyramid_face_map,
02096                               ARRAY_LENGTH(pyramid_face_map),
02097                               pyramid_inverse_face_map,
02098                               ARRAY_LENGTH(pyramid_inverse_face_map),
02099                               PYRAMID13,
02100                               "PYRAMID13");
02101         return conv;
02102       }
02103 
02104     case PYRAMID14:
02105       {
02106         const Conversion conv(pyramid14_node_map,
02107                               ARRAY_LENGTH(pyramid14_node_map),
02108                               pyramid14_node_map, // inverse node map same as forward node map
02109                               ARRAY_LENGTH(pyramid14_node_map),
02110                               pyramid_face_map,
02111                               ARRAY_LENGTH(pyramid_face_map),
02112                               pyramid_inverse_face_map,
02113                               ARRAY_LENGTH(pyramid_inverse_face_map),
02114                               PYRAMID14,
02115                               "PYRAMID14");
02116         return conv;
02117       }
02118 
02119     default:
02120       libmesh_error_msg("Unsupported element type: " << type);
02121     }
02122 
02123   libmesh_error_msg("We'll never get here!");
02124   const Conversion conv(tri3_node_map,
02125                         ARRAY_LENGTH(tri3_node_map),
02126                         tri3_node_map, // inverse node map same as forward node map
02127                         ARRAY_LENGTH(tri3_node_map),
02128                         tri_edge_map,
02129                         ARRAY_LENGTH(tri_edge_map),
02130                         tri_inverse_edge_map,
02131                         ARRAY_LENGTH(tri_inverse_edge_map),
02132                         TRI3,
02133                         "TRI3");
02134   return conv;
02135 }
02136 
02137 
02138 
02139 ExodusII_IO_Helper::NamesData::NamesData(size_t n_strings, size_t string_length) :
02140   data_table(n_strings),
02141   data_table_pointers(n_strings),
02142   counter(0),
02143   table_size(n_strings)
02144 {
02145   for (size_t i=0; i<n_strings; ++i)
02146     {
02147       data_table[i].resize(string_length + 1);
02148 
02149       // NULL-terminate these strings, just to be safe.
02150       data_table[i][0] = '\0';
02151 
02152       // Set pointer into the data_table
02153       data_table_pointers[i] = &(data_table[i][0]);
02154     }
02155 }
02156 
02157 
02158 
02159 void ExodusII_IO_Helper::NamesData::push_back_entry(const std::string & name)
02160 {
02161   libmesh_assert_less (counter, table_size);
02162 
02163   // 1.) Copy the C++ string into the vector<char>...
02164   size_t num_copied = name.copy(&data_table[counter][0], data_table[counter].size()-1);
02165 
02166   // 2.) ...And null-terminate it.
02167   data_table[counter][num_copied] = '\0';
02168 
02169   // Go to next row
02170   ++counter;
02171 }
02172 
02173 
02174 
02175 char** ExodusII_IO_Helper::NamesData::get_char_star_star()
02176 {
02177   return &data_table_pointers[0];
02178 }
02179 
02180 
02181 
02182 char* ExodusII_IO_Helper::NamesData::get_char_star(int i)
02183 {
02184   if (static_cast<unsigned>(i) >= table_size)
02185     libmesh_error_msg("Requested char* " << i << " but only have " << table_size << "!");
02186 
02187   else
02188     return &(data_table[i][0]);
02189 }
02190 
02191 
02192 } // namespace libMesh
02193 
02194 
02195 
02196 #endif // #ifdef LIBMESH_HAVE_EXODUS_API