// Copyright(C) 1999-2020, 2023 National Technology & Engineering Solutions // of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with // NTESS, the U.S. Government retains certain rights in this software. // // See packages/seacas/LICENSE for details #include "EJ_CodeTypes.h" #include "EJ_index_sort.h" // for index_coord_sort #include "EJ_mapping.h" // for eliminate_omitted_nodes #include "EJ_vector3d.h" // for vector3d #include "Ioss_NodeBlock.h" // for NodeBlock #include "Ioss_Property.h" // for Property #include "Ioss_Region.h" // for Region, NodeBlockContainer #include "Ioss_SmartAssert.h" #include // for max, min #include // for FLT_MAX #include // for size_t #include namespace { template void do_matching(std::vector &i_inrange, const RealVector &i_coord, size_t i_offset, std::vector &j_inrange, const RealVector &j_coord, size_t j_offset, double epsilon, int XYZ, std::vector &local_node_map); double max3(double x, double y, double z) { double max = x; if (y > max) { max = y; } if (z > max) { max = z; } return max; } void find_range(const std::vector &coord, vector3d &min, vector3d &max) { if (!coord.empty()) { min.set(coord[0], coord[1], coord[2]); max = min; for (size_t i = 3; i < coord.size(); i += 3) { if (min.x > coord[i + 0]) { min.x = coord[i + 0]; } if (min.y > coord[i + 1]) { min.y = coord[i + 1]; } if (min.z > coord[i + 2]) { min.z = coord[i + 2]; } if (max.x < coord[i + 0]) { max.x = coord[i + 0]; } if (max.y < coord[i + 1]) { max.y = coord[i + 1]; } if (max.z < coord[i + 2]) { max.z = coord[i + 2]; } } } else { min.set(0, 0, 0); max = min; } } template void find_in_range(const std::vector &coord, const vector3d &min, const vector3d &max, std::vector &in_range) { if (!coord.empty()) { for (size_t i = 0; i < coord.size(); i += 3) { if (coord[i + 0] > min.x && coord[i + 0] < max.x && coord[i + 1] > min.y && coord[i + 1] < max.y && coord[i + 2] > min.z && coord[i + 2] < max.z) { in_range.push_back(i / 3); } } } } } // namespace template void match_node_xyz(RegionVector &part_mesh, double tolerance, std::vector &global_node_map, std::vector &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 dummy; bool fill_global = false; eliminate_omitted_nodes(part_mesh, dummy, local_node_map, fill_global); SMART_ASSERT(dummy.empty()); // 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 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 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) { fmt::print("Parts {} and {} do not overlap.\n", ip, jp); continue; } min -= epsilon; max += epsilon; if (tolerance >= 0.0) { epsilon = tolerance; } std::vector j_inrange; std::vector 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 void match_node_xyz(RegionVector &part_mesh, double tolerance, std::vector &global_node_map, std::vector &local_node_map); template void match_node_xyz(RegionVector &part_mesh, double tolerance, std::vector &global_node_map, std::vector &local_node_map); namespace { template void do_matching(std::vector &i_inrange, const RealVector &i_coord, size_t i_offset, std::vector &j_inrange, const RealVector &j_coord, size_t j_offset, double epsilon, int XYZ, std::vector &local_node_map) { INT j2beg = 0; INT match = 0; INT compare = 0; double g_dismin = FLT_MAX; double dismax = -FLT_MAX; for (auto ii : i_inrange) { if (local_node_map[ii + i_offset] < 0) { continue; } double dismin = FLT_MAX; double dmin = FLT_MAX; INT node_dmin = -1; for (size_t j = j2beg; j < j_inrange.size(); j++) { compare++; INT jj = j_inrange[j]; if (jj < 0 || local_node_map[jj + j_offset] < 0) { continue; } if (i_coord[3 * ii + XYZ] - epsilon > j_coord[3 * jj + XYZ]) { j2beg = j; continue; } //... Since we are sorted on coordinate X|Y|Z, // if set 'j' X|Y|Z greater than set 'i' X|Y|Z+eps, go to next 'i' X1|Y1|Z1 coord. if (j_coord[3 * jj + XYZ] - epsilon > i_coord[3 * ii + XYZ]) { break; } double distance = max3(std::fabs(j_coord[3 * jj + 0] - i_coord[3 * ii + 0]), std::fabs(j_coord[3 * jj + 1] - i_coord[3 * ii + 1]), std::fabs(j_coord[3 * jj + 2] - i_coord[3 * ii + 2])); if (float(distance) <= float(epsilon)) { if (distance < dmin) { dmin = distance; node_dmin = j; } } else { if (distance < dismin) { dismin = distance; } } if (distance == 0.0) { break; } } if (dmin <= epsilon && node_dmin >= 0) { INT jnod = j_inrange[node_dmin] + j_offset; INT inod = ii + i_offset; match++; if (dmin > dismax) { dismax = dmin; } j_inrange[node_dmin] *= -1; SMART_ASSERT(jnod < (INT)local_node_map.size()); if (inod < jnod) { local_node_map[jnod] = inod; } else { local_node_map[inod] = jnod; } } else { if (dismin < g_dismin) { g_dismin = dismin; } } } fmt::print("\nNumber of nodes matched = {}\n", match); fmt::print("Number of comparisons = {}\n", compare); fmt::print("Tolerance used for matching = {}\n", epsilon); if (dismax > double(-FLT_MAX)) { fmt::print("Maximum distance between matched nodes = {}\n", dismax); } if (g_dismin < double(FLT_MAX)) { fmt::print("Minimum distance between nonmatched nodes = {}\n", g_dismin); } fmt::print("\n"); } } // namespace