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.
652 lines
22 KiB
652 lines
22 KiB
2 years ago
|
// Copyright(C) 1999-2022 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 <Ioss_Field.h> // for Field, etc
|
||
|
#include <Ioss_Map.h>
|
||
|
#include <Ioss_SmartAssert.h>
|
||
|
#include <Ioss_Sort.h>
|
||
|
#include <Ioss_Utils.h> // for IOSS_ERROR
|
||
|
#include <cstddef> // for size_t
|
||
|
#include <fmt/ostream.h>
|
||
|
#include <iterator> // for insert_iterator, inserter
|
||
|
#include <numeric>
|
||
|
#include <sstream>
|
||
|
#include <string>
|
||
|
#include <vector> // for vector, vector<>::iterator, etc
|
||
|
|
||
|
// If defined, then only build m_reverseMap when it is used.
|
||
|
#undef USE_LAZY_REVERSE
|
||
|
|
||
|
namespace {
|
||
|
template <typename INT> bool is_one2one(INT *ids, size_t num_to_get, size_t offset)
|
||
|
{
|
||
|
bool one2one = true;
|
||
|
INT map_offset = num_to_get > 0 ? ids[0] - 1 - offset : 0;
|
||
|
for (size_t i = 0; i < num_to_get; i++) {
|
||
|
if ((size_t)ids[i] != i + offset + 1 + map_offset) {
|
||
|
one2one = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return one2one;
|
||
|
}
|
||
|
} // namespace
|
||
|
|
||
|
void Ioss::Map::release_memory()
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
MapContainer().swap(m_map);
|
||
|
MapContainer().swap(m_reorder);
|
||
|
ReverseMapContainer().swap(m_reverse);
|
||
|
}
|
||
|
|
||
|
// Determines whether the input map is sequential (m_map[i] == i)
|
||
|
bool Ioss::Map::is_sequential(bool check_all) const
|
||
|
{
|
||
|
// Assumes the_map runs from [1..size) Slot zero will contain -1 if the
|
||
|
// vector is sequential; 1 if not sequential, and 0 if it has not
|
||
|
// yet been determined...
|
||
|
// Once the the_map has been determined to be sequential/not-sequential,
|
||
|
// slot zero is set appropriately.
|
||
|
// 'sequential' is defined here to mean i==the_map[i] for all
|
||
|
// 0<i<the_map.size()
|
||
|
|
||
|
// Arguably, an empty map is sequential...
|
||
|
if (m_map.empty()) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (!check_all) {
|
||
|
// Check slot zero...
|
||
|
if (m_map[0] == -1) {
|
||
|
return true;
|
||
|
}
|
||
|
if (m_map[0] == 1) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
auto &new_map = const_cast<Ioss::MapContainer &>(m_map);
|
||
|
size_t map_size = m_map.size();
|
||
|
if (m_offset == -1 && map_size > 1) {
|
||
|
m_offset = m_map[1] - 1;
|
||
|
}
|
||
|
for (int64_t i = 1; i < (int64_t)map_size; i++) {
|
||
|
if (m_map[i] != i + m_offset) {
|
||
|
new_map[0] = 1;
|
||
|
m_offset = -1;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
new_map[0] = -1;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Ioss::Map::set_size(size_t entity_count)
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
if (m_map.empty()) {
|
||
|
m_map.resize(entity_count + 1);
|
||
|
set_is_sequential(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Ioss::Map::build_reverse_map() { build_reverse_map(m_map.size() - 1, 0); }
|
||
|
void Ioss::Map::build_reverse_map_no_lock() { build_reverse_map__(m_map.size() - 1, 0); }
|
||
|
void Ioss::Map::build_reverse_map(int64_t num_to_get, int64_t offset)
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
build_reverse_map__(num_to_get, offset);
|
||
|
}
|
||
|
|
||
|
void Ioss::Map::build_reverse_map__(int64_t num_to_get, int64_t offset)
|
||
|
{
|
||
|
// Stored as an unordered map -- key:global_id, value:local_id
|
||
|
if (!is_sequential()) {
|
||
|
#if defined MAP_USE_SORTED_VECTOR
|
||
|
ReverseMapContainer new_ids;
|
||
|
if (m_reverse.empty()) {
|
||
|
// This is first time that the m_reverse map is being built..
|
||
|
// m_map is no longer 1-to-1.
|
||
|
// Just iterate m_map and add all values that are non-zero
|
||
|
new_ids.reserve(m_map.size() - 1);
|
||
|
for (size_t i = 1; i < m_map.size(); i++) {
|
||
|
if (m_map[i] != 0) {
|
||
|
new_ids.emplace_back(m_map[i], i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
new_ids.reserve(num_to_get);
|
||
|
for (int64_t i = 0; i < num_to_get; i++) {
|
||
|
int64_t local_id = offset + i + 1;
|
||
|
new_ids.emplace_back(m_map[local_id], local_id);
|
||
|
|
||
|
if (m_map[local_id] <= 0) {
|
||
|
std::ostringstream errmsg;
|
||
|
fmt::print(errmsg,
|
||
|
"\nERROR: {0} map detected non-positive global id {1} for {0} with local id {2} "
|
||
|
"on processor {3}.\n",
|
||
|
m_entityType, m_map[local_id], local_id, m_myProcessor);
|
||
|
IOSS_ERROR(errmsg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// new_ids is a vector of pairs <global_id, local_id>
|
||
|
Ioss::sort(new_ids);
|
||
|
|
||
|
int64_t new_id_min = new_ids.empty() ? 0 : new_ids.front().first;
|
||
|
int64_t old_id_max = m_reverse.empty() ? 0 : m_reverse.back().first;
|
||
|
if (new_ids.size() + 1 == m_map.size()) {
|
||
|
SMART_ASSERT(m_reverse.empty() || m_reverse.size() + 1 == m_map.size());
|
||
|
new_ids.swap(m_reverse);
|
||
|
}
|
||
|
else if (new_id_min > old_id_max) {
|
||
|
m_reverse.insert(m_reverse.end(), new_ids.begin(), new_ids.end());
|
||
|
}
|
||
|
else {
|
||
|
// Copy reverseElementMap to old_ids, empty reverseElementMap.
|
||
|
ReverseMapContainer old_ids;
|
||
|
old_ids.swap(m_reverse);
|
||
|
SMART_ASSERT(m_reverse.empty());
|
||
|
|
||
|
// Merge old_ids and new_ids to reverseElementMap.
|
||
|
m_reverse.reserve(old_ids.size() + new_ids.size());
|
||
|
std::merge(
|
||
|
old_ids.begin(), old_ids.end(), new_ids.begin(), new_ids.end(),
|
||
|
std::inserter(m_reverse, m_reverse.begin()),
|
||
|
[](const Ioss::IdPair &lhs, const Ioss::IdPair &rhs) { return lhs.first < rhs.first; });
|
||
|
}
|
||
|
|
||
|
// Check for duplicate ids...
|
||
|
// Maybe debug only...
|
||
|
verify_no_duplicate_ids(m_reverse);
|
||
|
#else
|
||
|
if (m_reverse.empty()) {
|
||
|
// This is first time that the m_reverse map is being built..
|
||
|
// m_map is no longer 1-to-1.
|
||
|
// Just iterate m_map and add all values that are non-zero
|
||
|
m_reverse.max_load_factor(0.95);
|
||
|
m_reverse.reserve(m_map.size());
|
||
|
for (size_t i = 1; i < m_map.size(); i++) {
|
||
|
if (m_map[i] != 0) {
|
||
|
bool ok = m_reverse.insert({m_map[i], i}).second;
|
||
|
if (!ok) {
|
||
|
std::ostringstream errmsg;
|
||
|
fmt::print(errmsg,
|
||
|
"\nERROR: Duplicate {0} global id detected on processor {1}, filename '{2}'.\n"
|
||
|
" Global id {3} assigned to local {0}s {4} and {5}.\n",
|
||
|
m_entityType, m_myProcessor, m_filename, m_map[i], i, m_reverse[m_map[i]]);
|
||
|
IOSS_ERROR(errmsg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for (int64_t i = 0; i < num_to_get; i++) {
|
||
|
int64_t local_id = offset + i + 1;
|
||
|
bool ok = m_reverse.insert({m_map[local_id], local_id}).second;
|
||
|
if (!ok) {
|
||
|
if (local_id != m_reverse[m_map[local_id]]) {
|
||
|
std::ostringstream errmsg;
|
||
|
fmt::print(errmsg,
|
||
|
"\nERROR: Duplicate {0} global id detected on processor {1}, filename '{2}'.\n"
|
||
|
" Global id {3} assigned to local {0}s {4} and {5}.\n",
|
||
|
m_entityType, m_myProcessor, m_filename, m_map[local_id], local_id,
|
||
|
m_reverse[m_map[local_id]]);
|
||
|
IOSS_ERROR(errmsg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (m_map[local_id] <= 0) {
|
||
|
std::ostringstream errmsg;
|
||
|
fmt::print(errmsg,
|
||
|
"\nERROR: {0} map detected non-positive global id {1} for {0} with local id {2} "
|
||
|
"on processor {3}.\n",
|
||
|
m_entityType, m_map[local_id], local_id, m_myProcessor);
|
||
|
IOSS_ERROR(errmsg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#if IOSS_DEBUG_OUTPUT
|
||
|
fmt::print("[{}] ({}) Map Size = {}\n", m_myProcessor, m_entityType, m_map.size());
|
||
|
fmt::print("[{}] ({}) Size = {}\n", m_myProcessor, m_entityType, m_reverse.size());
|
||
|
fmt::print("[{}] ({}) Bucket Count = {}\n", m_myProcessor, m_entityType, m_reverse.bucket_count());
|
||
|
fmt::print("[{}] ({}) Load Factor = {}\n", m_myProcessor, m_entityType, m_reverse.load_factor());
|
||
|
fmt::print("[{}] ({}) Max Load Factor = {}\n\n", m_myProcessor, m_entityType, m_reverse.max_load_factor());
|
||
|
#endif
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined MAP_USE_SORTED_VECTOR
|
||
|
void Ioss::Map::verify_no_duplicate_ids(std::vector<Ioss::IdPair> &reverse_map)
|
||
|
{
|
||
|
// Check for duplicate ids...
|
||
|
auto dup = std::adjacent_find(
|
||
|
reverse_map.begin(), reverse_map.end(),
|
||
|
[](const Ioss::IdPair &lhs, const Ioss::IdPair &rhs) { return lhs.first == rhs.first; });
|
||
|
if (dup != reverse_map.end()) {
|
||
|
auto other = dup + 1;
|
||
|
std::ostringstream errmsg;
|
||
|
fmt::print(errmsg,
|
||
|
"\nERROR: Duplicate {0} global id detected on processor {1}, filename '{2}'.\n"
|
||
|
" Global id {3} assigned to local {0}s {4} and {5}.\n",
|
||
|
m_entityType, m_myProcessor, m_filename, (*dup).first, (*dup).second,
|
||
|
(*other).second);
|
||
|
IOSS_ERROR(errmsg);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
template IOSS_EXPORT bool Ioss::Map::set_map(int *ids, size_t count, size_t offset,
|
||
|
bool in_define_mode);
|
||
|
template IOSS_EXPORT bool Ioss::Map::set_map(int64_t *ids, size_t count, size_t offset,
|
||
|
bool in_define_mode);
|
||
|
|
||
|
template <typename INT>
|
||
|
bool Ioss::Map::set_map(INT *ids, size_t count, size_t offset, bool in_define_mode)
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
if (in_define_mode && is_sequential()) {
|
||
|
// If the current map is one-to-one, check whether it will be one-to-one
|
||
|
// after adding these ids...
|
||
|
bool one2one = is_one2one(ids, count, offset);
|
||
|
if (one2one) {
|
||
|
// Further checks on how ids fit into previously set m_map entries (if any)
|
||
|
if (count > 0) {
|
||
|
INT tmp_offset = ids[0] - 1 - offset;
|
||
|
if (tmp_offset < 0 || (m_offset >= 0 && tmp_offset != m_offset)) {
|
||
|
one2one = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!one2one) {
|
||
|
// Up to this point, the id map has been one-to-one. Once we
|
||
|
// apply these `ids` to `m_map`, the map will no
|
||
|
// longer be one-to-one. The main consequence of this is that we
|
||
|
// now need an explicit reverseMap. The reverseMap is built
|
||
|
// incrementally with the current range of 'ids', but before
|
||
|
// that can be done, need to build a reverseMap of the current
|
||
|
// one-to-one data...
|
||
|
set_is_sequential(false);
|
||
|
#if !defined USE_LAZY_REVERSE
|
||
|
if (m_map.size() - 1 > count) {
|
||
|
build_reverse_map__(m_map.size() - 1, 0);
|
||
|
}
|
||
|
#endif
|
||
|
m_offset = 0;
|
||
|
}
|
||
|
else {
|
||
|
// Map is sequential beginning at ids[0]
|
||
|
if (count > 0) {
|
||
|
m_offset = ids[0] - 1 - offset;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Determine if `changed` which means an entity was redefined...
|
||
|
// This is used to determine whether a `reorder` map is needed.
|
||
|
bool changed = false; // True if redefining an entry
|
||
|
for (size_t i = 0; i < count; i++) {
|
||
|
int64_t local_id = offset + i + 1;
|
||
|
SMART_ASSERT((size_t)local_id < m_map.size())(local_id)(m_map.size());
|
||
|
if (m_map[local_id] > 0 && m_map[local_id] != ids[i]) {
|
||
|
changed = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined USE_LAZY_REVERSE
|
||
|
// Build this now before we redefine an entry
|
||
|
if (!in_define_mode && changed) {
|
||
|
build_reverse_map__(m_map.size() - 1, 0);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for (size_t i = 0; i < count; i++) {
|
||
|
int64_t local_id = offset + i + 1;
|
||
|
SMART_ASSERT((size_t)local_id < m_map.size())(local_id)(m_map.size());
|
||
|
|
||
|
if (ids[i] <= 0) {
|
||
|
std::ostringstream errmsg;
|
||
|
fmt::print(errmsg,
|
||
|
"\nERROR: {} mapping routines detected non-positive global id {}"
|
||
|
" for local id {} on processor {}, filename '{}'.\n",
|
||
|
m_entityType, ids[i], local_id, m_myProcessor, m_filename);
|
||
|
IOSS_ERROR(errmsg);
|
||
|
}
|
||
|
|
||
|
m_map[local_id] = ids[i];
|
||
|
if (local_id != ids[i] - m_offset) {
|
||
|
set_is_sequential(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (in_define_mode) {
|
||
|
if (changed) {
|
||
|
m_reverse.clear();
|
||
|
}
|
||
|
#if !defined USE_LAZY_REVERSE
|
||
|
build_reverse_map__(count, offset);
|
||
|
#endif
|
||
|
}
|
||
|
else if (changed) {
|
||
|
// Build the reorderEntityMap which does a direct mapping from
|
||
|
// the current topologies local order to the local order
|
||
|
// stored in the database if these two orders are different, that
|
||
|
// is if the ids order was redefined after the STATE_MODEL
|
||
|
// phase... This is 0-based and used for
|
||
|
// remapping output and input TRANSIENT fields.
|
||
|
build_reorder_map__(offset, count);
|
||
|
}
|
||
|
return changed;
|
||
|
}
|
||
|
|
||
|
void Ioss::Map::set_default(size_t count, size_t offset)
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
m_map.resize(count + 1);
|
||
|
for (size_t i = 1; i <= count; i++) {
|
||
|
m_map[i] = i + offset;
|
||
|
}
|
||
|
set_is_sequential(true);
|
||
|
}
|
||
|
|
||
|
#ifndef DOXYGEN_SKIP_THIS
|
||
|
template IOSS_EXPORT void Ioss::Map::reverse_map_data(int *data, size_t count) const;
|
||
|
template IOSS_EXPORT void Ioss::Map::reverse_map_data(int64_t *data, size_t count) const;
|
||
|
#endif
|
||
|
|
||
|
template <typename INT> void Ioss::Map::reverse_map_data(INT *data, size_t count) const
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
if (!is_sequential()) {
|
||
|
for (size_t i = 0; i < count; i++) {
|
||
|
INT global_id = data[i];
|
||
|
data[i] = (INT)global_to_local__(global_id, true);
|
||
|
}
|
||
|
}
|
||
|
else if (m_offset != 0) {
|
||
|
for (size_t i = 0; i < count; i++) {
|
||
|
data[i] -= m_offset;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Ioss::Map::reverse_map_data(void *data, const Ioss::Field &field, size_t count) const
|
||
|
{
|
||
|
if (field.get_type() == Ioss::Field::INTEGER) {
|
||
|
int *connect = static_cast<int *>(data);
|
||
|
reverse_map_data(connect, count);
|
||
|
}
|
||
|
else {
|
||
|
auto *connect = static_cast<int64_t *>(data);
|
||
|
reverse_map_data(connect, count);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifndef DOXYGEN_SKIP_THIS
|
||
|
template IOSS_EXPORT void Ioss::Map::map_data(int *data, size_t count) const;
|
||
|
template IOSS_EXPORT void Ioss::Map::map_data(int64_t *data, size_t count) const;
|
||
|
#endif
|
||
|
|
||
|
template <typename INT> void Ioss::Map::map_data(INT *data, size_t count) const
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
if (!is_sequential()) {
|
||
|
for (size_t i = 0; i < count; i++) {
|
||
|
data[i] = (INT)m_map[data[i]];
|
||
|
}
|
||
|
}
|
||
|
else if (m_offset != 0) {
|
||
|
for (size_t i = 0; i < count; i++) {
|
||
|
data[i] += m_offset;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Ioss::Map::map_data(void *data, const Ioss::Field &field, size_t count) const
|
||
|
{
|
||
|
if (field.get_type() == Ioss::Field::INTEGER) {
|
||
|
int *datum = static_cast<int *>(data);
|
||
|
map_data(datum, count);
|
||
|
}
|
||
|
else {
|
||
|
auto *datum = static_cast<int64_t *>(data);
|
||
|
map_data(datum, count);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifndef DOXYGEN_SKIP_THIS
|
||
|
template IOSS_EXPORT void Ioss::Map::map_implicit_data(int *data, size_t count,
|
||
|
size_t offset) const;
|
||
|
template IOSS_EXPORT void Ioss::Map::map_implicit_data(int64_t *data, size_t count,
|
||
|
size_t offset) const;
|
||
|
#endif
|
||
|
|
||
|
template <typename INT>
|
||
|
void Ioss::Map::map_implicit_data(INT *ids, size_t count, size_t offset) const
|
||
|
{
|
||
|
// Map the "local" ids (offset+1..offset+count) to the global ids. The local
|
||
|
// ids are implicit
|
||
|
if (is_sequential()) {
|
||
|
for (size_t i = 0; i < count; i++) {
|
||
|
ids[i] = static_cast<INT>(m_offset + offset + 1 + i);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for (size_t i = 0; i < count; i++) {
|
||
|
ids[i] = static_cast<INT>(m_map[offset + 1 + i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Ioss::Map::map_implicit_data(void *data, const Ioss::Field &field, size_t count,
|
||
|
size_t offset) const
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
if (field.get_type() == Ioss::Field::INTEGER) {
|
||
|
map_implicit_data(static_cast<int *>(data), count, offset);
|
||
|
}
|
||
|
else {
|
||
|
map_implicit_data(static_cast<int64_t *>(data), count, offset);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template IOSS_EXPORT size_t Ioss::Map::map_field_to_db_scalar_order(double *variables,
|
||
|
std::vector<double> &db_var,
|
||
|
size_t begin_offset,
|
||
|
size_t count, size_t stride,
|
||
|
size_t offset);
|
||
|
template IOSS_EXPORT size_t Ioss::Map::map_field_to_db_scalar_order(int *variables,
|
||
|
std::vector<double> &db_var,
|
||
|
size_t begin_offset,
|
||
|
size_t count, size_t stride,
|
||
|
size_t offset);
|
||
|
template IOSS_EXPORT size_t Ioss::Map::map_field_to_db_scalar_order(int64_t *variables,
|
||
|
std::vector<double> &db_var,
|
||
|
size_t begin_offset,
|
||
|
size_t count, size_t stride,
|
||
|
size_t offset);
|
||
|
|
||
|
template <typename T>
|
||
|
size_t Ioss::Map::map_field_to_db_scalar_order(T *variables, std::vector<double> &db_var,
|
||
|
size_t begin_offset, size_t count, size_t stride,
|
||
|
size_t offset)
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
size_t num_out = 0;
|
||
|
if (!m_reorder.empty()) {
|
||
|
size_t k = offset;
|
||
|
for (size_t j = begin_offset; j < count * stride; j += stride) {
|
||
|
// Map to storage location.
|
||
|
int64_t where = m_reorder[k++] - offset;
|
||
|
if (where >= 0) {
|
||
|
SMART_ASSERT(where < (int64_t)count)(where)(count);
|
||
|
db_var[where] = variables[j];
|
||
|
num_out++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
size_t k = 0;
|
||
|
for (size_t j = begin_offset; j < count * stride; j += stride) {
|
||
|
// Map to storage location.
|
||
|
db_var[k++] = variables[j];
|
||
|
}
|
||
|
num_out = count;
|
||
|
}
|
||
|
return num_out;
|
||
|
}
|
||
|
|
||
|
void Ioss::Map::build_reorder_map__(int64_t start, int64_t count)
|
||
|
{
|
||
|
// This routine builds a map that relates the current node id order
|
||
|
// to the original node ordering in affect at the time the file was
|
||
|
// created. That is, the node map used to define the topology of the
|
||
|
// model. Now, if there are changes in node ordering at the
|
||
|
// application level, we build the node reorder map to map the
|
||
|
// current order into the original order. An added complication is
|
||
|
// that this is more than just a reordering... It may be that the
|
||
|
// application has 'ghosted' nodes that it doesn't want to put out on
|
||
|
// the database, so the reorder map must handle a node that is not
|
||
|
// in the original mesh and map that to an invalid value (currently
|
||
|
// using -1 as invalid value...)
|
||
|
|
||
|
// Note: To further add confusion, the reorder map is 0-based
|
||
|
// and the reverse map and 'map' are 1-based. This is
|
||
|
// just a consequence of how they are intended to be used...
|
||
|
//
|
||
|
// `start` is based on a 0-based array -- start of the reorderMap to build.
|
||
|
|
||
|
if (m_reorder.empty()) {
|
||
|
// See if actually need a reorder map first...
|
||
|
bool need_reorder_map = false;
|
||
|
if (m_reverse.empty()) {
|
||
|
need_reorder_map = true;
|
||
|
}
|
||
|
else {
|
||
|
int64_t my_end = start + count;
|
||
|
for (int64_t i = start; i < my_end; i++) {
|
||
|
int64_t global_id = m_map[i + 1];
|
||
|
int64_t orig_local_id = global_to_local__(global_id) - 1;
|
||
|
|
||
|
// The reordering should only be a permutation of the original
|
||
|
// ordering within this entity block...
|
||
|
SMART_ASSERT(orig_local_id >= start && orig_local_id <= my_end)
|
||
|
(orig_local_id)(start)(my_end);
|
||
|
if (i != orig_local_id) {
|
||
|
need_reorder_map = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (need_reorder_map) {
|
||
|
int64_t map_size = m_map.size() - 1;
|
||
|
m_reorder.resize(map_size);
|
||
|
// If building a partial reorder map, assume all entries
|
||
|
// are a direct 1-1 and then let the partial fills overwrite
|
||
|
// if needed.
|
||
|
std::iota(m_reorder.begin(), m_reorder.end(), 0);
|
||
|
}
|
||
|
else {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int64_t my_end = start + count;
|
||
|
for (int64_t i = start; i < my_end; i++) {
|
||
|
int64_t global_id = m_map[i + 1];
|
||
|
int64_t orig_local_id = global_to_local__(global_id) - 1;
|
||
|
|
||
|
// The reordering should only be a permutation of the original
|
||
|
// ordering within this entity block...
|
||
|
SMART_ASSERT(orig_local_id >= start && orig_local_id <= my_end)(orig_local_id)(start)(my_end);
|
||
|
m_reorder[i] = orig_local_id;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Node and Element mapping functions. The ExodusII database
|
||
|
// stores ids in a local-id system (1..NUMNP), (1..NUMEL) but
|
||
|
// Sierra wants entities in a global system. These routines
|
||
|
// take care of the mapping from local <-> global
|
||
|
|
||
|
int64_t Ioss::Map::global_to_local(int64_t global, bool must_exist) const
|
||
|
{
|
||
|
IOSS_FUNC_ENTER(m_);
|
||
|
return global_to_local__(global, must_exist);
|
||
|
}
|
||
|
|
||
|
int64_t Ioss::Map::global_to_local__(int64_t global, bool must_exist) const
|
||
|
{
|
||
|
int64_t local = global;
|
||
|
#if defined USE_LAZY_REVERSE
|
||
|
if (!is_sequential() && m_reverse.empty() && m_reorder.empty()) {
|
||
|
auto *new_this = const_cast<Ioss::Map *>(this);
|
||
|
new_this->build_reverse_map_no_lock();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (!is_sequential() && !m_reverse.empty()) {
|
||
|
// Possible for !is_sequential() which means non-one-to-one, but
|
||
|
// reverseMap is empty (which implied one-to-one) if the ORIGINAL mapping defined
|
||
|
// during dbState == STATE_MODEL was one-to-one, but there is a
|
||
|
// reordering which is due to new id ordering defined after STATE_MODEL...
|
||
|
#if defined MAP_USE_SORTED_VECTOR
|
||
|
auto iter = std::lower_bound(
|
||
|
m_reverse.begin(), m_reverse.end(), global,
|
||
|
[](const Ioss::IdPair &lhs, int64_t val) -> bool { return lhs.first < val; });
|
||
|
if (iter != m_reverse.end() && iter->first == global) {
|
||
|
if (iter != m_reverse.end()) {
|
||
|
local = iter->second;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
local = 0;
|
||
|
}
|
||
|
#else
|
||
|
auto iter = m_reverse.find(global);
|
||
|
if (iter != m_reverse.end()) {
|
||
|
local = iter->second;
|
||
|
}
|
||
|
else {
|
||
|
local = 0;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
else if (!must_exist && global > static_cast<int64_t>(m_map.size()) - 1) {
|
||
|
local = 0;
|
||
|
}
|
||
|
else {
|
||
|
local = global - m_offset;
|
||
|
}
|
||
|
if (local > static_cast<int64_t>(m_map.size()) - 1) {
|
||
|
std::ostringstream errmsg;
|
||
|
fmt::print(errmsg,
|
||
|
"ERROR: Ioss Mapping routines detected {0} with global id equal to {1} returns a "
|
||
|
"local id of {2} which is\n"
|
||
|
"larger than the local {0} count {5} on processor {3}, filename '{4}'.\n"
|
||
|
"This should not happen, please report.\n",
|
||
|
m_entityType, global, local, m_myProcessor, m_filename, m_map.size() - 1);
|
||
|
IOSS_ERROR(errmsg);
|
||
|
}
|
||
|
else if (local <= 0 && must_exist) {
|
||
|
std::ostringstream errmsg;
|
||
|
fmt::print(errmsg,
|
||
|
"ERROR: Ioss Mapping routines could not find a {0} with global id equal to {1} in "
|
||
|
"the {0} map\n"
|
||
|
"on processor {2}, filename '{3}'.\n"
|
||
|
"This should not happen, please report.\n",
|
||
|
m_entityType, global, m_myProcessor, m_filename);
|
||
|
IOSS_ERROR(errmsg);
|
||
|
}
|
||
|
return local;
|
||
|
}
|