void MeshTools::Subdivision::tag_boundary_ghosts(MeshBase & mesh) { MeshBase::element_iterator el = mesh.elements_begin(); const MeshBase::element_iterator end_el = mesh.elements_end(); for (; el != end_el; ++el) { Elem * elem = *el; libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION); Tri3Subdivision * sd_elem = static_cast<Tri3Subdivision *>(elem); for (unsigned int i = 0; i < elem->n_sides(); ++i) { if (elem->neighbor(i) == libmesh_nullptr) { sd_elem->set_ghost(true); // set all other neighbors to ghosts as well if (elem->neighbor(next[i])) { Tri3Subdivision * nb = static_cast<Tri3Subdivision *>(elem->neighbor(next[i])); nb->set_ghost(true); } if (elem->neighbor(prev[i])) { Tri3Subdivision * nb = static_cast<Tri3Subdivision *>(elem->neighbor(prev[i])); nb->set_ghost(true); } } } } }
void QBase::init (const Elem &elem, const std::vector<Real> & /* vertex_distance_func */, unsigned int p_level) { // dispatch generic implementation this->init(elem.type(), p_level); }
void QComposite<QSubCell>::init (const Elem &elem, const std::vector<Real> &vertex_distance_func, unsigned int p_level) { libmesh_assert_equal_to (vertex_distance_func.size(), elem.n_vertices()); libmesh_assert_equal_to (_dim, elem.dim()); // if we are not cut, revert to simple base class init() method. if (!_elem_cutter.is_cut (elem, vertex_distance_func)) { _q_subcell.init (elem.type(), p_level); _points = _q_subcell.get_points(); _weights = _q_subcell.get_weights(); //this->print_info(); return; } // Get a pointer to the element's reference element. We want to // perform cutting on the reference element such that the quadrature // point locations of the subelements live in the reference // coordinate system, thereby eliminating the need for inverse // mapping. const Elem *reference_elem = elem.reference_elem(); libmesh_assert (reference_elem != NULL); _elem_cutter(*reference_elem, vertex_distance_func); //_elem_cutter(elem, vertex_distance_func); // clear our state & accumulate points from subelements _points.clear(); _weights.clear(); // inside subelem { const std::vector<Elem const*> &inside_elem (_elem_cutter.inside_elements()); std::cout << inside_elem.size() << " elements inside\n"; this->add_subelem_values(inside_elem); } // outside subelem { const std::vector<Elem const*> &outside_elem (_elem_cutter.outside_elements()); std::cout << outside_elem.size() << " elements outside\n"; this->add_subelem_values(outside_elem); } this->print_info(); }
void TetGenMeshInterface::delete_2D_hull_elements() { MeshBase::element_iterator it = this->_mesh.elements_begin(); const MeshBase::element_iterator end = this->_mesh.elements_end(); for (; it != end ; ++it) { Elem* elem = *it; // Check for proper element type. Yes, we legally delete elements while // iterating over them because no entries from the underlying container // are actually erased. if (elem->type() == TRI3) _mesh.delete_elem(elem); } }
unsigned TetGenMeshInterface::check_hull_integrity() { // Check for easy return: if the Mesh is empty (i.e. if // somebody called triangulate_conformingDelaunayMesh on // a Mesh with no elements, then hull integrity check must // fail... if (_mesh.n_elem() == 0) return 3; MeshBase::element_iterator it = this->_mesh.elements_begin(); const MeshBase::element_iterator end = this->_mesh.elements_end(); for (; it != end ; ++it) { Elem* elem = *it; // Check for proper element type if (elem->type() != TRI3) { //libMesh::err << "ERROR: Some of the elements in the original mesh were not TRI3!" << std::endl; //libmesh_error(); return 1; } for (unsigned int i=0; i<elem->n_neighbors(); ++i) { if (elem->neighbor(i) == NULL) { // libMesh::err << "ERROR: Non-convex hull, cannot be tetrahedralized." << std::endl; // libmesh_error(); return 2; } } } // If we made it here, return success! return 0; }
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 VTKIO::cells_to_vtk() { const MeshBase& mesh = MeshOutput<MeshBase>::mesh(); vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New(); vtkSmartPointer<vtkIdList> pts = vtkSmartPointer<vtkIdList>::New(); std::vector<int> types(mesh.n_active_local_elem()); unsigned active_element_counter = 0; vtkSmartPointer<vtkIntArray> elem_id = vtkSmartPointer<vtkIntArray>::New(); elem_id->SetName("libmesh_elem_id"); elem_id->SetNumberOfComponents(1); vtkSmartPointer<vtkIntArray> subdomain_id = vtkSmartPointer<vtkIntArray>::New(); subdomain_id->SetName("subdomain_id"); subdomain_id->SetNumberOfComponents(1); MeshBase::const_element_iterator it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator end = mesh.active_local_elements_end(); for (; it != end; ++it, ++active_element_counter) { Elem *elem = *it; pts->SetNumberOfIds(elem->n_nodes()); // get the connectivity for this element std::vector<dof_id_type> conn; elem->connectivity(0, VTK, conn); for (unsigned int i=0; i<conn.size(); ++i) { // If the node ID is not found in the _local_node_map, we'll // add it to the _vtk_grid. NOTE[JWP]: none of the examples // I have actually enters this section of code... if (_local_node_map.find(conn[i]) == _local_node_map.end()) { dof_id_type global_node_id = elem->node(i); const Node* the_node = mesh.node_ptr(global_node_id); // Error checking... if (the_node == NULL) { libMesh::err << "Error getting pointer to node " << global_node_id << "!" << std::endl; libmesh_error(); } // InsertNextPoint accepts either a double or float array of length 3. Real pt[3] = {0., 0., 0.}; for (unsigned int d=0; d<LIBMESH_DIM; ++d) pt[d] = (*the_node)(d); // Insert the point into the _vtk_grid vtkIdType local = _vtk_grid->GetPoints()->InsertNextPoint(pt); // Update the _local_node_map with the ID returned by VTK _local_node_map[global_node_id] = local; } // Otherwise, the node ID was found in the _local_node_map, so // insert it into the vtkIdList. pts->InsertId(i, _local_node_map[conn[i]]); } vtkIdType vtkcellid = cells->InsertNextCell(pts); types[active_element_counter] = this->get_elem_type(elem->type()); elem_id->InsertTuple1(vtkcellid, elem->id()); subdomain_id->InsertTuple1(vtkcellid, elem->subdomain_id()); } // end loop over active elements _vtk_grid->SetCells(&types[0], cells); _vtk_grid->GetCellData()->AddArray(elem_id); _vtk_grid->GetCellData()->AddArray(subdomain_id); }
void MeshTools::Subdivision::add_boundary_ghosts(MeshBase & mesh) { static const Real tol = 1e-5; // add the mirrored ghost elements (without using iterators, because the mesh is modified in the course) std::vector<Tri3Subdivision *> ghost_elems; std::vector<Node *> ghost_nodes; const unsigned int n_elem = mesh.n_elem(); for (unsigned int eid = 0; eid < n_elem; ++eid) { Elem * elem = mesh.elem(eid); libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION); // If the triangle happens to be in a corner (two boundary // edges), we perform a counter-clockwise loop by mirroring the // previous triangle until we come back to the original // triangle. This prevents degenerated triangles in the mesh // corners and guarantees that the node in the middle of the // loop is of valence=6. for (unsigned int i = 0; i < elem->n_sides(); ++i) { libmesh_assert_not_equal_to(elem->neighbor(i), elem); if (elem->neighbor(i) == libmesh_nullptr && elem->neighbor(next[i]) == libmesh_nullptr) { Elem * nelem = elem; unsigned int k = i; for (unsigned int l=0;l<4;l++) { // this is the vertex to be mirrored Point point = nelem->point(k) + nelem->point(next[k]) - nelem->point(prev[k]); // Check if the proposed vertex doesn't coincide // with one of the existing vertices. This is // necessary because for some triangulations, it can // happen that two mirrored ghost vertices coincide, // which would then lead to a zero size ghost // element below. Node * node = libmesh_nullptr; for (unsigned int j = 0; j < ghost_nodes.size(); ++j) { if ((*ghost_nodes[j] - point).size() < tol * (elem->point(k) - point).size()) { node = ghost_nodes[j]; break; } } // add the new vertex only if no other is nearby if (node == libmesh_nullptr) { node = mesh.add_point(point); ghost_nodes.push_back(node); } Tri3Subdivision * newelem = new Tri3Subdivision(); // add the first new ghost element to the list just as in the non-corner case if (l == 0) ghost_elems.push_back(newelem); newelem->set_node(0) = nelem->get_node(next[k]); newelem->set_node(1) = nelem->get_node(k); newelem->set_node(2) = node; newelem->set_neighbor(0, nelem); newelem->set_ghost(true); if (l>0) newelem->set_neighbor(2, libmesh_nullptr); nelem->set_neighbor(k, newelem); mesh.add_elem(newelem); mesh.get_boundary_info().add_node(nelem->get_node(k), 1); mesh.get_boundary_info().add_node(nelem->get_node(next[k]), 1); mesh.get_boundary_info().add_node(nelem->get_node(prev[k]), 1); mesh.get_boundary_info().add_node(node, 1); nelem = newelem; k = 2 ; } Tri3Subdivision * newelem = new Tri3Subdivision(); newelem->set_node(0) = elem->get_node(next[i]); newelem->set_node(1) = nelem->get_node(2); newelem->set_node(2) = elem->get_node(prev[i]); newelem->set_neighbor(0, nelem); nelem->set_neighbor(2, newelem); newelem->set_ghost(true); newelem->set_neighbor(2, elem); elem->set_neighbor(next[i],newelem); mesh.add_elem(newelem); break; } } for (unsigned int i = 0; i < elem->n_sides(); ++i) { libmesh_assert_not_equal_to(elem->neighbor(i), elem); if (elem->neighbor(i) == libmesh_nullptr) { // this is the vertex to be mirrored Point point = elem->point(i) + elem->point(next[i]) - elem->point(prev[i]); // Check if the proposed vertex doesn't coincide with // one of the existing vertices. This is necessary // because for some triangulations, it can happen that // two mirrored ghost vertices coincide, which would // then lead to a zero size ghost element below. Node * node = libmesh_nullptr; for (unsigned int j = 0; j < ghost_nodes.size(); ++j) { if ((*ghost_nodes[j] - point).size() < tol * (elem->point(i) - point).size()) { node = ghost_nodes[j]; break; } } // add the new vertex only if no other is nearby if (node == libmesh_nullptr) { node = mesh.add_point(point); ghost_nodes.push_back(node); } Tri3Subdivision * newelem = new Tri3Subdivision(); ghost_elems.push_back(newelem); newelem->set_node(0) = elem->get_node(next[i]); newelem->set_node(1) = elem->get_node(i); newelem->set_node(2) = node; newelem->set_neighbor(0, elem); newelem->set_ghost(true); elem->set_neighbor(i, newelem); mesh.add_elem(newelem); mesh.get_boundary_info().add_node(elem->get_node(i), 1); mesh.get_boundary_info().add_node(elem->get_node(next[i]), 1); mesh.get_boundary_info().add_node(elem->get_node(prev[i]), 1); mesh.get_boundary_info().add_node(node, 1); } } } // add the missing ghost elements (connecting new ghost nodes) std::vector<Tri3Subdivision *> missing_ghost_elems; std::vector<Tri3Subdivision *>::iterator ghost_el = ghost_elems.begin(); const std::vector<Tri3Subdivision *>::iterator end_ghost_el = ghost_elems.end(); for (; ghost_el != end_ghost_el; ++ghost_el) { Tri3Subdivision * elem = *ghost_el; libmesh_assert(elem->is_ghost()); for (unsigned int i = 0; i < elem->n_sides(); ++i) { if (elem->neighbor(i) == libmesh_nullptr && elem->neighbor(prev[i]) != libmesh_nullptr) { // go around counter-clockwise Tri3Subdivision * nb1 = static_cast<Tri3Subdivision *>(elem->neighbor(prev[i])); Tri3Subdivision * nb2 = nb1; unsigned int j = i; unsigned int n_nb = 0; while (nb1 != libmesh_nullptr && nb1->id() != elem->id()) { j = nb1->local_node_number(elem->node(i)); nb2 = nb1; nb1 = static_cast<Tri3Subdivision *>(nb1->neighbor(prev[j])); libmesh_assert(nb1 == libmesh_nullptr || nb1->id() != nb2->id()); n_nb++; } libmesh_assert_not_equal_to(nb2->id(), elem->id()); // Above, we merged coinciding ghost vertices. Therefore, we need // to exclude the case where there is no ghost element to add between // these two (identical) ghost nodes. if (elem->get_node(next[i])->id() == nb2->get_node(prev[j])->id()) break; // If the number of already present neighbors is less than 4, we add another extra element // so that the node in the middle of the loop ends up being of valence=6. // This case usually happens when the middle node corresponds to a corner of the original mesh, // and the extra element below prevents degenerated triangles in the mesh corners. if (n_nb < 4) { // this is the vertex to be mirrored Point point = nb2->point(j) + nb2->point(prev[j]) - nb2->point(next[j]); // Check if the proposed vertex doesn't coincide with one of the existing vertices. // This is necessary because for some triangulations, it can happen that two mirrored // ghost vertices coincide, which would then lead to a zero size ghost element below. Node * node = libmesh_nullptr; for (unsigned int k = 0; k < ghost_nodes.size(); ++k) { if ((*ghost_nodes[k] - point).size() < tol * (nb2->point(j) - point).size()) { node = ghost_nodes[k]; break; } } // add the new vertex only if no other is nearby if (node == libmesh_nullptr) { node = mesh.add_point(point); ghost_nodes.push_back(node); } Tri3Subdivision * newelem = new Tri3Subdivision(); newelem->set_node(0) = nb2->get_node(j); newelem->set_node(1) = nb2->get_node(prev[j]); newelem->set_node(2) = node; newelem->set_neighbor(0, nb2); newelem->set_neighbor(1, libmesh_nullptr); newelem->set_ghost(true); nb2->set_neighbor(prev[j], newelem); mesh.add_elem(newelem); mesh.get_boundary_info().add_node(nb2->get_node(j), 1); mesh.get_boundary_info().add_node(nb2->get_node(prev[j]), 1); mesh.get_boundary_info().add_node(node, 1); nb2 = newelem; j = nb2->local_node_number(elem->node(i)); } Tri3Subdivision * newelem = new Tri3Subdivision(); newelem->set_node(0) = elem->get_node(next[i]); newelem->set_node(1) = elem->get_node(i); newelem->set_node(2) = nb2->get_node(prev[j]); newelem->set_neighbor(0, elem); newelem->set_neighbor(1, nb2); newelem->set_neighbor(2, libmesh_nullptr); newelem->set_ghost(true); elem->set_neighbor(i, newelem); nb2->set_neighbor(prev[j], newelem); missing_ghost_elems.push_back(newelem); break; } } // end side loop } // end ghost element loop // add the missing ghost elements to the mesh std::vector<Tri3Subdivision *>::iterator missing_el = missing_ghost_elems.begin(); const std::vector<Tri3Subdivision *>::iterator end_missing_el = missing_ghost_elems.end(); for (; missing_el != end_missing_el; ++missing_el) mesh.add_elem(*missing_el); }
void unpack(std::vector<largest_id_type>::const_iterator in, Elem** out, MeshBase* mesh) { #ifndef NDEBUG const std::vector<largest_id_type>::const_iterator original_in = in; const largest_id_type incoming_header = *in++; libmesh_assert_equal_to (incoming_header, elem_magic_header); #endif // int 0: level const unsigned int level = static_cast<unsigned int>(*in++); #ifdef LIBMESH_ENABLE_AMR // int 1: p level const unsigned int p_level = static_cast<unsigned int>(*in++); // int 2: refinement flag const int rflag = *in++; libmesh_assert_greater_equal (rflag, 0); libmesh_assert_less (rflag, Elem::INVALID_REFINEMENTSTATE); const Elem::RefinementState refinement_flag = static_cast<Elem::RefinementState>(rflag); // int 3: p refinement flag const int pflag = *in++; libmesh_assert_greater_equal (pflag, 0); libmesh_assert_less (pflag, Elem::INVALID_REFINEMENTSTATE); const Elem::RefinementState p_refinement_flag = static_cast<Elem::RefinementState>(pflag); #else in += 3; #endif // LIBMESH_ENABLE_AMR // int 4: element type const int typeint = *in++; libmesh_assert_greater_equal (typeint, 0); libmesh_assert_less (typeint, INVALID_ELEM); const ElemType type = static_cast<ElemType>(typeint); const unsigned int n_nodes = Elem::type_to_n_nodes_map[type]; // int 5: processor id const processor_id_type processor_id = static_cast<processor_id_type>(*in++); libmesh_assert (processor_id < mesh->n_processors() || processor_id == DofObject::invalid_processor_id); // int 6: subdomain id const subdomain_id_type subdomain_id = static_cast<subdomain_id_type>(*in++); // int 7: dof object id const dof_id_type id = static_cast<dof_id_type>(*in++); libmesh_assert_not_equal_to (id, DofObject::invalid_id); #ifdef LIBMESH_ENABLE_UNIQUE_ID // int 8: dof object unique id const unique_id_type unique_id = static_cast<unique_id_type>(*in++); #endif #ifdef LIBMESH_ENABLE_AMR // int 9: parent dof object id const dof_id_type parent_id = static_cast<dof_id_type>(*in++); libmesh_assert (level == 0 || parent_id != DofObject::invalid_id); libmesh_assert (level != 0 || parent_id == DofObject::invalid_id); // int 10: local child id const unsigned int which_child_am_i = static_cast<unsigned int>(*in++); #else in += 2; #endif // LIBMESH_ENABLE_AMR // Make sure we don't miscount above when adding the "magic" header // plus the real data header libmesh_assert_equal_to (in - original_in, header_size + 1); Elem *elem = mesh->query_elem(id); // if we already have this element, make sure its // properties match, and update any missing neighbor // links, but then go on if (elem) { libmesh_assert_equal_to (elem->level(), level); libmesh_assert_equal_to (elem->id(), id); //#ifdef LIBMESH_ENABLE_UNIQUE_ID // No check for unqiue id sanity //#endif libmesh_assert_equal_to (elem->processor_id(), processor_id); libmesh_assert_equal_to (elem->subdomain_id(), subdomain_id); libmesh_assert_equal_to (elem->type(), type); libmesh_assert_equal_to (elem->n_nodes(), n_nodes); #ifndef NDEBUG // All our nodes should be correct for (unsigned int i=0; i != n_nodes; ++i) libmesh_assert(elem->node(i) == static_cast<dof_id_type>(*in++)); #else in += n_nodes; #endif #ifdef LIBMESH_ENABLE_AMR libmesh_assert_equal_to (elem->p_level(), p_level); libmesh_assert_equal_to (elem->refinement_flag(), refinement_flag); libmesh_assert_equal_to (elem->p_refinement_flag(), p_refinement_flag); libmesh_assert (!level || elem->parent() != NULL); libmesh_assert (!level || elem->parent()->id() == parent_id); libmesh_assert (!level || elem->parent()->child(which_child_am_i) == elem); #endif // Our neighbor links should be "close to" correct - we may have // to update them, but we can check for some inconsistencies. for (unsigned int n=0; n != elem->n_neighbors(); ++n) { const dof_id_type neighbor_id = static_cast<dof_id_type>(*in++); // If the sending processor sees a domain boundary here, // we'd better agree. if (neighbor_id == DofObject::invalid_id) { libmesh_assert (!(elem->neighbor(n))); continue; } // If the sending processor has a remote_elem neighbor here, // then all we know is that we'd better *not* have a domain // boundary. if (neighbor_id == remote_elem->id()) { libmesh_assert(elem->neighbor(n)); continue; } Elem *neigh = mesh->query_elem(neighbor_id); // The sending processor sees a neighbor here, so if we // don't have that neighboring element, then we'd better // have a remote_elem signifying that fact. if (!neigh) { libmesh_assert_equal_to (elem->neighbor(n), remote_elem); continue; } // The sending processor has a neighbor here, and we have // that element, but that does *NOT* mean we're already // linking to it. Perhaps we initially received both elem // and neigh from processors on which their mutual link was // remote? libmesh_assert(elem->neighbor(n) == neigh || elem->neighbor(n) == remote_elem); // If the link was originally remote, we should update it, // and make sure the appropriate parts of its family link // back to us. if (elem->neighbor(n) == remote_elem) { elem->set_neighbor(n, neigh); elem->make_links_to_me_local(n); } } // FIXME: We should add some debug mode tests to ensure that the // encoded indexing and boundary conditions are consistent. } else { // We don't already have the element, so we need to create it. // Find the parent if necessary Elem *parent = NULL; #ifdef LIBMESH_ENABLE_AMR // Find a child element's parent if (level > 0) { // Note that we must be very careful to construct the send // connectivity so that parents are encountered before // children. If we get here and can't find the parent that // is a fatal error. parent = mesh->elem(parent_id); } // Or assert that the sending processor sees no parent else libmesh_assert_equal_to (parent_id, static_cast<dof_id_type>(-1)); #else // No non-level-0 elements without AMR libmesh_assert_equal_to (level, 0); #endif elem = Elem::build(type,parent).release(); libmesh_assert (elem); #ifdef LIBMESH_ENABLE_AMR if (level != 0) { // Since this is a newly created element, the parent must // have previously thought of this child as a remote element. libmesh_assert_equal_to (parent->child(which_child_am_i), remote_elem); parent->add_child(elem, which_child_am_i); } // Assign the refinement flags and levels elem->set_p_level(p_level); elem->set_refinement_flag(refinement_flag); elem->set_p_refinement_flag(p_refinement_flag); libmesh_assert_equal_to (elem->level(), level); // If this element definitely should have children, assign // remote_elem to all of them for now, for consistency. Later // unpacked elements may overwrite that. if (!elem->active()) for (unsigned int c=0; c != elem->n_children(); ++c) elem->add_child(const_cast<RemoteElem*>(remote_elem), c); #endif // LIBMESH_ENABLE_AMR // Assign the IDs elem->subdomain_id() = subdomain_id; elem->processor_id() = processor_id; elem->set_id() = id; #ifdef LIBMESH_ENABLE_UNIQUE_ID elem->set_unique_id() = unique_id; #endif // Assign the connectivity libmesh_assert_equal_to (elem->n_nodes(), n_nodes); for (unsigned int n=0; n != n_nodes; n++) elem->set_node(n) = mesh->node_ptr (static_cast<dof_id_type>(*in++)); for (unsigned int n=0; n<elem->n_neighbors(); n++) { const dof_id_type neighbor_id = static_cast<dof_id_type>(*in++); if (neighbor_id == DofObject::invalid_id) continue; // We may be unpacking an element that was a ghost element on the // sender, in which case the element's neighbors may not all be // known by the packed element. We'll have to set such // neighbors to remote_elem ourselves and wait for a later // packed element to give us better information. if (neighbor_id == remote_elem->id()) { elem->set_neighbor(n, const_cast<RemoteElem*>(remote_elem)); continue; } // If we don't have the neighbor element, then it's a // remote_elem until we get it. Elem *neigh = mesh->query_elem(neighbor_id); if (!neigh) { elem->set_neighbor(n, const_cast<RemoteElem*>(remote_elem)); continue; } // If we have the neighbor element, then link to it, and // make sure the appropriate parts of its family link back // to us. elem->set_neighbor(n, neigh); elem->make_links_to_me_local(n); } elem->unpack_indexing(in); } in += elem->packed_indexing_size(); // If this is a coarse element, // add any element side boundary condition ids if (level == 0) for (unsigned int s = 0; s != elem->n_sides(); ++s) { const int num_bcs = *in++; libmesh_assert_greater_equal (num_bcs, 0); for(int bc_it=0; bc_it < num_bcs; bc_it++) mesh->boundary_info->add_side (elem, s, *in++); } // Return the new element *out = elem; }
Elem * Packing<Elem *>::unpack (std::vector<largest_id_type>::const_iterator in, MeshBase * mesh) { #ifndef NDEBUG const std::vector<largest_id_type>::const_iterator original_in = in; const largest_id_type incoming_header = *in++; libmesh_assert_equal_to (incoming_header, elem_magic_header); #endif // int 0: level const unsigned int level = cast_int<unsigned int>(*in++); #ifdef LIBMESH_ENABLE_AMR // int 1: p level const unsigned int p_level = cast_int<unsigned int>(*in++); // int 2: refinement flag and encoded has_children const int rflag = cast_int<int>(*in++); const int invalid_rflag = cast_int<int>(Elem::INVALID_REFINEMENTSTATE); libmesh_assert_greater_equal (rflag, 0); libmesh_assert_less (rflag, invalid_rflag*2+1); const bool has_children = (rflag > invalid_rflag); const Elem::RefinementState refinement_flag = has_children ? cast_int<Elem::RefinementState>(rflag - invalid_rflag - 1) : cast_int<Elem::RefinementState>(rflag); // int 3: p refinement flag const int pflag = cast_int<int>(*in++); libmesh_assert_greater_equal (pflag, 0); libmesh_assert_less (pflag, Elem::INVALID_REFINEMENTSTATE); const Elem::RefinementState p_refinement_flag = cast_int<Elem::RefinementState>(pflag); #else in += 3; #endif // LIBMESH_ENABLE_AMR // int 4: element type const int typeint = cast_int<int>(*in++); libmesh_assert_greater_equal (typeint, 0); libmesh_assert_less (typeint, INVALID_ELEM); const ElemType type = cast_int<ElemType>(typeint); const unsigned int n_nodes = Elem::type_to_n_nodes_map[type]; // int 5: processor id const processor_id_type processor_id = cast_int<processor_id_type>(*in++); libmesh_assert (processor_id < mesh->n_processors() || processor_id == DofObject::invalid_processor_id); // int 6: subdomain id const subdomain_id_type subdomain_id = cast_int<subdomain_id_type>(*in++); // int 7: dof object id const dof_id_type id = cast_int<dof_id_type>(*in++); libmesh_assert_not_equal_to (id, DofObject::invalid_id); #ifdef LIBMESH_ENABLE_UNIQUE_ID // int 8: dof object unique id const unique_id_type unique_id = cast_int<unique_id_type>(*in++); #endif #ifdef LIBMESH_ENABLE_AMR // int 9: parent dof object id. // Note: If level==0, then (*in) == invalid_id. In // this case, the equality check in cast_int<unsigned>(*in) will // never succeed. Therefore, we should only attempt the more // rigorous cast verification in cases where level != 0. const dof_id_type parent_id = (level == 0) ? static_cast<dof_id_type>(*in++) : cast_int<dof_id_type>(*in++); libmesh_assert (level == 0 || parent_id != DofObject::invalid_id); libmesh_assert (level != 0 || parent_id == DofObject::invalid_id); // int 10: local child id // Note: If level==0, then which_child_am_i is not valid, so don't // do the more rigorous cast verification. const unsigned int which_child_am_i = (level == 0) ? static_cast<unsigned int>(*in++) : cast_int<unsigned int>(*in++); #else in += 2; #endif // LIBMESH_ENABLE_AMR const dof_id_type interior_parent_id = static_cast<dof_id_type>(*in++); // Make sure we don't miscount above when adding the "magic" header // plus the real data header libmesh_assert_equal_to (in - original_in, header_size + 1); Elem * elem = mesh->query_elem_ptr(id); // if we already have this element, make sure its // properties match, and update any missing neighbor // links, but then go on if (elem) { libmesh_assert_equal_to (elem->level(), level); libmesh_assert_equal_to (elem->id(), id); //#ifdef LIBMESH_ENABLE_UNIQUE_ID // No check for unique id sanity //#endif libmesh_assert_equal_to (elem->processor_id(), processor_id); libmesh_assert_equal_to (elem->subdomain_id(), subdomain_id); libmesh_assert_equal_to (elem->type(), type); libmesh_assert_equal_to (elem->n_nodes(), n_nodes); #ifndef NDEBUG // All our nodes should be correct for (unsigned int i=0; i != n_nodes; ++i) libmesh_assert(elem->node_id(i) == cast_int<dof_id_type>(*in++)); #else in += n_nodes; #endif #ifdef LIBMESH_ENABLE_AMR libmesh_assert_equal_to (elem->refinement_flag(), refinement_flag); libmesh_assert_equal_to (elem->has_children(), has_children); #ifdef DEBUG if (elem->active()) { libmesh_assert_equal_to (elem->p_level(), p_level); libmesh_assert_equal_to (elem->p_refinement_flag(), p_refinement_flag); } #endif libmesh_assert (!level || elem->parent() != libmesh_nullptr); libmesh_assert (!level || elem->parent()->id() == parent_id); libmesh_assert (!level || elem->parent()->child_ptr(which_child_am_i) == elem); #endif // Our interior_parent link should be "close to" correct - we // may have to update it, but we can check for some // inconsistencies. { // If the sending processor sees no interior_parent here, we'd // better agree. if (interior_parent_id == DofObject::invalid_id) { if (elem->dim() < LIBMESH_DIM) libmesh_assert (!(elem->interior_parent())); } // If the sending processor has a remote_elem interior_parent, // then all we know is that we'd better have *some* // interior_parent else if (interior_parent_id == remote_elem->id()) { libmesh_assert(elem->interior_parent()); } else { Elem * ip = mesh->query_elem_ptr(interior_parent_id); // The sending processor sees an interior parent here, so // if we don't have that interior element, then we'd // better have a remote_elem signifying that fact. if (!ip) libmesh_assert_equal_to (elem->interior_parent(), remote_elem); else { // The sending processor has an interior_parent here, // and we have that element, but that does *NOT* mean // we're already linking to it. Perhaps we initially // received elem from a processor on which the // interior_parent link was remote? libmesh_assert(elem->interior_parent() == ip || elem->interior_parent() == remote_elem); // If the link was originally remote, update it if (elem->interior_parent() == remote_elem) { elem->set_interior_parent(ip); } } } } // Our neighbor links should be "close to" correct - we may have // to update a remote_elem link, and we can check for possible // inconsistencies along the way. // // For subactive elements, we don't bother keeping neighbor // links in good shape, so there's nothing we need to set or can // safely assert here. if (!elem->subactive()) for (auto n : elem->side_index_range()) { const dof_id_type neighbor_id = cast_int<dof_id_type>(*in++); // If the sending processor sees a domain boundary here, // we'd better agree. if (neighbor_id == DofObject::invalid_id) { libmesh_assert (!(elem->neighbor_ptr(n))); continue; } // If the sending processor has a remote_elem neighbor here, // then all we know is that we'd better *not* have a domain // boundary. if (neighbor_id == remote_elem->id()) { libmesh_assert(elem->neighbor_ptr(n)); continue; } Elem * neigh = mesh->query_elem_ptr(neighbor_id); // The sending processor sees a neighbor here, so if we // don't have that neighboring element, then we'd better // have a remote_elem signifying that fact. if (!neigh) { libmesh_assert_equal_to (elem->neighbor_ptr(n), remote_elem); continue; } // The sending processor has a neighbor here, and we have // that element, but that does *NOT* mean we're already // linking to it. Perhaps we initially received both elem // and neigh from processors on which their mutual link was // remote? libmesh_assert(elem->neighbor_ptr(n) == neigh || elem->neighbor_ptr(n) == remote_elem); // If the link was originally remote, we should update it, // and make sure the appropriate parts of its family link // back to us. if (elem->neighbor_ptr(n) == remote_elem) { elem->set_neighbor(n, neigh); elem->make_links_to_me_local(n); } } // Our p level and refinement flags should be "close to" correct // if we're not an active element - we might have a p level // increased or decreased by changes in remote_elem children. // // But if we have remote_elem children, then we shouldn't be // doing a projection on this inactive element on this // processor, so we won't need correct p settings. Couldn't // hurt to update, though. #ifdef LIBMESH_ENABLE_AMR if (elem->processor_id() != mesh->processor_id()) { elem->hack_p_level(p_level); elem->set_p_refinement_flag(p_refinement_flag); } #endif // LIBMESH_ENABLE_AMR // FIXME: We should add some debug mode tests to ensure that the // encoded indexing and boundary conditions are consistent. } else { // We don't already have the element, so we need to create it. // Find the parent if necessary Elem * parent = libmesh_nullptr; #ifdef LIBMESH_ENABLE_AMR // Find a child element's parent if (level > 0) { // Note that we must be very careful to construct the send // connectivity so that parents are encountered before // children. If we get here and can't find the parent that // is a fatal error. parent = mesh->elem_ptr(parent_id); } // Or assert that the sending processor sees no parent else libmesh_assert_equal_to (parent_id, DofObject::invalid_id); #else // No non-level-0 elements without AMR libmesh_assert_equal_to (level, 0); #endif elem = Elem::build(type,parent).release(); libmesh_assert (elem); #ifdef LIBMESH_ENABLE_AMR if (level != 0) { // Since this is a newly created element, the parent must // have previously thought of this child as a remote element. libmesh_assert_equal_to (parent->child_ptr(which_child_am_i), remote_elem); parent->add_child(elem, which_child_am_i); } // Assign the refinement flags and levels elem->set_p_level(p_level); elem->set_refinement_flag(refinement_flag); elem->set_p_refinement_flag(p_refinement_flag); libmesh_assert_equal_to (elem->level(), level); // If this element should have children, assign remote_elem to // all of them for now, for consistency. Later unpacked // elements may overwrite that. if (has_children) { const unsigned int nc = elem->n_children(); for (unsigned int c=0; c != nc; ++c) elem->add_child(const_cast<RemoteElem *>(remote_elem), c); } #endif // LIBMESH_ENABLE_AMR // Assign the IDs elem->subdomain_id() = subdomain_id; elem->processor_id() = processor_id; elem->set_id() = id; #ifdef LIBMESH_ENABLE_UNIQUE_ID elem->set_unique_id() = unique_id; #endif // Assign the connectivity libmesh_assert_equal_to (elem->n_nodes(), n_nodes); for (unsigned int n=0; n != n_nodes; n++) elem->set_node(n) = mesh->node_ptr (cast_int<dof_id_type>(*in++)); // Set interior_parent if found { // We may be unpacking an element that was a ghost element on the // sender, in which case the element's interior_parent may not be // known by the packed element. We'll have to set such // interior_parents to remote_elem ourselves and wait for a // later packed element to give us better information. if (interior_parent_id == remote_elem->id()) { elem->set_interior_parent (const_cast<RemoteElem *>(remote_elem)); } else if (interior_parent_id != DofObject::invalid_id) { // If we don't have the interior parent element, then it's // a remote_elem until we get it. Elem * ip = mesh->query_elem_ptr(interior_parent_id); if (!ip ) elem->set_interior_parent (const_cast<RemoteElem *>(remote_elem)); else elem->set_interior_parent(ip); } } for (auto n : elem->side_index_range()) { const dof_id_type neighbor_id = cast_int<dof_id_type>(*in++); if (neighbor_id == DofObject::invalid_id) continue; // We may be unpacking an element that was a ghost element on the // sender, in which case the element's neighbors may not all be // known by the packed element. We'll have to set such // neighbors to remote_elem ourselves and wait for a later // packed element to give us better information. if (neighbor_id == remote_elem->id()) { elem->set_neighbor(n, const_cast<RemoteElem *>(remote_elem)); continue; } // If we don't have the neighbor element, then it's a // remote_elem until we get it. Elem * neigh = mesh->query_elem_ptr(neighbor_id); if (!neigh) { elem->set_neighbor(n, const_cast<RemoteElem *>(remote_elem)); continue; } // If we have the neighbor element, then link to it, and // make sure the appropriate parts of its family link back // to us. elem->set_neighbor(n, neigh); elem->make_links_to_me_local(n); } elem->unpack_indexing(in); } in += elem->packed_indexing_size(); // If this is a coarse element, // add any element side or edge boundary condition ids if (level == 0) { for (auto s : elem->side_index_range()) { const boundary_id_type num_bcs = cast_int<boundary_id_type>(*in++); for (boundary_id_type bc_it=0; bc_it < num_bcs; bc_it++) mesh->get_boundary_info().add_side (elem, s, cast_int<boundary_id_type>(*in++)); } for (auto e : elem->edge_index_range()) { const boundary_id_type num_bcs = cast_int<boundary_id_type>(*in++); for (boundary_id_type bc_it=0; bc_it < num_bcs; bc_it++) mesh->get_boundary_info().add_edge (elem, e, cast_int<boundary_id_type>(*in++)); } for (unsigned short sf=0; sf != 2; ++sf) { const boundary_id_type num_bcs = cast_int<boundary_id_type>(*in++); for (boundary_id_type bc_it=0; bc_it < num_bcs; bc_it++) mesh->get_boundary_info().add_shellface (elem, sf, cast_int<boundary_id_type>(*in++)); } } // Return the new element return elem; }
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 }
void HPCoarsenTest::select_refinement (System & system) { START_LOG("select_refinement()", "HPCoarsenTest"); // The current mesh MeshBase & mesh = system.get_mesh(); // The dimensionality of the mesh const unsigned int dim = mesh.mesh_dimension(); // The number of variables in the system const unsigned int n_vars = system.n_vars(); // The DofMap for this system const DofMap & dof_map = system.get_dof_map(); // The system number (for doing bad hackery) const unsigned int sys_num = system.number(); // Check for a valid component_scale if (!component_scale.empty()) { if (component_scale.size() != n_vars) libmesh_error_msg("ERROR: component_scale is the wrong size:\n" \ << " component_scale.size()=" \ << component_scale.size() \ << "\n n_vars=" \ << n_vars); } else { // No specified scaling. Scale all variables by one. component_scale.resize (n_vars, 1.0); } // Resize the error_per_cell vectors to handle // the number of elements, initialize them to 0. std::vector<ErrorVectorReal> h_error_per_cell(mesh.max_elem_id(), 0.); std::vector<ErrorVectorReal> p_error_per_cell(mesh.max_elem_id(), 0.); // Loop over all the variables in the system for (unsigned int var=0; var<n_vars; var++) { // Possibly skip this variable if (!component_scale.empty()) if (component_scale[var] == 0.0) continue; // The type of finite element to use for this variable const FEType & fe_type = dof_map.variable_type (var); // Finite element objects for a fine (and probably a coarse) // element will be needed fe = FEBase::build (dim, fe_type); fe_coarse = FEBase::build (dim, fe_type); // Any cached coarse element results have expired coarse = libmesh_nullptr; unsigned int cached_coarse_p_level = 0; const FEContinuity cont = fe->get_continuity(); libmesh_assert (cont == DISCONTINUOUS || cont == C_ZERO || cont == C_ONE); // Build an appropriate quadrature rule qrule = fe_type.default_quadrature_rule(dim); // Tell the refined finite element about the quadrature // rule. The coarse finite element need not know about it fe->attach_quadrature_rule (qrule.get()); // We will always do the integration // on the fine elements. Get their Jacobian values, etc.. JxW = &(fe->get_JxW()); xyz_values = &(fe->get_xyz()); // The shape functions phi = &(fe->get_phi()); phi_coarse = &(fe_coarse->get_phi()); // The shape function derivatives if (cont == C_ZERO || cont == C_ONE) { dphi = &(fe->get_dphi()); dphi_coarse = &(fe_coarse->get_dphi()); } #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES // The shape function second derivatives if (cont == C_ONE) { d2phi = &(fe->get_d2phi()); d2phi_coarse = &(fe_coarse->get_d2phi()); } #endif // defined (LIBMESH_ENABLE_SECOND_DERIVATIVES) // Iterate over all the active elements in the mesh // that live on this processor. MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); for (; elem_it != elem_end; ++elem_it) { const Elem * elem = *elem_it; // We're only checking elements that are already flagged for h // refinement if (elem->refinement_flag() != Elem::REFINE) continue; const dof_id_type e_id = elem->id(); // Find the projection onto the parent element, // if necessary if (elem->parent() && (coarse != elem->parent() || cached_coarse_p_level != elem->p_level())) { Uc.resize(0); coarse = elem->parent(); cached_coarse_p_level = elem->p_level(); unsigned int old_parent_level = coarse->p_level(); (const_cast<Elem *>(coarse))->hack_p_level(elem->p_level()); this->add_projection(system, coarse, var); (const_cast<Elem *>(coarse))->hack_p_level(old_parent_level); // Solve the h-coarsening projection problem Ke.cholesky_solve(Fe, Uc); } fe->reinit(elem); // Get the DOF indices for the fine element dof_map.dof_indices (elem, dof_indices, var); // The number of quadrature points const unsigned int n_qp = qrule->n_points(); // The number of DOFS on the fine element const unsigned int n_dofs = cast_int<unsigned int>(dof_indices.size()); // The number of nodes on the fine element const unsigned int n_nodes = elem->n_nodes(); // The average element value (used as an ugly hack // when we have nothing p-coarsened to compare to) // Real average_val = 0.; Number average_val = 0.; // Calculate this variable's contribution to the p // refinement error if (elem->p_level() == 0) { unsigned int n_vertices = 0; for (unsigned int n = 0; n != n_nodes; ++n) if (elem->is_vertex(n)) { n_vertices++; const Node * const node = elem->get_node(n); average_val += system.current_solution (node->dof_number(sys_num,var,0)); } average_val /= n_vertices; } else { unsigned int old_elem_level = elem->p_level(); (const_cast<Elem *>(elem))->hack_p_level(old_elem_level - 1); fe_coarse->reinit(elem, &(qrule->get_points())); const unsigned int n_coarse_dofs = cast_int<unsigned int>(phi_coarse->size()); (const_cast<Elem *>(elem))->hack_p_level(old_elem_level); Ke.resize(n_coarse_dofs, n_coarse_dofs); Ke.zero(); Fe.resize(n_coarse_dofs); Fe.zero(); // Loop over the quadrature points for (unsigned int qp=0; qp<qrule->n_points(); qp++) { // The solution value at the quadrature point Number val = libMesh::zero; Gradient grad; Tensor hess; for (unsigned int i=0; i != n_dofs; i++) { dof_id_type dof_num = dof_indices[i]; val += (*phi)[i][qp] * system.current_solution(dof_num); if (cont == C_ZERO || cont == C_ONE) grad.add_scaled((*dphi)[i][qp], system.current_solution(dof_num)); // grad += (*dphi)[i][qp] * // system.current_solution(dof_num); if (cont == C_ONE) hess.add_scaled((*d2phi)[i][qp], system.current_solution(dof_num)); // hess += (*d2phi)[i][qp] * // system.current_solution(dof_num); } // The projection matrix and vector for (unsigned int i=0; i != Fe.size(); ++i) { Fe(i) += (*JxW)[qp] * (*phi_coarse)[i][qp]*val; if (cont == C_ZERO || cont == C_ONE) Fe(i) += (*JxW)[qp] * grad * (*dphi_coarse)[i][qp]; if (cont == C_ONE) Fe(i) += (*JxW)[qp] * hess.contract((*d2phi_coarse)[i][qp]); for (unsigned int j=0; j != Fe.size(); ++j) { Ke(i,j) += (*JxW)[qp] * (*phi_coarse)[i][qp]*(*phi_coarse)[j][qp]; if (cont == C_ZERO || cont == C_ONE) Ke(i,j) += (*JxW)[qp] * (*dphi_coarse)[i][qp]*(*dphi_coarse)[j][qp]; if (cont == C_ONE) Ke(i,j) += (*JxW)[qp] * ((*d2phi_coarse)[i][qp].contract((*d2phi_coarse)[j][qp])); } } } // Solve the p-coarsening projection problem Ke.cholesky_solve(Fe, Up); } // loop over the integration points on the fine element for (unsigned int qp=0; qp<n_qp; qp++) { Number value_error = 0.; Gradient grad_error; Tensor hessian_error; for (unsigned int i=0; i<n_dofs; i++) { const dof_id_type dof_num = dof_indices[i]; value_error += (*phi)[i][qp] * system.current_solution(dof_num); if (cont == C_ZERO || cont == C_ONE) grad_error.add_scaled((*dphi)[i][qp], system.current_solution(dof_num)); // grad_error += (*dphi)[i][qp] * // system.current_solution(dof_num); if (cont == C_ONE) hessian_error.add_scaled((*d2phi)[i][qp], system.current_solution(dof_num)); // hessian_error += (*d2phi)[i][qp] * // system.current_solution(dof_num); } if (elem->p_level() == 0) { value_error -= average_val; } else { for (unsigned int i=0; i<Up.size(); i++) { value_error -= (*phi_coarse)[i][qp] * Up(i); if (cont == C_ZERO || cont == C_ONE) grad_error.subtract_scaled((*dphi_coarse)[i][qp], Up(i)); // grad_error -= (*dphi_coarse)[i][qp] * Up(i); if (cont == C_ONE) hessian_error.subtract_scaled((*d2phi_coarse)[i][qp], Up(i)); // hessian_error -= (*d2phi_coarse)[i][qp] * Up(i); } } p_error_per_cell[e_id] += static_cast<ErrorVectorReal> (component_scale[var] * (*JxW)[qp] * TensorTools::norm_sq(value_error)); if (cont == C_ZERO || cont == C_ONE) p_error_per_cell[e_id] += static_cast<ErrorVectorReal> (component_scale[var] * (*JxW)[qp] * grad_error.norm_sq()); if (cont == C_ONE) p_error_per_cell[e_id] += static_cast<ErrorVectorReal> (component_scale[var] * (*JxW)[qp] * hessian_error.norm_sq()); } // Calculate this variable's contribution to the h // refinement error if (!elem->parent()) { // For now, we'll always start with an h refinement h_error_per_cell[e_id] = std::numeric_limits<ErrorVectorReal>::max() / 2; } else { FEInterface::inverse_map (dim, fe_type, coarse, *xyz_values, coarse_qpoints); unsigned int old_parent_level = coarse->p_level(); (const_cast<Elem *>(coarse))->hack_p_level(elem->p_level()); fe_coarse->reinit(coarse, &coarse_qpoints); (const_cast<Elem *>(coarse))->hack_p_level(old_parent_level); // The number of DOFS on the coarse element unsigned int n_coarse_dofs = cast_int<unsigned int>(phi_coarse->size()); // Loop over the quadrature points for (unsigned int qp=0; qp<n_qp; qp++) { // The solution difference at the quadrature point Number value_error = libMesh::zero; Gradient grad_error; Tensor hessian_error; for (unsigned int i=0; i != n_dofs; ++i) { const dof_id_type dof_num = dof_indices[i]; value_error += (*phi)[i][qp] * system.current_solution(dof_num); if (cont == C_ZERO || cont == C_ONE) grad_error.add_scaled((*dphi)[i][qp], system.current_solution(dof_num)); // grad_error += (*dphi)[i][qp] * // system.current_solution(dof_num); if (cont == C_ONE) hessian_error.add_scaled((*d2phi)[i][qp], system.current_solution(dof_num)); // hessian_error += (*d2phi)[i][qp] * // system.current_solution(dof_num); } for (unsigned int i=0; i != n_coarse_dofs; ++i) { value_error -= (*phi_coarse)[i][qp] * Uc(i); if (cont == C_ZERO || cont == C_ONE) // grad_error -= (*dphi_coarse)[i][qp] * Uc(i); grad_error.subtract_scaled((*dphi_coarse)[i][qp], Uc(i)); if (cont == C_ONE) hessian_error.subtract_scaled((*d2phi_coarse)[i][qp], Uc(i)); // hessian_error -= (*d2phi_coarse)[i][qp] * Uc(i); } h_error_per_cell[e_id] += static_cast<ErrorVectorReal> (component_scale[var] * (*JxW)[qp] * TensorTools::norm_sq(value_error)); if (cont == C_ZERO || cont == C_ONE) h_error_per_cell[e_id] += static_cast<ErrorVectorReal> (component_scale[var] * (*JxW)[qp] * grad_error.norm_sq()); if (cont == C_ONE) h_error_per_cell[e_id] += static_cast<ErrorVectorReal> (component_scale[var] * (*JxW)[qp] * hessian_error.norm_sq()); } } } } // Now that we've got our approximations for p_error and h_error, let's see // if we want to switch any h refinement flags to p refinement // Iterate over all the active elements in the mesh // that live on this processor. MeshBase::element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::element_iterator elem_end = mesh.active_local_elements_end(); for (; elem_it != elem_end; ++elem_it) { Elem * elem = *elem_it; // We're only checking elements that are already flagged for h // refinement if (elem->refinement_flag() != Elem::REFINE) continue; const dof_id_type e_id = elem->id(); unsigned int dofs_per_elem = 0, dofs_per_p_elem = 0; // Loop over all the variables in the system for (unsigned int var=0; var<n_vars; var++) { // The type of finite element to use for this variable const FEType & fe_type = dof_map.variable_type (var); // FIXME: we're overestimating the number of DOFs added by h // refinement FEType elem_fe_type = fe_type; elem_fe_type.order = static_cast<Order>(fe_type.order + elem->p_level()); dofs_per_elem += FEInterface::n_dofs(dim, elem_fe_type, elem->type()); elem_fe_type.order = static_cast<Order>(fe_type.order + elem->p_level() + 1); dofs_per_p_elem += FEInterface::n_dofs(dim, elem_fe_type, elem->type()); } const unsigned int new_h_dofs = dofs_per_elem * (elem->n_children() - 1); const unsigned int new_p_dofs = dofs_per_p_elem - dofs_per_elem; /* libMesh::err << "Cell " << e_id << ": h = " << elem->hmax() << ", p = " << elem->p_level() + 1 << "," << std::endl << " h_error = " << h_error_per_cell[e_id] << ", p_error = " << p_error_per_cell[e_id] << std::endl << " new_h_dofs = " << new_h_dofs << ", new_p_dofs = " << new_p_dofs << std::endl; */ const Real p_value = std::sqrt(p_error_per_cell[e_id]) * p_weight / new_p_dofs; const Real h_value = std::sqrt(h_error_per_cell[e_id]) / static_cast<Real>(new_h_dofs); if (p_value > h_value) { elem->set_p_refinement_flag(Elem::REFINE); elem->set_refinement_flag(Elem::DO_NOTHING); } } STOP_LOG("select_refinement()", "HPCoarsenTest"); }