$extrastylesheet
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