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.

1587 lines
57 KiB

2 years ago
// 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 <algorithm>
#include <cctype>
#include <cfloat>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <exception>
#include <iterator>
#include <limits>
#include <map>
#include <numeric>
#include <set>
#include <string>
#include <unistd.h>
#include <vector>
#include "add_to_log.h"
#include "fmt/ostream.h"
#include "time_stamp.h"
#include <exodusII.h>
#include <Ionit_Initializer.h>
#include <Ioss_SmartAssert.h>
#include <Ioss_SubSystem.h>
#include <Ioss_Transform.h>
#include <transform/Iotr_Factory.h>
#include "EJ_CodeTypes.h"
#include "EJ_SystemInterface.h"
#include "EJ_Version.h"
#include "EJ_mapping.h"
#include "EJ_match_xyz.h"
#include "EJ_vector3d.h"
#ifdef SEACAS_HAVE_MPI
#include <mpi.h>
#endif
namespace {
bool valid_variable(const std::string &variable, size_t id, const StringIdVector &variable_list);
void define_global_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list);
void define_nodal_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list, SystemInterface &interFace);
void define_element_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list);
void define_nset_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list);
void define_sset_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list);
void define_nodal_nodeset_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list, SystemInterface &interFace);
template <typename INT>
void output_nodeblock(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_node_map, std::vector<INT> &global_node_map);
template <typename INT>
void output_elementblock(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_node_map,
const std::vector<INT> &local_element_map, bool ignore_element_ids);
template <typename INT>
void output_nodeset(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_node_map);
template <typename INT>
void output_sideset(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_element_map);
template <typename INT>
void output_nodal_nodeset(Ioss::Region &output_region, RegionVector &part_mesh,
SystemInterface &interFace, const std::vector<INT> &local_node_map);
template <typename INT>
void output_transient_state(Ioss::Region &output_region, RegionVector &part_mesh, double time,
const std::vector<INT> &local_node_map, SystemInterface &interFace);
void process_nset_omissions(RegionVector &part_mesh, const Omissions &omit);
void process_sset_omissions(RegionVector &part_mesh, const Omissions &omit);
void process_assembly_omissions(RegionVector &part_mesh, const Omissions &omit);
int count_omissions(Ioss::Region *region)
{
int omitted = 0;
const auto &blocks = region->get_element_blocks();
for (const auto &block : blocks) {
if (block->property_exists(std::string("omitted"))) {
omitted++;
}
}
return omitted;
}
template <typename T> bool approx_equal(T v1, T v2, T offset)
{
#if 1
static const T tolerance = 100.0f * std::numeric_limits<float>::epsilon();
double d1 = std::fabs(v1 - v2);
double d2 = std::fabs((v1 - offset) + (v2 - offset)) * tolerance;
return d1 <= d2;
#else
return (float)v1 == (float)v2;
#endif
}
using EntityIdSet = std::set<std::pair<Ioss::EntityType, int64_t>>;
EntityIdSet id_set;
void set_id(Ioss::GroupingEntity *old_ge, Ioss::GroupingEntity *new_ge)
{
if (old_ge->property_exists("id")) {
int64_t id = old_ge->get_property("id").get_int();
bool succeed = id_set.insert(std::make_pair(new_ge->type(), id)).second;
if (succeed) {
// Add id as the property "id" to ge
new_ge->property_add(Ioss::Property("id", id));
}
}
}
} // namespace
namespace {
void transfer_elementblock(Ioss::Region &region, Ioss::Region &output_region,
bool create_assemblies, bool debug);
void transfer_assembly(Ioss::Region &region, Ioss::Region &output_region, bool debug);
void transfer_nodesets(Ioss::Region &region, Ioss::Region &output_region, bool debug);
void transfer_sidesets(Ioss::Region &region, Ioss::Region &output_region, bool debug);
void create_nodal_nodeset(Ioss::Region &region, Ioss::Region &output_region, bool debug);
void transfer_fields(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge,
Ioss::Field::RoleType role, const std::string &prefix = "");
void transfer_field_data(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge,
Ioss::Field::RoleType role, const std::string &prefix = "",
bool transfer_connectivity = true);
void transfer_field_data_internal(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge,
const std::string &field_name);
} // namespace
std::string tsFormat = "[%H:%M:%S] ";
// prototypes
template <typename INT>
double ejoin(SystemInterface &interFace, std::vector<Ioss::Region *> &part_mesh, INT dummy);
unsigned int debug_level = 0;
int main(int argc, char *argv[])
{
#ifdef SEACAS_HAVE_MPI
MPI_Init(&argc, &argv);
#endif
try {
SystemInterface::show_version();
Ioss::Init::Initializer io;
SystemInterface interFace;
bool ok = interFace.parse_options(argc, argv);
if (!ok) {
fmt::print(stderr, "\nERROR: Problems parsing command line arguments.\n\n");
exit(EXIT_FAILURE);
}
debug_level = interFace.debug();
if ((debug_level & 64) != 0U) {
ex_opts(EX_VERBOSE | EX_DEBUG);
}
else {
ex_opts(0);
}
int error = 0;
int int_byte_size = 4;
if (interFace.ints64bit()) {
int_byte_size = 8;
}
const Omissions &omissions = interFace.block_omissions();
const Omissions &inclusions = interFace.block_inclusions();
std::vector<Ioss::Region *> part_mesh(interFace.inputFiles_.size());
std::vector<Ioss::DatabaseIO *> dbi(interFace.inputFiles_.size());
for (size_t p = 0; p < interFace.inputFiles_.size(); p++) {
dbi[p] = Ioss::IOFactory::create("exodusII", interFace.inputFiles_[p], Ioss::READ_RESTART,
Ioss::ParallelUtils::comm_world());
if (dbi[p] == nullptr || !dbi[p]->ok(true)) {
std::exit(EXIT_FAILURE);
}
if (dbi[p]->int_byte_size_api() > int_byte_size) {
int_byte_size = dbi[p]->int_byte_size_api();
}
}
for (size_t p = 0; p < interFace.inputFiles_.size(); p++) {
dbi[p]->set_surface_split_type(Ioss::SPLIT_BY_DONT_SPLIT);
if (int_byte_size == 8) {
dbi[p]->set_int_byte_size_api(Ioss::USE_INT64_API);
}
if (interFace.disable_field_recognition()) {
dbi[p]->set_field_separator(1);
}
if (!omissions[p].empty() || !inclusions[p].empty()) {
dbi[p]->set_block_omissions(omissions[p], inclusions[p]);
}
// Generate a name for the region based on the part number...
std::string prefix = interFace.block_prefix();
std::string name = prefix + std::to_string(p + 1);
// NOTE: region owns database pointer at this time...
part_mesh[p] = new Ioss::Region(dbi[p], name);
int omission_count = count_omissions(part_mesh[p]);
part_mesh[p]->property_add(Ioss::Property("block_omission_count", omission_count));
vector3d offset = interFace.offset();
if (p > 0 && (offset.x != 0.0 || offset.y != 0.0 || offset.z != 0.0)) {
Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0];
Ioss::Field coord = nb->get_field("mesh_model_coordinates");
Ioss::Transform *transform = Iotr::Factory::create("offset3D");
assert(transform != nullptr);
std::vector<double> values(3);
values[0] = offset.x * p;
values[1] = offset.y * p;
values[2] = offset.z * p;
transform->set_properties("offset", values);
coord.add_transform(transform);
nb->field_erase("mesh_model_coordinates");
nb->field_add(coord);
}
}
process_nset_omissions(part_mesh, interFace.nset_omissions());
process_sset_omissions(part_mesh, interFace.sset_omissions());
process_assembly_omissions(part_mesh, interFace.assembly_omissions());
double time = 0.0;
if (int_byte_size == 4) {
time = ejoin(interFace, part_mesh, 0);
}
else {
time = ejoin(interFace, part_mesh, static_cast<int64_t>(0));
}
for (auto &pm : part_mesh) {
delete pm;
}
add_to_log(argv[0], time);
#ifdef SEACAS_HAVE_MPI
MPI_Comm parent_comm;
MPI_Comm_get_parent(&parent_comm);
if (parent_comm != MPI_COMM_NULL) {
MPI_Barrier(parent_comm);
}
MPI_Finalize();
#endif
return error;
}
catch (std::exception &e) {
fmt::print(stderr, "ERROR: Standard exception: {}\n", e.what());
}
}
template <typename INT>
double ejoin(SystemInterface &interFace, std::vector<Ioss::Region *> &part_mesh, INT /*dummy*/)
{
double begin = Ioss::Utils::timer();
size_t part_count = interFace.inputFiles_.size();
SMART_ASSERT(part_count == part_mesh.size());
Ioss::PropertyManager properties{};
if (sizeof(INT) == 8) {
properties.add(Ioss::Property("INTEGER_SIZE_DB", 8));
properties.add(Ioss::Property("INTEGER_SIZE_API", 8));
}
if (interFace.use_netcdf4()) {
properties.add(Ioss::Property("FILE_TYPE", "netcdf4"));
}
if (interFace.compression_level() > 0 || interFace.szip()) {
properties.add(Ioss::Property("FILE_TYPE", "netcdf4"));
properties.add(Ioss::Property("COMPRESSION_LEVEL", interFace.compression_level()));
properties.add(Ioss::Property("COMPRESSION_SHUFFLE", true));
if (interFace.szip()) {
properties.add(Ioss::Property("COMPRESSION_METHOD", "szip"));
}
else if (interFace.zlib()) {
properties.add(Ioss::Property("COMPRESSION_METHOD", "zlib"));
}
}
// Get maximum length of names used on any of the input files...
int max_name_length = 32;
for (const auto *mesh : part_mesh) {
int max_name_used = mesh->get_database()->maximum_symbol_length();
max_name_length = std::max(max_name_length, max_name_used);
}
properties.add(Ioss::Property("MAXIMUM_NAME_LENGTH", max_name_length));
properties.add(Ioss::Property("FLUSH_INTERVAL", 0));
Ioss::DatabaseIO *dbo =
Ioss::IOFactory::create("exodusII", interFace.outputName_, 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, "ejoin_output_region");
output_region.begin_mode(Ioss::STATE_DEFINE_MODEL);
output_region.property_add(Ioss::Property("code_name", qainfo[0]));
output_region.property_add(Ioss::Property("code_version", qainfo[2]));
if (debug_level & 1) {
fmt::print(stderr, "{}", time_stamp(tsFormat));
}
INT node_offset = 0;
INT element_offset = 0;
for (auto &pm : part_mesh) {
pm->property_add(Ioss::Property("node_offset", node_offset));
pm->property_add(Ioss::Property("element_offset", element_offset));
INT local_node_count = pm->get_property("node_count").get_int();
INT local_elem_count = pm->get_property("element_count").get_int();
node_offset += local_node_count;
element_offset += local_elem_count;
}
INT node_count = node_offset; // Sum of nodes in part meshes.
std::vector<INT> local_node_map(node_count);
std::vector<INT> global_node_map;
// This is the map from local element position to global element
// position (0-based). If there are no element block omissions, then
// the map is simply [0..number_elements). If there are omissions,
// Then local_element_map[j] will be -1 for an omitted element.
// If not omitted, local_element_map[part_offset+j] gives the global
// position of local element j in the current part.
std::vector<INT> local_element_map(element_offset);
build_local_element_map(part_mesh, local_element_map);
// Need a map from local node to global node. Position in the map
// is part_mesh.offset+local_node_position Return value is position
// in the global node list. If no mapping, then the list is simply
// 0..number_of_global_nodes where number_of_global_nodes is the sum
// of the node counts in the individual files.
// This routine also eliminates nodes if there are omitted element blocks.
if (interFace.match_node_ids()) {
build_reverse_node_map(output_region, part_mesh, global_node_map, local_node_map);
}
else if (interFace.match_node_xyz()) {
match_node_xyz(part_mesh, interFace.tolerance(), global_node_map, local_node_map);
}
else {
// Eliminate all nodes that were only connected to the omitted element blocks (if any).
bool fill_global = true;
eliminate_omitted_nodes(part_mesh, global_node_map, local_node_map, fill_global);
}
node_count = global_node_map.size();
size_t merged = local_node_map.size() - global_node_map.size();
if (merged > 0) {
fmt::print("*** {} Nodes were merged/omitted.\n", fmt::group_digits(merged));
}
// Verify nodemap...
#ifndef NDEBUG
std::vector<int> glob(node_count);
for (auto id : local_node_map) {
if (id >= 0) {
glob[id] = 1;
}
}
for (int i : glob) {
SMART_ASSERT(i == 1);
}
#endif
// Transfer some common data...
output_region.property_add(part_mesh[0]->get_property("title"));
// Define a node block...
std::string block_name = "nodeblock_1";
int spatial_dimension = part_mesh[0]->get_property("spatial_dimension").get_int();
auto block =
new Ioss::NodeBlock(output_region.get_database(), block_name, node_count, spatial_dimension);
block->property_add(Ioss::Property("id", 1));
output_region.add(block);
// Add element blocks, nodesets, sidesets
for (size_t p = 0; p < part_count; p++) {
transfer_elementblock(*part_mesh[p], output_region, interFace.create_assemblies(), false);
if (interFace.convert_nodes_to_nodesets(p + 1)) {
create_nodal_nodeset(*part_mesh[p], output_region, false);
}
if (!interFace.omit_nodesets()) {
transfer_nodesets(*part_mesh[p], output_region, false);
}
if (!interFace.omit_sidesets()) {
transfer_sidesets(*part_mesh[p], output_region, false);
}
transfer_assembly(*part_mesh[p], output_region, false);
}
if (!interFace.information_record_parts().empty()) {
const std::vector<int> &info_parts = interFace.information_record_parts();
if (info_parts[0] == 0) {
// Transfer info records from all parts...
for (const auto &pm : part_mesh) {
const StringVector &info = pm->get_information_records();
output_region.add_information_records(info);
}
}
else {
for (int info_part : info_parts) {
const StringVector &info = part_mesh[info_part - 1]->get_information_records();
output_region.add_information_records(info);
}
}
}
output_region.end_mode(Ioss::STATE_DEFINE_MODEL);
output_region.begin_mode(Ioss::STATE_MODEL);
output_nodeblock(output_region, part_mesh, local_node_map, global_node_map);
output_elementblock(output_region, part_mesh, local_node_map, local_element_map,
interFace.ignore_element_ids());
output_nodal_nodeset(output_region, part_mesh, interFace, local_node_map);
if (!interFace.omit_nodesets()) {
output_nodeset(output_region, part_mesh, local_node_map);
}
if (!interFace.omit_sidesets()) {
output_sideset(output_region, part_mesh, local_element_map);
}
output_region.end_mode(Ioss::STATE_MODEL);
// ####################TRANSIENT DATA SECTION###########################
// ***********************************************************************
// 9. Get Variable Information and names
if (debug_level & 1) {
fmt::print(stderr, "{}", time_stamp(tsFormat));
}
output_region.begin_mode(Ioss::STATE_DEFINE_TRANSIENT);
define_global_fields(output_region, part_mesh, interFace.global_var_names());
define_nodal_fields(output_region, part_mesh, interFace.node_var_names(), interFace);
define_nodal_nodeset_fields(output_region, part_mesh, interFace.node_var_names(), interFace);
define_element_fields(output_region, part_mesh, interFace.elem_var_names());
if (!interFace.omit_nodesets()) {
define_nset_fields(output_region, part_mesh, interFace.nset_var_names());
}
if (!interFace.omit_sidesets()) {
define_sset_fields(output_region, part_mesh, interFace.sset_var_names());
}
output_region.end_mode(Ioss::STATE_DEFINE_TRANSIENT);
// Get database times...
// Different parts can have either no times or the times must match
//! \todo If ts_min, ts_max, ts_step is specified, then only check steps in that range...
std::vector<double> global_times;
for (size_t p = 0; p < part_count; p++) {
int nts = part_mesh[p]->get_property("state_count").get_int();
std::vector<double> times(nts);
// If multiple databases have timesteps, they must all match.
for (int i = 0; i < nts; i++) {
times[i] = part_mesh[p]->get_state_time(i + 1);
}
if (nts > 0) {
if (global_times.empty()) {
std::copy(times.begin(), times.end(), std::back_inserter(global_times));
}
else {
if (global_times.size() != times.size()) {
fmt::print(stderr, "ERROR: Time step sizes must match.");
SMART_ASSERT(global_times.size() == times.size())(global_times.size())(times.size())(p);
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < global_times.size(); i++) {
if (!approx_equal(global_times[i], times[i], global_times[0])) {
fmt::print(stderr,
"ERROR: Time step {} in part {} does not match time steps in previous "
"part(s): previous: "
"{}, current: {}\n",
i + 1, p + 1, global_times[i], times[i]);
exit(EXIT_FAILURE);
}
}
}
}
}
output_region.begin_mode(Ioss::STATE_TRANSIENT);
fmt::print("\n");
int ts_min = interFace.step_min();
int ts_max = interFace.step_max();
int ts_step = interFace.step_interval();
int num_steps = static_cast<int>(global_times.size());
if (ts_min == -1 && ts_max == -1) {
ts_min = num_steps;
ts_max = num_steps;
}
ts_max = ts_max < num_steps ? ts_max : num_steps;
double ts_begin = Ioss::Utils::timer();
int steps = 0;
int nsteps = (ts_max - ts_min + 1) / ts_step;
for (int step = ts_min - 1; step < ts_max; step += ts_step) {
int ostep = output_region.add_state(global_times[step]);
output_region.begin_state(ostep);
output_transient_state(output_region, part_mesh, global_times[step], local_node_map, interFace);
fmt::print("\rWrote step {:4}/{:4}, time {:8.4e}", step + 1, nsteps, global_times[step]);
output_region.end_state(ostep);
steps++;
}
double end = Ioss::Utils::timer();
fmt::print("\n");
output_region.end_mode(Ioss::STATE_TRANSIENT);
/*************************************************************************/
// EXIT program
if (debug_level & 1) {
fmt::print(stderr, "{}", time_stamp(tsFormat));
}
output_region.output_summary(std::cout);
fmt::print("******* END *******\n");
fmt::print(stderr, "\nTotal Execution Time = {:.5} seconds.\n", end - begin);
if (steps > 0) {
fmt::print(stderr, "\tMesh = {:.5} seconds; Timesteps = {:.5} seconds / step.\n\n",
(ts_begin - begin), (end - ts_begin) / (double)(steps));
}
return end - begin;
}
namespace {
bool entity_is_omitted(Ioss::GroupingEntity *block)
{
bool omitted = block->get_optional_property("omitted", 0) == 1;
return omitted;
}
void transfer_elementblock(Ioss::Region &region, Ioss::Region &output_region,
bool create_assemblies, bool debug)
{
static int used_blocks = 0;
const std::string &prefix = region.name();
Ioss::Assembly *assem = nullptr;
if (create_assemblies) {
assem = new Ioss::Assembly(output_region.get_database(), region.name());
output_region.add(assem);
assem->property_add(Ioss::Property("name_in_output", region.name()));
}
const Ioss::ElementBlockContainer &ebs = region.get_element_blocks();
for (const auto &eb : ebs) {
if (!entity_is_omitted(eb)) {
std::string name = eb->name();
if (output_region.get_element_block(name) != nullptr) {
name = prefix + "_" + eb->name();
if (output_region.get_element_block(name) != nullptr) {
fmt::print(stderr, "ERROR: Duplicate element blocks named '{}'\n", name);
exit(EXIT_FAILURE);
}
}
eb->property_add(Ioss::Property("name_in_output", name));
if (debug) {
fmt::print(stderr, "{}, ", name);
}
std::string type = eb->topology()->name();
size_t num_elem = eb->entity_count();
if (num_elem > 0) {
auto ebn = new Ioss::ElementBlock(output_region.get_database(), name, type, num_elem);
ebn->property_add(Ioss::Property("original_block_order", used_blocks++));
output_region.add(ebn);
transfer_fields(eb, ebn, Ioss::Field::ATTRIBUTE);
set_id(eb, ebn);
if (eb->property_exists("original_topology_type")) {
std::string oes = eb->get_property("original_topology_type").get_string();
// Set the new property
ebn->property_add(Ioss::Property("original_topology_type", oes));
}
if (create_assemblies) {
assem->add(ebn);
}
}
}
}
}
void transfer_assembly(Ioss::Region &region, Ioss::Region &output_region, bool debug)
{
// All assemblies on the input parts will be transferred to the output mesh
// Possibly renamed if a name conflict
// Also need to update names of the entities in the assembly since they were possibly
// renamed on the output...
// TODO: handle renamed nested assemblies...
const std::string &prefix = region.name();
const Ioss::AssemblyContainer &assems = region.get_assemblies();
for (const auto &as : assems) {
if (!entity_is_omitted(as)) {
std::string name = as->name();
if (output_region.get_assembly(name) != nullptr) {
name = prefix + "_" + as->name();
if (output_region.get_assembly(name) != nullptr) {
fmt::print(stderr, "ERROR: Duplicate assemblies named '{}'\n", name);
exit(EXIT_FAILURE);
}
}
as->property_add(Ioss::Property("name_in_output", name));
if (debug) {
fmt::print(stderr, "{}, ", name);
}
size_t num_members = as->entity_count();
if (num_members > 0) {
auto member_type = as->get_member_type();
auto asn = new Ioss::Assembly(output_region.get_database(), name);
output_region.add(asn);
const auto &members = as->get_members();
for (const auto &member : members) {
auto output_name = member->get_property("name_in_output").get_string();
auto *entity = output_region.get_entity(output_name, member_type);
assert(entity != nullptr);
asn->add(entity);
}
}
}
}
}
void transfer_sidesets(Ioss::Region &region, Ioss::Region &output_region, bool debug)
{
const std::string &prefix = region.name();
const Ioss::SideSetContainer &fss = region.get_sidesets();
for (auto &fs : fss) {
if (!entity_is_omitted(fs)) {
std::string name = fs->name();
if (output_region.get_sideset(name) != nullptr) {
name = prefix + "_" + fs->name();
if (output_region.get_sideset(name) != nullptr) {
fmt::print(stderr, "ERROR: Duplicate side sets named '{}'\n", name);
exit(EXIT_FAILURE);
}
}
fs->property_add(Ioss::Property("name_in_output", name));
if (debug) {
fmt::print(stderr, "{}, ", name);
}
auto surf = new Ioss::SideSet(output_region.get_database(), name);
set_id(fs, surf);
const Ioss::SideBlockContainer &fbs = fs->get_side_blocks();
for (auto &fb : fbs) {
const std::string &fbname = prefix + "_" + fb->name();
if (debug) {
fmt::print(stderr, "{}, ", fbname);
}
fb->property_add(Ioss::Property("name_in_output", fbname));
std::string fbtype = fb->topology()->name();
std::string partype = fb->parent_element_topology()->name();
size_t num_side = fb->entity_count();
auto block =
new Ioss::SideBlock(output_region.get_database(), fbname, fbtype, partype, num_side);
surf->add(block);
}
output_region.add(surf);
}
}
}
// Create a nodeset on the output region consisting of all the nodes
// in the input region.
void create_nodal_nodeset(Ioss::Region &region, Ioss::Region &output_region, bool debug)
{
const std::string &prefix = region.name();
std::string name = prefix + "_nodes";
if (output_region.get_nodeset(name) != nullptr) {
fmt::print(stderr, "ERROR: Duplicate node sets named '{}'\n", name);
exit(EXIT_FAILURE);
}
if (debug) {
fmt::print(stderr, "{}, ", name);
}
size_t count = region.get_property("node_count").get_int();
auto ns = new Ioss::NodeSet(output_region.get_database(), name, count);
output_region.add(ns);
}
// Output the bulk data for a nodeset on the output region
// consisting of all the nodes in the input region.
template <typename INT>
void output_nodal_nodeset(Ioss::Region &output_region, RegionVector &part_mesh,
SystemInterface &interFace, const std::vector<INT> &local_node_map)
{
size_t part_count = part_mesh.size();
for (size_t p = 0; p < part_count; p++) {
if (interFace.convert_nodes_to_nodesets(p + 1)) {
std::string name = part_mesh[p]->name() + "_nodes";
Ioss::NodeSet *ons = output_region.get_nodeset(name);
SMART_ASSERT(ons != nullptr)(name);
Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0];
SMART_ASSERT(nb != nullptr);
size_t node_offset = part_mesh[p]->get_property("node_offset").get_int();
std::vector<INT> nodelist;
nb->get_field_data("ids", nodelist);
for (auto &node : nodelist) {
size_t loc_node = part_mesh[p]->node_global_to_local(node, true) - 1;
auto gpos = local_node_map[node_offset + loc_node];
if (gpos >= 0) {
node = gpos + 1;
}
}
ons->put_field_data("ids", nodelist);
// Output distribution factors -- set all to 1.0
std::vector<double> factors(nodelist.size(), 1.0);
ons->put_field_data("distribution_factors", factors);
}
}
}
void define_nodal_nodeset_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list, SystemInterface &interFace)
{
if (!variable_list.empty() && variable_list[0].first == "none") {
return;
}
size_t part_count = part_mesh.size();
for (size_t p = 0; p < part_count; p++) {
if (interFace.convert_nodes_to_nodesets(p + 1)) {
// Find nodeset in output region corresponding to the nodes in this part...
std::string name = part_mesh[p]->name() + "_nodes";
Ioss::NodeSet *ons = output_region.get_nodeset(name);
SMART_ASSERT(ons != nullptr)(name);
Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0];
SMART_ASSERT(nb != nullptr);
SMART_ASSERT(part_mesh[p]->get_property("node_count").get_int() == nb->entity_count());
Ioss::NameList fields = nb->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field_name : fields) {
if (valid_variable(field_name, 0, variable_list)) {
Ioss::Field field = nb->get_field(field_name);
ons->field_add(field);
}
}
}
}
}
void transfer_nodesets(Ioss::Region &region, Ioss::Region &output_region, bool debug)
{
const std::string &prefix = region.name();
const Ioss::NodeSetContainer &nss = region.get_nodesets();
for (auto &ns : nss) {
if (!entity_is_omitted(ns)) {
std::string name = ns->name();
if (output_region.get_nodeset(name) != nullptr) {
name = prefix + "_" + ns->name();
if (output_region.get_nodeset(name) != nullptr) {
fmt::print(stderr, "ERROR: Duplicate node sets named '{}'\n", name);
exit(EXIT_FAILURE);
}
}
ns->property_add(Ioss::Property("name_in_output", name));
if (debug) {
fmt::print(stderr, "{}, ", name);
}
size_t count = ns->entity_count();
auto node_set = new Ioss::NodeSet(output_region.get_database(), name, count);
output_region.add(node_set);
set_id(ns, node_set);
}
}
}
template <typename T, typename INT>
void map_element_vars(size_t loffset, size_t goffset, size_t entity_count, std::vector<T> &values,
std::vector<T> &global_values, INT *part_loc_elem_to_global)
{
// copy values to master element value information
T *local_values = &values[0];
for (size_t j = 0; j < entity_count; j++) {
size_t global_block_pos = part_loc_elem_to_global[(j + loffset)] - goffset;
global_values[global_block_pos] = local_values[j];
}
}
template <typename T>
void map_sideset_vars(size_t loffset, size_t entity_count, std::vector<T> &values,
std::vector<T> &global_values)
{
// copy values to master sideset value information
T *local_values = &values[0];
for (size_t j = 0; j < entity_count; j++) {
global_values[j + loffset] = local_values[j];
}
}
template <typename T, typename U>
void map_nodeset_vars(U & /*unused*/, int /*unused*/, int /*unused*/, std::vector<T> & /*unused*/,
std::vector<T> & /*unused*/)
{
SMART_ASSERT(1 == 0 && "Internal Error!");
}
} // namespace
namespace {
template <typename INT>
void output_nodeblock(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_node_map, std::vector<INT> &global_node_map)
{
Ioss::NodeBlock *onb = output_region.get_node_blocks()[0];
SMART_ASSERT(onb != nullptr);
onb->put_field_data("ids", global_node_map);
int spatial_dimension = output_region.get_property("spatial_dimension").get_int();
std::vector<double> coord(global_node_map.size() * spatial_dimension);
for (const auto &pm : part_mesh) {
Ioss::NodeBlock *nb = pm->get_node_blocks()[0];
SMART_ASSERT(nb != nullptr);
std::vector<double> coordinates;
nb->get_field_data("mesh_model_coordinates", coordinates);
size_t node_count = nb->entity_count();
size_t offset = pm->get_property("node_offset").get_int();
for (size_t i = 0; i < node_count; i++) {
auto glob_pos = local_node_map[i + offset];
if (glob_pos >= 0) {
coord[glob_pos * spatial_dimension + 0] = coordinates[i * spatial_dimension + 0];
coord[glob_pos * spatial_dimension + 1] = coordinates[i * spatial_dimension + 1];
coord[glob_pos * spatial_dimension + 2] = coordinates[i * spatial_dimension + 2];
}
}
}
onb->put_field_data("mesh_model_coordinates", coord);
}
template <typename INT>
void output_elementblock(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_node_map,
const std::vector<INT> &local_element_map, bool ignore_element_ids)
{
const Ioss::ElementBlockContainer &ebs = output_region.get_element_blocks();
size_t element_count = output_region.get_property("element_count").get_int();
std::vector<INT> ids(element_count);
if (ignore_element_ids) {
// Just generate 1..numel ids (much faster for large models)
std::iota(ids.begin(), ids.end(), 1);
}
else {
// Try to maintain the original element ids if possible...
generate_element_ids(part_mesh, local_element_map, ids);
}
size_t element_offset = 0;
for (auto &eb : ebs) {
eb->put_field_data("ids", &ids[element_offset], ids.size() * sizeof(INT));
element_offset += eb->entity_count();
}
SMART_ASSERT(element_offset == element_count);
// Connectivity...
for (const auto &pm : part_mesh) {
const Ioss::ElementBlockContainer &iebs = pm->get_element_blocks();
size_t node_offset = pm->get_property("node_offset").get_int();
for (auto &ieb : iebs) {
if (entity_is_omitted(ieb)) {
continue;
}
std::string name = pm->name() + "_" + ieb->name();
Ioss::ElementBlock *oeb = output_region.get_element_block(name);
if (oeb == nullptr) {
name = ieb->name();
oeb = output_region.get_element_block(name);
}
if (oeb != nullptr) {
std::vector<INT> connectivity;
ieb->get_field_data("connectivity_raw", connectivity);
SMART_ASSERT(ieb->entity_count() == oeb->entity_count());
for (auto &node : connectivity) {
// connectivity is in part-local node ids [1..num_node]
// loc_node = the position of node in the local [0..num_node)
// local_node_map[node_offset+loc_node] gives the position of this node in the global
// list
size_t loc_node = node - 1;
SMART_ASSERT(node_offset + loc_node < local_node_map.size());
auto gpos = local_node_map[node_offset + loc_node];
if (gpos >= 0) {
node = gpos + 1;
}
}
oeb->put_field_data("connectivity_raw", connectivity);
transfer_field_data(ieb, oeb, Ioss::Field::ATTRIBUTE);
}
}
}
}
template <typename INT>
void output_nodeset(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_node_map)
{
if (output_region.get_nodesets().empty()) {
return;
}
for (const auto &pm : part_mesh) {
size_t node_offset = pm->get_property("node_offset").get_int();
const Ioss::NodeSetContainer &ins = pm->get_nodesets();
for (auto &in : ins) {
if (!entity_is_omitted(in)) {
std::vector<INT> nodelist;
in->get_field_data("ids", nodelist);
std::string name = pm->name() + "_" + in->name();
Ioss::NodeSet *ons = output_region.get_nodeset(name);
if (ons == nullptr) {
name = in->name();
ons = output_region.get_nodeset(name);
}
SMART_ASSERT(ons != nullptr)(name);
SMART_ASSERT(in->entity_count() == ons->entity_count());
// This needs to make sure that the nodelist comes back as local id (1..numnodes)
for (auto &node : nodelist) {
size_t loc_node = pm->node_global_to_local(node, true) - 1;
auto gpos = local_node_map[node_offset + loc_node];
if (gpos >= 0) {
node = gpos + 1;
}
}
ons->put_field_data("ids", nodelist);
std::vector<double> df;
in->get_field_data("distribution_factors", df);
ons->put_field_data("distribution_factors", df);
}
}
}
}
template <typename INT>
void output_sideset(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_element_map)
{
const Ioss::SideSetContainer &os = output_region.get_sidesets();
Ioss::SideBlockContainer out_eb;
// Put all output side blocks in the same list...
for (auto &oss : os) {
const Ioss::SideBlockContainer &obs = oss->get_side_blocks();
std::copy(obs.begin(), obs.end(), std::back_inserter(out_eb));
}
// Assuming (with checks) that the output side blocks will be
// iterated in same order as input side blocks...
Ioss::SideBlockContainer::const_iterator II = out_eb.begin();
for (const auto &pm : part_mesh) {
size_t element_offset = pm->get_property("element_offset").get_int();
const Ioss::SideSetContainer &is = pm->get_sidesets();
for (auto &iss : is) {
if (!entity_is_omitted(iss)) {
const Ioss::SideBlockContainer &ebs = iss->get_side_blocks();
for (auto &eb : ebs) {
SMART_ASSERT((eb->name() == (*II)->name()) ||
(pm->name() + "_" + eb->name() == (*II)->name()))
(eb->name())((*II)->name());
SMART_ASSERT(eb->entity_count() == (*II)->entity_count());
std::vector<INT> elem_side_list;
eb->get_field_data("element_side_raw", elem_side_list);
// The 'elem_side_list' contains
// (local_element_position,side_ordinal) pairs. The
// 'local_element_position' is 1-based offset in the
// current part. Need to map to its location in the
// output region...
for (size_t i = 0; i < elem_side_list.size();
i += 2) { // just get the elem part of the pair...
size_t local_position = elem_side_list[i] - 1;
auto gpos = local_element_map[element_offset + local_position];
SMART_ASSERT(gpos >= 0)(gpos)(i); // Inactive elements should be filtered by Ioss
elem_side_list[i] = gpos + 1;
}
(*II)->put_field_data("element_side_raw", elem_side_list);
++II;
}
}
}
}
}
void output_globals(Ioss::Region &output_region, RegionVector &part_mesh)
{
for (const auto &pm : part_mesh) {
Ioss::NameList fields = pm->field_describe(Ioss::Field::REDUCTION);
for (const auto &field : fields) {
std::vector<double> data;
pm->get_field_data(field, data);
output_region.put_field_data(field, data);
}
}
}
template <typename INT>
void output_nodal(Ioss::Region &output_region, RegionVector &part_mesh,
const std::vector<INT> &local_node_map, SystemInterface &interFace)
{
size_t part_count = part_mesh.size();
Ioss::NodeBlock *onb = output_region.get_node_blocks()[0];
SMART_ASSERT(onb != nullptr);
size_t node_count = onb->entity_count();
Ioss::NameList fields = onb->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field : fields) {
size_t comp_count = onb->get_field(field).raw_storage()->component_count();
std::vector<double> data(node_count * comp_count);
for (size_t p = 0; p < part_count; p++) {
if (!interFace.convert_nodes_to_nodesets(p + 1)) {
size_t offset = part_mesh[p]->get_property("node_offset").get_int();
Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0];
SMART_ASSERT(nb != nullptr);
if (nb->field_exists(field)) {
SMART_ASSERT((int)comp_count == nb->get_field(field).raw_storage()->component_count());
std::vector<double> loc_data;
nb->get_field_data(field, loc_data);
size_t nc = nb->entity_count();
SMART_ASSERT(loc_data.size() == nc * comp_count);
for (size_t i = 0; i < nc; i++) {
auto glob_pos = local_node_map[offset + i];
if (glob_pos >= 0) {
for (size_t j = 0; j < comp_count; j++) {
data[glob_pos * comp_count + j] = loc_data[i * comp_count + j];
}
}
}
}
}
}
onb->put_field_data(field, data);
}
}
void output_nodal_nodeset_fields(Ioss::Region &output_region, RegionVector &part_mesh,
SystemInterface &interFace)
{
size_t part_count = part_mesh.size();
for (size_t p = 0; p < part_count; p++) {
if (interFace.convert_nodes_to_nodesets(p + 1)) {
// Find nodeset in output region corresponding to the nodes in this part...
std::string name = part_mesh[p]->name() + "_nodes";
Ioss::NodeSet *ons = output_region.get_nodeset(name);
SMART_ASSERT(ons != nullptr)(name);
Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0];
SMART_ASSERT(nb != nullptr);
SMART_ASSERT(part_mesh[p]->get_property("node_count").get_int() == nb->entity_count());
// NOTE: The node order in the output nodeset 'ons' was
// defined as the same node order in the input nodeblock 'nb',
// so we shouldn't have to do any reordering of the data at
// this time--just read then write.
Ioss::NameList fields = ons->field_describe(Ioss::Field::TRANSIENT);
std::vector<double> data;
for (const auto &field : fields) {
nb->get_field_data(field, data);
ons->put_field_data(field, data);
}
}
}
}
void output_element(Ioss::Region &output_region, RegionVector &part_mesh)
{
for (const auto &pm : part_mesh) {
const Ioss::ElementBlockContainer &iebs = pm->get_element_blocks();
for (auto &ieb : iebs) {
if (!entity_is_omitted(ieb)) {
std::string name = pm->name() + "_" + ieb->name();
Ioss::ElementBlock *oeb = output_region.get_element_block(name);
if (oeb == nullptr) {
name = ieb->name();
oeb = output_region.get_element_block(name);
}
if (oeb != nullptr) {
Ioss::NameList fields = ieb->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field : fields) {
if (oeb->field_exists(field)) {
transfer_field_data_internal(ieb, oeb, field);
}
}
}
}
}
}
}
void output_nset(Ioss::Region &output_region, RegionVector &part_mesh)
{
if (output_region.get_nodesets().empty()) {
return;
}
for (const auto &pm : part_mesh) {
const Ioss::NodeSetContainer &ins = pm->get_nodesets();
for (auto &in : ins) {
if (!entity_is_omitted(in)) {
std::string name = pm->name() + "_" + in->name();
Ioss::NodeSet *ons = output_region.get_nodeset(name);
if (ons == nullptr) {
name = in->name();
ons = output_region.get_nodeset(name);
}
SMART_ASSERT(ons != nullptr)(name);
Ioss::NameList fields = in->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field : fields) {
if (ons->field_exists(field)) {
transfer_field_data_internal(in, ons, field);
}
}
}
}
}
}
void output_sset(Ioss::Region &output_region, RegionVector &part_mesh)
{
const Ioss::SideSetContainer &os = output_region.get_sidesets();
if (os.empty()) {
return;
}
Ioss::SideBlockContainer out_eb;
// Put all output side blocks in the same list...
for (auto &oss : os) {
const Ioss::SideBlockContainer &obs = oss->get_side_blocks();
std::copy(obs.begin(), obs.end(), std::back_inserter(out_eb));
}
// Assuming (with checks) that the output side blocks will be
// iterated in same order as input side blocks...
Ioss::SideBlockContainer::const_iterator II = out_eb.begin();
for (const auto &pm : part_mesh) {
const Ioss::SideSetContainer &is = pm->get_sidesets();
for (auto &iss : is) {
if (!entity_is_omitted(iss)) {
const Ioss::SideBlockContainer &ebs = iss->get_side_blocks();
for (auto &eb : ebs) {
SMART_ASSERT((pm->name() + "_" + eb->name() == (*II)->name()) ||
(eb->name() == (*II)->name()));
Ioss::NameList fields = eb->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field : fields) {
if ((*II)->field_exists(field)) {
transfer_field_data_internal(eb, *II, field);
}
}
++II;
}
}
}
}
}
template <typename INT>
void output_transient_state(Ioss::Region &output_region, RegionVector &part_mesh, double time,
const std::vector<INT> &local_node_map, SystemInterface &interFace)
{
// Determine which state on each input mesh corresponds to 'time'
std::vector<int> steps(part_mesh.size());
for (size_t p = 0; p < part_mesh.size(); p++) {
double min_delta = 1.0e39;
size_t min_step = 0;
size_t nts = part_mesh[p]->get_property("state_count").get_int();
steps[p] = 0;
for (size_t i = 0; i < nts; i++) {
double delta = std::fabs(part_mesh[p]->get_state_time(i + 1) - time);
if (delta < min_delta) {
min_delta = delta;
min_step = i;
if (delta == 0.0) {
break;
}
}
else {
// Delta is increasing; times are moving apart...
// (Assumes monotonically increasing time values...)
break;
}
}
if (nts > 0) {
steps[p] = min_step + 1;
part_mesh[p]->begin_state(steps[p]);
}
}
output_globals(output_region, part_mesh);
output_nodal(output_region, part_mesh, local_node_map, interFace);
output_element(output_region, part_mesh);
output_nodal_nodeset_fields(output_region, part_mesh, interFace);
if (!interFace.omit_nodesets()) {
output_nset(output_region, part_mesh);
}
if (!interFace.omit_sidesets()) {
output_sset(output_region, part_mesh);
}
for (size_t p = 0; p < part_mesh.size(); p++) {
if (steps[p] != 0) {
part_mesh[p]->end_state(steps[p]);
}
}
}
void transfer_field_data(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge,
Ioss::Field::RoleType role, const std::string &prefix,
bool transfer_connectivity)
{
// Iterate through the TRANSIENT-role fields of the input
// database and transfer to output database.
Ioss::NameList state_fields = ige->field_describe(role);
// Complication here is that if the 'role' is 'Ioss::Field::MESH',
// then the 'ids' field must be transferred first...
if (role == Ioss::Field::MESH) {
for (const auto &field_name : state_fields) {
if (oge->field_exists(field_name)) {
if (field_name == "ids") {
transfer_field_data_internal(ige, oge, field_name);
break;
}
}
}
}
for (const auto &field_name : state_fields) {
// All of the 'Ioss::EntityBlock' derived classes have a
// 'connectivity' field, but it is only interesting on the
// Ioss::ElementBlock class. On the other classes, it just
// generates overhead...
if (!transfer_connectivity && field_name == "connectivity") {
continue;
}
if (field_name != "ids" && Ioss::Utils::substr_equal(prefix, field_name)) {
if (oge->field_exists(field_name)) {
transfer_field_data_internal(ige, oge, field_name);
}
}
}
}
void transfer_field_data_internal(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge,
const std::string &field_name)
{
static std::vector<double> data;
assert(ige->get_field(field_name).get_size() == oge->get_field(field_name).get_size());
ige->get_field_data(field_name, data);
oge->put_field_data(field_name, data);
}
void define_global_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list)
{
if (!variable_list.empty() && variable_list[0].first == "none") {
return;
}
for (const auto &pm : part_mesh) {
Ioss::NameList fields = pm->field_describe(Ioss::Field::REDUCTION);
for (const auto &field_name : fields) {
if (valid_variable(field_name, 0, variable_list)) {
Ioss::Field field = pm->get_field(field_name);
output_region.field_add(field);
}
}
}
}
void define_nodal_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list, SystemInterface &interFace)
{
if (!variable_list.empty() && variable_list[0].first == "none") {
return;
}
Ioss::NodeBlock *onb = output_region.get_node_blocks()[0];
SMART_ASSERT(onb != nullptr);
size_t node_count = onb->entity_count();
size_t part_count = part_mesh.size();
for (size_t p = 0; p < part_count; p++) {
if (!interFace.convert_nodes_to_nodesets(p + 1)) {
Ioss::NodeBlock *nb = part_mesh[p]->get_node_blocks()[0];
SMART_ASSERT(nb != nullptr);
Ioss::NameList fields = nb->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field_name : fields) {
if (valid_variable(field_name, 0, variable_list)) {
Ioss::Field field = nb->get_field(field_name);
field.reset_count(node_count);
onb->field_add(field);
}
}
}
}
}
void define_element_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list)
{
// Element Block Fields...
if (!variable_list.empty() && variable_list[0].first == "none") {
return;
}
for (const auto &pm : part_mesh) {
const Ioss::ElementBlockContainer &iebs = pm->get_element_blocks();
for (auto &ieb : iebs) {
if (!entity_is_omitted(ieb)) {
std::string name = pm->name() + "_" + ieb->name();
Ioss::ElementBlock *oeb = output_region.get_element_block(name);
if (oeb == nullptr) {
name = ieb->name();
oeb = output_region.get_element_block(name);
}
if (oeb != nullptr) {
size_t id = oeb->get_property("id").get_int();
Ioss::NameList fields = ieb->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field_name : fields) {
if (valid_variable(field_name, id, variable_list)) {
Ioss::Field field = ieb->get_field(field_name);
oeb->field_add(field);
}
}
}
}
}
}
}
void define_nset_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list)
{
// Nodeset fields...
if (!variable_list.empty() && variable_list[0].first == "none") {
return;
}
for (const auto &pm : part_mesh) {
const Ioss::NodeSetContainer &ins = pm->get_nodesets();
for (auto &in : ins) {
if (!entity_is_omitted(in)) {
std::string name = pm->name() + "_" + in->name();
Ioss::NodeSet *ons = output_region.get_nodeset(name);
if (ons == nullptr) {
name = in->name();
ons = output_region.get_nodeset(name);
}
SMART_ASSERT(ons != nullptr)(name);
size_t id = in->get_property("id").get_int();
Ioss::NameList fields = in->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field_name : fields) {
if (valid_variable(field_name, id, variable_list)) {
Ioss::Field field = in->get_field(field_name);
ons->field_add(field);
}
}
}
}
}
}
void define_sset_fields(Ioss::Region &output_region, RegionVector &part_mesh,
const StringIdVector &variable_list)
{
if (!variable_list.empty() && variable_list[0].first == "none") {
return;
}
const Ioss::SideSetContainer &os = output_region.get_sidesets();
Ioss::SideBlockContainer out_eb;
// Put all output side blocks in the same list...
for (auto &oss : os) {
const Ioss::SideBlockContainer &obs = oss->get_side_blocks();
std::copy(obs.begin(), obs.end(), std::back_inserter(out_eb));
}
// Assuming (with checks) that the output side blocks will be
// iterated in same order as input side blocks...
Ioss::SideBlockContainer::const_iterator II = out_eb.begin();
for (const auto &pm : part_mesh) {
const Ioss::SideSetContainer &is = pm->get_sidesets();
for (auto &iss : is) {
if (!entity_is_omitted(iss)) {
size_t id = iss->get_property("id").get_int();
const Ioss::SideBlockContainer &ebs = iss->get_side_blocks();
for (auto &eb : ebs) {
SMART_ASSERT((pm->name() + "_" + eb->name() == (*II)->name()) ||
(eb->name() == (*II)->name()));
Ioss::NameList fields = eb->field_describe(Ioss::Field::TRANSIENT);
for (const auto &field_name : fields) {
if (valid_variable(field_name, id, variable_list)) {
Ioss::Field field = eb->get_field(field_name);
(*II)->field_add(field);
}
}
++II;
}
}
}
}
}
void transfer_fields(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge,
Ioss::Field::RoleType role, const std::string &prefix)
{
// Check for transient fields...
Ioss::NameList fields = ige->field_describe(role);
// Iterate through results fields and transfer to output
// database... If a prefix is specified, only transfer fields
// whose names begin with the prefix
for (const auto &field_name : fields) {
if (field_name != "ids" && !oge->field_exists(field_name) &&
Ioss::Utils::substr_equal(prefix, field_name)) {
// If the field does not already exist, add it to the output node block
Ioss::Field field = ige->get_field(field_name);
oge->field_add(field);
}
}
}
bool valid_variable(const std::string &variable, size_t id, const StringIdVector &variable_list)
{
if (variable_list.empty() || variable_list[0].first == "all") {
return true;
}
if (variable_list[0].first == "none") {
return false;
}
for (const auto &var : variable_list) {
if (var.first == variable) {
if (id == 0 || id == var.second || var.second == 0) {
return true;
}
}
}
return false;
}
void process_nset_omissions(RegionVector &part_mesh, const Omissions &omit)
{
size_t part_count = part_mesh.size();
for (size_t p = 0; p < part_count; p++) {
if (!omit[p].empty()) {
// Get the nodesets for this part and set the "omitted" property on the nodeset
if (omit[p][0] == "ALL") {
const Ioss::NodeSetContainer &nodesets = part_mesh[p]->get_nodesets();
for (auto &ns : nodesets) {
ns->property_add(Ioss::Property(std::string("omitted"), 1));
}
}
else {
for (const auto &omitted : omit[p]) {
Ioss::NodeSet *ns = part_mesh[p]->get_nodeset(omitted);
if (ns != nullptr) {
ns->property_add(Ioss::Property(std::string("omitted"), 1));
}
}
}
}
}
}
void process_sset_omissions(RegionVector &part_mesh, const Omissions &omit)
{
size_t part_count = part_mesh.size();
for (size_t p = 0; p < part_count; p++) {
if (!omit[p].empty()) {
// Get the sidesets for this part and set the "omitted" property on the sideset
if (omit[p][0] == "ALL") {
const Ioss::SideSetContainer &sidesets = part_mesh[p]->get_sidesets();
for (auto &ss : sidesets) {
ss->property_add(Ioss::Property(std::string("omitted"), 1));
}
}
else {
for (const auto &omitted : omit[p]) {
Ioss::SideSet *ss = part_mesh[p]->get_sideset(omitted);
if (ss != nullptr) {
ss->property_add(Ioss::Property(std::string("omitted"), 1));
}
}
}
}
}
}
void process_assembly_omissions(RegionVector &part_mesh, const Omissions &omit)
{
size_t part_count = part_mesh.size();
for (size_t p = 0; p < part_count; p++) {
if (!omit[p].empty()) {
// Get the assemblies for this part and set the "omitted" property on the assembly
if (omit[p][0] == "ALL") {
const auto &assemblies = part_mesh[p]->get_assemblies();
for (auto &as : assemblies) {
as->property_add(Ioss::Property(std::string("omitted"), 1));
}
}
else {
for (const auto &omitted : omit[p]) {
auto *as = part_mesh[p]->get_assembly(omitted);
if (as != nullptr) {
as->property_add(Ioss::Property(std::string("omitted"), 1));
}
}
}
}
}
}
} // namespace