AxisAlignedBoundingBox DatabaseIO::get_bounding_box(const Ioss::ElementBlock *eb) const { if (elementBlockBoundingBoxes.empty()) { // Calculate the bounding boxes for all element blocks... std::vector<double> coordinates; Ioss::NodeBlock * nb = get_region()->get_node_blocks()[0]; nb->get_field_data("mesh_model_coordinates", coordinates); ssize_t nnode = nb->get_property("entity_count").get_int(); ssize_t ndim = nb->get_property("component_degree").get_int(); Ioss::ElementBlockContainer element_blocks = get_region()->get_element_blocks(); size_t nblock = element_blocks.size(); std::vector<double> minmax; minmax.reserve(6 * nblock); for (auto &block : element_blocks) { double xmin, ymin, zmin, xmax, ymax, zmax; if (block->get_database()->int_byte_size_api() == 8) { std::vector<int64_t> connectivity; block->get_field_data("connectivity_raw", connectivity); calc_bounding_box(ndim, nnode, coordinates, connectivity, xmin, ymin, zmin, xmax, ymax, zmax); } else { std::vector<int> connectivity; block->get_field_data("connectivity_raw", connectivity); calc_bounding_box(ndim, nnode, coordinates, connectivity, xmin, ymin, zmin, xmax, ymax, zmax); } minmax.push_back(xmin); minmax.push_back(ymin); minmax.push_back(zmin); minmax.push_back(-xmax); minmax.push_back(-ymax); minmax.push_back(-zmax); } util().global_array_minmax(minmax, Ioss::ParallelUtils::DO_MIN); for (size_t i = 0; i < element_blocks.size(); i++) { Ioss::ElementBlock * block = element_blocks[i]; const std::string & name = block->name(); AxisAlignedBoundingBox bbox(minmax[6 * i + 0], minmax[6 * i + 1], minmax[6 * i + 2], -minmax[6 * i + 3], -minmax[6 * i + 4], -minmax[6 * i + 5]); elementBlockBoundingBoxes[name] = bbox; } } return elementBlockBoundingBoxes[eb->name()]; }
void eliminate_omitted_nodes(RegionVector &part_mesh, std::vector<INT> &global_node_map, std::vector<INT> &local_node_map) { size_t offset = 0; size_t j = 0; size_t part_count = part_mesh.size(); for (size_t p = 0; p < part_count; p++) { bool has_omissions = part_mesh[p]->get_property("block_omission_count").get_int() > 0; Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0]; size_t loc_size = nb->get_property("entity_count").get_int(); if (has_omissions) { // If there are any omitted element blocks for this part, don't // map the nodes that are only connected to omitted element // blocks. std::vector<char> node_status; nb->get_field_data("node_connectivity_status", node_status); for (size_t i=0; i < node_status.size(); i++) { if (node_status[i] != 1) { local_node_map[offset+i] = j; global_node_map.push_back(j+1); j++; } else { local_node_map[offset+i] = -1; } } } else { for (size_t i=0; i < loc_size; i++) { local_node_map[offset+i] = j; global_node_map.push_back(j+1); j++; } } offset += loc_size; } }
void build_reverse_node_map(Ioss::Region &global, RegionVector &part_mesh, std::vector<INT> &global_node_map, std::vector<INT> &local_node_map) { // Instead of using <set> and <map>, consider using a sorted vector... // Append all local node maps to the global node map. // Sort the global node map // Remove duplicates. // Position within map is now the map... // When building the local-part node to global id, use binary_search... size_t part_count = part_mesh.size(); // Global node map and count. std::vector<std::vector<int> > global_nodes(part_count); size_t tot_size = 0; for (size_t p = 0; p < part_count; p++) { Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0]; size_t loc_size = nb->get_property("entity_count").get_int(); tot_size += loc_size; global_nodes[p].resize(loc_size); } global_node_map.resize(tot_size); size_t offset = 0; bool any_omitted_nodes = false; for (size_t p = 0; p < part_count; p++) { Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0]; nb->get_field_data("ids", global_nodes[p]); // If there are any omitted element blocks for this part, set // the global id of any nodes that are only connected to omitted // element blocks to 0. bool has_omissions = part_mesh[p]->get_property("block_omission_count").get_int() > 0; if (has_omissions) { std::vector<char> node_status; nb->get_field_data("node_connectivity_status", node_status); for (size_t i=0; i < node_status.size(); i++) { if (node_status[i] == 1) { any_omitted_nodes = true; global_nodes[p][i] = 0; } } } std::copy(global_nodes[p].begin(), global_nodes[p].end(), &global_node_map[offset]); offset += global_nodes[p].size(); } // Now, sort the global_node_map array and remove duplicates... uniqify(global_node_map); // If any omitted nodes, remove them from the global_node_map. // The id will be 0 if (any_omitted_nodes) { typename std::vector<INT>::iterator pos = std::remove(global_node_map.begin(), global_node_map.end(), 0); global_node_map.erase(pos, global_node_map.end()); } size_t output_node_count = global_node_map.size(); // See whether the node numbers are contiguous. If so, we can map // the nodes back to their original location. Since the nodes are // sorted and there are no duplicates, we just need to see if the id // at global_node_map.size() == global_node_map.size(); size_t max_id = global_node_map[output_node_count-1]; bool is_contiguous = max_id == output_node_count; std::cerr << "Node map " << (is_contiguous ? "is" : "is not") << " contiguous.\n"; // Create the map that maps from a local part node to the // global map. This combines the mapping local part node to // 'global id' and then 'global id' to global position. The // mapping is now a direct lookup instead of a lookup followed by // a reverse map. typedef typename std::vector<INT>::iterator V_INT_iterator; V_INT_iterator cur_pos = global_node_map.begin(); for (size_t p = 0; p < part_count; p++) { size_t noffset = part_mesh[p]->get_property("node_offset").get_int(); size_t node_count = global_nodes[p].size(); for (size_t i = 0; i < node_count; i++) { INT global_node = global_nodes[p][i]; if (global_node > 0) { if (cur_pos == global_node_map.end() || *cur_pos != global_node) { std::pair<V_INT_iterator, V_INT_iterator> iter = std::equal_range(global_node_map.begin(), global_node_map.end(), global_node); if (iter.first == iter.second) { INT n = global_node; std::cerr << n << "\n"; SMART_ASSERT(iter.first != iter.second); } cur_pos = iter.first; } size_t nodal_value = cur_pos - global_node_map.begin(); local_node_map[noffset+i] = nodal_value; ++cur_pos; } else { local_node_map[noffset+i] = -1; } } } // Update the nodal ids to give a unique, non-repeating set. If contiguous, then // there is nothing to do. If not contiguous, then need to determine if there are any // repeats (id reuse) and if so, generate a new id for the repeated uses. if (!is_contiguous) { bool repeat_found = false; INT id_last = global_node_map[0]; for (size_t i=1; i < output_node_count; i++) { if (global_node_map[i] == id_last) { global_node_map[i] = ++max_id; repeat_found = true; } else { id_last = global_node_map[i]; } } if (repeat_found) { std::cerr << "Duplicate node ids were found. Their ids have been renumbered to remove duplicates.\n"; } } }
void match_node_xyz(RegionVector &part_mesh, double tolerance, std::vector<INT> &global_node_map, std::vector<INT> &local_node_map) { // See if any omitted element blocks... bool has_omissions = false; for (auto & elem : part_mesh) { if (elem->get_property("block_omission_count").get_int() > 0) { has_omissions = true; break; } } if (!has_omissions) { for (size_t i=0; i < local_node_map.size(); i++) { local_node_map[i] = i; } } else { std::vector<INT> dummy; eliminate_omitted_nodes(part_mesh, dummy, local_node_map); // The local_node_map is not quite in the correct format after the // call to 'eliminate_omitted_nodes'. We need all non-omitted // nodes to have local_node_map[i] == i. for (size_t i=0; i < local_node_map.size(); i++) { if (local_node_map[i] >= 0) local_node_map[i] = i; } } size_t part_count = part_mesh.size(); enum {X=0, Y=1, Z=2}; for (size_t ip=0; ip < part_count; ip++) { vector3d i_max; vector3d i_min; std::vector<double> i_coord; Ioss::NodeBlock *inb = part_mesh[ip]->get_node_blocks()[0]; inb->get_field_data("mesh_model_coordinates", i_coord); find_range(i_coord, i_min, i_max); size_t i_offset = part_mesh[ip]->get_property("node_offset").get_int(); for (size_t jp=ip+1; jp < part_count; jp++) { vector3d j_max; vector3d j_min; std::vector<double> j_coord; Ioss::NodeBlock *jnb = part_mesh[jp]->get_node_blocks()[0]; jnb->get_field_data("mesh_model_coordinates", j_coord); find_range(j_coord, j_min, j_max); size_t j_offset = part_mesh[jp]->get_property("node_offset").get_int(); // See if the ranges overlap... vector3d max; vector3d min; max.x = std::min(i_max.x, j_max.x); max.y = std::min(i_max.y, j_max.y); max.z = std::min(i_max.z, j_max.z); min.x = std::max(i_min.x, j_min.x); min.y = std::max(i_min.y, j_min.y); min.z = std::max(i_min.z, j_min.z); double delta[3]; int XYZ = X; delta[XYZ] = max.x - min.x; delta[Y] = max.y - min.y; if (delta[Y] > delta[XYZ]) XYZ = Y; delta[Z] = max.z - min.z; if (delta[Z] > delta[XYZ]) XYZ = Z; double epsilon = (delta[X] + delta[Y] + delta[Z]) / 1.0e3; if (epsilon < 0.0) { std::cout << "Parts " << ip << " and " << jp << " do not overlap.\n"; continue; } min -= epsilon; max += epsilon; if (tolerance >= 0.0) epsilon = tolerance; std::vector<INT> j_inrange; std::vector<INT> i_inrange; find_in_range(j_coord, min, max, j_inrange); find_in_range(i_coord, min, max, i_inrange); // Index sort all nodes on the coordinate range with the maximum delta. index_coord_sort(i_coord, i_inrange, XYZ); index_coord_sort(j_coord, j_inrange, XYZ); if (i_inrange.size() < j_inrange.size()) { do_matching(i_inrange, i_coord, i_offset, j_inrange, j_coord, j_offset, epsilon, XYZ, local_node_map); } else { do_matching(j_inrange, j_coord, j_offset, i_inrange, i_coord, i_offset, epsilon, XYZ, local_node_map); } } } // Build the global and local maps... size_t j = 1; for (size_t i=0; i < local_node_map.size(); i++) { if (local_node_map[i] == (INT)i) { global_node_map.push_back(j); local_node_map[i] = j-1; j++; } else if (local_node_map[i] >= 0) { local_node_map[i] = local_node_map[local_node_map[i]]; } } }
template <typename INT> void FaceGenerator::generate_faces(INT /*dummy*/) { Ioss::NodeBlock *nb = region_.get_node_blocks()[0]; std::vector<INT> ids; nb->get_field_data("ids", ids); // Convert ids into hashed-ids auto starth = std::chrono::high_resolution_clock::now(); std::vector<size_t> hash_ids; hash_ids.reserve(ids.size()); for (auto id : ids) { hash_ids.push_back(id_hash(id)); } auto endh = std::chrono::high_resolution_clock::now(); size_t numel = region_.get_property("element_count").get_int(); faces_.reserve(3.3 * numel); Ioss::ElementBlockContainer ebs = region_.get_element_blocks(); for (auto eb : ebs) { const Ioss::ElementTopology *topo = eb->topology(); // Only handle continuum elements at this time... if (topo->parametric_dimension() != 3) { continue; } std::vector<INT> connectivity; eb->get_field_data("connectivity_raw", connectivity); std::vector<INT> elem_ids; eb->get_field_data("ids", elem_ids); int num_face_per_elem = topo->number_faces(); assert(num_face_per_elem <= 6); std::array<Ioss::IntVector, 6> face_conn; std::array<int, 6> face_count; for (int face = 0; face < num_face_per_elem; face++) { face_conn[face] = topo->face_connectivity(face + 1); face_count[face] = topo->face_type(face + 1)->number_corner_nodes(); } int num_node_per_elem = topo->number_nodes(); size_t num_elem = eb->get_property("entity_count").get_int(); for (size_t elem = 0, offset = 0; elem < num_elem; elem++, offset += num_node_per_elem) { for (int face = 0; face < num_face_per_elem; face++) { size_t id = 0; assert(face_count[face] <= 4); std::array<size_t, 4> conn = {{0, 0, 0, 0}}; for (int j = 0; j < face_count[face]; j++) { size_t fnode = offset + face_conn[face][j]; size_t gnode = connectivity[fnode]; conn[j] = ids[gnode - 1]; id += hash_ids[gnode - 1]; } create_face(faces_, id, conn, elem_ids[elem]); } } } auto endf = std::chrono::high_resolution_clock::now(); resolve_parallel_faces(region_, faces_, hash_ids, (INT)0); auto endp = std::chrono::high_resolution_clock::now(); auto diffh = endh - starth; auto difff = endf - endh; auto diffp = endp - endf; std::cout << "Node ID hash time: \t" << std::chrono::duration<double, std::milli>(diffh).count() << " ms\t" << hash_ids.size() / std::chrono::duration<double>(diffh).count() << " nodes/second\n"; std::cout << "Face generation time:\t" << std::chrono::duration<double, std::milli>(difff).count() << " ms\t" << faces_.size() / std::chrono::duration<double>(difff).count() << " faces/second.\n"; #ifdef HAVE_MPI size_t proc_count = region_.get_database()->util().parallel_size(); if (proc_count > 1) { std::cout << "Parallel time: \t" << std::chrono::duration<double, std::milli>(diffp).count() << " ms\t" << faces_.size() / std::chrono::duration<double>(diffp).count() << " faces/second.\n"; } #endif std::cout << "Total time: \t" << std::chrono::duration<double, std::milli>(endp - starth).count() << " ms\n\n"; }