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.
 
 
 
 
 
 

852 lines
32 KiB

// Copyright(C) 1999-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 <cstdlib>
#include <exception>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "add_to_log.h"
#define FMT_DEPRECATED_OSTREAM
#include "fmt/ostream.h"
#include "format_time.h"
#include "hwm.h"
#include "open_file_limit.h"
#include "time_stamp.h"
#include <Ionit_Initializer.h>
#include <Ioss_SmartAssert.h>
#include <Ioss_SubSystem.h>
#include <Ioss_Utils.h>
#include <cgns/Iocgns_Utils.h>
#include "CP_SystemInterface.h"
#include "CP_Version.h"
#ifdef SEACAS_HAVE_MPI
#include <mpi.h>
#endif
unsigned int debug_level = 0;
namespace {
std::string tsFormat = "[{:%H:%M:%S}] ";
using GlobalZgcMap = std::map<std::pair<std::string, std::string>, Ioss::ZoneConnectivity>;
using GlobalBcMap = std::map<std::pair<std::string, std::string>, Ioss::BoundaryCondition>;
using GlobalBlockMap = std::map<const std::string, const Ioss::StructuredBlock *>;
using GlobalIJKMap = std::map<const std::string, Ioss::IJK_t>;
using PartVector = std::vector<std::unique_ptr<Ioss::Region>>;
GlobalZgcMap generate_global_zgc(const PartVector &part_mesh);
GlobalBcMap generate_global_bc(const PartVector &part_mesh);
void info_structuredblock(const Ioss::Region &region);
void resolve_offsets(const PartVector &part_mesh, GlobalBlockMap &all_blocks);
void update_global_ijk(const PartVector &part_mesh, GlobalIJKMap &global_block);
void transfer_nodal_field(const Ioss::StructuredBlock *sb, const std::vector<double> &input,
std::vector<double> &output);
void transfer_cell_field(const Ioss::StructuredBlock *sb, const std::vector<double> &input,
std::vector<double> &output);
void transfer_nodal_coordinates(const PartVector &part_mesh, Ioss::Region &output_region,
bool minimize_open_files);
double transfer_step(const PartVector &part_mesh, Ioss::Region &output_region, int istep,
bool minimize_open_files);
void union_zgc_range(Ioss::ZoneConnectivity &zgc_i, const Ioss::ZoneConnectivity &zgc_j);
void union_bc_range(Ioss::IJK_t &g_beg, Ioss::IJK_t &g_end, const Ioss::IJK_t &l_beg,
const Ioss::IJK_t &l_end, const Ioss::IJK_t &offset);
int get_constant_face(const Ioss::IJK_t &beg, const Ioss::IJK_t &end)
{
for (int i = 0; i < 3; i++) {
if (beg[i] == end[i]) {
return (beg[i] == 1) ? i : i + 3;
}
}
return 7;
}
bool is_field_valid(const Cpup::StringVector &variable_list, const std::string &field_name)
{
if (variable_list.empty() ||
(variable_list.size() == 1 && Ioss::Utils::str_equal(variable_list[0], "all") == 0)) {
return true;
}
// At this point, the variable_list contains one or more entries
// of fields that should be output on combined file. Run through
// list and see if `field_name` is in the list.
return std::any_of(variable_list.begin(), variable_list.end(),
[&field_name](const auto &valid) {
return Ioss::Utils::str_equal(valid, field_name) == 0;
});
}
int verify_timestep_count(const PartVector &part_mesh)
{
int num_time_steps = part_mesh[0]->get_property("state_count").get_int();
bool differ = false;
for (const auto &part : part_mesh) {
int nts = part->get_property("state_count").get_int();
if (nts != num_time_steps) {
differ = true;
}
num_time_steps = num_time_steps < nts ? num_time_steps : nts;
}
if (differ) {
fmt::print(stderr,
"\nWARNING: The number of time steps is not the same on all input databases.\n"
" Using minimum count of {}\n\n",
num_time_steps);
}
else {
fmt::print(stderr, "\nNumber of time steps on input databases = {}\n\n", num_time_steps);
}
return num_time_steps;
}
} // namespace
template <typename INT> void cpup(Cpup::SystemInterface &interFace, INT dummy);
int main(int argc, char *argv[])
{
#ifdef SEACAS_HAVE_MPI
MPI_Init(&argc, &argv);
#endif
try {
Cpup::SystemInterface::show_version();
Ioss::Init::Initializer io;
Cpup::SystemInterface interFace;
bool ok = interFace.parse_options(argc, argv);
debug_level = interFace.debug();
if (!ok) {
fmt::print(stderr, "\nERROR: Problems parsing command line arguments.\n\n");
exit(EXIT_FAILURE);
}
int error = 0;
double begin = Ioss::Utils::timer();
cpup(interFace, static_cast<int64_t>(0));
double end = Ioss::Utils::timer();
fmt::print(stderr,
"\nTotal Execution Time = {:.2f} seconds, Maximum memory = {} MiBytes.\n******* "
"END *******\n\n",
end - begin,
fmt::group_digits((get_hwm_memory_info() + 1024 * 1024 - 1) / (1024 * 1024)));
add_to_log(argv[0], end - begin);
#ifdef SEACAS_HAVE_MPI
MPI_Finalize();
#endif
return (error);
}
catch (std::exception &e) {
fmt::print(stderr, "ERROR: Standard exception: {}\n", e.what());
}
exit(EXIT_SUCCESS);
}
template <typename INT> void cpup(Cpup::SystemInterface &interFace, INT /*dummy*/)
{
auto width = Ioss::Utils::number_width(interFace.processor_count(), false);
bool minimize_open_files = interFace.minimize_open_files();
if (!minimize_open_files) {
// Query the system to see if the number of files exceeds the system limit and we
// need to force use of minimize_open_files...
int max_files = open_file_limit() - 1; // We also have an output file.
if (interFace.processor_count() > max_files) {
minimize_open_files = true;
fmt::print("Single file mode... (Max open = {})\n", max_files);
}
}
PartVector part_mesh(interFace.processor_count());
for (int p = 0; p < interFace.processor_count(); p++) {
std::string root_dir = interFace.root_dir();
std::string sub_dir = interFace.sub_dir();
std::string prepend{""};
if (!root_dir.empty()) {
prepend = root_dir + "/";
}
else if (Ioss::Utils::is_path_absolute(prepend)) {
prepend = "";
}
else {
prepend = "./";
}
if (!sub_dir.empty()) {
prepend += sub_dir + "/";
}
prepend += interFace.basename() + "." + interFace.cgns_suffix();
auto filename = Ioss::Utils::decode_filename(prepend, p, interFace.processor_count());
if (debug_level & 1) {
fmt::print(stderr, "{} Processor rank {:{}}, file {}\n", time_stamp(tsFormat), p, width,
filename);
}
Ioss::DatabaseIO *dbi = Ioss::IOFactory::create("cgns", filename, Ioss::READ_RESTART,
Ioss::ParallelUtils::comm_world());
if (dbi == nullptr || !dbi->ok(true)) {
std::exit(EXIT_FAILURE);
}
dbi->set_field_separator(1);
// NOTE: region owns database pointer at this time...
std::string name = "CPUP_" + std::to_string(p + 1);
part_mesh[p] = std::make_unique<Ioss::Region>(dbi, name);
if (part_mesh[p]->mesh_type() != Ioss::MeshType::STRUCTURED) {
part_mesh[p]->output_summary(std::cerr);
fmt::print(stderr,
"\nERROR: Only UNSTRUCTURED CGNS file joining is supported at this time.\n");
exit(EXIT_FAILURE);
}
if (debug_level & 2) {
part_mesh[p]->output_summary(std::cerr);
fmt::print(stderr, "\n");
}
if (minimize_open_files) {
part_mesh[p]->get_database()->closeDatabase();
}
}
// Each processor may have a different set of zones. This routine
// will sync the information such that at end, there is a consistent
// set of structuredBlocks defined with the correct local and
// global, i,j,k ranges and offsets.
GlobalBlockMap all_blocks;
GlobalIJKMap global_block;
for (const auto &part : part_mesh) {
const auto &blocks = part->get_structured_blocks();
for (const auto &block : blocks) {
auto &name = block->name();
all_blocks[name] = block;
// Build map of unique blocks in the mesh.
auto name_proc = Iocgns::Utils::decompose_name(name, true);
global_block[name_proc.first];
// Set the zgc 'from_decomp' property...
for (const auto &zgc : block->m_zoneConnectivity) {
auto zgc_name_proc = Iocgns::Utils::decompose_name(zgc.m_donorName, true);
if (zgc_name_proc.first == name_proc.first) {
zgc.m_fromDecomp = true;
}
}
}
}
// Resolve the offsets for each split block based on the decomp-ZGC
// with the same block on other processors
resolve_offsets(part_mesh, all_blocks);
// Offsets are now correct. Need to calculate and update the global size of each unique block...
update_global_ijk(part_mesh, global_block);
// Need a consistent set of Boundary Conditions for each zone unioned across the proc-local-zones
GlobalBcMap global_bc = generate_global_bc(part_mesh);
// Need a consistent set of ZGC for each zone unioned across the proc-local-zones...
// Skip the ZGC that are "from_decomp"
GlobalZgcMap global_zgc = generate_global_zgc(part_mesh);
// Create output file...
Ioss::PropertyManager properties{};
properties.add(Ioss::Property("FLUSH_INTERVAL", 0));
Ioss::DatabaseIO *dbo =
Ioss::IOFactory::create("cgns", interFace.output_filename(), Ioss::WRITE_RESTART,
Ioss::ParallelUtils::comm_world(), properties);
if (dbo == nullptr || !dbo->ok(true)) {
std::exit(EXIT_FAILURE);
}
// NOTE: 'output_region' owns 'dbo' pointer at this time
Ioss::Region output_region(dbo, "cpup_output_region");
output_region.property_add(Ioss::Property("code_name", qainfo[0]));
output_region.property_add(Ioss::Property("code_version", qainfo[1] + ":" + qainfo[2]));
output_region.begin_mode(Ioss::STATE_DEFINE_MODEL);
// KLUGE: Remove this...
// Doesn't affect the output model at all, so not a big issue, ...
auto *nb = new Ioss::NodeBlock(dbo, "nodeblock_1", 1, 3);
output_region.add(nb);
// Create the output structured blocks...
for (auto &block_range : global_block) {
auto &block_name = block_range.first;
auto block = new Ioss::StructuredBlock(dbo, block_name, 3, block_range.second);
output_region.add(block);
// Add BC to the block...
for (const auto &bc_map : global_bc) {
if (bc_map.first.first == block_name) {
block->m_boundaryConditions.push_back(bc_map.second);
}
}
// Add ZGC to the block...
for (const auto &zgc_map : global_zgc) {
if (zgc_map.first.first == block_name) {
block->m_zoneConnectivity.push_back(zgc_map.second);
}
}
}
// Copy the sidesets and assemblies from the proc-0 input file to the output file...
auto &part = part_mesh[0];
const auto &ssets = part->get_sidesets();
for (const auto &sset : ssets) {
auto oss = new Ioss::SideSet(*sset);
output_region.add(oss);
}
const auto &assems = part->get_assemblies();
for (const auto &assem : assems) {
auto oass = new Ioss::Assembly(*assem);
output_region.add(oass);
}
if (debug_level & 4) {
info_structuredblock(output_region);
}
output_region.end_mode(Ioss::STATE_DEFINE_MODEL);
output_region.begin_mode(Ioss::STATE_MODEL);
transfer_nodal_coordinates(part_mesh, output_region, minimize_open_files);
output_region.end_mode(Ioss::STATE_MODEL);
// ******* Transient Data...
output_region.begin_mode(Ioss::STATE_DEFINE_TRANSIENT);
// ... Iterate the output_region structured blocks,
// .. Find corresponding structured blocks on part meshes.
// .. Add each valid block and node_block field
const auto &variable_list = interFace.var_names();
if (!(variable_list.size() == 1 && Ioss::Utils::str_equal(variable_list[0], "none") == 0)) {
const auto &blocks = output_region.get_structured_blocks();
for (const auto &block : blocks) {
int64_t num_cell = block->get_property("cell_count").get_int();
int64_t num_node = block->get_property("node_count").get_int();
auto &onb = block->get_node_block();
// Find all corresponding blocks on the input part meshes...
for (const auto &prt : part_mesh) {
const auto &pblocks = prt->get_structured_blocks();
for (const auto &pblock : pblocks) {
auto &name = pblock->name();
auto name_proc = Iocgns::Utils::decompose_name(name, true);
if (name_proc.first == block->name()) {
Ioss::NameList fields = pblock->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field_name : fields) {
if (is_field_valid(variable_list, field_name)) {
Ioss::Field field = pblock->get_field(field_name);
if (!block->field_exists(field_name)) {
// If the field does not already exist, add it to the output block...
field.reset_count(num_cell);
block->field_add(field);
}
}
}
// Now transfer the fields on the embedded node block...
auto pnb = pblock->get_node_block();
fields.clear();
pnb.field_describe(Ioss::Field::TRANSIENT, &fields);
for (const auto &field_name : fields) {
if (is_field_valid(variable_list, field_name)) {
Ioss::Field field = pnb.get_field(field_name);
if (!onb.field_exists(field_name)) {
field.reset_count(num_node);
// If the field does not already exist, add it to the output block...
onb.field_add(field);
}
}
}
break; // Should be only a single instance of each block on a part mesh.
}
}
}
}
}
output_region.end_mode(Ioss::STATE_DEFINE_TRANSIENT);
output_region.begin_mode(Ioss::STATE_TRANSIENT);
int num_time_steps = verify_timestep_count(part_mesh);
// Determine if user wants a subset of timesteps transferred to the output file.
int ts_min = interFace.step_min();
int ts_max = interFace.step_max();
int ts_step = interFace.step_interval();
if (ts_min == -1 && ts_max == -1) {
ts_min = num_time_steps;
ts_max = num_time_steps;
}
// Time steps for output file
int time_step_out = 0;
ts_max = ts_max < num_time_steps ? ts_max : num_time_steps;
if (ts_min <= ts_max) {
fmt::print(stderr, "\tTransferring step {} to step {} by {}\n", ts_min, ts_max, ts_step);
}
// Determine how many steps will be written...
int output_steps = (ts_max - ts_min) / ts_step + 1;
double start_time = Ioss::Utils::timer();
double cur_time = start_time;
for (int time_step = ts_min; time_step <= ts_max; time_step += ts_step) {
time_step_out++;
double time_val = transfer_step(part_mesh, output_region, time_step, minimize_open_files);
double time_per_step = Ioss::Utils::timer() - cur_time;
cur_time = Ioss::Utils::timer();
double elapsed = cur_time - start_time;
double avg_time_per_step = elapsed / time_step_out;
double percentage_done = (time_step_out * 100.0) / output_steps;
double estimated_remaining = avg_time_per_step * (output_steps - time_step_out);
if (debug_level & 1) {
fmt::print(stderr,
"{} \tWrote step {:6}, time {:8.4e}\t[{:5.1f}%, Elapsed={}, \tETA={}, \tTPS={}]\n",
time_stamp(tsFormat), fmt::group_digits(time_step), time_val, percentage_done,
format_time(elapsed), format_time(estimated_remaining),
format_time(time_per_step));
}
else {
fmt::print(stderr,
"\tWrote step {:6}, time {:8.4e}\t[{:5.1f}%, Elapsed={}, ETA={}, TPS={}] \r",
fmt::group_digits(time_step), time_val, percentage_done, format_time(elapsed),
format_time(estimated_remaining), format_time(time_per_step));
}
}
output_region.end_mode(Ioss::STATE_TRANSIENT);
fmt::print(stderr, "\n\n********************* OUTPUT DATABASE ********************\n");
output_region.output_summary(std::cerr);
}
namespace {
GlobalZgcMap generate_global_zgc(const PartVector &part_mesh)
{
GlobalZgcMap global_zgc;
for (const auto &part : part_mesh) {
const auto &blocks = part->get_structured_blocks();
for (const auto &block : blocks) {
auto name_proc = Iocgns::Utils::decompose_name(block->name(), true);
for (const auto &zgc : block->m_zoneConnectivity) {
if (!zgc.m_fromDecomp) {
auto &gzgc = global_zgc[std::make_pair(name_proc.first, zgc.m_connectionName)];
if (gzgc.m_connectionName.empty()) {
// First time this ZGC has been found. Copy from the per-proc instance and update...
gzgc.m_connectionName = zgc.m_connectionName;
gzgc.m_donorName = Iocgns::Utils::decompose_name(zgc.m_donorName, true).first;
gzgc.m_transform = zgc.m_transform;
}
// Create a temporary zgc; adjust its ranges to "global" by adding the offsets
// and then union if with the global instance...
Ioss::IJK_t own_off = block->get_ijk_offset();
auto tmp_zgc{zgc};
tmp_zgc.m_ownerRangeBeg[0] += own_off[0];
tmp_zgc.m_ownerRangeBeg[1] += own_off[1];
tmp_zgc.m_ownerRangeBeg[2] += own_off[2];
tmp_zgc.m_ownerRangeEnd[0] += own_off[0];
tmp_zgc.m_ownerRangeEnd[1] += own_off[1];
tmp_zgc.m_ownerRangeEnd[2] += own_off[2];
// Now find the donor block...
auto donor = Iocgns::Utils::decompose_name(zgc.m_donorName, true);
auto *donor_block = part_mesh[donor.second]->get_structured_block(zgc.m_donorName);
SMART_ASSERT(donor_block != nullptr);
Ioss::IJK_t don_off = donor_block->get_ijk_offset();
tmp_zgc.m_donorRangeBeg[0] += don_off[0];
tmp_zgc.m_donorRangeBeg[1] += don_off[1];
tmp_zgc.m_donorRangeBeg[2] += don_off[2];
tmp_zgc.m_donorRangeEnd[0] += don_off[0];
tmp_zgc.m_donorRangeEnd[1] += don_off[1];
tmp_zgc.m_donorRangeEnd[2] += don_off[2];
union_zgc_range(gzgc, tmp_zgc);
}
}
}
}
return global_zgc;
}
GlobalBcMap generate_global_bc(const PartVector &part_mesh)
{
GlobalBcMap global_bc;
for (const auto &part : part_mesh) {
const auto &blocks = part->get_structured_blocks();
for (const auto &block : blocks) {
Ioss::IJK_t offset = block->get_ijk_offset();
auto name_proc = Iocgns::Utils::decompose_name(block->name(), true);
const auto &sb_bc = block->m_boundaryConditions;
for (const auto &bc : sb_bc) {
auto &gbc = global_bc[std::make_pair(name_proc.first, bc.m_bcName)];
if (gbc.m_bcName.empty()) {
gbc.m_bcName = bc.m_bcName;
gbc.m_famName = bc.m_famName;
}
union_bc_range(gbc.m_rangeBeg, gbc.m_rangeEnd, bc.m_rangeBeg, bc.m_rangeEnd, offset);
SMART_ASSERT(gbc.which_face() == bc.which_face());
}
}
}
return global_bc;
}
double transfer_step(const PartVector &part_mesh, Ioss::Region &output_region, int istep,
bool minimize_open_files)
{
double time = part_mesh[0]->get_state_time(istep);
int ostep = output_region.add_state(time);
output_region.begin_state(ostep);
for (const auto &part : part_mesh) {
part->begin_state(istep);
}
const auto &blocks = output_region.get_structured_blocks();
for (const auto &block : blocks) {
int64_t num_cell = block->get_property("cell_count").get_int();
std::vector<double> output(num_cell);
std::vector<double> input;
Ioss::NameList fields = block->field_describe(Ioss::Field::TRANSIENT);
// Not sure if this is the best ordering of loops, but it minimizes the
// amount of data gathered at one time at the cost of multiple iterations
// through the block-finding loop...
for (const auto &field_name : fields) {
// Find all corresponding blocks on the input part meshes...
for (const auto &part : part_mesh) {
const auto &pblocks = part->get_structured_blocks();
for (const auto &pblock : pblocks) {
auto &name = pblock->name();
auto name_proc = Iocgns::Utils::decompose_name(name, true);
if (name_proc.first == block->name()) {
if (pblock->field_exists(field_name)) {
pblock->get_field_data(field_name, input);
transfer_cell_field(pblock, input, output);
}
break; // Should be only a single instance of each block on a part mesh.
}
}
if (minimize_open_files) {
part->get_database()->closeDatabase();
}
}
block->put_field_data(field_name, output);
}
}
// Now do the fields on the embedded node block...
for (const auto &block : blocks) {
int64_t num_node = block->get_property("node_count").get_int();
auto &onb = block->get_node_block();
std::vector<double> output(num_node);
std::vector<double> input;
Ioss::NameList fields = onb.field_describe(Ioss::Field::TRANSIENT);
// Not sure if this is the best ordering of loops, but it minimizes the
// amount of data gathered at one time at the cost of multiple iterations
// through the block-finding loop...
for (const auto &field_name : fields) {
// Find all corresponding blocks on the input part meshes...
for (const auto &part : part_mesh) {
const auto &pblocks = part->get_structured_blocks();
for (const auto &pblock : pblocks) {
auto &name = pblock->name();
auto name_proc = Iocgns::Utils::decompose_name(name, true);
if (name_proc.first == block->name()) {
auto &inb = pblock->get_node_block();
if (inb.field_exists(field_name)) {
inb.get_field_data(field_name, input);
transfer_nodal_field(pblock, input, output);
}
break; // Should be only a single instance of each block on a part mesh.
}
}
if (minimize_open_files) {
part->get_database()->closeDatabase();
}
}
onb.put_field_data(field_name, output);
}
}
output_region.end_state(ostep);
for (const auto &part : part_mesh) {
part->end_state(istep);
}
return time;
}
void resolve_offsets(const PartVector &part_mesh, GlobalBlockMap &all_blocks)
{
bool change_made;
do {
change_made = false;
for (const auto &part : part_mesh) {
const auto &blocks = part->get_structured_blocks();
for (const auto &block : blocks) {
for (const auto &zgc : block->m_zoneConnectivity) {
if (zgc.is_from_decomp()) {
auto plane = get_constant_face(zgc.m_ownerRangeBeg, zgc.m_ownerRangeEnd);
if (plane < 3) {
// This zone connects to another zone "below" it.
// Find the connecting zone and adjust the correct offset
auto donor = all_blocks[zgc.m_donorName];
auto offset = donor->get_ijk_offset();
auto range = donor->get_ijk_local();
auto my_offset = block->get_ijk_offset();
if (my_offset[plane] != range[plane] + offset[plane]) {
block->set_ijk_offset(plane, range[plane] + offset[plane]);
change_made = true;
}
}
}
}
}
}
} while (change_made);
}
void update_global_ijk(const PartVector &part_mesh, GlobalIJKMap &global_block)
{
for (const auto &part : part_mesh) {
const auto &blocks = part->get_structured_blocks();
for (const auto &block : blocks) {
auto ijk_o = block->get_ijk_offset();
auto ijk_g = block->get_ijk_global();
auto name_proc = Iocgns::Utils::decompose_name(block->name(), true);
auto &cur_global = global_block[name_proc.first];
cur_global[0] = std::max(ijk_o[0] + ijk_g[0], cur_global[0]);
cur_global[1] = std::max(ijk_o[1] + ijk_g[1], cur_global[1]);
cur_global[2] = std::max(ijk_o[2] + ijk_g[2], cur_global[2]);
}
}
for (const auto &part : part_mesh) {
const auto &blocks = part->get_structured_blocks();
for (const auto &block : blocks) {
auto name_proc = Iocgns::Utils::decompose_name(block->name(), true);
auto &cur_global = global_block[name_proc.first];
block->set_ijk_global(cur_global);
}
if (debug_level & 4) {
info_structuredblock(*part);
}
}
}
void info_structuredblock(const Ioss::Region &region)
{
const Ioss::StructuredBlockContainer &sbs = region.get_structured_blocks();
for (const auto &sb : sbs) {
auto ijk_global = sb->get_ijk_global();
fmt::print(stderr, "\n{} '{}' {}", sb->type_string(), sb->name(), fmt::join(ijk_global, "x"));
auto ijk_offset = sb->get_ijk_offset();
auto ijk_local = sb->get_ijk_local();
fmt::print(stderr, " [{}, Offset = {}] ", fmt::join(ijk_local, "x"),
fmt::join(ijk_offset, ", "));
int64_t num_cell = sb->get_property("cell_count").get_int();
int64_t num_node = sb->get_property("node_count").get_int();
fmt::print(stderr, " {:14} cells, {:14} nodes ", fmt::group_digits(num_cell),
fmt::group_digits(num_node));
if (!sb->m_zoneConnectivity.empty()) {
fmt::print(stderr, "\n\tConnectivity with other blocks:\n");
for (const auto &zgc : sb->m_zoneConnectivity) {
fmt::print(stderr, "{}\n", zgc);
}
}
if (!sb->m_boundaryConditions.empty()) {
fmt::print(stderr, "\tBoundary Conditions:\n");
for (const auto &bc : sb->m_boundaryConditions) {
fmt::print(stderr, "{}\n", bc);
}
}
}
}
void transfer_nodal_field(const Ioss::StructuredBlock *sb, const std::vector<double> &input,
std::vector<double> &output)
{
// Get the IJK subset that this nodeblock covers...
auto ijkg = sb->get_ijk_global();
auto ijkl = sb->get_ijk_local();
// Get the IJK offset...
auto offset = sb->get_ijk_offset();
SMART_ASSERT(input.size() == (size_t)(ijkl[0] + 1) * (ijkl[1] + 1) * (ijkl[2] + 1))
(input.size())(ijkl[0])(ijkl[1])(ijkl[2]);
SMART_ASSERT(output.size() == (size_t)(ijkg[0] + 1) * (ijkg[1] + 1) * (ijkg[2] + 1))
(output.size())(ijkg[0])(ijkg[1])(ijkg[2]);
size_t idx = 0;
for (int k = offset[2]; k < offset[2] + ijkl[2] + 1; k++) {
for (int j = offset[1]; j < offset[1] + ijkl[1] + 1; j++) {
for (int i = offset[0]; i < offset[0] + ijkl[0] + 1; i++) {
size_t oidx = i + (ijkg[0] + 1) * j + (ijkg[0] + 1) * (ijkg[1] + 1) * k;
output[oidx] = input[idx++];
}
}
}
}
void transfer_cell_field(const Ioss::StructuredBlock *sb, const std::vector<double> &input,
std::vector<double> &output)
{
// Get the IJK subset that this structured covers...
auto ijkg = sb->get_ijk_global();
auto ijkl = sb->get_ijk_local();
// Get the IJK offset...
auto offset = sb->get_ijk_offset();
SMART_ASSERT(input.size() == (size_t)ijkl[0] * ijkl[1] * ijkl[2])
(input.size())(ijkl[0])(ijkl[1])(ijkl[2]);
SMART_ASSERT(output.size() == (size_t)ijkg[0] * ijkg[1] * ijkg[2])
(output.size())(ijkg[0])(ijkg[1])(ijkg[2]);
size_t idx = 0;
for (int k = offset[2]; k < offset[2] + ijkl[2]; k++) {
for (int j = offset[1]; j < offset[1] + ijkl[1]; j++) {
for (int i = offset[0]; i < offset[0] + ijkl[0]; i++) {
size_t oidx = i + ijkg[0] * j + ijkg[0] * ijkg[1] * k;
output[oidx] = input[idx++];
}
}
}
}
void transfer_nodal_coordinates(const PartVector &part_mesh, Ioss::Region &output_region,
bool minimize_open_files)
{
// This implementation results in having to iterate over the part
// mesh 3 times -- once for each coordinate axis, but minimizes
// the memory requirements. Will monitor whether this results in
// excessive computation time, but does permit larger models to be
// processed.
std::array<std::string, 3> fields{"mesh_model_coordinates_x", "mesh_model_coordinates_y",
"mesh_model_coordinates_z"};
const auto &blocks = output_region.get_structured_blocks();
for (const auto &block : blocks) {
// Get size of node_block...
auto &onb = block->get_node_block();
size_t num_coord = onb.entity_count();
std::vector<double> coord(num_coord);
for (int dim = 0; dim < 3; dim++) {
// Find all corresponding blocks on the input part meshes...
for (const auto &part : part_mesh) {
const auto &pblocks = part->get_structured_blocks();
for (const auto &pblock : pblocks) {
auto &name = pblock->name();
auto name_proc = Iocgns::Utils::decompose_name(name, true);
if (name_proc.first == block->name()) {
std::vector<double> lcoord;
pblock->get_field_data(fields[dim], lcoord);
transfer_nodal_field(pblock, lcoord, coord);
break; // Should be only a single instance of each block on a part mesh.
}
}
if (minimize_open_files) {
part->get_database()->closeDatabase();
}
}
block->put_field_data(fields[dim], coord);
}
}
}
void union_bc_range(Ioss::IJK_t &g_beg, Ioss::IJK_t &g_end, const Ioss::IJK_t &l_beg,
const Ioss::IJK_t &l_end, const Ioss::IJK_t &offset)
{
for (int i = 0; i < 3; i++) {
g_beg[i] = g_beg[i] == 0 ? l_beg[i] + offset[i] : std::min(g_beg[i], l_beg[i] + offset[i]);
g_end[i] = std::max(g_end[i], l_end[i] + offset[i]);
}
}
void union_zgc_range(Ioss::ZoneConnectivity &zgc_i, const Ioss::ZoneConnectivity &zgc_j)
{
assert(zgc_i.m_transform == zgc_j.m_transform);
if (zgc_i.m_ownerRangeBeg[0] == 0 && zgc_i.m_ownerRangeBeg[1] == 0 &&
zgc_i.m_ownerRangeBeg[2] == 0) {
// This is a newly-created zgc that hasn't been unioned with anything yet.
zgc_i.m_ownerRangeBeg = zgc_j.m_ownerRangeBeg;
zgc_i.m_ownerRangeEnd = zgc_j.m_ownerRangeEnd;
zgc_i.m_donorRangeBeg = zgc_j.m_donorRangeBeg;
zgc_i.m_donorRangeEnd = zgc_j.m_donorRangeEnd;
}
else {
for (int i = 0; i < 3; i++) {
if (zgc_i.m_ownerRangeBeg[i] <= zgc_i.m_ownerRangeEnd[i]) {
zgc_i.m_ownerRangeBeg[i] = std::min(zgc_i.m_ownerRangeBeg[i], zgc_j.m_ownerRangeBeg[i]);
zgc_i.m_ownerRangeEnd[i] = std::max(zgc_i.m_ownerRangeEnd[i], zgc_j.m_ownerRangeEnd[i]);
}
else {
zgc_i.m_ownerRangeBeg[i] = std::max(zgc_i.m_ownerRangeBeg[i], zgc_j.m_ownerRangeBeg[i]);
zgc_i.m_ownerRangeEnd[i] = std::min(zgc_i.m_ownerRangeEnd[i], zgc_j.m_ownerRangeEnd[i]);
}
if (zgc_i.m_donorRangeBeg[i] <= zgc_i.m_donorRangeEnd[i]) {
zgc_i.m_donorRangeBeg[i] = std::min(zgc_i.m_donorRangeBeg[i], zgc_j.m_donorRangeBeg[i]);
zgc_i.m_donorRangeEnd[i] = std::max(zgc_i.m_donorRangeEnd[i], zgc_j.m_donorRangeEnd[i]);
}
else {
zgc_i.m_donorRangeBeg[i] = std::max(zgc_i.m_donorRangeBeg[i], zgc_j.m_donorRangeBeg[i]);
zgc_i.m_donorRangeEnd[i] = std::min(zgc_i.m_donorRangeEnd[i], zgc_j.m_donorRangeEnd[i]);
}
}
}
}
} // namespace