void ExodusII_IO::read (const std::string& fname) { // This is a serial-only process for now; // the Mesh should be read on processor 0 and // broadcast later // libmesh_assert_equal_to (libMesh::processor_id(), 0); #ifndef LIBMESH_HAVE_EXODUS_API libMesh::err << "ERROR, ExodusII API is not defined.\n" << "Input file " << fname << " cannot be read" << std::endl; libmesh_error(); #else // Get a reference to the mesh we are reading MeshBase& mesh = MeshInput<MeshBase>::mesh(); // Clear any existing mesh data mesh.clear(); // Keep track of what kinds of elements this file contains elems_of_dimension.clear(); elems_of_dimension.resize(4, false); #ifdef DEBUG this->verbose(true); #endif ExodusII_IO_Helper::ElementMaps em; // Instantiate the ElementMaps interface exio_helper->open(fname.c_str()); // Open the exodus file, if possible exio_helper->read_header(); // Get header information from exodus file exio_helper->print_header(); // Print header information //assertion fails due to inconsistent mesh dimension // libmesh_assert_equal_to (static_cast<unsigned int>(exio_helper->get_num_dim()), mesh.mesh_dimension()); // Be sure number of dimensions // is equal to the number of // dimensions in the mesh supplied. exio_helper->read_nodes(); // Read nodes from the exodus file mesh.reserve_nodes(exio_helper->get_num_nodes()); // Reserve space for the nodes. // Loop over the nodes, create Nodes with local processor_id 0. for (int i=0; i<exio_helper->get_num_nodes(); i++) mesh.add_point (Point(exio_helper->get_x(i), exio_helper->get_y(i), exio_helper->get_z(i)), i); libmesh_assert_equal_to (static_cast<unsigned int>(exio_helper->get_num_nodes()), mesh.n_nodes()); exio_helper->read_block_info(); // Get information about all the blocks mesh.reserve_elem(exio_helper->get_num_elem()); // Reserve space for the elements // Read in the element connectivity for each block. int nelem_last_block = 0; std::map<int, unsigned int> exodus_id_to_mesh_id; // Loop over all the blocks for (int i=0; i<exio_helper->get_num_elem_blk(); i++) { // Read the information for block i exio_helper->read_elem_in_block (i); int subdomain_id = exio_helper->get_block_id(i); // populate the map of names mesh.subdomain_name(static_cast<subdomain_id_type>(subdomain_id)) = exio_helper->get_block_name(i); // Set any relevant node/edge maps for this element const std::string type_str (exio_helper->get_elem_type()); const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(type_str); //if (_verbose) //libMesh::out << "Reading a block of " << type_str << " elements." << std::endl; // Loop over all the faces in this block int jmax = nelem_last_block+exio_helper->get_num_elem_this_blk(); for (int j=nelem_last_block; j<jmax; j++) { Elem* elem = Elem::build (conv.get_canonical_type()).release(); libmesh_assert (elem); elem->subdomain_id() = static_cast<subdomain_id_type>(subdomain_id) ; //elem->set_id(j);// Don't try to second guess the Element ID setting scheme! elems_of_dimension[elem->dim()] = true; elem = mesh.add_elem (elem); // Catch the Elem pointer that the Mesh throws back exodus_id_to_mesh_id[j+1] = elem->id(); // Set all the nodes for this element for (int k=0; k<exio_helper->get_num_nodes_per_elem(); k++) { int gi = (j-nelem_last_block)*exio_helper->get_num_nodes_per_elem() + conv.get_node_map(k); // global index int node_number = exio_helper->get_connect(gi); // Global node number (1-based) elem->set_node(k) = mesh.node_ptr((node_number-1)); // Set node number // Subtract 1 since // exodus is internally 1-based } } // running sum of # of elements per block, // (should equal total number of elements in the end) nelem_last_block += exio_helper->get_num_elem_this_blk(); } libmesh_assert_equal_to (static_cast<unsigned int>(nelem_last_block), mesh.n_elem()); // Set the mesh dimension to the largest encountered for an element for (unsigned int i=0; i!=4; ++i) if (elems_of_dimension[i]) mesh.set_mesh_dimension(i); // Read in sideset information -- this is useful for applying boundary conditions { exio_helper->read_sideset_info(); // Get basic information about ALL sidesets int offset=0; for (int i=0; i<exio_helper->get_num_side_sets(); i++) { offset += (i > 0 ? exio_helper->get_num_sides_per_set(i-1) : 0); // Compute new offset exio_helper->read_sideset (i, offset); mesh.boundary_info->sideset_name(exio_helper->get_side_set_id(i)) = exio_helper->get_side_set_name(i); } const std::vector<int>& elem_list = exio_helper->get_elem_list(); const std::vector<int>& side_list = exio_helper->get_side_list(); const std::vector<int>& id_list = exio_helper->get_id_list(); for (unsigned int e=0; e<elem_list.size(); e++) { // Set any relevant node/edge maps for this element Elem * elem = mesh.elem(exodus_id_to_mesh_id[elem_list[e]]); const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(elem->type()); mesh.boundary_info->add_side (exodus_id_to_mesh_id[elem_list[e]], conv.get_side_map(side_list[e]-1), id_list[e]); } } // Read nodeset info { exio_helper->read_nodeset_info(); for (int nodeset=0; nodeset<exio_helper->get_num_node_sets(); nodeset++) { int nodeset_id = exio_helper->get_nodeset_id(nodeset); mesh.boundary_info->nodeset_name(nodeset_id) = exio_helper->get_node_set_name(nodeset); exio_helper->read_nodeset(nodeset); const std::vector<int>& node_list = exio_helper->get_node_list(); for(unsigned int node=0; node<node_list.size(); node++) mesh.boundary_info->add_node(node_list[node]-1, nodeset_id); } } #if LIBMESH_DIM < 3 if (mesh.mesh_dimension() > LIBMESH_DIM) { libMesh::err << "Cannot open dimension " << mesh.mesh_dimension() << " mesh file when configured without " << mesh.mesh_dimension() << "D support." << std::endl; libmesh_error(); } #endif #endif }
void ExodusII_IO::read (const std::string & fname) { // Get a reference to the mesh we are reading MeshBase & mesh = MeshInput<MeshBase>::mesh(); // Clear any existing mesh data mesh.clear(); // Keep track of what kinds of elements this file contains elems_of_dimension.clear(); elems_of_dimension.resize(4, false); #ifdef DEBUG this->verbose(true); #endif // Instantiate the ElementMaps interface ExodusII_IO_Helper::ElementMaps em(*exio_helper); // Open the exodus file in EX_READ mode exio_helper->open(fname.c_str(), /*read_only=*/true); // Get header information from exodus file exio_helper->read_header(); // Read the QA records exio_helper->read_qa_records(); // Print header information exio_helper->print_header(); // Read nodes from the exodus file exio_helper->read_nodes(); // Reserve space for the nodes. mesh.reserve_nodes(exio_helper->num_nodes); // Read the node number map from the Exodus file. This is // required if we want to preserve the numbering of nodes as it // exists in the Exodus file. If the Exodus file does not contain // a node_num_map, the identity map is returned by this call. exio_helper->read_node_num_map(); // Loop over the nodes, create Nodes with local processor_id 0. for (int i=0; i<exio_helper->num_nodes; i++) { // Use the node_num_map to get the correct ID for Exodus int exodus_id = exio_helper->node_num_map[i]; // Catch the node that was added to the mesh Node * added_node = mesh.add_point (Point(exio_helper->x[i], exio_helper->y[i], exio_helper->z[i]), exodus_id-1); // If the Mesh assigned an ID different from what is in the // Exodus file, we should probably error. if (added_node->id() != static_cast<unsigned>(exodus_id-1)) libmesh_error_msg("Error! Mesh assigned node ID " \ << added_node->id() \ << " which is different from the (zero-based) Exodus ID " \ << exodus_id-1 \ << "!"); } // This assert is no longer valid if the nodes are not numbered // sequentially starting from 1 in the Exodus file. // libmesh_assert_equal_to (static_cast<unsigned int>(exio_helper->num_nodes), mesh.n_nodes()); // Get information about all the blocks exio_helper->read_block_info(); // Reserve space for the elements mesh.reserve_elem(exio_helper->num_elem); // Read the element number map from the Exodus file. This is // required if we want to preserve the numbering of elements as it // exists in the Exodus file. If the Exodus file does not contain // an elem_num_map, the identity map is returned by this call. exio_helper->read_elem_num_map(); // Read in the element connectivity for each block. int nelem_last_block = 0; // Loop over all the blocks for (int i=0; i<exio_helper->num_elem_blk; i++) { // Read the information for block i exio_helper->read_elem_in_block (i); int subdomain_id = exio_helper->get_block_id(i); // populate the map of names std::string subdomain_name = exio_helper->get_block_name(i); if (!subdomain_name.empty()) mesh.subdomain_name(static_cast<subdomain_id_type>(subdomain_id)) = subdomain_name; // Set any relevant node/edge maps for this element const std::string type_str (exio_helper->get_elem_type()); const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(type_str); // Loop over all the faces in this block int jmax = nelem_last_block+exio_helper->num_elem_this_blk; for (int j=nelem_last_block; j<jmax; j++) { Elem * elem = Elem::build (conv.get_canonical_type()).release(); libmesh_assert (elem); elem->subdomain_id() = static_cast<subdomain_id_type>(subdomain_id) ; // Use the elem_num_map to obtain the ID of this element in the Exodus file int exodus_id = exio_helper->elem_num_map[j]; // Assign this element the same ID it had in the Exodus // file, but make it zero-based by subtracting 1. Note: // some day we could use 1-based numbering in libmesh and // thus match the Exodus numbering exactly, but at the // moment libmesh is zero-based. elem->set_id(exodus_id-1); // Record that we have seen an element of dimension elem->dim() elems_of_dimension[elem->dim()] = true; // Catch the Elem pointer that the Mesh throws back elem = mesh.add_elem (elem); // If the Mesh assigned an ID different from what is in the // Exodus file, we should probably error. if (elem->id() != static_cast<unsigned>(exodus_id-1)) libmesh_error_msg("Error! Mesh assigned ID " \ << elem->id() \ << " which is different from the (zero-based) Exodus ID " \ << exodus_id-1 \ << "!"); // Set all the nodes for this element for (int k=0; k<exio_helper->num_nodes_per_elem; k++) { // global index int gi = (j-nelem_last_block)*exio_helper->num_nodes_per_elem + conv.get_node_map(k); // The entries in 'connect' are actually (1-based) // indices into the node_num_map, so to get the right // node ID we: // 1.) Subtract 1 from connect[gi] // 2.) Pass it through node_num_map to get the corresponding Exodus ID // 3.) Subtract 1 from that, since libmesh node numbering is "zero"-based, // even when the Exodus node numbering doesn't start with 1. int libmesh_node_id = exio_helper->node_num_map[exio_helper->connect[gi] - 1] - 1; // Set the node pointer in the Elem elem->set_node(k) = mesh.node_ptr(libmesh_node_id); } } // running sum of # of elements per block, // (should equal total number of elements in the end) nelem_last_block += exio_helper->num_elem_this_blk; } // This assert isn't valid if the Exodus file's numbering doesn't // start with 1! For example, if Exodus's elem_num_map is 21, 22, // 23, 24, 25, 26, 27, 28, 29, 30, ... 84, then by the time you are // done with the loop above, mesh.n_elem() will report 84 and // nelem_last_block will be 64. // libmesh_assert_equal_to (static_cast<unsigned>(nelem_last_block), mesh.n_elem()); // Set the mesh dimension to the largest encountered for an element for (unsigned char i=0; i!=4; ++i) if (elems_of_dimension[i]) mesh.set_mesh_dimension(i); // Read in sideset information -- this is useful for applying boundary conditions { // Get basic information about all sidesets exio_helper->read_sideset_info(); int offset=0; for (int i=0; i<exio_helper->num_side_sets; i++) { // Compute new offset offset += (i > 0 ? exio_helper->num_sides_per_set[i-1] : 0); exio_helper->read_sideset (i, offset); std::string sideset_name = exio_helper->get_side_set_name(i); if (!sideset_name.empty()) mesh.get_boundary_info().sideset_name (cast_int<boundary_id_type>(exio_helper->get_side_set_id(i))) = sideset_name; } for (unsigned int e=0; e<exio_helper->elem_list.size(); e++) { // The numbers in the Exodus file sidesets should be thought // of as (1-based) indices into the elem_num_map array. So, // to get the right element ID we have to: // 1.) Subtract 1 from elem_list[e] (to get a zero-based index) // 2.) Pass it through elem_num_map (to get the corresponding Exodus ID) // 3.) Subtract 1 from that, since libmesh is "zero"-based, // even when the Exodus numbering doesn't start with 1. dof_id_type libmesh_elem_id = cast_int<dof_id_type>(exio_helper->elem_num_map[exio_helper->elem_list[e] - 1] - 1); // Set any relevant node/edge maps for this element Elem * elem = mesh.elem(libmesh_elem_id); const ExodusII_IO_Helper::Conversion conv = em.assign_conversion(elem->type()); // Map the zero-based Exodus side numbering to the libmesh side numbering int mapped_side = conv.get_side_map(exio_helper->side_list[e]-1); // Check for errors if (mapped_side == ExodusII_IO_Helper::Conversion::invalid_id) libmesh_error_msg("Invalid 1-based side id: " \ << exio_helper->side_list[e] \ << " detected for " \ << Utility::enum_to_string(elem->type())); // Add this (elem,side,id) triplet to the BoundaryInfo object. mesh.get_boundary_info().add_side (libmesh_elem_id, cast_int<unsigned short>(mapped_side), cast_int<boundary_id_type>(exio_helper->id_list[e])); } } // Read nodeset info { exio_helper->read_nodeset_info(); for (int nodeset=0; nodeset<exio_helper->num_node_sets; nodeset++) { boundary_id_type nodeset_id = cast_int<boundary_id_type>(exio_helper->nodeset_ids[nodeset]); std::string nodeset_name = exio_helper->get_node_set_name(nodeset); if (!nodeset_name.empty()) mesh.get_boundary_info().nodeset_name(nodeset_id) = nodeset_name; exio_helper->read_nodeset(nodeset); for (unsigned int node=0; node<exio_helper->node_list.size(); node++) { // As before, the entries in 'node_list' are 1-based // indcies into the node_num_map array, so we have to map // them. See comment above. int libmesh_node_id = exio_helper->node_num_map[exio_helper->node_list[node] - 1] - 1; mesh.get_boundary_info().add_node(cast_int<dof_id_type>(libmesh_node_id), nodeset_id); } } } #if LIBMESH_DIM < 3 if (mesh.mesh_dimension() > LIBMESH_DIM) libmesh_error_msg("Cannot open dimension " \ << mesh.mesh_dimension() \ << " mesh file when configured without " \ << mesh.mesh_dimension() \ << "D support."); #endif }