You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
313 lines
9.4 KiB
313 lines
9.4 KiB
// 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 <algorithm> // for max, min
|
|
#include <cfloat> // for FLT_MAX
|
|
#include <cstddef> // for size_t
|
|
#include <fmt/format.h>
|
|
|
|
namespace {
|
|
template <typename INT>
|
|
void do_matching(std::vector<INT> &i_inrange, const RealVector &i_coord, size_t i_offset,
|
|
std::vector<INT> &j_inrange, const RealVector &j_coord, size_t j_offset,
|
|
double epsilon, int XYZ, std::vector<INT> &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<double> &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 <typename INT>
|
|
void find_in_range(const std::vector<double> &coord, const vector3d &min, const vector3d &max,
|
|
std::vector<INT> &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 <typename INT>
|
|
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;
|
|
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<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) {
|
|
fmt::print("Parts {} and {} do not overlap.\n", ip, jp);
|
|
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 void match_node_xyz(RegionVector &part_mesh, double tolerance,
|
|
std::vector<int> &global_node_map, std::vector<int> &local_node_map);
|
|
template void match_node_xyz(RegionVector &part_mesh, double tolerance,
|
|
std::vector<int64_t> &global_node_map,
|
|
std::vector<int64_t> &local_node_map);
|
|
|
|
namespace {
|
|
template <typename INT>
|
|
void do_matching(std::vector<INT> &i_inrange, const RealVector &i_coord, size_t i_offset,
|
|
std::vector<INT> &j_inrange, const RealVector &j_coord, size_t j_offset,
|
|
double epsilon, int XYZ, std::vector<INT> &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
|
|
|