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.
559 lines
19 KiB
559 lines
19 KiB
// Copyright(C) 2021, 2022, 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 <numeric>
|
|
|
|
#include "Cell.h"
|
|
|
|
#include <Ioss_NodeBlock.h>
|
|
#include <Ioss_SmartAssert.h>
|
|
#include <algorithm>
|
|
#include <fmt/format.h>
|
|
#include <string>
|
|
|
|
//! \file
|
|
extern unsigned int debug_level;
|
|
|
|
template <> struct fmt::formatter<Loc> : formatter<std::string>
|
|
{
|
|
// parse is inherited from formatter<std::string>.
|
|
template <typename FormatContext> auto format(Loc l, FormatContext &ctx)
|
|
{
|
|
std::string name = "unknown";
|
|
switch (l) {
|
|
case Loc::C: name = "Center"; break;
|
|
case Loc::BL: name = "Bottom Left"; break;
|
|
case Loc::B: name = "Bottom"; break;
|
|
case Loc::BR: name = "Bottom Right"; break;
|
|
case Loc::L: name = "Left"; break;
|
|
case Loc::R: name = "Right"; break;
|
|
case Loc::TL: name = "Top Left"; break;
|
|
case Loc::T: name = "Top"; break;
|
|
case Loc::TR: name = "Top Right"; break;
|
|
}
|
|
return formatter<std::string>::format(name, ctx);
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
// Iterate over the interior nodes on the specified face. Skips
|
|
// the corner nodes on the I-J intersections. Processes I-K and
|
|
// J-K corners.
|
|
template <typename INT>
|
|
void process_face_nodes(const std::vector<INT> &node_map, std::vector<INT> &nodes,
|
|
std::vector<INT> &procs, const std::vector<int64_t> &face_nodes,
|
|
size_t KK, int rank)
|
|
{
|
|
for (size_t i = KK; i < face_nodes.size() - KK; i++) {
|
|
auto idx = face_nodes[i];
|
|
nodes.push_back(node_map[idx + 1]);
|
|
procs.push_back(rank);
|
|
}
|
|
}
|
|
|
|
// Iterate over the specified corner nodes.
|
|
template <typename INT>
|
|
void process_corner_nodes(const std::vector<INT> &node_map, std::vector<INT> &nodes,
|
|
std::vector<INT> &procs, const std::vector<int64_t> &face_nodes,
|
|
size_t KK, int rank, Loc location)
|
|
{
|
|
if (location == Loc::BL || location == Loc::TL) {
|
|
for (size_t i = 0; i < KK; i++) {
|
|
auto idx = face_nodes[i];
|
|
nodes.push_back(node_map[idx + 1]);
|
|
procs.push_back(rank);
|
|
}
|
|
}
|
|
else {
|
|
for (size_t i = face_nodes.size() - KK; i < face_nodes.size(); i++) {
|
|
auto idx = face_nodes[i];
|
|
nodes.push_back(node_map[idx + 1]);
|
|
procs.push_back(rank);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return a vector (possibly empty) of the ranks of the cells that surround this
|
|
// cell. Includes the rank that this cell is on (cell_ranks[0]).
|
|
std::vector<int> get_shared_ranks(const std::array<int, 9> &cell_ranks)
|
|
{
|
|
std::vector<int> ranks(9);
|
|
std::copy(cell_ranks.begin(), cell_ranks.end(), ranks.begin());
|
|
|
|
// Set all `-1` (non-neighbor) values to match center rank...
|
|
for (auto &r : ranks) {
|
|
if (r == -1) {
|
|
r = cell_ranks[(int)Loc::C];
|
|
}
|
|
}
|
|
Ioss::Utils::uniquify(ranks);
|
|
return ranks;
|
|
}
|
|
} // namespace
|
|
|
|
void Cell::initialize(size_t i, size_t j, std::shared_ptr<UnitCell> unit_cell)
|
|
{
|
|
m_i = i;
|
|
m_j = j;
|
|
m_unitCell = unit_cell;
|
|
|
|
// These are not necessarily the correct ranks, but at this point can determine
|
|
// Whether this cell is surrounded by other cells, or is on the boundary.
|
|
set_rank(Loc::C, 0);
|
|
set_rank(Loc::L, m_i > 0 ? 0 : -1);
|
|
set_rank(Loc::B, m_j > 0 ? 0 : -1);
|
|
set_rank(Loc::BL, (m_i > 0 && m_j > 0) ? 0 : -1);
|
|
}
|
|
|
|
std::pair<double, double> Cell::get_coordinate_range(enum Axis axis) const
|
|
{
|
|
if (axis == Axis::X) {
|
|
return m_unitCell->minmax_x;
|
|
}
|
|
if (axis == Axis::Y) {
|
|
return m_unitCell->minmax_y;
|
|
}
|
|
return std::make_pair(0.0, 0.0);
|
|
}
|
|
|
|
// Number of nodes that will be added to global node count when this cell is added to
|
|
// grid -- accounts for coincident nodes if cell has neighbor(s)
|
|
size_t Cell::added_node_count(enum Mode mode, bool equivalence_nodes) const
|
|
{
|
|
// If no neighbors (to -I, -J), then all nodes would be added...
|
|
auto count = m_unitCell->m_region->get_property("node_count").get_int();
|
|
|
|
if (equivalence_nodes) {
|
|
if (mode == Mode::GLOBAL) {
|
|
if (has_neighbor_i()) {
|
|
count -= (m_unitCell->cell_JJ * m_unitCell->cell_KK);
|
|
}
|
|
|
|
if (has_neighbor_j()) {
|
|
count -= (m_unitCell->cell_II * m_unitCell->cell_KK);
|
|
}
|
|
|
|
if (has_neighbor_i() && has_neighbor_j()) {
|
|
count += m_unitCell->cell_KK;
|
|
}
|
|
}
|
|
else if (mode == Mode::PROCESSOR) {
|
|
if (has_neighbor_i() && !processor_boundary(Loc::L)) {
|
|
count -= (m_unitCell->cell_JJ * m_unitCell->cell_KK);
|
|
}
|
|
|
|
if (has_neighbor_j() && !processor_boundary(Loc::B)) {
|
|
count -= (m_unitCell->cell_II * m_unitCell->cell_KK);
|
|
}
|
|
|
|
if (has_neighbor_i() && has_neighbor_j() && !processor_boundary(Loc::L) &&
|
|
!processor_boundary(Loc::B)) {
|
|
count += m_unitCell->cell_KK;
|
|
}
|
|
|
|
// Now the "corner case" ;-) If there is a processor boundary below, but the cell to the BL is
|
|
// on the same rank as this cell, then we have already counted the IJ-line nodes, so need to
|
|
// subtract that count...
|
|
if (processor_boundary(Loc::B) && processor_boundary(Loc::L) &&
|
|
rank(Loc::BL) == rank(Loc::C)) {
|
|
count -= m_unitCell->cell_KK;
|
|
}
|
|
|
|
// Now the other "corner case"
|
|
if (processor_boundary(Loc::B) && rank(Loc::BR) == rank(Loc::C)) {
|
|
count -= m_unitCell->cell_KK;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
std::array<int, 9> Cell::categorize_processor_boundary_nodes(int the_rank) const
|
|
{
|
|
// Create a "unit cell" to categorize processor boundary nodes...
|
|
std::array<int, 9> bnd_nodes{0};
|
|
|
|
// Bottom...
|
|
if (rank(Loc::B) == the_rank) {
|
|
bnd_nodes[(int)Loc::B] = 1;
|
|
if ((rank(Loc::BL) != rank(Loc::C)) && (rank(Loc::L) != rank(Loc::C))) {
|
|
bnd_nodes[(int)Loc::BL] = 1;
|
|
}
|
|
if (rank(Loc::BR) != rank(Loc::C)) {
|
|
bnd_nodes[(int)Loc::BR] = 1;
|
|
}
|
|
}
|
|
|
|
// Left
|
|
if (rank(Loc::L) == the_rank) {
|
|
bnd_nodes[(int)Loc::L] = 1;
|
|
bnd_nodes[(int)Loc::TL] = 1;
|
|
if ((rank(Loc::BL) != rank(Loc::C)) && (rank(Loc::B) != rank(Loc::C))) {
|
|
bnd_nodes[(int)Loc::BL] = 1;
|
|
}
|
|
}
|
|
|
|
// Top
|
|
if (rank(Loc::T) == the_rank) {
|
|
bnd_nodes[(int)Loc::T] = 1;
|
|
bnd_nodes[(int)Loc::TR] = 1;
|
|
if (rank(Loc::L) != rank(Loc::C)) {
|
|
bnd_nodes[(int)Loc::TL] = 1;
|
|
}
|
|
}
|
|
|
|
// Right
|
|
if (rank(Loc::R) == the_rank) {
|
|
bnd_nodes[(int)Loc::R] = 1;
|
|
bnd_nodes[(int)Loc::TR] = 1;
|
|
if ((rank(Loc::BR) != rank(Loc::C)) && (rank(Loc::B) != rank(Loc::C))) {
|
|
bnd_nodes[(int)Loc::BR] = 1;
|
|
}
|
|
}
|
|
|
|
// Bottom Left
|
|
if (rank(Loc::BL) == the_rank) {
|
|
// If left and bottom *don't* match the_rank, then need to add this node
|
|
if ((rank(Loc::L) != the_rank) && (rank(Loc::B) != the_rank) &&
|
|
(rank(Loc::L) != rank(Loc::C)) && (rank(Loc::B) != rank(Loc::C))) {
|
|
bnd_nodes[(int)Loc::BL] = 1;
|
|
}
|
|
}
|
|
|
|
// Bottom Right
|
|
if (rank(Loc::BR) == the_rank) {
|
|
// If bottom *doesn't* match the_rank, then need to add this node
|
|
if ((rank(Loc::B) != the_rank) && (rank(Loc::B) != rank(Loc::C))) {
|
|
bnd_nodes[(int)Loc::BR] = 1;
|
|
}
|
|
}
|
|
|
|
// Top Left
|
|
if (rank(Loc::TL) == the_rank) {
|
|
// If left *doesn't* match the_rank, then need to add this node
|
|
if ((rank(Loc::L) != the_rank) && (rank(Loc::L) != rank(Loc::C))) {
|
|
bnd_nodes[(int)Loc::TL] = 1;
|
|
}
|
|
}
|
|
|
|
// Top Right
|
|
if (rank(Loc::TR) == the_rank) {
|
|
bnd_nodes[(int)Loc::TR] = 1;
|
|
}
|
|
|
|
return bnd_nodes;
|
|
}
|
|
|
|
size_t Cell::processor_boundary_node_count() const
|
|
{
|
|
// Get list of ranks that this cell shares nodes with...
|
|
auto ranks = get_shared_ranks(m_ranks);
|
|
if (ranks.size() == 1) {
|
|
// `ranks` contains center node, so if size == 1, does not touch
|
|
// any other processor.
|
|
return 0;
|
|
}
|
|
|
|
// Iterate `ranks` and for each rank, "color" the `bnd_nodes` that that rank touches...
|
|
// Skip center.
|
|
size_t b_count = 0;
|
|
for (int i = 0; i < (int)ranks.size(); i++) {
|
|
auto the_rank = ranks[i];
|
|
if (the_rank == rank(Loc::C)) {
|
|
continue;
|
|
}
|
|
|
|
// a "unit cell" categorizing processor boundary nodes...
|
|
// Size is 9. Value is '1' if nodes at this location are shared with rank `rank`
|
|
auto bnd_nodes = categorize_processor_boundary_nodes(the_rank);
|
|
|
|
// Now count how many nodes we have added...
|
|
// Edges (B, T, L, R) without corners
|
|
b_count += (m_unitCell->cell_II - 2) * (bnd_nodes[(int)Loc::B] + bnd_nodes[(int)Loc::T]);
|
|
b_count += (m_unitCell->cell_JJ - 2) * (bnd_nodes[(int)Loc::L] + bnd_nodes[(int)Loc::R]);
|
|
|
|
// Now the corners (BL, BR, TL, TR)
|
|
b_count += bnd_nodes[(int)Loc::BL] + bnd_nodes[(int)Loc::BR] + bnd_nodes[(int)Loc::TL] +
|
|
bnd_nodes[(int)Loc::TR];
|
|
}
|
|
|
|
// The counts above only account for a single KK plane. Now multiply by `m_unitCell->KK` to get
|
|
// total count.
|
|
b_count *= m_unitCell->cell_KK;
|
|
m_communicationNodeCount = b_count;
|
|
return b_count;
|
|
}
|
|
|
|
template void Cell::populate_node_communication_map(const std::vector<int> &node_map,
|
|
std::vector<int> &nodes,
|
|
std::vector<int> &procs) const;
|
|
template void Cell::populate_node_communication_map(const std::vector<int64_t> &node_map,
|
|
std::vector<int64_t> &nodes,
|
|
std::vector<int64_t> &procs) const;
|
|
|
|
template <typename INT>
|
|
void Cell::populate_node_communication_map(const std::vector<INT> &node_map,
|
|
std::vector<INT> &nodes, std::vector<INT> &procs) const
|
|
{
|
|
if (m_communicationNodeCount == 0) {
|
|
return;
|
|
}
|
|
|
|
nodes.reserve(m_communicationNodeCount);
|
|
procs.reserve(m_communicationNodeCount);
|
|
|
|
// Get list of ranks that this cell shares nodes with...
|
|
auto ranks = get_shared_ranks(m_ranks);
|
|
SMART_ASSERT(ranks.size() > 1);
|
|
|
|
auto KK = m_unitCell->cell_KK;
|
|
|
|
for (auto shared_rank : ranks) {
|
|
if (shared_rank == rank(Loc::C)) {
|
|
continue;
|
|
}
|
|
|
|
// a "unit cell" categorizing processor boundary nodes...
|
|
// Size is 9. Value is '1' if nodes at this location are shared with rank `rank`
|
|
auto bnd_nodes = categorize_processor_boundary_nodes(shared_rank);
|
|
|
|
// Handle Edges, but skip nodes on corners. They are handled later.
|
|
if (bnd_nodes[(int)Loc::B] == 1) {
|
|
process_face_nodes(node_map, nodes, procs, m_unitCell->min_J_face, KK, shared_rank);
|
|
}
|
|
|
|
if (bnd_nodes[(int)Loc::T] == 1) {
|
|
process_face_nodes(node_map, nodes, procs, m_unitCell->max_J_face, KK, shared_rank);
|
|
}
|
|
|
|
if (bnd_nodes[(int)Loc::L] == 1) {
|
|
process_face_nodes(node_map, nodes, procs, m_unitCell->min_I_face, KK, shared_rank);
|
|
}
|
|
|
|
if (bnd_nodes[(int)Loc::R] == 1) {
|
|
process_face_nodes(node_map, nodes, procs, m_unitCell->max_I_face, KK, shared_rank);
|
|
}
|
|
|
|
// Now the corners...
|
|
if (bnd_nodes[(int)Loc::BL] == 1) {
|
|
process_corner_nodes(node_map, nodes, procs, m_unitCell->min_J_face, KK, shared_rank,
|
|
Loc::BL);
|
|
}
|
|
|
|
if (bnd_nodes[(int)Loc::BR] == 1) {
|
|
process_corner_nodes(node_map, nodes, procs, m_unitCell->min_J_face, KK, shared_rank,
|
|
Loc::BR);
|
|
}
|
|
|
|
if (bnd_nodes[(int)Loc::TL] == 1) {
|
|
process_corner_nodes(node_map, nodes, procs, m_unitCell->max_J_face, KK, shared_rank,
|
|
Loc::TL);
|
|
}
|
|
|
|
if (bnd_nodes[(int)Loc::TR] == 1) {
|
|
process_corner_nodes(node_map, nodes, procs, m_unitCell->max_J_face, KK, shared_rank,
|
|
Loc::TR);
|
|
}
|
|
}
|
|
SMART_ASSERT(nodes.size() == procs.size())(nodes.size())(procs.size());
|
|
SMART_ASSERT(nodes.size() == m_communicationNodeCount)(nodes.size())(m_communicationNodeCount);
|
|
}
|
|
|
|
std::vector<int> Cell::categorize_nodes(enum Mode mode) const
|
|
{
|
|
auto nodes = m_unitCell->categorize_nodes(has_neighbor_i(), has_neighbor_j());
|
|
if (mode == Mode::PROCESSOR) {
|
|
// If there is a processor boundary to the left, then need to change categorization of
|
|
// all nodes on the left to '0'
|
|
if (processor_boundary(Loc::L)) {
|
|
const auto &min_I_face = m_unitCell->min_I_face;
|
|
for (const auto &node : min_I_face) {
|
|
nodes[node] -= 1;
|
|
}
|
|
}
|
|
if (processor_boundary(Loc::B)) {
|
|
const auto &min_J_face = m_unitCell->min_J_face;
|
|
for (const auto &node : min_J_face) {
|
|
nodes[node] -= 2;
|
|
}
|
|
}
|
|
|
|
// Now the "corner case" ;-) If there is a processor boundary below, but the cell to the BL is
|
|
// on the same rank as this cell, then we have already counted the IJ-line nodes, so need to
|
|
// categorize those nodes as already accounted for in a previous map...
|
|
if (processor_boundary(Loc::B) && processor_boundary(Loc::L) && rank(Loc::BL) == rank(Loc::C)) {
|
|
// Want KK() nodes -- First KK of min_i and of min_j. But since they match, can "unzero"
|
|
// min_i[0..KK)
|
|
for (size_t i = 0; i < m_unitCell->cell_KK; i++) {
|
|
nodes[m_unitCell->min_I_face[i]] = -1;
|
|
}
|
|
}
|
|
// Now the other "corner case"
|
|
if (processor_boundary(Loc::B) && rank(Loc::BR) == rank(Loc::C)) {
|
|
// Want KK() nodes -- First KK of max_i and Last KK of min_j. But since they match, can
|
|
// "unzero" max_i[0..KK)
|
|
for (size_t i = 0; i < m_unitCell->cell_KK; i++) {
|
|
nodes[m_unitCell->max_I_face[i]] = -1;
|
|
}
|
|
}
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
template std::vector<int64_t> Cell::generate_node_map(Mode, bool, int64_t) const;
|
|
template std::vector<int> Cell::generate_node_map(Mode, bool, int) const;
|
|
|
|
template <typename INT>
|
|
std::vector<INT> Cell::generate_node_map(Mode mode, bool equivalence_nodes, INT /*dummy*/) const
|
|
{
|
|
// Size is node_count + 1 to handle the 1-based connectivity values.
|
|
size_t cell_node_count = m_unitCell->m_region->get_property("node_count").get_int();
|
|
std::vector<INT> map(cell_node_count + 1);
|
|
|
|
INT offset = mode == Mode::PROCESSOR ? m_localNodeIdOffset : m_globalNodeIdOffset;
|
|
|
|
if (!equivalence_nodes || !(has_neighbor_i() || has_neighbor_j())) {
|
|
std::iota(map.begin(), map.end(), offset);
|
|
}
|
|
else if (has_neighbor_i() || has_neighbor_j()) {
|
|
// At least one neighboring cell and the nodes are being equivalenced
|
|
// Generate map for the "non-neighbored" nodes (not contiguous with a neighbor cell)
|
|
auto categorized_nodes = categorize_nodes(mode);
|
|
SMART_ASSERT(categorized_nodes.size() == cell_node_count)
|
|
(categorized_nodes.size())(cell_node_count);
|
|
offset++; // To deal with 1-based node numbers.
|
|
for (size_t n = 0; n < cell_node_count; n++) {
|
|
if (categorized_nodes[n] == 0) {
|
|
map[n + 1] = offset++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (equivalence_nodes && has_neighbor_i() &&
|
|
(mode == Mode::GLOBAL || (mode == Mode::PROCESSOR && rank(Loc::C) == rank(Loc::L)))) {
|
|
// Get the neighbor cell...
|
|
// iterate my unit cell's min_I_face() nodes to get index into map
|
|
// At this index, set value to this cells min_I_nodes() node
|
|
// which was created by the neighbor when he was processed...
|
|
SMART_ASSERT(min_I_nodes.size() == m_unitCell->min_I_face.size())
|
|
(m_i)(m_j)(min_I_nodes.size())(m_unitCell->min_I_face.size());
|
|
|
|
for (size_t i = 0; i < m_unitCell->min_I_face.size(); i++) {
|
|
auto idx = m_unitCell->min_I_face[i] + 1;
|
|
auto val = min_I_nodes[i];
|
|
map[idx] = (INT)val;
|
|
}
|
|
}
|
|
|
|
if (equivalence_nodes && has_neighbor_j() &&
|
|
(mode == Mode::GLOBAL || (mode == Mode::PROCESSOR && rank(Loc::C) == rank(Loc::B)))) {
|
|
SMART_ASSERT(min_J_nodes.size() == m_unitCell->min_J_face.size())
|
|
(m_i)(m_j)(min_J_nodes.size())(m_unitCell->min_J_face.size());
|
|
|
|
for (size_t i = 0; i < m_unitCell->min_J_face.size(); i++) {
|
|
auto idx = m_unitCell->min_J_face[i] + 1;
|
|
auto val = min_J_nodes[i];
|
|
map[idx] = (INT)val;
|
|
}
|
|
}
|
|
|
|
if (mode == Mode::PROCESSOR) {
|
|
// Now the "corner case" ;-) If there is a processor boundary below, but the cell to the BL is
|
|
// on the same rank as this cell, then we have already counted the IJ-line nodes, so need to
|
|
// categorize those nodes as already accounted for in a previous map...
|
|
if (processor_boundary(Loc::B) && processor_boundary(Loc::L) && rank(Loc::BL) == rank(Loc::C)) {
|
|
// Want KK() nodes -- First KK of min_i and of min_j. But since they match, can "unzero"
|
|
// min_i[0..KK)
|
|
auto KK = m_unitCell->cell_KK;
|
|
for (size_t i = 0; i < KK; i++) {
|
|
auto idx = m_unitCell->min_J_face[i] + 1;
|
|
auto val = min_J_nodes[i];
|
|
map[idx] = (INT)val;
|
|
}
|
|
}
|
|
// Now the other "corner case"
|
|
if (processor_boundary(Loc::B) && rank(Loc::BR) == rank(Loc::C)) {
|
|
// Want KK() nodes -- First KK of max_i and Last KK of min_j. But since they match, can
|
|
// "unzero" max_i[0..KK)
|
|
auto KK = m_unitCell->cell_KK;
|
|
auto j_offset = min_J_nodes.size() - KK;
|
|
for (size_t i = 0; i < KK; i++) {
|
|
auto idx = m_unitCell->min_J_face[j_offset + i] + 1;
|
|
auto val = min_J_nodes[j_offset + i];
|
|
map[idx] = (INT)val;
|
|
}
|
|
}
|
|
}
|
|
// Can now clean out the `min_I_nodes` and `min_J_nodes` lists since the data will no longer be
|
|
// needed.
|
|
Ioss::Utils::clear(min_I_nodes);
|
|
Ioss::Utils::clear(min_J_nodes);
|
|
|
|
return map;
|
|
}
|
|
|
|
template void Cell::populate_neighbor(Loc location, const std::vector<int64_t> &map,
|
|
const Cell &neighbor) const;
|
|
template void Cell::populate_neighbor(Loc location, const std::vector<int> &map,
|
|
const Cell &neighbor) const;
|
|
|
|
template <typename INT>
|
|
void Cell::populate_neighbor(Loc location, const std::vector<INT> &map, const Cell &neighbor) const
|
|
{
|
|
switch (location) {
|
|
case Loc::L:
|
|
neighbor.min_I_nodes.resize(m_unitCell->max_I_face.size());
|
|
for (size_t i = 0; i < m_unitCell->max_I_face.size(); i++) {
|
|
auto idx = m_unitCell->max_I_face[i] + 1;
|
|
auto val = map[idx];
|
|
neighbor.min_I_nodes[i] = val;
|
|
}
|
|
if (debug_level & 8) {
|
|
fmt::print("\nCell {} {}\n", neighbor.m_i, neighbor.m_j);
|
|
fmt::print("min_I_nodes: {}\n", fmt::join(neighbor.min_I_nodes, " "));
|
|
}
|
|
break;
|
|
|
|
case Loc::B:
|
|
neighbor.min_J_nodes.resize(m_unitCell->max_J_face.size());
|
|
for (size_t i = 0; i < m_unitCell->max_J_face.size(); i++) {
|
|
auto idx = m_unitCell->max_J_face[i] + 1;
|
|
auto val = map[idx];
|
|
neighbor.min_J_nodes[i] = val;
|
|
}
|
|
if (debug_level & 8) {
|
|
fmt::print("min_J_nodes: {}\n", fmt::join(neighbor.min_J_nodes, " "));
|
|
}
|
|
break;
|
|
|
|
case Loc::BR: {
|
|
neighbor.min_J_nodes.resize(m_unitCell->max_J_face.size());
|
|
auto KK = m_unitCell->cell_KK;
|
|
auto j_offset = neighbor.min_J_nodes.size() - KK;
|
|
for (size_t i = 0; i < KK; i++) {
|
|
auto idx = m_unitCell->max_J_face[i] + 1;
|
|
auto val = map[idx];
|
|
neighbor.min_J_nodes[j_offset + i] = val;
|
|
}
|
|
} break;
|
|
|
|
case Loc::BL: {
|
|
neighbor.min_J_nodes.resize(m_unitCell->max_J_face.size());
|
|
auto KK = m_unitCell->cell_KK;
|
|
auto j_offset = neighbor.min_J_nodes.size() - KK;
|
|
for (size_t i = 0; i < KK; i++) {
|
|
auto idx = m_unitCell->max_J_face[j_offset + i] + 1;
|
|
auto val = map[idx];
|
|
neighbor.min_J_nodes[i] = val;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
fmt::print(stderr, "\nINTERNAL ERROR: Unhandled direction in populate_neighbor(): {}\n",
|
|
location);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|