Cloned SEACAS for EXODUS library with extra build files for internal package management.
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.

314 lines
9.4 KiB

2 years ago
// 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