From da78685b2d3e2c972c0b1f444eadc53436f33c87 Mon Sep 17 00:00:00 2001 From: Tolu Okusanya Date: Tue, 2 Jul 2024 11:53:17 -0600 Subject: [PATCH 01/59] Ioss: Initial workflow design to support dynamic topology Basic idea is to create a topology modification observer that can be registered on the region. This provides a callback mechanism for re-defining the Region mesh when a topology change occurs. The API is still currently fluid and will change until something is settled upon. A pre-requsite to this was adding the capability for the Region to handle file creation such that it can either create multiple output files when the topology changes or uses the NETCDF capability to create multiple file groups within a single file. Unit tests are included to show how use cases drive the development --- .../libraries/ioss/src/Ioss_DatabaseIO.C | 5 + .../libraries/ioss/src/Ioss_DatabaseIO.h | 31 +- .../libraries/ioss/src/Ioss_DynamicTopology.C | 565 +++++++++++++++ .../libraries/ioss/src/Ioss_DynamicTopology.h | 156 ++++ .../libraries/ioss/src/Ioss_IOFactory.C | 2 + .../seacas/libraries/ioss/src/Ioss_Region.C | 309 ++++++-- .../seacas/libraries/ioss/src/Ioss_Region.h | 35 + .../ioss/src/exodus/Ioex_BaseDatabaseIO.C | 108 +++ .../ioss/src/exodus/Ioex_BaseDatabaseIO.h | 4 + .../libraries/ioss/src/exodus/Ioex_Utils.C | 3 +- .../ioss/src/unit_tests/CMakeLists.txt | 11 + .../src/unit_tests/UnitTestDynamicTopology.C | 684 ++++++++++++++++++ 12 files changed, 1858 insertions(+), 55 deletions(-) create mode 100644 packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C create mode 100644 packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h create mode 100644 packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C diff --git a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C index 7751bcdb41..8ab2ccac4d 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C @@ -379,6 +379,11 @@ namespace Ioss { DatabaseIO::~DatabaseIO() = default; + Ioss::DataSize DatabaseIO::int_byte_size_data_size() const + { + return dbIntSizeAPI; + } + int DatabaseIO::int_byte_size_api() const { if (dbIntSizeAPI == USE_INT32_API) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h index 90a83ffe2b..020cd71346 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h @@ -279,6 +279,18 @@ namespace Ioss { return create_subgroup_nl(group_name); } + bool open_root_group() + { + IOSS_FUNC_ENTER(m_); + return open_root_group_nl(); + } + + bool groups_describe(Ioss::NameList& names, Ioss::NameList *full_names = nullptr) + { + IOSS_FUNC_ENTER(m_); + return groups_describe_nl(names, full_names); + } + /** \brief Set the database to the given State. * * All transitions must begin from the 'STATE_CLOSED' state or be to @@ -504,6 +516,7 @@ namespace Ioss { IOSS_NODISCARD virtual int int_byte_size_db() const = 0; //! Returns 4 or 8 IOSS_NODISCARD int int_byte_size_api() const; //! Returns 4 or 8 virtual void set_int_byte_size_api(Ioss::DataSize size) const; + IOSS_NODISCARD Ioss::DataSize int_byte_size_data_size() const; /*! * The owning region of this database. @@ -739,6 +752,14 @@ namespace Ioss { virtual void closeDatabase_nl() const; virtual void flush_database_nl() const {} + virtual void release_memory_nl() + { + nodeMap.release_memory(); + edgeMap.release_memory(); + faceMap.release_memory(); + elemMap.release_memory(); + } + private: virtual bool ok_nl(bool /* write_message */, std::string * /* error_message */, int *bad_count) const @@ -759,16 +780,10 @@ namespace Ioss { return elemMap.global_to_local(global); } - virtual void release_memory_nl() - { - nodeMap.release_memory(); - edgeMap.release_memory(); - faceMap.release_memory(); - elemMap.release_memory(); - } - + virtual bool open_root_group_nl() { return false; } virtual bool open_group_nl(const std::string & /* group_name */) { return false; } virtual bool create_subgroup_nl(const std::string & /* group_name */) { return false; } + virtual bool groups_describe_nl(Ioss::NameList& /* names */, Ioss::NameList * /* full_names */) { return false; } virtual bool begin_nl(Ioss::State state) = 0; virtual bool end_nl(Ioss::State state) = 0; diff --git a/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C new file mode 100644 index 0000000000..85f7d6e783 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C @@ -0,0 +1,565 @@ +// Copyright(C) 1999-2024 National Technology & Engineering Solutions +// of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with +// NTESS, the U.S. Government retains certain rights in this software. +// +// See packages/seacas/LICENSE for details + +#include "Ioss_Assembly.h" +#include "Ioss_Blob.h" +#include "Ioss_CodeTypes.h" +#include "Ioss_CommSet.h" +#include "Ioss_CoordinateFrame.h" +#include "Ioss_DBUsage.h" +#include "Ioss_DatabaseIO.h" +#include "Ioss_DynamicTopology.h" +#include "Ioss_EdgeBlock.h" +#include "Ioss_EdgeSet.h" +#include "Ioss_ElementBlock.h" +#include "Ioss_ElementSet.h" +#include "Ioss_EntityBlock.h" +#include "Ioss_EntityType.h" +#include "Ioss_FaceBlock.h" +#include "Ioss_FaceSet.h" +#include "Ioss_Field.h" +#include "Ioss_FileInfo.h" +#include "Ioss_GroupingEntity.h" +#include "Ioss_IOFactory.h" +#include "Ioss_NodeBlock.h" +#include "Ioss_NodeSet.h" +#include "Ioss_Property.h" +#include "Ioss_PropertyManager.h" +#include "Ioss_Region.h" +#include "Ioss_SideBlock.h" +#include "Ioss_SideSet.h" +#include "Ioss_SmartAssert.h" +#include "Ioss_Sort.h" +#include "Ioss_State.h" +#include "Ioss_StructuredBlock.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Ioss_MeshType.h" +#include "Ioss_ParallelUtils.h" + +namespace Ioss { + +void DynamicTopologyObserver::check_region() const +{ + if(nullptr == m_region) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: A region has not been registered with the " + "Dynamic Topology Observer.\n\n"); + IOSS_ERROR(errmsg); + } +} + +void DynamicTopologyObserver::register_region(Region *region) +{ + if(nullptr != m_region && region != m_region) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: Attempt to re-register different region on " + "Dynamic Topology Observer.\n\n"); + IOSS_ERROR(errmsg); + } + + m_region = region; +} + +void DynamicTopologyObserver::set_cumulative_topology_modification(unsigned int type) +{ m_cumulativeTopologyModification = type; } + +unsigned int DynamicTopologyObserver::get_cumulative_topology_modification() const +{ return m_cumulativeTopologyModification; } + +unsigned int DynamicTopologyObserver::get_topology_modification() const +{ return m_topologyModification; } + +void DynamicTopologyObserver::set_topology_modification(unsigned int type) +{ + m_topologyModification |= type; + m_cumulativeTopologyModification |= type; +} + +void DynamicTopologyObserver::reset_topology_modification() +{ + m_topologyModification = TOPOLOGY_SAME; +} + +bool DynamicTopologyObserver::is_topology_modified() const +{ return m_topologyModification != 0; } + +const ParallelUtils &DynamicTopologyObserver::util() const +{ + check_region(); + return m_region->get_database()->util(); +} + +void DynamicTopologyObserver::synchronize_topology_modified_flags() +{ + check_region(); + int num_processors = m_region->get_database()->parallel_size(); + // Synchronize the topology flags between all processors in case + // it has not been set consistently. + if (num_processors > 1) { + static unsigned int buffer[2]; + buffer[0] = m_cumulativeTopologyModification; + buffer[1] = m_topologyModification; + + util().attribute_reduction(2*sizeof(unsigned int), reinterpret_cast(buffer)); + + m_cumulativeTopologyModification = buffer[0]; + m_topologyModification = buffer[1]; + } +} + +int DynamicTopologyObserver::get_cumulative_topology_modification_field() +{ + check_region(); + const std::string variable_name = topology_modification_change_name(); + + int ivalue = 0; + + if (m_region->field_exists(variable_name)) { + Field topo_field = m_region->get_field(variable_name); + if (topo_field.get_type() == Field::INTEGER) { + m_region->get_field_data(variable_name, &ivalue, sizeof(int)); + } else { + double value; + m_region->get_field_data(variable_name, &value, sizeof(double)); + ivalue = (int)value; + } + } + + int num_processors = m_region->get_database()->parallel_size(); + // Synchronize the value between all processors in case + // it has not been set consistently. + if (num_processors > 1) { + unsigned int buffer[1]; + buffer[0] = ivalue; + + util().attribute_reduction(sizeof(unsigned int), reinterpret_cast(buffer)); + + ivalue = (int)buffer[0]; + } + + m_cumulativeTopologyModification = ivalue; + + return ivalue; +} + +void DynamicTopologyObserver::define_model(Region& region) +{ + +} + +void DynamicTopologyObserver::write_model(Region& region) +{ + +} + +void DynamicTopologyObserver::define_transient(Region& region) +{ + +} + +DynamicTopologyFileControl::DynamicTopologyFileControl(Region *region, unsigned int fileCyclicCount, + IfDatabaseExistsBehavior &ifDatabaseExists, + unsigned int &dbChangeCount) + : m_region(region) + , m_fileCyclicCount(fileCyclicCount) + , m_ifDatabaseExists(ifDatabaseExists) + , m_dbChangeCount(dbChangeCount) +{ + if(nullptr == region) { + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR: null region passed in as argument to DynamicTopologyFileControl"); + IOSS_ERROR(errmsg); + } + + m_ioDB = region->get_property("base_filename").get_string(); + m_dbType = region->get_property("database_type").get_string(); +} + +const ParallelUtils &DynamicTopologyFileControl::util() const +{ + return m_region->get_database()->util(); +} + +bool DynamicTopologyFileControl::file_exists(const std::string &filename, + const std::string &db_type, + Ioss::DatabaseUsage db_usage) +{ + bool exists = false; + int par_size = m_region->get_database()->parallel_size(); + int par_rank = m_region->get_database()->parallel_rank(); + bool is_parallel = par_size > 1; + std::string full_filename = filename; + if (is_parallel && db_type == "exodusII" && db_usage != Ioss::WRITE_HISTORY) { + full_filename = Ioss::Utils::decode_filename(filename, par_rank, par_size); + } + + if (!is_parallel || par_rank == 0) { + // Now, see if this file exists... + // Don't want to do a system call on all processors since it can take minutes + // on some of the larger machines, filesystems, and processor counts... + Ioss::FileInfo file = Ioss::FileInfo(full_filename); + exists = file.exists(); + } + + if (is_parallel) { + int iexists = exists ? 1 : 0; + util().broadcast(iexists, 0); + exists = iexists == 1; + } + return exists; +} + +std::string DynamicTopologyFileControl::get_unique_filename(Ioss::DatabaseUsage db_usage) +{ + std::string filename = m_ioDB; + + do { + // Run this loop at least once for all files. If this is an automatic + // restart, then make sure that the generated file does not already exist, + // so keep running the loop until we generate a filename that doesn't exist... + std::ostringstream tmp_filename; + tmp_filename << m_ioDB; + + // Don't append the "-s000X" the first time in case the base filename doesn't + // exist -- we want write to the name specified by the user if at all possible and + // once that exists, then start adding on the suffix... + if (m_dbChangeCount > 1) { + tmp_filename << "-s" << std::setw(4) << std::setfill('0') << m_dbChangeCount; + } + filename = tmp_filename.str(); + ++m_dbChangeCount; + } while(file_exists(filename, m_dbType, db_usage)); + --m_dbChangeCount; + return filename; +} + +std::string DynamicTopologyFileControl::construct_database_filename(int& step, Ioss::DatabaseUsage db_usage) +{ + // Filename will be of the form -- ioDB-sxxxx where xxxx is step + // number. Assume maximum of 9999 steps (will do more, but won't have + // good lineup of step numbers. + // Check database for validity (filename and a type) + if(m_ioDB == "" || m_dbType == "") + { + std::string error_message; + if(m_dbType == "") + error_message += "The database TYPE has not been defined\n"; + + if(m_ioDB == "") + { + error_message += "The database FILENAME has not been defined\n"; + } + std::ostringstream errmsg; + fmt::print(errmsg, error_message); + IOSS_ERROR(errmsg); + } + assert(m_ioDB != ""); + assert(m_dbType != ""); + std::string filename = m_ioDB; + if(m_fileCyclicCount > 0) + { + // In this mode, we close the old file and open a new file + // every time this is called. The file suffix cycles through + // the first fileCyclicCount'th entries in A,B,C,D,E,F,... + if(step == 0) + step++; + + static std::string suffix = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + std::string tmp = "-" + suffix.substr((step - 1) % m_fileCyclicCount, 1); + filename += tmp; + m_properties.add(Ioss::Property("APPEND_OUTPUT", Ioss::DB_OVERWRITE)); + } + else + { + if(m_region->model_is_written()) + { + // After the initial open, we want to add suffix if the topology changes + // during the run + m_ifDatabaseExists = Ioss::DB_ADD_SUFFIX_OVERWRITE; + } + + // Handle complications of DB_APPEND mode... + // If in DB_APPEND mode, then we don't output metadata + // information, so some knowledge is needed at this level if + // we are appending. If user specified APPEND, but the file + // doesn't yet exist OR it does exist and we are not + // restarting, change the mode to OVERWRITE. + // 0. Must be restarting; either manual or automatic. + std::shared_ptr observer = m_region->get_mesh_modification_observer(); + + if(m_ifDatabaseExists == Ioss::DB_APPEND) + { + if(!observer->is_restart_requested()) + { + // Not restarting + m_ifDatabaseExists = Ioss::DB_OVERWRITE; + } + else if(!file_exists(m_ioDB, m_dbType, db_usage)) + { + m_ifDatabaseExists = Ioss::DB_OVERWRITE; + } + } + if(step > 1 || (m_dbChangeCount > 1)) + { + // Use the !is_input_event test since restart input files already have the + // -s000x extension... + if(m_ifDatabaseExists == Ioss::DB_APPEND) + { + std::ostringstream tmp_filename; + tmp_filename << m_ioDB; + filename = m_ioDB; + if(m_dbChangeCount > 1) + { + tmp_filename << "-s" << std::setw(4) << std::setfill('0') << m_dbChangeCount; + } + size_t inc = 0; + while(file_exists(tmp_filename.str(), m_dbType, db_usage)) + { + filename = tmp_filename.str(); + tmp_filename.clear(); + tmp_filename.str(""); + tmp_filename << m_ioDB << "-s" << std::setw(4) << std::setfill('0') << m_dbChangeCount + (++inc); + } + if(inc > 0) + { + m_dbChangeCount += (inc - 1); + } + else + { + m_ifDatabaseExists = Ioss::DB_OVERWRITE; + } + } + else if(m_ifDatabaseExists == Ioss::DB_ADD_SUFFIX) + { + filename = get_unique_filename(db_usage); + } + else if(m_ifDatabaseExists == Ioss::DB_ADD_SUFFIX_OVERWRITE) + { + if(m_dbChangeCount > 0) + { + std::ostringstream tmp_filename; + tmp_filename << m_ioDB << "-s" << std::setw(4) << std::setfill('0') << ++m_dbChangeCount; + filename = tmp_filename.str(); + } + else + { + filename = m_ioDB; + } + } + else + { + filename = m_ioDB; + } + } + else if(m_ifDatabaseExists == Ioss::DB_ADD_SUFFIX) + { + filename = get_unique_filename(db_usage); + } + else + { + filename = m_ioDB; + } + + m_properties.add(Ioss::Property("APPEND_OUTPUT", m_ifDatabaseExists)); + // A little complicated on deciding whether we are actually + // overwriting the database. The 'validate' routine for Results and + // History will call create_database once the parser block is + // ended. This routine will then create the database and the + // ioRegion_. However, the database will not really be opened or + // written to at this time. If the code is auto-restarting, then it will + // detect that the database exists and create a database with the + // -s000x extension. + // At this point, we need to skip the 'abort_if_exists' test if we + // are in this routine from the 'validate' and we are restarting + // since we won't really write to the file. So, the cases where we + // *don't* check are: + // -- is_input_event(db_usage) + // -- ifExists_ == DB_OVERWRITE || DB_ADD_SUFFIX_OVERWRITE || DB_APPEND + // -- is_automatic_restart() && step == 0 (coming from validate) + if(m_ifDatabaseExists != DB_OVERWRITE && + m_ifDatabaseExists != DB_APPEND && + m_ifDatabaseExists != DB_ADD_SUFFIX_OVERWRITE && + !(step == 0 && observer->is_automatic_restart())) + { + abort_if_exists(filename, m_dbType, db_usage); + } + } + return filename; +} + +bool DynamicTopologyFileControl::abort_if_exists(const std::string &filename, + const std::string &db_type, + Ioss::DatabaseUsage db_usage) +{ + // Check whether file with same name as database already exists. If so, + // print error message and stop... + // At the current time, only check on processor 0 and assume if it doesn't exist + // there, then it doesn't exist on other processors. Or, if it doesn't exist on + // processor 0, then it doesn't matter if it doesn't exist on other processors + // since we don't have all pieces... + + bool exists = file_exists(filename, db_type, db_usage); + if (exists) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: The database file named '{} exists" + "and would be overwritten if the code continued.\n\n" + "Input options specified that this file *not* be overwritten,\n" + "\tso you must rename or remove this file and restart the code.\n", + filename); + IOSS_ERROR(errmsg); + } + return exists; +} + +Ioss::DatabaseIO * DynamicTopologyFileControl::clone_output_database(int steps) +{ + auto current_db = m_region->get_database(); + + if (current_db->is_input()) + return nullptr; + + const Ioss::PropertyManager& current_properties = current_db->get_property_manager(); + Ioss::NameList names; + current_properties.describe(&names); + + // Iterate through properties and transfer to new output database... + Ioss::NameList::const_iterator I; + for (I = names.begin(); I != names.end(); ++I) { + if (!current_properties.exists(*I)) + m_properties.add(current_properties.get(*I)); + } + + auto db_usage = current_db->usage(); + + std::string filename = construct_database_filename(steps, db_usage); + + Ioss::DatabaseIO *db = Ioss::IOFactory::create(m_dbType, filename, db_usage, + current_db->util().communicator(), + m_properties); + + if (nullptr == db) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: unable to create output database named '{}'" + " of type '{}'", filename, m_dbType); + IOSS_ERROR(errmsg); + } + + assert(db != nullptr); + if(!db->ok(true)) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: unable to validate output database named '{}'" + " of type '{}'", filename, m_dbType); + IOSS_ERROR(errmsg); + } + + db->set_field_separator(current_db->get_field_separator()); + db->set_surface_split_type(current_db->get_surface_split_type()); + db->set_maximum_symbol_length(current_db->maximum_symbol_length()); + db->set_int_byte_size_api(current_db->int_byte_size_data_size()); + + return db; +} + +template +void update_database_for_grouping_entities(const T& container, Ioss::DatabaseIO *db) +{ + for(auto * entity : container) { + Ioss::GroupingEntity* ge = dynamic_cast(entity); + assert(ge != nullptr); + + if(ge->type() == Ioss::SIDESET) { + Ioss::SideSet *sset = dynamic_cast(ge); + assert(sset != nullptr); + + sset->reset_database(db); + const auto &sblocks = sset->get_side_blocks(); + for (const auto &sblock : sblocks) { + sblock->reset_database(db); + } + } else { + ge->reset_database(db); + } + } +} + +bool DynamicTopologyFileControl::replace_output_database(Ioss::DatabaseIO *db) +{ + auto current_db = m_region->get_database(); + + if (current_db->is_input()) + return false; + + current_db->finalize_database(); + current_db->flush_database(); + current_db->closeDatabase(); + delete current_db; + + m_region->reset_database(db); + db->set_region(m_region); + + update_database_for_grouping_entities(m_region->get_node_blocks(), db); + update_database_for_grouping_entities(m_region->get_edge_blocks(), db); + update_database_for_grouping_entities(m_region->get_face_blocks(), db); + update_database_for_grouping_entities(m_region->get_element_blocks(), db); + update_database_for_grouping_entities(m_region->get_sidesets(), db); + update_database_for_grouping_entities(m_region->get_nodesets(), db); + update_database_for_grouping_entities(m_region->get_edgesets(), db); + update_database_for_grouping_entities(m_region->get_facesets(), db); + update_database_for_grouping_entities(m_region->get_elementsets(), db); + update_database_for_grouping_entities(m_region->get_commsets(), db); + update_database_for_grouping_entities(m_region->get_structured_blocks(), db); + update_database_for_grouping_entities(m_region->get_assemblies(), db); + update_database_for_grouping_entities(m_region->get_blobs(), db); + + return true; +} + +void DynamicTopologyFileControl::clone_and_replace_output_database(int steps) +{ + auto db = clone_output_database(steps); + + if(nullptr != db) + replace_output_database(db); +} + +void DynamicTopologyFileControl::add_output_database_group(int steps) +{ + auto current_db = m_region->get_database(); + + std::ostringstream oss; + oss << "STEP-"; + oss << steps; + + current_db->release_memory(); + current_db->open_root_group(); + current_db->create_subgroup(oss.str()); +} + +} + + + + diff --git a/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h new file mode 100644 index 0000000000..376faa714b --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h @@ -0,0 +1,156 @@ +// Copyright(C) 1999-2024 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 + +#pragma once + +#include "Ioss_DatabaseIO.h" // for DatabaseIO +#include "Ioss_DBUsage.h" +#include "Ioss_EntityType.h" // for EntityType, etc +#include "Ioss_Field.h" +#include "Ioss_GroupingEntity.h" // for GroupingEntity +#include "Ioss_MeshType.h" +#include "Ioss_ParallelUtils.h" // for ParallelUtils +#include "Ioss_Property.h" // for Property +#include "Ioss_State.h" // for State +#include +#include // for size_t, nullptr +#include // for int64_t + +#include "Ioss_CodeTypes.h" +#include "Ioss_Utils.h" +#include "Ioss_VariableType.h" +#include "ioss_export.h" +#if !defined BUILT_IN_SIERRA +#include +#endif +#include // for less +#include // for ostream +#include // for map, map<>::value_compare +#include +#include // for string, operator< +#include // for pair +#include // for vector + +namespace Ioss { + class Region; + + /*! The TopologyModified enumeration is used as an argument to the + * topology_modified() functions in io to + * specify the type of topology modification that has ocurred. The + * calls to topology_modified() are cumulative between + * output steps, so a call with TOPOLOGY_REORDER followed by a call + * with TOPOLOGY_SHUFFLE will do the right thing. Typical examples + * of when these would be used are: + * - TOPOLOGY_SAME: No change, but easier to call function than not. + * - TOPOLOGY_REORDER: Element Death which reorders the Registrars + * - TOPOLOGY_SHUFFLE: Load Balancing + * - TOPOLOGY_HADAPT: H-Adaptivity + * - TOPOLOGY_GHOST: Ghost nodes/edges/faces/elements created/destroyed + * - TOPOLOGY_GEOMETRY: Model data is modified, overlap removal. + * - TOPOLOGY_CREATEDELETE: Surface erosion, particle creation + * - TOPOLOGY_UNKNOWN: Something else, catchall option. + */ + enum TopologyModified { + TOPOLOGY_SAME = ( 0), //!< No change, also used for initialization + TOPOLOGY_REORDER = (1U << 0), //!< Data structures reordered on processor, no change between procs. + TOPOLOGY_SHUFFLE = (1U << 1), //!< Globally the same, data moved among processors. + TOPOLOGY_HADAPT = (1U << 2), //!< Elements split/combined; not moved cross-proc + TOPOLOGY_GHOST = (1U << 3), //!< Ghost entities created/destroyed + TOPOLOGY_GEOMETRY = (1U << 4), //!< Geometry (mesh coordinates) modified. Restart needs to know this. + TOPOLOGY_CREATEFACE = (1U << 5), //!< Face/Edge are created/deleted. + TOPOLOGY_CREATEELEM = (1U << 6), //!< Elements are created/deleted. + TOPOLOGY_CREATENODE = (1U << 7), //!< Nodes are created/deleted. + TOPOLOGY_UNKNOWN = (1U << 8), //!< Unknown change, recreate from scratch. + TOPOLOGY_AUXILIARY = (1U << 9), //!< An AUXILIARY relation was created/modified. + TOPOLOGY_CONSTRAINT = (1U << 10) //!< Contact constraints + }; + + enum class FileControlOption { + CONTROL_NONE, + CONTROL_AUTO_MULTI_FILE, + CONTROL_AUTO_SINGLE_FILE + }; + + class IOSS_EXPORT DynamicTopologyObserver + { + public: + DynamicTopologyObserver(Region *region) + : m_region(region) {} + DynamicTopologyObserver() {} + + virtual ~DynamicTopologyObserver() {} + + virtual void reset_topology_modification(); + virtual void set_topology_modification(unsigned int type); + virtual unsigned int get_topology_modification() const; + + virtual unsigned int get_cumulative_topology_modification() const; + virtual void set_cumulative_topology_modification(unsigned int type); + + int get_cumulative_topology_modification_field(); + + virtual bool is_topology_modified() const; + virtual bool is_automatic_restart() const { return false; } + virtual bool is_restart_requested() const { return false; } + + static const std::string topology_modification_change_name() {return std::string("CUMULATIVE_TOPOLOGY_MODIFICATION");} + + void register_region(Region *region); + Region* get_region() const { return m_region; } + + virtual void define_model(Region& region); + virtual void write_model(Region& region); + virtual void define_transient(Region& region); + + virtual FileControlOption get_control_option() const { return FileControlOption::CONTROL_AUTO_MULTI_FILE; } + + protected: + Region *m_region{nullptr}; + unsigned int m_topologyModification{TOPOLOGY_SAME}; + unsigned int m_cumulativeTopologyModification{TOPOLOGY_SAME}; + + bool m_automaticRestart{false}; + bool m_restartRequested{false}; + + void check_region() const; + IOSS_NODISCARD const ParallelUtils &util() const; + void synchronize_topology_modified_flags(); + }; + + class IOSS_EXPORT DynamicTopologyFileControl + { + public: + DynamicTopologyFileControl(Region *region, unsigned int fileCyclicCount, + IfDatabaseExistsBehavior &ifDatabaseExists, + unsigned int &dbChangeCount); + + void clone_and_replace_output_database(int steps=0); + void add_output_database_group(int steps=0); + + private: + Region *m_region{nullptr}; + std::string m_ioDB; + std::string m_dbType; + + PropertyManager m_properties; + + unsigned int m_fileCyclicCount; + IfDatabaseExistsBehavior &m_ifDatabaseExists; + unsigned int &m_dbChangeCount; + + IOSS_NODISCARD const ParallelUtils &util() const; + + std::string get_unique_filename(DatabaseUsage db_usage); + std::string construct_database_filename(int& step, DatabaseUsage db_usage); + bool file_exists(const std::string &filename, const std::string &db_type, DatabaseUsage db_usage); + bool abort_if_exists(const std::string &filename, const std::string &db_type, + DatabaseUsage db_usage); + + DatabaseIO * clone_output_database(int steps); + bool replace_output_database(DatabaseIO *db); + }; + +} // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_IOFactory.C b/packages/seacas/libraries/ioss/src/Ioss_IOFactory.C index e020d8dd37..463040ed0f 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_IOFactory.C +++ b/packages/seacas/libraries/ioss/src/Ioss_IOFactory.C @@ -94,6 +94,8 @@ Ioss::DatabaseIO *Ioss::IOFactory::create(const std::string &type, const std::st } else { auto my_props(properties); + my_props.add(Property("database_type", type)); + Ioss::ParallelUtils pu(communicator); pu.add_environment_properties(my_props); if (my_props.exists("SHOW_CONFIG")) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_Region.C b/packages/seacas/libraries/ioss/src/Ioss_Region.C index 7f220b7381..b5a40b9b3e 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Region.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Region.C @@ -20,7 +20,9 @@ #include "Ioss_FaceBlock.h" #include "Ioss_FaceSet.h" #include "Ioss_Field.h" +#include "Ioss_FileInfo.h" #include "Ioss_GroupingEntity.h" +#include "Ioss_IOFactory.h" #include "Ioss_NodeBlock.h" #include "Ioss_NodeSet.h" #include "Ioss_Property.h" @@ -43,6 +45,11 @@ #include #include +#include +#include +#include +#include + #include "Ioss_MeshType.h" #include "Ioss_ParallelUtils.h" @@ -95,20 +102,30 @@ namespace { return count; } + void update_database(Ioss::DatabaseIO *db, Ioss::GroupingEntity *entity) + { + entity->reset_database(db); + } + void update_database(const Ioss::Region *region, Ioss::GroupingEntity *entity) { - entity->reset_database(region->get_database()); + update_database(region->get_database(), entity); } - void update_database(const Ioss::Region *region, Ioss::SideSet *sset) + void update_database(Ioss::DatabaseIO *db, Ioss::SideSet *sset) { - sset->reset_database(region->get_database()); + sset->reset_database(db); const auto &blocks = sset->get_side_blocks(); for (const auto &block : blocks) { - block->reset_database(region->get_database()); + block->reset_database(db); } } + void update_database(const Ioss::Region *region, Ioss::SideSet *sset) + { + update_database(region->get_database(), sset); + } + constexpr unsigned numberOfBits(unsigned x) { return x < 2 ? x : 1 + numberOfBits(x >> 1); } size_t compute_hash(Ioss::GroupingEntity *entity, size_t which) @@ -348,6 +365,9 @@ namespace Ioss { properties.add(Property(this, "state_count", Property::INTEGER)); properties.add(Property(this, "current_state", Property::INTEGER)); properties.add(Property(this, "database_name", Property::STRING)); + + property_add(Property("base_filename", iodatabase->get_filename())); + property_add(Property("database_type", iodatabase->get_property_manager().get_optional("database_type", ""))); } Region::~Region() @@ -358,63 +378,86 @@ namespace Ioss { // Region owns all sub-grouping entities it contains... try { IOSS_FUNC_ENTER(m_); - for (const auto &nb : nodeBlocks) { - delete (nb); - } + release_memory(); - for (const auto &eb : edgeBlocks) { - delete (eb); - } + // Region owns the database pointer even though other entities use it. + GroupingEntity::really_delete_database(); + } + catch (...) { + } + } - for (const auto &fb : faceBlocks) { - delete (fb); - } + void Region::release_memory() + { + for (const auto &nb : nodeBlocks) { + delete (nb); + } + nodeBlocks.clear(); - for (const auto &eb : elementBlocks) { - delete (eb); - } + for (const auto &eb : edgeBlocks) { + delete (eb); + } + edgeBlocks.clear(); - for (const auto &sb : structuredBlocks) { - delete (sb); - } + for (const auto &fb : faceBlocks) { + delete (fb); + } + faceBlocks.clear(); - for (const auto &ss : sideSets) { - delete (ss); - } + for (const auto &eb : elementBlocks) { + delete (eb); + } + elementBlocks.clear(); - for (const auto &ns : nodeSets) { - delete (ns); - } + for (const auto &sb : structuredBlocks) { + delete (sb); + } + structuredBlocks.clear(); - for (const auto &es : edgeSets) { - delete (es); - } + for (const auto &ss : sideSets) { + delete (ss); + } + sideSets.clear(); - for (const auto &fs : faceSets) { - delete (fs); - } + for (const auto &ns : nodeSets) { + delete (ns); + } + nodeSets.clear(); - for (const auto &es : elementSets) { - delete (es); - } + for (const auto &es : edgeSets) { + delete (es); + } + edgeSets.clear(); - for (const auto &cs : commSets) { - delete (cs); - } + for (const auto &fs : faceSets) { + delete (fs); + } + faceSets.clear(); - for (const auto &as : assemblies) { - delete (as); - } + for (const auto &es : elementSets) { + delete (es); + } + elementSets.clear(); - for (const auto &bl : blobs) { - delete (bl); - } + for (const auto &cs : commSets) { + delete (cs); + } + commSets.clear(); - // Region owns the database pointer even though other entities use it. - GroupingEntity::really_delete_database(); + for (const auto &as : assemblies) { + delete (as); } - catch (...) { + assemblies.clear(); + + for (const auto &bl : blobs) { + delete (bl); } + blobs.clear(); + + stateTimes.clear(); + + currentState = -1; + stateCount = 0; } void Region::delete_database() { GroupingEntity::really_delete_database(); } @@ -652,6 +695,27 @@ namespace Ioss { success = set_state(new_state); } else { + bool has_output_observer = topologyObserver && !get_database()->is_input(); + + if(new_state == STATE_DEFINE_MODEL) { + if(has_output_observer && (topologyObserver->get_control_option() == FileControlOption::CONTROL_AUTO_SINGLE_FILE)) { + if(!modelWritten) { + int steps = get_property("state_count").get_int(); + bool force_addition = true; + add_output_database_group(steps, force_addition); + } + } + } else if(new_state == STATE_TRANSIENT) { + if(has_output_observer && topologyObserver->is_topology_modified()) { + int steps = get_property("state_count").get_int(); + start_new_output_database_entry(steps); + + topologyObserver->define_model(*this); + topologyObserver->write_model(*this); + topologyObserver->define_transient(*this); + topologyObserver->reset_topology_modification(); + } + } switch (get_state()) { case STATE_CLOSED: // Make sure we can go to the specified state. @@ -785,6 +849,12 @@ namespace Ioss { else if (current_state == STATE_DEFINE_TRANSIENT) { transientDefined = true; } + else if (current_state == STATE_MODEL) { + modelWritten = true; + } + else if (current_state == STATE_TRANSIENT) { + transientWritten = true; + } return success; } @@ -2741,4 +2811,151 @@ namespace Ioss { } } + void Region::register_mesh_modification_observer(std::shared_ptr observer) + { + if(observer && observer->get_control_option() == FileControlOption::CONTROL_AUTO_SINGLE_FILE) { + const Ioss::PropertyManager &properties = get_database()->get_property_manager(); + if(!properties.exists("ENABLE_FILE_GROUPS")) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: File groups are not enabled in the database file '{}'.\n", + get_database()->get_filename()); + IOSS_ERROR(errmsg); + } + } + + topologyObserver = observer; + topologyObserver->register_region(this); + } + + void Region::start_new_output_database_entry(int steps) + { + if (get_database()->is_input()) + return; + + if(!topologyObserver) + return; + + switch(topologyObserver->get_control_option()) { + case FileControlOption::CONTROL_AUTO_MULTI_FILE: + clone_and_replace_output_database(steps); + break; + case FileControlOption::CONTROL_AUTO_SINGLE_FILE: + add_output_database_group(steps); + break; + case FileControlOption::CONTROL_NONE: + default: + return; + break; + } + } + + void Region::add_output_database_group(int steps, bool force_addition) + { + if (get_database()->is_input()) + return; + + if(!topologyObserver) + return; + + int state = steps; + if (topologyObserver->is_topology_modified() || force_addition) { + // Determine how many steps have been written already... + state = get_property("state_count").get_int(); + + if (state == 0) + state = steps; + + // See if this is a continuation database... + if (property_exists("state_offset")) + state += get_property("state_offset").get_int(); + + state++; // For the state we are going to write. + + release_memory(); + DynamicTopologyFileControl fileControl(this, fileCyclicCount, ifDatabaseExists, dbChangeCount); + fileControl.add_output_database_group(state); + } + } + + void Region::clone_and_replace_output_database(int steps) + { + if (get_database()->is_input()) + return; + + if(!topologyObserver) + return; + + int state = steps; + if (topologyObserver->is_topology_modified() || fileCyclicCount > 0) { + // Determine how many steps have been written already... + state = get_property("state_count").get_int(); + + // Needed for automatic restart... The current database has not + // been written to, but we want to open a new one instead of + // (possibly) overwriting the current one... + // If this is not an automatic restart, then we don't need a new database... + if (state == 0 && topologyObserver->is_automatic_restart()) + return; + + if (topologyObserver->is_automatic_restart() && ifDatabaseExists == Ioss::DB_APPEND) + return; + + if (state == 0) + state = steps; + + // See if this is a continutation database... + if (property_exists("state_offset")) + state += get_property("state_offset").get_int(); + + state++; // For the state we are going to write. + + release_memory(); + DynamicTopologyFileControl fileControl(this, fileCyclicCount, ifDatabaseExists, dbChangeCount); + fileControl.clone_and_replace_output_database(state); + } + } + + bool Region::load_group_mesh(const std::string &group_name) + { + // Check name for '/' which is not allowed since it is the + // separator character in a full group path + if (group_name.find('/') != std::string::npos) { + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR: Invalid group name '{}' contains a '/' which is not allowed.\n", + group_name); + IOSS_ERROR(errmsg); + } + + DatabaseIO *iodatabase = get_database(); + + if (!iodatabase->is_input()) + return false; + + if(!iodatabase->open_root_group()) + return false; + + if(!iodatabase->open_group(group_name)) + return false; + + release_memory(); + iodatabase->release_memory(); + + Region::set_state(STATE_CLOSED); + modelDefined = false; + transientDefined = false; + + Region::begin_mode(STATE_DEFINE_MODEL); + iodatabase->read_meta_data(); + Region::end_mode(STATE_DEFINE_MODEL); + if (iodatabase->open_create_behavior() != Ioss::DB_APPEND && + iodatabase->open_create_behavior() != Ioss::DB_MODIFY) { + modelDefined = true; + transientDefined = true; + Region::begin_mode(STATE_READONLY); + } + + return true; + } + } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_Region.h b/packages/seacas/libraries/ioss/src/Ioss_Region.h index b6aeb52fd1..fd7a543ab4 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Region.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Region.h @@ -8,6 +8,8 @@ #include "Ioss_CoordinateFrame.h" // for CoordinateFrame #include "Ioss_DatabaseIO.h" // for DatabaseIO +#include "Ioss_DBUsage.h" +#include "Ioss_DynamicTopology.h" #include "Ioss_EntityType.h" // for EntityType, etc #include "Ioss_Field.h" #include "Ioss_GroupingEntity.h" // for GroupingEntity @@ -28,6 +30,7 @@ #include // for less #include // for ostream #include // for map, map<>::value_compare +#include #include #include // for string, operator< #include // for pair @@ -55,6 +58,7 @@ namespace Ioss { namespace Ioss { class CoordinateFrame; + enum class MeshType; using AssemblyContainer = std::vector; @@ -281,7 +285,28 @@ namespace Ioss { const std::vector &entity_container, std::vector &field_data) const; + void register_mesh_modification_observer(std::shared_ptr observer); + std::shared_ptr get_mesh_modification_observer() const { return topologyObserver; } + + void start_new_output_database_entry(int steps=0); + + void set_topology_change_count(unsigned int new_count) {dbChangeCount = new_count;} + unsigned int get_topology_change_count() {return dbChangeCount;} + + void set_file_cyclic_count(unsigned int new_count) {fileCyclicCount = new_count;} + unsigned int get_file_cyclic_count() {return fileCyclicCount;} + + void set_if_database_exists_behavior(IfDatabaseExistsBehavior if_exists) {ifDatabaseExists = if_exists;} + + bool model_is_written() const { return modelWritten; } + bool transient_is_written() const { return transientWritten; } + + bool load_group_mesh(const std::string &group_name); + protected: + void clone_and_replace_output_database(int steps=0); + void add_output_database_group(int steps=0, bool force_addition=false); + int64_t internal_get_field_data(const Field &field, void *data, size_t data_size = 0) const override; @@ -307,6 +332,7 @@ namespace Ioss { bool end_mode_nl(State current_state); void delete_database() override; + void release_memory(); mutable std::map aliases_; ///< Stores alias mappings @@ -333,6 +359,15 @@ namespace Ioss { mutable int stateCount{0}; bool modelDefined{false}; bool transientDefined{false}; + + std::shared_ptr topologyObserver; + + unsigned int dbChangeCount{1}; //!< Used to track number of topology changes. + unsigned int fileCyclicCount{0}; //!< For cycling file-A, file-B, file-C, ..., File-A, typically restart only. + IfDatabaseExistsBehavior ifDatabaseExists{DB_OVERWRITE}; + + bool modelWritten{false}; + bool transientWritten{false}; }; } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index 6e0cbdc8a6..6900a31923 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -17,6 +17,7 @@ #include #include #include +#include #include "Ioex_Utils.h" #include "Ioss_Assembly.h" @@ -80,6 +81,8 @@ namespace { template void write_attribute_names(int exoid, ex_entity_type type, const std::vector &entities); + bool add_groups(int exoid, Ioss::NameList& names, Ioss::NameList *full_names = nullptr); + class AssemblyTreeFilter { public: @@ -483,6 +486,40 @@ namespace Ioex { ex_set_max_name_length(m_exodusFilePtr, maximumNameLength); } + bool BaseDatabaseIO::open_root_group_nl() + { + // Get existing file pointer... + bool success = false; + + int exoid = get_file_pointer(); + + int group_name_length = ex_inquire_int(exoid, EX_INQ_GROUP_NAME_LEN); + std::vector group_name(group_name_length+1, '\0'); + + // Get name of this group... + int idum; + float rdum; + int ierr = ex_inquire(exoid, EX_INQ_GROUP_NAME, &idum, &rdum, group_name.data()); + if (ierr < 0) { + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR: Could not open root group of group named '{}' in file '{}'.\n", m_groupName, + get_filename()); + IOSS_ERROR(errmsg); + } + + m_groupName = std::string(group_name.data()); + m_exodusFilePtr = ex_inquire_int(exoid, EX_INQ_GROUP_ROOT); + + if (m_exodusFilePtr < 0) { + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR: Could not open group named '{}' in file '{}'.\n", m_groupName, + get_filename()); + IOSS_ERROR(errmsg); + } + success = true; + return success; + } + bool BaseDatabaseIO::open_group_nl(const std::string &group_name) { // Get existing file pointer... @@ -2811,6 +2848,38 @@ namespace Ioex { // Write coordinate frame data... write_coordinate_frames(get_file_pointer(), get_region()->get_coordinate_frames()); } + + + bool BaseDatabaseIO::groups_describe_nl(Ioss::NameList& names, Ioss::NameList *full_names) + { + Ioss::SerializeIO serializeIO_(this); + + int group_root = ex_inquire_int(get_file_pointer(), EX_INQ_GROUP_ROOT); + return add_groups(group_root, names, full_names); + } + + void BaseDatabaseIO::release_memory_nl() + { + Ioss::DatabaseIO::release_memory_nl(); + + ids_.clear(); + m_groupCount.clear(); + + nodeCmapIds.clear(); + nodeCmapNodeCnts.clear(); + elemCmapIds.clear(); + elemCmapElemCnts.clear(); + + m_truthTable.clear(); + m_variables.clear(); + m_reductionVariables.clear(); + + m_reductionValues.clear(); + + nodeConnectivityStatus.clear(); + + activeNodeSetNodesIndex.clear(); + } } // namespace Ioex namespace { @@ -3112,4 +3181,43 @@ namespace { } #endif } + + bool add_groups(int exoid, Ioss::NameList& names, Ioss::NameList *full_names) + { + int idum; + float rdum; + + int group_name_length = ex_inquire_int(exoid, EX_INQ_GROUP_NAME_LEN); + std::vector group_name(group_name_length+1, '\0'); + + // Get name of this group... + int ierr = ex_inquire(exoid, EX_INQ_GROUP_NAME, &idum, &rdum, group_name.data()); + if (ierr < 0) { + return false; + } + names.push_back(std::string(group_name.data())); + + if(nullptr != full_names) { + std::fill(group_name.begin(), group_name.end(), '\0'); + ierr = ex_inquire(exoid, EX_INQ_FULL_GROUP_NAME, &idum, &rdum, group_name.data()); + if (ierr < 0) { + return false; + } + full_names->push_back(std::string(group_name.data())); + } + + int num_children = ex_inquire_int(exoid, EX_INQ_NUM_CHILD_GROUPS); + std::vector children(num_children); + ierr = ex_get_group_ids(exoid, nullptr, Data(children)); + if (ierr < 0) { + return false; + } + + bool rtn = true; + for (int i = 0; i < num_children; i++) { + rtn |= add_groups(children[i], names, full_names); + } + + return rtn; + } } // namespace diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h index 217d7536f5..4445387f32 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h @@ -99,8 +99,12 @@ namespace Ioex { IOSS_NODISCARD bool ok_nl(bool write_message = false, std::string *error_message = nullptr, int *bad_count = nullptr) const override; + void release_memory_nl() override; + + bool open_root_group_nl() override; bool open_group_nl(const std::string &group_name) override; bool create_subgroup_nl(const std::string &group_name) override; + bool groups_describe_nl(Ioss::NameList& names, Ioss::NameList *full_names = nullptr) override; bool begin_nl(Ioss::State state) override; bool end_nl(Ioss::State state) override; diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C index e6a341a4fa..16edecbfbf 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C @@ -18,6 +18,7 @@ #include #include #include +#include #include "Ioss_CoordinateFrame.h" #include "Ioss_DatabaseIO.h" @@ -383,7 +384,7 @@ namespace Ioex { // Try to decode an id from the name. std::string name_string = entity->get_property(prop_name).get_string(); std::string type_name = entity->short_type_string(); - if (std::strncmp(type_name.c_str(), name_string.c_str(), type_name.size()) == 0) { + if (strncasecmp(type_name.c_str(), name_string.c_str(), type_name.size()) == 0) { id = extract_id(name_string); if (id <= 0) { id = 1; diff --git a/packages/seacas/libraries/ioss/src/unit_tests/CMakeLists.txt b/packages/seacas/libraries/ioss/src/unit_tests/CMakeLists.txt index 17a7bbb440..db5ff09522 100644 --- a/packages/seacas/libraries/ioss/src/unit_tests/CMakeLists.txt +++ b/packages/seacas/libraries/ioss/src/unit_tests/CMakeLists.txt @@ -14,6 +14,17 @@ TRIBITS_ADD_EXECUTABLE( SOURCES unitMain.C UnitTestElementBlockBatchRead.C ) +TRIBITS_ADD_EXECUTABLE( + Utst_dynamictopology + SOURCES unitMain.C UnitTestDynamicTopology.C +) + +TRIBITS_ADD_TEST( + Utst_dynamictopology + NAME Utst_dynamictopology + NUM_MPI_PROCS 1 +) + TRIBITS_ADD_TEST( Utst_blockbatchread NAME Utst_blockbatchread diff --git a/packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C b/packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C new file mode 100644 index 0000000000..b8ece953a8 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C @@ -0,0 +1,684 @@ +// Copyright(C) 1999-2020, 2022 National Technology & Engineering Solutions +// of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with +// NTESS, the U.S. Government retains certain rights in this software. +// +// See packages/seacas/LICENSE for details + +#include +#include + +#ifdef SEACAS_HAVE_MPI +#include "mpi.h" +#endif +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include + +#include // for unlink + +#include "Ionit_Initializer.h" +#include "Ioss_DBUsage.h" +#include "Ioss_DatabaseIO.h" // for DatabaseIO +#include "Ioss_Field.h" // for Field, etc +#include "Ioss_IOFactory.h" +#include "Ioss_ParallelUtils.h" +#include "Ioss_Property.h" +#include "Ioss_FileInfo.h" +#include "Ioss_ElementBlock.h" +#include "Ioss_NodeBlock.h" +#include "Ioss_ParallelUtils.h" +#include "Ioss_Region.h" + +#include "Ioss_Utils.h" + +#include "exodus/Ioex_DatabaseIO.h" + +namespace { +std::string get_many_block_mesh_desc(unsigned numBlocks) +{ + std::ostringstream oss; + std::vector elementIds(numBlocks); + std::iota(elementIds.begin(), elementIds.end(), 1); + + unsigned proc = 0; + for (unsigned i = 0; i < numBlocks; ++i) { + unsigned elemId = elementIds[i]; + unsigned firstNodeId = i * 4 + 1; + oss << proc << "," << elemId << ",HEX_8,"; + for (unsigned node = firstNodeId; node < firstNodeId + 8; ++node) { + oss << node << ","; + } + unsigned blockId = i + 1; + oss << "block_" << blockId; + + if (i < numBlocks - 1) { + oss << "\n"; + } + + proc++; + } + + oss << "|coordinates:"; + + std::vector planeCoords = {0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0}; + + for (double coord : planeCoords) { + oss << coord << ","; + } + + for (unsigned i = 1; i <= numBlocks; ++i) { + for (unsigned point = 0; point < 4; ++point) { + planeCoords[3 * point + 2] += 1; + } + + for (double coord : planeCoords) { + oss << coord << ","; + } + } + + return oss.str(); +} + +void define_model(const Ioss::Region &i_region, Ioss::Region &o_region) +{ + Ioss::DatabaseIO *o_database = o_region.get_database(); + + o_region.begin_mode(Ioss::STATE_DEFINE_MODEL); + + auto& nodeblocks = o_region.get_node_blocks(); + + Ioss::NodeBlock *i_nb = i_region.get_node_blocks()[0]; + int64_t spatial_dim = 3; + int64_t num_nodes = i_nb->entity_count(); + Ioss::NodeBlock *o_nb = new Ioss::NodeBlock(o_database, "nodeblock_1", num_nodes, spatial_dim); + o_region.add(o_nb); + + for (Ioss::ElementBlock *i_eb : i_region.get_element_blocks()) { + Ioss::ElementBlock *o_eb = new Ioss::ElementBlock( + o_database, i_eb->name(), i_eb->topology()->name(), i_eb->entity_count()); + o_eb->property_add(i_eb->get_property("id")); + o_region.add(o_eb); + } + + o_region.end_mode(Ioss::STATE_DEFINE_MODEL); +} + +void write_model(const Ioss::Region &i_region, Ioss::Region &o_region) +{ + Ioss::NodeBlock *i_nb = i_region.get_node_blocks()[0]; + Ioss::NodeBlock *o_nb = o_region.get_node_blocks()[0]; + + o_region.begin_mode(Ioss::STATE_MODEL); + std::vector coordinates; + std::vector node_ids; + i_nb->get_field_data("ids", node_ids); + i_nb->get_field_data("mesh_model_coordinates", coordinates); + + o_nb->put_field_data("ids", node_ids); + o_nb->put_field_data("mesh_model_coordinates", coordinates); + + for (Ioss::ElementBlock *i_eb : i_region.get_element_blocks()) { + Ioss::ElementBlock *o_eb = o_region.get_element_block(i_eb->name()); + std::vector elem_ids; + std::vector connectivity; + + i_eb->get_field_data("ids", elem_ids); + i_eb->get_field_data("connectivity", connectivity); + + o_eb->put_field_data("ids", elem_ids); + o_eb->put_field_data("connectivity", connectivity); + } + + o_region.end_mode(Ioss::STATE_MODEL); +} + +void define_transient(const Ioss::Region &i_region, Ioss::Region &o_region, + const std::string &elemFieldName) +{ + o_region.begin_mode(Ioss::STATE_DEFINE_TRANSIENT); + + for (Ioss::ElementBlock *o_eb : o_region.get_element_blocks()) { + size_t num_elem = o_eb->get_property("entity_count").get_int(); + std::string storage = "scalar"; + + Ioss::Field field(elemFieldName, Ioss::Field::REAL, storage, 1, Ioss::Field::Field::TRANSIENT, + num_elem); + o_eb->field_add(field); + } + o_region.end_mode(Ioss::STATE_DEFINE_TRANSIENT); +} + +int write_transient(Ioss::Region &o_region, const std::string &elemFieldName, const double time) +{ + o_region.begin_mode(Ioss::STATE_TRANSIENT); + int step = o_region.add_state(time); + o_region.begin_state(step); + + for (Ioss::ElementBlock *o_eb : o_region.get_element_blocks()) { + size_t num_elem = o_eb->get_property("entity_count").get_int(); + + std::vector field_data(num_elem); + std::vector elem_ids; + + o_eb->get_field_data("ids", elem_ids); + for (size_t i = 0; i < elem_ids.size(); i++) { + field_data[i] = (double)elem_ids[i] + 100*time; + } + + o_eb->put_field_data(elemFieldName, field_data); + } + + o_region.end_state(step); + o_region.end_mode(Ioss::STATE_TRANSIENT); + + return step; +} + +class Observer : public Ioss::DynamicTopologyObserver +{ +public: + Observer(Ioss::Region& inputRegion_, + const std::string &elemFieldName_, + const Ioss::FileControlOption fileControlOption_) + : inputRegion(inputRegion_) + , elemFieldName(elemFieldName_) + , fileControlOption(fileControlOption_) + {} + + virtual ~Observer() {} + + void define_model(Ioss::Region& region) override + { + ::define_model(inputRegion, region); + } + + void write_model(Ioss::Region& region) override + { + ::write_model(inputRegion, region); + } + + void define_transient(Ioss::Region& region) override + { + ::define_transient(inputRegion, region, elemFieldName); + } + + Ioss::FileControlOption get_control_option() const + { + return fileControlOption; + } + +private: + Observer(); + + Ioss::Region& inputRegion; + const std::string elemFieldName; + Ioss::FileControlOption fileControlOption; +}; + +void run_simple_topology_change(const Ioss::Region& i_region, + Ioss::Region &o_region, + std::shared_ptr observer, + const std::string &elemFieldName) +{ + define_model(i_region, o_region); + write_model(i_region, o_region); + + define_transient(i_region, o_region, elemFieldName); + + double time = 0.0; + write_transient(o_region, elemFieldName, time); + + auto min_result1 = o_region.get_min_time(); + EXPECT_EQ(1, min_result1.first); + EXPECT_NEAR(0.0, min_result1.second, 1.0e-6); + + auto max_result1 = o_region.get_max_time(); + EXPECT_EQ(1, max_result1.first); + EXPECT_NEAR(0.0, max_result1.second, 1.0e-6); + + observer->set_topology_modification(Ioss::TOPOLOGY_UNKNOWN); + + time = 1.0; + write_transient(o_region, elemFieldName, time); + + auto min_result2 = o_region.get_min_time(); + EXPECT_EQ(1, min_result2.first); + EXPECT_NEAR(1.0, min_result2.second, 1.0e-6); + + auto max_result2 = o_region.get_max_time(); + EXPECT_EQ(1, max_result2.first); + EXPECT_NEAR(1.0, max_result2.second, 1.0e-6); + + observer->set_topology_modification(Ioss::TOPOLOGY_SAME); + + time = 2.0; + write_transient(o_region, elemFieldName, time); + + auto min_result3 = o_region.get_min_time(); + EXPECT_EQ(1, min_result3.first); + EXPECT_NEAR(1.0, min_result3.second, 1.0e-6); + + auto max_result3 = o_region.get_max_time(); + EXPECT_EQ(2, max_result3.first); + EXPECT_NEAR(2.0, max_result3.second, 1.0e-6); +} + +void cleanup_multi_files(const std::string &outFile) +{ + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + std::string file1 = Ioss::Utils::decode_filename(outFile, util.parallel_rank(), util.parallel_size()); + unlink(file1.c_str()); + + std::string file2 = Ioss::Utils::decode_filename(outFile + "-s0002", util.parallel_rank(), util.parallel_size()); + unlink(file2.c_str()); +} + +void run_multi_file_simple_topology_change(const std::string &elemFieldName, const std::string &outFile) +{ + Ioss::Init::Initializer io; + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + int numBlocks = util.parallel_size(); + + std::string meshDesc = get_many_block_mesh_desc(numBlocks); + + Ioss::PropertyManager propertyManager; + + Ioss::DatabaseIO *i_database = Ioss::IOFactory::create("textmesh", meshDesc, Ioss::READ_MODEL, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region i_region(i_database, "input_model"); + EXPECT_TRUE(i_database != nullptr); + EXPECT_TRUE(i_database->ok(true)); + + Ioss::DatabaseIO *o_database = Ioss::IOFactory::create("exodus", outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region(o_database, "output_model"); + EXPECT_TRUE(o_database != nullptr); + EXPECT_TRUE(o_database->ok(true)); + + auto fileControlOption = Ioss::FileControlOption::CONTROL_AUTO_MULTI_FILE; + auto observer = std::make_shared(i_region, elemFieldName, fileControlOption); + o_region.register_mesh_modification_observer(observer); + + run_simple_topology_change(i_region, o_region, observer, elemFieldName); +} + +TEST(TestDynamicWrite, multi_file_simple_topology_modification) +{ + std::string outFile("multiFileManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + cleanup_multi_files(outFile); + run_multi_file_simple_topology_change(elemFieldName, outFile); + cleanup_multi_files(outFile); +} + +void cleanup_single_file(const std::string &outFile) +{ + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + std::string file1 = Ioss::Utils::decode_filename(outFile, util.parallel_rank(), util.parallel_size()); + unlink(file1.c_str()); +} + +void run_single_file_simple_topology_change(const std::string &elemFieldName, const std::string &outFile) +{ + Ioss::Init::Initializer io; + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + int numBlocks = util.parallel_size(); + + std::string meshDesc = get_many_block_mesh_desc(numBlocks); + + Ioss::PropertyManager propertyManager; + + Ioss::DatabaseIO *i_database = Ioss::IOFactory::create("textmesh", meshDesc, Ioss::READ_MODEL, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region i_region(i_database, "input_model"); + EXPECT_TRUE(i_database != nullptr); + EXPECT_TRUE(i_database->ok(true)); + + propertyManager.add(Ioss::Property("ENABLE_FILE_GROUPS", 1)); + propertyManager.add(Ioss::Property("APPEND_OUTPUT", Ioss::DB_APPEND_GROUP)); + Ioss::DatabaseIO *o_database = Ioss::IOFactory::create("exodus", outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region(o_database, "output_model"); + EXPECT_TRUE(o_database != nullptr); + EXPECT_TRUE(o_database->ok(true)); + + auto fileControlOption = Ioss::FileControlOption::CONTROL_AUTO_SINGLE_FILE; + auto observer = std::make_shared(i_region, elemFieldName, fileControlOption); + o_region.register_mesh_modification_observer(observer); + + run_simple_topology_change(i_region, o_region, observer, elemFieldName); + + Ioss::NameList names; + Ioss::NameList full_names; + o_database->groups_describe(names, &full_names); + + std::vector gold_names{"/", "STEP-1", "STEP-2"}; + std::vector gold_full_names{"/", "/STEP-1", "/STEP-2"}; + + EXPECT_EQ(gold_names, names); + EXPECT_EQ(gold_full_names, full_names); +} + +TEST(TestDynamicWrite, single_file_simple_topology_modification) +{ + std::string outFile("singleFileManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + cleanup_single_file(outFile); + run_single_file_simple_topology_change(elemFieldName, outFile); + cleanup_single_file(outFile); +} + +TEST(TestDynamicWrite, single_file_groups_not_enabled) +{ + Ioss::Init::Initializer io; + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + int numBlocks = util.parallel_size(); + if(numBlocks > 1) GTEST_SKIP(); + + std::string meshDesc = "0,1,HEX_8,1,2,3,4,5,6,7,8,block_1" + "|coordinates:0,0,0,1,0,0,1,1,0,0,1,0,0,0,1,1,0,1,1,1,1,0,1,1"; + + Ioss::PropertyManager propertyManager; + + Ioss::DatabaseIO *i_database = Ioss::IOFactory::create("textmesh", meshDesc, Ioss::READ_MODEL, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region i_region(i_database, "input_model"); + EXPECT_TRUE(i_database != nullptr); + EXPECT_TRUE(i_database->ok(true)); + + std::string outFile("singleFileGroupsNotEnabled.g"); + std::string elemFieldName = "elem_field"; + cleanup_single_file(outFile); + + // Need the line below to allow this to pass + // propertyManager.add(Ioss::Property("ENABLE_FILE_GROUPS", 1)); + propertyManager.add(Ioss::Property("APPEND_OUTPUT", Ioss::DB_APPEND_GROUP)); + Ioss::DatabaseIO *o_database = Ioss::IOFactory::create("exodus", outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region(o_database, "output_model"); + EXPECT_TRUE(o_database != nullptr); + EXPECT_TRUE(o_database->ok(true)); + + auto fileControlOption = Ioss::FileControlOption::CONTROL_AUTO_SINGLE_FILE; + auto observer = std::make_shared(i_region, elemFieldName, fileControlOption); + EXPECT_THROW(o_region.register_mesh_modification_observer(observer), std::runtime_error); + cleanup_single_file(outFile); +} + +TEST(TestDynamicWrite, create_subgroup_with_file_reopen) +{ + std::string outFile("subgroupManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + Ioss::Init::Initializer io; + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + std::string file1 = Ioss::Utils::decode_filename(outFile, util.parallel_rank(), util.parallel_size()); + unlink(file1.c_str()); + + int numBlocks = util.parallel_size(); + if(numBlocks > 1) GTEST_SKIP(); + + std::string meshDesc = "0,1,HEX_8,1,2,3,4,5,6,7,8,block_1" + "|coordinates:0,0,0,1,0,0,1,1,0,0,1,0,0,0,1,1,0,1,1,1,1,0,1,1"; + + Ioss::PropertyManager propertyManager; + + Ioss::DatabaseIO *i_database = Ioss::IOFactory::create("textmesh", meshDesc, Ioss::READ_MODEL, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region i_region(i_database, "input_model"); + EXPECT_TRUE(i_database != nullptr); + EXPECT_TRUE(i_database->ok(true)); + + { + propertyManager.add(Ioss::Property("ENABLE_FILE_GROUPS", 1)); + Ioss::DatabaseIO *o_database = Ioss::IOFactory::create("exodus", outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region(o_database, "output_model"); + EXPECT_TRUE(o_database != nullptr); + EXPECT_TRUE(o_database->ok(true)); + o_database->create_subgroup("GROUP_1"); + } + + { + propertyManager.add(Ioss::Property("APPEND_OUTPUT", Ioss::DB_APPEND_GROUP)); + Ioss::DatabaseIO *o_database = Ioss::IOFactory::create("exodus", outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region(o_database, "output_model"); + EXPECT_TRUE(o_database != nullptr); + EXPECT_TRUE(o_database->ok(true)); + + // Group pointer is still at root level + o_database->create_subgroup("GROUP_2"); + + Ioss::NameList names; + Ioss::NameList full_names; + o_database->groups_describe(names, &full_names); + + std::vector gold_names{"/", "GROUP_1", "GROUP_2"}; + std::vector gold_full_names{"/", "/GROUP_1", "/GROUP_2"}; + + EXPECT_EQ(gold_names, names); + EXPECT_EQ(gold_full_names, full_names); + } + + unlink(file1.c_str()); +} + +TEST(TestDynamicWrite, create_subgroup_with_file_persistence_and_child_group) +{ + std::string outFile("subgroupManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + Ioss::Init::Initializer io; + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + std::string file1 = Ioss::Utils::decode_filename(outFile, util.parallel_rank(), util.parallel_size()); + unlink(file1.c_str()); + + int numBlocks = util.parallel_size(); + if(numBlocks > 1) GTEST_SKIP(); + + std::string meshDesc = "0,1,HEX_8,1,2,3,4,5,6,7,8,block_1" + "|coordinates:0,0,0,1,0,0,1,1,0,0,1,0,0,0,1,1,0,1,1,1,1,0,1,1"; + + Ioss::PropertyManager propertyManager; + + Ioss::DatabaseIO *i_database = Ioss::IOFactory::create("textmesh", meshDesc, Ioss::READ_MODEL, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region i_region(i_database, "input_model"); + EXPECT_TRUE(i_database != nullptr); + EXPECT_TRUE(i_database->ok(true)); + + { + propertyManager.add(Ioss::Property("ENABLE_FILE_GROUPS", 1)); + propertyManager.add(Ioss::Property("APPEND_OUTPUT", Ioss::DB_APPEND_GROUP)); + Ioss::DatabaseIO *o_database = Ioss::IOFactory::create("exodus", outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region(o_database, "output_model"); + EXPECT_TRUE(o_database != nullptr); + EXPECT_TRUE(o_database->ok(true)); + + o_database->create_subgroup("GROUP_1"); + + // Group pointer is at "GROUP_1" ... "GROUP_2" is a child + o_database->create_subgroup("GROUP_2"); + + Ioss::NameList names; + Ioss::NameList full_names; + o_database->groups_describe(names, &full_names); + + std::vector gold_names{"/", "GROUP_1", "GROUP_2"}; + std::vector gold_full_names{"/", "/GROUP_1", "/GROUP_1/GROUP_2"}; + + EXPECT_EQ(gold_names, names); + EXPECT_EQ(gold_full_names, full_names); + } + + unlink(file1.c_str()); +} + +TEST(TestDynamicWrite, create_subgroup_with_file_persistence_and_no_child_group) +{ + std::string outFile("subgroupManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + Ioss::Init::Initializer io; + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + std::string file1 = Ioss::Utils::decode_filename(outFile, util.parallel_rank(), util.parallel_size()); + unlink(file1.c_str()); + + int numBlocks = util.parallel_size(); + if(numBlocks > 1) GTEST_SKIP(); + + std::string meshDesc = "0,1,HEX_8,1,2,3,4,5,6,7,8,block_1" + "|coordinates:0,0,0,1,0,0,1,1,0,0,1,0,0,0,1,1,0,1,1,1,1,0,1,1"; + + Ioss::PropertyManager propertyManager; + + Ioss::DatabaseIO *i_database = Ioss::IOFactory::create("textmesh", meshDesc, Ioss::READ_MODEL, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region i_region(i_database, "input_model"); + EXPECT_TRUE(i_database != nullptr); + EXPECT_TRUE(i_database->ok(true)); + + { + propertyManager.add(Ioss::Property("ENABLE_FILE_GROUPS", 1)); + propertyManager.add(Ioss::Property("APPEND_OUTPUT", Ioss::DB_APPEND_GROUP)); + Ioss::DatabaseIO *o_database = Ioss::IOFactory::create("exodus", outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region(o_database, "output_model"); + EXPECT_TRUE(o_database != nullptr); + EXPECT_TRUE(o_database->ok(true)); + + o_database->create_subgroup("GROUP_1"); + + // Group pointer is reset to root group + EXPECT_TRUE(o_database->open_root_group()); + o_database->create_subgroup("GROUP_2"); + + Ioss::NameList names; + Ioss::NameList full_names; + o_database->groups_describe(names, &full_names); + + std::vector gold_names{"/", "GROUP_1", "GROUP_2"}; + std::vector gold_full_names{"/", "/GROUP_1", "/GROUP_2"}; + + EXPECT_EQ(gold_names, names); + EXPECT_EQ(gold_full_names, full_names); + } + + unlink(file1.c_str()); +} + +void test_single_file_simple_topology_change_data(Ioss::Region& i_region, const std::string& elemFieldName, + int gold_step, double gold_time) +{ + i_region.begin_state(gold_step); + for (Ioss::ElementBlock *i_eb : i_region.get_element_blocks()) { + size_t num_elem = i_eb->get_property("entity_count").get_int(); + + std::vector field_data(num_elem); + std::vector elem_ids; + + i_eb->get_field_data(elemFieldName, field_data); + i_eb->get_field_data("ids", elem_ids); + + for (size_t i = 0; i < elem_ids.size(); i++) { + double gold_value = (double)elem_ids[i] + 100*gold_time; + EXPECT_NEAR(gold_value, field_data[i], 1.0e-6); + } + } +} + +void read_and_test_single_file_simple_topology_change(const std::string& elemFieldName, const std::string& outFile) +{ + Ioss::PropertyManager propertyManager; + + Ioss::DatabaseIO *i_database = Ioss::IOFactory::create("exodus", outFile, Ioss::READ_RESTART, + Ioss::ParallelUtils::comm_world(), + propertyManager); + + Ioss::NameList names; + Ioss::NameList full_names; + i_database->groups_describe(names, &full_names); + + std::vector gold_names{"/", "STEP-1", "STEP-2"}; + std::vector gold_full_names{"/", "/STEP-1", "/STEP-2"}; + + EXPECT_EQ(gold_names, names); + EXPECT_EQ(gold_full_names, full_names); + + EXPECT_TRUE(i_database->open_group("STEP-1")); + + Ioss::Region i_region(i_database, "input_model"); + EXPECT_TRUE(i_database != nullptr); + EXPECT_TRUE(i_database->ok(true)); + + double gold_time = 0.0; + int gold_step = 1; + auto min_result1 = i_region.get_min_time(); + EXPECT_EQ(gold_step, min_result1.first); + EXPECT_NEAR(gold_time, min_result1.second, 1.0e-6); + + auto max_result1 = i_region.get_max_time(); + EXPECT_EQ(gold_step, max_result1.first); + EXPECT_NEAR(gold_time, max_result1.second, 1.0e-6); + test_single_file_simple_topology_change_data(i_region, elemFieldName, gold_step, gold_time); + + EXPECT_TRUE(i_region.load_group_mesh("STEP-2")); + + double gold_min_time = 1.0; + int gold_min_step = 1; + auto min_result2 = i_region.get_min_time(); + EXPECT_EQ(gold_min_step, min_result2.first); + EXPECT_NEAR(gold_min_time, min_result2.second, 1.0e-6); + test_single_file_simple_topology_change_data(i_region, elemFieldName, gold_min_step, gold_min_time); + + auto max_result2 = i_region.get_max_time(); + double gold_max_time = 2.0; + int gold_max_step = 2; + EXPECT_EQ(gold_max_step, max_result2.first); + EXPECT_NEAR(gold_max_time, max_result2.second, 1.0e-6); + test_single_file_simple_topology_change_data(i_region, elemFieldName, gold_max_step, gold_max_time); +} + +TEST(TestDynamicRead, single_file_simple_topology_modification) +{ + std::string outFile("singleFileManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + cleanup_single_file(outFile); + run_single_file_simple_topology_change(elemFieldName, outFile); + read_and_test_single_file_simple_topology_change(elemFieldName, outFile); + cleanup_single_file(outFile); +} + +} + + From a7a03e51d2b8fe1c39415405c389ccd4bf006b4a Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 15 May 2024 17:29:43 -0600 Subject: [PATCH 02/59] APREPRO: Remove unused variable [ci skip] --- packages/seacas/libraries/aprepro_lib/apr_parser.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/seacas/libraries/aprepro_lib/apr_parser.cc b/packages/seacas/libraries/aprepro_lib/apr_parser.cc index 45a3392132..b31b887a42 100644 --- a/packages/seacas/libraries/aprepro_lib/apr_parser.cc +++ b/packages/seacas/libraries/aprepro_lib/apr_parser.cc @@ -380,7 +380,6 @@ namespace SEAMS { int yylen = 0; // Error handling. - int yynerrs_ = 0; int yyerrstatus_ = 0; /// The lookahead symbol. @@ -1843,7 +1842,6 @@ namespace SEAMS { yyerrlab: // If not already recovering from an error, report this error. if (!yyerrstatus_) { - ++yynerrs_; context yyctx(*this, yyla); std::string msg = yysyntax_error_(yyctx); error(YY_MOVE(msg)); From d152dbce75895608b57d97514995c36237ec6051 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Thu, 16 May 2024 09:14:27 -0600 Subject: [PATCH 03/59] EXODUS: Import a specific exodus version from exomerge --- packages/seacas/scripts/exomerge2.py | 7 ++----- packages/seacas/scripts/exomerge3.py | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/seacas/scripts/exomerge2.py b/packages/seacas/scripts/exomerge2.py index 6ec456a6c7..0388975bf0 100755 --- a/packages/seacas/scripts/exomerge2.py +++ b/packages/seacas/scripts/exomerge2.py @@ -1,7 +1,7 @@ """ Exomerge is a lightweight Python interface for manipulating ExodusII files. -Copyright(C) 1999-2020, 2023 National Technology & Engineering Solutions +Copyright(C) 1999-2020, 2023, 2024 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. @@ -53,10 +53,7 @@ # import exodus module # (exodus.py should be in the same directory as this file) -try: - import exodus -except: - import exodus2 as exodus +import exodus2 as exodus # informal version number of this module __version__ = 8.6 diff --git a/packages/seacas/scripts/exomerge3.py b/packages/seacas/scripts/exomerge3.py index d07e1ea5e1..76779addcb 100644 --- a/packages/seacas/scripts/exomerge3.py +++ b/packages/seacas/scripts/exomerge3.py @@ -54,10 +54,7 @@ # import exodus module # (exodus.py should be in the same directory as this file) -try: - import exodus -except Exception: - import exodus3 as exodus +import exodus3 as exodus # informal version number of this module __version__ = "8.6.1" From cbd37f52cab4760185d00d1db722f3c532d6baeb Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Thu, 16 May 2024 15:29:34 -0600 Subject: [PATCH 04/59] IOSS: Add option to omit_blocks to io_shell --- .../libraries/ioss/src/Ioss_CopyDatabase.C | 13 +++++++++++++ .../libraries/ioss/src/Ioss_MeshCopyOptions.h | 1 + .../seacas/libraries/ioss/src/main/io_shell.C | 6 ++++++ .../libraries/ioss/src/main/shell_interface.C | 17 +++++++++++++++++ .../libraries/ioss/src/main/shell_interface.h | 4 ++++ 5 files changed, 41 insertions(+) diff --git a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C index 31016664ed..d65f4ce7e4 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C +++ b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C @@ -847,6 +847,19 @@ namespace { fmt::print(Ioss::DebugOut(), " Number of Nodes = {:14}\n", fmt::group_digits(num_nodes)); } + + if (options.omitted_blocks) { + size_t isize = inb->get_field("node_connectivity_status").get_size(); + pool.data.resize(isize); + inb->get_field_data("node_connectivity_status", pool.data.data(), isize); + + // Count number of "active" nodes + size_t active = + std::count_if(pool.data.begin(), pool.data.end(), [](auto &val) { return val >= 2; }); + fmt::print(Ioss::DebugOut(), " Number of Active Nodes = {:14}\n", + fmt::group_digits(active)); + } + auto *nb = new Ioss::NodeBlock(*inb); output_region.add(nb); diff --git a/packages/seacas/libraries/ioss/src/Ioss_MeshCopyOptions.h b/packages/seacas/libraries/ioss/src/Ioss_MeshCopyOptions.h index 585730006f..61e28d4d0f 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_MeshCopyOptions.h +++ b/packages/seacas/libraries/ioss/src/Ioss_MeshCopyOptions.h @@ -36,6 +36,7 @@ namespace Ioss { bool reverse{false}; // Used for testing CGNS bool add_proc_id{false}; // CGNS: Add proc_id field. bool boundary_sideset{false}; // Output a sideset of the boundary faces of the model + bool omitted_blocks{false}; // only used by Catalyst calls to `copy_database`; if false the // copy process skips the defining of the mesh geometry and the diff --git a/packages/seacas/libraries/ioss/src/main/io_shell.C b/packages/seacas/libraries/ioss/src/main/io_shell.C index 42ab148be3..251fe49cf6 100644 --- a/packages/seacas/libraries/ioss/src/main/io_shell.C +++ b/packages/seacas/libraries/ioss/src/main/io_shell.C @@ -73,6 +73,7 @@ namespace { options.add_proc_id = interFace.add_processor_id_field; options.boundary_sideset = interFace.boundary_sideset; options.ignore_qa_info = interFace.ignore_qa_info; + options.omitted_blocks = !interFace.omitted_blocks.empty(); return options; } @@ -263,6 +264,11 @@ namespace { } } + if (!interFace.omitted_blocks.empty()) { + std::vector inclusions{}; + dbi->set_block_omissions(interFace.omitted_blocks, inclusions); + } + // NOTE: 'region' owns 'db' pointer at this time... Ioss::Region region(dbi, "region_1"); diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.C b/packages/seacas/libraries/ioss/src/main/shell_interface.C index 93f68d817e..b505caba4a 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.C +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.C @@ -305,6 +305,13 @@ void IOShell::Interface::enroll_options() "\t\tDefault is to ignore empty blocks.", nullptr); + options_.enroll("omit_blocks", Ioss::GetLongOption::MandatoryValue, + "comma-separated list of element block names that should NOT be transferred to " + "output database\n" + "\t\tNote that currently any nodes connected to only empty blocks will be " + "retained in the output.", + nullptr); + options_.enroll("boundary_sideset", Ioss::GetLongOption::NoValue, "Output a sideset for all boundary faces of the model", nullptr); @@ -606,6 +613,16 @@ bool IOShell::Interface::parse_options(int argc, char **argv, int my_processor) } } + { + const char *temp = options_.retrieve("omit_blocks"); + if (temp != nullptr) { + auto omit_str = Ioss::tokenize(std::string(temp), ","); + for (const auto &str : omit_str) { + omitted_blocks.push_back(str); + } + } + } + { const char *temp = options_.retrieve("surface_split_scheme"); if (temp != nullptr) { diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.h b/packages/seacas/libraries/ioss/src/main/shell_interface.h index 4d422fdd11..09d61a4f11 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.h +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.h @@ -59,6 +59,10 @@ namespace IOShell { //! If non-empty, then it is a list of times that should be transferred to the output file. std::vector selected_times{}; + //! If non-empty, then it is a list of element blocks that should be omitted from the output + //! file + std::vector omitted_blocks{}; + //! If non-zero, then put `split_times` timesteps in each file. Then close file and start new //! file. // If `split_cyclic == 0`, then filenames will be From f185aa379c8084f298158ff19cb9eba413ecd026 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 20 May 2024 09:30:45 -0600 Subject: [PATCH 05/59] SIMPI: Install headers in incldue subdir so do not interfere with client use of mpi --- packages/zoltan/siMPI/pyMPI/siMPI/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zoltan/siMPI/pyMPI/siMPI/CMakeLists.txt b/packages/zoltan/siMPI/pyMPI/siMPI/CMakeLists.txt index 37d8f0caa4..99535f66fd 100644 --- a/packages/zoltan/siMPI/pyMPI/siMPI/CMakeLists.txt +++ b/packages/zoltan/siMPI/pyMPI/siMPI/CMakeLists.txt @@ -466,6 +466,7 @@ SET(SOURCES ${SOURCES} TRIBITS_ADD_LIBRARY( simpi + HEADERS_INSTALL_SUBDIR simpi HEADERS ${HEADERS} SOURCES ${SOURCES} ) From eb5eb697b7d36ab8173c1fa8f21d8399558fc2bf Mon Sep 17 00:00:00 2001 From: Alex Pelletier <108826411+ajpelle@users.noreply.github.com> Date: Mon, 20 May 2024 11:45:10 -0600 Subject: [PATCH 06/59] Add support for Exodus IOSS properties related to field interpretation to the Catalyst IOSS Database (#456) * Refactored testing infrastructure to add transient unstructured fields * First test failing, for wrong reason * Test Fixture completed. First failing test done. * Added failing tests for all 5 Props. Need to update Frwrk for Sur_Split_type * Refactored get*block() to be public * Added failing list for SURFACE_SPLIT_TYPE. Compromises made * Fixed conduit error related to scoping. Refactored tests a bit * Implemented Exodus IOSS properties. Removed asserts. Finished verifying fields * Refactored readField() to not have DatabaseIO as parameter --- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.C | 110 ++++++- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.h | 4 + .../src/catalyst_tests/Iocatalyst_BlockMesh.C | 20 ++ .../src/catalyst_tests/Iocatalyst_BlockMesh.h | 9 + .../catalyst_tests/Iocatalyst_BlockMeshSet.C | 106 ++++++- .../catalyst_tests/Iocatalyst_BlockMeshSet.h | 23 +- .../Iocatalyst_BlockMeshSetTest.C | 16 + .../catalyst_tests/Iocatalyst_BlockMeshTest.C | 18 ++ .../Iocatalyst_DatabaseIOTest.C | 126 ++++++++ .../Iocatalyst_DatabaseIOTest.h | 20 ++ .../Iocatalyst_ElementBlockTest.C | 284 +++++++++++++++++- 11 files changed, 710 insertions(+), 26 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C index 8c9ee4e2f7..69224c565a 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C @@ -8,6 +8,7 @@ #include #include "Ioss_CommSet.h" // for CommSet +#include "Ioss_CodeTypes.h" // for IOSS_SCALAR() #include "Ioss_DBUsage.h" // for DatabaseUsage, etc #include "Ioss_DatabaseIO.h" // for DatabaseIO #include "Ioss_EdgeBlock.h" // for EdgeBlock @@ -141,6 +142,8 @@ namespace Iocatalyst { inline static const std::string RANGEEND = "m_rangeEnd"; inline static const std::string FACE = "m_face"; + inline static const std::string SURFACESPLITTYPE = "surface_split_type"; + std::string getValuePath(const std::string &prop) { return detail::PROPERTIES + detail::FS + prop + detail::FS + detail::VALUE; @@ -305,12 +308,17 @@ namespace Iocatalyst { bool defineModel(Ioss::Region *region) { - assert(region->model_defined()); + if(!region->model_defined()) { + std::ostringstream errmsg; + errmsg << "Catalyst Write in defineModel(): model isn't defined in region"<<"\n"; + IOSS_ERROR(errmsg); + } auto &node = this->DBNode; node = conduit_cpp::Node(); node[detail::getAPISizePath()].set_int8(region->get_database()->int_byte_size_api()); + node[detail::SURFACESPLITTYPE].set_int8(region->get_database()->get_surface_split_type()); RegionContainer rc; rc.push_back(region); this->defineEntityGroup(node[detail::REGION], rc); @@ -810,6 +818,24 @@ namespace Iocatalyst { template bool readFields(const conduit_cpp::Node &&parent, GroupingEntityT *block) const { + //Assumption: count = entity_count (in block) + Ioss::DatabaseIO *dbase = block->get_database(); + Ioss::EntityType b_t = block->type(); + bool is_entity_block = false; + if(b_t == Ioss::EntityType::ELEMENTBLOCK || + b_t == Ioss::EntityType::EDGEBLOCK || + b_t == Ioss::EntityType::FACEBLOCK || + b_t == Ioss::EntityType::NODEBLOCK || + b_t == Ioss::EntityType::SIDEBLOCK || + b_t == Ioss::EntityType::STRUCTUREDBLOCK) { + is_entity_block = true; + } + const int FNAME_MAX_LENGTH = 255; + size_t field_count = 0; + char **field_names = + Ioss::Utils::get_name_array(parent.number_of_children(), FNAME_MAX_LENGTH); + size_t entity_count = 0; + for (conduit_index_t idx = 0, max = parent.number_of_children(); idx < max; ++idx) { auto &&child = parent[idx]; const auto name = child.name(); @@ -819,17 +845,43 @@ namespace Iocatalyst { const auto index = child[detail::INDEX].as_int64(); const auto storage = child[detail::STORAGE].as_string(); if (!block->field_exists(name)) { - block->field_add( + if(storage == IOSS_SCALAR() && role==Ioss::Field::TRANSIENT && is_entity_block) { + //Add to get_fields() call + char field_name[FNAME_MAX_LENGTH + 1]; + Ioss::Utils::copy_string(field_name, name, FNAME_MAX_LENGTH + 1); + Ioss::Utils::copy_string(field_names[field_count++], field_name, FNAME_MAX_LENGTH + 1); + if(entity_count == 0) + entity_count = count; + } + else { + block->field_add( Ioss::Field(name, type, storage, role, count, index).set_zero_copy_enabled()); + } } else { - // TODO verify field details. - auto field = block->get_fieldref(name); - if (!field.has_transform()) { + // Verify field details. + auto field_block = block->get_fieldref(name); + if (!field_block.has_transform()) { block->get_fieldref(name).set_zero_copy_enabled(); } - assert(field.get_type() == type); - auto f = block->get_fieldref(name); + auto field_conduit = Ioss::Field(name, type, storage, role, count, index).set_zero_copy_enabled(); + if(field_block != field_conduit) { + std::ostringstream errmsg; + errmsg << "Catalyst Read: Field '"<name().c_str()<< + "' of type '"<type_string()<<"' and differs from it\n"; + IOSS_ERROR(errmsg); + } + } + } + + //Apply Exodus Properties to Scalar Fields in Entity Blocks + if(field_count > 0) { + std::vector fields; + Ioss::Utils::get_fields(entity_count, field_names, field_count, Ioss::Field::TRANSIENT, + dbase, nullptr, fields); + for (const auto &field : fields) { + block->field_add(field.set_zero_copy_enabled()); } } @@ -1039,6 +1091,12 @@ namespace Iocatalyst { auto &node = this->DBNode; region->get_database()->set_int_byte_size_api( static_cast(node[detail::getAPISizePath()].as_int8())); + const auto write_split_type = + static_cast(node[detail::SURFACESPLITTYPE].as_int8()); + if(write_split_type != region->get_database()->get_surface_split_type()) { + static_cast(region->get_database())->set_split_type_changed(true); + } + auto tpath = detail::REGION + detail::FS + detail::TIME; if (node.has_path(tpath)) { region->add_state(node[tpath].to_float64()); @@ -1048,8 +1106,14 @@ namespace Iocatalyst { this->readEntityGroup(node[detail::ELEMENTBLOCKS], region); this->readEntityGroup(node[detail::EDGEBLOCKS], region); this->readEntityGroup(node[detail::FACEBLOCKS], region); - this->readEntityGroup(node[detail::SIDEBLOCKS], region); - this->readEntityGroup(node[detail::SIDESETS], region); + + bool surface_split_changed = + static_cast(region->get_database())->split_type_changed(); + if(!surface_split_changed) { + this->readEntityGroup(node[detail::SIDEBLOCKS], region); + this->readEntityGroup(node[detail::SIDESETS], region); + } + this->readEntityGroup(node[detail::NODESETS], region); this->readEntityGroup(node[detail::EDGESETS], region); this->readEntityGroup(node[detail::FACESETS], region); @@ -1116,11 +1180,19 @@ namespace Iocatalyst { bool DatabaseIO::end_nl(Ioss::State state) { - assert(this->dbState == state); + if(this->dbState != state) { + std::ostringstream errmsg; + errmsg << "Catalyst: dbState != state in end_nl"<<"\n"; + IOSS_ERROR(errmsg); + } if (!is_input()) { auto region = this->get_region(); - assert(region != nullptr); + if(region == nullptr) { + std::ostringstream errmsg; + errmsg << "Catalyst: region is nullptr in end_nl"<<"\n"; + IOSS_ERROR(errmsg); + } auto &impl = (*this->Impl.get()); switch (state) { @@ -1180,7 +1252,11 @@ namespace Iocatalyst { void DatabaseIO::read_meta_data_nl() { auto region = this->get_region(); - assert(region != nullptr); + if(region == nullptr) { + std::ostringstream errmsg; + errmsg << "Catalyst: region is nullptr in read_meta_data_nl()"<<"\n"; + IOSS_ERROR(errmsg); + } auto &impl = (*this->Impl.get()); impl.readModel(region); @@ -1189,7 +1265,11 @@ namespace Iocatalyst { void DatabaseIO::get_step_times_nl() { auto region = this->get_region(); - assert(region != nullptr); + if(region == nullptr) { + std::ostringstream errmsg; + errmsg << "Catalyst: region is nullptr in get_step_times_nl()"<<"\n"; + IOSS_ERROR(errmsg); + } auto &impl = (*this->Impl.get()); impl.readTime(region); @@ -1420,6 +1500,7 @@ namespace Iocatalyst { int64_t DatabaseIO::get_field_internal(const Ioss::SideBlock *sb, const Ioss::Field &field, void *data, size_t data_size) const { + if(split_type_changed()) { return -1; } auto &impl = (*this->Impl.get()); return impl.getField(detail::SIDEBLOCKS, sb, field, data, data_size); } @@ -1450,6 +1531,7 @@ namespace Iocatalyst { int64_t DatabaseIO::get_field_internal(const Ioss::SideSet *ss, const Ioss::Field &field, void *data, size_t data_size) const { + if(split_type_changed()) { return -1; } auto &impl = (*this->Impl.get()); return impl.getField(detail::SIDESETS, ss, field, data, data_size); } @@ -1531,6 +1613,7 @@ namespace Iocatalyst { int64_t DatabaseIO::get_zc_field_internal(const Ioss::SideBlock *sb, const Ioss::Field &field, void **data, size_t *data_size) const { + if(split_type_changed()) { return -1; } auto &impl = (*this->Impl.get()); return impl.getFieldZeroCopy(detail::SIDEBLOCKS, sb, field, data, data_size); } @@ -1561,6 +1644,7 @@ namespace Iocatalyst { int64_t DatabaseIO::get_zc_field_internal(const Ioss::SideSet *ss, const Ioss::Field &field, void **data, size_t *data_size) const { + if(split_type_changed()) { return -1; } auto &impl = (*this->Impl.get()); return impl.getFieldZeroCopy(detail::SIDESETS, ss, field, data, data_size); } diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.h b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.h index c9602e7a5b..743cebe6fa 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.h @@ -85,6 +85,9 @@ namespace Iocatalyst { std::string get_catalyst_dump_dir() const; + void set_split_type_changed(bool split_type_changed) { split_type_c = split_type_changed; } + IOSS_NODISCARD bool split_type_changed() const { return split_type_c; } + private: bool open_group_nl(const std::string & /* group_name */) override { return false; } bool create_subgroup_nl(const std::string & /* group_name */) override { return false; } @@ -202,5 +205,6 @@ namespace Iocatalyst { class ImplementationT; std::unique_ptr Impl; bool useDeepCopy; + bool split_type_c{false}; }; } // namespace Iocatalyst diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMesh.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMesh.C index 745b9cf91f..b5c5ab8bbb 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMesh.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMesh.C @@ -291,4 +291,24 @@ namespace Iocatalyst { return (extents.i + offset) * (extents.j + offset) * (extents.k + offset); } + std::map* BlockMesh::getTransientCellFieldMap() + { + return &(this->transientCellFields); + } + + std::map* BlockMesh::getTransientPointFieldMap() + { + return &(this->transientPointFields); + } + + void BlockMesh::addTransientCellField(std::string f_name, double f_value) + { + this->transientCellFields.insert({ f_name, f_value }); + } + + void BlockMesh::addTransientPointField(std::string f_name, double f_value) + { + this->transientPointFields.insert({ f_name, f_value }); + } + } // namespace Iocatalyst diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMesh.h b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMesh.h index 2732c6d60e..ba5c4caa21 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMesh.h +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMesh.h @@ -82,6 +82,12 @@ namespace Iocatalyst { static Extent getCoordsForID(ID id, Extent bounds); static ID getIDfromCoords(Extent coords, Extent bounds); + void addTransientCellField(std::string f_name, double f_value); + void addTransientPointField(std::string f_name, double f_value); + + std::map* getTransientCellFieldMap(); + std::map* getTransientPointFieldMap(); + private: Partition partition; Extent origin; @@ -98,6 +104,9 @@ namespace Iocatalyst { int getNumInBlockMesh(unsigned int offset) const; ID id; static ID _id; + + std::map transientCellFields; + std::map transientPointFields; }; } // namespace Iocatalyst diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.C index a94edd44af..10fa7f5721 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.C @@ -40,18 +40,20 @@ namespace Iocatalyst { { CatalystManager::getInstance().reset(); iop.isCatalyst = true; + + //Cat Writes writeIOSSFile(iop); - Ioss::PropertyManager cdbProps; + Ioss::PropertyManager cdbProps = Ioss::PropertyManager(iop.dbProps); cdbProps.add(Ioss::Property("CATALYST_CONDUIT_NODE", iop.getCatalystConduitNode())); + //Cat Reads here Ioss::DatabaseIO *cdbi = Ioss::IOFactory::create(CATALYST_DATABASE_TYPE, CATALYST_DUMMY_DATABASE, Ioss::READ_RESTART, Ioss::ParallelUtils::comm_world(), cdbProps); if (cdbi == nullptr || !cdbi->ok(true)) { return; } - - Ioss::PropertyManager properties; + Ioss::PropertyManager properties = Ioss::PropertyManager(iop.dbProps); Ioss::DatabaseIO *cdbo = Ioss::IOFactory::create(iop.dbType, iop.fileName, Ioss::WRITE_RESULTS, Ioss::ParallelUtils::comm_world(), properties); @@ -68,6 +70,30 @@ namespace Iocatalyst { Ioss::copy_database(cir, cor, options); } + Ioss::DatabaseIO* BlockMeshSet::getCatalystDatabase(IOSSparams &iop) + { + CatalystManager::getInstance().reset(); + iop.isCatalyst = true; + + //Write to Cat database + writeIOSSFile(iop); + + Ioss::PropertyManager cdbProps = Ioss::PropertyManager(iop.dbProps); + + //Get Conduit + cdbProps.add(Ioss::Property("CATALYST_CONDUIT_NODE", iop.getCatalystConduitNode())); + + //Read to Cat Database + Ioss::DatabaseIO *cdbi = + Ioss::IOFactory::create(CATALYST_DATABASE_TYPE, CATALYST_DUMMY_DATABASE, Ioss::READ_RESTART, + Ioss::ParallelUtils::comm_world(), cdbProps); + if (cdbi == nullptr || !cdbi->ok(true)) { + return nullptr; + } + + return cdbi; + } + void BlockMeshSet::writeIOSSFile(IOSSparams &iop) { openIOSSDatabase(iop); @@ -80,7 +106,7 @@ namespace Iocatalyst { void BlockMeshSet::openIOSSDatabase(IOSSparams &iop) { - Ioss::PropertyManager properties; + Ioss::PropertyManager properties = Ioss::PropertyManager(iop.dbProps); std::string dbType = iop.dbType; if (iop.isCatalyst) { dbType = CATALYST_DATABASE_TYPE; @@ -162,7 +188,8 @@ namespace Iocatalyst { { auto c_node = reinterpret_cast( ((Iocatalyst::DatabaseIO *)iop.databaseIO)->get_catalyst_conduit_node()); - conduit_node_set_node(conduit_cpp::c_node(&iop.conduitNode), c_node); + auto cpp_node = conduit_cpp::cpp_node(c_node); + iop.conduitNode.set(cpp_node); } void BlockMeshSet::writeStructuredBlockDefinitions(IOSSparams &iop) @@ -231,6 +258,7 @@ namespace Iocatalyst { void BlockMeshSet::writeStructuredTransientFieldDefinitions(IOSSparams &iop) { for (auto bm : bms) { + //Modify this to access field dict in "bm" and populate ioss block with those fields (as well). auto iossBlock = iop.region->get_structured_block(getStructuredBlockName(bm.getID())); iossBlock->field_add(Ioss::Field(IOSS_CELL_FIELD, Ioss::Field::REAL, IOSS_SCALAR_STORAGE, Ioss::Field::TRANSIENT)); @@ -331,9 +359,39 @@ namespace Iocatalyst { auto nodeBlock = iop.region->get_node_block("nodeblock"); nodeBlock->field_add(Ioss::Field(IOSS_POINT_FIELD, Ioss::Field::REAL, IOSS_SCALAR_STORAGE, Ioss::Field::TRANSIENT)); + + writeUnstructuredAddedTransientFields(bm, iop); } } + void BlockMeshSet::writeUnstructuredAddedTransientFields(BlockMesh bm, IOSSparams &iop) + { + writeUnstructuredAddedCellTransientFields(bm, iop); + writeUnstructuredAddedPointTransientFields(bm, iop); + } + + void BlockMeshSet::writeUnstructuredAddedCellTransientFields(BlockMesh bm, IOSSparams &iop) + { + auto cell_fields = bm.getTransientCellFieldMap(); + auto elemBlock = iop.region->get_element_block(getUnstructuredBlockName(bm.getID())); + for (auto itr = cell_fields->begin(); itr != cell_fields->end(); ++itr) + { + elemBlock->field_add(Ioss::Field(itr->first, Ioss::Field::REAL, IOSS_SCALAR_STORAGE, + Ioss::Field::TRANSIENT)); + } + } + + void BlockMeshSet::writeUnstructuredAddedPointTransientFields(BlockMesh bm, IOSSparams &iop) + { + auto point_fields = bm.getTransientPointFieldMap(); + auto nodeBlock = iop.region->get_node_block("nodeblock"); + for (auto itr = point_fields->begin(); itr != point_fields->end(); ++itr) + { + nodeBlock->field_add(Ioss::Field(itr->first, Ioss::Field::REAL, IOSS_SCALAR_STORAGE, + Ioss::Field::TRANSIENT)); + } + } + void BlockMeshSet::writeUnstructuredTransientBulkData(IOSSparams &iop) { std::vector values; @@ -353,9 +411,47 @@ namespace Iocatalyst { values.push_back(bm.getPartition().id); } nodeBlock->put_field_data(IOSS_POINT_FIELD, values); + + writeUnstructuredAddedTransientFieldsBulkData(bm, iop); } } + void BlockMeshSet::writeUnstructuredAddedTransientFieldsBulkData(BlockMesh bm, IOSSparams &iop) + { + writeUnstructuredAddedCellTransientFieldsBulkData(bm, iop); + writeUnstructuredAddedPointTransientFieldsBulkData(bm, iop); + } + + void BlockMeshSet::writeUnstructuredAddedCellTransientFieldsBulkData(BlockMesh bm, IOSSparams &iop) + { + auto cell_fields = bm.getTransientCellFieldMap(); + auto elemBlock = iop.region->get_element_block(getUnstructuredBlockName(bm.getID())); + std::vector values; + for (auto itr = cell_fields->begin(); itr != cell_fields->end(); ++itr) + { + int num_elements = elemBlock->get_field(itr->first).raw_count(); + for (int j = 0; j < num_elements; j++) { + values.push_back(itr->second + j*0.1); + } + elemBlock->put_field_data(itr->first, values); + } + } + + void BlockMeshSet::writeUnstructuredAddedPointTransientFieldsBulkData(BlockMesh bm, IOSSparams &iop) + { + auto point_fields = bm.getTransientPointFieldMap(); + auto nodeBlock = iop.region->get_node_block("nodeblock"); + std::vector values; + for (auto itr = point_fields->begin(); itr != point_fields->end(); ++itr) + { + int num_nodes = nodeBlock->get_field(itr->first).raw_count(); + for (int j = 0; j < num_nodes; j++) { + values.push_back(itr->second + j*0.1); + } + nodeBlock->put_field_data(itr->first, values); + } + } + std::string BlockMeshSet::getStructuredBlockName(int index) { return "StructuredBlock" + std::to_string(index); diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.h b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.h index 3041e92158..bc98721fe3 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.h +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.h @@ -26,8 +26,8 @@ namespace Iocatalyst { class IOSSparams { public: - IOSSparams(const std::string &fileName, const std::string &dbType) - : fileName(fileName), dbType(dbType), databaseIO(nullptr), isCatalyst(false) + IOSSparams(const std::string &fileName, const std::string &dbType, Ioss::PropertyManager dbProps = {}) + : fileName(fileName), dbType(dbType), databaseIO(nullptr), isCatalyst(false), dbProps(dbProps) { } bool isStructured() { return dbType == CGNS_DATABASE_TYPE; } @@ -39,6 +39,7 @@ namespace Iocatalyst { bool isCatalyst; std::unique_ptr region; conduit_cpp::Node conduitNode; + Ioss::PropertyManager dbProps; private: IOSSparams(); @@ -47,8 +48,15 @@ namespace Iocatalyst { void addBlockMesh(const BlockMesh &blockMesh); void writeIOSSFile(IOSSparams &iop); void writeCatalystIOSSFile(IOSSparams &iop); + Ioss::DatabaseIO* getCatalystDatabase(IOSSparams &iop); + int getNumLocalPointsInMeshSet(); + std::string getStructuredBlockName(int index); + std::string getStructuredNodeBlockName(int index); + + std::string getUnstructuredBlockName(int index); + private: std::vector bms; @@ -68,15 +76,16 @@ namespace Iocatalyst { void writeUnstructuredBlockDefinitions(IOSSparams &iop); void writeUnstructuredBlockBulkData(IOSSparams &iop); void writeUnstructuredTransientFieldDefinitions(IOSSparams &iop); + void writeUnstructuredAddedTransientFields(BlockMesh bm, IOSSparams &iop); + void writeUnstructuredAddedCellTransientFields(BlockMesh bm, IOSSparams &iop); + void writeUnstructuredAddedPointTransientFields(BlockMesh bm, IOSSparams &iop); void writeUnstructuredTransientBulkData(IOSSparams &iop); + void writeUnstructuredAddedTransientFieldsBulkData(BlockMesh bm, IOSSparams &iop); + void writeUnstructuredAddedCellTransientFieldsBulkData(BlockMesh bm, IOSSparams &iop); + void writeUnstructuredAddedPointTransientFieldsBulkData(BlockMesh bm, IOSSparams &iop); void saveConduitNode(IOSSparams &iop); - std::string getStructuredBlockName(int index); - std::string getStructuredNodeBlockName(int index); - - std::string getUnstructuredBlockName(int index); - inline static const std::string CGNS_DATABASE_TYPE = "cgns"; inline static const std::string EXODUS_DATABASE_TYPE = "exodus"; inline static const std::string IOSS_CELL_FIELD = "cell"; diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSetTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSetTest.C index b5a0094539..98b155b038 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSetTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSetTest.C @@ -31,4 +31,20 @@ TEST_F(Iocatalyst_DatabaseIOTest, GetNumLocalPointsInMeshSet) setOrigin(5, 0, 0); addBlockMesh(bmFour); EXPECT_EQ(bmSet.getNumLocalPointsInMeshSet(), 189); +} + +TEST_F(Iocatalyst_DatabaseIOTest, AddTransientFieldToBlockMesh) +{ + Iocatalyst::BlockMesh bmOne; + setBlockMeshSize(2, 2, 2); + + bmOne.addTransientCellField("foo_x", 2); + bmOne.addTransientPointField("bar_x", 3); + + addBlockMesh(bmOne); + + std::string exodusFileName = + "AddTransientFieldToBlockMesh" + CATALYST_TEST_FILE_NP + std::to_string(part.size) + EXODUS_FILE_EXTENSION; + Iocatalyst::BlockMeshSet::IOSSparams iop(exodusFileName, EXODUS_DATABASE_TYPE); + bmSet.writeIOSSFile(iop); } \ No newline at end of file diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshTest.C index 1f9d43dfa0..12a7985c3d 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshTest.C @@ -504,3 +504,21 @@ TEST_F(BlockMeshTest, GetGlobalIDForPointID) id = bmOne.getIDfromCoords(coords, bmOne.getGlobalPointExtents()); EXPECT_EQ(bmOne.getGlobalIDForPointID(27), id); } + +TEST_F(BlockMeshTest, AddTransientField) +{ + part.id = 0; + part.size = 1; + numBlocks.i = 2; + numBlocks.j = 2; + numBlocks.k = 2; + bmOne.init(part, numBlocks, origin); + + bmOne.addTransientCellField("foo_x", 2); + bmOne.addTransientPointField("bar_x", 3); + + auto cell_fields = bmOne.getTransientCellFieldMap(); + EXPECT_EQ((*cell_fields)["foo_x"], 2); + auto point_fields = bmOne.getTransientPointFieldMap(); + EXPECT_EQ((*point_fields)["bar_x"], 3); +} diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.C index e203091c0c..bdf3d43227 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.C @@ -5,6 +5,7 @@ // See packages/seacas/LICENSE for details #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include Iocatalyst_DatabaseIOTest::Iocatalyst_DatabaseIOTest() { @@ -90,6 +92,130 @@ void Iocatalyst_DatabaseIOTest::runUnstructuredTest(const std::string &testName) EXPECT_TRUE(regionsAreEqual(exodusFileName, catalystFileName, EXODUS_DATABASE_TYPE)); } +Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::writeAndGetExodusDatabaseOnRead(const std::string &testName, + Ioss::PropertyManager dbProps) +{ + std::string exodusFileName = + testName + CATALYST_TEST_FILE_NP + std::to_string(part.size) + EXODUS_FILE_EXTENSION; + Iocatalyst::BlockMeshSet::IOSSparams iop(exodusFileName, EXODUS_DATABASE_TYPE); + bmSet.writeIOSSFile(iop); + Ioss::DatabaseIO* exo_db = getDatabaseOnReadFromFileName(exodusFileName, EXODUS_DATABASE_TYPE, dbProps); + if(exo_db == nullptr) + { + EXPECT_TRUE(false) << "Exodus db unable to initialize on read"; + } + return exo_db; +} + +Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getExodusDatabaseFromFile(std::string &filename, + Ioss::PropertyManager dbProps) { + Ioss::PropertyManager edbProps(dbProps); + + std::string inputFileName = filename; + + Ioss::DatabaseIO *edbi = + Ioss::IOFactory::create(EXODUS_DATABASE_TYPE, inputFileName, Ioss::READ_RESTART, + Ioss::ParallelUtils::comm_self(), edbProps); + if (edbi == nullptr || !edbi->ok(true)) { + return nullptr; + } + + return edbi; +} + +conduit_cpp::Node Iocatalyst_DatabaseIOTest::getConduitFromExodusFile(std::string &filename, + Ioss::PropertyManager dbProps) +{ + Iocatalyst::CatalystManager::getInstance().reset(); + + Ioss::PropertyManager edbProps; + edbProps.add(Ioss::Property("SURFACE_SPLIT_TYPE", "TOPOLOGY")); + Ioss::DatabaseIO *edbi = getExodusDatabaseFromFile(filename, edbProps); + + //Create Cat Db on write + Ioss::PropertyManager cdbwProps(edbi->get_property_manager()); + Ioss::DatabaseIO *cdb_on_write = + Ioss::IOFactory::create(CATALYST_DATABASE_TYPE, CATALYST_DUMMY_DATABASE, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), cdbwProps); + if (cdb_on_write == nullptr || !cdb_on_write->ok(true)) { + return conduit_cpp::Node(); + } + + Ioss::Region cor(edbi); + Ioss::Region cir(cdb_on_write); + Ioss::MeshCopyOptions options; + options.data_storage_type = 1; + Ioss::copy_database(cor, cir, options); + + auto c_node = reinterpret_cast( + ((Iocatalyst::DatabaseIO *)cdb_on_write)->get_catalyst_conduit_node()); + conduit_cpp::Node conduitNode; + auto cpp_node = conduit_cpp::cpp_node(c_node); + conduitNode.set(cpp_node); + return conduitNode; + +} + +Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getCatalystDatabaseFromConduit(conduit_cpp::Node &conduitNode, + Ioss::PropertyManager dbProps) +{ + + Ioss::PropertyManager cdbrProps = Ioss::PropertyManager(dbProps); + cdbrProps.add(Ioss::Property("CATALYST_CONDUIT_NODE", conduit_cpp::c_node(&conduitNode))); + + //Give to Cat Db on read + Ioss::DatabaseIO *cdb_on_read = + Ioss::IOFactory::create(CATALYST_DATABASE_TYPE, CATALYST_DUMMY_DATABASE, Ioss::READ_RESTART, + Ioss::ParallelUtils::comm_world(), cdbrProps); + if (cdb_on_read == nullptr || !cdb_on_read->ok(true)) { + return nullptr; + } + + return cdb_on_read; +} + +Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getDatabaseOnReadFromFileName(const std::string &fileName, + const std::string &iossDatabaseType, + Ioss::PropertyManager dbProps) +{ + Ioss::PropertyManager dbaseProps = Ioss::PropertyManager(dbProps); + //dbProps.add(Ioss::Property("ENABLE_FIELD_RECOGNITION", "OFF")); + auto inputFileName = fileName; + Ioss::ParallelUtils pu; + int numRanks = pu.parallel_size(); + int rank = pu.parallel_rank(); + if (iossDatabaseType == EXODUS_DATABASE_TYPE && numRanks > 1) { + inputFileName += "." + std::to_string(numRanks) + "." + std::to_string(rank); + } + Ioss::DatabaseIO *dbi = + Ioss::IOFactory::create(iossDatabaseType, inputFileName, Ioss::READ_RESTART, + Ioss::ParallelUtils::comm_self(), dbaseProps); + if (dbi == nullptr || !dbi->ok(true)) { + return nullptr; + } + return dbi; +} + +/*Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::writeAndGetCatalystDatabaseOnRead(Ioss::PropertyManager dbProps) +{ + std::string exodusFileName = + "test_eb_1_enable_field_recog" + CATALYST_TEST_FILE_NP + std::to_string(part.size) + EXODUS_FILE_EXTENSION; + Iocatalyst::BlockMeshSet::IOSSparams iop(exodusFileName, EXODUS_DATABASE_TYPE); + + Ioss::DatabaseIO *cat_d = bmSet.getCatalystDatabase(iop); + + //Ioss::Region cir(cat_d); + //std::cout<<"Done Region!"< @@ -63,4 +81,6 @@ class IOCATALYST_EXPORT Iocatalyst_DatabaseIOTest : public ::testing::Test const std::string EXODUS_FILE_EXTENSION = ".ex2"; const std::string CATALYST_TEST_FILE_PREFIX = "catalyst_"; const std::string CATALYST_TEST_FILE_NP = "_np_"; + inline static const std::string CATALYST_DATABASE_TYPE = "catalyst"; + inline static const std::string CATALYST_DUMMY_DATABASE = "dummy.db"; }; diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ElementBlockTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ElementBlockTest.C index 1541359134..afcbaa9141 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ElementBlockTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ElementBlockTest.C @@ -5,6 +5,10 @@ // See packages/seacas/LICENSE for details #include +#include + +#include + TEST_F(Iocatalyst_DatabaseIOTest, WriteThreeElementBlocksWith24Cells) { @@ -58,4 +62,282 @@ TEST_F(Iocatalyst_DatabaseIOTest, WriteThreeElementBlocksWith835Cells) addBlockMesh(bmThree); runUnstructuredTest("test_eb_3_cells_835"); -} \ No newline at end of file +} + +TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_ENABLE_FIELD_RECOGNITION_ON) +{ + Iocatalyst::BlockMesh bm; + setBlockMeshSize(2, 2, 2); + + bm.addTransientCellField("foo_x", 2); + bm.addTransientCellField("foo_y", 3); + bm.addTransientCellField("foo_z", 4); + + + addBlockMesh(bm); + + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("ENABLE_FIELD_RECOGNITION", "ON")); + Ioss::DatabaseIO *exo_d = writeAndGetExodusDatabaseOnRead("test_eb_1_enable_field_recog", iossProp); + + Iocatalyst::BlockMeshSet::IOSSparams iop("cat", EXODUS_DATABASE_TYPE, iossProp); + + Ioss::DatabaseIO *cat_d = bmSet.getCatalystDatabase(iop); + + if(cat_d == nullptr){ EXPECT_TRUE(false) << "Catalyst db unable to initialize on read"; } + Ioss::Region cat_reg(cat_d); + + Ioss::Region exo_reg(exo_d); + + auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + + bool exo_foo_exists = exo_elemBlock->field_exists("foo"); + bool cat_foo_exists = cat_elemBlock->field_exists("foo"); + EXPECT_TRUE(exo_foo_exists); + EXPECT_TRUE(cat_foo_exists); + if(exo_foo_exists && cat_foo_exists) + EXPECT_TRUE(exo_elemBlock->get_field("foo") == cat_elemBlock->get_field("foo")); + + //Check foo_x doesn't exist + bool exo_foo_x_exists = exo_elemBlock->field_exists("foo_x"); + bool cat_foo_x_exists = cat_elemBlock->field_exists("foo_x"); + EXPECT_FALSE(exo_foo_x_exists); + EXPECT_FALSE(cat_foo_x_exists); +} + +//Sanity Test +TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_ENABLE_FIELD_RECOGNITION_OFF) +{ + Iocatalyst::BlockMesh bm; + setBlockMeshSize(2, 2, 2); + + bm.addTransientCellField("foo_x", 2); + bm.addTransientCellField("foo_y", 3); + bm.addTransientCellField("foo_z", 4); + + + addBlockMesh(bm); + + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("ENABLE_FIELD_RECOGNITION", "OFF")); + Ioss::DatabaseIO *exo_d = writeAndGetExodusDatabaseOnRead("test_eb_1_enable_field_recog", iossProp); + + Iocatalyst::BlockMeshSet::IOSSparams iop("cat", EXODUS_DATABASE_TYPE, iossProp); + + Ioss::DatabaseIO *cat_d = bmSet.getCatalystDatabase(iop); + + if(cat_d == nullptr){ EXPECT_TRUE(false) << "Catalyst db unable to initialize on read"; } + Ioss::Region cat_reg(cat_d); + + Ioss::Region exo_reg(exo_d); + + auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + + bool exo_foo_x_exists = exo_elemBlock->field_exists("foo_x"); + bool cat_foo_x_exists = cat_elemBlock->field_exists("foo_x"); + EXPECT_TRUE(exo_foo_x_exists); + EXPECT_TRUE(cat_foo_x_exists); + if(exo_foo_x_exists && cat_foo_x_exists) + EXPECT_TRUE(exo_elemBlock->get_field("foo_x") == cat_elemBlock->get_field("foo_x")); + +} + + +TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_IGNORE_REALN_FIELDS_ON) +{ + Iocatalyst::BlockMesh bm; + setBlockMeshSize(2, 2, 2); + + bm.addTransientCellField("foo_1", 2); + bm.addTransientCellField("foo_2", 3); + bm.addTransientCellField("foo_3", 4); + + + addBlockMesh(bm); + + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("IGNORE_REALN_FIELDS", "ON")); + Ioss::DatabaseIO *exo_d = writeAndGetExodusDatabaseOnRead("test_eb_1_ignore_realn_fields", iossProp); + + Iocatalyst::BlockMeshSet::IOSSparams iop("cat", EXODUS_DATABASE_TYPE, iossProp); + + Ioss::DatabaseIO *cat_d = bmSet.getCatalystDatabase(iop); + + if(cat_d == nullptr){ EXPECT_TRUE(false) << "Catalyst db unable to initialize on read"; } + Ioss::Region cat_reg(cat_d); + + Ioss::Region exo_reg(exo_d); + + auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + + bool exo_foo_1_exists = exo_elemBlock->field_exists("foo_1"); + bool cat_foo_1_exists = cat_elemBlock->field_exists("foo_1"); + EXPECT_TRUE(exo_foo_1_exists); + EXPECT_TRUE(cat_foo_1_exists); + if(exo_foo_1_exists && cat_foo_1_exists) + EXPECT_TRUE(exo_elemBlock->get_field("foo_1") == cat_elemBlock->get_field("foo_1")); +} + +TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_IGNORE_REALN_FIELDS_OFF) +{ + Iocatalyst::BlockMesh bm; + setBlockMeshSize(2, 2, 2); + + bm.addTransientCellField("foo_1", 2); + bm.addTransientCellField("foo_2", 3); + bm.addTransientCellField("foo_3", 4); + + + addBlockMesh(bm); + + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("IGNORE_REALN_FIELDS", "OFF")); + Ioss::DatabaseIO *exo_d = writeAndGetExodusDatabaseOnRead("test_eb_1_ignore_realn_fields_off", iossProp); + + Iocatalyst::BlockMeshSet::IOSSparams iop("cat", EXODUS_DATABASE_TYPE, iossProp); + + Ioss::DatabaseIO *cat_d = bmSet.getCatalystDatabase(iop); + + if(cat_d == nullptr){ EXPECT_TRUE(false) << "Catalyst db unable to initialize on read"; } + Ioss::Region cat_reg(cat_d); + + Ioss::Region exo_reg(exo_d); + + auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + + bool exo_foo_exists = exo_elemBlock->field_exists("foo"); + bool cat_foo_exists = cat_elemBlock->field_exists("foo"); + EXPECT_TRUE(exo_foo_exists); + EXPECT_TRUE(cat_foo_exists); + if(exo_foo_exists && cat_foo_exists) + EXPECT_TRUE(exo_elemBlock->get_field("foo") == cat_elemBlock->get_field("foo")); +} + +TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_FIELD_SUFFIX_SEPARATOR) +{ + Iocatalyst::BlockMesh bm; + setBlockMeshSize(2, 2, 2); + + bm.addTransientCellField("foo_x", 2); + bm.addTransientCellField("foo_y", 3); + bm.addTransientCellField("foo_z", 4); + bm.addTransientCellField("bar:x", 5); + bm.addTransientCellField("bar:y", 6); + bm.addTransientCellField("bar:z", 7); + + + addBlockMesh(bm); + + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("FIELD_SUFFIX_SEPARATOR", ":")); + + Ioss::DatabaseIO *exo_d = writeAndGetExodusDatabaseOnRead("test_eb_1_field_suf_sep", iossProp); + + Iocatalyst::BlockMeshSet::IOSSparams iop("cat", EXODUS_DATABASE_TYPE, iossProp); + Ioss::DatabaseIO *cat_d = bmSet.getCatalystDatabase(iop); + + + if(cat_d == nullptr){ EXPECT_TRUE(false) << "Catalyst db unable to initialize on read"; } + Ioss::Region cat_reg(cat_d); + + Ioss::Region exo_reg(exo_d); + + auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + + bool exo_foo_x_exists = exo_elemBlock->field_exists("foo_x"); + bool cat_foo_x_exists = cat_elemBlock->field_exists("foo_x"); + EXPECT_TRUE(exo_foo_x_exists); + EXPECT_TRUE(cat_foo_x_exists); + if(exo_foo_x_exists && cat_foo_x_exists) + EXPECT_TRUE(exo_elemBlock->get_field("foo_x") == cat_elemBlock->get_field("foo_x")); + + bool exo_bar_exists = exo_elemBlock->field_exists("bar"); + bool cat_bar_exists = cat_elemBlock->field_exists("bar"); + EXPECT_TRUE(exo_bar_exists); + EXPECT_TRUE(cat_bar_exists); + if(exo_bar_exists && cat_bar_exists) + EXPECT_TRUE(exo_elemBlock->get_field("bar") == cat_elemBlock->get_field("bar")); + +} + +TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_FIELD_STRIP_TRAILING_UNDERSCORE) +{ + Iocatalyst::BlockMesh bm; + setBlockMeshSize(2, 2, 2); + + bm.addTransientCellField("foo_x", 2); + bm.addTransientCellField("foo_y", 3); + bm.addTransientCellField("foo_z", 4); + + addBlockMesh(bm); + + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("FIELD_STRIP_TRAILING_UNDERSCORE", "ON")); + iossProp.add(Ioss::Property("FIELD_SUFFIX_SEPARATOR", "")); + + Ioss::DatabaseIO *exo_d = writeAndGetExodusDatabaseOnRead("test_eb_1_field_strip_tr_unders", iossProp); + + Iocatalyst::BlockMeshSet::IOSSparams iop("cat", EXODUS_DATABASE_TYPE, iossProp); + + Ioss::DatabaseIO *cat_d = bmSet.getCatalystDatabase(iop); + + if(cat_d == nullptr){ EXPECT_TRUE(false) << "Catalyst db unable to initialize on read"; } + Ioss::Region cat_reg(cat_d); + + Ioss::Region exo_reg(exo_d); + + auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + + bool exo_foo_exists = exo_elemBlock->field_exists("foo"); + bool cat_foo_exists = cat_elemBlock->field_exists("foo"); + EXPECT_TRUE(exo_foo_exists); + EXPECT_TRUE(cat_foo_exists); + if(exo_foo_exists && cat_foo_exists) + EXPECT_TRUE(exo_elemBlock->get_field("foo") == cat_elemBlock->get_field("foo")); + +} + +//Read from file. Can. Or available exodus file in test suite. +TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_SURFACE_SPLIT_TYPE) +{ + Iocatalyst::BlockMesh bm; + setBlockMeshSize(2, 2, 2); + + addBlockMesh(bm); + + //If write and read have same split type, we can handle. Else no. + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("SURFACE_SPLIT_TYPE", "BLOCK")); + + std::string exoFile = "can.ex2"; + + conduit_cpp::Node c_node = getConduitFromExodusFile(exoFile, iossProp); + + Ioss::DatabaseIO *cat_d = getCatalystDatabaseFromConduit(c_node, iossProp); + if(cat_d == nullptr){ EXPECT_TRUE(false) << "Catalyst db unable to initialize"; } + + Ioss::Region cat_reg(cat_d); + + Ioss::SideSetContainer cat_sideSets = cat_reg.get_sidesets(); + + EXPECT_TRUE(cat_sideSets.empty())<<"Cat sidesets not empty when different SURFACE_SPLIT_TYPE"; + + Ioss::PropertyManager iossProp_s; + iossProp_s.add(Ioss::Property("SURFACE_SPLIT_TYPE", "TOPOLOGY")); + cat_d = getCatalystDatabaseFromConduit(c_node, iossProp_s); + + if(cat_d == nullptr){ EXPECT_TRUE(false) << "Catalyst db unable to initialize"; } + + Ioss::Region cat_reg_same(cat_d); + + cat_sideSets = cat_reg_same.get_sidesets(); + + EXPECT_TRUE(!cat_sideSets.empty())<<"Cat sidesets empty when identical SURFACE_SPLIT_TYPE"; + +} From 583760d016b636cb4886438ef00104bbc376965b Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 20 May 2024 11:44:02 -0600 Subject: [PATCH 07/59] APREPRO: EQ and NE comparisons exp and string are valid --- packages/seacas/libraries/aprepro_lib/apr_aprepro.cc | 4 ++-- packages/seacas/libraries/aprepro_lib/apr_parser.cc | 10 +--------- packages/seacas/libraries/aprepro_lib/aprepro.yy | 8 ++++---- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/seacas/libraries/aprepro_lib/apr_aprepro.cc b/packages/seacas/libraries/aprepro_lib/apr_aprepro.cc index 6058de08d4..2f0a5e62e0 100644 --- a/packages/seacas/libraries/aprepro_lib/apr_aprepro.cc +++ b/packages/seacas/libraries/aprepro_lib/apr_aprepro.cc @@ -33,8 +33,8 @@ #endif namespace { - const std::string version_short{"6.31"}; - const std::string version_date{"(2024/05/13)"}; + const std::string version_short{"6.32"}; + const std::string version_date{"(2024/05/20)"}; const std::string version_string = version_short + " " + version_date; void output_copyright(); diff --git a/packages/seacas/libraries/aprepro_lib/apr_parser.cc b/packages/seacas/libraries/aprepro_lib/apr_parser.cc index b31b887a42..f9aef0ff57 100644 --- a/packages/seacas/libraries/aprepro_lib/apr_parser.cc +++ b/packages/seacas/libraries/aprepro_lib/apr_parser.cc @@ -2,7 +2,7 @@ // Skeleton implementation for Bison LALR(1) parsers in C++ -// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. +// Copyright (C) 2002-2015, 2018-2021, 2024 Free Software Foundation, Inc. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -749,15 +749,11 @@ namespace SEAMS { case 44: // bool: exp EQ sexp { (yylhs.value.val) = false; - yyerror(aprepro, "Comparison of arithmetic with string not defined"); - yyerrok; } break; case 45: // bool: exp NE sexp { (yylhs.value.val) = true; - yyerror(aprepro, "Comparison of arithmetic with string not defined"); - yyerrok; } break; case 46: // bool: sexp LT exp @@ -791,15 +787,11 @@ namespace SEAMS { case 50: // bool: sexp EQ exp { (yylhs.value.val) = false; - yyerror(aprepro, "Comparison of string with arithmetic not defined"); - yyerrok; } break; case 51: // bool: sexp NE exp { (yylhs.value.val) = true; - yyerror(aprepro, "Comparison of string with arithmetic not defined"); - yyerrok; } break; case 52: // bool: UNDVAR LT sexp diff --git a/packages/seacas/libraries/aprepro_lib/aprepro.yy b/packages/seacas/libraries/aprepro_lib/aprepro.yy index 23387e9a40..4496bc2ff3 100644 --- a/packages/seacas/libraries/aprepro_lib/aprepro.yy +++ b/packages/seacas/libraries/aprepro_lib/aprepro.yy @@ -189,15 +189,15 @@ bool: sexp LT sexp { $$ = (strcmp($1,$3) < 0 ? 1 : 0); } | exp GT sexp { $$ = false; yyerror(aprepro, "Comparison of arithmetic with string not defined"); yyerrok;} | exp LE sexp { $$ = false; yyerror(aprepro, "Comparison of arithmetic with string not defined"); yyerrok;} | exp GE sexp { $$ = false; yyerror(aprepro, "Comparison of arithmetic with string not defined"); yyerrok;} - | exp EQ sexp { $$ = false; yyerror(aprepro, "Comparison of arithmetic with string not defined"); yyerrok;} - | exp NE sexp { $$ = true; yyerror(aprepro, "Comparison of arithmetic with string not defined"); yyerrok;} + | exp EQ sexp { $$ = false; } + | exp NE sexp { $$ = true; } | sexp LT exp { $$ = false; yyerror(aprepro, "Comparison of string with arithmetic not defined"); yyerrok;} | sexp GT exp { $$ = false; yyerror(aprepro, "Comparison of string with arithmetic not defined"); yyerrok;} | sexp LE exp { $$ = false; yyerror(aprepro, "Comparison of string with arithmetic not defined"); yyerrok;} | sexp GE exp { $$ = false; yyerror(aprepro, "Comparison of string with arithmetic not defined"); yyerrok;} - | sexp EQ exp { $$ = false; yyerror(aprepro, "Comparison of string with arithmetic not defined"); yyerrok;} - | sexp NE exp { $$ = true; yyerror(aprepro, "Comparison of string with arithmetic not defined"); yyerrok;} + | sexp EQ exp { $$ = false; } + | sexp NE exp { $$ = true; } | UNDVAR LT sexp { $$ = (strcmp("",$3) < 0 ? 1 : 0); undefined_error(aprepro, $1->name); } | UNDVAR GT sexp { $$ = (strcmp("",$3) > 0 ? 1 : 0); undefined_error(aprepro, $1->name); } From 75e7dceb53cdbe1aab44bac9ae1f443c44cdf176 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 21 May 2024 10:32:22 -0600 Subject: [PATCH 08/59] IOSS: Clean up fmt includes/use --- .../libraries/ioss/src/Ioss_StructuredBlock.h | 7 +- .../ioss/src/Ioss_ZoneConnectivity.h | 4 + .../ioss/src/heartbeat/Iohb_Layout.C | 91 +++++++++++++++++++ .../ioss/src/heartbeat/Iohb_Layout.h | 79 ---------------- 4 files changed, 100 insertions(+), 81 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h index ece3ea2b6a..283b3af21b 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h +++ b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h @@ -12,10 +12,11 @@ #include "Ioss_NodeBlock.h" #include "Ioss_Property.h" #include "Ioss_ZoneConnectivity.h" +#if !defined BUILT_IN_SIERRA +#include +#endif #include #include -#include -#include #include #include #include @@ -370,6 +371,7 @@ namespace Ioss { }; } // namespace Ioss +#if !defined BUILT_IN_SIERRA #if FMT_VERSION >= 90000 namespace fmt { template <> struct formatter : ostream_formatter @@ -377,3 +379,4 @@ namespace fmt { }; } // namespace fmt #endif +#endif diff --git a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h index 9927e1b3d9..1df3ded943 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h @@ -10,8 +10,10 @@ #include #include #include +#if !defined BUILT_IN_SIERRA #include #include +#endif #include #include #include @@ -154,6 +156,7 @@ namespace Ioss { IOSS_EXPORT std::ostream &operator<<(std::ostream &os, const ZoneConnectivity &zgc); } // namespace Ioss +#if !defined BUILT_IN_SIERRA #if FMT_VERSION >= 90000 namespace fmt { template <> struct formatter : ostream_formatter @@ -161,3 +164,4 @@ namespace fmt { }; } // namespace fmt #endif +#endif diff --git a/packages/seacas/libraries/ioss/src/heartbeat/Iohb_Layout.C b/packages/seacas/libraries/ioss/src/heartbeat/Iohb_Layout.C index e08fd60a7f..39bcc59b9c 100644 --- a/packages/seacas/libraries/ioss/src/heartbeat/Iohb_Layout.C +++ b/packages/seacas/libraries/ioss/src/heartbeat/Iohb_Layout.C @@ -5,6 +5,9 @@ // See packages/seacas/LICENSE for details #include "heartbeat/Iohb_Layout.h" +#include +#include +#include #include namespace Iohb { @@ -21,4 +24,92 @@ namespace Iohb { fmt::print(layout_, "{}{:>{}}", legendStarted ? separator_ : "", label, fieldWidth_); legendStarted = true; } + + void Layout::output_common(const std::string &name) + { + if (count_++ > 0 && !separator_.empty()) { + fmt::print(layout_, "{}", separator_); + } + + if (showLabels && !name.empty()) { + fmt::print(layout_, "{}=", name); + } + } + + template void Layout::add(const std::string &name, const std::string &value); + template void Layout::add(const std::string &name, const int &value); + template void Layout::add(const std::string &name, const int64_t &value); + template void Layout::add(const std::string &name, const size_t &value); + + // Ideally, this would be in the include file, but when building in Sierra, we + // need to keep all `fmt` includes out of the include file due to some TPLs + // having an embedded fmt which is a different version than used by IOSS. + template void Layout::add(const std::string &name, const T &value) + { + output_common(name); + if (!showLabels && fieldWidth_ > 0) { + fmt::print(layout_, "{0:{1}}", value, fieldWidth_); + } + else { + fmt::print(layout_, "{}", value); + } + } + + template <> void Layout::add(const std::string &name, const double &value) + { + output_common(name); + if (precision_ == -1) { + // Use lib::fmt full precision output -- as many digits as needed to fully represent the + // double + fmt::print(layout_, "{}", value); + } + else if (!showLabels && fieldWidth_ > 0) { + fmt::print(layout_, "{0:{1}.{2}e}", value, fieldWidth_, precision_); + } + else { + fmt::print(layout_, "{0:.{1}e}", value, precision_); + } + } + + template void Layout::add(const std::string &name, const std::vector &value); + template void Layout::add(const std::string &name, const std::vector &value); + template void Layout::add(const std::string &name, const std::vector &value); + + template void Layout::add(const std::string &name, const std::vector &value) + { + if (value.size() == 1) { + add(name, value[0]); + } + else { + output_common(name); + if (!showLabels && fieldWidth_ > 0) { + fmt::print(layout_, "{0:{1}}", fmt::join(value, separator_), fieldWidth_); + } + else { + fmt::print(layout_, "{}", fmt::join(value, separator_)); + } + } + } + + template <> void Layout::add(const std::string &name, const std::vector &value) + { + if (value.size() == 1) { + add(name, value[0]); + } + else { + output_common(name); + if (precision_ == -1) { + // Use lib::fmt full precision output -- as many digits as needed to fully represent the + // double + fmt::print(layout_, "{}", fmt::join(value, separator_)); + } + else if (!showLabels && fieldWidth_ > 0) { + fmt::print(layout_, "{0:{2}.{1}e}", fmt::join(value, separator_), precision_, fieldWidth_); + } + else { + fmt::print(layout_, "{0:.{1}e}", fmt::join(value, separator_), precision_); + } + } + } + } // namespace Iohb diff --git a/packages/seacas/libraries/ioss/src/heartbeat/Iohb_Layout.h b/packages/seacas/libraries/ioss/src/heartbeat/Iohb_Layout.h index 34122957be..bc73c77b10 100644 --- a/packages/seacas/libraries/ioss/src/heartbeat/Iohb_Layout.h +++ b/packages/seacas/libraries/ioss/src/heartbeat/Iohb_Layout.h @@ -6,9 +6,6 @@ #pragma once -#include -#include -#include #include #include #include @@ -43,80 +40,4 @@ namespace Iohb { bool legendStarted{false}; }; - inline void Layout::output_common(const std::string &name) - { - if (count_++ > 0 && !separator_.empty()) { - fmt::print(layout_, "{}", separator_); - } - - if (showLabels && !name.empty()) { - fmt::print(layout_, "{}=", name); - } - } - - template inline void Layout::add(const std::string &name, const T &value) - { - output_common(name); - if (!showLabels && fieldWidth_ > 0) { - fmt::print(layout_, "{0:{1}}", value, fieldWidth_); - } - else { - fmt::print(layout_, "{}", value); - } - } - - template <> inline void Layout::add(const std::string &name, const double &value) - { - output_common(name); - if (precision_ == -1) { - // Use lib::fmt full precision output -- as many digits as needed to fully represent the - // double - fmt::print(layout_, "{}", value); - } - else if (!showLabels && fieldWidth_ > 0) { - fmt::print(layout_, "{0:{1}.{2}e}", value, fieldWidth_, precision_); - } - else { - fmt::print(layout_, "{0:.{1}e}", value, precision_); - } - } - - template - inline void Layout::add(const std::string &name, const std::vector &value) - { - if (value.size() == 1) { - add(name, value[0]); - } - else { - output_common(name); - if (!showLabels && fieldWidth_ > 0) { - fmt::print(layout_, "{0:{1}}", fmt::join(value, separator_), fieldWidth_); - } - else { - fmt::print(layout_, "{}", fmt::join(value, separator_)); - } - } - } - - template <> inline void Layout::add(const std::string &name, const std::vector &value) - { - if (value.size() == 1) { - add(name, value[0]); - } - else { - output_common(name); - if (precision_ == -1) { - // Use lib::fmt full precision output -- as many digits as needed to fully represent the - // double - fmt::print(layout_, "{}", fmt::join(value, separator_)); - } - else if (!showLabels && fieldWidth_ > 0) { - fmt::print(layout_, "{0:{2}.{1}e}", fmt::join(value, separator_), precision_, fieldWidth_); - } - else { - fmt::print(layout_, "{0:.{1}e}", fmt::join(value, separator_), precision_); - } - } - } - } // namespace Iohb From 64c4280c8c552beb08bb5cf3ff6af8039e137194 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 29 May 2024 16:04:38 -0600 Subject: [PATCH 09/59] GJOIN: Fix nset sset name handling --- packages/seacas/applications/gjoin/gj_muness.f | 8 +++++++- packages/seacas/applications/gjoin/gj_munnps.f | 12 +++++++++--- packages/seacas/applications/gjoin/gj_qainfo.blk | 7 ++++--- packages/seacas/applications/gjoin/gjoin.f | 11 +++++++++-- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/packages/seacas/applications/gjoin/gj_muness.f b/packages/seacas/applications/gjoin/gj_muness.f index f73f1c6f0e..ed71e0bab1 100644 --- a/packages/seacas/applications/gjoin/gj_muness.f +++ b/packages/seacas/applications/gjoin/gj_muness.f @@ -8,7 +8,8 @@ SUBROUTINE MUNESS (NUMESS, ISTAT, LESSEL, LESSDL, & IDESS, NEESS, NEDSS, IXEESS, IXEDSS, & LTEESS, LTSSS, FACSS, - & LTEX, LTSX, TDX, IXESS, IXDSS, NEX, NDX, ISCR, USESDF) + & LTEX, LTSX, TDX, IXESS, IXDSS, NEX, NDX, ISCR, USESDF, + $ NAMES, NAMSC) C======================================================================= C --*** MUNESS *** (GJOIN) Compress and rearrange element side sets @@ -42,6 +43,8 @@ SUBROUTINE MUNESS (NUMESS, ISTAT, LESSEL, LESSDL, C -- NDX - SCRATCH - size = NUMESS -- dist-face C -- ISCR - SCRATCH - size = NUMESS + include 'gj_namlen.blk' + INTEGER ISTAT(*) INTEGER IDESS(*) INTEGER NEESS(*), NEDSS(*) @@ -53,6 +56,8 @@ SUBROUTINE MUNESS (NUMESS, ISTAT, LESSEL, LESSDL, INTEGER ISCR(*) REAL FACSS(*), TDX(*) LOGICAL USESDF + character*(namlen) names(*) + character*(namlen) namsc(*) IF (NUMESS .LE. 0) RETURN @@ -91,6 +96,7 @@ SUBROUTINE MUNESS (NUMESS, ISTAT, LESSEL, LESSDL, 110 CONTINUE CALL ORDIX (JESS, IXESS, NUMESS, IDESS, ISCR, IDESS) + CALL ORDNAM (JESS, IXESS, NUMESS, NAMES, NAMSC, NAMES) CALL MOVINT (JESS, NEX, NEESS) CALL MOVINT (JESS, NDX, NEDSS) NUMESS = JESS diff --git a/packages/seacas/applications/gjoin/gj_munnps.f b/packages/seacas/applications/gjoin/gj_munnps.f index 8b63fc73d9..42db35fd46 100644 --- a/packages/seacas/applications/gjoin/gj_munnps.f +++ b/packages/seacas/applications/gjoin/gj_munnps.f @@ -7,7 +7,8 @@ C======================================================================= SUBROUTINE MUNNPS (NUMNPS, ISTAT, LNPSNL, & IDNPS, NNNPS, IXNNPS, LTNNPS, FACNPS, - & LTNX, FACX, IXNPS, NNX, ISCR, NODSCR, NUMNP) + & LTNX, FACX, IXNPS, NNX, ISCR, NODSCR, + $ NAMNS, NAMSC, NUMNP) C======================================================================= C --*** MUNNPS *** (GJOIN) Compress and rearrange nodal point sets @@ -36,6 +37,8 @@ SUBROUTINE MUNNPS (NUMNPS, ISTAT, LNPSNL, C -- NUMNP - IN -- number of nodes in model C -- NODSCR - SCRATCH - size = NUMNOD + include 'gj_namlen.blk' + INTEGER ISTAT(*) INTEGER IDNPS(*) INTEGER NNNPS(*) @@ -46,7 +49,9 @@ SUBROUTINE MUNNPS (NUMNPS, ISTAT, LNPSNL, INTEGER NNX(*) INTEGER ISCR(*) INTEGER NODSCR(*) - + character*(namlen) namns(*) + character*(namlen) namsc(*) + IF (NUMNPS .LE. 0) RETURN JNPS = 0 @@ -81,7 +86,7 @@ SUBROUTINE MUNNPS (NUMNPS, ISTAT, LNPSNL, NNEW = 0 DO 100 I = 1, NNNPS(N) - IF (nodscr(ltnnps(inn0+i)) .eq. 0) then + 2 IF (nodscr(ltnnps(inn0+i)) .eq. 0) then NNEW = NNEW + 1 LTNX(JNN0+NNEW) = LTNNPS(INN0+I) FACX(JNN0+NNEW) = FACNPS(INN0+I) @@ -93,6 +98,7 @@ SUBROUTINE MUNNPS (NUMNPS, ISTAT, LNPSNL, 120 CONTINUE CALL ORDIX (JNPS, IXNPS, NUMNPS, IDNPS, ISCR, IDNPS) + CALL ORDNAM (JNPS, IXNPS, NUMNPS, NAMNS, NAMSC, NAMNS) CALL MOVINT (JNPS, NNX, NNNPS) NUMNPS = JNPS JNN = 1 diff --git a/packages/seacas/applications/gjoin/gj_qainfo.blk b/packages/seacas/applications/gjoin/gj_qainfo.blk index f907641f6b..8dc80eb185 100644 --- a/packages/seacas/applications/gjoin/gj_qainfo.blk +++ b/packages/seacas/applications/gjoin/gj_qainfo.blk @@ -6,8 +6,8 @@ C See packages/seacas/LICENSE for details C -*- Mode: fortran -*- QAINFO(1) = 'GJoin2 ' - QAINFO(2) = '2024/03/19 ' - QAINFO(3) = ' 1.41 ' + QAINFO(2) = '2024/05/29 ' + QAINFO(3) = ' 1.42 ' C - Added EXPXYZ - By material matching C - Fixes in expxyz, matxyz, comand, irennp @@ -77,4 +77,5 @@ c - don't mclong if adding zero bytes c - remove warning about expxyz material matching routine c - fix name length issues c - fix parsing if no nodesets on models -c - handle mirroring of tet10 models \ No newline at end of file +c - handle mirroring of tet10 models +c - correct handling of nset and sset names \ No newline at end of file diff --git a/packages/seacas/applications/gjoin/gjoin.f b/packages/seacas/applications/gjoin/gjoin.f index 64b240b060..5be91707b7 100644 --- a/packages/seacas/applications/gjoin/gjoin.f +++ b/packages/seacas/applications/gjoin/gjoin.f @@ -551,13 +551,15 @@ PROGRAM GJOIN2 CALL MDRSRV ('NNNPO', KNNNO, NEWNPS) CALL MDRSRV ('ISCR', KISCR, NEWNPS) call mdrsrv ('nodscr', kndscr, newnp) + CALL MCFIND ('NAMNS', IDUM, LNAM) + CALL MCRSRV ('NAMSCR', KNMSC, LNAM) CALL MDSTAT (NERR, MEM) IF (NERR .GT. 0) GOTO 140 CALL MUNNPS (NEWNPS, IA(KINPSS), NEWNNL, & IA(KIDNS), IA(KNNNS), IA(KIXNNS), IA(KLTNNS), A(KFACNS), & IA(KLTNNO), A(KFACNO), IA(KIXNNO), IA(KNNNO), IA(KISCR), - * IA(KNDSCR), NEWNP) + * IA(KNDSCR), C(KNMNS), C(KNMSC), NEWNP) CALL MDDEL ('LTNNPO') CALL MDDEL ('FACNPO') @@ -565,6 +567,7 @@ PROGRAM GJOIN2 CALL MDDEL ('NNNPO') CALL MDDEL ('ISCR') call mddel ('nodscr') + call mddel ('NAMSCR') C --Squeeze the nodal point sets @@ -597,6 +600,8 @@ PROGRAM GJOIN2 CALL MDRSRV ('NEESO', KNESO, NEWESS) CALL MDRSRV ('NEDS0', KNDS0, NEWESS) CALL MDRSRV ('ISCR', KISCR, NEWESS) + CALL MCFIND ('NAMSS', IDUM, LNAM) + CALL MCRSRV ('NAMSCR', KNMSC, LNAM) CALL MDSTAT (NERR, MEM) IF (NERR .GT. 0) GOTO 140 @@ -604,7 +609,8 @@ PROGRAM GJOIN2 & IA(KIDSS), IA(KNESS), IA(KNDSS), IA(KIXESS), IA(KIXDSS), & IA(KLTESS), IA(KLTSSS), A(KFACSS), & IA(KLTESO), IA(KLTSSO), A(KFACS0), IA(KIXESO), IA(KIXDS0), - & IA(KNESO), IA(KNDS0), IA(KISCR), USESDF) + & IA(KNESO), IA(KNDS0), IA(KISCR), USESDF, + $ C(KNMSS), C(KNMSC)) CALL MDDEL ('LTEESO') CALL MDDEL ('LTSSO') @@ -614,6 +620,7 @@ PROGRAM GJOIN2 CALL MDDEL ('NEESO') CALL MDDEL ('NEDS0') CALL MDDEL ('ISCR') + CALL MCDEL ('NAMSCR') CALL MDSTAT (NERR, MEM) IF (NERR .GT. 0) GOTO 140 From 796db449091054cd463f769632d0ec1036246f21 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 29 May 2024 16:30:05 -0600 Subject: [PATCH 10/59] GJOIN: Fix mcdel call --- packages/seacas/applications/gjoin/gjoin.f | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/seacas/applications/gjoin/gjoin.f b/packages/seacas/applications/gjoin/gjoin.f index 5be91707b7..e55890da6c 100644 --- a/packages/seacas/applications/gjoin/gjoin.f +++ b/packages/seacas/applications/gjoin/gjoin.f @@ -567,7 +567,7 @@ PROGRAM GJOIN2 CALL MDDEL ('NNNPO') CALL MDDEL ('ISCR') call mddel ('nodscr') - call mddel ('NAMSCR') + call mcdel ('NAMSCR') C --Squeeze the nodal point sets From e26019573803721adb8bfa2245cb4720bd4cc241 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Thu, 30 May 2024 16:20:08 -0600 Subject: [PATCH 11/59] Discontinuous galerkin (#403) * EXODUS: refactor field metadata attribute output * EXODUS: Beginnings of basis read * EXODUS: basis query * EXODUS: these got lost with some branch mishaps * EXODUS: Spelling fix * EXODUS: See if can fix intel errors * EXODUS: Fix intel build * EXODUS: Move internal function to correct header * Windows fixes: make functions non-inline Making certain functions in Ioss::Utils non-inline to avoid export errors on Windows. Accessing the non-exported static variables was causing errors. This avoids that. Also added API to access current values for various streams. * eliminate warnings * IOSS: cgns - do not create assemblies from Unspecified VC * Getting kokkos build working again [ci skip] * TEST: Enable kokkos build * IOSS: Change byte-size output to debug * shellcheck recommendations * TESTING: Remove adios2 until figure out issues * EPU: Minor improvement in message (plural vs singular) * IOSS: Rename qsort to sort * IO_MODIFY: Better help output * IO_MODIFY: clean-up comment [ci skip] * Revert "EPU: Minor improvement in message (plural vs singular)" This reverts commit 6ca490ce687c42aa2b6ca0636b6107c14b2bd595. * IOSS: Switch to using doctest from Catch2 * IOSS: cgns - remove lambdas to see if fixes stria issue * IOSS: Remove shadowed variable * Revert "Remove shadowed variable." This reverts commit 4d54381a3264dea155de65d0588cb192b37c0e1f. * Revert "IOSS: cgns - remove lambdas to see if fixes stria issue" This reverts commit 8dde87a74344b8b57f0f948e58f73e82c950e074. * IOSS: cgns - get CG_BUILD_PARALLEL define for all versions * Update readme -- replace Catch2 with doctest [ci skip] * @wortiz has signed the CLA from Pull Request #303 * @mwestphal has signed the CLA from Pull Request #304 * BLOT: Fix new argument mismatch warning with gcc-12 * Fix a recursive warning on assignement copy operator * IOSS: DEBUG->DbgOut, WARNING->WarnOut to avoid windows issues * IOSS: new version of clang-format * New version of clang-format * New version of clang-format * TPL: Add INSTALL_PATH to metis/parmetis installs * MODULE: Remove old unused unsupported lines [ci skip] * @gjtempl has signed the CLA from Pull Request #305 * Updates for serial build Add SEACAS_HAVE_MPI include guards for ioss unit_tests that use MPI. These are still run for serial builds and this change allows them to compile and run. Add -DMATIO_SHARED:BOOL=${SHARED} to TPS/matio/runcmake.sh. Matio's CMake isn't respecting the CMAKE variable BUILD_SHARED_LIBS and still builds shared version when SHARED=NO. * IOSS: Handle uppercase field suffices * IOSS: Clean up uppercase suffix fix * ZELLIJ: Add offset option * KOKKOS: Use current version * TPL: Fix kokkos runcmake.sh for current version * EXODUS: Clean up * Copy top-level README to packages/seacas so available in Trilinos snapshot * CONFIG: Update name of kokkos library * EXODUS.PY: Add the get_block_id_map method This adds functionality to add being able to easily find the elements contained within EX_ELEM_BLOCK or other type without keeping track of global and internal indicies. * ZELLIJ: Better cache utilization for offset and scale * IOSS: Handle specified field ordering (via index) if present * IOSS: use reserve instead of resize since pushing back * IO_MODIFY: Add rename capability * IO_MODIFY: Clean up rename capability * CONFIG: Add gnubrew compiler option * CONFIG: Update hdf5 versions * IO_MODIFY: update help options * EXODUS: Fix problem with ex_get_block_id_map function * SUPES: Make a variable be a correct spelling * EXODUS: clang-analyzer fixes * Bring up-to-date with master * Clean up bad merge * EXODUS: Clean up bad merge * EXODUS: Clean up anothr bad merge * EPU: Bring in fix from master * EXODUS: Clean up python and varid lookup * EXODUS: Support cardinality and user-define suffices; refactor * EXODUS: Further refinement/refactoring of DG field interface * EXODUS: Refactor component separator to allow for empty value * EXODUS: Minor cleanup * EXODUS: Refactor function naems; add test of ex_field utils * EXODUS: Add missing file from last commit * EXODUS: Fix bad handling of user-defined suffices * EXODUS: Fix suffix handling in name building * CI: Fix cmake version requirement * EXODUS: More work on the basis metadata read * EXODUS: Fix null test -- use double pipe * EXODUS: refactor field metadata attribute output * EXODUS: Beginnings of basis read * EXODUS: basis query * EXODUS: these got lost with some branch mishaps * EXODUS: Spelling fix * EXODUS: See if can fix intel errors * EXODUS: Fix intel build * EXODUS: Move internal function to correct header * TEST: Enable kokkos build * EPU: Minor improvement in message (plural vs singular) * Revert "EPU: Minor improvement in message (plural vs singular)" This reverts commit 6ca490ce687c42aa2b6ca0636b6107c14b2bd595. * IOSS: Remove shadowed variable * Revert "Remove shadowed variable." This reverts commit 4d54381a3264dea155de65d0588cb192b37c0e1f. * CONFIG: Add gnubrew compiler option * Bring up-to-date with master * EXODUS: Support cardinality and user-define suffices; refactor * EXODUS: Further refinement/refactoring of DG field interface * EXODUS: Refactor component separator to allow for empty value * EXODUS: Minor cleanup * EXODUS: Refactor function naems; add test of ex_field utils * EXODUS: Add missing file from last commit * EXODUS: Fix bad handling of user-defined suffices * EXODUS: Fix suffix handling in name building * CI: Fix cmake version requirement * EXODUS: More work on the basis metadata read * EXODUS: Fix null test -- use double pipe * EXODUS: Test field metadata read/write on nodes * CI: Fix cxx standard setting * EXODUS: Fix bad merge * EXODUS: Fix bad merge * EXODUS: Fix varid query for assembly and blob * EXODUS.PY: See if this fixes ci build issues * EXODUS: Fix ex__get_varid function * Revert "EXODUS.PY: See if this fixes ci build issues" This reverts commit 86f202dd023db72dbe8f88e091a5812f6c4407fc. * EXODUS: Clean up ex_attribute struct * EXODUS: realpath is gnu extension in c11/c99 * EXODUS: Provide realpath prototype * EXODUS: Add missing functions for mingw * EXODUS: Fix ex_attribute initiialization * CI: Disable nczarr in netcdf * EXODUS: Fix codacy null / 0 issue * EXODUS: Removed unused struct member * IOSS: Clean up some includes; remove the ... for ??? * EXODUS: Fix bad master merge * EXODUS: Fix intel compiler build * BLOT: Increase resolution of node/element id output * IOSS: Change meta_data to metadata * EXODUS: Use internal naming for static function * Remove ioss as required library * EXODUS: Add function name to output so know where it came from * IOSS: Add function mapping Ioss field names to exodus ex_field_type * IOSS: Output field metadata basic implemetation * IOSS: Rename function for consistency * IOSS: Add missing serialize io call * APREPRO: Make strings test more robust * IOSS: Build io_modify only if exodus enabled * IOSS: Detect if exodus enabled for exodus include * GREPOS: Intel overoptimizes unless inimap separate file * GREPOS: Fix bad merge * CONFIG: Silence CMake warning about python interpreter finding * IOSS: Fix use of c++14/17 type-traits * EXODUS: Filter out attributes Field@ * IOSS: Add mapping from exodus type to ioss field type * IOSS: Handle composite fields with different component separators * IOSS: Field - Add print method; support 2 separators for composite fields * IOSS: get_fields works after enhanced field found some fields * IOSS: Read/interpret exodus files with field metadata * IOSS: Eliminate some char **names uses -- convert to NameList * IOSS: Use more NameList and IntVector * CI: Remove cmake policy; not recognized in docker build * IOSS: CGNS - earlier versions have the CG_BUILD_HDF5 define in cgnsconfig.h * CI: Update to latest version [ci skip] * IOSS: Minor change to trigger workflows * IOSS: Propogate name_array changes to parallel * IOSS: Add serialize io calls wehre needed * IOSS: Use correct export macro * IOSS: Scalar fields do not need metadata; clutters file also * Io modify handle cgns (#453) * IOSS: io_modify - try to get cgns structured mesh coordinate mods working * IOSS: io_modify - fix coordinate modification routines * IOSS: io_modify - try to handle block geometry modify in cgns * IOSS: io_info -- bbox works for structured mesh * IOSS: io_modify - redo geometry transformation code --------- Co-authored-by: Greg Sjaardema * EXODUS: Fix element count in test writer * IOSS: Handle user-defined field metadata output * IOSS: Handle composite field multiple separators * CI: See if can fix appveyor build [ci skip] * IOSS: Put debug output in ifdef * CI: Actually trigger build... * CI: See if this affects appveyor... * CI: Change install name/location * EXODUS: Fix formatting of comment; turn off clang-format * IOSS: Initial steps in adding Basis capability * IOSS: Initial steps in adding Basis capability to field metadata query * CI: mkdir lib before plugin path in case doesnot exist [ci skip] * APREPRO: Fix some issues found in sierra input files * Testing an undefined variable against a string in a boolean (EQ, NE, LT, ...) was always returning true. . Changed such that an undefined variable in a boolean test with a string expression treats the undefined variable as the empty string. `{if (undefined_var == "Greg")}' Will parse as `{if ("" == "Greg")}' and output an undefined_variable warning. * The `exp()` function will output an ERANGE error for both underflow and overflow. We are ok with underflow not giving an error. Changed so only get math error in case of overflow. * EXPLORE: Add/Refactor qa, qainfo and info list commands * CI: Better error handling in script * EPU: Delete input files if requested * APREPRO: Fix to_string when fmt:: full precision output being used * APREPRO: Refactor last change * IOSS: heartbeat - add HEARTBEAT_FLUSH_INTERVAL property, document 0 will flush every step * EXODUS: Fix output of Curl gradient field * IOSS: Get field-metadata basis somewhat working * APREPRO: Bring branch up-to-date and fix bad merge * IOSS: Refactor function names for ComposedVariable * EXODUS: Move basis to global attribute * IOSS: Add quadrature storage type; beginnings of allowing multiple basis types * EXODUS: Redo so basis, quad stored at global level * IOSS: Modify tokenize to optionally return empty tokens * IOSS: Start of refactor for allowing multiple basis; basis at root of exodus * EXODUS: Allow more than one basis; field can specify which basis it is using * IOSS: Refactor basis struct/type * Iniitial support for quadrature storage type * EXODUS: Treat Quad@ attribute as internal * EXODUS: Add quadrature type and multiple basis/quad types to test read/write * IOSS: cleanup quadrature implementation * IOSS: Better const correctness * IOSS: VariableType -- function to return quad/basis types * IOSS: Support quadrature type in ioss<->exodus type mapping * IOSS: exodus - output basis/quadrature types * IOSS: Better suffix upper/lower detection * EXODUS: Support nesting of user-defiend fields * IOSS: Better compare output for fields * IOSS: Remove debug print * IOSS: Allow nesting on user-defined fields; refactor basis/quad output * EXODUS: Remove unused variable * EXODUS: Output from test program to stdout, not stderr * EXODUS: Add test for enhanced field metadata * EXODUS: Fix pipestatus values * EXODUS: Fix windows build * EXODUS: Remove shadowed variable * EXODUS: Fix enhanced fields for random attribute ordering * EXODUS: Test - more robust when nc4 changes attribute ordering * EXODUS: get_X_metadata functions have count argument added so call-to-call order works * EXODUS: Check for a null pointer * EXODUS: Provide internal strsep and strlcat * EXODUS: Improve portability * EXODUS: A better way to get strdup * EXODUS: Fix some compiler warnings; see if that makes test run... * EXODUS: Fix memory leak in test code * EXODUS: Deallocat struct in correct place * EXODUS: Reorder unit test and update output * IOSS: Add type and type_string to variable types * IOSS: Refactor detailed field output * IOSS: Allow detailed field output * IOSS: Minor cleanup/refactor * NEM_SLICE: Minor fix * IOSS: Clean up fmt includes/use * IOSS: Rewrite how catylyst deals with field names * IOSS: Fix up some changes for catalyst for this branch * IOSS: Fix default separator setting; do not recognize fields if property set * EXODUS: Refactor get/put basis and quad metadata * IOSS: Minor tweak to field printing * IOSS: Refactor variable type determination * EXODUS: Update nesting define; fix test dump file * IOSS: Simplify datapool - remove seldom used capabilty * IOSS: Refactor some names; add print * EXODUS: Eliminate memory leak * EXODUS: Fix spelling error * EXODUS: Fix compiler warning * EXODUS: Clean up some function name/doc * Spelling fixes [ci skip] * IOSS: Add backward compatible interfaces back --------- Co-authored-by: Utkarsh Ayachit Co-authored-by: Greg Sjaardema Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Mathieu Westphal Co-authored-by: Gary Templet Co-authored-by: Mario LoPrinzi --- CMakeLists.txt | 2 +- TPL/netcdf/runcmake.sh | 1 + packages/seacas/CMakeLists.txt | 1 + packages/seacas/README.md | 1 - .../seacas/applications/blot/bl_version.f | 6 +- packages/seacas/applications/blot/matfac.f | 6 +- packages/seacas/applications/blot/scaprt.f | 4 +- .../seacas/applications/grepos/gp_mapvar.f | 1 + packages/seacas/applications/nem_slice/elb.h | 2 +- .../seacas/applications/slice/SL_Decompose.C | 22 +- packages/seacas/cmake/Dependencies.cmake | 2 +- .../seacas/libraries/exodus/CMakeLists.txt | 1 + .../exodus/include/exodus-element-types.md | 2 +- .../libraries/exodus/include/exodusII.h | 129 +++- .../libraries/exodus/include/exodusII_int.h | 5 +- .../libraries/exodus/src/ex_field_utils.c | 552 +++++++++++++++++ .../libraries/exodus/src/ex_get_attribute.c | 64 +- .../exodus/src/ex_get_field_metadata.c | 568 ++++++++++++++++++ .../exodus/src/ex_put_all_var_param_ext.c | 72 +-- .../libraries/exodus/src/ex_put_attribute.c | 84 +-- .../exodus/src/ex_put_field_metadata.c | 292 +++++++++ .../seacas/libraries/exodus/src/ex_utils.c | 85 ++- .../libraries/exodus/test/CMakeLists.txt | 12 + .../exodus/test/test-add-assembly.dmp | 1 - .../libraries/exodus/test/test-field-utils.c | 74 +++ .../seacas/libraries/exodus/test/testall.in | 105 ++-- .../exodus/test/testrd-field-metadata.c | 318 ++++++++++ .../exodus/test/testrd-field-metadata.dmp | 125 ++++ .../libraries/exodus/test/testwt-assembly.dmp | 1 - .../libraries/exodus/test/testwt-blob.c | 2 +- .../libraries/exodus/test/testwt-blob.dmp | 1 - .../exodus/test/testwt-field-metadata.c | 272 +++++++++ .../exodus/test/testwt-field-metadata.dmp | 56 ++ .../ioss/src/Ioss_BasisVariableType.h | 97 +++ .../libraries/ioss/src/Ioss_ChainGenerator.C | 15 +- .../seacas/libraries/ioss/src/Ioss_Compare.C | 10 +- .../ioss/src/Ioss_ComposedVariableType.C | 71 +++ .../ioss/src/Ioss_ComposedVariableType.h | 40 ++ .../ioss/src/Ioss_CompositeVariableType.C | 9 +- .../ioss/src/Ioss_CompositeVariableType.h | 14 +- .../ioss/src/Ioss_ConcreteVariableType.C | 7 +- .../ioss/src/Ioss_ConcreteVariableType.h | 17 +- .../ioss/src/Ioss_ConstructedVariableType.h | 4 + .../libraries/ioss/src/Ioss_CopyDatabase.C | 42 +- .../seacas/libraries/ioss/src/Ioss_DataPool.h | 8 +- .../libraries/ioss/src/Ioss_DatabaseIO.C | 27 +- .../libraries/ioss/src/Ioss_DatabaseIO.h | 41 +- .../libraries/ioss/src/Ioss_Decomposition.C | 38 +- .../libraries/ioss/src/Ioss_Decomposition.h | 2 +- .../seacas/libraries/ioss/src/Ioss_EdgeSet.C | 4 +- .../seacas/libraries/ioss/src/Ioss_EdgeSet.h | 2 +- .../libraries/ioss/src/Ioss_ElementBlock.C | 6 +- .../libraries/ioss/src/Ioss_ElementBlock.h | 4 +- .../libraries/ioss/src/Ioss_ElementSet.C | 2 +- .../libraries/ioss/src/Ioss_ElementSet.h | 2 +- .../libraries/ioss/src/Ioss_ElementTopology.C | 12 +- .../ioss/src/Ioss_ElementVariableType.h | 5 +- .../seacas/libraries/ioss/src/Ioss_FaceSet.C | 2 +- .../seacas/libraries/ioss/src/Ioss_FaceSet.h | 2 +- .../seacas/libraries/ioss/src/Ioss_Field.C | 82 ++- .../seacas/libraries/ioss/src/Ioss_Field.h | 32 +- .../libraries/ioss/src/Ioss_GroupingEntity.C | 14 +- .../libraries/ioss/src/Ioss_GroupingEntity.h | 2 +- .../libraries/ioss/src/Ioss_IOFactory.h | 2 +- .../ioss/src/Ioss_NamedSuffixVariableType.h | 7 +- .../libraries/ioss/src/Ioss_ParallelUtils.C | 4 +- .../ioss/src/Ioss_QuadratureVariableType.h | 82 +++ .../seacas/libraries/ioss/src/Ioss_Region.C | 2 +- .../seacas/libraries/ioss/src/Ioss_Region.h | 17 +- .../libraries/ioss/src/Ioss_SideBlock.C | 2 +- .../libraries/ioss/src/Ioss_SideBlock.h | 6 +- .../seacas/libraries/ioss/src/Ioss_SideSet.C | 10 +- .../seacas/libraries/ioss/src/Ioss_SideSet.h | 8 +- .../seacas/libraries/ioss/src/Ioss_Utils.C | 175 +++--- .../seacas/libraries/ioss/src/Ioss_Utils.h | 13 +- .../libraries/ioss/src/Ioss_VariableType.C | 159 ++++- .../libraries/ioss/src/Ioss_VariableType.h | 50 +- .../src/catalyst/Iocatalyst_CatalystManager.C | 8 +- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.C | 117 ++-- .../ioss/src/catalyst/Iocatalyst_IOFactory.C | 6 +- .../ioss/src/cgns/Iocgns_DatabaseIO.C | 10 +- .../ioss/src/cgns/Iocgns_IOFactory.C | 2 +- .../ioss/src/cgns/Iocgns_ParallelDatabaseIO.C | 2 +- .../libraries/ioss/src/cgns/Iocgns_Utils.C | 24 +- .../libraries/ioss/src/cgns/Iocgns_Utils.h | 2 +- .../ioss/src/exodus/Ioex_BaseDatabaseIO.C | 545 ++++++++++++++--- .../ioss/src/exodus/Ioex_BaseDatabaseIO.h | 12 +- .../ioss/src/exodus/Ioex_DatabaseIO.C | 17 +- .../ioss/src/exodus/Ioex_DecompositionData.C | 32 +- .../ioss/src/exodus/Ioex_DecompositionData.h | 8 +- .../ioss/src/exodus/Ioex_ParallelDatabaseIO.C | 19 +- .../libraries/ioss/src/exodus/Ioex_Utils.C | 241 +++++++- .../libraries/ioss/src/exodus/Ioex_Utils.h | 13 + .../ioss/src/exonull/Ioexnl_BaseDatabaseIO.C | 103 +--- .../ioss/src/exonull/Ioexnl_BaseDatabaseIO.h | 8 +- .../ioss/src/exonull/Ioexnl_DatabaseIO.C | 4 +- .../src/exonull/Ioexnl_DecompositionData.C | 18 +- .../src/exonull/Ioexnl_ParallelDatabaseIO.C | 4 +- .../ioss/src/gen_struc/Iogs_DatabaseIO.C | 2 +- .../ioss/src/gen_struc/Iogs_DatabaseIO.h | 9 +- .../ioss/src/gen_struc/Iogs_GeneratedMesh.C | 6 +- .../ioss/src/gen_struc/Iogs_GeneratedMesh.h | 4 +- .../ioss/src/generated/Iogn_DashSurfaceMesh.C | 2 +- .../ioss/src/generated/Iogn_DashSurfaceMesh.h | 11 +- .../ioss/src/generated/Iogn_DatabaseIO.C | 2 +- .../ioss/src/generated/Iogn_DatabaseIO.h | 6 +- .../ioss/src/generated/Iogn_GeneratedMesh.C | 6 +- .../ioss/src/generated/Iogn_GeneratedMesh.h | 4 +- .../libraries/ioss/src/main/cgns_decomp.C | 4 +- .../libraries/ioss/src/main/info_interface.C | 4 + .../libraries/ioss/src/main/info_interface.h | 2 + .../seacas/libraries/ioss/src/main/io_info.C | 90 +-- .../libraries/ioss/src/main/io_modify.C | 53 +- .../seacas/libraries/ioss/src/main/io_shell.C | 2 +- .../libraries/ioss/src/main/io_shell_ts.C | 78 +-- .../libraries/ioss/src/main/shell_interface.C | 7 +- .../seacas/libraries/ioss/src/main/skinner.C | 31 +- .../ioss/src/pamgen/Iopg_DatabaseIO.C | 4 +- .../ioss/src/pamgen/Iopg_DatabaseIO.h | 4 +- packages/seacas/libraries/ioss/src/tokenize.C | 4 +- packages/seacas/libraries/ioss/src/tokenize.h | 6 +- .../ioss/src/utest/Utst_structured_decomp.C | 2 +- .../visualization/cgns/Iovs_cgns_IOFactory.h | 2 +- .../exodus/Iovs_exodus_IOFactory.h | 2 +- signatures/version1/cla.json | 2 +- 125 files changed, 4475 insertions(+), 1088 deletions(-) create mode 100644 packages/seacas/libraries/exodus/src/ex_field_utils.c create mode 100644 packages/seacas/libraries/exodus/src/ex_get_field_metadata.c create mode 100644 packages/seacas/libraries/exodus/src/ex_put_field_metadata.c create mode 100644 packages/seacas/libraries/exodus/test/test-field-utils.c create mode 100644 packages/seacas/libraries/exodus/test/testrd-field-metadata.c create mode 100644 packages/seacas/libraries/exodus/test/testrd-field-metadata.dmp create mode 100644 packages/seacas/libraries/exodus/test/testwt-field-metadata.c create mode 100644 packages/seacas/libraries/exodus/test/testwt-field-metadata.dmp create mode 100644 packages/seacas/libraries/ioss/src/Ioss_BasisVariableType.h create mode 100644 packages/seacas/libraries/ioss/src/Ioss_ComposedVariableType.C create mode 100644 packages/seacas/libraries/ioss/src/Ioss_ComposedVariableType.h create mode 100644 packages/seacas/libraries/ioss/src/Ioss_QuadratureVariableType.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d36809819..3ea27aacdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ SET(Seacas_ENABLE_Zoltan_DEFAULT ON) SET(TRIBITS_HIDE_DEPRECATED_INCLUDE_DIRECTORIES_OVERRIDE TRUE) # Define the TriBITS minimum required CMake version -SET(TRIBITS_CMAKE_MINIMUM_REQUIRED 3.17.0) +SET(TRIBITS_CMAKE_MINIMUM_REQUIRED 3.22.0) # CMake requires this be in the top file and not in an include file :-( CMAKE_MINIMUM_REQUIRED(VERSION ${TRIBITS_CMAKE_MINIMUM_REQUIRED} FATAL_ERROR) diff --git a/TPL/netcdf/runcmake.sh b/TPL/netcdf/runcmake.sh index 3b37d34a81..22a89b18c1 100755 --- a/TPL/netcdf/runcmake.sh +++ b/TPL/netcdf/runcmake.sh @@ -67,6 +67,7 @@ cmake .. -DCMAKE_C_COMPILER:FILEPATH=${CC} \ -DENABLE_MMAP:BOOL=ON \ -DENABLE_DAP:BOOL=OFF \ -DENABLE_BYTERANGE:BOOL=OFF \ + -DENABLE_NCZARR:BOOL=OFF \ -DENABLE_V2_API:BOOL=OFF \ -DENABLE_FILTER_TESTING:BOOL=OFF \ -DENABLE_TESTS:BOOL=OFF \ diff --git a/packages/seacas/CMakeLists.txt b/packages/seacas/CMakeLists.txt index b9eb3ab2be..81ca71b50e 100644 --- a/packages/seacas/CMakeLists.txt +++ b/packages/seacas/CMakeLists.txt @@ -18,6 +18,7 @@ TRIBITS_ADD_OPTION_AND_DEFINE( set(${PROJECT_NAME}_C_Standard_DEFAULT c99) SET(${PROJECT_NAME}_CMAKE_CXX_STANDARD_DEFAULT 17) +SET(CMAKE_CXX_STANDARD 17) SET(${PROJECT_NAME}_CMAKE_CXX_STANDARDS_ALLOWED "(14|17|20)") ADVANCED_SET(CMAKE_CXX_STANDARD ${${PROJECT_NAME}_CMAKE_CXX_STANDARD_DEFAULT} CACHE STRING diff --git a/packages/seacas/README.md b/packages/seacas/README.md index 96d53e54ce..3ec3a1142c 100644 --- a/packages/seacas/README.md +++ b/packages/seacas/README.md @@ -13,4 +13,3 @@ Sandia National Laboratories, We appreciate feedback from users of this package. Please send comments, suggestions, and bug reports to Greg Sjaardema , - diff --git a/packages/seacas/applications/blot/bl_version.f b/packages/seacas/applications/blot/bl_version.f index 727c9050c2..47529f19d8 100644 --- a/packages/seacas/applications/blot/bl_version.f +++ b/packages/seacas/applications/blot/bl_version.f @@ -1,4 +1,4 @@ -C Copyright(C) 1999-2020 National Technology & Engineering Solutions +C Copyright(C) 1999-2020, 2024 National Technology & Engineering Solutions C of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with C NTESS, the U.S. Government retains certain rights in this software. C @@ -11,8 +11,8 @@ SUBROUTINE VERSION(QAINFO) CHARACTER*(MXQARC) QAINFO(6) QAINFO(1) = 'blot ' - QAINFO(2) = '2021/03/31 ' - QAINFO(3) = ' 3.141 ' + QAINFO(2) = '2024/03/25 ' + QAINFO(3) = ' 3.1415 ' QAINFO(4) = ' ' RETURN diff --git a/packages/seacas/applications/blot/matfac.f b/packages/seacas/applications/blot/matfac.f index 702e94d4ba..a847bb88e3 100644 --- a/packages/seacas/applications/blot/matfac.f +++ b/packages/seacas/applications/blot/matfac.f @@ -1,4 +1,4 @@ -C Copyright(C) 1999-2020 National Technology & Engineering Solutions +C Copyright(C) 1999-2020, 2024 National Technology & Engineering Solutions C of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with C NTESS, the U.S. Government retains certain rights in this software. C @@ -101,6 +101,6 @@ INTEGER FUNCTION MATFAC (LINKF1, MAXNPF, NPFS, iel, numnp, IERR) 160 CONTINUE RETURN -10000 FORMAT (' Poss. Contiguity Prob. at Hex ', I7, - * ', Nodes ', 4(I7,1X), 10(I7,1X)) +10000 FORMAT (' Poss. Contiguity Prob. at Hex ', I11, + * ', Nodes ', 4(I11,1X), 10(I11,1X)) END diff --git a/packages/seacas/applications/blot/scaprt.f b/packages/seacas/applications/blot/scaprt.f index faddcf539a..20de2989a7 100644 --- a/packages/seacas/applications/blot/scaprt.f +++ b/packages/seacas/applications/blot/scaprt.f @@ -1,4 +1,4 @@ -C Copyright(C) 1999-2020 National Technology & Engineering Solutions +C Copyright(C) 1999-2020, 2024 National Technology & Engineering Solutions C of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with C NTESS, the U.S. Government retains certain rights in this software. C @@ -94,7 +94,7 @@ SUBROUTINE SCAPRT (NAMECO, NAME, IVAR, TIMES, & STIM(2)(:LTIM), NUMMAX, ISTMAX 10000 FORMAT (4X, A7, 1X, A, 3X, 2 (1X, A), & 4X, A, 3X, A4, 3X, A4) -10010 FORMAT (4X, A7, 1X, A, 3X, 2 (1X, A), 4X, A, I7, I7) +10010 FORMAT (4X, A7, 1X, A, 3X, 2 (1X, A), 4X, A, I11, I11) ELSE WRITE (*, 10020) 'Range: ', SVAL(0)(:LVAL), & SXYZ0(1)(:LX), SXYZ0(2)(:LY), SXYZ0(3)(:LZ), diff --git a/packages/seacas/applications/grepos/gp_mapvar.f b/packages/seacas/applications/grepos/gp_mapvar.f index d55dd4d391..49858aec65 100644 --- a/packages/seacas/applications/grepos/gp_mapvar.f +++ b/packages/seacas/applications/grepos/gp_mapvar.f @@ -49,6 +49,7 @@ subroutine mapvar(nold, nnew, nvar, map, vars, scr) do 30 ivar = 1, nvar do 10 i = 1, nnew + write (*,*) i, map(i) scr(i) = vars(map(i) + nold * (ivar-1) ) 10 continue diff --git a/packages/seacas/applications/nem_slice/elb.h b/packages/seacas/applications/nem_slice/elb.h index df37f03f08..815c873e65 100644 --- a/packages/seacas/applications/nem_slice/elb.h +++ b/packages/seacas/applications/nem_slice/elb.h @@ -211,7 +211,7 @@ template struct Graph_Description std::vector adj{}; std::vector start{}; std::vector> sur_elem; - Graph_Description() = default; + Graph_Description() = default; }; /* Various constants */ diff --git a/packages/seacas/applications/slice/SL_Decompose.C b/packages/seacas/applications/slice/SL_Decompose.C index f7c872a5bd..cb27614147 100644 --- a/packages/seacas/applications/slice/SL_Decompose.C +++ b/packages/seacas/applications/slice/SL_Decompose.C @@ -45,6 +45,24 @@ extern double seacas_timer(); extern void progress(const std::string &output); namespace { + char **get_name_array(size_t count, int size) + { + auto *names = new char *[count]; + for (size_t i = 0; i < count; i++) { + names[i] = new char[size + 1]; + std::memset(names[i], '\0', size + 1); + } + return names; + } + + void delete_name_array(char **names, int count) + { + for (int i = 0; i < count; i++) { + delete[] names[i]; + } + delete[] names; + } + template void create_adjacency_list(const Ioss::Region ®ion, std::vector &pointer, std::vector &adjacency, INT) @@ -684,7 +702,7 @@ std::vector decompose_elements(const Ioss::Region ®ion, SystemInterface if (map_count > 0) { int max_name_length = ex_inquire_int(exoid, EX_INQ_DB_MAX_USED_NAME_LENGTH); max_name_length = max_name_length < 32 ? 32 : max_name_length; - char **names = Ioss::Utils::get_name_array(map_count, max_name_length); + char **names = get_name_array(map_count, max_name_length); int error = ex_get_names(exoid, EX_ELEM_MAP, names); if (error < 0) { exodus_error(__LINE__); @@ -701,7 +719,7 @@ std::vector decompose_elements(const Ioss::Region ®ion, SystemInterface break; } } - Ioss::Utils::delete_name_array(names, map_count); + delete_name_array(names, map_count); } if (!map_read) { diff --git a/packages/seacas/cmake/Dependencies.cmake b/packages/seacas/cmake/Dependencies.cmake index 3b1b3d6598..e75f5073f2 100644 --- a/packages/seacas/cmake/Dependencies.cmake +++ b/packages/seacas/cmake/Dependencies.cmake @@ -4,7 +4,7 @@ TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( Exodus_for libraries/exodus_for PT OPTIONAL ExoIIv2for32 libraries/exoIIv2for32 PT OPTIONAL Nemesis libraries/nemesis PT OPTIONAL - Ioss libraries/ioss PT REQUIRED + Ioss libraries/ioss PT OPTIONAL Chaco libraries/chaco PT OPTIONAL Aprepro_lib libraries/aprepro_lib PT OPTIONAL Supes libraries/supes PT OPTIONAL diff --git a/packages/seacas/libraries/exodus/CMakeLists.txt b/packages/seacas/libraries/exodus/CMakeLists.txt index 81ea023c27..d2be5f9fa8 100644 --- a/packages/seacas/libraries/exodus/CMakeLists.txt +++ b/packages/seacas/libraries/exodus/CMakeLists.txt @@ -35,6 +35,7 @@ TRIBITS_ADD_LIBRARY( ) set_property(TARGET exodus PROPERTY C_STANDARD 99) +set_property(TARGET exodus PROPERTY C_EXTENSIONS ON) if (SEACASExodus_ENABLE_SHARED) if (NOT BUILD_SHARED_LIBS) diff --git a/packages/seacas/libraries/exodus/include/exodus-element-types.md b/packages/seacas/libraries/exodus/include/exodus-element-types.md index 3eed7f1ea4..d90e7b05fa 100644 --- a/packages/seacas/libraries/exodus/include/exodus-element-types.md +++ b/packages/seacas/libraries/exodus/include/exodus-element-types.md @@ -78,7 +78,7 @@ library, the IOSS library, nem_slice, and nem_spread. | | 64 | X | X | | | Only the base topology name is required for the element type. For example, all -elements of a `hex` toplogy can be referred to by the name `hex` no +elements of a `hex` topology can be referred to by the name `hex` no matter how many nodes there are. However, it is also correct to append the node count to the topology type for the element name. For example, a 20-node hex could be named `hex20`. diff --git a/packages/seacas/libraries/exodus/include/exodusII.h b/packages/seacas/libraries/exodus/include/exodusII.h index 1377cf5e3e..67165d25c8 100644 --- a/packages/seacas/libraries/exodus/include/exodusII.h +++ b/packages/seacas/libraries/exodus/include/exodusII.h @@ -47,6 +47,9 @@ #include #include +/** Maximum length of name permitted by NetCDF */ +#define EX_MAX_NAME NC_MAX_NAME + #ifndef NC_INT64 #error "NetCDF version 4.1.2 or later is required." #endif @@ -282,6 +285,98 @@ enum ex_entity_type { }; typedef enum ex_entity_type ex_entity_type; +enum ex_field_type { + EX_FIELD_TYPE_INVALID = 0, + EX_FIELD_TYPE_USER_DEFINED, + EX_FIELD_TYPE_SEQUENCE, + EX_BASIS, + EX_QUADRATURE, + EX_SCALAR, + EX_VECTOR_1D, + EX_VECTOR_2D, + EX_VECTOR_3D, + EX_QUATERNION_2D, + EX_QUATERNION_3D, + EX_FULL_TENSOR_36, + EX_FULL_TENSOR_32, + EX_FULL_TENSOR_22, + EX_FULL_TENSOR_16, + EX_FULL_TENSOR_12, + EX_SYM_TENSOR_33, + EX_SYM_TENSOR_31, + EX_SYM_TENSOR_21, + EX_SYM_TENSOR_13, + EX_SYM_TENSOR_11, + EX_SYM_TENSOR_10, + EX_ASYM_TENSOR_03, + EX_ASYM_TENSOR_02, + EX_ASYM_TENSOR_01, + EX_MATRIX_2X2, + EX_MATRIX_3X3 +}; +typedef enum ex_field_type ex_field_type; + +#define EX_MAX_FIELD_NESTING 2 +typedef struct ex_field +{ + ex_entity_type entity_type; + int64_t entity_id; + char name[EX_MAX_NAME + 1]; /* Name of the field */ + /* + * For basis, user, quadrature -- what is name of the subtype. This + * is a comma-separated list of `nesting` names Use two consecutive + * commas for an empty type_name. Leave empty if no type_names + */ + int nesting; /* Number of composite fields (vector at each quadrature point = 2) */ + char type_name[EX_MAX_NAME + 1]; + ex_field_type type[EX_MAX_FIELD_NESTING]; /* ex_field_type of each nested field */ + int cardinality[EX_MAX_FIELD_NESTING]; /* 0 to calculate based on type */ + char component_separator[EX_MAX_FIELD_NESTING]; /* empty defaults to '_'; */ + char suffices[EX_MAX_NAME + 1]; /* Optional comma-separated list of suffices if type is + EX_FIELD_TYPE_USER_DEFINED */ +} ex_field; + +typedef struct ex_basis +{ + /* + clang-format off + * + * subc_dim: dimension of the subcell associated with the specified DoF ordinal + * -- 0 node, 1 edge, 2 face, 3 volume [Range: 0..3] + * subc_ordinal: ordinal of the subcell relative to its parent cell + * -- 0..n for each ordinal with the same subc dim [Range: <= DoF ordinal] + * subc_dof_ordinal: ordinal of the DoF relative to the subcell + * subc_num_dof: cardinality of the DoF set associated with this subcell. + * xi, eta, mu (ξ, η, ζ): Parametric coordinate location of the DoF + * -- (Only first ndim values are valid) + * + clang-format on + */ + + char name[EX_MAX_NAME + 1]; + int cardinality; /* number of `basis` points == dimension of non-null subc_*, xi, eta, mu */ + int *subc_dim; + int *subc_ordinal; + int *subc_dof_ordinal; + int *subc_num_dof; + double *xi; + double *eta; + double *zeta; +} ex_basis; + +typedef struct ex_quadrature +{ + char name[EX_MAX_NAME + 1]; + int cardinality; /* Number of quadrature points */ + int dimension; /* 1,2,3 -- spatial dimension of points */ + double *xi; /* xi (x) coordinate of points; dimension = cardinality or NULL */ + double * + eta; /* eta (y) coordinate of points; dimension = cardinality if dimension = 2 or 3 or NULL */ + double + *zeta; /* zeta (z) coordinate of points; dimension = cardinality if dimension == 3. or NULL */ + double *weight; /* weights for each point; dimension = cardinality or NULL */ +} ex_quadrature; + /*! * ex_opts() function codes - codes are OR'ed into exopts */ @@ -305,9 +400,6 @@ typedef enum ex_options ex_options; * @{ */ -/** Maximum length of name permitted by NetCDF */ -#define EX_MAX_NAME NC_MAX_NAME - /** Maximum length of QA record, element type name */ #define MAX_STR_LENGTH 32L /** Default maximum length of an entity name, attribute name, variable name. @@ -368,10 +460,10 @@ typedef enum ex_type ex_type; typedef struct ex_attribute { ex_entity_type entity_type; - ex_entity_id entity_id; - char name[NC_MAX_NAME + 1]; + int64_t entity_id; + char name[EX_MAX_NAME + 1]; ex_type type; /* int, double, text */ - size_t value_count; + int value_count; void *values; /* not accessed if NULL */ } ex_attribute; @@ -953,6 +1045,20 @@ EXODUS_EXPORT int ex_get_blob(int exoid, struct ex_blob *blob); EXODUS_EXPORT int ex_put_blobs(int exoid, size_t count, const struct ex_blob *blobs); EXODUS_EXPORT int ex_get_blobs(int exoid, struct ex_blob *blobs); +EXODUS_EXPORT int ex_put_field_metadata(int exoid, const ex_field field); +EXODUS_EXPORT int ex_put_field_suffices(int exoid, const ex_field field, const char *suffices); +EXODUS_EXPORT int ex_get_field_metadata(int exoid, ex_field *field); +EXODUS_EXPORT int ex_get_field_metadata_count(int exoid, ex_entity_type obj_type, ex_entity_id id); +EXODUS_EXPORT int ex_get_field_suffices(int exoid, const ex_field field, char *suffices); + +EXODUS_EXPORT int ex_get_basis_count(int exoid); +EXODUS_EXPORT int ex_get_basis(int exoid, ex_basis **pbasis, int *num_basis); +EXODUS_EXPORT int ex_put_basis(int exoid, const ex_basis basis); + +EXODUS_EXPORT int ex_get_quadrature_count(int exoid); +EXODUS_EXPORT int ex_get_quadrature(int exoid, ex_quadrature **pquad, int *num_quad); +EXODUS_EXPORT int ex_put_quadrature(int exoid, const ex_quadrature quad); + /* Write arbitrary integer, double, or text attributes on an entity */ EXODUS_EXPORT int ex_put_attribute(int exoid, const ex_attribute attributes); EXODUS_EXPORT int ex_put_attributes(int exoid, size_t attr_count, const ex_attribute *attributes); @@ -1202,6 +1308,17 @@ EXODUS_EXPORT int ex_put_elem_cmap(int exoid, /**< NetCDF/Exodus int processor /**< This processor ID */ ); +EXODUS_EXPORT int ex_initialize_basis_struct(ex_basis *basis, size_t num_basis, int mode); +EXODUS_EXPORT int ex_initialize_quadrature_struct(ex_quadrature *quad, size_t num_quad, int mode); + +EXODUS_EXPORT const char *ex_component_field_name(ex_field *field, + int component[EX_MAX_FIELD_NESTING]); +EXODUS_EXPORT const char *ex_field_component_suffix(ex_field *field, int nest_level, int component); +EXODUS_EXPORT int ex_field_cardinality(const ex_field_type field_type); +EXODUS_EXPORT const char *ex_field_type_name(const ex_field_type field_type); +EXODUS_EXPORT ex_field_type ex_string_to_field_type_enum(const char *field_name); +EXODUS_EXPORT const char *ex_field_type_enum_to_string(const ex_field_type field_type); + /*! @} */ /* Deprecated Code Handling Options: diff --git a/packages/seacas/libraries/exodus/include/exodusII_int.h b/packages/seacas/libraries/exodus/include/exodusII_int.h index 8cf09bf374..8657fa6755 100644 --- a/packages/seacas/libraries/exodus/include/exodusII_int.h +++ b/packages/seacas/libraries/exodus/include/exodusII_int.h @@ -1,6 +1,6 @@ /* - * Copyright(C) 1999-2020, 2022, 2023 National Technology & Engineering Solutions + * Copyright(C) 1999-2020, 2022, 2023, 2024 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. * @@ -88,6 +88,9 @@ extern "C" { #define EX_FILE_ID_MASK (0xffff0000) /**< Must match FILE_ID_MASK in NetCDF nc4internal.h */ #define EX_GRP_ID_MASK (0x0000ffff) /**< Must match GRP_ID_MASK in NetCDF nc4internal.h */ +/* Utility function to find variable to store entity attribute on */ +int exi_get_varid(int exoid, ex_entity_type obj_type, ex_entity_id id); + void exi_reset_error_status(void); #if defined(EXODUS_THREADSAFE) diff --git a/packages/seacas/libraries/exodus/src/ex_field_utils.c b/packages/seacas/libraries/exodus/src/ex_field_utils.c new file mode 100644 index 0000000000..cda2985cef --- /dev/null +++ b/packages/seacas/libraries/exodus/src/ex_field_utils.c @@ -0,0 +1,552 @@ +/* + * Copyright(C) 1999-2024 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 "exodusII.h" // for ex_err, etc +#include "exodusII_int.h" // for EX_FATAL, etc +#include +#include +#define _GNU_SOURCE +#include + +#define SIZE(X) sizeof(X) / sizeof(X[0]) + +char *my_strsep(char **stringp, const char *delim) +{ + char *rv = *stringp; + if (rv) { + *stringp += strcspn(*stringp, delim); + if (**stringp) + *(*stringp)++ = '\0'; + else + *stringp = NULL; + } + return rv; +} +size_t my_strlcat(char *restrict dst, const char *restrict src, size_t maxlen) +{ + const size_t srclen = strlen(src); + const size_t dstlen = strlen(dst); + if (dstlen == maxlen) + return maxlen + srclen; + if (srclen < maxlen - dstlen) { + memcpy(dst + dstlen, src, srclen + 1); + } + else { + memcpy(dst + dstlen, src, maxlen - 1); + dst[dstlen + maxlen - 1] = '\0'; + } + return dstlen + srclen; +} + +static int number_width(const size_t number) +{ + if (number == 0) { + return 1; + } + int width = (int)floor(log10(number)) + 1; + return width; +} + +static void verify_valid_component(int component, size_t cardinality, size_t suffix_size) +{ + assert(cardinality == suffix_size); + assert(component - 1 < suffix_size); +} + +const char *ex_component_field_name(ex_field *field, int component[EX_MAX_FIELD_NESTING]) +{ + // NOTE: This is not thread-safe. Return value is pointer to static `field_name` + // For thread-safety, it is up to calling code. + + // Return the name of the field corresponding to the specified 1-based component(s) + static char field_name[EX_MAX_NAME + 1]; + const char *suffices[EX_MAX_FIELD_NESTING] = {NULL}; + for (int i = 0; i < field->nesting; i++) { + suffices[i] = ex_field_component_suffix(field, i, component[i]); + } + + // Build up name incrementally which makes it easier to handle an empty component_separator... + sprintf(field_name, "%s", field->name); + + for (int i = 0; i < field->nesting; i++) { + if (field->component_separator[i]) { + size_t fnl = strlen(field_name); + field_name[fnl] = field->component_separator[i]; + field_name[fnl + 1] = '\0'; + } + my_strlcat(field_name, suffices[i], EX_MAX_NAME); + } + return field_name; +} + +int ex_initialize_basis_struct(ex_basis *basis, size_t num_basis, int mode) +{ + // Mode - 0 -- initialize struct to empty + // Mode > 0 -- allocate memory for dynamically sized fields. + // Mode < 0 -- deallocate memory for dynamically sized fields. + if (mode > 0) { + for (size_t i = 0; i < num_basis; i++) { + basis[i].subc_dim = calloc(basis[i].cardinality, sizeof(int)); + basis[i].subc_ordinal = calloc(basis[i].cardinality, sizeof(int)); + basis[i].subc_dof_ordinal = calloc(basis[i].cardinality, sizeof(int)); + basis[i].subc_num_dof = calloc(basis[i].cardinality, sizeof(int)); + basis[i].xi = calloc(basis[i].cardinality, sizeof(double)); + basis[i].eta = calloc(basis[i].cardinality, sizeof(double)); + basis[i].zeta = calloc(basis[i].cardinality, sizeof(double)); + if (basis[i].subc_dim == NULL || basis[i].subc_ordinal == NULL || + basis[i].subc_dof_ordinal == NULL || basis[i].subc_num_dof == NULL || + basis[i].xi == NULL || basis[i].eta == NULL || basis[i].zeta == NULL) { + return EX_FATAL; + } + } + } + if (mode < 0) { + for (size_t i = 0; i < num_basis; i++) { + free(basis[i].subc_dim); + free(basis[i].subc_ordinal); + free(basis[i].subc_dof_ordinal); + free(basis[i].subc_num_dof); + free(basis[i].xi); + free(basis[i].eta); + free(basis[i].zeta); + } + } + /* Fall through if `cardinality < 0` */ + if (mode <= 0) { + for (size_t i = 0; i < num_basis; i++) { + basis[i].name[0] = '\0'; + basis[i].cardinality = 0; + basis[i].subc_dim = NULL; + basis[i].subc_ordinal = NULL; + basis[i].subc_dof_ordinal = NULL; + basis[i].subc_num_dof = NULL; + basis[i].xi = NULL; + basis[i].eta = NULL; + basis[i].zeta = NULL; + } + } + return EX_NOERR; +} + +int ex_initialize_quadrature_struct(ex_quadrature *quad, size_t num_quad, int mode) +{ + // Mode - 0 -- initialize struct to empty + // Mode > 0 -- allocate memory for dynamically sized fields. + // Mode < 0 -- deallocate memory for dynamically sized fields. + if (mode > 0) { + for (size_t i = 0; i < num_quad; i++) { + quad[i].xi = calloc(quad[i].cardinality, sizeof(double)); + quad[i].eta = calloc(quad[i].cardinality, sizeof(double)); + quad[i].zeta = calloc(quad[i].cardinality, sizeof(double)); + quad[i].weight = calloc(quad[i].cardinality, sizeof(double)); + if (quad[i].xi == NULL || quad[i].eta == NULL || quad[i].zeta == NULL || + quad[i].weight == NULL) { + return EX_FATAL; + } + } + } + if (mode < 0) { + for (size_t i = 0; i < num_quad; i++) { + free(quad[i].xi); + free(quad[i].eta); + free(quad[i].zeta); + free(quad[i].weight); + } + } + if (mode <= 0) { + for (size_t i = 0; i < num_quad; i++) { + quad[i].name[0] = '\0'; + quad[i].cardinality = 0; + quad[i].xi = NULL; + quad[i].eta = NULL; + quad[i].zeta = NULL; + quad[i].weight = NULL; + } + } + return EX_NOERR; +} + +const char *ex_field_component_suffix(ex_field *field, int nest_level, int component) +{ +#define X "X" +#define Y "Y" +#define Z "Z" +#define Q "Q" +#define S "S" + +#define XX "XX" +#define YY "YY" +#define ZZ "ZZ" +#define XY "XY" +#define YZ "YZ" +#define ZX "ZC" +#define YX "YX" +#define ZY "ZY" +#define XZ "XZ" + + switch (field->type[nest_level]) { + case EX_VECTOR_1D: { + static const char *suffix[] = {X}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_VECTOR_2D: { + static const char *suffix[] = {X, Y}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_VECTOR_3D: { + static const char *suffix[] = {X, Y, Z}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + + case EX_QUATERNION_2D: { + static const char *suffix[] = {S, Q}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_QUATERNION_3D: { + static const char *suffix[] = {X, Y, Z, Q}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + + case EX_FULL_TENSOR_12: { + static const char *suffix[] = {XX, XY, YX}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_FULL_TENSOR_16: { + static const char *suffix[] = {XX, XY, YZ, ZX, YX, ZY, XZ}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_FULL_TENSOR_22: { + static const char *suffix[] = {XX, YY, XY, YX}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_FULL_TENSOR_32: { + static const char *suffix[] = {XX, YY, ZZ, XY, YX}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_FULL_TENSOR_36: { + static const char *suffix[] = {XX, YY, ZZ, XY, YZ, ZX, YX, ZY, XZ}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + + case EX_SYM_TENSOR_10: { + static const char *suffix[] = {XX}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_SYM_TENSOR_11: { + static const char *suffix[] = {XX, XY}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_SYM_TENSOR_13: { + static const char *suffix[] = {XX, XY, YZ, ZX}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_SYM_TENSOR_21: { + static const char *suffix[] = {XX, YY, XY}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_SYM_TENSOR_31: { + static const char *suffix[] = {XX, YY, ZZ, XY}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_SYM_TENSOR_33: { + static const char *suffix[] = {XX, YY, ZZ, XY, YZ, ZX}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + + case EX_ASYM_TENSOR_01: { + static const char *suffix[] = {XY}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_ASYM_TENSOR_02: { + static const char *suffix[] = {XY, YZ}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_ASYM_TENSOR_03: { + static const char *suffix[] = {XY, YZ, ZX}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + + case EX_MATRIX_2X2: { + static const char *suffix[] = {"11", "12", "21", "22"}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + case EX_MATRIX_3X3: { + static const char *suffix[] = {"11", "12", "13", "21", "22", "23", "31", "32", "33"}; + verify_valid_component(component, ex_field_cardinality(field->type[nest_level]), SIZE(suffix)); + return suffix[component - 1]; + } + + case EX_FIELD_TYPE_USER_DEFINED: { + if (field->suffices[0] != '\0') { + // `user_suffices` is a comma-separated string. Assume component is valid. + char *string = strdup(field->suffices); + char *tofree = string; + char *token = my_strsep(&string, ","); + for (int i = 0; i < component - 1; i++) { + token = my_strsep(&string, ","); + } + if (token != NULL) { + static char user_suffix[32 + 1]; + ex_copy_string(user_suffix, token, 32); + free(tofree); + return user_suffix; + } + free(tofree); + } + return "invalid"; + } + case EX_FIELD_TYPE_SEQUENCE: { + // Suffices are just 1...#components. + static char user_suffix[32]; + static char format[8]; + int width = number_width(field->cardinality[nest_level]); + sprintf(format, "%c%d%dd", '%', 0, width); + sprintf(user_suffix, format, component); + return user_suffix; + } + + case EX_BASIS: + case EX_QUADRATURE: { + // Suffices are just 0...#components-1. + static char user_suffix[32]; + static char format[8]; + int width = number_width(field->cardinality[nest_level]); + sprintf(format, "%c%d%dd", '%', 0, width); + sprintf(user_suffix, format, component - 1); + return user_suffix; + } + + case EX_SCALAR: + case EX_FIELD_TYPE_INVALID: + default: return "invalid"; + } +} + +int ex_field_cardinality(const ex_field_type field_type) +{ + switch (field_type) { + case EX_FIELD_TYPE_USER_DEFINED: return -1; + case EX_FIELD_TYPE_SEQUENCE: return -1; + case EX_QUADRATURE: return -1; + case EX_BASIS: return -1; + case EX_SCALAR: return 1; + + case EX_VECTOR_1D: return 1; + case EX_VECTOR_2D: return 2; + case EX_VECTOR_3D: return 3; + + case EX_QUATERNION_2D: return 2; + case EX_QUATERNION_3D: return 4; + + case EX_FULL_TENSOR_12: return 3; + case EX_FULL_TENSOR_16: return 7; + case EX_FULL_TENSOR_22: return 4; + case EX_FULL_TENSOR_32: return 5; + case EX_FULL_TENSOR_36: return 9; + + case EX_SYM_TENSOR_10: return 1; + case EX_SYM_TENSOR_11: return 2; + case EX_SYM_TENSOR_13: return 4; + case EX_SYM_TENSOR_21: return 3; + case EX_SYM_TENSOR_31: return 4; + case EX_SYM_TENSOR_33: return 6; + + case EX_ASYM_TENSOR_01: return 1; + case EX_ASYM_TENSOR_02: return 2; + case EX_ASYM_TENSOR_03: return 3; + + case EX_MATRIX_2X2: return 4; + case EX_MATRIX_3X3: return 9; + + case EX_FIELD_TYPE_INVALID: return -1; + } + return -1; +} + +/** The reverse of `field_type_enum_to_string` Used to convert the + * string into a `field_type` enum. The string is parsed from the + * database as a user-readable attribute. For example, EX_VECTOR_2D + * is on the database instead of a raw number 2 + */ +ex_field_type ex_string_to_field_type_enum(const char *field_name) +{ + if (strcmp(field_name, "EX_FIELD_TYPE_USER_DEFINED") == 0) { + return EX_FIELD_TYPE_USER_DEFINED; + } + if (strcmp(field_name, "EX_FIELD_TYPE_SEQUENCE") == 0) { + return EX_FIELD_TYPE_SEQUENCE; + } + if (strcmp(field_name, "EX_BASIS") == 0) { + return EX_BASIS; + } + if (strcmp(field_name, "EX_QUADRATURE") == 0) { + return EX_QUADRATURE; + } + if (strcmp(field_name, "EX_SCALAR") == 0) { + return EX_SCALAR; + } + if (strcmp(field_name, "EX_VECTOR_1D") == 0) { + return EX_VECTOR_1D; + } + if (strcmp(field_name, "EX_VECTOR_2D") == 0) { + return EX_VECTOR_2D; + } + if (strcmp(field_name, "EX_VECTOR_3D") == 0) { + return EX_VECTOR_3D; + } + if (strcmp(field_name, "EX_QUATERNION_2D") == 0) { + return EX_QUATERNION_2D; + } + if (strcmp(field_name, "EX_QUATERNION_3D") == 0) { + return EX_QUATERNION_3D; + } + if (strcmp(field_name, "EX_FULL_TENSOR_36") == 0) { + return EX_FULL_TENSOR_36; + } + if (strcmp(field_name, "EX_FULL_TENSOR_32") == 0) { + return EX_FULL_TENSOR_32; + } + if (strcmp(field_name, "EX_FULL_TENSOR_22") == 0) { + return EX_FULL_TENSOR_22; + } + if (strcmp(field_name, "EX_FULL_TENSOR_16") == 0) { + return EX_FULL_TENSOR_16; + } + if (strcmp(field_name, "EX_FULL_TENSOR_12") == 0) { + return EX_FULL_TENSOR_12; + } + if (strcmp(field_name, "EX_SYMMETRIC_TENSOR_33") == 0) { + return EX_SYM_TENSOR_33; + } + if (strcmp(field_name, "EX_SYMMETRIC_TENSOR_31") == 0) { + return EX_SYM_TENSOR_31; + } + if (strcmp(field_name, "EX_SYMMETRIC_TENSOR_21") == 0) { + return EX_SYM_TENSOR_21; + } + if (strcmp(field_name, "EX_SYMMETRIC_TENSOR_13") == 0) { + return EX_SYM_TENSOR_13; + } + if (strcmp(field_name, "EX_SYMMETRIC_TENSOR_11") == 0) { + return EX_SYM_TENSOR_11; + } + if (strcmp(field_name, "EX_SYMMETRIC_TENSOR_10") == 0) { + return EX_SYM_TENSOR_10; + } + if (strcmp(field_name, "EX_ASYMMETRIC_TENSOR_03") == 0) { + return EX_ASYM_TENSOR_03; + } + if (strcmp(field_name, "EX_ASYMMETRIC_TENSOR_02") == 0) { + return EX_ASYM_TENSOR_02; + } + if (strcmp(field_name, "EX_ASYMMETRIC_TENSOR_01") == 0) { + return EX_ASYM_TENSOR_01; + } + if (strcmp(field_name, "EX_MATRIX_2X2") == 0) { + return EX_MATRIX_2X2; + } + if (strcmp(field_name, "EX_MATRIX_3X3") == 0) { + return EX_MATRIX_3X3; + } + if (strcmp(field_name, "EX_FIELD_TYPE_INVALID") == 0) { + return EX_FIELD_TYPE_INVALID; + } + return EX_FIELD_TYPE_INVALID; +} + +/** Used to convert a `field_type` enum to a string so it can be + * written to the database as a user-readable attribute. For + * example, EX_VECTOR_2D would appear instead of a raw number 2 + */ +const char *ex_field_type_enum_to_string(const ex_field_type field_type) +{ + switch (field_type) { + case EX_FIELD_TYPE_USER_DEFINED: return "EX_FIELD_TYPE_USER_DEFINED"; + case EX_FIELD_TYPE_SEQUENCE: return "EX_FIELD_TYPE_SEQUENCE"; + case EX_BASIS: return "EX_BASIS"; + case EX_QUADRATURE: return "EX_QUADRATURE"; + case EX_SCALAR: return "EX_SCALAR"; + case EX_VECTOR_1D: return "EX_VECTOR_1D"; + case EX_VECTOR_2D: return "EX_VECTOR_2D"; + case EX_VECTOR_3D: return "EX_VECTOR_3D"; + case EX_QUATERNION_2D: return "EX_QUATERNION_2D"; + case EX_QUATERNION_3D: return "EX_QUATERNION_3D"; + case EX_FULL_TENSOR_36: return "EX_FULL_TENSOR_36"; + case EX_FULL_TENSOR_32: return "EX_FULL_TENSOR_32"; + case EX_FULL_TENSOR_22: return "EX_FULL_TENSOR_22"; + case EX_FULL_TENSOR_16: return "EX_FULL_TENSOR_16"; + case EX_FULL_TENSOR_12: return "EX_FULL_TENSOR_12"; + case EX_SYM_TENSOR_33: return "EX_SYMMETRIC_TENSOR_33"; + case EX_SYM_TENSOR_31: return "EX_SYMMETRIC_TENSOR_31"; + case EX_SYM_TENSOR_21: return "EX_SYMMETRIC_TENSOR_21"; + case EX_SYM_TENSOR_13: return "EX_SYMMETRIC_TENSOR_13"; + case EX_SYM_TENSOR_11: return "EX_SYMMETRIC_TENSOR_11"; + case EX_SYM_TENSOR_10: return "EX_SYMMETRIC_TENSOR_10"; + case EX_ASYM_TENSOR_03: return "EX_ASYMMETRIC_TENSOR_03"; + case EX_ASYM_TENSOR_02: return "EX_ASYMMETRIC_TENSOR_02"; + case EX_ASYM_TENSOR_01: return "EX_ASYMMETRIC_TENSOR_01"; + case EX_MATRIX_2X2: return "EX_MATRIX_2X2"; + case EX_MATRIX_3X3: return "EX_MATRIX_3X3"; + case EX_FIELD_TYPE_INVALID: return "EX_FIELD_TYPE_INVALID"; + } + return "EX_FIELD_TYPE_INVALID"; +} + +const char *ex_field_type_name(const ex_field_type field_type) +{ + switch (field_type) { + case EX_FIELD_TYPE_USER_DEFINED: return "user defined"; + case EX_FIELD_TYPE_SEQUENCE: return "sequence"; + case EX_BASIS: return "basis"; + case EX_QUADRATURE: return "quadrature"; + case EX_SCALAR: return "scalar"; + case EX_VECTOR_1D: return "vector 1D"; + case EX_VECTOR_2D: return "vector 2D"; + case EX_VECTOR_3D: return "vector 3D"; + case EX_QUATERNION_2D: return "quaternion 2D"; + case EX_QUATERNION_3D: return "quaternion 3D"; + case EX_FULL_TENSOR_36: return "full tensor 36"; + case EX_FULL_TENSOR_32: return "full tensor 32"; + case EX_FULL_TENSOR_22: return "full tensor 22"; + case EX_FULL_TENSOR_16: return "full tensor 16"; + case EX_FULL_TENSOR_12: return "full tensor 12"; + case EX_SYM_TENSOR_33: return "symmetric tensor 33"; + case EX_SYM_TENSOR_31: return "symmetric tensor 31"; + case EX_SYM_TENSOR_21: return "symmetric tensor 21"; + case EX_SYM_TENSOR_13: return "symmetric tensor 13"; + case EX_SYM_TENSOR_11: return "symmetric tensor 11"; + case EX_SYM_TENSOR_10: return "symmetric tensor 10"; + case EX_ASYM_TENSOR_03: return "asymmetric tensor 03"; + case EX_ASYM_TENSOR_02: return "asymmetric tensor 02"; + case EX_ASYM_TENSOR_01: return "asymmetric tensor 01"; + case EX_MATRIX_2X2: return "matrix 2x2"; + case EX_MATRIX_3X3: return "matrix 3x3"; + case EX_FIELD_TYPE_INVALID: return "invalid"; + } + return "invalid"; +} diff --git a/packages/seacas/libraries/exodus/src/ex_get_attribute.c b/packages/seacas/libraries/exodus/src/ex_get_attribute.c index 6ac12f47e3..261d7ebadf 100644 --- a/packages/seacas/libraries/exodus/src/ex_get_attribute.c +++ b/packages/seacas/libraries/exodus/src/ex_get_attribute.c @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2022 National Technology & Engineering Solutions + * Copyright(C) 1999-2022, 2024 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. * @@ -28,7 +28,7 @@ static bool exi_is_internal_attribute(const char *name, ex_entity_type obj_type) if (name[0] == '_') { return true; } - else if ((strcmp(name, "elem_type") == 0) || (strcmp(name, "entity_type1") == 0) || + else if ((strcmp(name, ATT_NAME_ELB) == 0) || (strcmp(name, "entity_type1") == 0) || (strcmp(name, "entity_type2") == 0)) { return true; } @@ -42,62 +42,16 @@ static bool exi_is_internal_attribute(const char *name, ex_entity_type obj_type) (strcmp(name, ATT_LAST_WRITTEN_TIME) == 0))) { return true; } - return false; -} - -static int exi_get_varid(int exoid, ex_entity_type obj_type, ex_entity_id id) -{ - char errmsg[MAX_ERR_LENGTH]; - int status = 0; - - if (exi_check_valid_file_id(exoid, __func__) == EX_FATAL) { - return (EX_FATAL); - } - - /* First, locate index of this objects id `obj_type` id array */ - int id_ndx = exi_id_lkup(exoid, obj_type, id); - if (id_ndx <= 0) { - ex_get_err(NULL, NULL, &status); - if (status != 0) { - if (status == EX_NULLENTITY) { /* NULL object? */ - return EX_NOERR; - } - snprintf(errmsg, MAX_ERR_LENGTH, - "ERROR: failed to locate %s id %" PRId64 " in id array in file id %d", - ex_name_of_object(obj_type), id, exoid); - ex_err_fn(exoid, __func__, errmsg, status); - return EX_FATAL; - } + else if (strncmp(name, "Field@", 6) == 0) { + return true; } - - const char *entryptr = NULL; - switch (obj_type) { - case EX_ASSEMBLY: entryptr = VAR_ENTITY_ASSEMBLY(id_ndx); break; - case EX_BLOB: entryptr = VAR_ENTITY_BLOB(id_ndx); break; - case EX_NODE_SET: entryptr = VAR_NODE_NS(id_ndx); break; - case EX_EDGE_SET: entryptr = VAR_EDGE_ES(id_ndx); break; - case EX_FACE_SET: entryptr = VAR_FACE_FS(id_ndx); break; - case EX_SIDE_SET: entryptr = VAR_ELEM_SS(id_ndx); break; - case EX_ELEM_SET: entryptr = VAR_ELEM_ELS(id_ndx); break; - case EX_EDGE_BLOCK: entryptr = VAR_EBCONN(id_ndx); break; - case EX_FACE_BLOCK: entryptr = VAR_FBCONN(id_ndx); break; - case EX_ELEM_BLOCK: entryptr = VAR_CONN(id_ndx); break; - default: - snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type, - __func__); - ex_err(__func__, errmsg, EX_BADPARAM); - return EX_FATAL; + else if (strncmp(name, "Basis@", 6) == 0) { + return true; } - - int varid = 0; - if ((status = nc_inq_varid(exoid, entryptr, &varid)) != NC_NOERR) { - snprintf(errmsg, MAX_ERR_LENGTH, - "ERROR: failed to locate entity list array for %s %" PRId64 " in file id %d", - ex_name_of_object(obj_type), id, exoid); - ex_err_fn(exoid, __func__, errmsg, status); - return EX_FATAL; + else if (strncmp(name, "Quad@", 5) == 0) { + return true; } - return varid; + return false; } static int exi_get_attribute_count(int exoid, ex_entity_type obj_type, ex_entity_id id, int *varid) diff --git a/packages/seacas/libraries/exodus/src/ex_get_field_metadata.c b/packages/seacas/libraries/exodus/src/ex_get_field_metadata.c new file mode 100644 index 0000000000..7d1dbdd70e --- /dev/null +++ b/packages/seacas/libraries/exodus/src/ex_get_field_metadata.c @@ -0,0 +1,568 @@ +/* + * Copyright(C) 1999-2024 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 "exodusII.h" // for ex_err, etc +#include "exodusII_int.h" // for EX_FATAL, etc +#include +#include + +static const char *exi_get_metadata_attribute(char *name, const char *prefix, int pre_len) +{ + /* + * Each field or basis attribute metadata attribute consists of 2 or more attributes. + * Return the string corresponding to {type} in an attribute of the form "Field@{name}@{type}" + * or "Basis@{name}@{type}" or "Quad@{name}@{type}". + */ + + if (strncmp(name, prefix, pre_len) == 0) { + /* Return the suffix (if any) following the last "@" */ + char *suffix = strrchr(name, '@'); + if (suffix != NULL) { + suffix++; + return suffix; + } + } + return NULL; +} + +static const char *exi_get_attribute_metadata_name(char *attrib, int offset) +{ + /* + * PRECONDITION: `attrib` is a basis or field metadata attribute of the form + * "Basis@{name}@{type}" or "Field@{name}@{type}" `offset` is the length + * of `Basis@` or `Field@` or `Quad@` + * + * Returns the `{name}` portion in `name` + */ + static char name[EX_MAX_NAME + 1]; + memset(name, '\0', EX_MAX_NAME + 1); + for (int i = 0; attrib[i + offset] != '@'; i++) { + name[i] = attrib[i + offset]; + } + return name; +} + +static int exi_get_attribute_count(int exoid, ex_entity_type obj_type, ex_entity_id id, int *varid) +{ + int att_count = 0; + int status; + + if (obj_type == EX_GLOBAL) { + *varid = NC_GLOBAL; + + if ((status = nc_inq(exoid, NULL, NULL, &att_count, NULL)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get GLOBAL attribute count"); + ex_err_fn(exoid, __func__, errmsg, status); + return EX_FATAL; + } + } + else { + *varid = exi_get_varid(exoid, obj_type, id); + if (*varid <= 0) { + /* Error message handled in exi_get_varid */ + return 0; + } + + if ((status = nc_inq_var(exoid, *varid, NULL, NULL, NULL, NULL, &att_count)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to get attribute count on %s with id %" PRId64, + ex_name_of_object(obj_type), id); + ex_err_fn(exoid, __func__, errmsg, status); + return EX_FATAL; + } + } + return att_count; +} + +int ex_get_field_metadata_count(int exoid, ex_entity_type obj_type, ex_entity_id id) +{ + EX_FUNC_ENTER(); + + int varid; + int att_count = exi_get_attribute_count(exoid, obj_type, id, &varid); + if (att_count < 0) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Negative attribute count (%d) on %s with id %" PRId64, + att_count, ex_name_of_object(obj_type), id); + ex_err_fn(exoid, __func__, errmsg, EX_INTERNAL); + EX_FUNC_LEAVE(EX_FATAL); + } + + /* Get names of each attribute and see if it is a 'Field metadata' name */ + int count = 0; + for (int i = 0; i < att_count; i++) { + char name[EX_MAX_NAME + 1]; + int status; + if ((status = nc_inq_attname(exoid, varid, i, name)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to get attribute named %s on %s with id %" PRId64, name, + ex_name_of_object(obj_type), id); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + const char *type = exi_get_metadata_attribute(name, "Field@", 6); + if (type != NULL && strcmp("type", type) == 0) { + count++; + } + } + EX_FUNC_LEAVE(count); +} + +/*! Get the values for the specified attribute. */ +int ex_get_field_metadata(int exoid, ex_field *field) +{ + EX_FUNC_ENTER(); + + int varid; + int att_count = exi_get_attribute_count(exoid, field->entity_type, field->entity_id, &varid); + if (att_count < 0) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Negative attribute count (%d) on %s with id %" PRId64, + att_count, ex_name_of_object(field->entity_type), field->entity_id); + ex_err_fn(exoid, __func__, errmsg, EX_INTERNAL); + EX_FUNC_LEAVE(EX_FATAL); + } + + /* Iterate through each Field metadata field and populate `field` */ + int count = 0; + for (int i = 0; i < att_count; i++) { + char attr_name[EX_MAX_NAME + 1]; + int status; + if ((status = nc_inq_attname(exoid, varid, i, attr_name)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to get attribute named %s on %s with id %" PRId64, attr_name, + ex_name_of_object(field->entity_type), field->entity_id); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + + const char *fld_type = exi_get_metadata_attribute(attr_name, "Field@", 6); + if (fld_type != NULL) { + /* Get the field name. We know that the `name` is of the form "Field@{name}@{item}" */ + const char *fld_name = exi_get_attribute_metadata_name(attr_name, 6); + + /* If this is the first time we have seen this `fld_name`, then increment count and + * store the name */ + int found = -1; + int which = 0; + for (int ii = 0; ii < count; ii++) { + if (strcmp(field[ii].name, fld_name) == 0) { + found = ii; + which = ii; + break; + } + } + + if (found == -1) { + which = count; + strcpy(field[count].name, fld_name); + count++; + } + + nc_type type; /* integer, double, character, ... */ + size_t val_count; /* how many `type` values */ + if ((status = nc_inq_att(exoid, varid, attr_name, &type, &val_count)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to get parameters for attribute named %s on %s with id %" PRId64, + attr_name, ex_name_of_object(field->entity_type), field->entity_id); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + + if (strcmp(fld_type, "type") == 0) { + status = nc_get_att_int(exoid, varid, attr_name, (int *)field[which].type); + if (field[which].nesting == 0) { + field[which].nesting = val_count; + } + } + else if (strcmp(fld_type, "separator") == 0) { + status = nc_get_att_text(exoid, varid, attr_name, field[which].component_separator); + } + else if (strcmp(fld_type, "cardinality") == 0) { + status = nc_get_att_int(exoid, varid, attr_name, field[which].cardinality); + if (field[which].nesting == 0) { + field[which].nesting = val_count; + } + } + else if (strcmp(fld_type, "type_name") == 0) { + status = nc_get_att_text(exoid, varid, attr_name, field[which].type_name); + } + else if (strcmp(fld_type, "suffices") == 0) { + status = nc_get_att_text(exoid, varid, attr_name, field[which].suffices); + } + else { + char errmsg[MAX_ERR_LENGTH]; + snprintf( + errmsg, MAX_ERR_LENGTH, + "ERROR: Invalid field metadata attribute type %s on field %s on %s with id %" PRId64, + fld_type, fld_name, ex_name_of_object(field->entity_type), field->entity_id); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + if (status != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to read field metadata attribute type %s on field %s on %s with id " + "%" PRId64, + fld_type, fld_name, ex_name_of_object(field->entity_type), field->entity_id); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + } + } + EX_FUNC_LEAVE(EX_NOERR); +} + +int exi_get_metadata_count(int exoid, const char *which) +{ + EX_FUNC_ENTER(); + + int varid; + int att_count = exi_get_attribute_count(exoid, EX_GLOBAL, 0, &varid); + if (att_count < 0) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Negative attribute count (%d)", att_count); + ex_err_fn(exoid, __func__, errmsg, EX_INTERNAL); + EX_FUNC_LEAVE(EX_FATAL); + } + + /* Get names of each attribute and see if it is a `which` metadata name */ + size_t att_len = strlen(which); + int count = 0; + for (int i = 0; i < att_count; i++) { + char name[EX_MAX_NAME + 1]; + int status; + if ((status = nc_inq_attname(exoid, varid, i, name)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get attribute named %s", name); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + const char *type = exi_get_metadata_attribute(name, which, att_len); + if (type != NULL && strcmp("cardinality", type) == 0) { + count++; + } + } + EX_FUNC_LEAVE(count); +} + +int ex_get_basis_count(int exoid) { return exi_get_metadata_count(exoid, "Basis@"); } + +int ex_get_quadrature_count(int exoid) { return exi_get_metadata_count(exoid, "Quad@"); } + +int ex_get_basis(int exoid, ex_basis **pbasis, int *num_basis) +{ + /* + * -- If this function is called and there is no basis metadata on the + * entity, it will return EX_NOTFOUND; + * + * -- If there are basis defined on the database, it will: + * - determine number of basis defined on database and return the count in `num_basis` + * - allocate `num_basis` copies of `ex_basis` + * - determine the cardinality of each basis and allocate the array members of each basis + * struct. + * - populate the array members for each basis. + * + * Upon return, the `pbasis` will contain `num_basis` structs fully populated. + */ + + // TODO: Should all array members of the struct be allocated and initialized to zero, or + // only the members that are actually defined on the database... + EX_FUNC_ENTER(); + + *num_basis = ex_get_basis_count(exoid); + if (*num_basis == 0) { + EX_FUNC_LEAVE(EX_NOTFOUND); + } + + int varid; + int att_count = exi_get_attribute_count(exoid, EX_GLOBAL, 0, &varid); + if (att_count < 0) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Negative attribute count (%d)", att_count); + ex_err_fn(exoid, __func__, errmsg, EX_INTERNAL); + EX_FUNC_LEAVE(EX_FATAL); + } + + if (*pbasis == NULL) { + struct ex_basis *basis = (ex_basis *)calloc(*num_basis, sizeof(struct ex_basis)); + *pbasis = basis; + } + struct ex_basis *basis = *pbasis; + + // First, iterate through each attribute and get the basis name and cardinality + int count = 0; + for (int att = 0; att < att_count; att++) { + char attr_name[EX_MAX_NAME + 1]; + int status; + if ((status = nc_inq_attname(exoid, varid, att, attr_name)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get attribute named %s", attr_name); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + + if (strncmp("Basis@", attr_name, 6) != 0) { + continue; + } + + const char *basis_type = exi_get_metadata_attribute(attr_name, "Basis@", 6); + if (basis_type != NULL && strcmp(basis_type, "cardinality") == 0) { + /* Get the basis name. We know that the `name` is of the form "Basis@{name}@{item}" */ + const char *basis_name = exi_get_attribute_metadata_name(attr_name, 6); + strcpy(basis[count].name, basis_name); + + int cardinality = 0; + status = nc_get_att_int(exoid, varid, attr_name, &cardinality); + basis[count].cardinality = cardinality; + count++; + if (count == *num_basis) { + break; + } + continue; + } + } + + if (count != *num_basis) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: Internal error populating basis name and cardinality. Did not find correct " + "number of basis attributes."); + ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM); + EX_FUNC_LEAVE(EX_FATAL); + } + + ex_initialize_basis_struct(basis, *num_basis, 1); + + // Now iterate the attributes again and fully populate the basis struct(s) + for (int att = 0; att < att_count; att++) { + char attr_name[EX_MAX_NAME + 1]; + int status; + if ((status = nc_inq_attname(exoid, varid, att, attr_name)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get attribute named %s", attr_name); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + + if (strncmp("Basis@", attr_name, 6) != 0) { + continue; + } + + const char *basis_type = exi_get_metadata_attribute(attr_name, "Basis@", 6); + if (basis_type != NULL) { + /* Get the basis name. We know that the `name` is of the form "Basis@{name}@{item}" */ + const char *basis_name = exi_get_attribute_metadata_name(attr_name, 6); + + // There is no guarantee that we will be getting names in the same order as above since + // attributes can be in any order. But, the name and cardinality for each basis will already + // be set, so we just need to find the correct one... + int which = -1; + for (int i = 0; i < *num_basis; i++) { + if (strcmp(basis[i].name, basis_name) == 0) { + which = i; + break; + } + } + + if (which == -1) { + // Internal error... + } + + if (strcmp(basis_type, "cardinality") == 0) { + // Cardinality already set; skip + } + else if (strcmp(basis_type, "subc_dim") == 0) { + status = nc_get_att_int(exoid, varid, attr_name, basis[which].subc_dim); + } + else if (status == NC_NOERR && strcmp(basis_type, "subc_ordinal") == 0) { + status = nc_get_att_int(exoid, varid, attr_name, basis[which].subc_ordinal); + } + else if (status == NC_NOERR && strcmp(basis_type, "subc_dof_ordinal") == 0) { + status = nc_get_att_int(exoid, varid, attr_name, basis[which].subc_dof_ordinal); + } + else if (status == NC_NOERR && strcmp(basis_type, "subc_num_dof") == 0) { + status = nc_get_att_int(exoid, varid, attr_name, basis[which].subc_num_dof); + } + else if (status == NC_NOERR && strcmp(basis_type, "xi") == 0) { + status = nc_get_att_double(exoid, varid, attr_name, basis[which].xi); + } + else if (status == NC_NOERR && strcmp(basis_type, "eta") == 0) { + status = nc_get_att_double(exoid, varid, attr_name, basis[which].eta); + } + else if (status == NC_NOERR && strcmp(basis_type, "zeta") == 0) { + status = nc_get_att_double(exoid, varid, attr_name, basis[which].zeta); + } + + if (status != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to read Basis %s metadata", + basis[which].name); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + } + } + EX_FUNC_LEAVE(EX_NOERR); +} + +int ex_get_quadrature(int exoid, ex_quadrature **pquad, int *num_quad) +{ + /* + * -- If this function is called and there is no quadrature metadata on the + * entity, it will return EX_NOTFOUND; + * + * -- If there are quadrature defined on the database, it will: + * - determine number of quadrature defined on database and return the count in `num_quad` + * - allocate `num_quad` copies of `ex_quad` + * - determine the cardinality of each quadrature and allocate the array members of each + * quadrature struct. + * - populate the array members for each quadrature. + * + * Upon return, the `pquad` will contain `num_basis` structs fully populated. + */ + + // TODO: Should all array members of the struct(xi, eta, zeta, + // weight) be allocated and initialized to zero, or only the + // members that are actually defined on the database... + EX_FUNC_ENTER(); + + *num_quad = ex_get_quadrature_count(exoid); + if (*num_quad == 0) { + EX_FUNC_LEAVE(EX_NOTFOUND); + } + + int varid; + int att_count = exi_get_attribute_count(exoid, EX_GLOBAL, 0, &varid); + if (att_count < 0) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Negative attribute count (%d)", att_count); + ex_err_fn(exoid, __func__, errmsg, EX_INTERNAL); + EX_FUNC_LEAVE(EX_FATAL); + } + + if (*pquad == NULL) { + struct ex_quadrature *quad = (ex_quadrature *)calloc(*num_quad, sizeof(struct ex_quadrature)); + *pquad = quad; + } + struct ex_quadrature *quad = *pquad; + + // First, iterate through each attribute and get the quadrature name and cardinality + int count = 0; + for (int att = 0; att < att_count; att++) { + char attr_name[EX_MAX_NAME + 1]; + int status; + if ((status = nc_inq_attname(exoid, varid, att, attr_name)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get attribute named %s", attr_name); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + + if (strncmp("Quad@", attr_name, 5) != 0) { + continue; + } + + const char *quadrature_type = exi_get_metadata_attribute(attr_name, "Quad@", 5); + if (quadrature_type != NULL && strcmp(quadrature_type, "cardinality") == 0) { + /* Get the quadrature name. We know that the `name` is of the form "Quad@{name}@{item}" */ + const char *quadrature_name = exi_get_attribute_metadata_name(attr_name, 5); + strcpy(quad[count].name, quadrature_name); + + int cardinality = 0; + status = nc_get_att_int(exoid, varid, attr_name, &cardinality); + quad[count].cardinality = cardinality; + count++; + if (count == *num_quad) { + break; + } + continue; + } + } + + if (count != *num_quad) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: Internal error populating quadrature name and cardinality. Did not find " + "correct number of quadrature attributes."); + ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM); + EX_FUNC_LEAVE(EX_FATAL); + } + + ex_initialize_quadrature_struct(quad, *num_quad, 1); + + // Now iterate the attributes again and fully populate the quadrature struct(s) + for (int att = 0; att < att_count; att++) { + char attr_name[EX_MAX_NAME + 1]; + int status; + if ((status = nc_inq_attname(exoid, varid, att, attr_name)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get attribute named %s", attr_name); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + + if (strncmp("Quad@", attr_name, 5) != 0) { + continue; + } + + const char *quadrature_type = exi_get_metadata_attribute(attr_name, "Quad@", 5); + if (quadrature_type != NULL) { + + /* Get the quadrature name. We know that the `name` is of the form "Quad@{name}@{item}" */ + const char *quadrature_name = exi_get_attribute_metadata_name(attr_name, 5); + + // There is no guarantee that we will be getting names in the same order as above since + // attributes can be in any order. But, the name and cardinality for each quadrature will + // already be set, so we just need to find the correct one... + int which = -1; + for (int i = 0; i < *num_quad; i++) { + if (strcmp(quad[i].name, quadrature_name) == 0) { + which = i; + break; + } + } + + if (which == -1) { + // Internal error... + } + + if (strcmp(quadrature_type, "cardinality") == 0) { + // Cardinality already set; skip + } + else if (strcmp(quadrature_type, "xi") == 0) { + status = nc_get_att_double(exoid, varid, attr_name, quad[which].xi); + } + else if (status == NC_NOERR && strcmp(quadrature_type, "eta") == 0) { + status = nc_get_att_double(exoid, varid, attr_name, quad[which].eta); + } + else if (status == NC_NOERR && strcmp(quadrature_type, "zeta") == 0) { + status = nc_get_att_double(exoid, varid, attr_name, quad[which].zeta); + } + else if (status == NC_NOERR && strcmp(quadrature_type, "weight") == 0) { + status = nc_get_att_double(exoid, varid, attr_name, quad[which].weight); + } + // NOTE: Do not put an else since will fall through if the + // arrays are NULL even though quadrature_type is valid. + + if (status != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to read Quadrature %s metadata", + quad[which].name); + ex_err_fn(exoid, __func__, errmsg, status); + EX_FUNC_LEAVE(EX_FATAL); + } + } + } + EX_FUNC_LEAVE(EX_NOERR); +} diff --git a/packages/seacas/libraries/exodus/src/ex_put_all_var_param_ext.c b/packages/seacas/libraries/exodus/src/ex_put_all_var_param_ext.c index 7f2a238f7b..074a79d84f 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_all_var_param_ext.c +++ b/packages/seacas/libraries/exodus/src/ex_put_all_var_param_ext.c @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2023 National Technology & Engineering Solutions + * Copyright(C) 1999-2024 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. * @@ -29,11 +29,11 @@ static int *get_status_array(int exoid, int var_count, const char *VARIABLE, con static int put_truth_table(int exoid, int varid, int *table, const char *label); static int define_truth_table(ex_entity_type obj_type, int exoid, int num_ent, int num_var, int *var_tab, int *status_tab, void_int *ids, const char *label); -static int ex_define_vars(int exoid, ex_entity_type obj_type, const char *entity_name, - const char *entity_blk_name, int numvar, const char *DNAME, int dimid1, - int dimid2, int DVAL, void_int **entity_ids, const char *VNOV, - const char *VTV, int **status_var, int *truth_table, - int *truth_table_var); +static int exi_define_vars(int exoid, ex_entity_type obj_type, const char *entity_name, + const char *entity_blk_name, int numvar, const char *DNAME, int dimid1, + int dimid2, int DVAL, void_int **entity_ids, const char *VNOV, + const char *VTV, int **status_var, int *truth_table, + int *truth_table_var); #define EX_GET_IDS_STATUS(TNAME, NUMVAR, DNAME, DID, DVAL, VIDS, EIDS, VSTAT, VSTATVAL) \ if (NUMVAR > 0) { \ @@ -200,52 +200,53 @@ int ex_put_all_var_param_ext(int exoid, const ex_var_params *vp) } } - if (ex_define_vars(exoid, EX_EDGE_BLOCK, "edge", "edge block", vp->num_edge, DIM_NUM_EDG_VAR, - numedblkdim, numedvardim, num_edge_blk, &edblk_ids, VAR_NAME_EDG_VAR, - VAR_EBLK_TAB, &edblk_stat, vp->edge_var_tab, &edblk_varid) != EX_NOERR) { + if (exi_define_vars(exoid, EX_EDGE_BLOCK, "edge", "edge block", vp->num_edge, DIM_NUM_EDG_VAR, + numedblkdim, numedvardim, num_edge_blk, &edblk_ids, VAR_NAME_EDG_VAR, + VAR_EBLK_TAB, &edblk_stat, vp->edge_var_tab, &edblk_varid) != EX_NOERR) { goto error_ret; } - if (ex_define_vars(exoid, EX_FACE_BLOCK, "face", "face block", vp->num_face, DIM_NUM_FAC_VAR, - numfablkdim, numfavardim, num_face_blk, &fablk_ids, VAR_NAME_FAC_VAR, - VAR_FBLK_TAB, &fablk_stat, vp->face_var_tab, &fablk_varid) != EX_NOERR) { + if (exi_define_vars(exoid, EX_FACE_BLOCK, "face", "face block", vp->num_face, DIM_NUM_FAC_VAR, + numfablkdim, numfavardim, num_face_blk, &fablk_ids, VAR_NAME_FAC_VAR, + VAR_FBLK_TAB, &fablk_stat, vp->face_var_tab, &fablk_varid) != EX_NOERR) { goto error_ret; } - if (ex_define_vars(exoid, EX_ELEM_BLOCK, "element", "element block", vp->num_elem, - DIM_NUM_ELE_VAR, numelblkdim, numelvardim, num_elem_blk, &eblk_ids, - VAR_NAME_ELE_VAR, VAR_ELEM_TAB, &eblk_stat, vp->elem_var_tab, - &eblk_varid) != EX_NOERR) { + if (exi_define_vars(exoid, EX_ELEM_BLOCK, "element", "element block", vp->num_elem, + DIM_NUM_ELE_VAR, numelblkdim, numelvardim, num_elem_blk, &eblk_ids, + VAR_NAME_ELE_VAR, VAR_ELEM_TAB, &eblk_stat, vp->elem_var_tab, + &eblk_varid) != EX_NOERR) { goto error_ret; } - if (ex_define_vars(exoid, EX_NODE_SET, "nodeset", "node set", vp->num_nset, DIM_NUM_NSET_VAR, - numnsetdim, nsetvardim, num_nset, &nset_ids, VAR_NAME_NSET_VAR, VAR_NSET_TAB, - &nset_stat, vp->nset_var_tab, &nset_varid) != EX_NOERR) { + if (exi_define_vars(exoid, EX_NODE_SET, "nodeset", "node set", vp->num_nset, DIM_NUM_NSET_VAR, + numnsetdim, nsetvardim, num_nset, &nset_ids, VAR_NAME_NSET_VAR, VAR_NSET_TAB, + &nset_stat, vp->nset_var_tab, &nset_varid) != EX_NOERR) { goto error_ret; } - if (ex_define_vars(exoid, EX_EDGE_SET, "edgeset", "edge set", vp->num_eset, DIM_NUM_ESET_VAR, - numesetdim, esetvardim, num_eset, &eset_ids, VAR_NAME_ESET_VAR, VAR_ESET_TAB, - &eset_stat, vp->eset_var_tab, &eset_varid) != EX_NOERR) { + if (exi_define_vars(exoid, EX_EDGE_SET, "edgeset", "edge set", vp->num_eset, DIM_NUM_ESET_VAR, + numesetdim, esetvardim, num_eset, &eset_ids, VAR_NAME_ESET_VAR, VAR_ESET_TAB, + &eset_stat, vp->eset_var_tab, &eset_varid) != EX_NOERR) { goto error_ret; } - if (ex_define_vars(exoid, EX_FACE_SET, "faceset", "face set", vp->num_fset, DIM_NUM_FSET_VAR, - numfsetdim, fsetvardim, num_fset, &fset_ids, VAR_NAME_FSET_VAR, VAR_FSET_TAB, - &fset_stat, vp->fset_var_tab, &fset_varid) != EX_NOERR) { + if (exi_define_vars(exoid, EX_FACE_SET, "faceset", "face set", vp->num_fset, DIM_NUM_FSET_VAR, + numfsetdim, fsetvardim, num_fset, &fset_ids, VAR_NAME_FSET_VAR, VAR_FSET_TAB, + &fset_stat, vp->fset_var_tab, &fset_varid) != EX_NOERR) { goto error_ret; } - if (ex_define_vars(exoid, EX_SIDE_SET, "sideset", "side set", vp->num_sset, DIM_NUM_SSET_VAR, - numssetdim, ssetvardim, num_sset, &sset_ids, VAR_NAME_SSET_VAR, VAR_SSET_TAB, - &sset_stat, vp->sset_var_tab, &sset_varid) != EX_NOERR) { + if (exi_define_vars(exoid, EX_SIDE_SET, "sideset", "side set", vp->num_sset, DIM_NUM_SSET_VAR, + numssetdim, ssetvardim, num_sset, &sset_ids, VAR_NAME_SSET_VAR, VAR_SSET_TAB, + &sset_stat, vp->sset_var_tab, &sset_varid) != EX_NOERR) { goto error_ret; } - if (ex_define_vars(exoid, EX_ELEM_SET, "elemset", "element set", vp->num_elset, DIM_NUM_ELSET_VAR, - numelsetdim, elsetvardim, num_elset, &elset_ids, VAR_NAME_ELSET_VAR, - VAR_ELSET_TAB, &elset_stat, vp->elset_var_tab, &elset_varid) != EX_NOERR) { + if (exi_define_vars(exoid, EX_ELEM_SET, "elemset", "element set", vp->num_elset, + DIM_NUM_ELSET_VAR, numelsetdim, elsetvardim, num_elset, &elset_ids, + VAR_NAME_ELSET_VAR, VAR_ELSET_TAB, &elset_stat, vp->elset_var_tab, + &elset_varid) != EX_NOERR) { goto error_ret; } @@ -517,10 +518,11 @@ static int define_truth_table(ex_entity_type obj_type, int exoid, int num_ent, i return NC_NOERR; } -static int ex_define_vars(int exoid, ex_entity_type obj_type, const char *entity_name, - const char *entity_blk_name, int numvar, const char *DNAME, int dimid1, - int dimid2, int DVAL, void_int **entity_ids, const char *VNOV, - const char *VTV, int **status_var, int *truth_table, int *truth_table_var) +static int exi_define_vars(int exoid, ex_entity_type obj_type, const char *entity_name, + const char *entity_blk_name, int numvar, const char *DNAME, int dimid1, + int dimid2, int DVAL, void_int **entity_ids, const char *VNOV, + const char *VTV, int **status_var, int *truth_table, + int *truth_table_var) { if (numvar > 0) { int status; diff --git a/packages/seacas/libraries/exodus/src/ex_put_attribute.c b/packages/seacas/libraries/exodus/src/ex_put_attribute.c index 7da9ee97be..370cfe31ef 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_attribute.c +++ b/packages/seacas/libraries/exodus/src/ex_put_attribute.c @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2022 National Technology & Engineering Solutions + * Copyright(C) 1999-2022, 2024 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. * @@ -22,88 +22,6 @@ attributes which are currently supported in Exodus. */ -static int exi_get_varid(int exoid, ex_entity_type obj_type, ex_entity_id id) -{ - const char *entryptr = NULL; - char errmsg[MAX_ERR_LENGTH]; - - int id_ndx = 0; - int status = 0; - int varid = 0; - - if (exi_check_valid_file_id(exoid, __func__) == EX_FATAL) { - EX_FUNC_LEAVE(EX_FATAL); - } - - if (obj_type == EX_GLOBAL) { - return NC_GLOBAL; - } - - if (obj_type == EX_ASSEMBLY) { - if ((status = nc_inq_varid(exoid, VAR_ENTITY_ASSEMBLY(id), &varid)) != NC_NOERR) { - snprintf(errmsg, MAX_ERR_LENGTH, - "ERROR: failed to locate %s id %" PRId64 " in id array in file id %d", - ex_name_of_object(obj_type), id, exoid); - ex_err_fn(exoid, __func__, errmsg, status); - return EX_FATAL; - } - return varid; - } - - if (obj_type == EX_BLOB) { - if ((status = nc_inq_varid(exoid, VAR_ENTITY_BLOB(id), &varid)) != NC_NOERR) { - snprintf(errmsg, MAX_ERR_LENGTH, - "ERROR: failed to locate %s id %" PRId64 " in id array in file id %d", - ex_name_of_object(obj_type), id, exoid); - ex_err_fn(exoid, __func__, errmsg, status); - return EX_FATAL; - } - return varid; - } - - /* Everything else ... */ - /* First, locate index of this objects id `obj_type` id array */ - id_ndx = exi_id_lkup(exoid, obj_type, id); - if (id_ndx <= 0) { - ex_get_err(NULL, NULL, &status); - if (status != 0) { - if (status == EX_NULLENTITY) { /* NULL object? */ - return EX_NOERR; - } - snprintf(errmsg, MAX_ERR_LENGTH, - "ERROR: failed to locate %s id %" PRId64 " in id array in file id %d", - ex_name_of_object(obj_type), id, exoid); - ex_err_fn(exoid, __func__, errmsg, status); - return EX_FATAL; - } - } - - switch (obj_type) { - case EX_NODE_SET: entryptr = VAR_NODE_NS(id_ndx); break; - case EX_EDGE_SET: entryptr = VAR_EDGE_ES(id_ndx); break; - case EX_FACE_SET: entryptr = VAR_FACE_FS(id_ndx); break; - case EX_SIDE_SET: entryptr = VAR_ELEM_SS(id_ndx); break; - case EX_ELEM_SET: entryptr = VAR_ELEM_ELS(id_ndx); break; - case EX_EDGE_BLOCK: entryptr = VAR_EBCONN(id_ndx); break; - case EX_FACE_BLOCK: entryptr = VAR_FBCONN(id_ndx); break; - case EX_ELEM_BLOCK: entryptr = VAR_CONN(id_ndx); break; - default: - snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type, - __func__); - ex_err(__func__, errmsg, EX_BADPARAM); - return EX_FATAL; - } - - if ((status = nc_inq_varid(exoid, entryptr, &varid)) != NC_NOERR) { - snprintf(errmsg, MAX_ERR_LENGTH, - "ERROR: failed to locate entity list array for %s %" PRId64 " in file id %d", - ex_name_of_object(obj_type), id, exoid); - ex_err_fn(exoid, __func__, errmsg, status); - return EX_FATAL; - } - return varid; -} - /* define and output a double attribute */ int ex_put_double_attribute(int exoid, ex_entity_type obj_type, ex_entity_id id, const char *atr_name, int num_values, const double *values) diff --git a/packages/seacas/libraries/exodus/src/ex_put_field_metadata.c b/packages/seacas/libraries/exodus/src/ex_put_field_metadata.c new file mode 100644 index 0000000000..36ad8a30f8 --- /dev/null +++ b/packages/seacas/libraries/exodus/src/ex_put_field_metadata.c @@ -0,0 +1,292 @@ +/* + * Copyright(C) 2022, 2023, 2024 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 "exodusII.h" // for ex_err, etc +#include "exodusII_int.h" // for EX_FATAL, etc + +static int exi_print_type_error(int status, const char *name, const char *type, + const char *attribute, int exoid, const char *func) +{ + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to store '%s' for %s '%s' in file id %d", + attribute, type, name, exoid); + ex_err_fn(exoid, func, errmsg, status); + return EX_FATAL; +} + +static int exi_print_attribute_error(int status, const char *name, const char *attribute, + ex_entity_type entity_type, ex_entity_id entity_id, int exoid, + const char *func) +{ + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to store field metadata '%s' for field '%s' on %s with id %" PRId64 + " in file id %d", + attribute, name, ex_name_of_object(entity_type), entity_id, exoid); + ex_err_fn(exoid, func, errmsg, status); + return EX_FATAL; +} + +int ex_put_field_metadata(int exoid, const ex_field field) +{ + /* + * The attribute name is `Field@{name}@type` + * + * Field `nesting` is calculated as size of `type` field + * The `type` field is a sequence of integers which are the values of the `ex_field_type` enum. + * NOTE: For backward compatibility, we can only add new entries to this enum at the end. + * + * If size of `component_separator` == 0 then the default '_' separator used by all component + * levels If size of `component_separator` == 1 then that separator used by all component levels + * Else the size must equal nesting and it specifies a potentially different separator for each + * level. + */ +#if 0 + fprintf(stderr, + "ex_put_field_metadata: Field '%s' of type '%s' with separator '%s' on block %lld\n", + field.name, ex_field_type_enum_to_string(field.type[0]), field.component_separator, + field.entity_id); +#endif + + int status = 0; + static char *field_template = "Field@%s@%s"; + char attribute_name[NC_MAX_NAME + 1]; + sprintf(attribute_name, field_template, field.name, "type"); + if ((status = ex_put_integer_attribute(exoid, field.entity_type, field.entity_id, attribute_name, + field.nesting, field.type)) != EX_NOERR) { + return exi_print_attribute_error(status, field.name, "type", field.entity_type, field.entity_id, + exoid, __func__); + } + + sprintf(attribute_name, field_template, field.name, "type_name"); + if ((status = ex_put_text_attribute(exoid, field.entity_type, field.entity_id, attribute_name, + field.type_name)) != EX_NOERR) { + return exi_print_attribute_error(status, field.name, "type_name", field.entity_type, + field.entity_id, exoid, __func__); + } + + sprintf(attribute_name, field_template, field.name, "separator"); + if ((status = ex_put_text_attribute(exoid, field.entity_type, field.entity_id, attribute_name, + field.component_separator)) != EX_NOERR) { + return exi_print_attribute_error(status, field.name, "separator", field.entity_type, + field.entity_id, exoid, __func__); + } + + bool needs_cardinality = false; + for (int i = 0; i < field.nesting; i++) { + if (field.type[i] == EX_FIELD_TYPE_USER_DEFINED || field.type[i] == EX_FIELD_TYPE_SEQUENCE) { + needs_cardinality = true; + break; + } + } + if (needs_cardinality) { + sprintf(attribute_name, field_template, field.name, "cardinality"); + if ((status = ex_put_integer_attribute(exoid, field.entity_type, field.entity_id, + attribute_name, field.nesting, field.cardinality)) != + EX_NOERR) { + return exi_print_attribute_error(status, field.name, "cardinality", field.entity_type, + field.entity_id, exoid, __func__); + } + } + + return EX_NOERR; +} + +int exi_put_type_attribute(int exoid, const char *att_root, const char *name, const char *type, + ex_type value_type, int cardinality, const void *entry) +{ + int status = EX_NOERR; + if (entry != NULL) { + static char *template = "%s@%s@%s"; + char attribute_name[NC_MAX_NAME + 1]; + sprintf(attribute_name, template, att_root, name, type); + if (value_type == EX_INTEGER) { + status = ex_put_integer_attribute(exoid, EX_GLOBAL, 0, attribute_name, cardinality, entry); + } + else if (value_type == EX_DOUBLE) { + status = ex_put_double_attribute(exoid, EX_GLOBAL, 0, attribute_name, cardinality, entry); + } + } + return status; +} + +int exi_put_basis_attribute(int exoid, const char *basis_name, const char *type, ex_type value_type, + int cardinality, const void *basis_entry) +{ + return exi_put_type_attribute(exoid, "Basis", basis_name, type, value_type, cardinality, + basis_entry); +} + +int exi_put_quad_attribute(int exoid, const char *quad_name, const char *type, ex_type value_type, + int cardinality, const void *quad_entry) +{ + return exi_put_type_attribute(exoid, "Quad", quad_name, type, value_type, cardinality, + quad_entry); +} + +int ex_put_basis(int exoid, const ex_basis basis) +{ + /* + * typedef struct ex_basis { + * char name[EX_MAX_NAME + 1]; + * int cardinality; -- number of `basis` points == dimension of non-null subc_*, xi, eta, zeta + * int *subc_dim; + * int *subc_ordinal; + * int *subc_dof_ordinal; + * int *subc_num_dof; + * double *xi; + * double *eta; + * double *zeta; + * } ex_basis; + */ + + int status; + if ((status = exi_put_basis_attribute(exoid, basis.name, "cardinality", EX_INTEGER, 1, + &basis.cardinality)) != EX_NOERR) { + return exi_print_type_error(status, basis.name, "basis", "cardinality", exoid, __func__); + } + + if ((status = exi_put_basis_attribute(exoid, basis.name, "subc_dim", EX_INTEGER, + basis.cardinality, basis.subc_dim)) != EX_NOERR) { + return exi_print_type_error(status, basis.name, "basis", "subc_dim", exoid, __func__); + } + + if ((status = exi_put_basis_attribute(exoid, basis.name, "subc_ordinal", EX_INTEGER, + basis.cardinality, basis.subc_ordinal)) != EX_NOERR) { + return exi_print_type_error(status, basis.name, "basis", "subc_ordinal", exoid, __func__); + } + + if ((status = exi_put_basis_attribute(exoid, basis.name, "subc_dof_ordinal", EX_INTEGER, + basis.cardinality, basis.subc_dof_ordinal)) != EX_NOERR) { + return exi_print_type_error(status, basis.name, "basis", "subc_dof_ordinal", exoid, __func__); + } + + if ((status = exi_put_basis_attribute(exoid, basis.name, "subc_num_dof", EX_INTEGER, + basis.cardinality, basis.subc_num_dof)) != EX_NOERR) { + return exi_print_type_error(status, basis.name, "basis", "subc_num_dof", exoid, __func__); + } + + if ((status = exi_put_basis_attribute(exoid, basis.name, "xi", EX_DOUBLE, basis.cardinality, + basis.xi)) != EX_NOERR) { + return exi_print_type_error(status, basis.name, "basis", "xi", exoid, __func__); + } + + if ((status = exi_put_basis_attribute(exoid, basis.name, "eta", EX_DOUBLE, basis.cardinality, + basis.eta)) != EX_NOERR) { + return exi_print_type_error(status, basis.name, "basis", "eta", exoid, __func__); + } + + if ((status = exi_put_basis_attribute(exoid, basis.name, "zeta", EX_DOUBLE, basis.cardinality, + basis.zeta)) != EX_NOERR) { + return exi_print_type_error(status, basis.name, "basis", "zeta", exoid, __func__); + } + return EX_NOERR; +} + +int ex_put_quadrature(int exoid, const ex_quadrature quad) +{ + /* + * typedef struct ex_quad { + * char name[EX_MAX_NAME + 1]; + * int cardinality; -- number of `quad` points == dimension of non-null subc_*, xi, eta, zeta + * int *subc_dim; + * int *subc_ordinal; + * int *subc_dof_ordinal; + * int *subc_num_dof; + * double *xi; + * double *eta; + * double *zeta; + * } ex_quad; + */ + + int status; + if ((status = exi_put_quad_attribute(exoid, quad.name, "cardinality", EX_INTEGER, 1, + &quad.cardinality)) != EX_NOERR) { + return exi_print_type_error(status, quad.name, "quad", "cardinality", exoid, __func__); + } + + if ((status = exi_put_quad_attribute(exoid, quad.name, "xi", EX_DOUBLE, quad.cardinality, + quad.xi)) != EX_NOERR) { + return exi_print_type_error(status, quad.name, "quad", "xi", exoid, __func__); + } + + if ((status = exi_put_quad_attribute(exoid, quad.name, "eta", EX_DOUBLE, quad.cardinality, + quad.eta)) != EX_NOERR) { + return exi_print_type_error(status, quad.name, "quad", "eta", exoid, __func__); + } + + if ((status = exi_put_quad_attribute(exoid, quad.name, "zeta", EX_DOUBLE, quad.cardinality, + quad.zeta)) != EX_NOERR) { + return exi_print_type_error(status, quad.name, "quad", "zeta", exoid, __func__); + } + + if ((status = exi_put_quad_attribute(exoid, quad.name, "weight", EX_DOUBLE, quad.cardinality, + quad.weight)) != EX_NOERR) { + return exi_print_type_error(status, quad.name, "quad", "weight", exoid, __func__); + } + return EX_NOERR; +} + +int ex_put_field_suffices(int exoid, const ex_field field, const char *suffices) +{ + /* + * For a user-defined field metadata type, output the `cardinality`-count suffices. + * The suffices are in a single comma-separated string. + * This call is only valid if the field metadata type is user-defined. + * + * Example: cardinality = 4, type is EX_USER_DEFINED, name is "Species" + * Suffices = "h2o,gas,ch4,methane" + * The fields would be "Species_h2o", "Species_gas", "Species_ch4", "Species_methane" + * + * Accesses field.entity_type, field.entity_id, field.name, field.type, field.cardinality + * The attribute name is `Field@{name}@suffices` + */ + int status; + char errmsg[MAX_ERR_LENGTH]; + + char attribute_name[NC_MAX_NAME + 1]; + static char *field_template = "Field@%s@%s"; + + if (field.type[0] != EX_FIELD_TYPE_USER_DEFINED) { + snprintf( + errmsg, MAX_ERR_LENGTH, + "ERROR: Field '%s' is not of type EX_FIELD_TYPE_USER_DEFINED; cannot specify suffices.", + field.name); + ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM); + return EX_FATAL; + } + + /* Count the commas in the comma-separated list of suffices. Must be one less than the field + * cardinality... */ + int comma_count = 0; + for (size_t i = 0; i < strlen(suffices); i++) { + if (suffices[i] == ',') { + comma_count++; + } + } + if (comma_count + 1 != field.cardinality[0]) { + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: Field '%s' cardinality is %d but there were %d suffices defined. These must " + "be equal.", + field.name, field.cardinality[0], comma_count + 1); + ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM); + return EX_FATAL; + } + + sprintf(attribute_name, field_template, field.name, "suffices"); + if ((status = ex_put_text_attribute(exoid, field.entity_type, field.entity_id, attribute_name, + suffices)) != EX_NOERR) { + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to store field suffices for field '%s' on %s with id %" PRId64 + " in file id %d", + field.name, ex_name_of_object(field.entity_type), field.entity_id, exoid); + ex_err_fn(exoid, __func__, errmsg, status); + return EX_FATAL; + } + return EX_NOERR; +} diff --git a/packages/seacas/libraries/exodus/src/ex_utils.c b/packages/seacas/libraries/exodus/src/ex_utils.c index d345477ecc..e23c6007b1 100644 --- a/packages/seacas/libraries/exodus/src/ex_utils.c +++ b/packages/seacas/libraries/exodus/src/ex_utils.c @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2023 National Technology & Engineering Solutions + * Copyright(C) 1999-2024 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. * @@ -11,8 +11,8 @@ * *****************************************************************************/ -#if defined(DEBUG_QSORT) -#endif +#define _GNU_SOURCE +#include #include #include @@ -2290,6 +2290,85 @@ char *ex_copy_string(char *dest, char const *source, size_t elements) return d; } +/* Used by the entity attribute code `ex_get_attribute(s)` and `ex_put_attribute(s)` */ +int exi_get_varid(int exoid, ex_entity_type obj_type, ex_entity_id id) +{ + if (exi_check_valid_file_id(exoid, __func__) == EX_FATAL) { + EX_FUNC_LEAVE(EX_FATAL); + } + + if (obj_type == EX_GLOBAL) { + return NC_GLOBAL; + } + + int status = 0; + char errmsg[MAX_ERR_LENGTH]; + + if (obj_type == EX_NODAL) { + /* For the nodal entity attributes, we store it on the + "coor_names" variable. Not exactly logical, but it exists in any + model that has nodes, so it at least gives us a place to put the + attribute. Another possibility would be the nodal x-coordinate + variable... + */ + int varid = 0; + if ((status = nc_inq_varid(exoid, VAR_NAME_COOR, &varid)) != NC_NOERR) { + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to locate node block variable id in file id %d", exoid); + ex_err_fn(exoid, __func__, errmsg, status); + return EX_FATAL; + } + return varid; + } + + /* Everything else ... */ + /* First, locate index of this objects id `obj_type` id array */ + /* First, locate index of this objects id `obj_type` id array */ + int id_ndx = exi_id_lkup(exoid, obj_type, id); + if (id_ndx <= 0) { + ex_get_err(NULL, NULL, &status); + if (status != 0) { + if (status == EX_NULLENTITY) { /* NULL object? */ + return EX_NOERR; + } + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to locate %s id %" PRId64 " in id array in file id %d", + ex_name_of_object(obj_type), id, exoid); + ex_err_fn(exoid, __func__, errmsg, status); + return EX_FATAL; + } + } + + const char *entryptr = NULL; + switch (obj_type) { + case EX_ASSEMBLY: entryptr = VAR_ENTITY_ASSEMBLY(id_ndx); break; + case EX_BLOB: entryptr = VAR_ENTITY_BLOB(id_ndx); break; + case EX_NODE_SET: entryptr = VAR_NODE_NS(id_ndx); break; + case EX_EDGE_SET: entryptr = VAR_EDGE_ES(id_ndx); break; + case EX_FACE_SET: entryptr = VAR_FACE_FS(id_ndx); break; + case EX_SIDE_SET: entryptr = VAR_ELEM_SS(id_ndx); break; + case EX_ELEM_SET: entryptr = VAR_ELEM_ELS(id_ndx); break; + case EX_EDGE_BLOCK: entryptr = VAR_EBCONN(id_ndx); break; + case EX_FACE_BLOCK: entryptr = VAR_FBCONN(id_ndx); break; + case EX_ELEM_BLOCK: entryptr = VAR_CONN(id_ndx); break; + default: + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type, + __func__); + ex_err(__func__, errmsg, EX_BADPARAM); + return EX_FATAL; + } + + int varid = 0; + if ((status = nc_inq_varid(exoid, entryptr, &varid)) != NC_NOERR) { + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to locate entity list array for %s %" PRId64 " in file id %d", + ex_name_of_object(obj_type), id, exoid); + ex_err_fn(exoid, __func__, errmsg, status); + return EX_FATAL; + } + return varid; +} + /* * Code from: * https://stackoverflow.com/questions/11034002/how-to-get-absolute-path-of-file-or-directory-that-does-not-exist diff --git a/packages/seacas/libraries/exodus/test/CMakeLists.txt b/packages/seacas/libraries/exodus/test/CMakeLists.txt index 1e8eea43c6..fc3596d894 100644 --- a/packages/seacas/libraries/exodus/test/CMakeLists.txt +++ b/packages/seacas/libraries/exodus/test/CMakeLists.txt @@ -66,6 +66,7 @@ endif() IF ( NETCDF_NCDUMP_BINARY ) SET(NETCDF_NCDUMP ${NETCDF_NCDUMP_BINARY}) SET( cbind_OLDTESTS + test-field-utils testwt testwtd testwtd-to-f @@ -108,6 +109,8 @@ IF ( NETCDF_NCDUMP_BINARY ) test-add-assembly testwt-blob testrd-blob + testwt-field-metadata + testrd-field-metadata ) IF (SEACASExodus_ENABLE_THREADSAFE) @@ -155,6 +158,15 @@ IF ( NETCDF_NCDUMP_BINARY ) set_property(TARGET ${testName} PROPERTY C_STANDARD 99) ENDFOREACH ( testName ) + TRIBITS_ADD_TEST( + test-field-utils + NOEXEPREFIX + NOEXESUFFIX + NAME exodus-test-field-utils + COMM mpi serial + NUM_MPI_PROCS 1 + ) + IF (NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") TRIBITS_ADD_TEST( testall diff --git a/packages/seacas/libraries/exodus/test/test-add-assembly.dmp b/packages/seacas/libraries/exodus/test/test-add-assembly.dmp index 1f261ef724..e8c5d69146 100644 --- a/packages/seacas/libraries/exodus/test/test-add-assembly.dmp +++ b/packages/seacas/libraries/exodus/test/test-add-assembly.dmp @@ -91,7 +91,6 @@ variables: // global attributes: :file_size = 1 ; :title = "This is a test" ; - :SOLID_MODEL = "STEP-X-43-1547836-Rev 0" ; data: time_whole = 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1 ; diff --git a/packages/seacas/libraries/exodus/test/test-field-utils.c b/packages/seacas/libraries/exodus/test/test-field-utils.c new file mode 100644 index 0000000000..9c8ae5aa83 --- /dev/null +++ b/packages/seacas/libraries/exodus/test/test-field-utils.c @@ -0,0 +1,74 @@ +#include +#include +#undef NDEBUG +#include + +#define SIZE(X) sizeof(X) / sizeof(X[0]) + +int main(int argc, char **argv) +{ + ex_field_type types[] = {EX_FIELD_TYPE_USER_DEFINED, + EX_FIELD_TYPE_SEQUENCE, + EX_QUADRATURE, + EX_BASIS, + EX_SCALAR, + EX_VECTOR_1D, + EX_VECTOR_2D, + EX_VECTOR_3D, + EX_QUATERNION_2D, + EX_QUATERNION_3D, + EX_FULL_TENSOR_12, + EX_FULL_TENSOR_16, + EX_FULL_TENSOR_22, + EX_FULL_TENSOR_32, + EX_FULL_TENSOR_36, + EX_SYM_TENSOR_10, + EX_SYM_TENSOR_11, + EX_SYM_TENSOR_13, + EX_SYM_TENSOR_21, + EX_SYM_TENSOR_31, + EX_SYM_TENSOR_33, + EX_ASYM_TENSOR_01, + EX_ASYM_TENSOR_02, + EX_ASYM_TENSOR_03, + EX_MATRIX_2X2, + EX_MATRIX_3X3, + EX_FIELD_TYPE_INVALID}; + + int num_enum = SIZE(types); + for (int i = 0; i < num_enum; i++) { + const char *string_type = ex_field_type_enum_to_string(types[i]); + ex_field_type return_type = ex_string_to_field_type_enum(string_type); + assert(return_type == types[i]); + } + + for (int i = 0; i < num_enum; i++) { + struct ex_field field = (ex_field){.entity_type = EX_ELEM_BLOCK, + .entity_id = 100, + .name = "Testing", + .type = {types[i]}, + .nesting = 1, + .component_separator[0] = '_'}; + + int cardinality = ex_field_cardinality(types[i]); + // Make sure the numeric suffices are formatted correctly "00", "01", ... "10" -- all same + // width. + if (cardinality == -1 && (types[i] == EX_FIELD_TYPE_SEQUENCE || types[i] == EX_BASIS)) { + field.cardinality[0] = 10; + cardinality = 10; + } + if (cardinality > 0 && types[i] != EX_SCALAR) { + fprintf(stderr, "%s (%s) components: ", ex_field_type_enum_to_string(field.type[0]), + ex_field_type_name(field.type[0])); + for (int j = 1; j <= cardinality; j++) { + fprintf(stderr, "%s%c", ex_field_component_suffix(&field, 0, j), + j == cardinality ? ' ' : ','); + } + fprintf(stderr, "\n"); + } + else { + fprintf(stderr, "%s (%s)\n", ex_field_type_enum_to_string(field.type[0]), + ex_field_type_name(field.type[0])); + } + } +} diff --git a/packages/seacas/libraries/exodus/test/testall.in b/packages/seacas/libraries/exodus/test/testall.in index 45d0a2e86f..889cf15434 100755 --- a/packages/seacas/libraries/exodus/test/testall.in +++ b/packages/seacas/libraries/exodus/test/testall.in @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# Copyright(C) 1999-2021, 2023 National Technology & Engineering Solutions +# Copyright(C) 1999-2021, 2023, 2024 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. # @@ -41,7 +41,7 @@ ${PREFIX} ${BINDIR}/testwt${SUFFIX} >> test.output ret_status=$((ret_status+$?)) # Filter out the "maximum_name_length" attribute. Moves around in ncdump output for nc4 vs nc3 ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee testwt.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end testwt, status = $ret_status" >> test.output echo "testrd - single precision read test..." @@ -55,7 +55,7 @@ echo "begin testcp_ss" >> test.output ${PREFIX} ${BINDIR}/testcp${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 testcp.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testcp_ss.dmp | tee testcp_ss.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testcp_ss, status = $ret_status" >> test.output echo "testcp_sd - single-to-double precision copy test..." @@ -63,7 +63,7 @@ echo "begin testcp_sd" >> test.output ${PREFIX} ${BINDIR}/testcpd${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 testcpd.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testcp_sd.dmp | tee testcp_sd.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testcp_sd, status = $ret_status" >> test.output echo "testcp_nl - normal_model to large_model single precision copy test..." @@ -71,7 +71,7 @@ echo "begin testcp_nl" >> test.output ${PREFIX} ${BINDIR}/testcp_nl${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 testcp_nl.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testcp_nl.dmp | tee testcp_nl.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testcp_nl, status = $ret_status" >> test.output echo "testcp_transient - copy mesh and transient data..." @@ -79,7 +79,7 @@ echo "begin testcp_tran" >> test.output ${PREFIX} ${BINDIR}/testcp_tran${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 testcp.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testcp_tran.dmp | tee testcp_tran.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testcp_tran, status = $ret_status" >> test.output echo "testwt_clb - single precision write test using concatenated puts..." @@ -87,7 +87,7 @@ echo "begin testwt_clb" >> test.output ${PREFIX} ${BINDIR}/testwt_clb${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/test_clb.dmp | tee testwt_clb.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwt_clb, status = $ret_status" >> test.output echo "testwtd - double precision write test..." @@ -95,7 +95,7 @@ echo "begin testwtd" >> test.output ${PREFIX} ${BINDIR}/testwtd${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testd.dmp | tee testwtd.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwtd, status = $ret_status" >> test.output echo "testrdd - double precision read test..." @@ -109,7 +109,7 @@ echo "begin testcp_dd" >> test.output ${PREFIX} ${BINDIR}/testcpd${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 testcpd.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testcp_dd.dmp | tee testcp_dd.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testcp_dd, status = $ret_status" >> test.output echo "testcp_ds - double-to-single precision copy test..." @@ -117,7 +117,7 @@ echo "begin testcp_ds" >> test.output ${PREFIX} ${BINDIR}/testcp${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 testcp.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testcp_ds.dmp | tee testcp_ds.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testcp_ds, status = $ret_status" >> test.output echo "testwtd-to-f - double precision compute, convert to single precision on disk..." @@ -125,7 +125,7 @@ echo "begin testwtd-to-f" >> test.output ${PREFIX} ${BINDIR}/testwtd-to-f${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee testwtd-to-f.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end testwtd-to-f, status = $ret_status" >> test.output echo "testwt1 - single precision write files with several side sets..." @@ -134,7 +134,7 @@ echo "begin testwt1" >> test.output ${PREFIX} ${BINDIR}/testwt1${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/test1.dmp | tee testwt1.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwt1, status = $ret_status" >> test.output echo "testrd1 - single precision read test of a file with several side sets..." @@ -151,7 +151,7 @@ echo "begin testwt_ss" >> test.output ${PREFIX} ${BINDIR}/testwt_ss${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testwt_ss.dmp | tee testwt_ss.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwt_ss, status = $ret_status" >> test.output echo "testrd_ss - read test of a file with several side sets..." @@ -166,9 +166,9 @@ echo "begin testwt2" >> test.output ${PREFIX} ${BINDIR}/testwt2${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/test2-1.dmp | tee testwt2-1.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) ${NCDUMP} -d5,5 test2.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/test2-2.dmp | tee testwt2-2.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwt2, status = $ret_status" >> test.output echo "testrdwt - read from one and write to another (simultaneously open) file..." @@ -176,7 +176,7 @@ echo "begin testrdwt" >> test.output ${PREFIX} ${BINDIR}/testrdwt${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test2.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/test2.dmp | tee testrdwt.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testrdwt, status = $ret_status" >> test.output echo "testwt_nc - write x y z components of coordinates separately..." @@ -185,7 +185,7 @@ ${PREFIX} ${BINDIR}/testwt_nc${SUFFIX} >> test.output ret_status=$((ret_status+$?)) # Filter out the "maximum_name_length" attribute. Moves around in ncdump output for nc4 vs nc3 ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee testwt_nc.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end testwt_nc, status = $ret_status" >> test.output echo "testrd_nc - read x y z components of coordinates separately..." @@ -199,7 +199,7 @@ echo "begin testwt-zeron" >> test.output ${PREFIX} ${BINDIR}/testwt-zeron${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testwt-zeron.dmp | tee testwt-zeron.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwt-zeron, status = $ret_status" >> test.output echo "testrd - read test of file with zero nodes and elements..." @@ -213,7 +213,7 @@ echo "begin testwt-zeroe" >> test.output ${PREFIX} ${BINDIR}/testwt-zeroe${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testwt-zeroe.dmp | tee testwt-zeroe.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwt-zeroe, status = $ret_status" >> test.output echo "testrd - read test of file with zero elements..." @@ -233,7 +233,7 @@ echo "begin testwt-nsided" >> test.output ${PREFIX} ${BINDIR}/testwt-nsided${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test-nsided.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testwt-nsided.dmp | tee testwt-nsided.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwt-nsided, status = $ret_status" >> test.output echo "testrd-nsided - read file with nsided elements..." @@ -247,7 +247,7 @@ echo "begin testwt-nfaced" >> test.output ${PREFIX} ${BINDIR}/testwt-nfaced${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test-nfaced.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size| ${DIFF} - ${SRCDIR}/testwt-nfaced.dmp | tee testwt-nfaced.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) echo "end testwt-nfaced, status = $ret_status" >> test.output echo "testrd-nfaced - read file with nfaced elements..." @@ -262,7 +262,7 @@ echo "begin testwt-long-name" >> test.output ${PREFIX} ${BINDIR}/testwt-long-name${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | ${DIFF} - ${SRCDIR}/testwt-long-name.dmp | tee testwt-long-name.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end testwt-long-name, status = $ret_status" >> test.output echo "testrd - read long name file truncating to 32 characters on read..." @@ -295,7 +295,7 @@ ${PREFIX} ${BINDIR}/testwt-results${SUFFIX} >> test.output ret_status=$((ret_status+$?)) # Filter out the "maximum_name_length" attribute. Moves around in ncdump output for nc4 vs nc3 ${NCDUMP} -d5,5 test-results.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | ${DIFF} - ${SRCDIR}/testwt-results.dmp | tee testwt-results.re -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end testwt-results, status = $ret_status" >> test.output echo "testwt-assembly - write file containing assembly data" @@ -303,8 +303,8 @@ echo "begin testwt-assembly" >> test.output ${PREFIX} ${BINDIR}/testwt-assembly${SUFFIX} >> test.output ret_status=$((ret_status+$?)) # Filter out the "maximum_name_length" attribute. Moves around in ncdump output for nc4 vs nc3 -${NCDUMP} -d5,5 test-assembly.exo | grep -v version | grep -v int64_status| grep -v floating_point | grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | ${DIFF} - ${SRCDIR}/testwt-assembly.dmp | tee testwt-assembly.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) +${NCDUMP} -d5,5 test-assembly.exo | grep -v version | grep -v int64_status| grep -v floating_point | grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | grep -v ":SOLID_MODEL" | ${DIFF} - ${SRCDIR}/testwt-assembly.dmp | tee testwt-assembly.res +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[8]})) echo "end testwt-assembly, status = $ret_status" >> test.output echo "testrd-assembly - read file containing assembly data" @@ -318,8 +318,8 @@ echo "begin test-add-assembly" >> test.output ${PREFIX} ${BINDIR}/test-add-assembly${SUFFIX} >> test.output ret_status=$((ret_status+$?)) # Filter out the "maximum_name_length" attribute. Moves around in ncdump output for nc4 vs nc3 -${NCDUMP} -d5,5 test-assembly.exo | grep -v version | grep -v int64_status | grep -v floating_point | grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | ${DIFF} - ${SRCDIR}/test-add-assembly.dmp | tee test-add-assembly.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) +${NCDUMP} -d5,5 test-assembly.exo | grep -v version | grep -v int64_status | grep -v floating_point | grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | grep -v ":SOLID_MODEL" | ${DIFF} - ${SRCDIR}/test-add-assembly.dmp | tee test-add-assembly.res +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[8]})) echo "end test-add-assembly, status = $ret_status" >> test.output echo "testwt-blob - write file containing blob data" @@ -327,8 +327,8 @@ echo "begin testwt-blob" >> test.output ${PREFIX} ${BINDIR}/testwt-blob${SUFFIX} >> test.output ret_status=$((ret_status+$?)) # Filter out the "maximum_name_length" attribute. Moves around in ncdump output for nc4 vs nc3 -${NCDUMP} -d5,5 test-blob.exo | grep -v version | grep -v int64_status | grep -v floating_point | grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | ${DIFF} - ${SRCDIR}/testwt-blob.dmp | tee testwt-blob.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) +${NCDUMP} -d5,5 test-blob.exo | grep -v version | grep -v int64_status | grep -v floating_point | grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | grep -v ":SOLID_MODEL" | ${DIFF} - ${SRCDIR}/testwt-blob.dmp | tee testwt-blob.res +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[8]})) echo "end testwt-blob, status = $ret_status" >> test.output echo "testrd-blob - read file containing blob data" @@ -343,7 +343,7 @@ ${PREFIX} ${BINDIR}/testwt-oned${SUFFIX} >> test.output ret_status=$((ret_status+$?)) # Filter out the "maximum_name_length" attribute. Moves around in ncdump output for nc4 vs nc3 ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | ${DIFF} - ${SRCDIR}/testwt-oned.dmp | tee testwt-oned.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end testwt-oned, status = $ret_status" >> test.output echo "testrd - read test of 1D file..." @@ -352,6 +352,29 @@ ${PREFIX} ${BINDIR}/testrd${SUFFIX} | grep -v version | ${DIFF} - ${SRCDIR}/test ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[2]})) echo "end testrd 1D, status = $ret_status" >> test.output +echo "testwt-field-metadata - write mesh with enhanced field metadata..." +echo "begin testwt-field-metadata" >> test.output +${PREFIX} ${BINDIR}/testwt-field-metadata${SUFFIX} >> test.output +ret_status=$((ret_status+$?)) +# Filter out the "maximum_name_length" attribute. Moves around in ncdump output for nc4 vs nc3 +${NCDUMP} -d5,5 test-field-metadata.exo | grep @ |sort | ${DIFF} - ${SRCDIR}/testwt-field-metadata.dmp | tee testwt-field-metadata.res +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) +echo "end testwt-field-metadata, status = $ret_status" >> test.output + +# NOTE: netcdf-4 can put attributes in different order, so in that +# case, we only test the return status of the application, not diff +# the output. +echo "testrd-field-metadata - read test of enhanced field metadata file..." +echo "begin testrd-field-metadata" >> test.output +if [ "$1" == "netcdf4" ]; then + ${PREFIX} ${BINDIR}/testrd-field-metadata${SUFFIX} | tee testrd-field-metadata.res + ret_status=$((ret_status+$?)) +else + ${PREFIX} ${BINDIR}/testrd-field-metadata${SUFFIX} | ${DIFF} - ${SRCDIR}/testrd-field-metadata.dmp | tee testrd-field-metadata.res + ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[1]})) +fi +echo "end testrd-field-metadata, status = $ret_status" >> test.output + if [ "$THREAD_SAFE" == "YES" ]; then echo "test_ts_nvar - each thread writes data for a single nodal variable..." @@ -359,13 +382,13 @@ echo "begin test_ts_nvar" >> test.output ${PREFIX} ${BINDIR}/test_ts_nvar${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | ${DIFF} - ${SRCDIR}/test_ts_nvar.dmp | tee testwt-long-name.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end test_ts_nvar, status = $ret_status" >> test.output echo "test_ts_nvar_rd - each thread reads data for a single nodal variable..." echo "begin test_ts_nvar_rd" >> test.output ${PREFIX} ${BINDIR}/test_ts_nvar_rd${SUFFIX} | ${DIFF} - ${SRCDIR}/test_ts_nvar_rd.dmp | tee test_ts_nvar_rd.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[2]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[1]})) echo "end test_ts_nvar_rd, status = $ret_status" >> test.output @@ -374,7 +397,7 @@ echo "begin test_ts_partial_nvar" >> test.output ${PREFIX} ${BINDIR}/test_ts_partial_nvar${SUFFIX} >> test.output ret_status=$((ret_status+$?)) ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v "maximum_name_length" | ${DIFF} - ${SRCDIR}/test_ts_partial_nvar.dmp | tee testwt-long-name.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end test_ts_partial_nvar, status = $ret_status" >> test.output echo "test_ts_partial_nvar_rd - each thread reads data for a single nodal variable..." @@ -391,28 +414,28 @@ ${PREFIX} ${BINDIR}/test_ts_files${SUFFIX} >> test.output ret_status=$((ret_status+$?)) mv test0.exo test.exo ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee test_ts_files0.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) mv test1.exo test.exo ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee test_ts_files1.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) mv test2.exo test.exo ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee test_ts_files2.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) mv test3.exo test.exo ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee test_ts_files3.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) mv test4.exo test.exo ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee test_ts_files4.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) mv test5.exo test.exo ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee test_ts_files5.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) mv test6.exo test.exo ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee test_ts_files6.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) mv test7.exo test.exo ${NCDUMP} -d5,5 test.exo | grep -v version | grep -v int64_status| grep -v _FillValue |grep -v word_size|grep -v maximum_name_length | ${DIFF} - ${SRCDIR}/test.dmp | tee test_ts_files7.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[5]})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[6]})) echo "end test_ts_files, status = $ret_status" >> test.output echo "test_ts_errval - multiple threads calling ex_err and ex_get_err..." diff --git a/packages/seacas/libraries/exodus/test/testrd-field-metadata.c b/packages/seacas/libraries/exodus/test/testrd-field-metadata.c new file mode 100644 index 0000000000..21011c26a3 --- /dev/null +++ b/packages/seacas/libraries/exodus/test/testrd-field-metadata.c @@ -0,0 +1,318 @@ +/* + * Copyright(C) 1999-2024 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 + */ +#undef NDEBUG +#include "exodusII.h" +#include +#include +#include +#include + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +char *my_strdup(const char *s) +{ + size_t slen = strlen(s); + char *result = malloc(slen + 1); + if (result == NULL) { + return NULL; + } + + memcpy(result, s, slen + 1); + return result; +} + +char *my_strsep(char **stringp, const char *delim) +{ + assert(delim != NULL); + char *rv = *stringp; + if (rv) { + assert(stringp != NULL); + assert(*stringp != NULL); + *stringp += strcspn(*stringp, delim); + if (**stringp) + *(*stringp)++ = '\0'; + else + *stringp = NULL; + } + return rv; +} + +#define EXCHECK(funcall) \ + do { \ + int error = (funcall); \ + printf("after %s, error = %d\n", TOSTRING(funcall), error); \ + if (error != EX_NOERR) { \ + fprintf(stderr, "Error calling %s\n", TOSTRING(funcall)); \ + ex_close(exoid); \ + exit(-1); \ + } \ + } while (0) + +static char *get_type_name(char *type_name, size_t which) +{ + if (type_name != NULL && type_name[0] != '\0') { + char *string = my_strdup(type_name); + char *tofree = string; + char *token = my_strsep(&string, ","); + for (int i = 0; i < which; i++) { + token = my_strsep(&string, ","); + } + if (token != NULL) { + static char tmp_type_name[256 + 1]; + ex_copy_string(tmp_type_name, token, 256); + free(tofree); + return tmp_type_name; + } + free(tofree); + } + return NULL; +} + +static void get_field_cardinality(ex_field *field, ex_basis *basis, int bas_cnt, + ex_quadrature *quad, int quad_cnt) +{ + for (int j = 0; j < field->nesting; j++) { + if (field->cardinality[j] == 0) { + if (field->type[j] == EX_BASIS) { + int found = 0; + char *basis_type = get_type_name(field->type_name, j); + if (basis != NULL) { + for (int k = 0; k < bas_cnt; k++) { + if (strcmp(basis[k].name, basis_type) == 0) { + field->cardinality[j] = basis[k].cardinality; + found = 1; + break; + } + } + } + if (found == 0) { + fprintf(stderr, "ERROR: Could not find basis named `%s` for field `%s`\n", basis_type, + field->name); + } + } + else if (field->type[j] == EX_QUADRATURE) { + int found = 0; + char *quad_type = get_type_name(field->type_name, j); + if (quad != NULL) { + for (int k = 0; k < quad_cnt; k++) { + if (strcmp(quad[k].name, quad_type) == 0) { + field->cardinality[j] = quad[k].cardinality; + found = 1; + break; + } + } + } + if (found == 0) { + fprintf(stderr, "ERROR: Could not find quadrature named `%s` for field `%s`\n", quad_type, + field->name); + } + } + else { + field->cardinality[j] = ex_field_cardinality(field->type[j]); + } + } + } +} + +static void print_basis_metadata(ex_basis *basis, size_t num_basis) +{ + for (size_t j = 0; j < num_basis; j++) { + printf("\nBasis Metadata: Name: `%s`, Cardinality: %d\n", basis[j].name, basis[j].cardinality); + printf("ordinal,\t subc: _dim\t_ordinal\t_dof_ordinal\t_num_dof\t xi eta zeta\n"); + for (int i = 0; i < basis[j].cardinality; i++) { + double xi = basis[j].xi != NULL ? basis[j].xi[i] : 0.0; + double eta = basis[j].eta != NULL ? basis[j].eta[i] : 0.0; + double zeta = basis[j].zeta != NULL ? basis[j].zeta[i] : 0.0; + printf("%8d\t%8d\t%8d\t%8d\t%8d\t%6.3f\t%6.3f\t%6.3f\n", i, basis[j].subc_dim[i], + basis[j].subc_ordinal[i], basis[j].subc_dof_ordinal[i], basis[j].subc_num_dof[i], xi, + eta, zeta); + } + } +} + +static void print_quad_metadata(ex_quadrature *quad, size_t num_quad) +{ + for (size_t j = 0; j < num_quad; j++) { + printf("\nQuadrature Metadata: Name: `%s`, Cardinality: %d\n", quad[j].name, + quad[j].cardinality); + printf("ordinal,\t xi eta zeta weight\n"); + for (int i = 0; i < quad[j].cardinality; i++) { + double xi = quad[j].xi != NULL ? quad[j].xi[i] : 0.0; + double eta = quad[j].eta != NULL ? quad[j].eta[i] : 0.0; + double zeta = quad[j].zeta != NULL ? quad[j].zeta[i] : 0.0; + double wgt = quad[j].weight != NULL ? quad[j].weight[i] : 1.0; + printf("%8d\t%6.3f\t%6.3f\t%6.3f\t%6.3f\n", i, xi, eta, zeta, wgt); + } + } +} + +static void print_field_metadata(ex_field *field) +{ + printf("\n"); + printf("Field Metadata: Name: `%s`, Nesting: %d\n", field->name, field->nesting); + for (int j = 0; j < field->nesting; j++) { + char sep = field->component_separator[j] == 0 ? ' ' : field->component_separator[j]; + if (field->type[j] == EX_BASIS) { + char *basis_type = get_type_name(field->type_name, j); + printf("\tNesting level: %d, Type: %s (%s), Cardinality: %d, Separator: \"%c\"\n", j, + ex_field_type_enum_to_string(field->type[j]), basis_type, field->cardinality[j], sep); + } + else if (field->type[j] == EX_QUADRATURE) { + char *quad_type = get_type_name(field->type_name, j); + printf("\tNesting level: %d, Type: %s (%s), Cardinality: %d, Separator: \"%c\"\n", j, + ex_field_type_enum_to_string(field->type[j]), quad_type, field->cardinality[j], sep); + } + else { + printf("\tNesting level: %d, Type: %s, Cardinality: %d, Separator: \"%c\"\n", j, + ex_field_type_enum_to_string(field->type[j]), field->cardinality[j], sep); + } + if (field->type[0] == EX_FIELD_TYPE_USER_DEFINED) { + printf("\tUser-defined suffices: %s\n", field->suffices); + } + } +} + +static void print_full_field_names(ex_field *field) +{ + int component[EX_MAX_FIELD_NESTING]; + if (field->nesting == 1) { + for (int jj = 1; jj <= field->cardinality[0]; jj++) { + component[0] = jj; + const char *name = ex_component_field_name(field, component); + printf("\t\tComponent %d, Full name = %s\n", jj, name); + } + } + else if (field->nesting == 2) { + for (int kk = 1; kk <= field->cardinality[1]; kk++) { + for (int jj = 1; jj <= field->cardinality[0]; jj++) { + component[0] = jj; + component[1] = kk; + const char *name = ex_component_field_name(field, component); + printf("\t\tComponent %d %d, Full name = %s\n", jj, kk, name); + } + } + } +#if EX_MAX_FIELD_NESTING > 2 + else if (field->nesting == 3) { + for (int ii = 1; ii <= field->cardinality[2]; ii++) { + for (int kk = 1; kk <= field->cardinality[1]; kk++) { + for (int jj = 1; jj <= field->cardinality[0]; jj++) { + component[0] = jj; + component[1] = kk; + component[2] = ii; + const char *name = ex_component_field_name(field, component); + printf("\t\tComponent %d %d %d, Full name = %s\n", jj, kk, ii, name); + } + } + } + } +#endif +#if EX_MAX_FIELD_NESTING > 3 + else if (field->nesting == 4) { + for (int mm = 1; mm <= field->cardinality[3]; mm++) { + for (int ii = 1; ii <= field->cardinality[2]; ii++) { + for (int kk = 1; kk <= field->cardinality[1]; kk++) { + for (int jj = 1; jj <= field->cardinality[0]; jj++) { + component[0] = jj; + component[1] = kk; + component[2] = ii; + component[3] = mm; + const char *name = ex_component_field_name(field, component); + printf("\t\tComponent %d %d %d %d, Full name = %s\n", jj, kk, ii, mm, name); + } + } + } + } + } +#endif +} + +int main(int argc, char **argv) +{ + ex_opts(EX_VERBOSE | EX_ABORT); + + /* open EXODUS II files */ + float version; + int CPU_word_size = 0; /* sizeof(float) */ + int IO_word_size = 0; /* use what is stored in file */ + + int exoid = ex_open("test-field-metadata.exo", EX_READ, &CPU_word_size, &IO_word_size, &version); + + printf("\nafter ex_open\n"); + if (exoid < 0) { + exit(1); + } + + // ------------------------------------------------------------------------ + int quad_cnt = 0; + ex_quadrature *quad = NULL; + EXCHECK(ex_get_quadrature(exoid, &quad, &quad_cnt)); + print_quad_metadata(quad, quad_cnt); + + // ------------------------------------------------------------------------ + int bas_cnt = 0; + ex_basis *basis = NULL; + EXCHECK(ex_get_basis(exoid, &basis, &bas_cnt)); + print_basis_metadata(basis, bas_cnt); + + /* Check for nodal fields... */ + { + int fld_cnt = ex_get_field_metadata_count(exoid, EX_NODAL, 0); + assert(fld_cnt == 2); + ex_field fields[2] = {{.entity_type = EX_NODAL}, {.entity_type = EX_NODAL}}; + EXCHECK(ex_get_field_metadata(exoid, fields)); + + for (int i = 0; i < fld_cnt; i++) { + get_field_cardinality(&fields[i], NULL, 0, NULL, 0); + print_field_metadata(&fields[i]); + print_full_field_names(&fields[i]); + } + } + + { + int fld_cnt = ex_get_field_metadata_count(exoid, EX_ELEM_BLOCK, 10); + assert(fld_cnt == 2); + ex_field fields[2] = {{.entity_id = 10, .entity_type = EX_ELEM_BLOCK}, + {.entity_id = 10, .entity_type = EX_ELEM_BLOCK}}; + EXCHECK(ex_get_field_metadata(exoid, fields)); + + for (int i = 0; i < fld_cnt; i++) { + get_field_cardinality(&fields[i], NULL, 0, NULL, 0); + print_field_metadata(&fields[i]); + print_full_field_names(&fields[i]); + } + } + { + int fld_cnt = ex_get_field_metadata_count(exoid, EX_ELEM_BLOCK, 11); + assert(fld_cnt == 3); + ex_field fields[3] = {{.entity_id = 11, .entity_type = EX_ELEM_BLOCK}, + {.entity_id = 11, .entity_type = EX_ELEM_BLOCK}, + {.entity_id = 11, .entity_type = EX_ELEM_BLOCK}}; + EXCHECK(ex_get_field_metadata(exoid, fields)); + + for (int i = 0; i < fld_cnt; i++) { + get_field_cardinality(&fields[i], basis, bas_cnt, quad, quad_cnt); + print_field_metadata(&fields[i]); + print_full_field_names(&fields[i]); + } + } + + // Now, deallocate any memory allocated on the `basis` struct. + EXCHECK(ex_initialize_basis_struct(basis, bas_cnt, -1)); + EXCHECK(ex_initialize_quadrature_struct(quad, quad_cnt, -1)); + free(basis); + free(quad); + + int fld_cnt = ex_get_field_metadata_count(exoid, EX_ELEM_BLOCK, 12); + assert(fld_cnt == 0); + + int error = ex_close(exoid); + printf("\nafter ex_close, error = %3d\n", error); + return 0; +} diff --git a/packages/seacas/libraries/exodus/test/testrd-field-metadata.dmp b/packages/seacas/libraries/exodus/test/testrd-field-metadata.dmp new file mode 100644 index 0000000000..4e54650f27 --- /dev/null +++ b/packages/seacas/libraries/exodus/test/testrd-field-metadata.dmp @@ -0,0 +1,125 @@ + +after ex_open +after ex_get_quadrature(exoid, &quad, &quad_cnt), error = 0 + +Quadrature Metadata: Name: `2x2x2`, Cardinality: 8 +ordinal, xi eta zeta weight + 0 -0.577 -0.577 -0.577 1.000 + 1 0.577 -0.577 -0.577 1.000 + 2 -0.577 0.577 -0.577 1.000 + 3 0.577 0.577 -0.577 1.000 + 4 -0.577 -0.577 0.577 1.000 + 5 0.577 -0.577 0.577 1.000 + 6 -0.577 0.577 0.577 1.000 + 7 0.577 0.577 0.577 1.000 + +Quadrature Metadata: Name: `1x2x1`, Cardinality: 2 +ordinal, xi eta zeta weight + 0 -0.500 0.500 -0.500 1.000 + 1 0.500 -0.500 0.500 1.000 +after ex_get_basis(exoid, &basis, &bas_cnt), error = 0 + +Basis Metadata: Name: `HGRAD_QUAD_C2_FEM`, Cardinality: 9 +ordinal, subc: _dim _ordinal _dof_ordinal _num_dof xi eta zeta + 0 0 0 0 1 -1.000 -1.000 0.000 + 1 0 1 0 1 1.000 -1.000 0.000 + 2 0 2 0 1 1.000 1.000 0.000 + 3 0 3 0 1 -1.000 1.000 0.000 + 4 1 0 0 1 0.000 -1.000 0.000 + 5 1 1 0 1 1.000 0.000 0.000 + 6 1 2 0 1 0.000 1.000 0.000 + 7 1 3 0 1 -1.000 0.000 0.000 + 8 2 0 0 1 0.000 0.000 0.000 + +Basis Metadata: Name: `TESTING_SECOND_BASIS`, Cardinality: 3 +ordinal, subc: _dim _ordinal _dof_ordinal _num_dof xi eta zeta + 0 0 0 0 1 -1.000 -1.000 1.000 + 1 0 1 0 1 1.000 -1.000 -1.000 + 2 0 2 0 1 1.000 1.000 1.000 +after ex_get_field_metadata(exoid, fields), error = 0 + +Field Metadata: Name: `Disp`, Nesting: 1 + Nesting level: 0, Type: EX_VECTOR_3D, Cardinality: 3, Separator: " " + Component 1, Full name = DispX + Component 2, Full name = DispY + Component 3, Full name = DispZ + +Field Metadata: Name: `Velocity`, Nesting: 1 + Nesting level: 0, Type: EX_VECTOR_3D, Cardinality: 3, Separator: "%" + Component 1, Full name = Velocity%X + Component 2, Full name = Velocity%Y + Component 3, Full name = Velocity%Z +after ex_get_field_metadata(exoid, fields), error = 0 + +Field Metadata: Name: `Disp`, Nesting: 1 + Nesting level: 0, Type: EX_VECTOR_3D, Cardinality: 3, Separator: " " + Component 1, Full name = DispX + Component 2, Full name = DispY + Component 3, Full name = DispZ + +Field Metadata: Name: `Velocity`, Nesting: 1 + Nesting level: 0, Type: EX_VECTOR_3D, Cardinality: 3, Separator: "%" + Component 1, Full name = Velocity%X + Component 2, Full name = Velocity%Y + Component 3, Full name = Velocity%Z +after ex_get_field_metadata(exoid, fields), error = 0 + +Field Metadata: Name: `Gradient`, Nesting: 2 + Nesting level: 0, Type: EX_VECTOR_3D, Cardinality: 3, Separator: "-" + Nesting level: 1, Type: EX_BASIS (HGRAD_QUAD_C2_FEM), Cardinality: 9, Separator: "$" + Component 1 1, Full name = Gradient-X$0 + Component 2 1, Full name = Gradient-Y$0 + Component 3 1, Full name = Gradient-Z$0 + Component 1 2, Full name = Gradient-X$1 + Component 2 2, Full name = Gradient-Y$1 + Component 3 2, Full name = Gradient-Z$1 + Component 1 3, Full name = Gradient-X$2 + Component 2 3, Full name = Gradient-Y$2 + Component 3 3, Full name = Gradient-Z$2 + Component 1 4, Full name = Gradient-X$3 + Component 2 4, Full name = Gradient-Y$3 + Component 3 4, Full name = Gradient-Z$3 + Component 1 5, Full name = Gradient-X$4 + Component 2 5, Full name = Gradient-Y$4 + Component 3 5, Full name = Gradient-Z$4 + Component 1 6, Full name = Gradient-X$5 + Component 2 6, Full name = Gradient-Y$5 + Component 3 6, Full name = Gradient-Z$5 + Component 1 7, Full name = Gradient-X$6 + Component 2 7, Full name = Gradient-Y$6 + Component 3 7, Full name = Gradient-Z$6 + Component 1 8, Full name = Gradient-X$7 + Component 2 8, Full name = Gradient-Y$7 + Component 3 8, Full name = Gradient-Z$7 + Component 1 9, Full name = Gradient-X$8 + Component 2 9, Full name = Gradient-Y$8 + Component 3 9, Full name = Gradient-Z$8 + +Field Metadata: Name: `Curl`, Nesting: 1 + Nesting level: 0, Type: EX_QUADRATURE (2x2x2), Cardinality: 8, Separator: "@" + Component 1, Full name = Curl@0 + Component 2, Full name = Curl@1 + Component 3, Full name = Curl@2 + Component 4, Full name = Curl@3 + Component 5, Full name = Curl@4 + Component 6, Full name = Curl@5 + Component 7, Full name = Curl@6 + Component 8, Full name = Curl@7 + +Field Metadata: Name: `Species`, Nesting: 2 + Nesting level: 0, Type: EX_FIELD_TYPE_USER_DEFINED, Cardinality: 4, Separator: "_" + User-defined suffices: h2o,gas,ch4,methane + Nesting level: 1, Type: EX_QUADRATURE (1x2x1), Cardinality: 2, Separator: "-" + User-defined suffices: h2o,gas,ch4,methane + Component 1 1, Full name = Species_h2o-0 + Component 2 1, Full name = Species_gas-0 + Component 3 1, Full name = Species_ch4-0 + Component 4 1, Full name = Species_methane-0 + Component 1 2, Full name = Species_h2o-1 + Component 2 2, Full name = Species_gas-1 + Component 3 2, Full name = Species_ch4-1 + Component 4 2, Full name = Species_methane-1 +after ex_initialize_basis_struct(basis, bas_cnt, -1), error = 0 +after ex_initialize_quadrature_struct(quad, quad_cnt, -1), error = 0 + +after ex_close, error = 0 diff --git a/packages/seacas/libraries/exodus/test/testwt-assembly.dmp b/packages/seacas/libraries/exodus/test/testwt-assembly.dmp index 9f54305ddc..57ce632ef7 100644 --- a/packages/seacas/libraries/exodus/test/testwt-assembly.dmp +++ b/packages/seacas/libraries/exodus/test/testwt-assembly.dmp @@ -85,7 +85,6 @@ variables: // global attributes: :file_size = 1 ; :title = "This is a test" ; - :SOLID_MODEL = "STEP-X-43-1547836-Rev 0" ; data: time_whole = 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1 ; diff --git a/packages/seacas/libraries/exodus/test/testwt-blob.c b/packages/seacas/libraries/exodus/test/testwt-blob.c index 1bbb9f1010..d9152fdeab 100644 --- a/packages/seacas/libraries/exodus/test/testwt-blob.c +++ b/packages/seacas/libraries/exodus/test/testwt-blob.c @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2021 National Technology & Engineering Solutions + * Copyright(C) 1999-2021, 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. * diff --git a/packages/seacas/libraries/exodus/test/testwt-blob.dmp b/packages/seacas/libraries/exodus/test/testwt-blob.dmp index 90b214dd06..6d32177ea0 100644 --- a/packages/seacas/libraries/exodus/test/testwt-blob.dmp +++ b/packages/seacas/libraries/exodus/test/testwt-blob.dmp @@ -44,7 +44,6 @@ variables: // global attributes: :file_size = 1 ; :title = "This is a test" ; - :SOLID_MODEL = "STEP-X-43-1547836-Rev 0" ; data: time_whole = 0.01, 0.02, 0.03, 0.04 ; diff --git a/packages/seacas/libraries/exodus/test/testwt-field-metadata.c b/packages/seacas/libraries/exodus/test/testwt-field-metadata.c new file mode 100644 index 0000000000..642aa5ba5f --- /dev/null +++ b/packages/seacas/libraries/exodus/test/testwt-field-metadata.c @@ -0,0 +1,272 @@ +/* + * Copyright(C) 2022, 2023, 2024 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 + */ + +#undef NDEBUG +#include +#include +#include +#include +#include + +#include "exodusII.h" + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +#define EXCHECK(funcall) \ + do { \ + int error = (funcall); \ + printf("after %s, error = %d\n", TOSTRING(funcall), error); \ + if (error != EX_NOERR) { \ + fprintf(stderr, "Error calling %s\n", TOSTRING(funcall)); \ + ex_close(exoid); \ + exit(-1); \ + } \ + } while (0) + +int main(int argc, char **argv) +{ + ex_opts(EX_VERBOSE); + + /* Specify compute and i/o word size */ + int CPU_word_size = 8; + int IO_word_size = 8; + + /* create EXODUS II file */ + int exoid = ex_create("test-field-metadata.exo", /* filename path */ + EX_CLOBBER, /* create mode */ + &CPU_word_size, /* CPU double word size in bytes */ + &IO_word_size); /* I/O double word size in bytes */ + printf("after ex_create for test.exo, exoid = %d\n", exoid); + printf(" cpu word size: %d io word size: %d\n", CPU_word_size, IO_word_size); + + int num_elem_blk = 3; + { + ex_init_params par = {.num_dim = 3, + .num_nodes = 1, + .num_elem = 3, + .num_elem_blk = num_elem_blk, + .num_node_sets = 0, + .num_side_sets = 0, + .num_assembly = 0}; + + char *title = "This is a test"; + ex_copy_string(par.title, title, MAX_LINE_LENGTH + 1); + EXCHECK(ex_put_init_ext(exoid, &par)); + } + + double coord[] = {0.0}; + EXCHECK(ex_put_coord(exoid, coord, coord, coord)); + + /* ======================================================================== */ + /* write element block parameters */ + struct ex_block blocks[num_elem_blk]; + for (int i = 0; i < num_elem_blk; i++) { + blocks[i] = (ex_block){.type = EX_ELEM_BLOCK, .num_entry = 1, .id = i + 10}; + ex_copy_string(blocks[i].topology, "sphere", MAX_STR_LENGTH + 1); + blocks[i].num_nodes_per_entry = 1; + } + + EXCHECK(ex_put_block_params(exoid, num_elem_blk, blocks)); + + int connect[] = {1}; + for (int i = 0; i < num_elem_blk; i++) { + EXCHECK(ex_put_conn(exoid, EX_ELEM_BLOCK, blocks[i].id, connect, NULL, NULL)); + } + + /* Write element block names */ + for (int i = 0; i < num_elem_blk; i++) { + char block_names[32]; + sprintf(block_names, "block_%c", i + 'A'); + EXCHECK(ex_put_name(exoid, EX_ELEM_BLOCK, blocks[i].id, block_names)); + } + + { + int units[] = {1, 0, 0, -1}; + + EXCHECK(ex_put_integer_attribute(exoid, EX_ELEM_BLOCK, 11, "Units", 4, units)); + EXCHECK(ex_put_text_attribute(exoid, EX_GLOBAL, 0, "SOLID_MODEL", "STEP-X-43-1547836-Rev 0")); + } + + /* ======================================================================== */ + /* Transient Variables */ + char *var_names[] = { + "DispX", "DispY", "DispZ", "Velocity%X", "Velocity%Y", + "Velocity%Z", "Gradient-X$0", "Gradient-Y$0", "Gradient-Z$0", "Gradient-X$1", + "Gradient-Y$1", "Gradient-Z$1", "Gradient-X$2", "Gradient-Y$2", "Gradient-Z$2", + "Gradient-X$3", "Gradient-Y$3", "Gradient-Z$3", "Gradient-X$4", "Gradient-Y$4", + "Gradient-Z$4", "Gradient-X$5", "Gradient-Y$5", "Gradient-Z$5", "Gradient-X$6", + "Gradient-Y$6", "Gradient-Z$6", "Gradient-X$7", "Gradient-Y$7", "Gradient-Z$7", + "Gradient-X$8", "Gradient-Y$8", "Gradient-Z$8", "Curl@1", "Curl@2", + "Curl@3", "Curl@4", "Curl@5", "Curl@6", "Curl@7", + "Curl@8", "Species_h2o-0", "Species_gas-0", "Species_ch4-0", "Species_methane-0", + "Species_h2o-1", "Species_gas-1", "Species_ch4-1", "Species_methane-1"}; + int num_block_vars = sizeof(var_names) / sizeof(var_names[0]); + int num_node_vars = 6; + + EXCHECK(ex_put_variable_param(exoid, EX_ELEM_BLOCK, num_block_vars)); + EXCHECK(ex_put_variable_names(exoid, EX_ELEM_BLOCK, num_block_vars, var_names)); + EXCHECK(ex_put_variable_param(exoid, EX_NODAL, num_node_vars)); + EXCHECK(ex_put_variable_names(exoid, EX_NODAL, num_node_vars, var_names)); + + int vname = 0; + { + struct ex_field field = (ex_field){.entity_type = EX_ELEM_BLOCK, + .entity_id = blocks[0].id, + .name = "Disp", + .type = {EX_VECTOR_3D}, + .nesting = 1, + .component_separator[0] = 0}; + EXCHECK(ex_put_field_metadata(exoid, field)); + + /* Put same field on the nodes... */ + field.entity_type = EX_NODAL; + EXCHECK(ex_put_field_metadata(exoid, field)); + + int cardinality = + field.cardinality[0] != 0 ? field.cardinality[0] : ex_field_cardinality(field.type[0]); + for (int i = 0; i < cardinality; i++) { + const char *name = ex_component_field_name(&field, (int[]){i + 1}); + assert(strcmp(var_names[vname++], name) == 0); + } + } + + { + struct ex_field field = (ex_field){.entity_type = EX_ELEM_BLOCK, + .entity_id = blocks[0].id, + .name = "Velocity", + .type = {EX_VECTOR_3D}, + .nesting = 1, + .component_separator[0] = '%'}; + EXCHECK(ex_put_field_metadata(exoid, field)); + + /* Put same field on the nodes... */ + field.entity_type = EX_NODAL; + EXCHECK(ex_put_field_metadata(exoid, field)); + + int cardinality = + field.cardinality[0] != 0 ? field.cardinality[0] : ex_field_cardinality(field.type[0]); + for (int i = 0; i < cardinality; i++) { + const char *name = ex_component_field_name(&field, (int[]){i + 1}); + assert(strcmp(var_names[vname++], name) == 0); + } + } + + { + double Q = 1.0 / sqrt(3.0); + double xi[] = {-Q, Q, -Q, Q, -Q, Q, -Q, Q}; + double eta[] = {-Q, -Q, Q, Q, -Q, -Q, Q, Q}; + double zeta[] = {-Q, -Q, -Q, -Q, Q, Q, Q, Q}; + double weight[] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + + struct ex_quadrature quad = (ex_quadrature){ + .name = "2x2x2", .cardinality = 8, .xi = xi, .eta = eta, .zeta = zeta, .weight = weight}; + EXCHECK(ex_put_quadrature(exoid, quad)); + } + + { + double xi[] = {-0.5, 0.5}; + double eta[] = {0.5, -0.5}; + double zeta[] = {-0.5, 0.5}; + double weight[] = {1.0, 1.0}; + + struct ex_quadrature quad = (ex_quadrature){ + .name = "1x2x1", .cardinality = 2, .xi = xi, .eta = eta, .zeta = zeta, .weight = weight}; + EXCHECK(ex_put_quadrature(exoid, quad)); + } + + { + int subc_dim[] = {0, 0, 0, 0, 1, 1, 1, 1, 2}; + int subc_ordinal[] = {0, 1, 2, 3, 0, 1, 2, 3, 0}; + int subc_dof_ordinal[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + int subc_num_dof[] = {1, 1, 1, 1, 1, 1, 1, 1, 1}; + double xi[] = {-1.0, 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, -1.0, 0.0}; + double eta[] = {-1.0, -1.0, 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0}; + double zeta[] = {1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0}; + + struct ex_basis basis = (ex_basis){.name = "HGRAD_QUAD_C2_FEM", + .cardinality = 9, + .subc_dim = subc_dim, + .subc_ordinal = subc_ordinal, + .subc_dof_ordinal = subc_dof_ordinal, + .subc_num_dof = subc_num_dof, + .xi = xi, + .eta = eta, + .zeta = NULL}; + EXCHECK(ex_put_basis(exoid, basis)); + + struct ex_basis basis1 = (ex_basis){.name = "TESTING_SECOND_BASIS", + .cardinality = 3, + .subc_dim = subc_dim, + .subc_ordinal = subc_ordinal, + .subc_dof_ordinal = subc_dof_ordinal, + .subc_num_dof = subc_num_dof, + .xi = xi, + .eta = eta, + .zeta = zeta}; + + EXCHECK(ex_put_basis(exoid, basis1)); + + struct ex_field field = (ex_field){.entity_type = EX_ELEM_BLOCK, + .entity_id = blocks[1].id, + .name = "Gradient", + .type_name = {",HGRAD_QUAD_C2_FEM"}, + .type = {EX_VECTOR_3D, EX_BASIS}, + .nesting = 2, + .component_separator = {'-', '$'}}; + EXCHECK(ex_put_field_metadata(exoid, field)); + + struct ex_field field2 = (ex_field){.entity_type = EX_ELEM_BLOCK, + .entity_id = blocks[1].id, + .name = "Curl", + .type_name = {"2x2x2"}, + .type = {EX_QUADRATURE}, + .nesting = 1, + .component_separator = {'@'}}; + EXCHECK(ex_put_field_metadata(exoid, field2)); + } + + { + struct ex_field field = (ex_field){.entity_type = EX_ELEM_BLOCK, + .entity_id = blocks[1].id, + .name = "Species", + .type = {EX_FIELD_TYPE_USER_DEFINED, EX_QUADRATURE}, + .type_name = {",1x2x1"}, + .nesting = 2, + .cardinality = {4}, + .component_separator = {'_', '-'}}; + EXCHECK(ex_put_field_metadata(exoid, field)); + EXCHECK(ex_put_field_suffices(exoid, field, "h2o,gas,ch4,methane")); + } + + { /* Output time steps ... */ + for (int ts = 0; ts < 1; ts++) { + double time_val = (double)(ts + 1) / 100.0; + + EXCHECK(ex_put_time(exoid, ts + 1, &time_val)); + + /* write variables */ + for (int k = 0; k < num_elem_blk; k++) { + double *var_vals = (double *)calloc(blocks[k].num_entry, CPU_word_size); + for (int var_idx = 0; var_idx < num_block_vars; var_idx++) { + for (int elem = 0; elem < blocks[k].num_entry; elem++) { + var_vals[elem] = (double)(var_idx + 2) * time_val + elem; + } + EXCHECK(ex_put_var(exoid, ts + 1, EX_ELEM_BLOCK, var_idx + 1, blocks[k].id, + blocks[k].num_entry, var_vals)); + } + free(var_vals); + } + } + } + + /* close the EXODUS files + */ + EXCHECK(ex_close(exoid)); + return 0; +} diff --git a/packages/seacas/libraries/exodus/test/testwt-field-metadata.dmp b/packages/seacas/libraries/exodus/test/testwt-field-metadata.dmp new file mode 100644 index 0000000000..9b9e075aa8 --- /dev/null +++ b/packages/seacas/libraries/exodus/test/testwt-field-metadata.dmp @@ -0,0 +1,56 @@ + :Basis@HGRAD_QUAD_C2_FEM@cardinality = 9 ; + :Basis@HGRAD_QUAD_C2_FEM@eta = -1., -1., 1., 1., -1., 0., 1., 0., 0. ; + :Basis@HGRAD_QUAD_C2_FEM@subc_dim = 0, 0, 0, 0, 1, 1, 1, 1, 2 ; + :Basis@HGRAD_QUAD_C2_FEM@subc_dof_ordinal = 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + :Basis@HGRAD_QUAD_C2_FEM@subc_num_dof = 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + :Basis@HGRAD_QUAD_C2_FEM@subc_ordinal = 0, 1, 2, 3, 0, 1, 2, 3, 0 ; + :Basis@HGRAD_QUAD_C2_FEM@xi = -1., 1., 1., -1., 0., 1., 0., -1., 0. ; + :Basis@TESTING_SECOND_BASIS@cardinality = 3 ; + :Basis@TESTING_SECOND_BASIS@eta = -1., -1., 1. ; + :Basis@TESTING_SECOND_BASIS@subc_dim = 0, 0, 0 ; + :Basis@TESTING_SECOND_BASIS@subc_dof_ordinal = 0, 0, 0 ; + :Basis@TESTING_SECOND_BASIS@subc_num_dof = 1, 1, 1 ; + :Basis@TESTING_SECOND_BASIS@subc_ordinal = 0, 1, 2 ; + :Basis@TESTING_SECOND_BASIS@xi = -1., 1., 1. ; + :Basis@TESTING_SECOND_BASIS@zeta = 1., -1., 1. ; + :Quad@1x2x1@cardinality = 2 ; + :Quad@1x2x1@eta = 0.5, -0.5 ; + :Quad@1x2x1@weight = 1., 1. ; + :Quad@1x2x1@xi = -0.5, 0.5 ; + :Quad@1x2x1@zeta = -0.5, 0.5 ; + :Quad@2x2x2@cardinality = 8 ; + :Quad@2x2x2@eta = -0.57735, -0.57735, 0.57735, 0.57735, -0.57735, -0.57735, 0.57735, 0.57735 ; + :Quad@2x2x2@weight = 1., 1., 1., 1., 1., 1., 1., 1. ; + :Quad@2x2x2@xi = -0.57735, 0.57735, -0.57735, 0.57735, -0.57735, 0.57735, -0.57735, 0.57735 ; + :Quad@2x2x2@zeta = -0.57735, -0.57735, -0.57735, -0.57735, 0.57735, 0.57735, 0.57735, 0.57735 ; + connect1:Field@Disp@separator = "" ; + connect1:Field@Disp@type = 8 ; + connect1:Field@Disp@type_name = "" ; + connect1:Field@Velocity@separator = "%" ; + connect1:Field@Velocity@type = 8 ; + connect1:Field@Velocity@type_name = "" ; + connect2:Field@Curl@separator = "@" ; + connect2:Field@Curl@type = 4 ; + connect2:Field@Curl@type_name = "2x2x2" ; + connect2:Field@Gradient@separator = "-$" ; + connect2:Field@Gradient@type = 8, 3 ; + connect2:Field@Gradient@type_name = ",HGRAD_QUAD_C2_FEM" ; + connect2:Field@Species@cardinality = 4, 0 ; + connect2:Field@Species@separator = "_-" ; + connect2:Field@Species@suffices = "h2o,gas,ch4,methane" ; + connect2:Field@Species@type = 1, 4 ; + connect2:Field@Species@type_name = ",1x2x1" ; + coor_names:Field@Disp@separator = "" ; + coor_names:Field@Disp@type = 8 ; + coor_names:Field@Disp@type_name = "" ; + coor_names:Field@Velocity@separator = "%" ; + coor_names:Field@Velocity@type = 8 ; + coor_names:Field@Velocity@type_name = "" ; + "Curl@1", + "Curl@2", + "Curl@3", + "Curl@4", + "Curl@5", + "Curl@6", + "Curl@7", + "Curl@8", diff --git a/packages/seacas/libraries/ioss/src/Ioss_BasisVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_BasisVariableType.h new file mode 100644 index 0000000000..7a78e1cfa0 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_BasisVariableType.h @@ -0,0 +1,97 @@ +/* + * Copyright(C) 2024 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 + */ +#pragma once + +#include "ioss_export.h" + +#include "Ioss_CodeTypes.h" +#include + +#include "Ioss_VariableType.h" + +namespace Ioss { + struct BasisComponent + { + int subc_dim; + int subc_ordinal; + int subc_dof_ordinal; + int subc_num_dof; + double xi; + double eta; + double zeta; + }; + + struct Basis + { + size_t size() const { return basies.size(); } + std::vector basies; + }; + + class IOSS_EXPORT BasisVariableType : public VariableType + { + public: + // 'which' is 1-based + IOSS_NODISCARD std::string label(int which, const char /* suffix_sep */) const override + { + assert(which > 0 && which <= component_count()); + if (component_count() == 1) { + return ""; + } + return VariableType::numeric_label(which - 1, component_count(), name()); + } + + BasisVariableType(const std::string &my_name, const Ioss::Basis &basis, bool delete_me) + : Ioss::VariableType(my_name, basis.size(), delete_me), m_basis_type_(my_name), + m_basis_(basis) + { + } + + BasisVariableType(const BasisVariableType &) = delete; + + IOSS_NODISCARD VariableType::Type type() const override { return Type::BASIS; } + IOSS_NODISCARD std::string type_string() const override { return "Basis"; } + + IOSS_NODISCARD const Ioss::Basis &get_basis() const { return m_basis_; } + IOSS_NODISCARD const Ioss::BasisComponent &get_basis_component(int which) const + { + assert(which > 0 && which <= component_count()); + return m_basis_.basies[which - 1]; + } + + void print() const override final; + + private: + std::string m_basis_type_{}; + Ioss::Basis m_basis_{}; + }; +} // namespace Ioss + +#if 0 +typedef struct ex_basis +{ + /* + * subc_dim: dimension of the subcell associated with the specified DoF ordinal + * -- 0 node, 1 edge, 2 face, 3 volume [Range: 0..3] + * subc_ordinal: ordinal of the subcell relative to its parent cell + * -- 0..n for each ordinal with the same subc dim [Range: <= DoF ordinal] + * subc_dof_ordinal: ordinal of the DoF relative to the subcell + * subc_num_dof: cardinality of the DoF set associated with this subcell. + * xi, eta, mu (ξ, η, ζ): Parametric coordinate location of the DoF + * -- (Only first ndim values are valid) + */ + char name[EX_MAX_NAME + 1]; + int cardinality; /* number of `basis` points == dimension of non-null subc_*, xi, eta, mu */ + int *subc_dim; + int *subc_ordinal; + int *subc_dof_ordinal; + int *subc_num_dof; + double *xi; + double *eta; + double *zeta; +} ex_basis; +#endif diff --git a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C index f37e59d507..8f16e84524 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C @@ -1,4 +1,4 @@ -// Copyright(C) 2022, 2023 National Technology & Engineering Solutions +// Copyright(C) 2022, 2023, 2024 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. // @@ -46,15 +46,14 @@ namespace { return -1; } - std::vector get_adjacent_blocks(Ioss::Region ®ion, - const std::string &surface_list) + Ioss::NameList get_adjacent_blocks(Ioss::Region ®ion, const std::string &surface_list) { - std::vector adjacent_blocks; + Ioss::NameList adjacent_blocks; if (surface_list == "ALL") { const Ioss::SideSetContainer &fss = region.get_sidesets(); for (const auto &fs : fss) { // Save a list of all blocks that are adjacent to the surfaces... - std::vector blocks; + Ioss::NameList blocks; fs->block_membership(blocks); for (const auto &block : blocks) { adjacent_blocks.push_back(block); // May have duplicates at this point. @@ -67,7 +66,7 @@ namespace { auto *sset = region.get_sideset(surface); if (sset != nullptr) { // Save a list of all blocks that are adjacent to the surfaces... - std::vector blocks; + Ioss::NameList blocks; sset->block_membership(blocks); for (const auto &block : blocks) { adjacent_blocks.push_back(block); // May have duplicates at this point. @@ -88,7 +87,7 @@ namespace { const std::string &adj_block, Ioss::chain_t &element_chains, front_t &front) { - std::vector blocks; + Ioss::NameList blocks; fs->block_membership(blocks); for (const auto &fs_block : blocks) { if (fs_block == adj_block) { @@ -209,7 +208,7 @@ namespace Ioss { // Determine which element block(s) are adjacent to the faceset specifying "lines" // The `adjacent_blocks` contains the names of all element blocks that are adjacent to the // surface(s) that specify the faces at the 'root' of the lines... - std::vector adjacent_blocks = get_adjacent_blocks(region, surface_list); + Ioss::NameList adjacent_blocks = get_adjacent_blocks(region, surface_list); if (adjacent_blocks.empty()) { fmt::print("WARNING: No surfaces in the model matched the input surface list ({}).\n\tNo " "chains will be generated.\n", diff --git a/packages/seacas/libraries/ioss/src/Ioss_Compare.C b/packages/seacas/libraries/ioss/src/Ioss_Compare.C index 9de8877b9f..9d99ecb35d 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Compare.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Compare.C @@ -995,10 +995,8 @@ namespace { { bool overall_result = true; - const std::vector &in_information_records_1 = - input_region_1.get_information_records(); - const std::vector &in_information_records_2 = - input_region_2.get_information_records(); + const Ioss::NameList &in_information_records_1 = input_region_1.get_information_records(); + const Ioss::NameList &in_information_records_2 = input_region_2.get_information_records(); if (in_information_records_1.size() != in_information_records_2.size()) { fmt::print(Ioss::WarnOut(), COUNT_MISMATCH, "INFORMATION RECORD", @@ -1025,8 +1023,8 @@ namespace { // Each QA record consists of four strings. For now, require identical ordering // (i.e., records in the same order) for equality. - const std::vector &in_qa_1 = input_region_1.get_qa_records(); - const std::vector &in_qa_2 = input_region_2.get_qa_records(); + const Ioss::NameList &in_qa_1 = input_region_1.get_qa_records(); + const Ioss::NameList &in_qa_2 = input_region_2.get_qa_records(); bool printed = false; if (in_qa_1.size() != in_qa_2.size()) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_ComposedVariableType.C b/packages/seacas/libraries/ioss/src/Ioss_ComposedVariableType.C new file mode 100644 index 0000000000..dd8e4313f2 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_ComposedVariableType.C @@ -0,0 +1,71 @@ +// Copyright(C) 1999-2020, 2024 National Technology & Engineering Solutions +// of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with +// NTESS, the U.S. Government retains certain rights in this software. +// +// See packages/seacas/LICENSE for details + +#include "Ioss_ComposedVariableType.h" +#include "Ioss_VariableType.h" +#include +#include +#include + +namespace Ioss { + std::string ComposedVariableType::composed_name(const std::string &base, + const std::string &secondary) + { + static std::string SEPARATOR("*"); + std::string name = base; + name += SEPARATOR; + name += secondary; + return name; + } + + VariableType *ComposedVariableType::composed_variable_type(const VariableType *inst, + const VariableType *secondary) + { + // See if we already constructed this composed type... + std::string composed_type = + ComposedVariableType::composed_name(inst->name(), secondary->name()); + + VariableType *comp_inst = nullptr; + auto iter = registry().find(composed_type); + if (iter == registry().end()) { + // Not found, construct new type... + comp_inst = new ComposedVariableType(inst, secondary, true); + } + else { + comp_inst = (*iter).second; + } + return comp_inst; + } + + ComposedVariableType::ComposedVariableType(const VariableType *base_type, + const VariableType *secondary_type, bool delete_me) + : VariableType(composed_name(base_type->name(), secondary_type->name()), + base_type->component_count() * secondary_type->component_count(), delete_me), + baseType(base_type), secondaryType(secondary_type) + { + } + + std::string ComposedVariableType::label(int which, const char suffix_sep) const + { + static char tmp_sep[2]; + + // NOTE: 'which' is 1-based + assert(which > 0 && which <= component_count()); + + int base_comp = baseType->component_count(); + int copies = secondaryType->component_count(); + int which_instance = (which - 1) / base_comp; + int which_base = (which - 1) % base_comp; + + std::string my_label = baseType->label(which_base + 1); + if (suffix_sep != 0 && base_comp > 1) { + tmp_sep[0] = suffix_sep; + my_label += tmp_sep; + } + my_label += VariableType::numeric_label(which_instance, copies, name()); + return my_label; + } +} // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_ComposedVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_ComposedVariableType.h new file mode 100644 index 0000000000..0a1bf8a797 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_ComposedVariableType.h @@ -0,0 +1,40 @@ +// Copyright(C) 1999-2020, 2022, 2024 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 + +#pragma once + +#include "ioss_export.h" + +#include "Ioss_CodeTypes.h" +#include "Ioss_VariableType.h" // for VariableType +#include // for string + +namespace Ioss { + class IOSS_EXPORT ComposedVariableType : public VariableType + { + public: + IOSS_NODISCARD static std::string composed_name(const std::string &base, + const std::string &secondary); + IOSS_NODISCARD static VariableType *composed_variable_type(const VariableType *inst, + const VariableType *secondary); + + IOSS_NODISCARD VariableType::Type type() const override { return Type::COMPOSED; } + IOSS_NODISCARD std::string type_string() const override; + + IOSS_NODISCARD std::string label(int which, char suffix_sep = '_') const override; + + ComposedVariableType(const VariableType *base_type, const VariableType *secondary, + bool delete_me); + ComposedVariableType(const ComposedVariableType &) = delete; + + IOSS_NODISCARD const VariableType *get_base_type() const { return baseType; } + IOSS_NODISCARD const VariableType *get_secondary_type() const { return secondaryType; } + + private: + const VariableType *baseType{nullptr}; + const VariableType *secondaryType{nullptr}; + }; +} // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.C b/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.C index 2885411505..6265c03f17 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.C +++ b/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2020 National Technology & Engineering Solutions +// Copyright(C) 1999-2020, 2024 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. // @@ -63,7 +63,7 @@ namespace Ioss { int which_instance = (which - 1) / base_comp; int which_base = (which - 1) % base_comp; - std::string my_label = baseType->label(which_base + 1, suffix_sep); + std::string my_label = baseType->label(which_base + 1); if (suffix_sep != 0 && base_comp > 1) { tmp_sep[0] = suffix_sep; my_label += tmp_sep; @@ -71,9 +71,4 @@ namespace Ioss { my_label += VariableType::numeric_label(which_instance + 1, copies_, name()); return my_label; } - - const VariableType *CompositeVariableType::GetBaseType() const { return baseType; } - - int CompositeVariableType::GetNumCopies() const { return copies_; } - } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h index f2da831fb0..904fef550b 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h @@ -20,13 +20,23 @@ namespace Ioss { IOSS_NODISCARD static VariableType *composite_variable_type(const VariableType *inst, int copies); + IOSS_NODISCARD VariableType::Type type() const override { return Type::COMPOSITE; } + IOSS_NODISCARD std::string type_string() const override; + IOSS_NODISCARD std::string label(int which, char suffix_sep = '_') const override; CompositeVariableType(const std::string &my_name, int number_components, bool delete_me); CompositeVariableType(const VariableType *base_type, int copies, bool delete_me); CompositeVariableType(const CompositeVariableType &) = delete; - IOSS_NODISCARD const VariableType *GetBaseType() const; - IOSS_NODISCARD int GetNumCopies() const; + IOSS_NODISCARD const VariableType *get_base_type() const { return baseType; } + IOSS_NODISCARD int get_num_copies() const { return copies_; } + + // Kept for backward compatibility... + IOSS_NODISCARD __attribute__((__deprecated__)) const VariableType *getBaseType() const + { + return baseType; + } + IOSS_NODISCARD __attribute__((__deprecated__)) int getNumCopies() const { return copies_; } private: const VariableType *baseType; diff --git a/packages/seacas/libraries/ioss/src/Ioss_ConcreteVariableType.C b/packages/seacas/libraries/ioss/src/Ioss_ConcreteVariableType.C index 6b25267b48..9cc8d00999 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ConcreteVariableType.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ConcreteVariableType.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2020, 2022, 2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2020, 2022, 2023, 2024 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. // @@ -91,7 +91,7 @@ std::string Ioss::Invalid_Storage::label(int /*which*/, const char /*suffix_sep* } std::string Ioss::Invalid_Storage::label_name(const std::string &base, int /*which*/, - const char /*suffix_sep*/, + const char /*suffix_sep*/, const char /*suffix_sep*/, bool /*suffices_uppercase*/) const { return base; @@ -115,7 +115,8 @@ std::string Ioss::Scalar::label(IOSS_MAYBE_UNUSED int which, const char /*suffix } std::string Ioss::Scalar::label_name(const std::string &base, int /*which*/, - const char /*suffix_sep*/, bool /*suffices_uppercase*/) const + const char /*suffix_sep*/, const char /*suffix_sep*/, + bool /*suffices_uppercase*/) const { return base; } diff --git a/packages/seacas/libraries/ioss/src/Ioss_ConcreteVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_ConcreteVariableType.h index 25e62ebd0b..890333d9af 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ConcreteVariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ConcreteVariableType.h @@ -28,6 +28,9 @@ namespace Ioss { static void factory(); \ X(const X &) = delete; \ \ + IOSS_NODISCARD VariableType::Type type() const override { return Type::STANDARD; } \ + IOSS_NODISCARD std::string type_string() const override { return "Standard"; } \ + \ protected: \ X(); \ \ @@ -39,11 +42,14 @@ namespace Ioss { public: Invalid_Storage(const Invalid_Storage &) = delete; IOSS_NODISCARD std::string label(int which, char suffix_sep = '_') const override; - IOSS_NODISCARD std::string label_name(const std::string &base, int /*which*/, char suffix_sep, - bool suffices_uppercase) const override; + IOSS_NODISCARD std::string label_name(const std::string &base, int /*which*/, char suffix_sep1, + char suffix_sep2, bool suffices_uppercase) const override; IOSS_NODISCARD int suffix_count() const override { return 0; } static void factory(); + IOSS_NODISCARD VariableType::Type type() const override { return Type::UNKNOWN; } + IOSS_NODISCARD std::string type_string() const override { return "Invalid"; } + protected: Invalid_Storage(); }; @@ -53,11 +59,14 @@ namespace Ioss { public: Scalar(const Scalar &) = delete; IOSS_NODISCARD std::string label(int which, char suffix_sep = '_') const override; - IOSS_NODISCARD std::string label_name(const std::string &base, int /*which*/, char suffix_sep, - bool suffices_uppercase) const override; + IOSS_NODISCARD std::string label_name(const std::string &base, int /*which*/, char suffix_sep1, + char suffix_sep2, bool suffices_uppercase) const override; IOSS_NODISCARD int suffix_count() const override { return 0; } static void factory(); + IOSS_NODISCARD VariableType::Type type() const override { return Type::SCALAR; } + IOSS_NODISCARD std::string type_string() const override { return "Scalar"; } + protected: Scalar(); }; diff --git a/packages/seacas/libraries/ioss/src/Ioss_ConstructedVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_ConstructedVariableType.h index c22b2bcbaf..34673148bb 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ConstructedVariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ConstructedVariableType.h @@ -16,6 +16,10 @@ namespace Ioss { { public: IOSS_NODISCARD std::string label(int which, char suffix_sep = '_') const override; + + IOSS_NODISCARD VariableType::Type type() const override { return Type::CONSTRUCTED; } + IOSS_NODISCARD std::string type_string() const override { return "Constructed"; } + ConstructedVariableType(const std::string &my_name, int number_components, bool delete_me); explicit ConstructedVariableType(int number_components, bool delete_me); ConstructedVariableType(const ConstructedVariableType &) = delete; diff --git a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C index d65f4ce7e4..7b7473c5e0 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C +++ b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C @@ -1204,8 +1204,6 @@ namespace { size_t isize = ige->get_field(field_name).get_size(); assert(isize == oge->get_field(field_name).get_size()); - int basic_type = ige->get_field(field_name).get_type(); - if (field_name == "mesh_model_coordinates_x") { return; } @@ -1261,25 +1259,6 @@ namespace { switch (options.data_storage_type) { case 1: ige->get_field_data(field_name, pool.data.data(), isize); break; - case 2: - if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { - ige->get_field_data(field_name, pool.data); - } - else if (basic_type == Ioss::Field::INT32) { - ige->get_field_data(field_name, pool.data_int); - } - else if (basic_type == Ioss::Field::INT64) { - ige->get_field_data(field_name, pool.data_int64); - } - else if (basic_type == Ioss::Field::REAL) { - ige->get_field_data(field_name, pool.data_double); - } - else if (basic_type == Ioss::Field::COMPLEX) { - ige->get_field_data(field_name, pool.data_complex); - } - else { - } - break; #ifdef SEACAS_HAVE_KOKKOS case 3: if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { @@ -1355,25 +1334,6 @@ namespace { switch (options.data_storage_type) { case 1: oge->put_field_data(field_name, pool.data.data(), isize); break; - case 2: - if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { - oge->put_field_data(field_name, pool.data); - } - else if (basic_type == Ioss::Field::INT32) { - oge->put_field_data(field_name, pool.data_int); - } - else if (basic_type == Ioss::Field::INT64) { - oge->put_field_data(field_name, pool.data_int64); - } - else if (basic_type == Ioss::Field::REAL) { - oge->put_field_data(field_name, pool.data_double); - } - else if (basic_type == Ioss::Field::COMPLEX) { - oge->put_field_data(field_name, pool.data_complex); - } - else { - } - break; #ifdef SEACAS_HAVE_KOKKOS case 3: if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { @@ -1448,7 +1408,7 @@ namespace { { out.add_information_records(in.get_information_records()); - const std::vector &qa = in.get_qa_records(); + const Ioss::NameList &qa = in.get_qa_records(); for (size_t i = 0; i < qa.size(); i += 4) { out.add_qa_record(qa[i + 0], qa[i + 1], qa[i + 2], qa[i + 3]); } diff --git a/packages/seacas/libraries/ioss/src/Ioss_DataPool.h b/packages/seacas/libraries/ioss/src/Ioss_DataPool.h index c7dcb9c4a5..b1ffb25188 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DataPool.h +++ b/packages/seacas/libraries/ioss/src/Ioss_DataPool.h @@ -1,4 +1,4 @@ -// Copyright(C) 2020, 2021, 2022 National Technology & Engineering Solutions +// Copyright(C) 2020, 2021, 2022, 2024 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. // @@ -16,11 +16,7 @@ namespace Ioss { struct IOSS_EXPORT DataPool { // Data space shared by most field input/output routines... - std::vector data{}; - std::vector data_int{}; - std::vector data_int64{}; - std::vector data_double{}; - std::vector data_complex{}; + std::vector data{}; #ifdef SEACAS_HAVE_KOKKOS Kokkos::View data_view_char{}; Kokkos::View data_view_int{}; diff --git a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C index 8ab2ccac4d..870140a1d2 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2024 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. // @@ -722,10 +722,10 @@ namespace Ioss { return; } - std::string prop = properties.get(property_name).get_string(); - std::vector groups = tokenize(prop, ":"); + std::string prop = properties.get(property_name).get_string(); + Ioss::NameList groups = tokenize(prop, ":"); for (auto &group : groups) { - std::vector group_spec = tokenize(group, ","); + Ioss::NameList group_spec = tokenize(group, ","); // group_spec should contain the name of the new group as // the first location and the members of the group as subsequent @@ -746,7 +746,7 @@ namespace Ioss { template void DatabaseIO::create_group(EntityType /*type*/, const std::string &type_name, - const std::vector &group_spec, const T * /*set_type*/) + const Ioss::NameList &group_spec, const T * /*set_type*/) { fmt::print(Ioss::WarnOut(), "Grouping of {0} sets is not yet implemented.\n" @@ -755,9 +755,8 @@ namespace Ioss { } template <> - void DatabaseIO::create_group(EntityType type, const std::string & /*type_name*/, - const std::vector &group_spec, - const SideSet * /*set_type*/) + void DatabaseIO::create_group(EntityType type, const std::string &/*type_name*/, + const Ioss::NameList &group_spec, const SideSet * /*set_type*/) { // Not generalized yet... This only works for T == SideSet if (type != SIDESET) { @@ -845,7 +844,7 @@ namespace Ioss { * * \param[in] info The strings to add. */ - void DatabaseIO::add_information_records(const std::vector &info) + void DatabaseIO::add_information_records(const Ioss::NameList &info) { informationRecords.reserve(informationRecords.size() + info.size()); informationRecords.insert(informationRecords.end(), info.begin(), info.end()); @@ -879,8 +878,8 @@ namespace Ioss { qaRecords.push_back(time); } - void DatabaseIO::set_block_omissions(const std::vector &omissions, - const std::vector &inclusions) + void DatabaseIO::set_block_omissions(const Ioss::NameList &omissions, + const Ioss::NameList &inclusions) { if (!omissions.empty() && !inclusions.empty()) { // Only one can be non-empty @@ -920,8 +919,8 @@ namespace Ioss { } } - void DatabaseIO::set_assembly_omissions(const std::vector &omissions, - const std::vector &inclusions) + void DatabaseIO::set_assembly_omissions(const Ioss::NameList &omissions, + const Ioss::NameList &inclusions) { if (!omissions.empty() && !inclusions.empty()) { // Only one can be non-empty @@ -1025,7 +1024,7 @@ namespace Ioss { } void DatabaseIO::get_block_adjacencies_nl(const Ioss::ElementBlock *eb, - std::vector &block_adjacency) const + Ioss::NameList &block_adjacency) const { if (!blockAdjacenciesCalculated) { compute_block_adjacencies(); diff --git a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h index 020cd71346..3c0f348b55 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h @@ -356,11 +356,11 @@ namespace Ioss { * * \returns The informative strings. */ - IOSS_NODISCARD const std::vector &get_information_records() const + IOSS_NODISCARD const Ioss::NameList &get_information_records() const { return informationRecords; } - void add_information_records(const std::vector &info); + void add_information_records(const Ioss::NameList &info); void add_information_record(const std::string &info); // QA Records: @@ -380,7 +380,7 @@ namespace Ioss { * \returns All QA records in a single vector. Every 4 consecutive elements of the * vector make up a single QA record. */ - IOSS_NODISCARD const std::vector &get_qa_records() const { return qaRecords; } + IOSS_NODISCARD const Ioss::NameList &get_qa_records() const { return qaRecords; } void add_qa_record(const std::string &code, const std::string &code_qa, const std::string &date, const std::string &time); @@ -492,19 +492,17 @@ namespace Ioss { void set_surface_split_type(Ioss::SurfaceSplitType split_type) { splitType = split_type; } IOSS_NODISCARD Ioss::SurfaceSplitType get_surface_split_type() const { return splitType; } - void set_block_omissions(const std::vector &omissions, - const std::vector &inclusions = {}); + void set_block_omissions(const Ioss::NameList &omissions, + const Ioss::NameList &inclusions = {}); - void set_assembly_omissions(const std::vector &omissions, - const std::vector &inclusions = {}); + void set_assembly_omissions(const Ioss::NameList &omissions, + const Ioss::NameList &inclusions = {}); - void get_block_adjacencies(const Ioss::ElementBlock *eb, - std::vector &block_adjacency) const + void get_block_adjacencies(const Ioss::ElementBlock *eb, Ioss::NameList &block_adjacency) const { return get_block_adjacencies_nl(eb, block_adjacency); } - void compute_block_membership(Ioss::SideBlock *efblock, - std::vector &block_membership) const + void compute_block_membership(Ioss::SideBlock *efblock, Ioss::NameList &block_membership) const { return compute_block_membership_nl(efblock, block_membership); } @@ -638,7 +636,7 @@ namespace Ioss { const std::string &type_name, const T *set_type); template void create_group(EntityType type, const std::string &type_name, - const std::vector &group_spec, const T *set_type); + const Ioss::NameList &group_spec, const T *set_type); // Create new sets as groups of existing exodus sets... void handle_groups(); @@ -731,13 +729,13 @@ namespace Ioss { // element ids and offsets are still calculated assuming that the // blocks exist in the model... // Only one of these can have values and the other must be empty. - std::vector blockOmissions{}; - std::vector blockInclusions{}; - std::vector assemblyOmissions{}; - std::vector assemblyInclusions{}; + Ioss::NameList blockOmissions{}; + Ioss::NameList blockInclusions{}; + Ioss::NameList assemblyOmissions{}; + Ioss::NameList assemblyInclusions{}; - std::vector informationRecords{}; - std::vector qaRecords{}; + Ioss::NameList informationRecords{}; + Ioss::NameList qaRecords{}; //---Node Map -- Maps internal (1..NUMNP) ids to global ids used on the // application side. global = nodeMap[local] @@ -795,11 +793,10 @@ namespace Ioss { virtual bool end_state_nl(int state, double time); void get_block_adjacencies_nl(const Ioss::ElementBlock *eb, - std::vector &block_adjacency) const; + Ioss::NameList &block_adjacency) const; - virtual void - compute_block_membership_nl(Ioss::SideBlock * /* efblock */, - std::vector & /* block_membership */) const + virtual void compute_block_membership_nl(Ioss::SideBlock * /* efblock */, + Ioss::NameList & /* block_membership */) const { } diff --git a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C index 13ff0e6b05..efda361b47 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2023 National Technology & Engineering Solutions + * Copyright(C) 1999-2024 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. * @@ -138,29 +138,29 @@ namespace { namespace Ioss { - IOSS_EXPORT const std::vector &valid_decomp_methods() + IOSS_EXPORT const Ioss::NameList &valid_decomp_methods() { - static const std::vector valid_methods{"EXTERNAL" + static const Ioss::NameList valid_methods{"EXTERNAL" #ifdef SEACAS_HAVE_MPI - , - "LINEAR", - "MAP", - "VARIABLE" + , + "LINEAR", + "MAP", + "VARIABLE" #if !defined(NO_ZOLTAN_SUPPORT) - , - "BLOCK", - "CYCLIC", - "RANDOM", - "RCB", - "RIB", - "HSFC" + , + "BLOCK", + "CYCLIC", + "RANDOM", + "RCB", + "RIB", + "HSFC" #endif #if !defined(NO_PARMETIS_SUPPORT) - , - "KWAY", - "KWAY_GEOM", - "GEOM_KWAY", - "METIS_SFC" + , + "KWAY", + "KWAY_GEOM", + "GEOM_KWAY", + "METIS_SFC" #endif #endif }; diff --git a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.h b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.h index a22334a068..5bae8e227b 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.h @@ -39,7 +39,7 @@ #endif namespace Ioss { - IOSS_EXPORT const std::vector &valid_decomp_methods(); + IOSS_EXPORT const Ioss::NameList &valid_decomp_methods(); class IOSS_EXPORT BlockDecompositionData { diff --git a/packages/seacas/libraries/ioss/src/Ioss_EdgeSet.C b/packages/seacas/libraries/ioss/src/Ioss_EdgeSet.C index ed8928d348..c917486520 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_EdgeSet.C +++ b/packages/seacas/libraries/ioss/src/Ioss_EdgeSet.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2020, 2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2020, 2023, 2024 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. // @@ -54,4 +54,4 @@ Ioss::Property Ioss::EdgeSet::get_implicit_property(const std::string &my_name) return Ioss::EntitySet::get_implicit_property(my_name); } -void Ioss::EdgeSet::block_membership(std::vector & /*block_members*/) {} +void Ioss::EdgeSet::block_membership(Ioss::NameList & /*block_members*/) {} diff --git a/packages/seacas/libraries/ioss/src/Ioss_EdgeSet.h b/packages/seacas/libraries/ioss/src/Ioss_EdgeSet.h index de9e578707..438d20ab70 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_EdgeSet.h +++ b/packages/seacas/libraries/ioss/src/Ioss_EdgeSet.h @@ -43,7 +43,7 @@ namespace Ioss { // An example would be 'element_block_count' for a region. IOSS_NODISCARD Property get_implicit_property(const std::string &my_name) const override; - void block_membership(std::vector &block_membership) override; + void block_membership(Ioss::NameList &block_membership) override; protected: int64_t internal_get_field_data(const Field &field, void *data, diff --git a/packages/seacas/libraries/ioss/src/Ioss_ElementBlock.C b/packages/seacas/libraries/ioss/src/Ioss_ElementBlock.C index 2762da54a8..a7decd5544 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ElementBlock.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ElementBlock.C @@ -60,14 +60,14 @@ namespace Ioss { return get_database()->get_zc_field(this, field, data, data_size); } - void ElementBlock::get_block_adjacencies(std::vector &block_adjacency) const + void ElementBlock::get_block_adjacencies(Ioss::NameList &block_adjacency) const { get_database()->get_block_adjacencies(this, block_adjacency); } - std::vector ElementBlock::get_block_adjacencies() const + Ioss::NameList ElementBlock::get_block_adjacencies() const { - std::vector block_adjacency; + Ioss::NameList block_adjacency; get_database()->get_block_adjacencies(this, block_adjacency); return block_adjacency; } diff --git a/packages/seacas/libraries/ioss/src/Ioss_ElementBlock.h b/packages/seacas/libraries/ioss/src/Ioss_ElementBlock.h index 2a849b8d25..317263ca27 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ElementBlock.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ElementBlock.h @@ -43,8 +43,8 @@ namespace Ioss { // An example would be 'element_block_count' for a region. IOSS_NODISCARD Property get_implicit_property(const std::string &my_name) const override; - IOSS_NODISCARD std::vector get_block_adjacencies() const; - void get_block_adjacencies(std::vector &block_adjacency) const; + IOSS_NODISCARD Ioss::NameList get_block_adjacencies() const; + void get_block_adjacencies(Ioss::NameList &block_adjacency) const; IOSS_NODISCARD AxisAlignedBoundingBox get_bounding_box() const; IOSS_NODISCARD bool operator==(const Ioss::ElementBlock &rhs) const; IOSS_NODISCARD bool operator!=(const Ioss::ElementBlock &rhs) const; diff --git a/packages/seacas/libraries/ioss/src/Ioss_ElementSet.C b/packages/seacas/libraries/ioss/src/Ioss_ElementSet.C index b6ee6f0a5c..465f69a5e7 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ElementSet.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ElementSet.C @@ -53,4 +53,4 @@ Ioss::Property Ioss::ElementSet::get_implicit_property(const std::string &my_nam return Ioss::EntitySet::get_implicit_property(my_name); } -void Ioss::ElementSet::block_membership(std::vector & /*block_members*/) {} +void Ioss::ElementSet::block_membership(Ioss::NameList & /*block_members*/) {} diff --git a/packages/seacas/libraries/ioss/src/Ioss_ElementSet.h b/packages/seacas/libraries/ioss/src/Ioss_ElementSet.h index e4e846db90..87667706f4 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ElementSet.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ElementSet.h @@ -43,7 +43,7 @@ namespace Ioss { // An example would be 'element_block_count' for a region. IOSS_NODISCARD Property get_implicit_property(const std::string &my_name) const override; - void block_membership(std::vector &block_membership) override; + void block_membership(Ioss::NameList &block_membership) override; protected: int64_t internal_get_field_data(const Field &field, void *data, diff --git a/packages/seacas/libraries/ioss/src/Ioss_ElementTopology.C b/packages/seacas/libraries/ioss/src/Ioss_ElementTopology.C index 6bdb9de951..0e4690835b 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ElementTopology.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ElementTopology.C @@ -10,13 +10,13 @@ #include "Ioss_Super.h" // for Super #include "Ioss_Utils.h" -#include // for assert -#include // for size_t +#include +#include #include -#include // for basic_ostream, etc -#include // for string, char_traits, etc -#include // for pair -#include // for vector +#include +#include +#include +#include void Ioss::ETRegistry::insert(const Ioss::ETM_VP &value, bool delete_me) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_ElementVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_ElementVariableType.h index 7488407dd0..d47c3d59b0 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ElementVariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ElementVariableType.h @@ -21,13 +21,16 @@ namespace Ioss { return ""; } IOSS_NODISCARD std::string label_name(const std::string &base, int /*which*/, - const char /*suffix_sep*/, + const char /*suffix_sep*/, const char /*suffix_sep*/, bool /* suffices_uppercase */) const override { return base; } IOSS_NODISCARD int suffix_count() const override { return 0; } + IOSS_NODISCARD VariableType::Type type() const override { return Type::ELEMENT; } + IOSS_NODISCARD std::string type_string() const override { return "Element"; } + protected: ElementVariableType(const std::string &type, int comp_count); }; diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceSet.C b/packages/seacas/libraries/ioss/src/Ioss_FaceSet.C index 44307bfb0e..4d033ce976 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceSet.C +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceSet.C @@ -54,4 +54,4 @@ Ioss::Property Ioss::FaceSet::get_implicit_property(const std::string &my_name) return Ioss::EntitySet::get_implicit_property(my_name); } -void Ioss::FaceSet::block_membership(std::vector & /*block_members*/) {} +void Ioss::FaceSet::block_membership(Ioss::NameList & /*block_members*/) {} diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceSet.h b/packages/seacas/libraries/ioss/src/Ioss_FaceSet.h index e5d1a3b766..7e3eb0e87b 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceSet.h +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceSet.h @@ -43,7 +43,7 @@ namespace Ioss { // An example would be 'element_block_count' for a region. IOSS_NODISCARD Property get_implicit_property(const std::string &my_name) const override; - void block_membership(std::vector &block_membership) override; + void block_membership(Ioss::NameList &block_membership) override; protected: int64_t internal_get_field_data(const Field &field, void *data, diff --git a/packages/seacas/libraries/ioss/src/Ioss_Field.C b/packages/seacas/libraries/ioss/src/Ioss_Field.C index c1f9f51a69..c110b825c5 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Field.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Field.C @@ -35,6 +35,31 @@ namespace { } // namespace +namespace Ioss { + std::ostream &operator<<(std::ostream &os, const Field &fld) + { + Ioss::NameList components(fld.get_component_count(Field::InOut::INPUT)); + for (size_t i = 0; i < components.size(); i++) { + components[i] = fld.get_component_name(i + 1, Field::InOut::INPUT, 1); + } + auto storage = fld.raw_storage()->name(); + if (storage == "scalar") { + fmt::print(os, "\tField: {}, Storage: {}\t{}\t{}\n", fld.get_name(), + fld.raw_storage()->name(), fld.type_string(), fld.role_string()); + } + else { + fmt::print(os, + "\tField: {}, Storage: {} ({}),\t{},\t{}, Sep1: '{}', Sep2: '{}'\n" + "\t\t\tComponents ({}): {}\n", + fld.get_name(), fld.raw_storage()->name(), fld.raw_storage()->type_string(), + fld.type_string(), fld.role_string(), fld.get_suffix_separator(0), + fld.get_suffix_separator(1), fld.get_component_count(Field::InOut::INPUT), + fmt::join(components, ", ")); + } + return os; + } +} // namespace Ioss + /** \brief Create an empty field. */ Ioss::Field::Field() { rawStorage_ = transStorage_ = Ioss::VariableType::factory("invalid"); } @@ -103,6 +128,27 @@ Ioss::Field::Field(std::string name, const Ioss::Field::BasicType type, size_ = internal_get_size(type_, rawCount_, rawStorage_); } +/** \brief Create a field. + * + * \param[in] name The name of the field + * \param[in] type The basic data type of data held in the field. + * \param[in] storage The storage class of the data (ConstructedVariableType, + * CompositeVariableType, etc) + * \param[in] secondary The secondary storage class of the data (typically "basis") [For a + * ComposedVariableType field] \param[in] role The category of information held in the field (MESH, + * ATTRIBUTE, TRANSIENT, REDUCTION, etc) \param[in] value_count The number of items in the field. + * \param[in] index + * + */ +Ioss::Field::Field(std::string name, BasicType type, const std::string &storage, + const std::string &secondary, RoleType role, size_t value_count, size_t index) + : name_(std::move(name)), rawCount_(value_count), transCount_(value_count), index_(index), + type_(type), role_(role) +{ + rawStorage_ = transStorage_ = Ioss::VariableType::factory(storage, secondary); + size_ = internal_get_size(type_, rawCount_, rawStorage_); +} + int Ioss::Field::get_component_count(Ioss::Field::InOut in_out) const { const auto *storage = (in_out == InOut::INPUT) ? raw_storage() : transformed_storage(); @@ -111,12 +157,16 @@ int Ioss::Field::get_component_count(Ioss::Field::InOut in_out) const std::string Ioss::Field::get_component_name(int component_index, InOut in_out, char suffix) const { - char suffix_separator = get_suffix_separator(); - if (suffix_separator == 1) { - suffix_separator = suffix != 1 ? suffix : '_'; + char suffix_separator0 = get_suffix_separator(0); + if (suffix_separator0 == 1) { + suffix_separator0 = suffix != 1 ? suffix : '_'; + } + char suffix_separator1 = get_suffix_separator(1); + if (suffix_separator1 == 1) { + suffix_separator1 = suffix != 1 ? suffix : '_'; } const auto *storage = (in_out == InOut::INPUT) ? raw_storage() : transformed_storage(); - return storage->label_name(get_name(), component_index, suffix_separator, + return storage->label_name(get_name(), component_index, suffix_separator0, suffix_separator1, get_suffices_uppercase()); } @@ -280,47 +330,47 @@ bool Ioss::Field::equal_(const Ioss::Field &rhs, bool quiet) const if (this->type_ != rhs.type_) { if (!quiet) { - fmt::print(Ioss::OUTPUT(), "\tFIELD type mismatch ({} v. {})\n", this->type_string(), - rhs.type_string()); + fmt::print(Ioss::OUTPUT(), "\tFIELD {} type mismatch ({} v. {})\n", this->name_, + this->type_string(), rhs.type_string()); } is_same = false; } if (this->role_ != rhs.role_) { if (!quiet) { - fmt::print(Ioss::OUTPUT(), "\tFIELD role mismatch ({} v. {})\n", this->role_string(), - rhs.role_string()); + fmt::print(Ioss::OUTPUT(), "\tFIELD {} role mismatch ({} v. {})\n", this->name_, + this->role_string(), rhs.role_string()); } is_same = false; } if (this->rawCount_ != rhs.rawCount_) { if (!quiet) { - fmt::print(Ioss::OUTPUT(), "\tFIELD rawCount mismatch ({} v. {})\n", this->rawCount_, - rhs.rawCount_); + fmt::print(Ioss::OUTPUT(), "\tFIELD {} rawCount mismatch ({} v. {})\n", this->name_, + this->rawCount_, rhs.rawCount_); } is_same = false; } if (this->transCount_ != rhs.transCount_) { if (!quiet) { - fmt::print(Ioss::OUTPUT(), "\tFIELD transCount mismatch ({} v. {})\n", this->transCount_, - rhs.transCount_); + fmt::print(Ioss::OUTPUT(), "\tFIELD {} transCount mismatch ({} v. {})\n", this->name_, + this->transCount_, rhs.transCount_); } is_same = false; } if (this->get_size() != rhs.get_size()) { if (!quiet) { - fmt::print(Ioss::OUTPUT(), "\tFIELD size mismatch ({} v. {})\n", this->get_size(), - rhs.get_size()); + fmt::print(Ioss::OUTPUT(), "\tFIELD {} size mismatch ({} v. {})\n", this->name_, + this->get_size(), rhs.get_size()); } is_same = false; } if (!quiet) { if (this->get_suffices_uppercase() != rhs.get_suffices_uppercase()) { - fmt::print(Ioss::OUTPUT(), "\tFIELD suffices_uppercase mismatch ({} v. {})\n", + fmt::print(Ioss::OUTPUT(), "\tFIELD {} suffices_uppercase mismatch ({} v. {})\n", this->name_, this->get_suffices_uppercase(), rhs.get_suffices_uppercase()); is_same = false; } @@ -328,7 +378,7 @@ bool Ioss::Field::equal_(const Ioss::Field &rhs, bool quiet) const if (!quiet) { if (this->zero_copy_enabled() != rhs.zero_copy_enabled()) { - fmt::print(Ioss::OUTPUT(), "\tFIELD zero_copy_enabled mismatch ({} v. {})\n", + fmt::print(Ioss::OUTPUT(), "\tFIELD {} zero_copy_enabled mismatch ({} v. {})\n", this->name_, this->zero_copy_enabled(), rhs.zero_copy_enabled()); is_same = false; } diff --git a/packages/seacas/libraries/ioss/src/Ioss_Field.h b/packages/seacas/libraries/ioss/src/Ioss_Field.h index 897fafb4bb..80c7aae156 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Field.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Field.h @@ -8,6 +8,9 @@ #include "Ioss_CodeTypes.h" #include // for size_t +#if !defined BUILT_IN_SIERRA +#include +#endif #include #include // for string #include // for vector @@ -108,6 +111,9 @@ namespace Ioss { Field(std::string name, BasicType type, const std::string &storage, int copies, RoleType role, size_t value_count = 0, size_t index = 0); + Field(std::string name, BasicType type, const std::string &storage, + const std::string &secondary, RoleType role, size_t value_count = 0, size_t index = 0); + Field(std::string name, BasicType type, const VariableType *storage, RoleType role, size_t value_count = 0, size_t index = 0); @@ -136,13 +142,17 @@ namespace Ioss { char suffix = 1) const; IOSS_NODISCARD int get_component_count(InOut in_out) const; - Field &set_suffix_separator(char suffix_separator) + Field &set_suffix_separator(char suffix_separator1, char suffix_separator2 = 2) { - suffixSeparator_ = suffix_separator; + suffixSeparator1_ = suffix_separator1; + suffixSeparator2_ = suffix_separator2 == 2 ? suffix_separator1 : suffix_separator2; return *this; } - IOSS_NODISCARD char get_suffix_separator() const { return suffixSeparator_; } - Field &set_suffices_uppercase(bool true_false) + IOSS_NODISCARD char get_suffix_separator(int index = 0) const + { + return index == 0 ? suffixSeparator1_ : suffixSeparator2_; + } + Field &set_suffices_uppercase(bool true_false) { sufficesUppercase_ = true_false; return *this; @@ -228,10 +238,22 @@ namespace Ioss { const VariableType *transStorage_{nullptr}; // Storage type after transformation std::vector transforms_{}; - char suffixSeparator_{1}; // Value = 1 means unset; use database default. + char suffixSeparator1_{1}; // Value = 1 means unset; use database default. + char suffixSeparator2_{1}; // Value = 1 means unset; use database default. bool sufficesUppercase_{false}; // True if the suffices are uppercase on database... mutable bool zeroCopyable_{false}; // True if the field is zero-copyable. bool equal_(const Ioss::Field &rhs, bool quiet) const; }; + IOSS_EXPORT std::ostream &operator<<(std::ostream &os, const Field &fld); } // namespace Ioss + +#if !defined BUILT_IN_SIERRA +#if FMT_VERSION >= 90000 +namespace fmt { + template <> struct formatter : ostream_formatter + { + }; +} // namespace fmt +#endif +#endif diff --git a/packages/seacas/libraries/ioss/src/Ioss_GroupingEntity.C b/packages/seacas/libraries/ioss/src/Ioss_GroupingEntity.C index 59f6b2da75..0050e99304 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_GroupingEntity.C +++ b/packages/seacas/libraries/ioss/src/Ioss_GroupingEntity.C @@ -550,14 +550,12 @@ bool Ioss::GroupingEntity::equal_(const Ioss::GroupingEntity &rhs, bool quiet) c } } - if (rhs_fields.size() > lhs_fields.size()) { - // See which fields are missing from input #1... - // NOTE: `quiet` mode has already exited by this point. - for (auto &field : rhs_fields) { - if (!this->field_exists(field)) { - fmt::print(Ioss::OUTPUT(), "{}: FIELD ({}) not found in input #1\n", name(), field); - same = false; - } + // See which fields are missing from input #1... + // NOTE: `quiet` mode has already exited by this point. + for (auto &field : rhs_fields) { + if (!this->field_exists(field)) { + fmt::print(Ioss::OUTPUT(), "{}: FIELD ({}) not found in input #1\n", name(), field); + same = false; } } return same; diff --git a/packages/seacas/libraries/ioss/src/Ioss_GroupingEntity.h b/packages/seacas/libraries/ioss/src/Ioss_GroupingEntity.h index 6c7faec5b1..e6123be18c 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_GroupingEntity.h +++ b/packages/seacas/libraries/ioss/src/Ioss_GroupingEntity.h @@ -128,7 +128,7 @@ namespace Ioss { * Entries are pushed onto the "block_members" vector, so it will be * appended to if it is not empty at entry to the function. */ - virtual void block_membership(std::vector & /* block_members */) {} + virtual void block_membership(Ioss::NameList & /* block_members */) {} IOSS_NODISCARD std::string get_filename() const; diff --git a/packages/seacas/libraries/ioss/src/Ioss_IOFactory.h b/packages/seacas/libraries/ioss/src/Ioss_IOFactory.h index 2467a21b7b..5d07acda3f 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_IOFactory.h +++ b/packages/seacas/libraries/ioss/src/Ioss_IOFactory.h @@ -24,7 +24,7 @@ namespace Ioss { class IOFactory; - using NameList = std::vector; + using NameList = Ioss::NameList; using IOFactoryMap = std::map>; class DatabaseIO; diff --git a/packages/seacas/libraries/ioss/src/Ioss_NamedSuffixVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_NamedSuffixVariableType.h index 0d7d647439..c81c922d40 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_NamedSuffixVariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_NamedSuffixVariableType.h @@ -32,12 +32,17 @@ namespace Ioss { } NamedSuffixVariableType(const NamedSuffixVariableType &) = delete; + IOSS_NODISCARD VariableType::Type type() const override { return Type::NAMED_SUFFIX; } + IOSS_NODISCARD std::string type_string() const override { return "NamedSuffix"; } + //! Define the suffix list for this field. // 'which' is 1-based to conform to the 'label' function usage. // If user doesn't add suffices, then 'label' will return "UNSET" void add_suffix(size_t which, const std::string &suffix) { suffixList[which - 1] = suffix; } + void print() const override final; + private: - std::vector suffixList{}; + Ioss::NameList suffixList{}; }; } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_ParallelUtils.C b/packages/seacas/libraries/ioss/src/Ioss_ParallelUtils.C index f81486815d..47938091e2 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ParallelUtils.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ParallelUtils.C @@ -55,11 +55,11 @@ void Ioss::ParallelUtils::add_environment_properties(Ioss::PropertyManager &prop if (get_environment("IOSS_PROPERTIES", env_props, parallel_size() > 1)) { // env_props string should be of the form // "PROP1=VALUE1:PROP2=VALUE2:..." - std::vector prop_val = tokenize(env_props, ":"); + Ioss::NameList prop_val = tokenize(env_props, ":"); int rank = parallel_rank(); for (auto &elem : prop_val) { - std::vector property = tokenize(elem, "="); + Ioss::NameList property = tokenize(elem, "="); if (property.size() != 2) { std::ostringstream errmsg; fmt::print( diff --git a/packages/seacas/libraries/ioss/src/Ioss_QuadratureVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_QuadratureVariableType.h new file mode 100644 index 0000000000..ea52aeded2 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_QuadratureVariableType.h @@ -0,0 +1,82 @@ +/* + * Copyright(C) 2024 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 + */ +#pragma once + +#include "ioss_export.h" + +#include "Ioss_CodeTypes.h" +#include + +#include "Ioss_VariableType.h" + +namespace Ioss { + struct QuadraturePoint + { + double xi; + double eta; + double zeta; + double weight; + }; + + class IOSS_EXPORT QuadratureVariableType : public VariableType + { + public: + // 'which' is 1-based + IOSS_NODISCARD std::string label(int which, const char /* suffix_sep */) const override + { + assert(which > 0 && which <= component_count()); + if (component_count() == 1) { + return ""; + } + return VariableType::numeric_label(which, component_count(), name()); + } + + QuadratureVariableType(const std::string &my_name, + const std::vector &quad_points, bool delete_me) + : Ioss::VariableType(Ioss::Utils::lowercase(my_name), quad_points.size(), delete_me), + m_quadratureType_(my_name), m_quadrature_(quad_points) + { + } + + QuadratureVariableType(const QuadratureVariableType &) = delete; + + IOSS_NODISCARD VariableType::Type type() const override { return Type::QUADRATURE; } + IOSS_NODISCARD std::string type_string() const override { return "Quadrature"; } + + IOSS_NODISCARD std::vector get_quadrature() const + { + return m_quadrature_; + } + IOSS_NODISCARD Ioss::QuadraturePoint get_quadrature_component(int which) const + { + assert(which > 0 && which <= component_count()); + return m_quadrature_[which - 1]; + } + + void print() const override final; + + private: + std::string m_quadratureType_{}; + std::vector m_quadrature_{}; + }; +} // namespace Ioss + +#if 0 +typedef struct ex_quadrature +{ + char name[EX_MAX_NAME + 1]; + int cardinality; /* Number of quadrature points */ + int dimension; /* 1,2,3 -- spatial dimension of points */ + double *xi; /* xi (x) coordinate of points; dimension = cardinality or NULL */ + double * + eta; /* eta (y) coordinate of points; dimension = cardinality if dimension = 2 or 3 or NULL */ + double + *zeta; /* zeta (z) coordinate of points; dimension = cardinality if dimension == 3. or NULL */ + double *weight; /* weights for each point; dimension = cardinality or NULL */ +} ex_quadrature; +#endif diff --git a/packages/seacas/libraries/ioss/src/Ioss_Region.C b/packages/seacas/libraries/ioss/src/Ioss_Region.C index b5a40b9b3e..c58a2e4ce0 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Region.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Region.C @@ -1788,7 +1788,7 @@ namespace Ioss { * */ int Region::get_aliases(const std::string &my_name, EntityType type, - std::vector &aliases) const + Ioss::NameList &aliases) const { IOSS_FUNC_ENTER(m_); size_t size = aliases.size(); diff --git a/packages/seacas/libraries/ioss/src/Ioss_Region.h b/packages/seacas/libraries/ioss/src/Ioss_Region.h index fd7a543ab4..7d04b9b154 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Region.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Region.h @@ -233,8 +233,7 @@ namespace Ioss { IOSS_NODISCARD const AliasMap &get_alias_map(EntityType entity_type) const; /// Get a map containing all aliases defined for the entity with basename 'my_name' - int get_aliases(const std::string &my_name, EntityType type, - std::vector &aliases) const; + int get_aliases(const std::string &my_name, EntityType type, Ioss::NameList &aliases) const; // This routine transfers all relevant aliases from the 'this' // region and applies them to the 'to' file. @@ -272,11 +271,11 @@ namespace Ioss { // An example would be 'element_block_count' for a region. IOSS_NODISCARD Property get_implicit_property(const std::string &my_name) const override; - IOSS_NODISCARD const std::vector &get_information_records() const; - void add_information_records(const std::vector &info); - void add_information_record(const std::string &info); + IOSS_NODISCARD const Ioss::NameList &get_information_records() const; + void add_information_records(const Ioss::NameList &info); + void add_information_record(const std::string &info); - IOSS_NODISCARD const std::vector &get_qa_records() const; + IOSS_NODISCARD const Ioss::NameList &get_qa_records() const; void add_qa_record(const std::string &code, const std::string &code_qa, const std::string &date = "", const std::string &time = ""); @@ -391,7 +390,7 @@ inline int64_t Ioss::Region::node_global_to_local(int64_t global, bool must_exis * * \returns The informative strings. */ -inline const std::vector &Ioss::Region::get_information_records() const +inline const Ioss::NameList &Ioss::Region::get_information_records() const { IOSS_FUNC_ENTER(m_); return get_database()->get_information_records(); @@ -401,7 +400,7 @@ inline const std::vector &Ioss::Region::get_information_records() c * * \param[in] info The strings to add. */ -inline void Ioss::Region::add_information_records(const std::vector &info) +inline void Ioss::Region::add_information_records(const Ioss::NameList &info) { IOSS_FUNC_ENTER(m_); return get_database()->add_information_records(info); @@ -449,7 +448,7 @@ inline void Ioss::Region::add_qa_record(const std::string &code, const std::stri * \returns All QA records in a single vector. Every 4 consecutive elements of the * vector make up a single QA record. */ -inline const std::vector &Ioss::Region::get_qa_records() const +inline const Ioss::NameList &Ioss::Region::get_qa_records() const { IOSS_FUNC_ENTER(m_); return get_database()->get_qa_records(); diff --git a/packages/seacas/libraries/ioss/src/Ioss_SideBlock.C b/packages/seacas/libraries/ioss/src/Ioss_SideBlock.C index f84221658a..53b9600da7 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_SideBlock.C +++ b/packages/seacas/libraries/ioss/src/Ioss_SideBlock.C @@ -143,7 +143,7 @@ Ioss::Property Ioss::SideBlock::get_implicit_property(const std::string &my_name return Ioss::EntityBlock::get_implicit_property(my_name); } -void Ioss::SideBlock::block_membership(std::vector &block_members) +void Ioss::SideBlock::block_membership(Ioss::NameList &block_members) { // Simplest case. If the surfaces are split by element block, then this will // return non-null diff --git a/packages/seacas/libraries/ioss/src/Ioss_SideBlock.h b/packages/seacas/libraries/ioss/src/Ioss_SideBlock.h index 8807d86afb..c400456a08 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_SideBlock.h +++ b/packages/seacas/libraries/ioss/src/Ioss_SideBlock.h @@ -79,7 +79,7 @@ namespace Ioss { IOSS_NODISCARD const SideSet *owner() const { return owner_; } IOSS_NODISCARD const Ioss::GroupingEntity *contained_in() const override { return owner_; } - void block_membership(std::vector &block_members) override; + void block_membership(Ioss::NameList &block_members) override; // Handle implicit properties -- These are calculated from data stored // in the grouping entity instead of having an explicit value assigned. @@ -137,8 +137,8 @@ namespace Ioss { const EntityBlock *parentBlock_{nullptr}; // Pointer to the SideSet (if any) that contains this side block. - std::vector blockMembership{}; // What element blocks do the - // elements in this sideset belong to. + Ioss::NameList blockMembership{}; // What element blocks do the + // elements in this sideset belong to. mutable int consistentSideNumber{-1}; }; } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_SideSet.C b/packages/seacas/libraries/ioss/src/Ioss_SideSet.C index 2fb019a420..b23927a37e 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_SideSet.C +++ b/packages/seacas/libraries/ioss/src/Ioss_SideSet.C @@ -165,12 +165,12 @@ int Ioss::SideSet::max_parametric_dimension() const return max_par_dim; } -void Ioss::SideSet::block_membership(std::vector &block_members) +void Ioss::SideSet::block_membership(Ioss::NameList &block_members) { IOSS_FUNC_ENTER(m_); if (blockMembership.empty()) { for (auto &sb : sideBlocks) { - std::vector blocks; + Ioss::NameList blocks; sb->block_membership(blocks); blockMembership.insert(blockMembership.end(), blocks.begin(), blocks.end()); } @@ -202,11 +202,11 @@ bool Ioss::SideSet::equal_(const SideSet &rhs, const bool /* quiet */) const } // COMPARE block membership - std::vector lhs_block_membership = this->blockMembership; - std::vector rhs_block_membership = rhs.blockMembership; + Ioss::NameList lhs_block_membership = this->blockMembership; + Ioss::NameList rhs_block_membership = rhs.blockMembership; for (const auto &lhs_block_member : lhs_block_membership) { - std::vector::iterator it; + Ioss::NameList::iterator it; for (it = rhs_block_membership.begin(); it != rhs_block_membership.end(); ++it) { if ((*it) == lhs_block_member) { break; diff --git a/packages/seacas/libraries/ioss/src/Ioss_SideSet.h b/packages/seacas/libraries/ioss/src/Ioss_SideSet.h index 71fbcc9679..1032a59626 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_SideSet.h +++ b/packages/seacas/libraries/ioss/src/Ioss_SideSet.h @@ -45,7 +45,7 @@ namespace Ioss { IOSS_NODISCARD size_t block_count() const { return sideBlocks.size(); } IOSS_NODISCARD SideBlock *get_block(size_t which) const; - void block_membership(std::vector &block_members) override; + void block_membership(Ioss::NameList &block_members) override; // Handle implicit properties -- These are calculated from data stored // in the grouping entity instead of having an explicit value assigned. @@ -70,8 +70,8 @@ namespace Ioss { bool equal_(const SideSet &rhs, bool quiet) const; private: - SideBlockContainer sideBlocks; - std::vector blockMembership; // What element blocks do the - // elements in this sideset belong to. + SideBlockContainer sideBlocks; + Ioss::NameList blockMembership; // What element blocks do the + // elements in this sideset belong to. }; } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_Utils.C b/packages/seacas/libraries/ioss/src/Ioss_Utils.C index a9c8978fc8..71f6d5fd16 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Utils.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Utils.C @@ -74,12 +74,12 @@ namespace { //////////////////////////////////////////////////////////////////////// bool is_separator(const char separator, const char value) { return separator == value; } - int match(const char *name1, const char *name2) + size_t match(const char *name1, const char *name2) { - int l1 = static_cast(std::strlen(name1)); - int l2 = static_cast(std::strlen(name2)); - int len = l1 < l2 ? l1 : l2; - for (int i = 0; i < len; i++) { + auto l1 = std::strlen(name1); + auto l2 = std::strlen(name2); + auto len = l1 < l2 ? l1 : l2; + for (size_t i = 0; i < len; i++) { if (name1[i] != name2[i]) { while (i > 0 && (isdigit(name1[i - 1]) != 0) && (isdigit(name2[i - 1]) != 0)) { i--; @@ -91,6 +91,11 @@ namespace { return len; } + size_t match(const std::string &name1, const std::string &name2) + { + return match(name1.c_str(), name2.c_str()); + } + // Split 'str' into 'tokens' based on the 'separator' character. // If 'str' starts with 1 or more 'separator', they are part of the // first token and not used for splitting. If there are multiple @@ -99,8 +104,7 @@ namespace { // characters of the next token. // `__this___is_a_string__for_tokens` will split to 6 tokens: // `__this` `__is` `a` `string` `_for` `tokens` - void field_tokenize(const std::string &str, const char separator, - std::vector &tokens) + Ioss::NameList field_tokenize(const std::string &str, const char separator) { std::string curr_token; // Skip leading separators... @@ -108,6 +112,7 @@ namespace { while (i < str.length() && is_separator(separator, str[i])) { curr_token += str[i++]; } + Ioss::NameList tokens; for (; i < str.length(); ++i) { char curr_char = str[i]; @@ -129,6 +134,7 @@ namespace { if (!curr_token.empty()) { tokens.push_back(curr_token); } + return tokens; } } // namespace @@ -275,24 +281,6 @@ std::string Ioss::Utils::encode_entity_name(const std::string &entity_type, int6 return fmt::format("{}_{}", entity_type, id); } -char **Ioss::Utils::get_name_array(size_t count, int size) -{ - auto *names = new char *[count]; - for (size_t i = 0; i < count; i++) { - names[i] = new char[size + 1]; - std::memset(names[i], '\0', size + 1); - } - return names; -} - -void Ioss::Utils::delete_name_array(char **names, int count) -{ - for (int i = 0; i < count; i++) { - delete[] names[i]; - } - delete[] names; -} - std::string Ioss::Utils::fixup_type(const std::string &base, int nodes_per_element, int spatial) { std::string type = base; @@ -388,8 +376,9 @@ int Ioss::Utils::field_warning(const Ioss::GroupingEntity *ge, const Ioss::Field } namespace { - const Ioss::VariableType *match_composite_field(char **names, Ioss::IntVector &which_names, - const char suffix_separator) + const Ioss::VariableType *match_composite_field(Ioss::NameList &names, + Ioss::IntVector &which_names, + const char suffix_separator) { // ASSUME: Fields are in order... // The field we are trying to match will be a composite field of @@ -456,7 +445,7 @@ namespace { return type; } - const Ioss::VariableType *match_single_field(char **names, Ioss::IntVector &which_names, + const Ioss::VariableType *match_single_field(Ioss::NameList &names, Ioss::IntVector &which_names, const char suffix_separator, bool ignore_realn_fields) { @@ -478,9 +467,9 @@ namespace { return type; } - Ioss::Field get_next_field(char **names, int num_names, size_t count, - Ioss::Field::RoleType fld_role, const char suffix_separator, - const int *truth_table, bool ignore_realn_fields) + Ioss::Field get_next_field(Ioss::NameList &names, size_t count, Ioss::Field::RoleType fld_role, + const char suffix_separator, const int *truth_table, + bool ignore_realn_fields) { // NOTE: 'names' are all lowercase at this point. @@ -488,12 +477,12 @@ namespace { // must have an field_suffix_sep in the name separating the suffixes from // the main name. - // Find first unused name (used names have '\0' as first character... - int index = 0; - bool found_valid = false; - for (index = 0; index < num_names; index++) { + // Find first unused name (used names are empty()) + size_t index = 0; + bool found_valid = false; + for (index = 0; index < names.size(); index++) { assert(truth_table == nullptr || truth_table[index] == 1 || truth_table[index] == 0); - if ((truth_table == nullptr || truth_table[index] == 1) && names[index][0] != '\0') { + if ((truth_table == nullptr || truth_table[index] == 1) && !names[index].empty()) { found_valid = true; break; } @@ -507,10 +496,10 @@ namespace { // At this point, name[index] should be a valid potential field // name and all names[i] with i < index are either already used or // not valid for this grouping entity (truth_table entry == 0). - assert(index < num_names && names[index][0] != '\0' && + assert(index < names.size() && !names[index].empty() && (truth_table == nullptr || truth_table[index] == 1)); - char *name = names[index]; - auto name_length = strlen(name); + auto name = names[index]; + auto name_length = name.length(); // Split the name up into tokens separated by the // 'suffix_separator'. Note that the basename itself could @@ -520,8 +509,7 @@ namespace { // (back_stress_xx_01). At the current time, a composite variable // type can only contain two non-composite variable types, so we // only need to look to be concerned with the last 1 or 2 tokens... - std::vector tokens; - field_tokenize(name, suffix_separator, tokens); + auto tokens = field_tokenize(name, suffix_separator); size_t num_tokens = tokens.size(); // Check that tokenizer did not return empty tokens... @@ -531,7 +519,7 @@ namespace { // Return a SCALAR field Ioss::Field field(name, Ioss::Field::REAL, IOSS_SCALAR(), fld_role, count); field.set_index(index); - names[index][0] = '\0'; + names[index] = ""; return field; } @@ -571,13 +559,13 @@ namespace { // It is possible that the first name(s) that match with two // suffices have a basename that match other names with only a // single suffix lc_cam_x, lc_cam_y, lc_sfarea. - for (int id = index + 1; id < num_names; id++) { - char *tst_name = names[id]; - std::vector subtokens; - field_tokenize(tst_name, suffix_separator, subtokens); + for (size_t id = index + 1; id < names.size(); id++) { + auto tst_name = names[id]; + auto subtokens = field_tokenize(tst_name, suffix_separator); if ((truth_table == nullptr || truth_table[id] == 1) && // Defined on this entity - std::strncmp(name, tst_name, bn_len) == 0 && // base portion must match - (!same_length || (strlen(tst_name) == name_length)) && + Ioss::Utils::str_equal(name.substr(0, bn_len), + tst_name.substr(0, bn_len)) && // base portion must match + (!same_length || tst_name.length() == name_length) && subtokens.size() == num_tokens) { which_names.push_back(id); } @@ -605,18 +593,18 @@ namespace { field.set_suffix_separator(suffix_separator); } // Are suffices upper or lowercase... - std::vector tmp; - field_tokenize(names[which_names[0]], suffix_separator, tmp); + auto tmp = field_tokenize(names[which_names[0]], suffix_separator); // If a composite variable, then need to check the interior suffix, not the last (which // will be 1,2,...) bool is_composite = dynamic_cast( field.transformed_storage()) != nullptr; + Ioss::Suffix suffix{tmp[tmp.size() - (is_composite ? 2 : 1)]}; field.set_suffices_uppercase(suffix.is_uppercase()); field.set_index(index); for (const auto &which_name : which_names) { - names[which_name][0] = '\0'; + names[which_name] = ""; } return field; } @@ -626,12 +614,11 @@ namespace { Ioss::Field field(name, Ioss::Field::REAL, IOSS_SCALAR(), fld_role, count); // Are suffices upper or lowercase... - std::vector tmp; - field_tokenize(names[which_names[0]], suffix_separator, tmp); + auto tmp = field_tokenize(names[which_names[0]], suffix_separator); Ioss::Suffix suffix{tmp[tmp.size() - 1]}; field.set_suffices_uppercase(suffix.is_uppercase()); field.set_index(index); - names[index][0] = '\0'; + names[index] = ""; return field; } } @@ -641,7 +628,7 @@ namespace { } // common - bool define_field(size_t nmatch, size_t match_length, char **names, + bool define_field(size_t nmatch, size_t match_length, Ioss::NameList &names, size_t index, std::vector &suffices, size_t entity_count, Ioss::Field::RoleType fld_role, std::vector &fields, bool strip_trailing_, bool ignore_realn_fields, char suffix_separator) @@ -655,12 +642,11 @@ namespace { nmatch = 1; } else { - char *name = names[0]; - name[match_length] = '\0'; - auto suffix = suffix_separator; - if (strip_trailing_ && name[match_length - 1] == '_') { - name[match_length - 1] = '\0'; - suffix = '_'; + auto name = names[index].substr(0, match_length); + auto suffix = suffix_separator; + if (strip_trailing_ && name.back() == '_') { + name = name.substr(0, name.length() - 1); + suffix = '_'; } Ioss::Field field(name, Ioss::Field::REAL, type, fld_role, entity_count); if (suffix != suffix_separator) { @@ -672,7 +658,7 @@ namespace { fields.push_back(field); } for (size_t j = 0; j < nmatch; j++) { - names[j][0] = '\0'; + names[index + j] = ""; } return true; } @@ -681,11 +667,11 @@ namespace { // NOTE: nmatch could be reset inside previous if block. // This is not an 'else' block, it is a new if block. if (nmatch == 1) { - Ioss::Field field(names[0], Ioss::Field::REAL, IOSS_SCALAR(), fld_role, entity_count); + Ioss::Field field(names[index], Ioss::Field::REAL, IOSS_SCALAR(), fld_role, entity_count); if (field.is_valid()) { fields.push_back(field); } - names[0][0] = '\0'; + names[index] = ""; return false; } return false; // Can't get here... Quiet the compiler @@ -694,9 +680,8 @@ namespace { // Read scalar fields off an input database and determine whether // they are components of a higher order type (vector, tensor, ...). -void Ioss::Utils::get_fields(int64_t entity_count, // The number of objects in this entity. - char **names, // Raw list of field names from exodus - int num_names, // Number of names in list +void Ioss::Utils::get_fields(int64_t entity_count, // The number of objects in this entity. + Ioss::NameList &names, // Raw list of field names from exodus Ioss::Field::RoleType fld_role, // Role of field const Ioss::DatabaseIO *db, int *local_truth, // Truth table for this entity; @@ -710,19 +695,19 @@ void Ioss::Utils::get_fields(int64_t entity_count, // The number of objects in t if (!enable_field_recognition) { // Create a separate field for each name. - for (int i = 0; i < num_names; i++) { - if (local_truth == nullptr || local_truth[i] == 1) { + for (size_t i = 0; i < names.size(); i++) { + if (!names[i].empty() && (local_truth == nullptr || local_truth[i] == 1)) { Ioss::Field field(names[i], Ioss::Field::REAL, IOSS_SCALAR(), fld_role, entity_count); field.set_index(i); fields.push_back(field); - names[i][0] = '\0'; + names[i] = ""; } } } else if (suffix_separator != 0) { while (true) { // NOTE: 'get_next_field' determines storage type (vector, tensor,...) - Ioss::Field field = get_next_field(names, num_names, entity_count, fld_role, suffix_separator, + Ioss::Field field = get_next_field(names, entity_count, fld_role, suffix_separator, local_truth, ignore_realn_fields); if (field.is_valid()) { fields.push_back(field); @@ -735,19 +720,22 @@ void Ioss::Utils::get_fields(int64_t entity_count, // The number of objects in t else { // This routine is used if there is no field component separator. E.g., // fieldx, fieldy, fieldz instead of field_x field_y field_z - int nmatch = 1; - int ibeg = 0; - int pmat = 0; + size_t nmatch = 1; + size_t ibeg = 0; + size_t pmat = 0; std::vector suffices; top: - while (ibeg + nmatch < num_names) { + while (ibeg + nmatch < names.size()) { if (local_truth != nullptr) { - while (ibeg < num_names && local_truth[ibeg] == 0) { + while (ibeg < names.size() && local_truth[ibeg] == 0) { ibeg++; } } - for (int i = ibeg + 1; i < num_names; i++) { + while (ibeg < names.size() && names[ibeg].empty()) { + ibeg++; + } + for (size_t i = ibeg + 1; i < names.size(); i++) { auto mat = match(names[ibeg], names[i]); if (local_truth != nullptr && local_truth[i] == 0) { mat = 0; @@ -763,8 +751,7 @@ void Ioss::Utils::get_fields(int64_t entity_count, // The number of objects in t // should match only 3 characters of the basename (sig), but // sigxx and sigxy will match 4 characters) so consider a // valid match if the match length is >= previous match length. - if ((std::strlen(names[ibeg]) == std::strlen(names[i])) && mat > 0 && - (pmat == 0 || mat >= pmat)) { + if ((names[ibeg].length() == names[i].length()) && mat > 0 && (pmat == 0 || mat >= pmat)) { nmatch++; if (nmatch == 2) { // Get suffix for first field in the match @@ -779,7 +766,7 @@ void Ioss::Utils::get_fields(int64_t entity_count, // The number of objects in t else { bool multi_component = - define_field(nmatch, pmat, &names[ibeg], suffices, entity_count, fld_role, fields, + define_field(nmatch, pmat, names, ibeg, suffices, entity_count, fld_role, fields, strip_trailing_, ignore_realn_fields, suffix_separator); if (!multi_component) { // Although we matched multiple suffices, it wasn't a @@ -803,10 +790,10 @@ void Ioss::Utils::get_fields(int64_t entity_count, // The number of objects in t // have forms a multi-component field; if not, then define a // scalar field and jump up to the loop again to handle the others // that had been gathered. - if (ibeg < num_names) { + if (ibeg < names.size()) { if (local_truth == nullptr || local_truth[ibeg] == 1) { bool multi_component = - define_field(nmatch, pmat, &names[ibeg], suffices, entity_count, fld_role, fields, + define_field(nmatch, pmat, names, ibeg, suffices, entity_count, fld_role, fields, strip_trailing_, ignore_realn_fields, suffix_separator); clear(suffices); if (nmatch > 1 && !multi_component) { @@ -1037,7 +1024,7 @@ double Ioss::Utils::timer() return std::chrono::duration(now - initial_time).count(); } -void Ioss::Utils::input_file(const std::string &file_name, std::vector *lines, +void Ioss::Utils::input_file(const std::string &file_name, Ioss::NameList *lines, size_t max_line_length) { // Create an ifstream for the input file. This does almost the same @@ -1357,7 +1344,7 @@ std::string Ioss::Utils::get_type_from_file(const std::string &filename) } void Ioss::Utils::info_fields(const Ioss::GroupingEntity *ige, Ioss::Field::RoleType role, - const std::string &header, const std::string &suffix) + const std::string &header, const std::string &suffix, bool detail) { Ioss::NameList fields = ige->field_describe(role); @@ -1380,13 +1367,19 @@ void Ioss::Utils::info_fields(const Ioss::GroupingEntity *ige, Ioss::Field::Role cur_out = header.size() + suffix.size() + 16; // Assume 2 tabs... } for (const auto &field_name : fields) { - const Ioss::VariableType *var_type = ige->get_field(field_name).raw_storage(); - int comp_count = var_type->component_count(); - fmt::print("{1:>{0}s}:{2} ", max_width, field_name, comp_count); - cur_out += max_width + 4; - if (cur_out + max_width >= width) { - fmt::print(suffix); - cur_out = 8; + if (detail) { + const auto &field_ref = ige->get_fieldref(field_name); + fmt::print("{}{}", field_ref, suffix); + } + else { + const Ioss::VariableType *var_type = ige->get_field(field_name).raw_storage(); + int comp_count = var_type->component_count(); + fmt::print("{1:>{0}s}:{2} ", max_width, field_name, comp_count); + cur_out += max_width + 4; + if (cur_out + max_width >= width) { + fmt::print(suffix); + cur_out = 8; + } } } if (!header.empty()) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_Utils.h b/packages/seacas/libraries/ioss/src/Ioss_Utils.h index e0479488ec..cb623ce1c1 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Utils.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Utils.h @@ -309,9 +309,6 @@ namespace Ioss { IOSS_NODISCARD static int log_power_2(uint64_t value); - IOSS_NODISCARD static char **get_name_array(size_t count, int size); - static void delete_name_array(char **names, int count); - /** \brief Get formatted time and date strings. * * Fill time_string and date_string with current time and date @@ -476,7 +473,7 @@ namespace Ioss { const std::string &type, const std::string &working_directory); - static void get_fields(int64_t entity_count, char **names, int num_names, + static void get_fields(int64_t entity_count, Ioss::NameList &names, Ioss::Field::RoleType fld_role, const DatabaseIO *db, int *local_truth, std::vector &fields); @@ -519,7 +516,7 @@ namespace Ioss { * \param[out] lines The vector of strings containing the lines of the file * \param[in] max_line_length The maximum number of characters in any line of the file. */ - static void input_file(const std::string &file_name, std::vector *lines, + static void input_file(const std::string &file_name, Ioss::NameList *lines, size_t max_line_length = 0); template IOSS_NODISCARD static std::string to_string(const T &t) @@ -565,14 +562,14 @@ namespace Ioss { static void generate_history_mesh(Ioss::Region *region); static void info_fields(const Ioss::GroupingEntity *ige, Ioss::Field::RoleType role, - const std::string &header, const std::string &suffix = "\n\t"); + const std::string &header, const std::string &suffix = "\n\t", + bool detail = false); static void info_property(const Ioss::GroupingEntity *ige, Ioss::Property::Origin origin, const std::string &header, const std::string &suffix = "\n\t", bool print_empty = false); - static void insert_sort_and_unique(const std::vector &src, - std::vector &dest) + static void insert_sort_and_unique(const Ioss::NameList &src, Ioss::NameList &dest) { dest.insert(dest.end(), src.begin(), src.end()); std::sort(dest.begin(), dest.end(), std::less<>()); diff --git a/packages/seacas/libraries/ioss/src/Ioss_VariableType.C b/packages/seacas/libraries/ioss/src/Ioss_VariableType.C index 8ff109ba0b..c14a7cfa15 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_VariableType.C +++ b/packages/seacas/libraries/ioss/src/Ioss_VariableType.C @@ -1,12 +1,15 @@ -// Copyright(C) 1999-2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2024 National Technology & Engineering Solutions // of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with // NTESS, the U.S. Government retains certain rights in this software. // // See packages/seacas/LICENSE for details +#include "Ioss_BasisVariableType.h" +#include "Ioss_ComposedVariableType.h" #include "Ioss_CompositeVariableType.h" #include "Ioss_ConstructedVariableType.h" #include "Ioss_NamedSuffixVariableType.h" +#include "Ioss_QuadratureVariableType.h" #include "Ioss_Utils.h" #include "Ioss_VariableType.h" #include @@ -43,19 +46,62 @@ namespace Ioss { { std::string low_type = Utils::lowercase(type); registry().insert(VTM_ValuePair(low_type, this), delete_me); + } + + VariableType::Type VariableType::type() const { return Type::UNKNOWN; } + std::string VariableType::type_string() const { return "Unknown"; } + std::string CompositeVariableType::type_string() const + { + return fmt::format("Composite: {}*{}", baseType->type_string(), copies_); + } + std::string ComposedVariableType::type_string() const + { + return fmt::format("Composed: {}*{}", baseType->type_string(), secondaryType->type_string()); + } + + void VariableType::print() const + { + fmt::print("\tVariableType '{}' of type '{}' with {} components.\n\n", name(), type_string(), + component_count()); + } + + void BasisVariableType::print() const + { + fmt::print("\tVariableType '{}' of type '{}' with {} components\n\t\tordinal subc: _dim, " + "_ordinal, _dof_ordinal, _num_dof\t xi eta zeta\n", + name(), type_string(), component_count()); + for (int i = 0; i < component_count(); i++) { + auto basis = get_basis_component(i + 1); + fmt::print("\t\t {:6}\t\t{:6}\t{:6}\t{:6}\t{:6}\t\t{:6.3}\t{:6.3}\t{:6.3}\n", i + 1, + basis.subc_dim, basis.subc_ordinal, basis.subc_dof_ordinal, basis.subc_num_dof, + basis.xi, basis.eta, basis.zeta); + } + fmt::print("\n"); + } + + void QuadratureVariableType::print() const + { + fmt::print("\tVariableType '{}' of type '{}' with {} components\n\t\t\t xi eta zeta " + "weight\n", + name(), type_string(), component_count()); + for (int i = 0; i < component_count(); i++) { + auto quad = get_quadrature_component(i + 1); + fmt::print("\t\t{}\t{:6.3}\t{:6.3}\t{:6.3}\t{:6.3}\n", i + 1, quad.xi, quad.eta, quad.zeta, + quad.weight); + } + fmt::print("\n"); + } - // Register uppercase version also - std::string up_type = Utils::uppercase(type); - registry().insert(VTM_ValuePair(up_type, this), false); + void NamedSuffixVariableType::print() const + { + fmt::print("\tVariableType '{}' of type '{}' with {} components\n\t\tSuffices: {}\n\n", name(), + type_string(), component_count(), fmt::join(suffixList, ", ")); } void VariableType::alias(const std::string &base, const std::string &syn) { registry().insert( VTM_ValuePair(Utils::lowercase(syn), const_cast(factory(base))), false); - // Register uppercase version also - std::string up_type = Utils::uppercase(syn); - registry().insert(VTM_ValuePair(up_type, const_cast(factory(base))), false); } Registry &VariableType::registry() @@ -90,6 +136,24 @@ namespace Ioss { return names; } + std::vector VariableType::external_types(Ioss::VariableType::Type type) + { + auto vars = registry().m_deleteThese; + + std::vector user_vars; + if (type == Ioss::VariableType::Type::UNKNOWN) { + user_vars = vars; + } + else { + for (auto *var : vars) { + if (var->type() == type) { + user_vars.push_back(var); + } + } + } + return user_vars; + } + bool VariableType::add_field_type_mapping(const std::string &raw_field, const std::string &raw_type) { @@ -104,8 +168,8 @@ namespace Ioss { return registry().customFieldTypes.insert(std::make_pair(field, type)).second; } - bool VariableType::create_named_suffix_field_type(const std::string &type_name, - const std::vector &suffices) + bool VariableType::create_named_suffix_type(const std::string &type_name, + const Ioss::NameList &suffices) { size_t count = suffices.size(); if (count < 1) { @@ -128,6 +192,40 @@ namespace Ioss { return true; } + bool VariableType::create_basis_type(const std::string &type_name, const Ioss::Basis &basis) + { + // See if the variable already exists... + std::string basis_name = Utils::lowercase(type_name); + if (registry().find(basis_name) != registry().end()) { + return false; + } + + // Create the variable. Note that the 'true' argument means Ioss will delete + // the pointer. + new BasisVariableType(type_name, basis, true); + return true; + } + + bool VariableType::create_quadrature_type(const std::string &type_name, + const std::vector &quad_points) + { + size_t count = quad_points.size(); + if (count < 1) { + return false; + } + + // See if the variable already exists... + std::string quad_name = Utils::lowercase(type_name); + if (registry().find(quad_name) != registry().end()) { + return false; + } + + // Create the variable. Note that the 'true' argument means Ioss will delete + // the pointer. + auto *var_type = new QuadratureVariableType(type_name, quad_points, true); + return (var_type != nullptr); + } + bool VariableType::get_field_type_mapping(const std::string &field, std::string *type) { // Returns true if a mapping exists, 'type' contains the mapped type. @@ -171,6 +269,39 @@ namespace Ioss { return inst; } + const VariableType *VariableType::factory(const std::string &raw_name, + const std::string &secondary) + { + VariableType *inst = nullptr; + std::string lc_name = Utils::lowercase(raw_name); + auto iter = registry().find(lc_name); + if (iter == registry().end()) { + bool can_construct = build_variable_type(lc_name); + if (can_construct) { + iter = registry().find(lc_name); + assert(iter != registry().end()); + inst = (*iter).second; + } + else { + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR: The variable type '{}' is not supported.\n", raw_name); + IOSS_ERROR(errmsg); + } + } + else { + inst = (*iter).second; + } + + if (!secondary.empty()) { + auto *sec_type = factory(secondary); + if (sec_type != nullptr) { + inst = ComposedVariableType::composed_variable_type(inst, sec_type); + } + } + assert(inst != nullptr); + return inst; + } + const VariableType *VariableType::factory(const std::vector &suffices, bool ignore_realn_fields) { @@ -238,14 +369,14 @@ namespace Ioss { return result; } - std::string VariableType::label_name(const std::string &base, int which, const char suffix_sep, - bool suffices_uppercase) const + std::string VariableType::label_name(const std::string &base, int which, const char suffix_sep1, + const char suffix_sep2, bool suffices_uppercase) const { std::string my_name = base; - std::string suffix = label(which, suffix_sep); + std::string suffix = label(which, suffix_sep2); if (!suffix.empty()) { - if (suffix_sep != 0) { - my_name += suffix_sep; + if (suffix_sep1 != 0) { + my_name += suffix_sep1; } if (suffices_uppercase) { my_name += Ioss::Utils::uppercase(suffix); diff --git a/packages/seacas/libraries/ioss/src/Ioss_VariableType.h b/packages/seacas/libraries/ioss/src/Ioss_VariableType.h index 4d7efaf43e..c34670f23b 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_VariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_VariableType.h @@ -17,6 +17,8 @@ #include "ioss_export.h" namespace Ioss { + struct Basis; + struct QuadraturePoint; class VariableType; } // namespace Ioss @@ -67,11 +69,42 @@ namespace Ioss { class IOSS_EXPORT VariableType { public: + enum class Type { + UNKNOWN, + SCALAR, + STANDARD, + COMPOSED, + COMPOSITE, + CONSTRUCTED, + ELEMENT, + NAMED_SUFFIX, + BASIS, + QUADRATURE + }; + static void alias(const std::string &base, const std::string &syn); static int describe(NameList *names); IOSS_NODISCARD static NameList describe(); - static bool create_named_suffix_field_type(const std::string &type_name, - const std::vector &suffices); + + // Return a list of types that have been defined + // externally... Basically a subset of the types in the + // `Registry::m_deleteThese` list... + IOSS_NODISCARD static std::vector + external_types(Ioss::VariableType::Type type); + + static bool create_named_suffix_type(const std::string &type_name, + const Ioss::NameList &suffices); + + // Backward compatibility... + __attribute__((__deprecated__)) static bool + create_named_suffix_field_type(const std::string &type_name, const Ioss::NameList &suffices) + { + return create_named_suffix_type(type_name, suffices); + } + + static bool create_basis_type(const std::string &type_name, const Ioss::Basis &basis); + static bool create_quadrature_type(const std::string &type_name, + const std::vector &quad_points); static bool get_field_type_mapping(const std::string &field, std::string *type); static bool add_field_type_mapping(const std::string &raw_field, const std::string &raw_type); @@ -79,28 +112,35 @@ namespace Ioss { VariableType &operator=(const VariableType &) = delete; virtual ~VariableType() = default; + virtual void print() const; + IOSS_NODISCARD int component_count() const; // Override this function if the derived class has no suffices // For example, a 'vector_2d' has suffices "x" and "y" // A 'quad4' has no suffices... IOSS_NODISCARD virtual int suffix_count() const; - IOSS_NODISCARD std::string name() const; + IOSS_NODISCARD std::string name() const; + IOSS_NODISCARD virtual Type type() const = 0; + IOSS_NODISCARD virtual std::string type_string() const = 0; IOSS_NODISCARD static std::string numeric_label(int which, int ncomp, const std::string &name); IOSS_NODISCARD virtual std::string label(int which, char suffix_sep = '_') const = 0; IOSS_NODISCARD virtual std::string label_name(const std::string &base, int which, - char suffix_sep = '_', + char suffix_sep1 = '_', char suffix_sep2 = '_', bool suffices_uppercase = false) const; IOSS_NODISCARD virtual bool match(const std::vector &suffices) const; IOSS_NODISCARD static const VariableType *factory(const std::string &raw_name, int copies = 1); + IOSS_NODISCARD static const VariableType *factory(const std::string &raw_name, + const std::string &secondary); IOSS_NODISCARD static const VariableType *factory(const std::vector &suffices, bool ignore_realn_fields = false); + static Registry ®istry(); + protected: VariableType(const std::string &type, int comp_count, bool delete_me = false); - static Registry ®istry(); private: const std::string name_; diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_CatalystManager.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_CatalystManager.C index 18eb533f41..991c8b28fa 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_CatalystManager.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_CatalystManager.C @@ -295,8 +295,8 @@ namespace Iocatalyst { void CatalystManager::broadCastString(IOSS_MAYBE_UNUSED std::string &s, IOSS_MAYBE_UNUSED const Ioss::ParallelUtils &putils) { - IOSS_MAYBE_UNUSED(s); - IOSS_MAYBE_UNUSED(putils); + IOSS_PAR_UNUSED(s); + IOSS_PAR_UNUSED(putils); #ifdef SEACAS_HAVE_MPI int size = s.size(); putils.broadcast(size); @@ -310,8 +310,8 @@ namespace Iocatalyst { void CatalystManager::broadCastStatusCode(IOSS_MAYBE_UNUSED bool &statusCode, IOSS_MAYBE_UNUSED const Ioss::ParallelUtils &putils) { - IOSS_MAYBE_UNUSED(statusCode); - IOSS_MAYBE_UNUSED(putils); + IOSS_PAR_UNUSED(statusCode); + IOSS_PAR_UNUSED(putils); #ifdef SEACAS_HAVE_MPI int code = statusCode; diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C index 69224c565a..d682ff764e 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C @@ -7,8 +7,8 @@ #include #include -#include "Ioss_CommSet.h" // for CommSet #include "Ioss_CodeTypes.h" // for IOSS_SCALAR() +#include "Ioss_CommSet.h" // for CommSet #include "Ioss_DBUsage.h" // for DatabaseUsage, etc #include "Ioss_DatabaseIO.h" // for DatabaseIO #include "Ioss_EdgeBlock.h" // for EdgeBlock @@ -142,7 +142,7 @@ namespace Iocatalyst { inline static const std::string RANGEEND = "m_rangeEnd"; inline static const std::string FACE = "m_face"; - inline static const std::string SURFACESPLITTYPE = "surface_split_type"; + inline static const std::string SURFACESPLITTYPE = "surface_split_type"; std::string getValuePath(const std::string &prop) { @@ -308,9 +308,10 @@ namespace Iocatalyst { bool defineModel(Ioss::Region *region) { - if(!region->model_defined()) { + if (!region->model_defined()) { std::ostringstream errmsg; - errmsg << "Catalyst Write in defineModel(): model isn't defined in region"<<"\n"; + errmsg << "Catalyst Write in defineModel(): model isn't defined in region" + << "\n"; IOSS_ERROR(errmsg); } @@ -818,22 +819,17 @@ namespace Iocatalyst { template bool readFields(const conduit_cpp::Node &&parent, GroupingEntityT *block) const { - //Assumption: count = entity_count (in block) - Ioss::DatabaseIO *dbase = block->get_database(); - Ioss::EntityType b_t = block->type(); - bool is_entity_block = false; - if(b_t == Ioss::EntityType::ELEMENTBLOCK || - b_t == Ioss::EntityType::EDGEBLOCK || - b_t == Ioss::EntityType::FACEBLOCK || - b_t == Ioss::EntityType::NODEBLOCK || - b_t == Ioss::EntityType::SIDEBLOCK || - b_t == Ioss::EntityType::STRUCTUREDBLOCK) { - is_entity_block = true; + // Assumption: count = entity_count (in block) + Ioss::DatabaseIO *dbase = block->get_database(); + Ioss::EntityType b_t = block->type(); + bool is_entity_block = false; + if (b_t == Ioss::EntityType::ELEMENTBLOCK || b_t == Ioss::EntityType::EDGEBLOCK || + b_t == Ioss::EntityType::FACEBLOCK || b_t == Ioss::EntityType::NODEBLOCK || + b_t == Ioss::EntityType::SIDEBLOCK || b_t == Ioss::EntityType::STRUCTUREDBLOCK) { + is_entity_block = true; } - const int FNAME_MAX_LENGTH = 255; - size_t field_count = 0; - char **field_names = - Ioss::Utils::get_name_array(parent.number_of_children(), FNAME_MAX_LENGTH); + Ioss::NameList field_names; + field_names.reserve(parent.number_of_children()); size_t entity_count = 0; for (conduit_index_t idx = 0, max = parent.number_of_children(); idx < max; ++idx) { @@ -845,17 +841,15 @@ namespace Iocatalyst { const auto index = child[detail::INDEX].as_int64(); const auto storage = child[detail::STORAGE].as_string(); if (!block->field_exists(name)) { - if(storage == IOSS_SCALAR() && role==Ioss::Field::TRANSIENT && is_entity_block) { - //Add to get_fields() call - char field_name[FNAME_MAX_LENGTH + 1]; - Ioss::Utils::copy_string(field_name, name, FNAME_MAX_LENGTH + 1); - Ioss::Utils::copy_string(field_names[field_count++], field_name, FNAME_MAX_LENGTH + 1); - if(entity_count == 0) + if (storage == IOSS_SCALAR() && role == Ioss::Field::TRANSIENT && is_entity_block) { + // Add to get_fields() call + field_names.push_back(name); + if (entity_count == 0) entity_count = count; } else { block->field_add( - Ioss::Field(name, type, storage, role, count, index).set_zero_copy_enabled()); + Ioss::Field(name, type, storage, role, count, index).set_zero_copy_enabled()); } } else { @@ -864,22 +858,23 @@ namespace Iocatalyst { if (!field_block.has_transform()) { block->get_fieldref(name).set_zero_copy_enabled(); } - auto field_conduit = Ioss::Field(name, type, storage, role, count, index).set_zero_copy_enabled(); - if(field_block != field_conduit) { + auto field_conduit = + Ioss::Field(name, type, storage, role, count, index).set_zero_copy_enabled(); + if (field_block != field_conduit) { std::ostringstream errmsg; - errmsg << "Catalyst Read: Field '"<name().c_str()<< - "' of type '"<type_string()<<"' and differs from it\n"; + errmsg << "Catalyst Read: Field '" << name << "' from conduit " + << "already exists in block '" << block->name().c_str() << "' of type '" + << block->type_string() << "' and differs from it\n"; IOSS_ERROR(errmsg); } } } - - //Apply Exodus Properties to Scalar Fields in Entity Blocks - if(field_count > 0) { + + // Apply Exodus Properties to Scalar Fields in Entity Blocks + if (!field_names.empty()) { std::vector fields; - Ioss::Utils::get_fields(entity_count, field_names, field_count, Ioss::Field::TRANSIENT, - dbase, nullptr, fields); + Ioss::Utils::get_fields(entity_count, field_names, Ioss::Field::TRANSIENT, dbase, nullptr, + fields); for (const auto &field : fields) { block->field_add(field.set_zero_copy_enabled()); } @@ -1091,10 +1086,10 @@ namespace Iocatalyst { auto &node = this->DBNode; region->get_database()->set_int_byte_size_api( static_cast(node[detail::getAPISizePath()].as_int8())); - const auto write_split_type = - static_cast(node[detail::SURFACESPLITTYPE].as_int8()); - if(write_split_type != region->get_database()->get_surface_split_type()) { - static_cast(region->get_database())->set_split_type_changed(true); + const auto write_split_type = + static_cast(node[detail::SURFACESPLITTYPE].as_int8()); + if (write_split_type != region->get_database()->get_surface_split_type()) { + static_cast(region->get_database())->set_split_type_changed(true); } auto tpath = detail::REGION + detail::FS + detail::TIME; @@ -1107,9 +1102,9 @@ namespace Iocatalyst { this->readEntityGroup(node[detail::EDGEBLOCKS], region); this->readEntityGroup(node[detail::FACEBLOCKS], region); - bool surface_split_changed = - static_cast(region->get_database())->split_type_changed(); - if(!surface_split_changed) { + bool surface_split_changed = + static_cast(region->get_database())->split_type_changed(); + if (!surface_split_changed) { this->readEntityGroup(node[detail::SIDEBLOCKS], region); this->readEntityGroup(node[detail::SIDESETS], region); } @@ -1180,17 +1175,19 @@ namespace Iocatalyst { bool DatabaseIO::end_nl(Ioss::State state) { - if(this->dbState != state) { + if (this->dbState != state) { std::ostringstream errmsg; - errmsg << "Catalyst: dbState != state in end_nl"<<"\n"; + errmsg << "Catalyst: dbState != state in end_nl" + << "\n"; IOSS_ERROR(errmsg); } if (!is_input()) { auto region = this->get_region(); - if(region == nullptr) { + if (region == nullptr) { std::ostringstream errmsg; - errmsg << "Catalyst: region is nullptr in end_nl"<<"\n"; + errmsg << "Catalyst: region is nullptr in end_nl" + << "\n"; IOSS_ERROR(errmsg); } @@ -1252,9 +1249,10 @@ namespace Iocatalyst { void DatabaseIO::read_meta_data_nl() { auto region = this->get_region(); - if(region == nullptr) { + if (region == nullptr) { std::ostringstream errmsg; - errmsg << "Catalyst: region is nullptr in read_meta_data_nl()"<<"\n"; + errmsg << "Catalyst: region is nullptr in read_meta_data_nl()" + << "\n"; IOSS_ERROR(errmsg); } @@ -1265,9 +1263,10 @@ namespace Iocatalyst { void DatabaseIO::get_step_times_nl() { auto region = this->get_region(); - if(region == nullptr) { + if (region == nullptr) { std::ostringstream errmsg; - errmsg << "Catalyst: region is nullptr in get_step_times_nl()"<<"\n"; + errmsg << "Catalyst: region is nullptr in get_step_times_nl()" + << "\n"; IOSS_ERROR(errmsg); } @@ -1500,7 +1499,9 @@ namespace Iocatalyst { int64_t DatabaseIO::get_field_internal(const Ioss::SideBlock *sb, const Ioss::Field &field, void *data, size_t data_size) const { - if(split_type_changed()) { return -1; } + if (split_type_changed()) { + return -1; + } auto &impl = (*this->Impl.get()); return impl.getField(detail::SIDEBLOCKS, sb, field, data, data_size); } @@ -1531,7 +1532,9 @@ namespace Iocatalyst { int64_t DatabaseIO::get_field_internal(const Ioss::SideSet *ss, const Ioss::Field &field, void *data, size_t data_size) const { - if(split_type_changed()) { return -1; } + if (split_type_changed()) { + return -1; + } auto &impl = (*this->Impl.get()); return impl.getField(detail::SIDESETS, ss, field, data, data_size); } @@ -1613,7 +1616,9 @@ namespace Iocatalyst { int64_t DatabaseIO::get_zc_field_internal(const Ioss::SideBlock *sb, const Ioss::Field &field, void **data, size_t *data_size) const { - if(split_type_changed()) { return -1; } + if (split_type_changed()) { + return -1; + } auto &impl = (*this->Impl.get()); return impl.getFieldZeroCopy(detail::SIDEBLOCKS, sb, field, data, data_size); } @@ -1644,7 +1649,9 @@ namespace Iocatalyst { int64_t DatabaseIO::get_zc_field_internal(const Ioss::SideSet *ss, const Ioss::Field &field, void **data, size_t *data_size) const { - if(split_type_changed()) { return -1; } + if (split_type_changed()) { + return -1; + } auto &impl = (*this->Impl.get()); return impl.getFieldZeroCopy(detail::SIDESETS, ss, field, data, data_size); } diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_IOFactory.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_IOFactory.C index 1a13b1bc37..9579fe9c94 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_IOFactory.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_IOFactory.C @@ -4,9 +4,9 @@ // // See packages/seacas/LICENSE for details -#include "Ioss_DBUsage.h" // for DatabaseUsage -#include "Ioss_IOFactory.h" // for IOFactory -#include // for DatabaseIO +#include "Ioss_DBUsage.h" // for DatabaseUsage +#include "Ioss_IOFactory.h" // for IOFactory +#include #include #include // for nullptr #include // for string diff --git a/packages/seacas/libraries/ioss/src/cgns/Iocgns_DatabaseIO.C b/packages/seacas/libraries/ioss/src/cgns/Iocgns_DatabaseIO.C index 52e8bca91c..e0b17839a3 100644 --- a/packages/seacas/libraries/ioss/src/cgns/Iocgns_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/cgns/Iocgns_DatabaseIO.C @@ -1058,8 +1058,8 @@ namespace Iocgns { int tot_names = util().gather(num_bc, CGNS_MAX_NAME_LENGTH + 1, bc_names, all_bc_names); if (myProcessor == 0) { - int off_name = 0; - std::vector bc; + int off_name = 0; + Ioss::NameList bc; for (int ibc = 0; ibc < tot_names; ibc++) { bc.emplace_back(&all_bc_names[off_name]); off_name += CGNS_MAX_NAME_LENGTH + 1; @@ -1079,8 +1079,8 @@ namespace Iocgns { bc_names.resize(tot_names * (CGNS_MAX_NAME_LENGTH + 1)); util().broadcast(bc_names); - std::vector bc; - int off_name = 0; + Ioss::NameList bc; + int off_name = 0; for (int ibc = 0; ibc < tot_names; ibc++) { bc.emplace_back(&bc_names[off_name]); off_name += CGNS_MAX_NAME_LENGTH + 1; @@ -1631,7 +1631,7 @@ namespace Iocgns { m_zoneOffset.resize(num_zones + 1); // use 1-based zones... elementCount = - Utils::common_write_meta_data(get_file_pointer(), *get_region(), m_zoneOffset, false); + Utils::common_write_metadata(get_file_pointer(), *get_region(), m_zoneOffset, false); } void DatabaseIO::get_step_times_nl() diff --git a/packages/seacas/libraries/ioss/src/cgns/Iocgns_IOFactory.C b/packages/seacas/libraries/ioss/src/cgns/Iocgns_IOFactory.C index 26c87a87ff..1c445a6f45 100644 --- a/packages/seacas/libraries/ioss/src/cgns/Iocgns_IOFactory.C +++ b/packages/seacas/libraries/ioss/src/cgns/Iocgns_IOFactory.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2020, 2022 National Technology & Engineering Solutions +// Copyright(C) 1999-2020, 2022, 2023, 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. // diff --git a/packages/seacas/libraries/ioss/src/cgns/Iocgns_ParallelDatabaseIO.C b/packages/seacas/libraries/ioss/src/cgns/Iocgns_ParallelDatabaseIO.C index 16f4bdd027..3ba956c993 100644 --- a/packages/seacas/libraries/ioss/src/cgns/Iocgns_ParallelDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/cgns/Iocgns_ParallelDatabaseIO.C @@ -797,7 +797,7 @@ namespace Iocgns { m_zoneOffset.resize(num_zones + 1); // use 1-based zones... elementCount = - Utils::common_write_meta_data(get_file_pointer(), *get_region(), m_zoneOffset, true); + Utils::common_write_metadata(get_file_pointer(), *get_region(), m_zoneOffset, true); } void ParallelDatabaseIO::get_step_times_nl() diff --git a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C index af70a4ba3f..f4fa782a21 100644 --- a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C +++ b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C @@ -448,6 +448,7 @@ namespace { fmt::print("\t+{2:-^{0}}+{2:-^{1}}+\n", max_name, max_face, ""); } #endif + } // namespace std::pair Iocgns::Utils::decompose_name(const std::string &name, bool is_parallel) @@ -893,7 +894,7 @@ namespace { // Now clean out existing ZGC lists for all blocks and add on the consolidated instances. // Also create a vector for mapping from zone to sb name. - std::vector sb_names(structured_blocks.size() + 1); + Ioss::NameList sb_names(structured_blocks.size() + 1); for (auto &sb : structured_blocks) { sb->m_zoneConnectivity.clear(); auto zone = sb->get_property("zone").get_int(); @@ -1066,8 +1067,8 @@ void Iocgns::Utils::write_state_meta_data(int file_ptr, const Ioss::Region ®i } } -size_t Iocgns::Utils::common_write_meta_data(int file_ptr, const Ioss::Region ®ion, - std::vector &zone_offset, bool is_parallel_io) +size_t Iocgns::Utils::common_write_metadata(int file_ptr, const Ioss::Region ®ion, + std::vector &zone_offset, bool is_parallel_io) { #if !IOSS_ENABLE_HYBRID // Make sure mesh is not hybrid... @@ -1081,7 +1082,7 @@ size_t Iocgns::Utils::common_write_meta_data(int file_ptr, const Ioss::Region &r } #endif - region.get_database()->progress("\tEnter common_write_meta_data"); + region.get_database()->progress("\tEnter common_write_metadata"); int base = 0; int phys_dimension = region.get_property("spatial_dimension").get_int(); CGERR(cg_base_write(file_ptr, "Base", phys_dimension, phys_dimension, &base)); @@ -1433,7 +1434,7 @@ size_t Iocgns::Utils::common_write_meta_data(int file_ptr, const Ioss::Region &r } } - region.get_database()->progress("\tReturn from common_write_meta_data"); + region.get_database()->progress("\tReturn from common_write_metadata"); return element_count; } @@ -2302,19 +2303,20 @@ void Iocgns::Utils::add_transient_variables(int cgns_file_ptr, const std::vector int field_count = 0; CGCHECK(cg_nfields(cgns_file_ptr, b, z, sol, &field_count)); - char **field_names = Ioss::Utils::get_name_array(field_count, CGNS_MAX_NAME_LENGTH); + Ioss::NameList field_names; + field_names.reserve(field_count); for (int field = 1; field <= field_count; field++) { CGNS_ENUMT(DataType_t) data_type; char field_name[CGNS_MAX_NAME_LENGTH + 1]; CGCHECK(cg_field_info(cgns_file_ptr, b, z, sol, field, &data_type, field_name)); - Ioss::Utils::copy_string(field_names[field - 1], field_name, CGNS_MAX_NAME_LENGTH + 1); + field_names.emplace_back(field_name); } // Convert raw field names into composite fields (a_x, a_y, a_z ==> 3D vector 'a') std::vector fields; if (grid_loc == CGNS_ENUMV(CellCenter)) { size_t entity_count = block->entity_count(); - Ioss::Utils::get_fields(entity_count, field_names, field_count, Ioss::Field::TRANSIENT, + Ioss::Utils::get_fields(entity_count, field_names, Ioss::Field::TRANSIENT, region->get_database(), nullptr, fields); size_t index = 1; for (const auto &field : fields) { @@ -2338,7 +2340,7 @@ void Iocgns::Utils::add_transient_variables(int cgns_file_ptr, const std::vector IOSS_ERROR(errmsg); } size_t entity_count = nb->entity_count(); - Ioss::Utils::get_fields(entity_count, field_names, field_count, Ioss::Field::TRANSIENT, + Ioss::Utils::get_fields(entity_count, field_names, Ioss::Field::TRANSIENT, region->get_database(), nullptr, fields); size_t index = 1; for (const auto &field : fields) { @@ -2347,8 +2349,6 @@ void Iocgns::Utils::add_transient_variables(int cgns_file_ptr, const std::vector nb->field_add(field); } } - - Ioss::Utils::delete_name_array(field_names, field_count); } }; // ========================================== @@ -2420,7 +2420,7 @@ void Iocgns::Utils::set_line_decomposition(int cgns_file_ptr, const std::string int num_families = 0; CGCHECKNP(cg_nfamilies(cgns_file_ptr, base, &num_families)); - std::vector families; + Ioss::NameList families; families.reserve(num_families); for (int family = 1; family <= num_families; family++) { char name[CGNS_MAX_NAME_LENGTH + 1]; diff --git a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.h b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.h index 15c6f727e7..874f0dd79f 100644 --- a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.h +++ b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.h @@ -300,7 +300,7 @@ namespace Iocgns { static void write_state_meta_data(int file_ptr, const Ioss::Region ®ion, bool is_parallel_io); - static size_t common_write_meta_data(int file_ptr, const Ioss::Region ®ion, + static size_t common_write_metadata(int file_ptr, const Ioss::Region ®ion, std::vector &zone_offset, bool is_parallel); static size_t resolve_nodes(Ioss::Region ®ion, int my_processor, bool is_parallel); IOSS_NODISCARD static std::vector>> diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index 6900a31923..c243fa44dd 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -21,8 +21,11 @@ #include "Ioex_Utils.h" #include "Ioss_Assembly.h" +#include "Ioss_BasisVariableType.h" #include "Ioss_Blob.h" #include "Ioss_CodeTypes.h" +#include "Ioss_ComposedVariableType.h" +#include "Ioss_CompositeVariableType.h" #include "Ioss_DBUsage.h" #include "Ioss_DatabaseIO.h" #include "Ioss_EdgeBlock.h" @@ -31,6 +34,7 @@ #include "Ioss_ElementSet.h" #include "Ioss_EntityBlock.h" #include "Ioss_EntityType.h" +#include "Ioss_Enumerate.h" #include "Ioss_FaceBlock.h" #include "Ioss_FaceSet.h" #include "Ioss_Field.h" @@ -39,11 +43,13 @@ #include "Ioss_IOFactory.h" #include "Ioss_Map.h" #include "Ioss_MeshType.h" +#include "Ioss_NamedSuffixVariableType.h" #include "Ioss_NodeBlock.h" #include "Ioss_NodeSet.h" #include "Ioss_ParallelUtils.h" #include "Ioss_Property.h" #include "Ioss_PropertyManager.h" +#include "Ioss_QuadratureVariableType.h" #include "Ioss_Region.h" #include "Ioss_SerializeIO.h" #include "Ioss_SideBlock.h" @@ -96,7 +102,7 @@ namespace { { } - void update_list_from_assembly_tree(size_t assemblyIndex, std::vector &list) + void update_list_from_assembly_tree(size_t assemblyIndex, Ioss::NameList &list) { // Walk the tree without cyclic dependency if (assemblyIndex < m_assemblies.size()) { @@ -139,7 +145,7 @@ namespace { } } - void update_assembly_filter_list(std::vector &assemblyFilterList) + void update_assembly_filter_list(Ioss::NameList &assemblyFilterList) { for (size_t i = 0; i < m_assemblies.size(); ++i) { if (m_visitedAssemblies[i]) { @@ -196,6 +202,7 @@ namespace { delete[] assembly.name; } } + } // namespace namespace Ioex { @@ -641,7 +648,7 @@ namespace Ioex { if (!using_parallel_io() || myProcessor == 0) { // dump info records, include the product_registry // See if the input file was specified as a property on the database... - std::vector input_lines; + Ioss::NameList input_lines; if (get_region()->property_exists("input_file_name")) { std::string filename = get_region()->get_property("input_file_name").get_string(); // Determine size of input file so can embed it in info records... @@ -666,7 +673,7 @@ namespace Ioex { total_lines = in_lines + qa_lines + info_rec_size + config_lines; // 'total_lines' pointers to char buffers - info = Ioss::Utils::get_name_array(total_lines, max_line_length); + info = Ioex::get_name_array(total_lines, max_line_length); int i = 0; Ioss::Utils::copy_string(info[i++], Ioss::Utils::platform_information(), max_line_length + 1); @@ -693,7 +700,7 @@ namespace Ioex { int ierr = 0; if (!using_parallel_io() || myProcessor == 0) { ierr = ex_put_info(get_file_pointer(), total_lines, info); - Ioss::Utils::delete_name_array(info, total_lines); + Ioex::delete_name_array(info, total_lines); } else { // This call only sets the `total_lines` metadata on the other ranks... @@ -729,8 +736,8 @@ namespace Ioex { // Query number of assemblies... auto assemblies = get_exodus_assemblies(get_file_pointer()); if (!assemblies.empty()) { - std::vector exclusions; - std::vector inclusions; + Ioss::NameList exclusions; + Ioss::NameList inclusions; AssemblyTreeFilter inclusionFilter(get_region(), Ioss::ELEMENTBLOCK, assemblies); AssemblyTreeFilter exclusionFilter(get_region(), Ioss::ELEMENTBLOCK, assemblies); @@ -1041,8 +1048,8 @@ namespace Ioex { } // common - void BaseDatabaseIO::compute_block_membership_nl(Ioss::SideBlock *efblock, - std::vector &block_membership) const + void BaseDatabaseIO::compute_block_membership_nl(Ioss::SideBlock *efblock, + Ioss::NameList &block_membership) const { const Ioss::ElementBlockContainer &element_blocks = get_region()->get_element_blocks(); assert(Ioss::Utils::check_block_order(element_blocks)); @@ -1639,6 +1646,7 @@ namespace Ioex { } if (nvar > 0) { + if (truth_table.empty()) { truth_table.resize(block_count * nvar); @@ -1673,55 +1681,177 @@ namespace Ioex { // Get the variable names and add as fields. Need to decode these // into vector/tensor/... eventually, for now store all as // scalars. - char **names = Ioss::Utils::get_name_array(nvar, maximumNameLength); // Read the names... // (Currently, names are read for every block. We could save them...) + Ioss::NameList names; { Ioss::SerializeIO serializeIO_(this); + names = Ioex::get_variable_names(nvar, maximumNameLength, get_file_pointer(), type); + } + + // Add to VariableNameMap so can determine exodusII index given a + // Sierra field name. exodusII index is just 'i+1' + for (int i = 0; i < nvar; i++) { + std::string var = names[i]; + if (lowerCaseVariableNames) { + Ioss::Utils::fixup_name(var); + } + variables.insert(VNMValuePair(var, i + 1)); + } + + int offset = position * nvar; + int *local_truth = nullptr; + if (!truth_table.empty()) { + local_truth = &truth_table[offset]; + } + + // If the file contains field metadata, define fields via that mechanism... + auto fields = get_fields_via_field_metadata(entity, type, names); + + // Now, either get all fields via suffix matching, or if the file did not + // specify all fields in the field metadata, get the rest... + Ioss::Utils::get_fields(entity->entity_count(), names, Ioss::Field::TRANSIENT, this, + local_truth, fields); + + for (auto &field : fields) { + if (lowerCaseVariableNames) { + Ioss::Utils::fixup_name(field.get_name()); + } + entity->field_add(field); + } + + for (auto [i, name] : Ioss::enumerate(names)) { + // Verify that all names were used for a field... + SMART_ASSERT(name.empty() || (local_truth && local_truth[i] == 0))(i)(name); + } + } + return nvar; + } - int ierr = ex_get_variable_names(get_file_pointer(), type, nvar, names); + std::vector + Ioex::BaseDatabaseIO::get_fields_via_field_metadata(Ioss::GroupingEntity *entity, + ex_entity_type type, Ioss::NameList &names) + { + std::vector fields; + if (!entity->get_database()->get_field_recognition()) { + return fields; + } + // See if this entity is using enhanced field attributes... + auto id = entity->get_optional_property("id", 0); + int enhanced_fld_cnt; + { + Ioss::SerializeIO serializeIO_(this); + enhanced_fld_cnt = ex_get_field_metadata_count(get_file_pointer(), type, id); + } + + if (enhanced_fld_cnt > 0) { + std::vector exo_fields(enhanced_fld_cnt); + for (auto &field : exo_fields) { + field.entity_type = type; + field.entity_id = id; + } + + { + Ioss::SerializeIO serializeIO_(this); + int ierr = ex_get_field_metadata(get_file_pointer(), Data(exo_fields)); if (ierr < 0) { Ioex::exodus_error(get_file_pointer(), __LINE__, __func__, __FILE__); } + } - // Add to VariableNameMap so can determine exodusII index given a - // Sierra field name. exodusII index is just 'i+1' - for (int i = 0; i < nvar; i++) { - std::string var = names[i]; - if (lowerCaseVariableNames) { - Ioss::Utils::fixup_name(var); - } - variables.insert(VNMValuePair(var, i + 1)); - } + for (const auto &exo_field : exo_fields) { + std::string ios_field_type{}; - int offset = position * nvar; - int *local_truth = nullptr; - if (!truth_table.empty()) { - local_truth = &truth_table[offset]; + auto type_names = Ioss::tokenize(exo_field.type_name, ",", true); + if (exo_field.type[0] == EX_FIELD_TYPE_SEQUENCE) { + ios_field_type = fmt::format("Real[{}]", exo_field.cardinality[0]); + } + else if (exo_field.type[0] == EX_FIELD_TYPE_USER_DEFINED) { + auto suffices = Ioss::tokenize(exo_field.suffices, ","); + Ioss::VariableType::create_named_suffix_type(exo_field.name, suffices); + ios_field_type = exo_field.name; + } + else if (exo_field.type[0] == EX_BASIS || exo_field.type[0] == EX_QUADRATURE) { + ios_field_type = Ioss::Utils::lowercase(type_names[0]); + } + else { + ios_field_type = Ioex::map_ioss_field_type(exo_field.type[0]); } - std::vector fields; - int64_t count = entity->entity_count(); - Ioss::Utils::get_fields(count, names, nvar, Ioss::Field::TRANSIENT, this, local_truth, - fields); + int num_copies = 1; + std::string secondary_field_type{}; - for (auto &field : fields) { - if (lowerCaseVariableNames) { - Ioss::Utils::fixup_name(field.get_name()); + if (exo_field.nesting == 2) { + // For IOSS, the nesting is basically N copies of the field + // at nesting level 1, so we just need to verify that the + // field type is `EX_FIELD_TYPE_SEQUENCE`, `EX_BASIS`, or + // `EX_QUADRATURE` and then get the cardinality... + if (exo_field.type[1] == EX_FIELD_TYPE_SEQUENCE) { + num_copies = exo_field.cardinality[1]; + } + else if (exo_field.type[1] == EX_BASIS || exo_field.type[1] == EX_QUADRATURE) { + secondary_field_type = Ioss::Utils::lowercase(type_names[1]); + } + else { + fmt::print("ERROR: Unrecognized field type for nested field.\n"); } - entity->field_add(field); + } + if (secondary_field_type.empty()) { + fields.emplace_back(exo_field.name, Ioss::Field::REAL, ios_field_type, num_copies, + Ioss::Field::TRANSIENT, entity->entity_count()); + } + else { + fields.emplace_back(exo_field.name, Ioss::Field::REAL, ios_field_type, + secondary_field_type, Ioss::Field::TRANSIENT, entity->entity_count()); } - for (int i = 0; i < nvar; i++) { - // Verify that all names were used for a field... - assert(names[i][0] == '\0' || (local_truth && local_truth[i] == 0)); - delete[] names[i]; + auto &field = fields.back(); + if (exo_field.nesting == 1) { + field.set_suffix_separator(exo_field.component_separator[0]); + } + else { + field.set_suffix_separator(exo_field.component_separator[0], + exo_field.component_separator[1]); + } + + if (lowerCaseVariableNames) { + field.set_suffices_uppercase(false); + } + + // Now remove the used field+component names from `names` to verify that we found all + // fields on this entity... (Also set suffices_uppercase...) + for (int i = 0; i < field.get_component_count(Ioss::Field::InOut::INPUT); i++) { + auto comp_name = field.get_component_name(i + 1, Ioss::Field::InOut::INPUT); + // Find `comp_name` in `names`... + for (size_t j = 0; j < names.size(); j++) { + if (Ioss::Utils::str_equal(comp_name, names[j])) { + if (!lowerCaseVariableNames && + field.get_component_count(Ioss::Field::InOut::INPUT) > 1 && i == 0) { + // Find the last-most alphabetic character... + auto len = names[j].length(); + for (size_t k = 0; k < len; k++) { + if (isalpha(names[j][len - k - 1])) { + field.set_suffices_uppercase(isupper(names[j][len - k - 1])); + break; + } + } + } + names[j] = ""; + break; + } + } + } + +#if IOSS_DEBUG_OUTPUT + if (myProcessor == 0) { + fmt::print(Ioss::DebugOut(), "Enhanced Field: Adding to {} {}:\n\t{}\n", + entity->type_string(), entity->name(), field); } - delete[] names; +#endif } } - return nvar; + return fields; } // common @@ -1742,45 +1872,36 @@ namespace Ioex { // Get the variable names and add as fields. Need to decode these // into vector/tensor/... eventually, for now store all as // scalars. - char **names = Ioss::Utils::get_name_array(nvar, maximumNameLength); - - // Read the names... - // (Currently, names are read for every block. We could save them...) + Ioss::NameList names; { Ioss::SerializeIO serializeIO_(this); + names = + Ioex::get_reduction_variable_names(nvar, maximumNameLength, get_file_pointer(), type); + } - int ierr = ex_get_reduction_variable_names(get_file_pointer(), type, nvar, names); - if (ierr < 0) { - Ioex::exodus_error(get_file_pointer(), __LINE__, __func__, __FILE__); - } - - // Add to VariableNameMap so can determine exodusII index given a - // Sierra field name. exodusII index is just 'i+1' - auto &variables = m_reductionVariables[type]; - for (int i = 0; i < nvar; i++) { - if (lowerCaseVariableNames) { - Ioss::Utils::fixup_name(names[i]); - } - variables.insert(VNMValuePair(std::string(names[i]), i + 1)); + // Add to VariableNameMap so can determine exodusII index given a + // Sierra field name. exodusII index is just 'i+1' + auto &variables = m_reductionVariables[type]; + for (int i = 0; i < nvar; i++) { + if (lowerCaseVariableNames) { + Ioss::Utils::fixup_name(names[i]); } + variables.insert(VNMValuePair(std::string(names[i]), i + 1)); + } - int *local_truth = nullptr; + int *local_truth = nullptr; - std::vector fields; - int64_t count = 1; - Ioss::Utils::get_fields(count, names, nvar, Ioss::Field::REDUCTION, this, local_truth, - fields); + std::vector fields; + int64_t count = 1; + Ioss::Utils::get_fields(count, names, Ioss::Field::REDUCTION, this, local_truth, fields); - for (const auto &field : fields) { - entity->field_add(field); - } + for (const auto &field : fields) { + entity->field_add(field); + } - for (int i = 0; i < nvar; i++) { - // Verify that all names were used for a field... - assert(names[i][0] == '\0' || (local_truth && local_truth[i] == 0)); - delete[] names[i]; - } - delete[] names; + for (int i = 0; i < nvar; i++) { + // Verify that all names were used for a field... + assert(names[i].empty() || (local_truth && local_truth[i] == 0)); } } return nvar; @@ -1905,9 +2026,232 @@ namespace Ioex { output_results_names(type, m_reductionVariables[type], true); } } + + // Output field metadata + output_field_metadata(); } } + namespace { + void internal_output_field_metadata(int exoid, ex_entity_type type, + Ioss::GroupingEntity *entity) + { + // Get all transient fields on this entity... + char default_separator = entity->get_database()->get_field_separator(); + auto results_fields = entity->field_describe(Ioss::Field::TRANSIENT); + for (const auto &field_name : results_fields) { + const auto &field = entity->get_fieldref(field_name); + + ex_field exo_field{}; + Ioss::Utils::copy_string(exo_field.name, field_name); + exo_field.entity_type = type; + exo_field.entity_id = entity->get_optional_property("id", 0); + + auto *storage = field.transformed_storage(); + auto storage_type = storage->type(); + + if (storage_type == Ioss::VariableType::Type::COMPOSED) { + exo_field.nesting = 2; + + const auto *composed = dynamic_cast(storage); + assert(composed != nullptr); + exo_field.type[0] = Ioex::map_ioss_field_type(composed->get_base_type()); + exo_field.cardinality[0] = composed->get_base_type()->component_count(); + char separator0 = field.get_suffix_separator(); + exo_field.component_separator[0] = separator0 == 1 ? default_separator : separator0; + + if (exo_field.type[0] == EX_FIELD_TYPE_USER_DEFINED) { + assert(composed->get_base_type()->type() == Ioss::VariableType::Type::NAMED_SUFFIX); + auto nsvt = + dynamic_cast(composed->get_base_type()); + assert(nsvt != nullptr); + std::string suffices{}; + for (int i = 0; i < nsvt->component_count(); i++) { + if (i > 0) { + suffices += ","; + } + suffices += nsvt->label(i + 1, 0); + } + Ioss::Utils::copy_string(exo_field.suffices, suffices.c_str(), EX_MAX_NAME + 1); + } + + exo_field.type[1] = Ioex::map_ioss_field_type(composed->get_secondary_type()); + exo_field.cardinality[1] = composed->get_secondary_type()->component_count(); + char separator1 = field.get_suffix_separator(1); + exo_field.component_separator[1] = separator1 == 1 ? default_separator : separator1; + if (exo_field.type[1] == EX_BASIS || exo_field.type[1] == EX_QUADRATURE) { + exo_field.type_name[0] = ','; + Ioss::Utils::copy_string(&exo_field.type_name[1], + composed->get_secondary_type()->name(), EX_MAX_NAME); + } + } + else if (storage_type == Ioss::VariableType::Type::COMPOSITE) { + exo_field.nesting = 2; + + const auto *composite = dynamic_cast(storage); + assert(composite != nullptr); + exo_field.type[0] = Ioex::map_ioss_field_type(composite->get_base_type()); + exo_field.cardinality[0] = composite->get_base_type()->component_count(); + char separator0 = field.get_suffix_separator(); + exo_field.component_separator[0] = separator0 == 1 ? default_separator : separator0; + + exo_field.type[1] = EX_FIELD_TYPE_SEQUENCE; + exo_field.cardinality[1] = composite->get_num_copies(); + char separator1 = field.get_suffix_separator(1); + exo_field.component_separator[1] = separator1 == 1 ? default_separator : separator1; + } + else { + exo_field.nesting = 1; + exo_field.type[0] = Ioex::map_ioss_field_type(storage); + if (exo_field.type[0] == EX_FIELD_TYPE_SEQUENCE) { + exo_field.cardinality[0] = storage->component_count(); + } + if (exo_field.type[0] == EX_BASIS) { + assert(storage->type() == Ioss::VariableType::Type::BASIS); + const auto *basis = dynamic_cast(storage); + assert(basis != nullptr); + exo_field.cardinality[0] = storage->component_count(); + Ioss::Utils::copy_string(exo_field.type_name, basis->name()); + } + if (exo_field.type[0] == EX_QUADRATURE) { + assert(storage->type() == Ioss::VariableType::Type::QUADRATURE); + const auto *quad = dynamic_cast(storage); + assert(quad != nullptr); + exo_field.cardinality[0] = storage->component_count(); + Ioss::Utils::copy_string(exo_field.type_name, quad->name()); + } + if (exo_field.type[0] == EX_FIELD_TYPE_USER_DEFINED) { + assert(storage->type() == Ioss::VariableType::Type::NAMED_SUFFIX); + auto nsvt = dynamic_cast(storage); + assert(nsvt != nullptr); + exo_field.cardinality[0] = nsvt->component_count(); + std::string suffices{}; + for (int i = 0; i < nsvt->component_count(); i++) { + if (i > 0) { + suffices += ","; + } + suffices += nsvt->label(i + 1, 0); + } + Ioss::Utils::copy_string(exo_field.suffices, suffices.c_str(), EX_MAX_NAME + 1); + } + char separator = field.get_suffix_separator(); + exo_field.component_separator[0] = separator == 1 ? default_separator : separator; + } + + if (exo_field.type[0] != EX_SCALAR) { + ex_put_field_metadata(exoid, exo_field); + if (exo_field.type[0] == EX_FIELD_TYPE_USER_DEFINED) { + ex_put_field_suffices(exoid, exo_field, exo_field.suffices); + } + } + } + } + + void output_basis(int exoid, const Ioss::VariableType *var) + { + const auto *basis = dynamic_cast(var); + assert(basis != nullptr); + ex_basis exo_basis{}; + exo_basis.cardinality = basis->component_count(); + ex_initialize_basis_struct(&exo_basis, 1, 1); + Ioss::Utils::copy_string(exo_basis.name, basis->name(), EX_MAX_NAME); + for (int i = 0; i < basis->component_count(); i++) { + auto component = basis->get_basis_component(i + 1); + exo_basis.subc_dim[i] = component.subc_dim; + exo_basis.subc_ordinal[i] = component.subc_ordinal; + exo_basis.subc_dof_ordinal[i] = component.subc_dof_ordinal; + exo_basis.subc_num_dof[i] = component.subc_num_dof; + exo_basis.xi[i] = component.xi; + exo_basis.eta[i] = component.eta; + exo_basis.zeta[i] = component.zeta; + } + ex_put_basis(exoid, exo_basis); + ex_initialize_basis_struct(&exo_basis, 1, -1); + } + + void output_quad(int exoid, const Ioss::VariableType *var) + { + const auto *quadrature = dynamic_cast(var); + assert(quadrature != nullptr); + ex_quadrature exo_quadrature{}; + exo_quadrature.cardinality = quadrature->component_count(); + ex_initialize_quadrature_struct(&exo_quadrature, 1, 1); + Ioss::Utils::copy_string(exo_quadrature.name, quadrature->name(), EX_MAX_NAME); + const auto &quad = quadrature->get_quadrature(); + for (const auto &[i, component] : Ioss::enumerate(quad)) { + exo_quadrature.xi[i] = component.xi; + exo_quadrature.eta[i] = component.eta; + exo_quadrature.zeta[i] = component.zeta; + exo_quadrature.weight[i] = component.weight; + } + ex_put_quadrature(exoid, exo_quadrature); + ex_initialize_quadrature_struct(&exo_quadrature, 1, -1); + } + + void output_type_metadata(int exoid) + { + // Iterate the list and output the `quadrature` and `basis` types... + auto basis_list = Ioss::VariableType::external_types(Ioss::VariableType::Type::BASIS); + for (auto &var : basis_list) { + output_basis(exoid, var); + } + + auto quad_list = Ioss::VariableType::external_types(Ioss::VariableType::Type::QUADRATURE); + for (auto &var : quad_list) { + output_quad(exoid, var); + } + } + + template + void internal_output_field_metadata(int exoid, ex_entity_type type, std::vector entities) + { + for (const auto &entity : entities) { + internal_output_field_metadata(exoid, type, entity); + } + } + } // namespace + + void BaseDatabaseIO::output_field_metadata() + { + Ioss::SerializeIO serializeIO_(this); + // Output the 'basis' and 'quadrature' type metadata... + output_type_metadata(get_file_pointer()); + + const Ioss::NodeBlockContainer &node_blocks = get_region()->get_node_blocks(); + assert(node_blocks.size() <= 1); + internal_output_field_metadata(get_file_pointer(), EX_NODE_BLOCK, node_blocks); + + const Ioss::EdgeBlockContainer &edge_blocks = get_region()->get_edge_blocks(); + internal_output_field_metadata(get_file_pointer(), EX_EDGE_BLOCK, edge_blocks); + + const Ioss::FaceBlockContainer &face_blocks = get_region()->get_face_blocks(); + internal_output_field_metadata(get_file_pointer(), EX_FACE_BLOCK, face_blocks); + + const Ioss::ElementBlockContainer &element_blocks = get_region()->get_element_blocks(); + internal_output_field_metadata(get_file_pointer(), EX_ELEM_BLOCK, element_blocks); + + const Ioss::NodeSetContainer &nodesets = get_region()->get_nodesets(); + internal_output_field_metadata(get_file_pointer(), EX_NODE_SET, nodesets); + + const Ioss::EdgeSetContainer &edgesets = get_region()->get_edgesets(); + internal_output_field_metadata(get_file_pointer(), EX_EDGE_SET, edgesets); + + const Ioss::FaceSetContainer &facesets = get_region()->get_facesets(); + internal_output_field_metadata(get_file_pointer(), EX_FACE_SET, facesets); + + const Ioss::ElementSetContainer &elementsets = get_region()->get_elementsets(); + internal_output_field_metadata(get_file_pointer(), EX_ELEM_SET, elementsets); + + const auto &blobs = get_region()->get_blobs(); + internal_output_field_metadata(get_file_pointer(), EX_BLOB, blobs); + + const auto &assemblies = get_region()->get_assemblies(); + internal_output_field_metadata(get_file_pointer(), EX_ASSEMBLY, assemblies); + + const Ioss::SideSetContainer &sidesets = get_region()->get_sidesets(); + internal_output_field_metadata(get_file_pointer(), EX_SIDE_SET, sidesets); + } + // common template void BaseDatabaseIO::internal_gather_results_metadata(ex_entity_type type, @@ -2081,8 +2425,8 @@ namespace Ioex { if (var_count > 0) { size_t name_length = 0; // Push into a char** array... - std::vector var_names(var_count); - std::vector variable_names(var_count); + std::vector var_names(var_count); + Ioss::NameList variable_names(var_count); for (const auto &variable : variables) { size_t index = variable.second; assert(index > 0 && index <= var_count); @@ -2254,12 +2598,6 @@ namespace Ioex { assert(block != nullptr); if (attribute_count > 0) { - size_t my_element_count = block->entity_count(); - - // Get the attribute names. May not exist or may be blank... - char **names = Ioss::Utils::get_name_array(attribute_count, maximumNameLength); - int64_t id = block->get_property("id").get_int(); - // Some older applications do not want to used named // attributes; in this case, just create a field for each // attribute named "attribute_1", "attribute_2", ..., "attribute_#" @@ -2268,19 +2606,26 @@ namespace Ioex { bool attributes_named = true; // Possibly reset below; note that even if ignoring // attribute names, they are still 'named' + size_t my_element_count = block->entity_count(); + Ioss::NameList names; + names.reserve(attribute_count); if (properties.exists("IGNORE_ATTRIBUTE_NAMES")) { for (int i = 0; i < attribute_count; i++) { - std::string tmp = fmt::format("attribute_{}", i + 1); - Ioss::Utils::copy_string(names[i], tmp, maximumNameLength + 1); + names.emplace_back(fmt::format("attribute_{}", i + 1)); } } else { // Use attribute names if they exist. + + // Get the attribute names. May not exist or may be blank... + char **cnames = Ioex::get_name_array(attribute_count, maximumNameLength); + int64_t id = block->get_property("id").get_int(); + { Ioss::SerializeIO serializeIO_(this); if (block->entity_count() != 0) { ex_entity_type entity_type = Ioex::map_exodus_type(block->type()); - int ierr = ex_get_attr_names(get_file_pointer(), entity_type, id, &names[0]); + int ierr = ex_get_attr_names(get_file_pointer(), entity_type, id, &cnames[0]); if (ierr < 0) { Ioex::exodus_error(get_file_pointer(), __LINE__, __func__, __FILE__); } @@ -2292,30 +2637,37 @@ namespace Ioex { std::vector cname(attribute_count * (maximumNameLength + 1)); if (block->entity_count() != 0) { for (int i = 0; i < attribute_count; i++) { - std::memcpy(&cname[i * (maximumNameLength + 1)], names[i], maximumNameLength + 1); + std::memcpy(&cname[i * (maximumNameLength + 1)], cnames[i], maximumNameLength + 1); } } util().attribute_reduction(attribute_count * (maximumNameLength + 1), Data(cname)); for (int i = 0; i < attribute_count; i++) { - std::memcpy(names[i], &cname[i * (maximumNameLength + 1)], maximumNameLength + 1); + std::memcpy(cnames[i], &cname[i * (maximumNameLength + 1)], maximumNameLength + 1); } } // Convert to lowercase. attributes_named = true; for (int i = 0; i < attribute_count; i++) { - fix_bad_name(names[i]); - Ioss::Utils::fixup_name(names[i]); - if (names[i][0] == '\0' || (!(std::isalnum(names[i][0]) || names[i][0] == '_'))) { + if (cnames[i][0] == '\0' || (!(std::isalnum(cnames[i][0]) || cnames[i][0] == '_'))) { attributes_named = false; } } + if (attributes_named) { + for (int i = 0; i < attribute_count; i++) { + fix_bad_name(cnames[i]); + Ioss::Utils::fixup_name(cnames[i]); + names.emplace_back(cnames[i]); + } + } + // Release memory... + Ioex::delete_name_array(cnames, attribute_count); } if (attributes_named) { std::vector attributes; - Ioss::Utils::get_fields(my_element_count, names, attribute_count, Ioss::Field::ATTRIBUTE, - this, nullptr, attributes); + Ioss::Utils::get_fields(my_element_count, names, Ioss::Field::ATTRIBUTE, this, nullptr, + attributes); int offset = 1; for (const auto &field : attributes) { if (block->field_exists(field.get_name())) { @@ -2460,13 +2812,10 @@ namespace Ioex { block->field_add(Ioss::Field(std::move(att_name), Ioss::Field::REAL, storage, Ioss::Field::ATTRIBUTE, my_element_count, 1)); - - // Release memory... - Ioss::Utils::delete_name_array(names, attribute_count); } } - void BaseDatabaseIO::common_write_meta_data(Ioss::IfDatabaseExistsBehavior behavior) + void BaseDatabaseIO::common_write_metadata(Ioss::IfDatabaseExistsBehavior behavior) { Ioss::Region *region = get_region(); @@ -2700,7 +3049,7 @@ namespace Ioex { } } - void BaseDatabaseIO::output_other_meta_data() + void BaseDatabaseIO::output_other_metadata() { // Write attribute names (if any)... write_attribute_names(get_file_pointer(), EX_NODE_SET, get_region()->get_nodesets()); @@ -2791,7 +3140,7 @@ namespace Ioex { } if (node_map_cnt > 0) { - char **names = Ioss::Utils::get_name_array(node_map_cnt, maximumNameLength); + char **names = Ioex::get_name_array(node_map_cnt, maximumNameLength); auto *node_block = get_region()->get_node_blocks()[0]; // If there are node_maps, then there is a node_block auto node_map_fields = node_block->field_describe(Ioss::Field::MAP); @@ -2809,11 +3158,11 @@ namespace Ioex { } } ex_put_names(get_file_pointer(), EX_NODE_MAP, names); - Ioss::Utils::delete_name_array(names, node_map_cnt); + Ioex::delete_name_array(names, node_map_cnt); } if (elem_map_cnt > 0) { - char **names = Ioss::Utils::get_name_array(elem_map_cnt, maximumNameLength); + char **names = Ioex::get_name_array(elem_map_cnt, maximumNameLength); for (const auto &field_name : elem_map_fields) { // Now, we need to find an element block that has this field... for (const auto &block : blocks) { @@ -2842,7 +3191,7 @@ namespace Ioex { } } ex_put_names(get_file_pointer(), EX_ELEM_MAP, names); - Ioss::Utils::delete_name_array(names, elem_map_cnt); + Ioex::delete_name_array(names, elem_map_cnt); } // Write coordinate frame data... @@ -2897,8 +3246,8 @@ namespace { check_attribute_index_order(ge); - std::vector names(attribute_count); - std::vector names_str(attribute_count); + std::vector names(attribute_count); + Ioss::NameList names_str(attribute_count); // Get the attribute fields... Ioss::NameList results_fields = ge->field_describe(Ioss::Field::ATTRIBUTE); diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h index 4445387f32..bf6484e9c7 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h @@ -129,8 +129,8 @@ namespace Ioex { Ioss::Map &entity_map, void *ids, size_t num_to_get, size_t offset) const; - void compute_block_membership_nl(Ioss::SideBlock *efblock, - std::vector &block_membership) const override; + void compute_block_membership_nl(Ioss::SideBlock *efblock, + Ioss::NameList &block_membership) const override; IOSS_NODISCARD int int_byte_size_db() const override; void set_int_byte_size_api(Ioss::DataSize size) const override; @@ -242,8 +242,9 @@ namespace Ioex { void add_attribute_fields(Ioss::GroupingEntity *block, int attribute_count, const std::string &type); - void common_write_meta_data(Ioss::IfDatabaseExistsBehavior behavior); - void output_other_meta_data(); + void common_write_metadata(Ioss::IfDatabaseExistsBehavior behavior); + void output_other_metadata(); + void output_field_metadata(); int64_t internal_add_results_fields(ex_entity_type type, Ioss::GroupingEntity *entity, int64_t position, int64_t block_count, @@ -252,6 +253,9 @@ namespace Ioex { int64_t add_results_fields(Ioss::GroupingEntity *entity, int64_t position = 0); int64_t add_reduction_results_fields(Ioss::GroupingEntity *entity); void add_mesh_reduction_fields(int64_t id, Ioss::GroupingEntity *entity); + std::vector get_fields_via_field_metadata(Ioss::GroupingEntity *entity, + ex_entity_type type, + Ioss::NameList &names); void add_region_fields(); void store_reduction_field(const Ioss::Field &field, const Ioss::GroupingEntity *ge, diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C index a9399e8681..0dea422a17 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C @@ -480,6 +480,9 @@ namespace Ioex { read_region(); read_communication_metadata(); + + Ioex::read_exodus_basis(get_file_pointer()); + Ioex::read_exodus_quadrature(get_file_pointer()); } get_step_times_nl(); @@ -622,13 +625,13 @@ namespace Ioex { // Get information records from database and add to informationRecords... int num_info = ex_inquire_int(get_file_pointer(), EX_INQ_INFO); if (num_info > 0) { - char **info_rec = Ioss::Utils::get_name_array( - num_info, max_line_length); // 'total_lines' pointers to char buffers + char **info_rec = + Ioex::get_name_array(num_info, max_line_length); // 'total_lines' pointers to char buffers ex_get_info(get_file_pointer(), info_rec); for (int i = 0; i < num_info; i++) { add_information_record(info_rec[i]); } - Ioss::Utils::delete_name_array(info_rec, num_info); + Ioex::delete_name_array(info_rec, num_info); } } @@ -982,7 +985,7 @@ namespace Ioex { bool map_read = false; int map_count = ex_inquire_int(get_file_pointer(), inquiry_type); if (read_exodus_map && map_count > 0) { - char **names = Ioss::Utils::get_name_array(map_count, maximumNameLength); + char **names = Ioex::get_name_array(map_count, maximumNameLength); int ierr = ex_get_names(get_file_pointer(), entity_type, names); if (ierr < 0) { Ioex::exodus_error(get_file_pointer(), __LINE__, __func__, __FILE__); @@ -1012,7 +1015,7 @@ namespace Ioex { } } } - Ioss::Utils::delete_name_array(names, map_count); + Ioex::delete_name_array(names, map_count); } if (!map_read) { @@ -5338,7 +5341,7 @@ namespace Ioex { void DatabaseIO::write_meta_data(Ioss::IfDatabaseExistsBehavior behavior) { Ioss::Region *region = get_region(); - common_write_meta_data(behavior); + common_write_metadata(behavior); char the_title[max_line_length + 1]; @@ -5396,7 +5399,7 @@ namespace Ioex { if (ierr < 0) { Ioex::exodus_error(get_file_pointer(), __LINE__, __func__, __FILE__); } - output_other_meta_data(); + output_other_metadata(); } } } diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C index b3edaccb2e..0c4ab1e54b 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C @@ -16,26 +16,26 @@ #include "Ioss_Utils.h" #include "exodus/Ioex_Utils.h" -#include // for lower_bound, copy, etc -#include // for assert -#include // for INT_MAX +#include +#include +#include #include -#include // for exit, EXIT_FAILURE +#include #include #include -#include // for operator<<, ostringstream, etc -#include // for distance -#include // for map -#include // for accumulate -#include // for pair, make_pair +#include +#include +#include +#include +#include #if !defined(NO_PARMETIS_SUPPORT) -#include // for ParMETIS_V3_Mesh2Dual, etc +#include #endif #if !defined(NO_ZOLTAN_SUPPORT) -#include // for Zoltan_Initialize -#include // for Zoltan +#include +#include #endif namespace { @@ -185,7 +185,7 @@ namespace Ioex { if (map_count > 0) { int max_name_length = ex_inquire_int(filePtr, EX_INQ_DB_MAX_USED_NAME_LENGTH); max_name_length = max_name_length < 32 ? 32 : max_name_length; - char **names = Ioss::Utils::get_name_array(map_count, max_name_length); + char **names = Ioex::get_name_array(map_count, max_name_length); ex_get_names(filePtr, EX_ELEM_MAP, names); for (int i = 0; i < map_count; i++) { @@ -197,7 +197,7 @@ namespace Ioex { break; } } - Ioss::Utils::delete_name_array(names, map_count); + Ioex::delete_name_array(names, map_count); } if (!map_read) { @@ -221,7 +221,7 @@ namespace Ioex { if (var_count > 0) { int max_name_length = ex_inquire_int(filePtr, EX_INQ_DB_MAX_USED_NAME_LENGTH); max_name_length = max_name_length < 32 ? 32 : max_name_length; - char **names = Ioss::Utils::get_name_array(var_count, max_name_length); + char **names = Ioex::get_name_array(var_count, max_name_length); ex_get_variable_names(filePtr, EX_ELEM_BLOCK, var_count, names); for (int i = 0; i < var_count; i++) { @@ -230,7 +230,7 @@ namespace Ioex { break; } } - Ioss::Utils::delete_name_array(names, var_count); + Ioex::delete_name_array(names, var_count); } if (var_index == 0) { diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.h index 5ad1e1e407..651476b517 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.h @@ -33,10 +33,10 @@ namespace Ioss { namespace Ioex { struct IOEX_EXPORT BlockFieldData { - int64_t id{0}; - size_t comp_count{0}; - std::vector var_name; - std::vector var_index; + int64_t id{0}; + size_t comp_count{0}; + Ioss::NameList var_name; + std::vector var_index; BlockFieldData() : id(0), comp_count(0) {} BlockFieldData(const int64_t id_) : id(id_), comp_count(0) {} diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C index b565b761af..ec617e93d6 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C @@ -648,7 +648,7 @@ namespace Ioex { } } - // Check whether we are on a NFS filesyste -- composed output is sometimes slow/hangs + // Check whether we are on a NFS filesystem -- composed output is sometimes slow/hangs // on NFS if (myProcessor == 0) { if (file.is_nfs()) { @@ -782,6 +782,9 @@ namespace Ioex { decomp->decompose_model(exoid); read_region(); + Ioex::read_exodus_basis(get_file_pointer()); + Ioex::read_exodus_quadrature(get_file_pointer()); + get_elemblocks(); get_step_times_nl(); @@ -893,13 +896,13 @@ namespace Ioex { // Get information records from database and add to informationRecords... int num_info = ex_inquire_int(get_file_pointer(), EX_INQ_INFO); if (num_info > 0) { - char **info_rec = Ioss::Utils::get_name_array( - num_info, max_line_length); // 'total_lines' pointers to char buffers + char **info_rec = + Ioex::get_name_array(num_info, max_line_length); // 'total_lines' pointers to char buffers ex_get_info(get_file_pointer(), info_rec); for (int i = 0; i < num_info; i++) { add_information_record(info_rec[i]); } - Ioss::Utils::delete_name_array(info_rec, num_info); + Ioex::delete_name_array(info_rec, num_info); } } @@ -1019,7 +1022,7 @@ namespace Ioex { bool map_read = false; int map_count = ex_inquire_int(get_file_pointer(), inquiry_type); if (map_count > 0) { - char **names = Ioss::Utils::get_name_array(map_count, maximumNameLength); + char **names = Ioex::get_name_array(map_count, maximumNameLength); int ierr = ex_get_names(get_file_pointer(), entity_type, names); if (ierr < 0) { Ioex::exodus_error(get_file_pointer(), __LINE__, __func__, __FILE__); @@ -1041,7 +1044,7 @@ namespace Ioex { map_read = true; } } - Ioss::Utils::delete_name_array(names, map_count); + Ioex::delete_name_array(names, map_count); } if (!map_read) { @@ -4797,7 +4800,7 @@ namespace Ioex { void ParallelDatabaseIO::write_meta_data(Ioss::IfDatabaseExistsBehavior behavior) { Ioss::Region *region = get_region(); - common_write_meta_data(behavior); + common_write_metadata(behavior); char the_title[max_line_length + 1]; @@ -4846,7 +4849,7 @@ namespace Ioex { if (behavior != Ioss::DB_APPEND && behavior != Ioss::DB_MODIFY) { output_node_map(); - output_other_meta_data(); + output_other_metadata(); } } diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C index 16edecbfbf..12ac92d9e6 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C @@ -11,7 +11,6 @@ #include "Ioss_VariableType.h" #include "exodus/Ioex_Utils.h" #include -#include #include #include #include @@ -20,14 +19,18 @@ #include #include +#include "Ioss_BasisVariableType.h" #include "Ioss_CoordinateFrame.h" #include "Ioss_DatabaseIO.h" #include "Ioss_ElementBlock.h" #include "Ioss_Field.h" #include "Ioss_GroupingEntity.h" +#include "Ioss_NamedSuffixVariableType.h" #include "Ioss_ParallelUtils.h" #include "Ioss_Property.h" +#include "Ioss_QuadratureVariableType.h" #include "exodusII.h" +#include "exodusII_int.h" namespace { size_t match(const std::string &name1, const std::string &name2) @@ -98,7 +101,6 @@ namespace { } } } - } // namespace namespace Ioex { @@ -119,6 +121,128 @@ namespace Ioex { } } + std::string map_ioss_field_type(ex_field_type type) + { + if (type == EX_VECTOR_2D) + return "vector_2d"; + if (type == EX_VECTOR_3D) + return "vector_3d"; + if (type == EX_SCALAR) + return "scalar"; + if (type == EX_VECTOR_1D) + return "vector_1d"; + if (type == EX_QUATERNION_2D) + return "quaternion_2d"; + if (type == EX_QUATERNION_3D) + return "quaternion_3d"; + if (type == EX_FULL_TENSOR_36) + return "full_tensor_36"; + if (type == EX_FULL_TENSOR_32) + return "full_tensor_32"; + if (type == EX_FULL_TENSOR_22) + return "full_tensor_22"; + if (type == EX_FULL_TENSOR_16) + return "full_tensor_16"; + if (type == EX_FULL_TENSOR_12) + return "full_tensor_12"; + if (type == EX_SYM_TENSOR_33) + return "sym_tensor_33"; + if (type == EX_SYM_TENSOR_31) + return "sym_tensor_31"; + if (type == EX_SYM_TENSOR_21) + return "sym_tensor_21"; + if (type == EX_SYM_TENSOR_13) + return "sym_tensor_13"; + if (type == EX_SYM_TENSOR_11) + return "sym_tensor_11"; + if (type == EX_SYM_TENSOR_10) + return "sym_tensor_10"; + if (type == EX_ASYM_TENSOR_03) + return "asym_tensor_03"; + if (type == EX_ASYM_TENSOR_02) + return "asym_tensor_02"; + if (type == EX_ASYM_TENSOR_01) + return "asym_tensor_01"; + if (type == EX_MATRIX_2X2) + return "matrix_22"; + if (type == EX_MATRIX_3X3) + return "matrix_33"; + if (type == EX_FIELD_TYPE_SEQUENCE) + return "Real"; + if (type == EX_BASIS) + return "Basis"; + if (type == EX_QUADRATURE) + return "Quadrature"; + return "invalid"; + } + + ex_field_type map_ioss_field_type(const Ioss::VariableType *type) + { + if (type->name() == "vector_2d") + return EX_VECTOR_2D; + if (type->name() == "vector_3d") + return EX_VECTOR_3D; + if (type->name() == "scalar") + return EX_SCALAR; + if (type->name() == "vector_1d") + return EX_VECTOR_1D; + if (type->name() == "quaternion_2d") + return EX_QUATERNION_2D; + if (type->name() == "quaternion_3d") + return EX_QUATERNION_3D; + if (type->name() == "full_tensor_36") + return EX_FULL_TENSOR_36; + if (type->name() == "full_tensor_32") + return EX_FULL_TENSOR_32; + if (type->name() == "full_tensor_22") + return EX_FULL_TENSOR_22; + if (type->name() == "full_tensor_16") + return EX_FULL_TENSOR_16; + if (type->name() == "full_tensor_12") + return EX_FULL_TENSOR_12; + if (type->name() == "sym_tensor_33") + return EX_SYM_TENSOR_33; + if (type->name() == "sym_tensor_31") + return EX_SYM_TENSOR_31; + if (type->name() == "sym_tensor_21") + return EX_SYM_TENSOR_21; + if (type->name() == "sym_tensor_13") + return EX_SYM_TENSOR_13; + if (type->name() == "sym_tensor_11") + return EX_SYM_TENSOR_11; + if (type->name() == "sym_tensor_10") + return EX_SYM_TENSOR_10; + if (type->name() == "asym_tensor_03") + return EX_ASYM_TENSOR_03; + if (type->name() == "asym_tensor_02") + return EX_ASYM_TENSOR_02; + if (type->name() == "asym_tensor_01") + return EX_ASYM_TENSOR_01; + if (type->name() == "matrix_22") + return EX_MATRIX_2X2; + if (type->name() == "matrix_33") + return EX_MATRIX_3X3; + + if (Ioss::Utils::substr_equal("Real", type->name())) + return EX_FIELD_TYPE_SEQUENCE; + + // This may be a basis, quadratur, or user type... + auto nsvt = dynamic_cast(type); + if (nsvt != nullptr) { + return EX_FIELD_TYPE_USER_DEFINED; + } + auto bvt = dynamic_cast(type); + if (bvt != nullptr) { + return EX_BASIS; + } + auto qvt = dynamic_cast(type); + if (qvt != nullptr) { + return EX_QUADRATURE; + } + + return EX_FIELD_TYPE_INVALID; + } + Ioss::EntityType map_exodus_type(ex_entity_type type) { switch (type) { @@ -138,6 +262,62 @@ namespace Ioex { } } + int read_exodus_basis(int exoid) + { + auto bas_cnt = ex_get_basis_count(exoid); + if (bas_cnt == 0) { + return 0; + } + std::vector exo_basis(bas_cnt); + auto *pbasis = Data(exo_basis); + ex_get_basis(exoid, &pbasis, &bas_cnt); + + for (const auto &ebasis : exo_basis) { + Ioss::Basis basis; + for (int i = 0; i < ebasis.cardinality; i++) { + Ioss::BasisComponent bc{ + ebasis.subc_dim[i], ebasis.subc_ordinal[i], ebasis.subc_dof_ordinal[i], + ebasis.subc_num_dof[i], ebasis.xi[i], ebasis.eta[i], + ebasis.zeta[i]}; + basis.basies.push_back(bc); + } + Ioss::VariableType::create_basis_type(ebasis.name, basis); + } + + // deallocate any memory allocated in the 'ex_basis' structs. + ex_initialize_basis_struct(Data(exo_basis), exo_basis.size(), -1); + + return bas_cnt; + } + + int read_exodus_quadrature(int exoid) + { + auto quad_cnt = ex_get_quadrature_count(exoid); + if (quad_cnt == 0) { + return quad_cnt; + } + + std::vector exo_quadrature(quad_cnt); + auto *pquad = Data(exo_quadrature); + ex_get_quadrature(exoid, &pquad, &quad_cnt); + + for (const auto &equadrature : exo_quadrature) { + std::vector quadrature; + quadrature.reserve(equadrature.cardinality); + for (int i = 0; i < equadrature.cardinality; i++) { + Ioss::QuadraturePoint q{equadrature.xi[i], equadrature.eta[i], equadrature.zeta[i], + equadrature.weight[i]}; + quadrature.push_back(q); + } + Ioss::VariableType::create_quadrature_type(equadrature.name, quadrature); + } + + // deallocate any memory allocated in the 'ex_quadrature' structs. + ex_initialize_quadrature_struct(Data(exo_quadrature), exo_quadrature.size(), -1); + + return quad_cnt; + } + ex_entity_type map_exodus_type(Ioss::EntityType type) { switch (type) { @@ -159,6 +339,59 @@ namespace Ioex { } } + char **get_name_array(size_t count, int size) + { + auto *names = new char *[count]; + for (size_t i = 0; i < count; i++) { + names[i] = new char[size + 1]; + std::memset(names[i], '\0', size + 1); + } + return names; + } + + void delete_name_array(char **names, int count) + { + for (int i = 0; i < count; i++) { + delete[] names[i]; + } + delete[] names; + } + + Ioss::NameList get_variable_names(int nvar, int maximumNameLength, int exoid, ex_entity_type type) + { + char **names = get_name_array(nvar, maximumNameLength); + int ierr = ex_get_variable_names(exoid, type, nvar, names); + if (ierr < 0) { + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); + } + + Ioss::NameList name_str; + name_str.reserve(nvar); + for (int i = 0; i < nvar; i++) { + name_str.emplace_back(names[i]); + } + delete_name_array(names, nvar); + return name_str; + } + + Ioss::NameList get_reduction_variable_names(int nvar, int maximumNameLength, int exoid, + ex_entity_type type) + { + char **names = get_name_array(nvar, maximumNameLength); + int ierr = ex_get_reduction_variable_names(exoid, type, nvar, names); + if (ierr < 0) { + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); + } + + Ioss::NameList name_str; + name_str.reserve(nvar); + for (int i = 0; i < nvar; i++) { + name_str.emplace_back(names[i]); + } + delete_name_array(names, nvar); + return name_str; + } + bool read_last_time_attribute(int exodusFilePtr, double *value) { // Check whether the "last_written_time" attribute exists. If it does, @@ -535,7 +768,7 @@ namespace Ioex { } // Get the names of the maps... - char **names = Ioss::Utils::get_name_array(map_count, name_length); + char **names = get_name_array(map_count, name_length); int ierr = ex_get_names(exoid, EX_ELEM_MAP, names); if (ierr < 0) { Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); @@ -585,7 +818,7 @@ namespace Ioex { i = ii - 1; } - Ioss::Utils::delete_name_array(names, map_count); + delete_name_array(names, map_count); return map_count; } diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.h index a08f9d80f1..70be7426c8 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.h @@ -88,6 +88,12 @@ namespace Ioex { IOSS_NODISCARD IOEX_EXPORT Ioss::EntityType map_exodus_type(ex_entity_type type); IOSS_NODISCARD IOEX_EXPORT ex_entity_type map_exodus_type(Ioss::EntityType type); + IOSS_NODISCARD IOEX_EXPORT ex_field_type map_ioss_field_type(const Ioss::VariableType *type); + IOSS_NODISCARD IOEX_EXPORT std::string map_ioss_field_type(ex_field_type type); + + IOEX_EXPORT int read_exodus_basis(int exoid); + IOEX_EXPORT int read_exodus_quadrature(int exoid); + IOEX_EXPORT void update_last_time_attribute(int exodusFilePtr, double value); IOEX_EXPORT bool read_last_time_attribute(int exodusFilePtr, double *value); @@ -106,6 +112,13 @@ namespace Ioex { IOEX_EXPORT int add_map_fields(int exoid, Ioss::ElementBlock *block, int64_t my_element_count, size_t name_length); + IOSS_NODISCARD IOEX_EXPORT char **get_name_array(size_t count, int size); + IOEX_EXPORT void delete_name_array(char **names, int count); + IOSS_NODISCARD IOEX_EXPORT Ioss::NameList get_variable_names(int nvar, int maximumNameLength, + int exoid, ex_entity_type type); + IOSS_NODISCARD IOEX_EXPORT Ioss::NameList + get_reduction_variable_names(int nvar, int maximumNameLength, int exoid, ex_entity_type type); + IOEX_EXPORT void add_coordinate_frames(int exoid, Ioss::Region *region); IOEX_EXPORT void write_coordinate_frames(int exoid, const Ioss::CoordinateFrameContainer &frames); diff --git a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_BaseDatabaseIO.C index e8497735fa..b97dea9003 100644 --- a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_BaseDatabaseIO.C @@ -62,8 +62,6 @@ namespace { EX_NODE_SET, EX_EDGE_SET, EX_FACE_SET, EX_ELEM_SET, EX_SIDE_SET}); - const size_t max_line_length = MAX_LINE_LENGTH; - void check_variable_consistency(const ex_var_params &exo_params, int my_processor, const std::string &filename, const Ioss::ParallelUtils &util); @@ -71,6 +69,25 @@ namespace { template void write_attribute_names(int exoid, ex_entity_type type, const std::vector &entities); + + char **get_name_array(size_t count, int size) + { + auto *names = new char *[count]; + for (size_t i = 0; i < count; i++) { + names[i] = new char[size + 1]; + std::memset(names[i], '\0', size + 1); + } + return names; + } + + void delete_name_array(char **names, int count) + { + for (int i = 0; i < count; i++) { + delete[] names[i]; + } + delete[] names; + } + } // namespace namespace Ioexnl { @@ -266,67 +283,7 @@ namespace Ioexnl { } // common - void BaseDatabaseIO::put_info() - { - int total_lines = 0; - char **info = nullptr; - - if (!using_parallel_io() || myProcessor == 0) { - // dump info records, include the product_registry - // See if the input file was specified as a property on the database... - std::vector input_lines; - if (get_region()->property_exists("input_file_name")) { - std::string filename = get_region()->get_property("input_file_name").get_string(); - // Determine size of input file so can embed it in info records... - Ioss::Utils::input_file(filename, &input_lines, max_line_length); - } - - // Get configuration information for IOSS library. - // Split into strings and remove empty lines... - std::string config = Ioss::IOFactory::show_configuration(); - std::replace(std::begin(config), std::end(config), '\t', ' '); - auto lines = Ioss::tokenize(config, "\n"); - lines.erase(std::remove_if(lines.begin(), lines.end(), - [](const std::string &line) { return line.empty(); }), - lines.end()); - - // See if the client added any "information_records" - size_t info_rec_size = informationRecords.size(); - size_t in_lines = input_lines.size(); - size_t qa_lines = 1; // Platform info - size_t config_lines = lines.size(); - - total_lines = in_lines + qa_lines + info_rec_size + config_lines; - - // 'total_lines' pointers to char buffers - info = Ioss::Utils::get_name_array(total_lines, max_line_length); - - int i = 0; - Ioss::Utils::copy_string(info[i++], Ioss::Utils::platform_information(), max_line_length + 1); - - // Copy input file lines into 'info' array... - for (size_t j = 0; j < input_lines.size(); j++, i++) { - Ioss::Utils::copy_string(info[i], input_lines[j], max_line_length + 1); - } - - // Copy "information_records" property data ... - for (size_t j = 0; j < informationRecords.size(); j++, i++) { - Ioss::Utils::copy_string(info[i], informationRecords[j], max_line_length + 1); - } - - for (size_t j = 0; j < lines.size(); j++, i++) { - Ioss::Utils::copy_string(info[i], lines[j], max_line_length + 1); - } - } - - if (using_parallel_io()) { - util().broadcast(total_lines); - } - - if (!using_parallel_io() || myProcessor == 0) { - Ioss::Utils::delete_name_array(info, total_lines); - } - } + void BaseDatabaseIO::put_info() {} // common int BaseDatabaseIO::get_current_state() const @@ -423,8 +380,8 @@ namespace Ioexnl { } // common - void BaseDatabaseIO::compute_block_membership_nl(Ioss::SideBlock *efblock, - std::vector &block_membership) const + void BaseDatabaseIO::compute_block_membership_nl(Ioss::SideBlock *efblock, + Ioss::NameList &block_membership) const { const Ioss::ElementBlockContainer &element_blocks = get_region()->get_element_blocks(); assert(Ioss::Utils::check_block_order(element_blocks)); @@ -1046,7 +1003,7 @@ namespace Ioexnl { void BaseDatabaseIO::finalize_write(int, double) {} - void BaseDatabaseIO::common_write_meta_data(Ioss::IfDatabaseExistsBehavior behavior) + void BaseDatabaseIO::common_write_metadata(Ioss::IfDatabaseExistsBehavior behavior) { Ioss::Region *region = get_region(); @@ -1280,7 +1237,7 @@ namespace Ioexnl { } } - void BaseDatabaseIO::output_other_meta_data() + void BaseDatabaseIO::output_other_metadata() { // Write attribute names (if any)... write_attribute_names(get_file_pointer(), EX_NODE_SET, get_region()->get_nodesets()); @@ -1356,7 +1313,7 @@ namespace Ioexnl { } if (node_map_cnt > 0) { - char **names = Ioss::Utils::get_name_array(node_map_cnt, maximumNameLength); + char **names = get_name_array(node_map_cnt, maximumNameLength); auto *node_block = get_region()->get_node_blocks()[0]; // If there are node_maps, then there is a node_block auto node_map_fields = node_block->field_describe(Ioss::Field::MAP); @@ -1373,11 +1330,11 @@ namespace Ioexnl { } } } - Ioss::Utils::delete_name_array(names, node_map_cnt); + delete_name_array(names, node_map_cnt); } if (elem_map_cnt > 0) { - char **names = Ioss::Utils::get_name_array(elem_map_cnt, maximumNameLength); + char **names = get_name_array(elem_map_cnt, maximumNameLength); for (const auto &field_name : elem_map_fields) { // Now, we need to find an element block that has this field... for (const auto &block : blocks) { @@ -1405,7 +1362,7 @@ namespace Ioexnl { } } } - Ioss::Utils::delete_name_array(names, elem_map_cnt); + delete_name_array(names, elem_map_cnt); } // Write coordinate frame data... @@ -1428,8 +1385,8 @@ namespace { check_attribute_index_order(ge); - std::vector names(attribute_count); - std::vector names_str(attribute_count); + std::vector names(attribute_count); + Ioss::NameList names_str(attribute_count); // Get the attribute fields... Ioss::NameList results_fields = ge->field_describe(Ioss::Field::ATTRIBUTE); diff --git a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_BaseDatabaseIO.h b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_BaseDatabaseIO.h index 50334df262..bb004b5be2 100644 --- a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_BaseDatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_BaseDatabaseIO.h @@ -121,8 +121,8 @@ namespace Ioexnl { Ioss::Map &entity_map, void *ids, size_t num_to_get, size_t offset) const; - void compute_block_membership_nl(Ioss::SideBlock *efblock, - std::vector &block_membership) const override; + void compute_block_membership_nl(Ioss::SideBlock *efblock, + Ioss::NameList &block_membership) const override; IOSS_NODISCARD int int_byte_size_db() const override; void set_int_byte_size_api(Ioss::DataSize size) const override; @@ -216,8 +216,8 @@ namespace Ioexnl { void add_attribute_fields(Ioss::GroupingEntity *block, int attribute_count, const std::string &type); - void common_write_meta_data(Ioss::IfDatabaseExistsBehavior behavior); - void output_other_meta_data(); + void common_write_metadata(Ioss::IfDatabaseExistsBehavior behavior); + void output_other_metadata(); int64_t internal_add_results_fields(ex_entity_type type, Ioss::GroupingEntity *entity, int64_t position, int64_t block_count, diff --git a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DatabaseIO.C b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DatabaseIO.C index 236a36b0a1..bb23b829f5 100644 --- a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DatabaseIO.C @@ -1247,7 +1247,7 @@ namespace Ioexnl { void DatabaseIO::write_meta_data(Ioss::IfDatabaseExistsBehavior behavior) { Ioss::Region *region = get_region(); - common_write_meta_data(behavior); + common_write_metadata(behavior); char the_title[max_line_length + 1]; @@ -1297,7 +1297,7 @@ namespace Ioexnl { put_info(); } - output_other_meta_data(); + output_other_metadata(); } } } diff --git a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.C b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.C index 6e8152754e..84848ee2bc 100644 --- a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.C +++ b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.C @@ -16,18 +16,18 @@ #include "Ioss_Utils.h" #include "exonull/Ioexnl_Utils.h" -#include // for lower_bound, copy, etc -#include // for assert -#include // for INT_MAX +#include +#include +#include #include -#include // for exit, EXIT_FAILURE +#include #include #include -#include // for operator<<, ostringstream, etc -#include // for distance -#include // for map -#include // for accumulate -#include // for pair, make_pair +#include +#include +#include +#include +#include namespace Ioexnl { template DecompositionData::DecompositionData(const Ioss::PropertyManager &props, diff --git a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_ParallelDatabaseIO.C b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_ParallelDatabaseIO.C index 5bd79bc573..4676a8a85a 100644 --- a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_ParallelDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_ParallelDatabaseIO.C @@ -1407,7 +1407,7 @@ namespace Ioexnl { void ParallelDatabaseIO::write_meta_data(Ioss::IfDatabaseExistsBehavior behavior) { Ioss::Region *region = get_region(); - common_write_meta_data(behavior); + common_write_metadata(behavior); char the_title[max_line_length + 1]; @@ -1447,7 +1447,7 @@ namespace Ioexnl { if (behavior != Ioss::DB_APPEND && behavior != Ioss::DB_MODIFY) { output_node_map(); - output_other_meta_data(); + output_other_metadata(); } } diff --git a/packages/seacas/libraries/ioss/src/gen_struc/Iogs_DatabaseIO.C b/packages/seacas/libraries/ioss/src/gen_struc/Iogs_DatabaseIO.C index de7eace03e..4e0cf39be1 100644 --- a/packages/seacas/libraries/ioss/src/gen_struc/Iogs_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/gen_struc/Iogs_DatabaseIO.C @@ -517,7 +517,7 @@ namespace Iogs { sideset->property_add(Ioss::Property("guid", util().generate_guid(ifs + 1))); get_region()->add(sideset); - std::vector touching_blocks = m_generatedMesh->sideset_touching_blocks(ifs + 1); + Ioss::NameList touching_blocks = m_generatedMesh->sideset_touching_blocks(ifs + 1); if (touching_blocks.size() == 1) { std::string sd_block_name = name + "_quad4"; std::string side_topo_name = "quad4"; diff --git a/packages/seacas/libraries/ioss/src/gen_struc/Iogs_DatabaseIO.h b/packages/seacas/libraries/ioss/src/gen_struc/Iogs_DatabaseIO.h index 523823fba5..3a71d0a480 100644 --- a/packages/seacas/libraries/ioss/src/gen_struc/Iogs_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/gen_struc/Iogs_DatabaseIO.h @@ -87,10 +87,7 @@ namespace Iogs { void setGeneratedMesh(Iogs::GeneratedMesh *generatedMesh) { m_generatedMesh = generatedMesh; } - IOSS_NODISCARD const std::vector &get_sideset_names() const - { - return m_sideset_names; - } + IOSS_NODISCARD const Ioss::NameList &get_sideset_names() const { return m_sideset_names; } private: void read_meta_data_nl() override; @@ -151,8 +148,8 @@ namespace Iogs { void add_transient_fields(Ioss::GroupingEntity *entity); - GeneratedMesh *m_generatedMesh{nullptr}; - std::vector m_sideset_names{}; + GeneratedMesh *m_generatedMesh{nullptr}; + Ioss::NameList m_sideset_names{}; double currentTime{0.0}; int spatialDimension{3}; diff --git a/packages/seacas/libraries/ioss/src/gen_struc/Iogs_GeneratedMesh.C b/packages/seacas/libraries/ioss/src/gen_struc/Iogs_GeneratedMesh.C index 6a13fec34d..d558e39241 100644 --- a/packages/seacas/libraries/ioss/src/gen_struc/Iogs_GeneratedMesh.C +++ b/packages/seacas/libraries/ioss/src/gen_struc/Iogs_GeneratedMesh.C @@ -156,7 +156,7 @@ namespace Iogs { offZ = off_z; } - void GeneratedMesh::parse_options(const std::vector &groups) + void GeneratedMesh::parse_options(const Ioss::NameList &groups) { for (size_t i = 1; i < groups.size(); i++) { auto option = Ioss::tokenize(groups[i], ":"); @@ -822,9 +822,9 @@ namespace Iogs { element_surface_map(loc, elem_sides); } - std::vector GeneratedMesh::sideset_touching_blocks(int64_t /*set_id*/) const + Ioss::NameList GeneratedMesh::sideset_touching_blocks(int64_t /*set_id*/) const { - std::vector result(1, "block_1"); + Ioss::NameList result(1, "block_1"); return result; } diff --git a/packages/seacas/libraries/ioss/src/gen_struc/Iogs_GeneratedMesh.h b/packages/seacas/libraries/ioss/src/gen_struc/Iogs_GeneratedMesh.h index 6c13764803..a45b2ea9a2 100644 --- a/packages/seacas/libraries/ioss/src/gen_struc/Iogs_GeneratedMesh.h +++ b/packages/seacas/libraries/ioss/src/gen_struc/Iogs_GeneratedMesh.h @@ -356,7 +356,7 @@ namespace Iogs { */ virtual void sideset_elem_sides(int64_t id, Ioss::Int64Vector &elem_sides) const; - IOSS_NODISCARD virtual std::vector sideset_touching_blocks(int64_t set_id) const; + IOSS_NODISCARD virtual Ioss::NameList sideset_touching_blocks(int64_t set_id) const; IOSS_NODISCARD int64_t get_num_x() const { return numX; } IOSS_NODISCARD int64_t get_num_y() const { return numY; } @@ -376,7 +376,7 @@ namespace Iogs { GeneratedMesh &operator=(const GeneratedMesh &); void set_variable_count(const std::string &type, size_t count); - void parse_options(const std::vector &groups); + void parse_options(const Ioss::NameList &groups); void show_parameters() const; void initialize(); diff --git a/packages/seacas/libraries/ioss/src/generated/Iogn_DashSurfaceMesh.C b/packages/seacas/libraries/ioss/src/generated/Iogn_DashSurfaceMesh.C index 953ffaa7d1..074774cb14 100644 --- a/packages/seacas/libraries/ioss/src/generated/Iogn_DashSurfaceMesh.C +++ b/packages/seacas/libraries/ioss/src/generated/Iogn_DashSurfaceMesh.C @@ -350,7 +350,7 @@ namespace Iogn { } } - std::vector ExodusMesh::sideset_touching_blocks(int64_t setId) const + Ioss::NameList ExodusMesh::sideset_touching_blocks(int64_t setId) const { return mExodusData.sidesetTouchingBlocks[setId - 1]; } diff --git a/packages/seacas/libraries/ioss/src/generated/Iogn_DashSurfaceMesh.h b/packages/seacas/libraries/ioss/src/generated/Iogn_DashSurfaceMesh.h index 3e5f9fa672..673d987eb3 100644 --- a/packages/seacas/libraries/ioss/src/generated/Iogn_DashSurfaceMesh.h +++ b/packages/seacas/libraries/ioss/src/generated/Iogn_DashSurfaceMesh.h @@ -63,17 +63,16 @@ namespace Iogn { // element side (1-based) The side id is: side_id = // 10*element_id + local_side_number This assumes that all // sides in a sideset are boundary sides. - std::vector> sidesetConnectivity; - std::vector> sidesetTouchingBlocks; + std::vector> sidesetConnectivity; + std::vector sidesetTouchingBlocks; ExodusData() = delete; ExodusData(std::vector coords, std::vector> elemBlockConnectivity, std::vector globalNumOfElemsInBlock, std::vector localNumOfElemsInBlock, std::vector blockTopoData, int globalNumNodes, std::vector globalIdsOfLocalElems, std::vector globalIdsLocalNodes, - std::vector> sidesetConn = std::vector>(), - std::vector> sidesetBlocks = - std::vector>()) + std::vector> sidesetConn = std::vector>(), + std::vector sidesetBlocks = std::vector()) : coordinates(std::move(coords)), elementBlockConnectivity(std::move(elemBlockConnectivity)), globalNumberOfElementsInBlock(std::move(globalNumOfElemsInBlock)), @@ -226,7 +225,7 @@ namespace Iogn { void sideset_elem_sides(int64_t setId, std::vector &elem_sides) const override; - std::vector sideset_touching_blocks(int64_t setId) const override; + Ioss::NameList sideset_touching_blocks(int64_t setId) const override; void nodeset_nodes(int64_t nset_id, std::vector &nodes) const override; diff --git a/packages/seacas/libraries/ioss/src/generated/Iogn_DatabaseIO.C b/packages/seacas/libraries/ioss/src/generated/Iogn_DatabaseIO.C index 086e9325e4..aab00bca2b 100644 --- a/packages/seacas/libraries/ioss/src/generated/Iogn_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/generated/Iogn_DatabaseIO.C @@ -671,7 +671,7 @@ namespace Iogn { sideset->property_add(Ioss::Property("guid", util().generate_guid(iss + 1))); get_region()->add(sideset); - std::vector touching_blocks = m_generatedMesh->sideset_touching_blocks(iss + 1); + Ioss::NameList touching_blocks = m_generatedMesh->sideset_touching_blocks(iss + 1); if (touching_blocks.size() == 1) { std::string sd_block_name = name; sd_block_name += "_"; diff --git a/packages/seacas/libraries/ioss/src/generated/Iogn_DatabaseIO.h b/packages/seacas/libraries/ioss/src/generated/Iogn_DatabaseIO.h index c7e3aa04d6..7ee2e50488 100644 --- a/packages/seacas/libraries/ioss/src/generated/Iogn_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/generated/Iogn_DatabaseIO.h @@ -96,7 +96,7 @@ namespace Iogn { void setGeneratedMesh(Iogn::GeneratedMesh *generatedMesh) { m_generatedMesh = generatedMesh; } - const std::vector &get_sideset_names() const { return m_sideset_names; } + const Ioss::NameList &get_sideset_names() const { return m_sideset_names; } private: void read_meta_data_nl() override; @@ -159,8 +159,8 @@ namespace Iogn { void add_transient_fields(Ioss::GroupingEntity *entity); - GeneratedMesh *m_generatedMesh{nullptr}; - std::vector m_sideset_names{}; + GeneratedMesh *m_generatedMesh{nullptr}; + Ioss::NameList m_sideset_names{}; double currentTime{0.0}; int spatialDimension{3}; diff --git a/packages/seacas/libraries/ioss/src/generated/Iogn_GeneratedMesh.C b/packages/seacas/libraries/ioss/src/generated/Iogn_GeneratedMesh.C index 7c22d6467d..9fa3400924 100644 --- a/packages/seacas/libraries/ioss/src/generated/Iogn_GeneratedMesh.C +++ b/packages/seacas/libraries/ioss/src/generated/Iogn_GeneratedMesh.C @@ -213,7 +213,7 @@ namespace Iogn { offZ = off_z; } - void GeneratedMesh::parse_options(const std::vector &groups) + void GeneratedMesh::parse_options(const Ioss::NameList &groups) { for (size_t i = 1; i < groups.size(); i++) { auto option = Ioss::tokenize(groups[i], ":"); @@ -1661,9 +1661,9 @@ namespace Iogn { } } - std::vector GeneratedMesh::sideset_touching_blocks(int64_t /*set_id*/) const + Ioss::NameList GeneratedMesh::sideset_touching_blocks(int64_t /*set_id*/) const { - std::vector result(1, "block_1"); + Ioss::NameList result(1, "block_1"); return result; } diff --git a/packages/seacas/libraries/ioss/src/generated/Iogn_GeneratedMesh.h b/packages/seacas/libraries/ioss/src/generated/Iogn_GeneratedMesh.h index 0595e4c59c..aac60376b5 100644 --- a/packages/seacas/libraries/ioss/src/generated/Iogn_GeneratedMesh.h +++ b/packages/seacas/libraries/ioss/src/generated/Iogn_GeneratedMesh.h @@ -452,7 +452,7 @@ namespace Iogn { */ virtual void sideset_elem_sides(int64_t id, Ioss::Int64Vector &elem_sides) const; - virtual std::vector sideset_touching_blocks(int64_t set_id) const; + virtual Ioss::NameList sideset_touching_blocks(int64_t set_id) const; IOSS_NODISCARD int64_t get_num_x() const { return numX; } IOSS_NODISCARD int64_t get_num_y() const { return numY; } @@ -469,7 +469,7 @@ namespace Iogn { template void raw_connectivity(int64_t block_number, INT *connect) const; void set_variable_count(const std::string &type, size_t count); - void parse_options(const std::vector &groups); + void parse_options(const Ioss::NameList &groups); void show_parameters() const; void initialize(); diff --git a/packages/seacas/libraries/ioss/src/main/cgns_decomp.C b/packages/seacas/libraries/ioss/src/main/cgns_decomp.C index d793f3941c..850afe05fb 100644 --- a/packages/seacas/libraries/ioss/src/main/cgns_decomp.C +++ b/packages/seacas/libraries/ioss/src/main/cgns_decomp.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2024 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. // @@ -316,7 +316,7 @@ namespace { } } - // In Iocgns::Utils::common_write_meta_data, there is code to make + // In Iocgns::Utils::common_write_metadata, there is code to make // sure that the zgc.m_connectionName is unique for all zgc instances on // a zone / processor pair (if !parallel_io which is file-per-processor) // The uniquification appends a letter from 'A' to 'Z' to the name diff --git a/packages/seacas/libraries/ioss/src/main/info_interface.C b/packages/seacas/libraries/ioss/src/main/info_interface.C index a6699f0f44..2e10d616c6 100644 --- a/packages/seacas/libraries/ioss/src/main/info_interface.C +++ b/packages/seacas/libraries/ioss/src/main/info_interface.C @@ -77,6 +77,9 @@ void Info::Interface::enroll_options() "\t\tPrimarily used for testing", nullptr); + options_.enroll("detailed_field_info", Ioss::GetLongOption::NoValue, + "Output very detailed information about each field", nullptr); + options_.enroll("use_generic_names", Ioss::GetLongOption::NoValue, "Use generic names (type_id) instead of names in database", nullptr); @@ -199,6 +202,7 @@ bool Info::Interface::parse_options(int argc, char **argv) summary_ = options_.retrieve("summary") != nullptr; showConfig_ = options_.retrieve("configuration") != nullptr; queryTimeOnly_ = options_.retrieve("query_timesteps_only") != nullptr; + fieldDetails_ = options_.retrieve("detailed_field_info") != nullptr; filetype_ = options_.get_option_value("db_type", filetype_); filetype_ = options_.get_option_value("in_type", filetype_); diff --git a/packages/seacas/libraries/ioss/src/main/info_interface.h b/packages/seacas/libraries/ioss/src/main/info_interface.h index 65d304582c..a5191171ea 100644 --- a/packages/seacas/libraries/ioss/src/main/info_interface.h +++ b/packages/seacas/libraries/ioss/src/main/info_interface.h @@ -32,6 +32,7 @@ namespace Info { bool list_groups() const { return listGroups_; } bool show_config() const { return showConfig_; } bool query_timesteps_only() const { return queryTimeOnly_; } + bool field_details() const { return fieldDetails_; } int surface_split_scheme() const { return surfaceSplitScheme_; } char field_suffix_separator() const { return fieldSuffixSeparator_; } @@ -66,6 +67,7 @@ namespace Info { bool showConfig_{false}; bool summary_{false}; bool queryTimeOnly_{false}; + bool fieldDetails_{false}; char fieldSuffixSeparator_{'_'}; diff --git a/packages/seacas/libraries/ioss/src/main/io_info.C b/packages/seacas/libraries/ioss/src/main/io_info.C index e3dabc7cbd..c8f25eaf60 100644 --- a/packages/seacas/libraries/ioss/src/main/io_info.C +++ b/packages/seacas/libraries/ioss/src/main/io_info.C @@ -64,15 +64,15 @@ namespace { void info_timesteps(Ioss::Region ®ion); void info_nodeblock(Ioss::Region ®ion, const Info::Interface &interFace); - void info_edgeblock(Ioss::Region ®ion); - void info_faceblock(Ioss::Region ®ion); + void info_edgeblock(Ioss::Region ®ion, const Info::Interface &interFace); + void info_faceblock(Ioss::Region ®ion, const Info::Interface &interFace); void info_elementblock(Ioss::Region ®ion, const Info::Interface &interFace); void info_structuredblock(Ioss::Region ®ion, const Info::Interface &interFace); - void info_nodesets(Ioss::Region ®ion); - void info_edgesets(Ioss::Region ®ion); - void info_facesets(Ioss::Region ®ion); - void info_elementsets(Ioss::Region ®ion); + void info_nodesets(Ioss::Region ®ion, const Info::Interface &interFace); + void info_edgesets(Ioss::Region ®ion, const Info::Interface &interFace); + void info_facesets(Ioss::Region ®ion, const Info::Interface &interFace); + void info_elementsets(Ioss::Region ®ion, const Info::Interface &interFace); void info_sidesets(Ioss::Region ®ion, const Info::Interface &interFace); void info_coordinate_frames(Ioss::Region ®ion); @@ -83,6 +83,14 @@ namespace { void info_aliases(const Ioss::Region ®ion, const Ioss::GroupingEntity *ige, bool nl_pre, bool nl_post); + void info_variable_types() + { + auto var_list = Ioss::VariableType::external_types(Ioss::VariableType::Type::UNKNOWN); + for (auto &var : var_list) { + var->print(); + } + } + void file_info(const Info::Interface &interFace); void group_info(Info::Interface &interFace); @@ -206,7 +214,7 @@ namespace { if (!custom_field.empty()) { auto suffices = Ioss::tokenize(custom_field, ","); if (suffices.size() > 1) { - Ioss::VariableType::create_named_suffix_field_type("UserDefined", suffices); + Ioss::VariableType::create_named_suffix_type("UserDefined", suffices); } } @@ -265,7 +273,8 @@ namespace { Ioss::Utils::info_fields(&nb, Ioss::Field::ATTRIBUTE, prefix + "\tAttributes: ", "\n\t\t" + prefix); Ioss::Utils::info_fields(&nb, Ioss::Field::TRANSIENT, - prefix + "\tTransient: ", "\n\t\t" + prefix); + prefix + "\tTransient: ", "\n\t\t" + prefix, + interFace.field_details()); if (interFace.compute_bbox() && region.mesh_type() != Ioss::MeshType::STRUCTURED) { print_bbox(nb); @@ -299,7 +308,8 @@ namespace { fmt::group_digits(num_node)); info_aliases(region, sb, true, false); - Ioss::Utils::info_fields(sb, Ioss::Field::TRANSIENT, "\n\tTransient: "); + Ioss::Utils::info_fields(sb, Ioss::Field::TRANSIENT, "\n\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(sb, Ioss::Field::REDUCTION, "\n\tTransient (Reduction): ", "\t"); info_nodeblock(region, sb->get_node_block(), interFace, "\t"); fmt::print("\n"); @@ -344,6 +354,7 @@ namespace { Ioss::Utils::info_property(®ion, Ioss::Property::ATTRIBUTE, "\tAttributes (Reduction): ", "\t"); Ioss::Utils::info_fields(®ion, Ioss::Field::REDUCTION, "\tTransient (Reduction): ", "\t"); + info_variable_types(); } void info_assemblies(Ioss::Region ®ion) @@ -397,13 +408,14 @@ namespace { Ioss::Utils::info_property(eb, Ioss::Property::ATTRIBUTE, "\tAttributes (Reduction): ", "\t"); if (interFace.adjacencies()) { - std::vector blocks = eb->get_block_adjacencies(); + Ioss::NameList blocks = eb->get_block_adjacencies(); fmt::print("\n\tAdjacent to {} element block(s):\t", blocks.size()); for (const auto &block : blocks) { fmt::print("{} ", block); } } - Ioss::Utils::info_fields(eb, Ioss::Field::TRANSIENT, "\n\tTransient: "); + Ioss::Utils::info_fields(eb, Ioss::Field::TRANSIENT, "\n\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(eb, Ioss::Field::REDUCTION, "\n\tTransient (Reduction): "); if (interFace.compute_bbox()) { @@ -412,7 +424,7 @@ namespace { } } - void info_edgeblock(Ioss::Region ®ion) + void info_edgeblock(Ioss::Region ®ion, const Info::Interface &interFace) { const Ioss::EdgeBlockContainer &ebs = region.get_edge_blocks(); for (auto &eb : ebs) { @@ -427,19 +439,20 @@ namespace { Ioss::Utils::info_fields(eb, Ioss::Field::ATTRIBUTE, "\tAttributes: "); #if 0 - std::vector blocks = eb->get_block_adjacencies(); + Ioss::NameList blocks = eb->get_block_adjacencies(); fmt::print("\tAdjacent to {} edge block(s):\t", blocks.size()); for (auto &block : blocks) { fmt::print("{} ", block); } #endif - Ioss::Utils::info_fields(eb, Ioss::Field::TRANSIENT, "\n\tTransient: "); + Ioss::Utils::info_fields(eb, Ioss::Field::TRANSIENT, "\n\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(eb, Ioss::Field::REDUCTION, "\n\tTransient (Reduction): "); fmt::print("\n"); } } - void info_faceblock(Ioss::Region ®ion) + void info_faceblock(Ioss::Region ®ion, const Info::Interface &interFace) { const Ioss::FaceBlockContainer &ebs = region.get_face_blocks(); for (auto &eb : ebs) { @@ -454,13 +467,14 @@ namespace { Ioss::Utils::info_fields(eb, Ioss::Field::ATTRIBUTE, "\tAttributes: "); #if 0 - std::vector blocks = eb->get_block_adjacencies(); + Ioss::NameList blocks = eb->get_block_adjacencies(); fmt::print("\tAdjacent to {} face block(s):\t", blocks.size()); for (auto &block : blocks) { fmt::print("{} ", block); } #endif - Ioss::Utils::info_fields(eb, Ioss::Field::TRANSIENT, "\n\tTransient: "); + Ioss::Utils::info_fields(eb, Ioss::Field::TRANSIENT, "\n\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(eb, Ioss::Field::REDUCTION, "\n\tTransient (Reduction): "); fmt::print("\n"); } @@ -480,10 +494,11 @@ namespace { #endif } info_aliases(region, fs, true, false); - Ioss::Utils::info_fields(fs, Ioss::Field::TRANSIENT, "\n\tTransient: "); + Ioss::Utils::info_fields(fs, Ioss::Field::TRANSIENT, "\n\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(fs, Ioss::Field::REDUCTION, "\n\tTransient (Reduction): "); if (interFace.adjacencies()) { - std::vector blocks; + Ioss::NameList blocks; fs->block_membership(blocks); fmt::print("\n\t\tTouches {} element block(s):\t", blocks.size()); for (const auto &block : blocks) { @@ -499,14 +514,15 @@ namespace { fmt::print("\t{}, {:8} sides, {:3d} attributes, {:8} distribution factors.\n", name(fb), fmt::group_digits(count), num_attrib, fmt::group_digits(num_dist)); info_df(fb, "\t\t"); - Ioss::Utils::info_fields(fb, Ioss::Field::TRANSIENT, "\t\tTransient: ", "\n\t\t"); + Ioss::Utils::info_fields(fb, Ioss::Field::TRANSIENT, "\t\tTransient: ", "\n\t\t", + interFace.field_details()); Ioss::Utils::info_fields(fb, Ioss::Field::REDUCTION, "\t\tTransient (Reduction): ", "\n\t\t"); } } } - void info_nodesets(Ioss::Region ®ion) + void info_nodesets(Ioss::Region ®ion, const Info::Interface &interFace) { const Ioss::NodeSetContainer &nss = region.get_nodesets(); for (auto &ns : nss) { @@ -519,12 +535,13 @@ namespace { info_aliases(region, ns, false, true); info_df(ns, "\t"); Ioss::Utils::info_fields(ns, Ioss::Field::ATTRIBUTE, "\tAttributes: "); - Ioss::Utils::info_fields(ns, Ioss::Field::TRANSIENT, "\tTransient: "); + Ioss::Utils::info_fields(ns, Ioss::Field::TRANSIENT, "\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(ns, Ioss::Field::REDUCTION, "\tTransient (Reduction): "); } } - void info_edgesets(Ioss::Region ®ion) + void info_edgesets(Ioss::Region ®ion, const Info::Interface &interFace) { const Ioss::EdgeSetContainer &nss = region.get_edgesets(); for (auto &ns : nss) { @@ -535,12 +552,13 @@ namespace { info_aliases(region, ns, false, true); info_df(ns, "\t"); Ioss::Utils::info_fields(ns, Ioss::Field::ATTRIBUTE, "\tAttributes: "); - Ioss::Utils::info_fields(ns, Ioss::Field::TRANSIENT, "\tTransient: "); + Ioss::Utils::info_fields(ns, Ioss::Field::TRANSIENT, "\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(ns, Ioss::Field::REDUCTION, "\tTransient (Reduction): "); } } - void info_facesets(Ioss::Region ®ion) + void info_facesets(Ioss::Region ®ion, const Info::Interface &interFace) { const Ioss::FaceSetContainer &fss = region.get_facesets(); for (auto &fs : fss) { @@ -551,12 +569,13 @@ namespace { info_aliases(region, fs, false, true); info_df(fs, "\t"); Ioss::Utils::info_fields(fs, Ioss::Field::ATTRIBUTE, "\tAttributes: "); - Ioss::Utils::info_fields(fs, Ioss::Field::TRANSIENT, "\tTransient: "); + Ioss::Utils::info_fields(fs, Ioss::Field::TRANSIENT, "\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(fs, Ioss::Field::REDUCTION, "\tTransient (Reduction): "); } } - void info_elementsets(Ioss::Region ®ion) + void info_elementsets(Ioss::Region ®ion, const Info::Interface &interFace) { const Ioss::ElementSetContainer &ess = region.get_elementsets(); for (auto &es : ess) { @@ -565,7 +584,8 @@ namespace { info_aliases(region, es, false, true); info_df(es, "\t"); Ioss::Utils::info_fields(es, Ioss::Field::ATTRIBUTE, "\tAttributes: "); - Ioss::Utils::info_fields(es, Ioss::Field::TRANSIENT, "\tTransient: "); + Ioss::Utils::info_fields(es, Ioss::Field::TRANSIENT, "\tTransient: ", "\n\t", + interFace.field_details()); Ioss::Utils::info_fields(es, Ioss::Field::REDUCTION, "\tTransient (Reduction): "); } } @@ -590,7 +610,7 @@ namespace { void info_aliases(const Ioss::Region ®ion, const Ioss::GroupingEntity *ige, bool nl_pre, bool nl_post) { - std::vector aliases; + Ioss::NameList aliases; if (region.get_aliases(ige->name(), ige->type(), aliases) > 0) { if (nl_pre) { fmt::print("\n"); @@ -688,15 +708,15 @@ namespace Ioss { info_region(region); info_assemblies(region); info_nodeblock(region, interFace); - info_edgeblock(region); - info_faceblock(region); + info_edgeblock(region, interFace); + info_faceblock(region, interFace); info_elementblock(region, interFace); info_structuredblock(region, interFace); - info_nodesets(region); - info_edgesets(region); - info_facesets(region); - info_elementsets(region); + info_nodesets(region, interFace); + info_edgesets(region, interFace); + info_facesets(region, interFace); + info_elementsets(region, interFace); info_sidesets(region, interFace); info_blobs(region); diff --git a/packages/seacas/libraries/ioss/src/main/io_modify.C b/packages/seacas/libraries/ioss/src/main/io_modify.C index 698302263f..b0461dc297 100644 --- a/packages/seacas/libraries/ioss/src/main/io_modify.C +++ b/packages/seacas/libraries/ioss/src/main/io_modify.C @@ -118,17 +118,16 @@ namespace { Ioss::NameList get_name_list(const Ioss::Region ®ion, Ioss::EntityType type); void handle_help(const std::string &topic); - bool handle_delete(const std::vector &tokens, Ioss::Region ®ion); - void handle_list(const std::vector &tokens, const Ioss::Region ®ion, + bool handle_delete(const Ioss::NameList &tokens, Ioss::Region ®ion); + void handle_list(const Ioss::NameList &tokens, const Ioss::Region ®ion, bool show_attribute = false); - void handle_graph(const std::vector &tokens, const Ioss::Region ®ion); - bool handle_assembly(const std::vector &tokens, Ioss::Region ®ion, - bool allow_modify); - bool handle_attribute(const std::vector &tokens, Ioss::Region ®ion); - bool handle_geometry(const std::vector &tokens, Ioss::Region ®ion); - bool handle_time(const std::vector &tokens, Ioss::Region ®ion); - bool handle_rename(const std::vector &tokens, Ioss::Region ®ion); - void update_assembly_info(Ioss::Region ®ion, const Modify::Interface &interFace); + void handle_graph(const Ioss::NameList &tokens, const Ioss::Region ®ion); + bool handle_assembly(const Ioss::NameList &tokens, Ioss::Region ®ion, bool allow_modify); + bool handle_attribute(const Ioss::NameList &tokens, Ioss::Region ®ion); + bool handle_geometry(const Ioss::NameList &tokens, Ioss::Region ®ion); + bool handle_time(const Ioss::NameList &tokens, Ioss::Region ®ion); + bool handle_rename(const Ioss::NameList &tokens, Ioss::Region ®ion); + void update_assembly_info(Ioss::Region ®ion, const Modify::Interface &interFace); void modify_time(Ioss::Region ®ion, double scale, double offset); @@ -153,7 +152,7 @@ namespace { void info_time(const Ioss::Region ®ion); template - void info_entities(const std::vector &entities, const std::vector &tokens, + void info_entities(const std::vector &entities, const Ioss::NameList &tokens, const Ioss::Region ®ion, const std::string &type, bool show_property = false) { @@ -696,8 +695,7 @@ namespace { } } - void handle_list(const std::vector &tokens, const Ioss::Region ®ion, - bool show_attribute) + void handle_list(const Ioss::NameList &tokens, const Ioss::Region ®ion, bool show_attribute) { if (tokens.size() > 1) { if (Ioss::Utils::substr_equal(tokens[1], "summary")) { @@ -764,9 +762,9 @@ namespace { class Graph { - std::map m_vertices; - std::vector m_vertex; - std::vector> m_adj; // Pointer to an array containing adjacency std::lists + std::map m_vertices; + Ioss::NameList m_vertex; + std::vector m_adj; // Pointer to an array containing adjacency std::lists bool is_cyclic_internal(int v, std::vector &visited, std::vector &recStack); int vertex(const std::string &node); @@ -854,7 +852,7 @@ namespace { return false; } - void handle_graph(const std::vector &tokens, const Ioss::Region ®ion) + void handle_graph(const Ioss::NameList &tokens, const Ioss::Region ®ion) { if (tokens.size() == 1) { handle_help("graph"); @@ -915,7 +913,7 @@ namespace { } } - bool handle_delete(const std::vector &tokens, Ioss::Region ®ion) + bool handle_delete(const Ioss::NameList &tokens, Ioss::Region ®ion) { // Returns true if requested assembly was deleted. // False if assembly does not exist, or was not deletable (not created during this run) @@ -957,7 +955,7 @@ namespace { return false; } - bool handle_attribute(const std::vector &tokens, Ioss::Region ®ion) + bool handle_attribute(const Ioss::NameList &tokens, Ioss::Region ®ion) { // 0 1 2 3 4 5... // ATTRIBUTE {{ent_name}} ADD {{att_name}} STRING {{values...}} @@ -1023,7 +1021,7 @@ namespace { ge->property_add(Ioss::Property(att_name, values, Ioss::Property::ATTRIBUTE)); } else if (Ioss::Utils::substr_equal(tokens[4], "integer")) { - std::vector values(value_count); + Ioss::IntVector values(value_count); for (size_t i = 0; i < value_count; i++) { try { int val = std::stoi(tokens[i + 5]); @@ -1085,7 +1083,7 @@ namespace { return false; } - bool handle_rename(const std::vector &tokens, Ioss::Region ®ion) + bool handle_rename(const Ioss::NameList &tokens, Ioss::Region ®ion) { // 0 1 2 3 4 // RENAME {{ent_name}} TO {{new_name}} @@ -1193,14 +1191,14 @@ namespace { auto node_count = region.get_property("node_count").get_int(); if (blocks.empty() || blocks.size() == (size_t)region.get_property("element_block_count").get_int()) { - return std::vector(node_count, 1); + return Ioss::IntVector(node_count, 1); } else { - std::vector node_filter(node_count); + Ioss::IntVector node_filter(node_count); // Iterate all element blocks in 'blocks', get connectivity_raw // and set `node_filter` to 1 for nodes in connectivity list. if (region.get_database()->int_byte_size_api() == 4) { - std::vector connect; + Ioss::IntVector connect; for (const auto *block : blocks) { block->get_field_data("connectivity_raw", connect); for (auto node : connect) { @@ -1221,7 +1219,7 @@ namespace { } } - bool handle_time(const std::vector &tokens, Ioss::Region ®ion) + bool handle_time(const Ioss::NameList &tokens, Ioss::Region ®ion) { // 0 1 2 // TIME SCALE {{scale}} @@ -1254,7 +1252,7 @@ namespace { return false; } - bool handle_geometry(const std::vector &tokens, Ioss::Region ®ion) + bool handle_geometry(const Ioss::NameList &tokens, Ioss::Region ®ion) { // 0 1 2 3 4 5... // GEOMETRY ROTATE {{X|Y|Z}} {{angle}} ... @@ -1461,8 +1459,7 @@ namespace { return false; } - bool handle_assembly(const std::vector &tokens, Ioss::Region ®ion, - bool allow_modify) + bool handle_assembly(const Ioss::NameList &tokens, Ioss::Region ®ion, bool allow_modify) { bool changed = false; Ioss::Assembly *assem = nullptr; diff --git a/packages/seacas/libraries/ioss/src/main/io_shell.C b/packages/seacas/libraries/ioss/src/main/io_shell.C index 251fe49cf6..b4f7f5e76e 100644 --- a/packages/seacas/libraries/ioss/src/main/io_shell.C +++ b/packages/seacas/libraries/ioss/src/main/io_shell.C @@ -122,7 +122,7 @@ int main(int argc, char *argv[]) if (!interFace.customField.empty()) { auto suffices = Ioss::tokenize(interFace.customField, ","); if (suffices.size() > 1) { - Ioss::VariableType::create_named_suffix_field_type("UserDefined", suffices); + Ioss::VariableType::create_named_suffix_type("UserDefined", suffices); } } std::string in_file = interFace.inputFile[0]; diff --git a/packages/seacas/libraries/ioss/src/main/io_shell_ts.C b/packages/seacas/libraries/ioss/src/main/io_shell_ts.C index f2db27fcf4..45bf61a950 100644 --- a/packages/seacas/libraries/ioss/src/main/io_shell_ts.C +++ b/packages/seacas/libraries/ioss/src/main/io_shell_ts.C @@ -984,25 +984,6 @@ namespace { pool.data.resize(isize); switch (interFace.data_storage_type) { case 1: ige->get_field_data(field_name, Data(pool.data), isize); break; - case 2: - if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { - ige->get_field_data(field_name, Data(pool.data), isize); - } - else if ((basic_type == Ioss::Field::INTEGER) || (basic_type == Ioss::Field::INT32)) { - ige->get_field_data(field_name, pool.data_int); - } - else if (basic_type == Ioss::Field::INT64) { - ige->get_field_data(field_name, pool.data_int64); - } - else if (basic_type == Ioss::Field::REAL) { - ige->get_field_data(field_name, pool.data_double); - } - else if (basic_type == Ioss::Field::COMPLEX) { - ige->get_field_data(field_name, pool.data_complex); - } - else { - } - break; #ifdef SEACAS_HAVE_KOKKOS case 3: if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { @@ -1078,25 +1059,6 @@ namespace { switch (interFace.data_storage_type) { case 1: oge->put_field_data(out_field_name, Data(pool.data), osize); break; - case 2: - if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { - oge->put_field_data(field_name, Data(pool.data), osize); - } - else if ((basic_type == Ioss::Field::INTEGER) || (basic_type == Ioss::Field::INT32)) { - oge->put_field_data(field_name, pool.data_int); - } - else if (basic_type == Ioss::Field::INT64) { - oge->put_field_data(field_name, pool.data_int64); - } - else if (basic_type == Ioss::Field::REAL) { - oge->put_field_data(field_name, pool.data_double); - } - else if (basic_type == Ioss::Field::COMPLEX) { - oge->put_field_data(field_name, pool.data_complex); - } - else { - } - break; #ifdef SEACAS_HAVE_KOKKOS case 3: if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { @@ -1258,25 +1220,6 @@ namespace { pool.data.resize(isize); switch (interFace.data_storage_type) { case 1: ige->get_field_data(field_name, Data(pool.data), isize); break; - case 2: - if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { - ige->get_field_data(field_name, Data(pool.data), isize); - } - else if ((basic_type == Ioss::Field::INTEGER) || (basic_type == Ioss::Field::INT32)) { - ige->get_field_data(field_name, pool.data_int); - } - else if (basic_type == Ioss::Field::INT64) { - ige->get_field_data(field_name, pool.data_int64); - } - else if (basic_type == Ioss::Field::REAL) { - ige->get_field_data(field_name, pool.data_double); - } - else if (basic_type == Ioss::Field::COMPLEX) { - ige->get_field_data(field_name, pool.data_complex); - } - else { - } - break; #ifdef SEACAS_HAVE_KOKKOS case 3: if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { @@ -1352,25 +1295,6 @@ namespace { switch (interFace.data_storage_type) { case 1: oge->put_field_data(field_name, Data(pool.data), isize); break; - case 2: - if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { - oge->put_field_data(field_name, Data(pool.data), isize); - } - else if ((basic_type == Ioss::Field::INTEGER) || (basic_type == Ioss::Field::INT32)) { - oge->put_field_data(field_name, pool.data_int); - } - else if (basic_type == Ioss::Field::INT64) { - oge->put_field_data(field_name, pool.data_int64); - } - else if (basic_type == Ioss::Field::REAL) { - oge->put_field_data(field_name, pool.data_double); - } - else if (basic_type == Ioss::Field::COMPLEX) { - oge->put_field_data(field_name, pool.data_complex); - } - else { - } - break; #ifdef SEACAS_HAVE_KOKKOS case 3: if ((basic_type == Ioss::Field::CHARACTER) || (basic_type == Ioss::Field::STRING)) { @@ -1446,7 +1370,7 @@ namespace { { out.add_information_records(in.get_information_records()); - const std::vector &qa = in.get_qa_records(); + const Ioss::NameList &qa = in.get_qa_records(); for (size_t i = 0; i < qa.size(); i += 4) { out.add_qa_record(qa[i + 0], qa[i + 1], qa[i + 2], qa[i + 3]); } diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.C b/packages/seacas/libraries/ioss/src/main/shell_interface.C index b505caba4a..b9a9284621 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.C +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.C @@ -648,9 +648,6 @@ bool IOShell::Interface::parse_options(int argc, char **argv, int my_processor) if (std::strcmp(temp, "POINTER") == 0) { data_storage_type = 1; } - else if (std::strcmp(temp, "STD_VECTOR") == 0) { - data_storage_type = 2; - } #ifdef SEACAS_HAVE_KOKKOS else if (std::strcmp(temp, "KOKKOS_VIEW_1D") == 0) { data_storage_type = 3; @@ -667,10 +664,10 @@ bool IOShell::Interface::parse_options(int argc, char **argv, int my_processor) if (my_processor == 0) { fmt::print(stderr, "ERROR: Option data_storage must be one of\n"); #ifdef SEACAS_HAVE_KOKKOS - fmt::print(stderr, " POINTER, STD_VECTOR, KOKKOS_VIEW_1D, KOKKOS_VIEW_2D, or " + fmt::print(stderr, " POINTER, KOKKOS_VIEW_1D, KOKKOS_VIEW_2D, or " "KOKKOS_VIEW_2D_LAYOUTRIGHT_HOSTSPACE\n"); #else - fmt::print(stderr, " POINTER, or STD_VECTOR\n"); + fmt::print(stderr, " POINTER\n"); #endif } return false; diff --git a/packages/seacas/libraries/ioss/src/main/skinner.C b/packages/seacas/libraries/ioss/src/main/skinner.C index 38a335bdb1..6ab0a4f0bc 100644 --- a/packages/seacas/libraries/ioss/src/main/skinner.C +++ b/packages/seacas/libraries/ioss/src/main/skinner.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2024 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. // @@ -51,7 +51,7 @@ namespace { std::string version = "1.02"; void transfer_field_data(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge, - Ioss::Field::RoleType role, const std::vector &ref_nodes); + Ioss::Field::RoleType role, const Ioss::IntVector &ref_nodes); void transfer_field_data(Ioss::EntityBlock *ige, Ioss::EntityBlock *oge, Ioss::Field::RoleType role, @@ -85,13 +85,13 @@ namespace { fmt::print("\t+{2:-^{0}}+{2:-^{1}}+\n", max_name, max_face, ""); } - std::vector get_selected_steps(Ioss::Region ®ion, const Skinner::Interface &options) + Ioss::IntVector get_selected_steps(Ioss::Region ®ion, const Skinner::Interface &options) { // This routine checks all steps of the input database and selects those which // meet the requirements specified in `options`. The returned (1-based) vector will have a // value of `1` if the step is to be output and `0` if skipped. - int step_count = (int)region.get_property("state_count").get_int(); - std::vector selected_steps(step_count + 1); + int step_count = (int)region.get_property("state_count").get_int(); + Ioss::IntVector selected_steps(step_count + 1); // If user specified a list of times to transfer to output database, // process the list and find the times on the input database that are @@ -242,8 +242,8 @@ namespace { } // Iterate the boundary faces and determine which nodes are referenced... - size_t my_node_count = region.get_property("node_count").get_int(); - std::vector ref_nodes(my_node_count); + size_t my_node_count = region.get_property("node_count").get_int(); + Ioss::IntVector ref_nodes(my_node_count); for (const auto &boundaries : boundary_faces) { for (const auto &face : boundaries.second) { for (auto &gnode : face.connectivity_) { @@ -263,15 +263,15 @@ namespace { Ioss::NodeBlock *nb = region.get_node_blocks()[0]; nb->get_field_data("mesh_model_coordinates", coord_in); - std::vector ids; + Ioss::IntVector ids; nb->get_field_data("ids", ids); - std::vector owner; + Ioss::IntVector owner; nb->get_field_data("owning_processor", owner); - std::vector ref_ids(ref_count); + Ioss::IntVector ref_ids(ref_count); std::vector coord_out(3 * ref_count); - std::vector owner_out(ref_count); + Ioss::IntVector owner_out(ref_count); size_t j = 0; for (size_t i = 0; i < ref_nodes.size(); i++) { @@ -357,8 +357,8 @@ namespace { auto *block = output_region.get_element_block(name); size_t node_count = block->topology()->number_corner_nodes(); - std::vector conn; - std::vector elids; + Ioss::IntVector conn; + Ioss::IntVector elids; conn.reserve(node_count * boundary.size()); elids.reserve(boundary.size()); @@ -493,8 +493,7 @@ namespace { } void transfer_field_data_internal(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge, - const std::string &field_name, - const std::vector &ref_nodes) + const std::string &field_name, const Ioss::IntVector &ref_nodes) { if (oge->field_exists(field_name)) { std::vector in; @@ -520,7 +519,7 @@ namespace { } void transfer_field_data(Ioss::GroupingEntity *ige, Ioss::GroupingEntity *oge, - Ioss::Field::RoleType role, const std::vector &ref_nodes) + Ioss::Field::RoleType role, const Ioss::IntVector &ref_nodes) { // Iterate through the TRANSIENT-role fields of the input // database and transfer to output database. diff --git a/packages/seacas/libraries/ioss/src/pamgen/Iopg_DatabaseIO.C b/packages/seacas/libraries/ioss/src/pamgen/Iopg_DatabaseIO.C index 76ae6a309b..60322ee6d7 100644 --- a/packages/seacas/libraries/ioss/src/pamgen/Iopg_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/pamgen/Iopg_DatabaseIO.C @@ -1473,8 +1473,8 @@ namespace Iopg { return elemMap; } - void DatabaseIO::compute_block_membership_nl(Ioss::SideBlock *sideblock, - std::vector &block_membership) const + void DatabaseIO::compute_block_membership_nl(Ioss::SideBlock *sideblock, + Ioss::NameList &block_membership) const { Ioss::IntVector block_ids(elementBlockCount); if (elementBlockCount == 1) { diff --git a/packages/seacas/libraries/ioss/src/pamgen/Iopg_DatabaseIO.h b/packages/seacas/libraries/ioss/src/pamgen/Iopg_DatabaseIO.h index 810bb10e21..4a06939971 100644 --- a/packages/seacas/libraries/ioss/src/pamgen/Iopg_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/pamgen/Iopg_DatabaseIO.h @@ -80,8 +80,8 @@ namespace Iopg { IOSS_NODISCARD std::string title() const { return databaseTitle; } IOSS_NODISCARD int maximum_symbol_length() const override { return 32; } - void compute_block_membership_nl(Ioss::SideBlock *efblock, - std::vector &block_membership) const override; + void compute_block_membership_nl(Ioss::SideBlock *efblock, + Ioss::NameList &block_membership) const override; private: void read_meta_data_nl() override; diff --git a/packages/seacas/libraries/ioss/src/tokenize.C b/packages/seacas/libraries/ioss/src/tokenize.C index ba43c4c0f4..94f1dff747 100644 --- a/packages/seacas/libraries/ioss/src/tokenize.C +++ b/packages/seacas/libraries/ioss/src/tokenize.C @@ -8,14 +8,14 @@ #include using TokenList = std::vector; -TokenList Ioss::tokenize(const std::string &str, const std::string &separators) +TokenList Ioss::tokenize(const std::string &str, const std::string &separators, bool allow_empty) { TokenList tokens; auto first = std::begin(str); while (first != std::end(str)) { const auto second = std::find_first_of(first, std::end(str), std::begin(separators), std::end(separators)); - if (first != second) { + if (allow_empty || (first != second)) { tokens.emplace_back(first, second); } if (second == std::end(str)) { diff --git a/packages/seacas/libraries/ioss/src/tokenize.h b/packages/seacas/libraries/ioss/src/tokenize.h index 57c518bb9e..73bc21f20d 100644 --- a/packages/seacas/libraries/ioss/src/tokenize.h +++ b/packages/seacas/libraries/ioss/src/tokenize.h @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2020, 2022 National Technology & Engineering Solutions + * Copyright(C) 1999-2020, 2022, 2024 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. * @@ -17,6 +17,6 @@ namespace Ioss { * Take the 'str' argument and split it using the list of characters * in separators as separators. Use tokens to return the result. */ - IOSS_EXPORT std::vector tokenize(const std::string &str, - const std::string &separators); + IOSS_EXPORT std::vector + tokenize(const std::string &str, const std::string &separators, bool allow_empty = false); } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/utest/Utst_structured_decomp.C b/packages/seacas/libraries/ioss/src/utest/Utst_structured_decomp.C index ae51e88236..7713bf98ef 100644 --- a/packages/seacas/libraries/ioss/src/utest/Utst_structured_decomp.C +++ b/packages/seacas/libraries/ioss/src/utest/Utst_structured_decomp.C @@ -154,7 +154,7 @@ void check_split_assign(std::vector &zones, } } - // In Iocgns::Utils::common_write_meta_data, there is code to make + // In Iocgns::Utils::common_write_metadata, there is code to make // sure that the zgc.m_connectionName is unique for all zgc instances on // a zone / processor pair (if !parallel_io which is file-per-processor) // The uniquification appends a letter from 'A' to 'Z' to the name diff --git a/packages/seacas/libraries/ioss/src/visualization/cgns/Iovs_cgns_IOFactory.h b/packages/seacas/libraries/ioss/src/visualization/cgns/Iovs_cgns_IOFactory.h index 3a0e75b6d3..4cddabaf2d 100644 --- a/packages/seacas/libraries/ioss/src/visualization/cgns/Iovs_cgns_IOFactory.h +++ b/packages/seacas/libraries/ioss/src/visualization/cgns/Iovs_cgns_IOFactory.h @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2021 National Technology & Engineering Solutions +// Copyright(C) 1999-2021, 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. // diff --git a/packages/seacas/libraries/ioss/src/visualization/exodus/Iovs_exodus_IOFactory.h b/packages/seacas/libraries/ioss/src/visualization/exodus/Iovs_exodus_IOFactory.h index 28a5d01441..0d0fca14ad 100644 --- a/packages/seacas/libraries/ioss/src/visualization/exodus/Iovs_exodus_IOFactory.h +++ b/packages/seacas/libraries/ioss/src/visualization/exodus/Iovs_exodus_IOFactory.h @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2021 National Technology & Engineering Solutions +// Copyright(C) 1999-2021, 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. // diff --git a/signatures/version1/cla.json b/signatures/version1/cla.json index b42bc58b74..7516f5c063 100644 --- a/signatures/version1/cla.json +++ b/signatures/version1/cla.json @@ -161,4 +161,4 @@ "pullRequestNo": 448 } ] -} \ No newline at end of file +} From cca436d1320805183af6f01258b71d4e3e96286b Mon Sep 17 00:00:00 2001 From: Alex Pelletier <108826411+ajpelle@users.noreply.github.com> Date: Fri, 31 May 2024 08:36:06 -0600 Subject: [PATCH 12/59] Catalyst: Add support for Exodus IOSS Properties in Conduit Representation (#458) * Made conduit mimic exodus prop changes in Cat Database on read * Added field_data equality checks btwn Exodus and Catalyst DBs in exo prop tests --- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.C | 104 +++++++++++++++++- .../catalyst_tests/Iocatalyst_BlockMeshSet.C | 2 + .../Iocatalyst_DatabaseIOTest.h | 1 + .../Iocatalyst_ElementBlockTest.C | 75 ++++++++++++- 4 files changed, 177 insertions(+), 5 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C index d682ff764e..e4c0b8f35d 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C @@ -816,8 +816,104 @@ namespace Iocatalyst { return true; } + std::vector getScalarNamesFromNonScalarField(const Ioss::Field &field) const + { + int ncomp = field.get_component_count(Ioss::Field::InOut::INPUT); + std::vector fnames; + for(int i=1; i<=ncomp; i++){ + fnames.push_back(field.get_component_name(i, Ioss::Field::InOut::INPUT)); + } + return fnames; + } + + template + std::vector getInterweavedScalarDataFromConduitNode(const std::vector fnames, conduit_cpp::Node &node) const + { + int ncomp = fnames.size(); + auto &&t_node = node[fnames[0] + detail::FS + detail::VALUE]; + int num_get = t_node.number_of_elements(); + std::vector vals(ncomp*num_get); + + for(int i=0; i(child_value.element_ptr(0))[i]; + } + } + return vals; + } + + template + void addFieldNodeAndDataToConduitNode(const Ioss::Field &field, void *data, conduit_cpp::Node &node) const + { + int ncomp = field.get_component_count(Ioss::Field::InOut::INPUT); + int num_get = field.raw_count(); + node[field.get_name()] = conduit_cpp::Node(); + auto &&f_node = node[field.get_name()]; + f_node[detail::ROLE].set(static_cast(field.get_role())); + f_node[detail::TYPE].set(static_cast(field.get_type())); + f_node[detail::COUNT].set(static_cast(field.raw_count())); + f_node[detail::INDEX].set(static_cast(field.get_index())); + f_node[detail::COMPONENTCOUNT].set(static_cast(ncomp)); + f_node[detail::STORAGE].set(field.raw_storage()->name()); + f_node[detail::VALUE].set(static_cast< T *>(data), ncomp * num_get); + } + + void combineScalarFieldsInConduitNodeToNonScalarField( + const Ioss::Field &field, conduit_cpp::Node &node) const + { + std::vector fnames = + this->getScalarNamesFromNonScalarField(field); + + switch (field.get_type()) { + case Ioss::Field::BasicType::DOUBLE: + this->addFieldNodeAndDataToConduitNode( + field, + this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), + node + ); + break; + + case Ioss::Field::BasicType::INT32: + this->addFieldNodeAndDataToConduitNode( + field, + this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), + node + ); + break; + + case Ioss::Field::BasicType::INT64: + this->addFieldNodeAndDataToConduitNode( + field, + this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), + node + ); + break; + + case Ioss::Field::BasicType::CHARACTER: + this->addFieldNodeAndDataToConduitNode( + field, + this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), + node + ); + break; + default: + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR in {} on read: {}, unsupported field type: {}\n", __func__, + field.get_name(), field.type_string()); + IOSS_ERROR(errmsg); + } + + //Remove Related Scalars from Conduit Node + for(int i=0; i - bool readFields(const conduit_cpp::Node &&parent, GroupingEntityT *block) const + bool readFields(conduit_cpp::Node &&parent, GroupingEntityT *block) const { // Assumption: count = entity_count (in block) Ioss::DatabaseIO *dbase = block->get_database(); @@ -878,6 +974,12 @@ namespace Iocatalyst { for (const auto &field : fields) { block->field_add(field.set_zero_copy_enabled()); } + + for (const auto &field : fields) { + if(field.raw_storage()->name() != IOSS_SCALAR()) { + this->combineScalarFieldsInConduitNodeToNonScalarField(field, parent); + } + } } return true; diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.C index 10fa7f5721..9a7d870851 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_BlockMeshSet.C @@ -434,6 +434,7 @@ namespace Iocatalyst { values.push_back(itr->second + j*0.1); } elemBlock->put_field_data(itr->first, values); + values.clear(); } } @@ -449,6 +450,7 @@ namespace Iocatalyst { values.push_back(itr->second + j*0.1); } nodeBlock->put_field_data(itr->first, values); + values.clear(); } } diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.h b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.h index 62572ed949..ec5c63ac2a 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.h +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.h @@ -63,6 +63,7 @@ class IOCATALYST_EXPORT Iocatalyst_DatabaseIOTest : public ::testing::Test void *data; size_t dataSize; g->get_field_data(name, &data, &dataSize); + ASSERT_GT(dataSize, 0) << "DataSize is not greater than 0 for field "<(data); std::vector zcBuffer(b, b + field.get_size()); EXPECT_EQ(dcBuffer, zcBuffer); diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ElementBlockTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ElementBlockTest.C index afcbaa9141..9c8fec4c2d 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ElementBlockTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ElementBlockTest.C @@ -4,10 +4,18 @@ // // See packages/seacas/LICENSE for details -#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include -#include TEST_F(Iocatalyst_DatabaseIOTest, WriteThreeElementBlocksWith24Cells) @@ -92,6 +100,8 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_ENABLE_FIELD_RECOGNITION_ON) auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + checkEntityContainerZeroCopyFields(cat_reg.get_element_blocks()); + bool exo_foo_exists = exo_elemBlock->field_exists("foo"); bool cat_foo_exists = cat_elemBlock->field_exists("foo"); EXPECT_TRUE(exo_foo_exists); @@ -99,6 +109,17 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_ENABLE_FIELD_RECOGNITION_ON) if(exo_foo_exists && cat_foo_exists) EXPECT_TRUE(exo_elemBlock->get_field("foo") == cat_elemBlock->get_field("foo")); + //Check field data for equality + auto cat_field = cat_elemBlock->get_fieldref("foo"); + std::vector dcBuffer(cat_field.get_size()); + cat_elemBlock->get_field_data("foo", Data(dcBuffer), dcBuffer.size()); + + exo_reg.begin_state(1); + auto exo_field = exo_elemBlock->get_fieldref("foo"); + std::vector deBuffer(exo_field.get_size()); + exo_elemBlock->get_field_data("foo", Data(deBuffer), deBuffer.size()); + EXPECT_EQ(dcBuffer, deBuffer); + //Check foo_x doesn't exist bool exo_foo_x_exists = exo_elemBlock->field_exists("foo_x"); bool cat_foo_x_exists = cat_elemBlock->field_exists("foo_x"); @@ -135,6 +156,8 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_ENABLE_FIELD_RECOGNITION_OFF) auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + checkEntityContainerZeroCopyFields(cat_reg.get_element_blocks()); + bool exo_foo_x_exists = exo_elemBlock->field_exists("foo_x"); bool cat_foo_x_exists = cat_elemBlock->field_exists("foo_x"); EXPECT_TRUE(exo_foo_x_exists); @@ -173,12 +196,15 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_IGNORE_REALN_FIELDS_ON) auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + checkEntityContainerZeroCopyFields(cat_reg.get_element_blocks()); + bool exo_foo_1_exists = exo_elemBlock->field_exists("foo_1"); bool cat_foo_1_exists = cat_elemBlock->field_exists("foo_1"); EXPECT_TRUE(exo_foo_1_exists); EXPECT_TRUE(cat_foo_1_exists); if(exo_foo_1_exists && cat_foo_1_exists) EXPECT_TRUE(exo_elemBlock->get_field("foo_1") == cat_elemBlock->get_field("foo_1")); + } TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_IGNORE_REALN_FIELDS_OFF) @@ -186,8 +212,8 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_IGNORE_REALN_FIELDS_OFF) Iocatalyst::BlockMesh bm; setBlockMeshSize(2, 2, 2); - bm.addTransientCellField("foo_1", 2); - bm.addTransientCellField("foo_2", 3); + bm.addTransientCellField("foo_1", 3); + bm.addTransientCellField("foo_2", 2); bm.addTransientCellField("foo_3", 4); @@ -209,12 +235,26 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_IGNORE_REALN_FIELDS_OFF) auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + checkEntityContainerZeroCopyFields(cat_reg.get_element_blocks()); + bool exo_foo_exists = exo_elemBlock->field_exists("foo"); bool cat_foo_exists = cat_elemBlock->field_exists("foo"); EXPECT_TRUE(exo_foo_exists); EXPECT_TRUE(cat_foo_exists); if(exo_foo_exists && cat_foo_exists) EXPECT_TRUE(exo_elemBlock->get_field("foo") == cat_elemBlock->get_field("foo")); + + //Check field data for equality + auto cat_field = cat_elemBlock->get_fieldref("foo"); + std::vector dcBuffer(cat_field.get_size()); + cat_elemBlock->get_field_data("foo", Data(dcBuffer), dcBuffer.size()); + + exo_reg.begin_state(1); + auto exo_field = exo_elemBlock->get_fieldref("foo"); + std::vector deBuffer(exo_field.get_size()); + exo_elemBlock->get_field_data("foo", Data(deBuffer), deBuffer.size()); + EXPECT_EQ(dcBuffer, deBuffer); + } TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_FIELD_SUFFIX_SEPARATOR) @@ -249,6 +289,8 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_FIELD_SUFFIX_SEPARATOR) auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + checkEntityContainerZeroCopyFields(cat_reg.get_element_blocks()); + bool exo_foo_x_exists = exo_elemBlock->field_exists("foo_x"); bool cat_foo_x_exists = cat_elemBlock->field_exists("foo_x"); EXPECT_TRUE(exo_foo_x_exists); @@ -262,6 +304,17 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_FIELD_SUFFIX_SEPARATOR) EXPECT_TRUE(cat_bar_exists); if(exo_bar_exists && cat_bar_exists) EXPECT_TRUE(exo_elemBlock->get_field("bar") == cat_elemBlock->get_field("bar")); + + //Check bar field data for equality + auto cat_field = cat_elemBlock->get_fieldref("bar"); + std::vector dcBuffer(cat_field.get_size()); + cat_elemBlock->get_field_data("bar", Data(dcBuffer), dcBuffer.size()); + + exo_reg.begin_state(1); + auto exo_field = exo_elemBlock->get_fieldref("bar"); + std::vector deBuffer(exo_field.get_size()); + exo_elemBlock->get_field_data("bar", Data(deBuffer), deBuffer.size()); + EXPECT_EQ(dcBuffer, deBuffer); } @@ -294,12 +347,25 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_FIELD_STRIP_TRAILING_UNDERSCORE) auto cat_elemBlock = cat_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); auto exo_elemBlock = exo_reg.get_element_block(bmSet.getUnstructuredBlockName(bm.getID())); + checkEntityContainerZeroCopyFields(cat_reg.get_element_blocks()); + bool exo_foo_exists = exo_elemBlock->field_exists("foo"); bool cat_foo_exists = cat_elemBlock->field_exists("foo"); EXPECT_TRUE(exo_foo_exists); EXPECT_TRUE(cat_foo_exists); if(exo_foo_exists && cat_foo_exists) EXPECT_TRUE(exo_elemBlock->get_field("foo") == cat_elemBlock->get_field("foo")); + + //Check field data for equality + auto cat_field = cat_elemBlock->get_fieldref("foo"); + std::vector dcBuffer(cat_field.get_size()); + cat_elemBlock->get_field_data("foo", Data(dcBuffer), dcBuffer.size()); + + exo_reg.begin_state(1); + auto exo_field = exo_elemBlock->get_fieldref("foo"); + std::vector deBuffer(exo_field.get_size()); + exo_elemBlock->get_field_data("foo", Data(deBuffer), deBuffer.size()); + EXPECT_EQ(dcBuffer, deBuffer); } @@ -325,6 +391,7 @@ TEST_F(Iocatalyst_DatabaseIOTest, Exodus_Prop_SURFACE_SPLIT_TYPE) Ioss::Region cat_reg(cat_d); Ioss::SideSetContainer cat_sideSets = cat_reg.get_sidesets(); + checkEntityContainerZeroCopyFields(cat_sideSets); EXPECT_TRUE(cat_sideSets.empty())<<"Cat sidesets not empty when different SURFACE_SPLIT_TYPE"; From d15a9f8fea28c54613d6258c8c51713eb0560f26 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Fri, 31 May 2024 08:45:53 -0600 Subject: [PATCH 13/59] EXODUS: Increment version for latest enhanced field changes --- packages/seacas/libraries/exodus/include/exodusII.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/seacas/libraries/exodus/include/exodusII.h b/packages/seacas/libraries/exodus/include/exodusII.h index 67165d25c8..dd545b5ec7 100644 --- a/packages/seacas/libraries/exodus/include/exodusII.h +++ b/packages/seacas/libraries/exodus/include/exodusII.h @@ -55,12 +55,12 @@ #endif /* EXODUS version number */ -#define EXODUS_VERSION "8.25" -#define EXODUS_VERSION_MAJOR 8 -#define EXODUS_VERSION_MINOR 25 -#define EXODUS_RELEASE_DATE "July 28, 2023" +#define EXODUS_VERSION "9.00" +#define EXODUS_VERSION_MAJOR 9 +#define EXODUS_VERSION_MINOR 0 +#define EXODUS_RELEASE_DATE "May 30, 2024" -#define EX_API_VERS 8.25f +#define EX_API_VERS 9.00f #define EX_API_VERS_NODOT (100 * EXODUS_VERSION_MAJOR + EXODUS_VERSION_MINOR) #define EX_VERS EX_API_VERS From adc1480efde6b6c9b70ed915b3e892c14898044b Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Fri, 31 May 2024 10:15:23 -0600 Subject: [PATCH 14/59] Address a few coverity warnings --- packages/seacas/libraries/exodus/src/ex_field_utils.c | 4 +++- packages/seacas/libraries/ioss/src/Ioss_Utils.C | 8 ++++---- .../libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/seacas/libraries/exodus/src/ex_field_utils.c b/packages/seacas/libraries/exodus/src/ex_field_utils.c index cda2985cef..f054e84935 100644 --- a/packages/seacas/libraries/exodus/src/ex_field_utils.c +++ b/packages/seacas/libraries/exodus/src/ex_field_utils.c @@ -79,7 +79,9 @@ const char *ex_component_field_name(ex_field *field, int component[EX_MAX_FIELD_ field_name[fnl] = field->component_separator[i]; field_name[fnl + 1] = '\0'; } - my_strlcat(field_name, suffices[i], EX_MAX_NAME); + if (suffices[i] != NULL) { + my_strlcat(field_name, suffices[i], EX_MAX_NAME); + } } return field_name; } diff --git a/packages/seacas/libraries/ioss/src/Ioss_Utils.C b/packages/seacas/libraries/ioss/src/Ioss_Utils.C index 71f6d5fd16..4ea1f2f3b9 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Utils.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Utils.C @@ -498,8 +498,8 @@ namespace { // not valid for this grouping entity (truth_table entry == 0). assert(index < names.size() && !names[index].empty() && (truth_table == nullptr || truth_table[index] == 1)); - auto name = names[index]; - auto name_length = name.length(); + const auto &name = names[index]; + auto name_length = name.length(); // Split the name up into tokens separated by the // 'suffix_separator'. Note that the basename itself could @@ -560,8 +560,8 @@ namespace { // suffices have a basename that match other names with only a // single suffix lc_cam_x, lc_cam_y, lc_sfarea. for (size_t id = index + 1; id < names.size(); id++) { - auto tst_name = names[id]; - auto subtokens = field_tokenize(tst_name, suffix_separator); + const auto &tst_name = names[id]; + auto subtokens = field_tokenize(tst_name, suffix_separator); if ((truth_table == nullptr || truth_table[id] == 1) && // Defined on this entity Ioss::Utils::str_equal(name.substr(0, bn_len), tst_name.substr(0, bn_len)) && // base portion must match diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index c243fa44dd..982254fc49 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -2156,7 +2156,7 @@ namespace Ioex { ex_initialize_basis_struct(&exo_basis, 1, 1); Ioss::Utils::copy_string(exo_basis.name, basis->name(), EX_MAX_NAME); for (int i = 0; i < basis->component_count(); i++) { - auto component = basis->get_basis_component(i + 1); + const auto &component = basis->get_basis_component(i + 1); exo_basis.subc_dim[i] = component.subc_dim; exo_basis.subc_ordinal[i] = component.subc_ordinal; exo_basis.subc_dof_ordinal[i] = component.subc_dof_ordinal; From 73f9874d841ad7ffdb5e5235c7762676a83e1a9e Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Fri, 31 May 2024 12:51:38 -0600 Subject: [PATCH 15/59] IOSS: Add omit_sets option to io_shell --- .../libraries/ioss/src/Ioss_CopyDatabase.C | 73 ++++++++++++------- .../libraries/ioss/src/Ioss_MeshCopyOptions.h | 13 ++-- .../seacas/libraries/ioss/src/main/io_shell.C | 8 +- .../libraries/ioss/src/main/shell_interface.C | 16 ++++ .../libraries/ioss/src/main/shell_interface.h | 5 +- 5 files changed, 78 insertions(+), 37 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C index 7b7473c5e0..bf6d2092a2 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C +++ b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C @@ -1034,27 +1034,36 @@ namespace { { const auto &fss = region.get_sidesets(); for (const auto &ss : fss) { - const std::string &name = ss->name(); - if (options.debug && rank == 0) { - fmt::print(Ioss::DebugOut(), "{}, ", name); - } - auto *surf = new Ioss::SideSet(*ss); - output_region.add(surf); - - // Fix up the optional 'owner_block' in copied SideBlocks... - const auto &fbs = ss->get_side_blocks(); - for (const auto &ifb : fbs) { - if (ifb->parent_block() != nullptr) { - const auto &fb_name = ifb->parent_block()->name(); - auto *parent = dynamic_cast( - output_region.get_entity(fb_name, Ioss::ELEMENTBLOCK)); - if (parent == nullptr) { - parent = dynamic_cast( - output_region.get_entity(fb_name, Ioss::STRUCTUREDBLOCK)); - } + const std::string &name = ss->name(); + auto lc_name = Ioss::Utils::lowercase(name); + if (std::find(options.omitted_sets.begin(), options.omitted_sets.end(), lc_name) == + options.omitted_sets.end()) { + if (options.debug && rank == 0) { + fmt::print(Ioss::DebugOut(), "{}, ", name); + } + auto *surf = new Ioss::SideSet(*ss); + output_region.add(surf); + + // Fix up the optional 'owner_block' in copied SideBlocks... + const auto &fbs = ss->get_side_blocks(); + for (const auto &ifb : fbs) { + if (ifb->parent_block() != nullptr) { + const auto &fb_name = ifb->parent_block()->name(); + auto *parent = dynamic_cast( + output_region.get_entity(fb_name, Ioss::ELEMENTBLOCK)); + if (parent == nullptr) { + parent = dynamic_cast( + output_region.get_entity(fb_name, Ioss::STRUCTUREDBLOCK)); + } - auto *ofb = surf->get_side_block(ifb->name()); - ofb->set_parent_block(parent); + auto *ofb = surf->get_side_block(ifb->name()); + ofb->set_parent_block(parent); + } + } + } + else { + if (options.debug && rank == 0) { + fmt::print(Ioss::DebugOut(), "{}(omitted), ", name); } } } @@ -1075,16 +1084,24 @@ namespace { if (!sets.empty()) { size_t total_entities = 0; for (const auto &set : sets) { - const std::string &name = set->name(); - if (options.debug && rank == 0) { - fmt::print(Ioss::DebugOut(), "{}, ", name); + const std::string &name = set->name(); + auto lc_name = Ioss::Utils::lowercase(name); + if (std::find(options.omitted_sets.begin(), options.omitted_sets.end(), lc_name) == + options.omitted_sets.end()) { + if (options.debug && rank == 0) { + fmt::print(Ioss::DebugOut(), "{}, ", name); + } + size_t count = set->entity_count(); + total_entities += count; + auto *o_set = new T(*set); + output_region.add(o_set); + } + else { + if (options.debug && rank == 0) { + fmt::print(Ioss::DebugOut(), "{}(omitted), ", name); + } } - size_t count = set->entity_count(); - total_entities += count; - auto *o_set = new T(*set); - output_region.add(o_set); } - if (options.output_summary && rank == 0) { fmt::print(Ioss::DebugOut(), " Number of {:20s} = {:14}", (*sets.begin())->type_string() + "s", fmt::group_digits(sets.size())); diff --git a/packages/seacas/libraries/ioss/src/Ioss_MeshCopyOptions.h b/packages/seacas/libraries/ioss/src/Ioss_MeshCopyOptions.h index 61e28d4d0f..8130a4aedb 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_MeshCopyOptions.h +++ b/packages/seacas/libraries/ioss/src/Ioss_MeshCopyOptions.h @@ -13,12 +13,13 @@ namespace Ioss { struct IOSS_EXPORT MeshCopyOptions { - std::vector selected_times{}; - double minimum_time{0.0}; - double maximum_time{0.0}; - double delay{0.0}; - double time_scale{1.0}; - double time_offset{0.0}; + std::vector selected_times{}; + std::vector omitted_sets{}; + double minimum_time{0.0}; + double maximum_time{0.0}; + double delay{0.0}; + double time_scale{1.0}; + double time_offset{0.0}; double rel_tolerance{}; double abs_tolerance{}; diff --git a/packages/seacas/libraries/ioss/src/main/io_shell.C b/packages/seacas/libraries/ioss/src/main/io_shell.C index b4f7f5e76e..1673575195 100644 --- a/packages/seacas/libraries/ioss/src/main/io_shell.C +++ b/packages/seacas/libraries/ioss/src/main/io_shell.C @@ -42,7 +42,7 @@ namespace { std::string codename; - std::string version = "6.7 (2024/05/01)"; + std::string version = "6.8 (2024/05/31)"; bool mem_stats = false; @@ -74,6 +74,12 @@ namespace { options.boundary_sideset = interFace.boundary_sideset; options.ignore_qa_info = interFace.ignore_qa_info; options.omitted_blocks = !interFace.omitted_blocks.empty(); + + options.omitted_sets = interFace.omitted_sets; + Ioss::sort(options.omitted_sets); + for (auto &name : options.omitted_sets) { + name = Ioss::Utils::lowercase(name); + } return options; } diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.C b/packages/seacas/libraries/ioss/src/main/shell_interface.C index b9a9284621..309dc6a885 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.C +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.C @@ -312,6 +312,12 @@ void IOShell::Interface::enroll_options() "retained in the output.", nullptr); + options_.enroll("omit_sets", Ioss::GetLongOption::MandatoryValue, + "comma-separated list of nodeset/edgeset/faceset/elemset/sideset names that " + "should NOT be transferred to " + "output database", + nullptr); + options_.enroll("boundary_sideset", Ioss::GetLongOption::NoValue, "Output a sideset for all boundary faces of the model", nullptr); @@ -623,6 +629,16 @@ bool IOShell::Interface::parse_options(int argc, char **argv, int my_processor) } } + { + const char *temp = options_.retrieve("omit_sets"); + if (temp != nullptr) { + auto omit_str = Ioss::tokenize(std::string(temp), ","); + for (const auto &str : omit_str) { + omitted_sets.push_back(str); + } + } + } + { const char *temp = options_.retrieve("surface_split_scheme"); if (temp != nullptr) { diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.h b/packages/seacas/libraries/ioss/src/main/shell_interface.h index 09d61a4f11..36c77f38e1 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.h +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.h @@ -59,9 +59,10 @@ namespace IOShell { //! If non-empty, then it is a list of times that should be transferred to the output file. std::vector selected_times{}; - //! If non-empty, then it is a list of element blocks that should be omitted from the output - //! file + //! If non-empty, then it is a list of element blocks, nodesets, + //! sidesets that should be omitted from the output file std::vector omitted_blocks{}; + std::vector omitted_sets{}; //! If non-zero, then put `split_times` timesteps in each file. Then close file and start new //! file. From c0ac9952ec341f4cab9fd71c8512e244adbf4c22 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Fri, 31 May 2024 16:13:25 -0600 Subject: [PATCH 16/59] IOSS: io_shell help formatting cleanup --- packages/seacas/libraries/ioss/src/main/shell_interface.C | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.C b/packages/seacas/libraries/ioss/src/main/shell_interface.C index 309dc6a885..ce293550ea 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.C +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.C @@ -319,7 +319,7 @@ void IOShell::Interface::enroll_options() nullptr); options_.enroll("boundary_sideset", Ioss::GetLongOption::NoValue, - "Output a sideset for all boundary faces of the model", nullptr); + "Output a sideset for all boundary faces of the model", nullptr, nullptr, true); options_.enroll( "delay", Ioss::GetLongOption::MandatoryValue, @@ -331,12 +331,12 @@ void IOShell::Interface::enroll_options() "Data type used internally to store field data\n" "\t\tOptions are: POINTER, STD_VECTOR, KOKKOS_VIEW_1D, KOKKOS_VIEW_2D, " "KOKKOS_VIEW_2D_LAYOUTRIGHT_HOSTSPACE", - "POINTER", nullptr, true); + "POINTER"); #else options_.enroll("data_storage", Ioss::GetLongOption::MandatoryValue, "Data type used internally to store field data\n" "\t\tOptions are: POINTER, STD_VECTOR", - "POINTER", nullptr, true); + "POINTER"); #endif options_.enroll("debug", Ioss::GetLongOption::NoValue, "turn on debugging output", nullptr); From 59cdf257d4ee4355f609a206be816f566084eca8 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Fri, 31 May 2024 16:34:23 -0600 Subject: [PATCH 17/59] EXODUS_FOR: Fix potential integer overflow --- .../seacas/libraries/exodus_for/src/exo_jack.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/seacas/libraries/exodus_for/src/exo_jack.c b/packages/seacas/libraries/exodus_for/src/exo_jack.c index af02b4700e..c0fea886a7 100644 --- a/packages/seacas/libraries/exodus_for/src/exo_jack.c +++ b/packages/seacas/libraries/exodus_for/src/exo_jack.c @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2023 National Technology & Engineering Solutions + * Copyright(C) 1999-2024 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. * @@ -2039,7 +2039,7 @@ void F2C(exgvan, EXGVAN)(int *idexo, char *var_type, int *num_vars, char *var_na #if Build64 void F2C(expvtt, EXPVTT)(int *idexo, int *num_entity, int *num_var, int64_t *var_tab, int *ierr) { - int *var_tab4 = i8i4(*num_entity * *num_var, var_tab); + int *var_tab4 = i8i4((int64_t)(*num_entity) * (int64_t)(*num_var), var_tab); *ierr = ex_put_truth_table(*idexo, EX_ELEM_BLOCK, *num_entity, *num_var, var_tab4); free(var_tab4); } @@ -2057,7 +2057,7 @@ void F2C(expvtt, EXPVTT)(int *idexo, int *num_entity, int *num_var, int *var_tab #if Build64 void F2C(expnstt, EXPNSTT)(int *idexo, int *num_entity, int *num_var, int64_t *var_tab, int *ierr) { - int *var_tab4 = i8i4(*num_entity * *num_var, var_tab); + int *var_tab4 = i8i4((int64_t)(*num_entity) * (int64_t)(*num_var), var_tab); *ierr = ex_put_truth_table(*idexo, EX_NODE_SET, *num_entity, *num_var, var_tab4); free(var_tab4); } @@ -2075,7 +2075,7 @@ void F2C(expnstt, EXPNSTT)(int *idexo, int *num_entity, int *num_var, int *var_t #if Build64 void F2C(expsstt, EXPSSTT)(int *idexo, int *num_entity, int *num_var, int64_t *var_tab, int *ierr) { - int *var_tab4 = i8i4(*num_entity * *num_var, var_tab); + int *var_tab4 = i8i4((int64_t)(*num_entity) * (int64_t)(*num_var), var_tab); *ierr = ex_put_truth_table(*idexo, EX_SIDE_SET, *num_entity, *num_var, var_tab4); free(var_tab4); } @@ -2095,7 +2095,7 @@ void F2C(exgvtt, EXGVTT)(int *idexo, int *num_entity, int *num_var, int64_t *var { int *var_tab4 = malloc(*num_entity * *num_var * sizeof(int)); *ierr = ex_get_truth_table(*idexo, EX_ELEM_BLOCK, *num_entity, *num_var, var_tab4); - i4i8(*num_entity * *num_var, var_tab4, var_tab); + i4i8((int64_t)(*num_entity) * (int64_t)(*num_var), var_tab4, var_tab); free(var_tab4); #else void F2C(exgvtt, EXGVTT)(int *idexo, int *num_entity, int *num_var, int *var_tab, int *ierr) @@ -2113,7 +2113,7 @@ void F2C(exgnstt, EXGNSTT)(int *idexo, int *num_entity, int *num_var, int64_t *v { int *var_tab4 = malloc(*num_entity * *num_var * sizeof(int)); *ierr = ex_get_truth_table(*idexo, EX_NODE_SET, *num_entity, *num_var, var_tab4); - i4i8(*num_entity * *num_var, var_tab4, var_tab); + i4i8((int64_t)(*num_entity) * (int64_t)(*num_var), var_tab4, var_tab); free(var_tab4); } #else @@ -2132,7 +2132,7 @@ void F2C(exgsstt, EXGSSTT)(int *idexo, int *num_entity, int *num_var, int64_t *v { int *var_tab4 = malloc(*num_entity * *num_var * sizeof(int)); *ierr = ex_get_truth_table(*idexo, EX_SIDE_SET, *num_entity, *num_var, var_tab4); - i4i8(*num_entity * *num_var, var_tab4, var_tab); + i4i8((int64_t)(*num_entity) * (int64_t)(*num_var), var_tab4, var_tab); free(var_tab4); } #else From 89dfefb199b8584cbba6b74fbd95fd10073b0504 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 3 Jun 2024 12:10:45 -0600 Subject: [PATCH 18/59] CI: Try to clean up hdf5 install; make some git clones faster --- install-tpl.sh | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/install-tpl.sh b/install-tpl.sh index f626ccf2da..f57a0502b8 100755 --- a/install-tpl.sh +++ b/install-tpl.sh @@ -418,22 +418,12 @@ then then echo "${txtgrn}+++ HDF5${txtrst}" hdf_suffix="" - if [ "${H5VERSION}" == "V18" ]; then - hdf_version="1.8.23" - hdf_base="1.8" - elif [ "${H5VERSION}" == "V110" ]; then - hdf_version="1.10.10" - hdf_base="1.10" + if [ "${H5VERSION}" == "V110" ]; then + hdf_version="hdf5-1_10_11" elif [ "${H5VERSION}" == "V112" ]; then - hdf_version="1.12.2" - hdf_base="1.12" - elif [ "${H5VERSION}" == "V113" ]; then - hdf_version="1.13.1" - hdf_base="1.13" + hdf_version="hdf5-1_12_3" elif [ "${H5VERSION}" == "V114" ]; then - hdf_version="1.14.3" - hdf_base="1.14" - hdf_suffix="" + hdf_version="hdf5_1.14.4.3" elif [ "${H5VERSION}" == "develop" ]; then hdf_version="develop" else @@ -449,21 +439,21 @@ then rm -rf hdf5-${hdf_version}${hdf_suffix} rm -f hdf5-${hdf_version}${hdf_suffix}.tar.bz2 if [ "${H5VERSION}" == "develop" ]; then - git clone https://github.com/HDFGroup/hdf5.git hdf5-develop + git clone --depth=1 https://github.com/HDFGroup/hdf5.git hdf5-develop else - wget --no-check-certificate https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-${hdf_base}/hdf5-${hdf_version}/src/hdf5-${hdf_version}${hdf_suffix}.tar.bz2 + wget --no-check-certificate https://github.com/HDFGroup/hdf5/archive/refs/tags/${hdf_version}.tar.gz fi if [ "${H5VERSION}" != "develop" ] then - tar -jxf hdf5-${hdf_version}${hdf_suffix}.tar.bz2 - rm -f hdf5-${hdf_version}${hdf_suffix}.tar.bz2 + tar -zxf ${hdf_version}.tar.gz + rm -f ${hdf_version}.tar.gz fi fi if [ "$BUILD" == "YES" ] then echo "${txtgrn}+++ Configuring, Building, and Installing...${txtrst}" - cd hdf5-${hdf_version}${hdf_suffix} || exit + cd hdf5-${hdf_version} || exit rm -rf build mkdir build cd build || exit @@ -695,7 +685,7 @@ then then echo "${txtgrn}+++ Downloading...${txtrst}" rm -rf parmetis - git clone https://github.com/gsjaardema/parmetis + git clone --depth=1 https://github.com/gsjaardema/parmetis fi if [ "$BUILD" == "YES" ] @@ -869,7 +859,7 @@ then then echo "${txtgrn}+++ Downloading...${txtrst}" rm -rf ADIOS2 - git clone https://github.com/ornladios/ADIOS2.git + git clone --depth=1 https://github.com/ornladios/ADIOS2.git fi if [ "$BUILD" == "YES" ] @@ -1104,7 +1094,7 @@ then then echo "${txtgrn}+++ Downloading...${txtrst}" rm -rf faodel* - git clone https://github.com/sandialabs/faodel.git + git clone --depth=1 https://github.com/sandialabs/faodel.git fi if [ "$BUILD" == "YES" ] From dbb642f616ed7d8de2a68f6122200ada05cbf6d0 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 3 Jun 2024 18:29:47 -0600 Subject: [PATCH 19/59] IOSS: Print filename on the open/create/close timing output --- .../seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C | 2 +- packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C | 3 ++- .../libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index 982254fc49..9a0cc0949f 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -422,7 +422,7 @@ namespace Ioex { double t_end = Ioss::Utils::timer(); double duration = util().global_minmax(t_end - t_begin, Ioss::ParallelUtils::DO_MAX); if (myProcessor == 0) { - fmt::print(Ioss::DebugOut(), "File Close Time = {}\n", duration); + fmt::print(Ioss::DebugOut(), "File Close Time = {} ({})\n", duration, get_filename()); } } } diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C index 0dea422a17..c9dee42895 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C @@ -281,7 +281,8 @@ namespace Ioex { if (do_timer) { double t_end = Ioss::Utils::timer(); double duration = t_end - t_begin; - fmt::print(Ioss::DebugOut(), "Input File Open Time = {}\n", duration); + fmt::print(Ioss::DebugOut(), "Input File Open Time = {} ({})\n", duration, + decoded_filename()); } bool is_ok = check_valid_file_ptr(write_message, error_msg, bad_count, abort_if_error); diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C index ec617e93d6..f819428074 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C @@ -539,7 +539,7 @@ namespace Ioex { double t_end = Ioss::Utils::timer(); double duration = util().global_minmax(t_end - t_begin, Ioss::ParallelUtils::DO_MAX); if (myProcessor == 0) { - fmt::print(Ioss::DebugOut(), "File Open Time = {}\n", duration); + fmt::print(Ioss::DebugOut(), "File Open Time = {} ({})\n", duration, filename); } } @@ -671,7 +671,7 @@ namespace Ioex { double duration = util().global_minmax(t_end - t_begin, Ioss::ParallelUtils::DO_MAX); std::string open_create = fileExists ? "Open" : "Create"; if (myProcessor == 0) { - fmt::print(Ioss::DebugOut(), "File {} Time = {}\n", open_create, duration); + fmt::print(Ioss::DebugOut(), "File {} Time = {} ({})\n", open_create, duration, filename); } } From 778a564de0a0b3c89336108c110531c7fdd5432e Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 4 Jun 2024 07:53:12 -0600 Subject: [PATCH 20/59] IOSS: Use c++14 deprecated syntax --- .../seacas/libraries/ioss/src/Ioss_CompositeVariableType.h | 4 ++-- packages/seacas/libraries/ioss/src/Ioss_VariableType.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h index 904fef550b..53f0bb9b64 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h @@ -32,11 +32,11 @@ namespace Ioss { IOSS_NODISCARD int get_num_copies() const { return copies_; } // Kept for backward compatibility... - IOSS_NODISCARD __attribute__((__deprecated__)) const VariableType *getBaseType() const + IOSS_NODISCARD [[deprecated("Use get_base_type")]] const VariableType *getBaseType() const { return baseType; } - IOSS_NODISCARD __attribute__((__deprecated__)) int getNumCopies() const { return copies_; } + IOSS_NODISCARD [[deprecated("Use get_num_copies")]] int getNumCopies() const { return copies_; } private: const VariableType *baseType; diff --git a/packages/seacas/libraries/ioss/src/Ioss_VariableType.h b/packages/seacas/libraries/ioss/src/Ioss_VariableType.h index c34670f23b..4c19e0aff9 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_VariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_VariableType.h @@ -96,7 +96,7 @@ namespace Ioss { const Ioss::NameList &suffices); // Backward compatibility... - __attribute__((__deprecated__)) static bool + [[deprecated("Use create_named_suffix_type")]] static bool create_named_suffix_field_type(const std::string &type_name, const Ioss::NameList &suffices) { return create_named_suffix_type(type_name, suffices); From 5206a48ee8c1971d29f41ff471d1a32b635763f7 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 4 Jun 2024 15:29:16 -0600 Subject: [PATCH 21/59] IOSS: Better output of open/close/create times --- .../libraries/ioss/src/exodus/Ioex_DatabaseIO.C | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C index c9dee42895..9a38458021 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C @@ -272,17 +272,19 @@ namespace Ioex { bool do_timer = false; Ioss::Utils::check_set_bool_property(properties, "IOSS_TIME_FILE_OPEN_CLOSE", do_timer); - double t_begin = (do_timer ? Ioss::Utils::timer() : 0); + double t_begin = ((do_timer && isParallel) ? Ioss::Utils::timer() : 0); int app_opt_val = ex_opts(EX_VERBOSE); m_exodusFilePtr = ex_open(decoded_filename().c_str(), EX_READ | mode, &cpu_word_size, &io_word_size, &version); - if (do_timer) { + if (do_timer && isParallel) { double t_end = Ioss::Utils::timer(); - double duration = t_end - t_begin; - fmt::print(Ioss::DebugOut(), "Input File Open Time = {} ({})\n", duration, - decoded_filename()); + double duration = util().global_minmax(t_end - t_begin, Ioss::ParallelUtils::DO_MAX); + if (myProcessor == 0) { + fmt::print(Ioss::DebugOut(), "Input File Open Time = {} ({})\n", duration, + decoded_filename()); + } } bool is_ok = check_valid_file_ptr(write_message, error_msg, bad_count, abort_if_error); From c0b290dcbf2e9df717d2bd1221279f9c69afda6b Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Thu, 6 Jun 2024 16:10:38 -0600 Subject: [PATCH 22/59] SLICE: Use default data_stroage_type --- packages/seacas/applications/slice/Slice.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/seacas/applications/slice/Slice.C b/packages/seacas/applications/slice/Slice.C index cbd1961c7b..39d0257368 100644 --- a/packages/seacas/applications/slice/Slice.C +++ b/packages/seacas/applications/slice/Slice.C @@ -1428,7 +1428,7 @@ namespace { Ioss::MeshCopyOptions options{}; options.ints_64_bit = sizeof(INT) == 64; options.delete_timesteps = true; - options.data_storage_type = 2; + options.data_storage_type = 1; options.verbose = true; // Copy mesh portion of input region to the output region From 1026965b088a42ff28e2b2b3f0c6b5f09cd5e542 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 10 Jun 2024 10:27:16 -0600 Subject: [PATCH 23/59] EXPLORE: Fix behavior after bad parse warning --- packages/seacas/applications/explore/exp_qainfo.blk | 5 +++-- packages/seacas/applications/explore/exp_rixid.f | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/seacas/applications/explore/exp_qainfo.blk b/packages/seacas/applications/explore/exp_qainfo.blk index df09d169cd..b43b841aa3 100644 --- a/packages/seacas/applications/explore/exp_qainfo.blk +++ b/packages/seacas/applications/explore/exp_qainfo.blk @@ -8,8 +8,8 @@ C See packages/seacas/LICENSE for details QAINFO(2) = ' ' QAINFO(3) = ' ' - QAINFO(2)(:8) = '20240418' - QAINFO(3)(:8) = ' 4.02' + QAINFO(2)(:8) = '20240610' + QAINFO(3)(:8) = ' 4.03' c..Dynamic dimensioning of block names+other changes c..compress output of distribution factors @@ -77,3 +77,4 @@ c..Add select nodes sset {id}... c..Fix memory overrun in check routines c..Call MDFREE() c..Refactor element select to allow add +c..Fix behavior after bad parse warning diff --git a/packages/seacas/applications/explore/exp_rixid.f b/packages/seacas/applications/explore/exp_rixid.f index c58e7e59a8..12e5a3e624 100644 --- a/packages/seacas/applications/explore/exp_rixid.f +++ b/packages/seacas/applications/explore/exp_rixid.f @@ -65,13 +65,13 @@ SUBROUTINE RIXID (INLINE, IFLD, INTYP, CFIELD, IFIELD, IF (FFMATC (IFLD, INTYP, CFIELD, 'ADD', 3)) THEN CALL FFADDC ('ADD', INLINE) ELSE + NUMSEL = 0 IF (.NOT. FFNUMB (IFLD, INTYP)) THEN ERRMSG = & 'Expected "OFF" or "ADD" or ' // SELMSG // ' range' CALL PRTERR ('CMDERR', ERRMSG(:LENSTR(ERRMSG))) GOTO 130 END IF - NUMSEL = 0 END IF 110 CONTINUE From 657b46a806966e022a71abd4debfa8bedfc0670c Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 10 Jun 2024 10:38:34 -0600 Subject: [PATCH 24/59] EXPLORE: Better warning/info message on SELECT --- packages/seacas/applications/explore/exp_comand.f | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/seacas/applications/explore/exp_comand.f b/packages/seacas/applications/explore/exp_comand.f index 73479c3b08..92a73869d2 100644 --- a/packages/seacas/applications/explore/exp_comand.f +++ b/packages/seacas/applications/explore/exp_comand.f @@ -319,7 +319,8 @@ SUBROUTINE COMAND (A, IA, EXODUS, DBNAME, QAREC, INFO, IF (LISTYP .EQ. ' ') THEN CALL ABRSTR (LISTYP, WORD, SELTBL) IF (LISTYP .NE. ' ') THEN - CALL PRTERR ('CMDREQ', 'Please use the SELECT command') + CALL PRTERR ('CMDREQ', 'Please use the SELECT ' + $ // LISTYP(:LENSTR(LISTYP)) // ' command') VERB = 'SELECT' ELSE LISTYP = WORD From ea75dd932d88a80dcab41c6bfda0b549b85c3012 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Thu, 6 Jun 2024 07:44:17 -0600 Subject: [PATCH 25/59] EXPLORE: Fix extra double quote in output [ci skip] --- packages/seacas/applications/explore/exp_comand.f | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/seacas/applications/explore/exp_comand.f b/packages/seacas/applications/explore/exp_comand.f index 92a73869d2..d9ce8cb288 100644 --- a/packages/seacas/applications/explore/exp_comand.f +++ b/packages/seacas/applications/explore/exp_comand.f @@ -251,7 +251,7 @@ SUBROUTINE COMAND (A, IA, EXODUS, DBNAME, QAREC, INFO, WRITE (*, *) CALL PRTERR ('CMDREQ', - & 'Use "precision low|normal|high|#" to control" output precision') + & 'Use "precision low|normal|high|#" to control output precision') if (domape .and. domapn) then call PRTERR('CMDREQ', From 94a3d956e8c78eb8fb0a75496fecd28b9558a865 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 11 Jun 2024 08:07:38 -0600 Subject: [PATCH 26/59] SLICE: Fix order of file close and mpi_finalize --- packages/seacas/applications/slice/Slice.C | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/seacas/applications/slice/Slice.C b/packages/seacas/applications/slice/Slice.C index 39d0257368..9cb38e7e79 100644 --- a/packages/seacas/applications/slice/Slice.C +++ b/packages/seacas/applications/slice/Slice.C @@ -387,12 +387,11 @@ int main(int argc, char *argv[]) dbi->set_surface_split_type(Ioss::SPLIT_BY_DONT_SPLIT); dbi->set_field_separator(0); - // NOTE: 'region' owns 'db' pointer at this time... - Ioss::Region region(dbi, "region_1"); - - region.output_summary(std::cerr, true); - try { + // NOTE: 'region' owns 'db' pointer at this time... + Ioss::Region region(dbi, "region_1"); + region.output_summary(std::cerr, true); + if (dbi->int_byte_size_api() == 4) { progress("4-byte slice"); slice(region, nem_file, interFace, 1); From d96320629eb121713131d73937db6d5b9e79a2cb Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 11 Jun 2024 16:46:04 -0600 Subject: [PATCH 27/59] Line decomp parallel (#459) * IOSS: refactor to reduce strings; give flexibility and efficiency * IOSS: Start of support for parallel line decomp * IOSS: Explicit template instantiation fix * IOSS: Allow passing filename down into line_decomp * IOSS: initial line_decompose(); compile/link does not run * IOSS: Refactor to try to eliminate duplicate code * IOSS: Add some missing includes * SLICE: Fix data_storage type * Share decomp code between slice and ioss line decomp * IOSS: Fix serial build * Pull element centroid into common utils class * Templative line_decompose; pass correct vector * Handle specified in guided_decompose * Remove unused function * elementToProc does not need 64-bit range * EXPLORE: Fix behavior after bad parse warning * EXPLORE: Better warning/info message on SELECT * Minor rearrange include files * IOSS: See if this fixes/affects msys2 build * IOSS: Add some logging/hwm code to line decomp * IOSS: Another try to see how affects msys2 build * IOSS: Better hwm logging output * Unify Slice and DecompositionUtils zoltan_decompose * IOSS: Fix msys2 build * clang-format run * SLICE: Version should be updated for latest chagnes * IOSS: Reduce storage potentially; fix zoltan free call * SLICE: Fix order of file close and mpi_finalize * IOSS: Enable decomposition statistics for line decomp * IOSS: io_shell - add_processor_id_field works for exodus also * IOSS: compose output will add a proc_id map to output * IOSS: thread-safe output_processor_id_map * CI: safer variable naming --------- Co-authored-by: Greg Sjaardema --- cmake-config | 10 +- .../seacas/applications/slice/SL_Decompose.C | 495 +-------------- .../seacas/applications/slice/SL_Decompose.h | 16 +- .../seacas/applications/slice/SL_Version.h | 4 +- packages/seacas/applications/slice/Slice.C | 13 +- .../libraries/ioss/src/Ioss_ChainGenerator.C | 58 +- .../libraries/ioss/src/Ioss_Decomposition.C | 26 +- .../libraries/ioss/src/Ioss_Decomposition.h | 5 +- .../ioss/src/Ioss_DecompositionUtils.C | 581 ++++++++++++++++++ .../ioss/src/Ioss_DecompositionUtils.h | 48 ++ .../libraries/ioss/src/Ioss_FaceGenerator.C | 21 +- .../libraries/ioss/src/Ioss_FaceGenerator.h | 10 +- .../seacas/libraries/ioss/src/Ioss_Property.h | 3 +- .../ioss/src/exodus/Ioex_DatabaseIO.C | 4 +- .../ioss/src/exodus/Ioex_DecompositionData.C | 86 ++- .../ioss/src/exodus/Ioex_DecompositionData.h | 14 +- .../ioss/src/exodus/Ioex_ParallelDatabaseIO.C | 31 +- .../ioss/src/exodus/Ioex_ParallelDatabaseIO.h | 2 + .../libraries/ioss/src/main/shell_interface.C | 8 +- 19 files changed, 853 insertions(+), 582 deletions(-) create mode 100644 packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C create mode 100644 packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.h diff --git a/cmake-config b/cmake-config index b786fc8800..1b9662c3ed 100755 --- a/cmake-config +++ b/cmake-config @@ -442,9 +442,9 @@ if [ "$SANITIZER" != "NO" ] ; then #sanitizer=dataflow #: DataFlowSanitizer, a general data flow analysis. #sanitizer=cfi #: control flow integrity checks. Requires -flto. #sanitizer=safe-stack #: safe stack protection against stack-based memory corruption errors. -SANITIZE="-fsanitize=${SANITIZER} -fno-omit-frame-pointer -fPIC" +OPT_SANITIZE="-fsanitize=${SANITIZER} -fno-omit-frame-pointer -fPIC" if [ "$SANITIZER" == "integer" ] ; then - SANITIZE="$SANITIZE -fno-sanitize=unsigned-integer-overflow" + OPT_SANITIZE="$OPT_SANITIZE -fno-sanitize=unsigned-integer-overflow" fi fi @@ -488,9 +488,9 @@ cmake -G "${GENERATOR}" \ -D CMAKE_CXX_COMPILER:FILEPATH=${CXX} \ -D CMAKE_C_COMPILER:FILEPATH=${CC} \ -D CMAKE_Fortran_COMPILER:FILEPATH=${FC} \ --D CMAKE_CXX_FLAGS="${CXXFLAGS} ${CXX_WARNING_FLAGS} ${SANITIZE}" \ --D CMAKE_C_FLAGS="${CFLAGS} ${C_WARNING_FLAGS} ${SANITIZE}" \ --D CMAKE_Fortran_FLAGS="${FFLAGS} ${F77_WARNING_FLAGS} ${SANITIZE}" \ +-D CMAKE_CXX_FLAGS="${CXXFLAGS} ${CXX_WARNING_FLAGS} ${OPT_SANITIZE}" \ +-D CMAKE_C_FLAGS="${CFLAGS} ${C_WARNING_FLAGS} ${OPT_SANITIZE}" \ +-D CMAKE_Fortran_FLAGS="${FFLAGS} ${F77_WARNING_FLAGS} ${OPT_SANITIZE}" \ -D Seacas_ENABLE_STRONG_C_COMPILE_WARNINGS=${EXTRA_WARNINGS} \ -D Seacas_ENABLE_STRONG_CXX_COMPILE_WARNINGS=${EXTRA_WARNINGS} \ -D CMAKE_INSTALL_RPATH:PATH=${INSTALL_PATH}/lib \ diff --git a/packages/seacas/applications/slice/SL_Decompose.C b/packages/seacas/applications/slice/SL_Decompose.C index cb27614147..a93ef45866 100644 --- a/packages/seacas/applications/slice/SL_Decompose.C +++ b/packages/seacas/applications/slice/SL_Decompose.C @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -35,11 +36,6 @@ using idx_t = int; #endif -#if USE_ZOLTAN -#include // for Zoltan_Initialize -#include // for Zoltan -#endif - extern int debug_level; extern double seacas_timer(); extern void progress(const std::string &output); @@ -126,248 +122,6 @@ namespace { [](char a, char b) { return std::tolower(a) == std::tolower(b); }); } -#if USE_ZOLTAN - template - std::tuple, std::vector, std::vector> - get_element_centroid(const Ioss::Region ®ion, IOSS_MAYBE_UNUSED INT dummy) - { - size_t element_count = region.get_property("element_count").get_int(); - - // The zoltan methods supported in slice are all geometry based - // and use the element centroid. - std::vector x(element_count); - std::vector y(element_count); - std::vector z(element_count); - - const auto *nb = region.get_node_blocks()[0]; - std::vector coor; - nb->get_field_data("mesh_model_coordinates", coor); - - const auto &blocks = region.get_element_blocks(); - size_t el = 0; - for (auto &eb : blocks) { - std::vector connectivity; - eb->get_field_data("connectivity_raw", connectivity); - size_t blk_element_count = eb->entity_count(); - size_t blk_element_nodes = eb->topology()->number_nodes(); - - for (size_t j = 0; j < blk_element_count; j++) { - for (size_t k = 0; k < blk_element_nodes; k++) { - auto node = connectivity[j * blk_element_nodes + k] - 1; - x[el] += coor[node * 3 + 0]; - y[el] += coor[node * 3 + 1]; - z[el] += coor[node * 3 + 2]; - } - x[el] /= blk_element_nodes; - y[el] /= blk_element_nodes; - z[el] /= blk_element_nodes; - el++; - } - } - return {x, y, z}; - } - /*****************************************************************************/ - /***** Global data structure used by Zoltan callbacks. *****/ - /***** Could implement Zoltan callbacks without global data structure, *****/ - /***** but using the global data structure makes implementation quick. *****/ - struct - { - size_t ndot; /* Length of x, y, z, and part (== # of elements) */ - int *vwgt; /* vertex weights */ - double *x; /* x-coordinates */ - double *y; /* y-coordinates */ - double *z; /* z-coordinates */ - } Zoltan_Data; - - /*****************************************************************************/ - /***** ZOLTAN CALLBACK FUNCTIONS *****/ - int zoltan_num_dim(void * /*data*/, int *ierr) - { - /* Return dimensionality of coordinate data. - * Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign. - */ - *ierr = ZOLTAN_OK; - if (Zoltan_Data.z != nullptr) { - return 3; - } - if (Zoltan_Data.y != nullptr) { - return 2; - } - return 1; - } - - int zoltan_num_obj(void * /*data*/, int *ierr) - { - /* Return number of objects. - * Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign. - */ - *ierr = ZOLTAN_OK; - return Zoltan_Data.ndot; - } - - void zoltan_obj_list(void * /*data*/, int /*ngid_ent*/, int /*nlid_ent*/, ZOLTAN_ID_PTR gids, - ZOLTAN_ID_PTR /*lids*/, int wdim, float *wgts, int *ierr) - { - /* Return list of object IDs. - * Return only global IDs; don't need local IDs since running in serial. - * gids are array indices for coordinate and vwgts arrays. - * Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign. - */ - std::iota(gids, gids + Zoltan_Data.ndot, 0); - if (wdim != 0) { - for (size_t i = 0; i < Zoltan_Data.ndot; i++) { - wgts[i] = static_cast(Zoltan_Data.vwgt[i]); - } - } - - *ierr = ZOLTAN_OK; - } - - void zoltan_geom(void * /*data*/, int /*ngid_ent*/, int /*nlid_ent*/, int nobj, - const ZOLTAN_ID_PTR gids, ZOLTAN_ID_PTR /*lids*/, int ndim, double *geom, - int *ierr) - { - /* Return coordinates for objects. - * gids are array indices for coordinate arrays. - * Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign. - */ - - for (size_t i = 0; i < static_cast(nobj); i++) { - size_t j = gids[i]; - geom[i * ndim] = Zoltan_Data.x[j]; - if (ndim > 1) { - geom[i * ndim + 1] = Zoltan_Data.y[j]; - } - if (ndim > 2) { - geom[i * ndim + 2] = Zoltan_Data.z[j]; - } - } - - *ierr = ZOLTAN_OK; - } - - template - void decompose_zoltan(const Ioss::Region ®ion, int ranks, SystemInterface &interFace, - std::vector &elem_to_proc, const std::vector &weights, - IOSS_MAYBE_UNUSED INT dummy) - { - if (ranks == 1) { - return; - } - - size_t element_count = region.get_property("element_count").get_int(); - if (element_count != static_cast(static_cast(element_count))) { - fmt::print(stderr, "ERROR: Cannot have a mesh with more than 2.1 Billion elements in a " - "Zoltan decomposition.\n"); - exit(EXIT_FAILURE); - } - - auto [x, y, z] = get_element_centroid(region, dummy); - - // Copy mesh data and pointers into structure accessible from callback fns. - Zoltan_Data.ndot = element_count; - Zoltan_Data.vwgt = const_cast(Data(weights)); - - if (interFace.ignore_x_ && interFace.ignore_y_) { - Zoltan_Data.x = Data(z); - } - else if (interFace.ignore_x_ && interFace.ignore_z_) { - Zoltan_Data.x = Data(y); - } - else if (interFace.ignore_y_ && interFace.ignore_z_) { - Zoltan_Data.x = Data(x); - } - else if (interFace.ignore_x_) { - Zoltan_Data.x = Data(y); - Zoltan_Data.y = Data(z); - } - else if (interFace.ignore_y_) { - Zoltan_Data.x = Data(x); - Zoltan_Data.y = Data(z); - } - else if (!interFace.ignore_z_) { - Zoltan_Data.x = Data(x); - Zoltan_Data.y = Data(y); - } - else { - Zoltan_Data.x = Data(x); - Zoltan_Data.y = Data(y); - Zoltan_Data.z = Data(z); - } - - // Initialize Zoltan - int argc = 0; - char **argv = nullptr; - - float ver = 0.0; - Zoltan_Initialize(argc, argv, &ver); - fmt::print("Using Zoltan version {:.2}, method {}\n", static_cast(ver), - interFace.decomposition_method()); - - Zoltan zz(Ioss::ParallelUtils::comm_world()); - - // Register Callback functions - // Using global Zoltan_Data; could register it here instead as data field. - zz.Set_Num_Obj_Fn(zoltan_num_obj, nullptr); - zz.Set_Obj_List_Fn(zoltan_obj_list, nullptr); - zz.Set_Num_Geom_Fn(zoltan_num_dim, nullptr); - zz.Set_Geom_Multi_Fn(zoltan_geom, nullptr); - - // Set parameters for Zoltan - zz.Set_Param("DEBUG_LEVEL", "0"); - std::string str = fmt::format("{}", ranks); - zz.Set_Param("NUM_GLOBAL_PARTS", str); - zz.Set_Param("OBJ_WEIGHT_DIM", "1"); - zz.Set_Param("LB_METHOD", interFace.decomposition_method()); - zz.Set_Param("NUM_LID_ENTRIES", "0"); - zz.Set_Param("REMAP", "0"); - zz.Set_Param("RETURN_LISTS", "PARTITION_ASSIGNMENTS"); - zz.Set_Param("RCB_RECTILINEAR_BLOCKS", "1"); - - int num_global = sizeof(INT) / sizeof(ZOLTAN_ID_TYPE); - num_global = num_global < 1 ? 1 : num_global; - - // Call partitioner - int changes = 0; - int num_local = 0; - int num_import = 1; - int num_export = 1; - ZOLTAN_ID_PTR import_global_ids = nullptr; - ZOLTAN_ID_PTR import_local_ids = nullptr; - ZOLTAN_ID_PTR export_global_ids = nullptr; - ZOLTAN_ID_PTR export_local_ids = nullptr; - int *import_procs = nullptr; - int *import_to_part = nullptr; - int *export_procs = nullptr; - int *export_to_part = nullptr; - int rc = zz.LB_Partition(changes, num_global, num_local, num_import, import_global_ids, - import_local_ids, import_procs, import_to_part, num_export, - export_global_ids, export_local_ids, export_procs, export_to_part); - - if (rc != ZOLTAN_OK) { - fmt::print(stderr, "ERROR: Problem during call to Zoltan LB_Partition.\n"); - goto End; - } - - // Sanity check - if (element_count != static_cast(num_export)) { - fmt::print(stderr, "Sanity check failed; ndot {} != num_export {}.\n", element_count, - static_cast(num_export)); - goto End; - } - - elem_to_proc.resize(element_count); - for (size_t i = 0; i < element_count; i++) { - elem_to_proc[i] = export_to_part[i]; - } - - End: - /* Clean up */ - Zoltan::LB_Free_Part(&export_global_ids, &export_local_ids, &export_procs, &export_to_part); - Zoltan::LB_Free_Part(&export_global_ids, &export_local_ids, &export_procs, &export_to_part); - } -#endif - #if USE_METIS int get_common_node_count(const Ioss::Region ®ion) { @@ -464,64 +218,6 @@ namespace { } #endif - void output_histogram(const std::vector &proc_work, size_t avg_work, size_t median) - { - fmt::print("Work-per-processor Histogram\n"); - std::array histogram{}; - - auto wmin = *std::min_element(proc_work.begin(), proc_work.end()); - auto wmax = *std::max_element(proc_work.begin(), proc_work.end()); - - size_t hist_size = std::min(size_t(16), (wmax - wmin)); - hist_size = std::min(hist_size, proc_work.size()); - - if (hist_size <= 1) { - fmt::print("\tWork is the same on all processors; no histogram needed.\n\n"); - return; - } - - auto delta = double(wmax + 1 - wmin) / hist_size; - for (const auto &pw : proc_work) { - auto bin = size_t(double(pw - wmin) / delta); - SMART_ASSERT(bin < hist_size)(bin)(hist_size); - histogram[bin]++; - } - - size_t proc_width = Ioss::Utils::number_width(proc_work.size(), true); - size_t work_width = Ioss::Utils::number_width(wmax, true); - - fmt::print("\n\t{:^{}} {:^{}}\n", "Work Range", 2 * work_width + 2, "#", proc_width); - auto hist_max = *std::max_element(histogram.begin(), histogram.end()); - for (size_t i = 0; i < hist_size; i++) { - int max_star = 50; - int star_cnt = ((double)histogram[i] / hist_max * max_star); - std::string stars(star_cnt, '*'); - for (int j = 9; j < star_cnt;) { - stars[j] = '|'; - j += 10; - } - if (histogram[i] > 0 && star_cnt == 0) { - stars = '.'; - } - size_t w1 = wmin + size_t(i * delta); - size_t w2 = wmin + size_t((i + 1) * delta); - std::string postfix; - if (w1 <= avg_work && avg_work < w2) { - postfix += "average"; - } - if (w1 <= median && median < w2) { - if (!postfix.empty()) { - postfix += ", "; - } - postfix += "median"; - } - fmt::print("\t{:{}}..{:{}} ({:{}}):\t{:{}} {}\n", fmt::group_digits(w1), work_width, - fmt::group_digits(w2), work_width, fmt::group_digits(histogram[i]), proc_width, - stars, max_star, postfix); - } - fmt::print("\n"); - } - void scale_decomp(std::vector &elem_to_proc, int iscale, size_t num_proc) { // Do the scaling (integer division...) @@ -576,15 +272,15 @@ namespace { } // namespace template std::vector decompose_elements(const Ioss::Region ®ion, SystemInterface &interFace, - const std::vector &weights, - IOSS_MAYBE_UNUSED int dummy); + const std::vector &weights, + IOSS_MAYBE_UNUSED int dummy); template std::vector decompose_elements(const Ioss::Region ®ion, SystemInterface &interFace, - const std::vector &weights, + const std::vector &weights, IOSS_MAYBE_UNUSED int64_t dummy); template std::vector decompose_elements(const Ioss::Region ®ion, SystemInterface &interFace, - const std::vector &weights, IOSS_MAYBE_UNUSED INT dummy) + const std::vector &weights, IOSS_MAYBE_UNUSED INT dummy) { progress(__func__); // Populate the 'elem_to_proc' vector with a mapping from element to processor. @@ -648,13 +344,9 @@ std::vector decompose_elements(const Ioss::Region ®ion, SystemInterface else if (interFace.decomposition_method() == "rcb" || interFace.decomposition_method() == "rib" || interFace.decomposition_method() == "hsfc") { -#if USE_ZOLTAN - decompose_zoltan(region, interFace.processor_count(), interFace, elem_to_proc, weights, dummy); -#else - fmt::print(stderr, "ERROR: Zoltan library not enabled in this version of slice.\n" - " The 'rcb', 'rib', and 'hsfc' methods are not available.\n\n"); - std::exit(1); -#endif + Ioss::DecompUtils::decompose_zoltan( + region, interFace.processor_count(), interFace.decomposition_method(), elem_to_proc, + weights, interFace.ignore_x_, interFace.ignore_y_, interFace.ignore_z_, dummy); } else if (interFace.decomposition_method() == "rb" || interFace.decomposition_method() == "kway") { @@ -824,174 +516,3 @@ std::vector decompose_elements(const Ioss::Region ®ion, SystemInterface assert(elem_to_proc.size() == element_count); return elem_to_proc; } - -template -std::map> string_chains(const Ioss::chain_t &element_chains) -{ - std::map> chains; - - for (size_t i = 0; i < element_chains.size(); i++) { - auto &chain_entry = element_chains[i]; - if (chain_entry.link >= 0) { - chains[chain_entry.element].push_back(i + 1); - } - } - return chains; -} - -template std::vector line_decomp_weights(const Ioss::chain_t &element_chains, - size_t element_count); -template std::vector line_decomp_weights(const Ioss::chain_t &element_chains, - size_t element_count); - -template -std::vector line_decomp_weights(const Ioss::chain_t &element_chains, size_t element_count) -{ - auto chains = string_chains(element_chains); - - if ((debug_level & 16) != 0) { - for (const auto &[chain_root, chain_elements] : chains) { - fmt::print("Chain Root: {} contains: {}\n", chain_root, fmt::join(chain_elements, ", ")); - } - } - - std::vector weights(element_count, 1); - // Now, for each chain... - for (const auto &[chain_root, chain_elements] : chains) { - // * Set the weights of all elements in the chain... - // * non-root = 0, root = length of chain. - for (const auto &element : chain_elements) { - weights[element - 1] = 0; - } - weights[chain_root - 1] = static_cast(chain_elements.size()); - } - return weights; -} - -template void line_decomp_modify(const Ioss::chain_t &element_chains, - std::vector &elem_to_proc, int proc_count); -template void line_decomp_modify(const Ioss::chain_t &element_chains, - std::vector &elem_to_proc, int proc_count); - -template -void line_decomp_modify(const Ioss::chain_t &element_chains, std::vector &elem_to_proc, - int proc_count) -{ - // Get a map of all chains and the elements in the chains. Map key will be root. - auto chains = string_chains(element_chains); - - // Delta: elements added/removed from each processor... - std::vector delta(proc_count); - - // Now, for each chain... - for (const auto &[chain_root, chain_elements] : chains) { - if ((debug_level & 16) != 0) { - fmt::print("Chain Root: {} contains: {}\n", chain_root, fmt::join(chain_elements, ", ")); - } - - std::vector chain_proc_count(proc_count); - - // * get processors used by elements in the chain... - for (const auto &element : chain_elements) { - auto proc = elem_to_proc[element - 1]; - chain_proc_count[proc]++; - } - - // * Now, subtract the `delta` from each count - for (int i = 0; i < proc_count; i++) { - chain_proc_count[i] -= delta[i]; - } - - // * Assign all elements in the chain to processor at chain root - // * Update the deltas for all processors that gain/lose elements... - auto root_proc = elem_to_proc[chain_root - 1]; - for (const auto &element : chain_elements) { - if (elem_to_proc[element - 1] != root_proc) { - auto old_proc = elem_to_proc[element - 1]; - elem_to_proc[element - 1] = root_proc; - delta[root_proc]++; - delta[old_proc]--; - } - } - } - - std::vector proc_element_count(proc_count); - for (auto proc : elem_to_proc) { - proc_element_count[proc]++; - } - if ((debug_level & 32) != 0) { - fmt::print("\nElements/Processor: {}\n", fmt::join(proc_element_count, ", ")); - fmt::print("Delta/Processor: {}\n", fmt::join(delta, ", ")); - } -} - -template void output_decomposition_statistics(const std::vector &elem_to_proc, int proc_count, - size_t number_elements); -template void output_decomposition_statistics(const std::vector &elem_to_proc, - int proc_count, size_t number_elements); -template -void output_decomposition_statistics(const std::vector &elem_to_proc, int proc_count, - size_t number_elements) -{ - // Output histogram of elements / rank... - std::vector elem_per_rank(proc_count); - for (INT proc : elem_to_proc) { - elem_per_rank[proc]++; - } - - size_t proc_width = Ioss::Utils::number_width(proc_count, false); - size_t work_width = Ioss::Utils::number_width(number_elements, true); - - auto min_work = *std::min_element(elem_per_rank.begin(), elem_per_rank.end()); - auto max_work = *std::max_element(elem_per_rank.begin(), elem_per_rank.end()); - size_t median = 0; - { - auto pw_copy(elem_per_rank); - std::nth_element(pw_copy.begin(), pw_copy.begin() + pw_copy.size() / 2, pw_copy.end()); - median = pw_copy[pw_copy.size() / 2]; - fmt::print("\nElements per processor:\n\tMinimum = {}, Maximum = {}, Median = {}, Ratio = " - "{:.3}\n\n", - fmt::group_digits(min_work), fmt::group_digits(max_work), fmt::group_digits(median), - (double)(max_work) / min_work); - } - if (min_work == max_work) { - fmt::print("\nWork on all processors is {}\n\n", fmt::group_digits(min_work)); - } - else { - int max_star = 40; - int min_star = max_star * ((double)min_work / (double)(max_work)); - min_star = std::max(1, min_star); - int delta = max_star - min_star; - - double avg_work = (double)number_elements / (double)proc_count; - for (size_t i = 0; i < elem_per_rank.size(); i++) { - int star_cnt = - (double)(elem_per_rank[i] - min_work) / (max_work - min_work) * delta + min_star; - std::string stars(star_cnt, '*'); - std::string format = "\tProcessor {:{}}, work = {:{}} ({:.2f})\t{}\n"; - if (elem_per_rank[i] == max_work) { - fmt::print( -#if !defined __NVCC__ - fg(fmt::color::red), -#endif - format, i, proc_width, fmt::group_digits(elem_per_rank[i]), work_width, - (double)elem_per_rank[i] / avg_work, stars); - } - else if (elem_per_rank[i] == min_work) { - fmt::print( -#if !defined __NVCC__ - fg(fmt::color::green), -#endif - format, i, proc_width, fmt::group_digits(elem_per_rank[i]), work_width, - elem_per_rank[i] / avg_work, stars); - } - else { - fmt::print(format, i, proc_width, fmt::group_digits(elem_per_rank[i]), work_width, - elem_per_rank[i] / avg_work, stars); - } - } - - // Output Histogram... - output_histogram(elem_per_rank, (size_t)avg_work, median); - } -} diff --git a/packages/seacas/applications/slice/SL_Decompose.h b/packages/seacas/applications/slice/SL_Decompose.h index f2e49c6361..8310570193 100644 --- a/packages/seacas/applications/slice/SL_Decompose.h +++ b/packages/seacas/applications/slice/SL_Decompose.h @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2024 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. // @@ -12,16 +12,4 @@ #pragma once template std::vector decompose_elements(const Ioss::Region ®ion, SystemInterface &interFace, - const std::vector &weights, IOSS_MAYBE_UNUSED INT dummy); - -template -void line_decomp_modify(const Ioss::chain_t &element_chains, std::vector &elem_to_proc, - int proc_count); - -template -void output_decomposition_statistics(const std::vector &elem_to_proc, int proc_count, - size_t number_elements); - -template -std::vector line_decomp_weights(const Ioss::chain_t &element_chains, - size_t element_count); + const std::vector &weights, IOSS_MAYBE_UNUSED INT dummy); diff --git a/packages/seacas/applications/slice/SL_Version.h b/packages/seacas/applications/slice/SL_Version.h index 72fb5eaf86..1560a65a3c 100644 --- a/packages/seacas/applications/slice/SL_Version.h +++ b/packages/seacas/applications/slice/SL_Version.h @@ -9,6 +9,6 @@ static const std::array qainfo{ "slice", - "2024/04/03", - "2.2.01", + "2024/06/10", + "2.3.00", }; diff --git a/packages/seacas/applications/slice/Slice.C b/packages/seacas/applications/slice/Slice.C index 9cb38e7e79..be73865ceb 100644 --- a/packages/seacas/applications/slice/Slice.C +++ b/packages/seacas/applications/slice/Slice.C @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -1373,7 +1374,7 @@ namespace { Ioss::PropertyManager properties = set_properties(interFace); Ioss::chain_t element_chains; - std::vector weights; + std::vector weights; if (interFace.lineDecomp_) { element_chains = Ioss::generate_element_chains(region, interFace.lineSurfaceList_, debug_level, dummy); @@ -1381,8 +1382,8 @@ namespace { if (interFace.decomposition_method() == "rcb" || interFace.decomposition_method() == "rib" || interFace.decomposition_method() == "hsfc") { - weights = - line_decomp_weights(element_chains, region.get_property("element_count").get_int()); + weights = Ioss::DecompUtils::line_decomp_weights( + element_chains, region.get_property("element_count").get_int()); progress("generate_element_weights"); } } @@ -1399,12 +1400,12 @@ namespace { if (interFace.lineDecomp_) { // Make sure all elements on a chain are on the same processor rank... - line_decomp_modify(element_chains, elem_to_proc, interFace.processor_count()); + Ioss::DecompUtils::line_decomp_modify(element_chains, elem_to_proc, + interFace.processor_count()); } if (debug_level & 32) { - output_decomposition_statistics(elem_to_proc, interFace.processor_count(), - elem_to_proc.size()); + Ioss::DecompUtils::output_decomposition_statistics(elem_to_proc, interFace.processor_count()); } if (!create_split_files) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C index 8f16e84524..92d762eae9 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C @@ -84,14 +84,14 @@ namespace { template void get_line_front(Ioss::SideSet *fs, const Ioss::ElementBlock *block, - const std::string &adj_block, Ioss::chain_t &element_chains, - front_t &front) + Ioss::chain_t &element_chains, front_t &front) { + const auto adj_block_name = block->name(); Ioss::NameList blocks; fs->block_membership(blocks); for (const auto &fs_block : blocks) { - if (fs_block == adj_block) { - // This faceset has some elements that are in `adj_block` -- put those in the `front` + if (fs_block == adj_block_name) { + // This faceset has some elements that are in `adj_block_name` -- put those in the `front` // list. Get list of "sides" in this faceset... std::vector element_side; assert(fs->side_block_count() == 1); @@ -117,23 +117,19 @@ namespace { } template - front_t get_line_front(Ioss::Region ®ion, const std::string &adj_block, + front_t get_line_front(Ioss::Region ®ion, const Ioss::ElementBlock *block, Ioss::chain_t &element_chains, const std::string &surface_list) { front_t front; // Since lines can not cross element blocks, we can process everything a block at a time. - const auto *block = region.get_element_block(adj_block); assert(block != nullptr); - if (block->topology()->shape() != Ioss::ElementShape::HEX) { - fmt::print("Skipping Element Block {}; it does not contain HEX elements.\n", adj_block); - return front; - } + assert(block->topology()->shape() == Ioss::ElementShape::HEX); if (surface_list == "ALL") { const Ioss::SideSetContainer &fss = region.get_sidesets(); for (const auto &fs : fss) { - get_line_front(fs, block, adj_block, element_chains, front); + get_line_front(fs, block, element_chains, front); } } else { @@ -141,7 +137,7 @@ namespace { for (const auto &surface : selected_surfaces) { auto *sset = region.get_sideset(surface); if (sset != nullptr) { - get_line_front(sset, block, adj_block, element_chains, front); + get_line_front(sset, block, element_chains, front); } } } @@ -201,33 +197,47 @@ namespace Ioss { size_t numel = region.get_property("element_count").get_int(); Ioss::chain_t element_chains(numel); - // Generate the faces for use later... - Ioss::FaceGenerator face_generator(region); - face_generator.generate_faces((INT)0, true, true); - // Determine which element block(s) are adjacent to the faceset specifying "lines" // The `adjacent_blocks` contains the names of all element blocks that are adjacent to the // surface(s) that specify the faces at the 'root' of the lines... - Ioss::NameList adjacent_blocks = get_adjacent_blocks(region, surface_list); - if (adjacent_blocks.empty()) { + Ioss::NameList adjacent_block_names = get_adjacent_blocks(region, surface_list); + if (adjacent_block_names.empty()) { fmt::print("WARNING: No surfaces in the model matched the input surface list ({}).\n\tNo " "chains will be generated.\n", surface_list); } - for (const auto &adj_block : adjacent_blocks) { + + // Get the EB* corresponding to the EB names... + Ioss::ElementBlockContainer adjacent_blocks; + adjacent_blocks.reserve(adjacent_block_names.size()); + for (const auto &blk_name : adjacent_block_names) { + auto *eb = region.get_element_block(blk_name); + assert(eb != nullptr); + if (eb->topology()->shape() != Ioss::ElementShape::HEX) { + fmt::print("Skipping Element Block {}; it does not contain HEX elements.\n", blk_name); + } + else { + adjacent_blocks.push_back(eb); + } + } + + // Generate the faces for use later... (only generate on the blocks touching the front) + Ioss::FaceGenerator face_generator(region); + face_generator.generate_block_faces(adjacent_blocks, (INT)0, true); + + for (const auto *block : adjacent_blocks) { // Get the offset into the element_chains vector... - const auto *block = region.get_element_block(adj_block); - auto offset = block->get_offset() + 1; - auto count = block->entity_count(); + auto offset = block->get_offset() + 1; + auto count = block->entity_count(); - auto front = get_line_front(region, adj_block, element_chains, surface_list); + auto front = get_line_front(region, block, element_chains, surface_list); if (front.empty()) { continue; } // We want a vector giving us the Face for each face of each element in the block... connectivity_t face_connectivity(count); - generate_face_connectivity(face_generator.faces(adj_block), static_cast(offset), + generate_face_connectivity(face_generator.faces(block), static_cast(offset), face_connectivity); // For each face on the "front" (at the beginning the boundary sideset faces) diff --git a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C index efda361b47..48df8029e0 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C @@ -301,6 +301,13 @@ namespace Ioss { props.get("PARMETIS_COMMON_NODE_COUNT").get_int() > 0) { m_commonNodeCount = props.get("PARMETIS_COMMON_NODE_COUNT").get_int(); } + + if (props.exists("LINE_DECOMPOSITION")) { + // The value of the property should be a comma-separated list of surface/sideset names from + // which the lines will grow, or the value "ALL" for all surfaces in the model. + m_lineDecomp = true; + m_decompExtra = props.get("LINE_DECOMPOSITION").get_string(); + } } template IOSS_EXPORT void @@ -452,6 +459,12 @@ namespace Ioss { if (m_method == "MAP") { guided_decompose(); } + if (m_method == "SPECIFIED") { + // Currently used for line decomposition with another decomposition type. + // The line-modified decomposition is done prior to this and builds the + // `m_elementToProc` which is then used here to decompose the elements... + guided_decompose(); + } show_progress("\tfinished with decomposition method"); Ioss::sort(importElementMap); @@ -666,7 +679,7 @@ namespace Ioss { template void Decomposition::guided_decompose() { show_progress(__func__); - assert(m_method == "MAP" || m_method == "VARIABLE"); + assert(m_method == "MAP" || m_method == "VARIABLE" || m_method == "SPECIFIED"); // - Read my portion of the map / variable. // - count # of exports to each rank // -- exportElementCount[proc] @@ -675,13 +688,7 @@ namespace Ioss { // - communicate to all proc -- becomes importElementMap. // Create `exportElementIndex` from `exportElementCount` - std::string label; - if (m_method == "MAP") { - label = "map"; - } - else { - label = "variable"; - } + std::string label = m_method; // If the "m_decompExtra" string contains a comma, then the // value following the comma is either an integer "scale" @@ -699,7 +706,7 @@ namespace Ioss { // [0..m_processorCount). double scale = 1.0; auto pos = m_decompExtra.find(","); - if (pos != std::string::npos) { + if (m_method != "SPECIFIED" && pos != std::string::npos) { // Extract the string following the comma... auto scale_str = m_decompExtra.substr(pos + 1); if (scale_str == "AUTO" || scale_str == "auto") { @@ -1080,6 +1087,7 @@ namespace Ioss { #endif #if !defined(NO_ZOLTAN_SUPPORT) + template void Decomposition::zoltan_decompose(Zoltan &zz) { show_progress(__func__); diff --git a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.h b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.h index 5bae8e227b..14ca00bd4e 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.h @@ -277,6 +277,7 @@ namespace Ioss { void simple_decompose(); void simple_node_decompose(); void guided_decompose(); + void line_decompose(); void calculate_element_centroids(const std::vector &x, const std::vector &y, const std::vector &z); @@ -801,11 +802,13 @@ namespace Ioss { size_t m_importPreLocalNodeIndex{0}; bool m_retainFreeNodes{true}; + bool m_lineDecomp{false}; bool m_showProgress{false}; bool m_showHWM{false}; - std::vector m_elementToProc; // Used by "MAP" scheme... + std::vector m_elementToProc; // Used by "MAP" scheme... std::vector m_centroids; + std::vector m_weights; std::vector m_pointer; // Index into adjacency, processor list for each element... std::vector m_adjacency; // Size is sum of element connectivity sizes diff --git a/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C new file mode 100644 index 0000000000..b065b56154 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C @@ -0,0 +1,581 @@ +/* + * Copyright(C) 2024 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 +#include +#include +#include + +#include "Ioss_ChainGenerator.h" +#include "Ioss_CodeTypes.h" +#include "Ioss_DecompositionUtils.h" +#include "Ioss_ElementBlock.h" +#include "Ioss_NodeBlock.h" +#include "Ioss_ParallelUtils.h" +#include "Ioss_Region.h" +#include "Ioss_SmartAssert.h" + +#include +#include +#include +#if !defined __NVCC__ +#include +#endif + +#if !defined(NO_ZOLTAN_SUPPORT) +#include // for Zoltan_Initialize +#include // for Zoltan +#endif + +namespace { +#if !defined(NO_ZOLTAN_SUPPORT) + /*****************************************************************************/ + /***** Global data structure used by Zoltan callbacks. *****/ + /***** Could implement Zoltan callbacks without global data structure, *****/ + /***** but using the global data structure makes implementation quick. *****/ + struct + { + size_t ndot; /* Length of x, y, z, and part (== # of elements) */ + float *vwgt; /* vertex weights */ + double *x; /* x-coordinates */ + double *y; /* y-coordinates */ + double *z; /* z-coordinates */ + } Zoltan_Data; + + /*****************************************************************************/ + /***** ZOLTAN CALLBACK FUNCTIONS *****/ + int zoltan_num_dim(void * /*data*/, int *ierr) + { + /* Return dimensionality of coordinate data. + * Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign. + */ + *ierr = ZOLTAN_OK; + if (Zoltan_Data.z != nullptr) { + return 3; + } + if (Zoltan_Data.y != nullptr) { + return 2; + } + return 1; + } + + int zoltan_num_obj(void * /*data*/, int *ierr) + { + /* Return number of objects. + * Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign. + */ + *ierr = ZOLTAN_OK; + return Zoltan_Data.ndot; + } + + void zoltan_obj_list(void * /*data*/, int /*ngid_ent*/, int /*nlid_ent*/, ZOLTAN_ID_PTR gids, + ZOLTAN_ID_PTR /*lids*/, int wdim, float *wgts, int *ierr) + { + /* Return list of object IDs. + * Return only global IDs; don't need local IDs since running in serial. + * gids are array indices for coordinate and vwgts arrays. + * Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign. + */ + std::iota(gids, gids + Zoltan_Data.ndot, 0); + if (wdim != 0) { + for (size_t i = 0; i < Zoltan_Data.ndot; i++) { + wgts[i] = static_cast(Zoltan_Data.vwgt[i]); + } + } + + *ierr = ZOLTAN_OK; + } + + void zoltan_geom(void * /*data*/, int /*ngid_ent*/, int /*nlid_ent*/, int nobj, + const ZOLTAN_ID_PTR gids, ZOLTAN_ID_PTR /*lids*/, int ndim, double *geom, + int *ierr) + { + /* Return coordinates for objects. + * gids are array indices for coordinate arrays. + * Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign. + */ + + for (size_t i = 0; i < static_cast(nobj); i++) { + size_t j = gids[i]; + geom[i * ndim] = Zoltan_Data.x[j]; + if (ndim > 1) { + geom[i * ndim + 1] = Zoltan_Data.y[j]; + } + if (ndim > 2) { + geom[i * ndim + 2] = Zoltan_Data.z[j]; + } + } + + *ierr = ZOLTAN_OK; + } +#endif + + template + std::map> string_chains(const Ioss::chain_t &element_chains) + { + std::map> chains; + + for (size_t i = 0; i < element_chains.size(); i++) { + auto &chain_entry = element_chains[i]; + if (chain_entry.link >= 0) { + chains[chain_entry.element].push_back(i + 1); + } + } + return chains; + } + + void output_histogram(const std::vector &proc_work, size_t avg_work, size_t median) + { + fmt::print("Work-per-processor Histogram\n"); + std::array histogram{}; + + auto wmin = *std::min_element(proc_work.begin(), proc_work.end()); + auto wmax = *std::max_element(proc_work.begin(), proc_work.end()); + + size_t hist_size = std::min(size_t(16), (wmax - wmin)); + hist_size = std::min(hist_size, proc_work.size()); + + if (hist_size <= 1) { + fmt::print("\tWork is the same on all processors; no histogram needed.\n\n"); + return; + } + + auto delta = double(wmax + 1 - wmin) / hist_size; + for (const auto &pw : proc_work) { + auto bin = size_t(double(pw - wmin) / delta); + SMART_ASSERT(bin < hist_size)(bin)(hist_size); + histogram[bin]++; + } + + size_t proc_width = Ioss::Utils::number_width(proc_work.size(), true); + size_t work_width = Ioss::Utils::number_width(wmax, true); + + fmt::print("\n\t{:^{}} {:^{}}\n", "Work Range", 2 * work_width + 2, "#", proc_width); + auto hist_max = *std::max_element(histogram.begin(), histogram.end()); + for (size_t i = 0; i < hist_size; i++) { + int max_star = 50; + int star_cnt = ((double)histogram[i] / hist_max * max_star); + std::string stars(star_cnt, '*'); + for (int j = 9; j < star_cnt;) { + stars[j] = '|'; + j += 10; + } + if (histogram[i] > 0 && star_cnt == 0) { + stars = '.'; + } + size_t w1 = wmin + size_t(i * delta); + size_t w2 = wmin + size_t((i + 1) * delta); + std::string postfix; + if (w1 <= avg_work && avg_work < w2) { + postfix += "average"; + } + if (w1 <= median && median < w2) { + if (!postfix.empty()) { + postfix += ", "; + } + postfix += "median"; + } + fmt::print("\t{:{}}..{:{}} ({:{}}):\t{:{}} {}\n", fmt::group_digits(w1), work_width, + fmt::group_digits(w2), work_width, fmt::group_digits(histogram[i]), proc_width, + stars, max_star, postfix); + } + fmt::print("\n"); + } +} // namespace + +namespace Ioss { + template IOSS_EXPORT void + DecompUtils::decompose_zoltan(const Ioss::Region ®ion, int ranks, const std::string &method, + std::vector &elem_to_proc, const std::vector &weights, + bool ignore_x, bool ignore_y, bool ignore_z, + IOSS_MAYBE_UNUSED int dummy); + template IOSS_EXPORT void + DecompUtils::decompose_zoltan(const Ioss::Region ®ion, int ranks, const std::string &method, + std::vector &elem_to_proc, const std::vector &weights, + bool ignore_x, bool ignore_y, bool ignore_z, + IOSS_MAYBE_UNUSED int64_t dummy); + + template + void DecompUtils::decompose_zoltan(const Ioss::Region ®ion, int ranks, + const std::string &method, std::vector &elem_to_proc, + const std::vector &weights, bool ignore_x, + bool ignore_y, bool ignore_z, IOSS_MAYBE_UNUSED INT dummy) + { +#if defined(NO_ZOLTAN_SUPPORT) + fmt::print(stderr, "ERROR: Zoltan library not enabled in this version of slice.\n" + " The 'rcb', 'rib', and 'hsfc' methods are not available.\n\n"); + std::exit(1); +#else + if (ranks == 1) { + return; + } + + size_t element_count = region.get_property("element_count").get_int(); + if (element_count != static_cast(static_cast(element_count))) { + fmt::print(stderr, "ERROR: Cannot have a mesh with more than 2.1 Billion elements in a " + "Zoltan decomposition.\n"); + exit(EXIT_FAILURE); + } + + auto [x, y, z] = Ioss::DecompUtils::get_element_centroid(region, dummy); + + // Copy mesh data and pointers into structure accessible from callback fns. + Zoltan_Data.ndot = element_count; + Zoltan_Data.vwgt = const_cast(Data(weights)); + + if (ignore_x && ignore_y) { + x.clear(); + y.clear(); + Zoltan_Data.x = Data(z); + } + else if (ignore_x && ignore_z) { + x.clear(); + z.clear(); + Zoltan_Data.x = Data(y); + } + else if (ignore_y && ignore_z) { + y.clear(); + z.clear(); + Zoltan_Data.x = Data(x); + } + else if (ignore_x) { + x.clear(); + Zoltan_Data.x = Data(y); + Zoltan_Data.y = Data(z); + } + else if (ignore_y) { + y.clear(); + Zoltan_Data.x = Data(x); + Zoltan_Data.y = Data(z); + } + else if (ignore_z) { + z.clear(); + Zoltan_Data.x = Data(x); + Zoltan_Data.y = Data(y); + } + else { + Zoltan_Data.x = Data(x); + Zoltan_Data.y = Data(y); + Zoltan_Data.z = Data(z); + } + + // Initialize Zoltan + int argc = 0; + char **argv = nullptr; + + float ver = 0.0; + Zoltan_Initialize(argc, argv, &ver); + fmt::print("Using Zoltan version {:.2}, method {}\n", static_cast(ver), method); + + Zoltan zz(Ioss::ParallelUtils::comm_self()); + + // Register Callback functions + // Using global Zoltan_Data; could register it here instead as data field. + zz.Set_Num_Obj_Fn(zoltan_num_obj, nullptr); + zz.Set_Obj_List_Fn(zoltan_obj_list, nullptr); + zz.Set_Num_Geom_Fn(zoltan_num_dim, nullptr); + zz.Set_Geom_Multi_Fn(zoltan_geom, nullptr); + + // Set parameters for Zoltan + zz.Set_Param("DEBUG_LEVEL", "0"); + std::string str = fmt::format("{}", ranks); + zz.Set_Param("NUM_GLOBAL_PARTS", str); + zz.Set_Param("OBJ_WEIGHT_DIM", "1"); + zz.Set_Param("LB_METHOD", method); + zz.Set_Param("NUM_LID_ENTRIES", "0"); + zz.Set_Param("REMAP", "0"); + zz.Set_Param("RETURN_LISTS", "PARTITION_ASSIGNMENTS"); + zz.Set_Param("RCB_RECTILINEAR_BLOCKS", "1"); + + int num_global = sizeof(INT) / sizeof(ZOLTAN_ID_TYPE); + num_global = num_global < 1 ? 1 : num_global; + + // Call partitioner + int changes = 0; + int num_local = 0; + int num_import = 1; + int num_export = 1; + ZOLTAN_ID_PTR import_global_ids = nullptr; + ZOLTAN_ID_PTR import_local_ids = nullptr; + ZOLTAN_ID_PTR export_global_ids = nullptr; + ZOLTAN_ID_PTR export_local_ids = nullptr; + int *import_procs = nullptr; + int *import_to_part = nullptr; + int *export_procs = nullptr; + int *export_to_part = nullptr; + int rc = zz.LB_Partition(changes, num_global, num_local, num_import, import_global_ids, + import_local_ids, import_procs, import_to_part, num_export, + export_global_ids, export_local_ids, export_procs, export_to_part); + + if (rc != ZOLTAN_OK) { + fmt::print(stderr, "ERROR: Problem during call to Zoltan LB_Partition.\n"); + goto End; + } + + // Sanity check + if (element_count != static_cast(num_export)) { + fmt::print(stderr, "Sanity check failed; ndot {} != num_export {}.\n", element_count, + static_cast(num_export)); + goto End; + } + + elem_to_proc.resize(element_count); + for (size_t i = 0; i < element_count; i++) { + elem_to_proc[i] = export_to_part[i]; + } + + End: + /* Clean up */ + Zoltan::LB_Free_Part(&import_global_ids, &import_local_ids, &import_procs, &import_to_part); + Zoltan::LB_Free_Part(&export_global_ids, &export_local_ids, &export_procs, &export_to_part); +#endif + } + + template + int DecompUtils::line_decompose(Region ®ion, size_t num_ranks, const std::string &method, + const std::string &surface_list, + std::vector &element_to_proc, INT dummy) + { + + Ioss::chain_t element_chains = + Ioss::generate_element_chains(region, surface_list, 0, dummy); + region.get_database()->progress("Ioss::generate_element_chains"); + + std::vector weights = + line_decomp_weights(element_chains, region.get_property("element_count").get_int()); + region.get_database()->progress("generate_element_weights"); + + double start = Ioss::Utils::timer(); + decompose_zoltan(region, num_ranks, method, element_to_proc, weights, false, false, false, + dummy); + double end = Ioss::Utils::timer(); + fmt::print(stderr, "Decompose elements = {:.5}\n", end - start); + region.get_database()->progress("exit decompose_elements"); + + // Make sure all elements on a chain are on the same processor rank... + line_decomp_modify(element_chains, element_to_proc, num_ranks); + + return 1; + } + + template IOSS_EXPORT int DecompUtils::line_decompose(Region ®ion, size_t num_ranks, + const std::string &method, + const std::string &surface_list, + std::vector &element_to_proc, + int dummy); + template IOSS_EXPORT int DecompUtils::line_decompose(Region ®ion, size_t num_ranks, + const std::string &method, + const std::string &surface_list, + std::vector &element_to_proc, + int64_t dummy); + + template + std::vector DecompUtils::line_decomp_weights(const Ioss::chain_t &element_chains, + size_t element_count) + { + int debug_level = 0; + auto chains = string_chains(element_chains); + + if ((debug_level & 16) != 0) { + for (const auto &[chain_root, chain_elements] : chains) { + fmt::print("Chain Root: {} contains: {}\n", chain_root, fmt::join(chain_elements, ", ")); + } + } + + std::vector weights(element_count, 1); + // Now, for each chain... + for (const auto &[chain_root, chain_elements] : chains) { + // * Set the weights of all elements in the chain... + // * non-root = 0, root = length of chain. + for (const auto &element : chain_elements) { + weights[element - 1] = 0; + } + weights[chain_root - 1] = static_cast(chain_elements.size()); + } + return weights; + } + template IOSS_EXPORT std::vector + DecompUtils::line_decomp_weights(const Ioss::chain_t &element_chains, size_t element_count); + template IOSS_EXPORT std::vector + DecompUtils::line_decomp_weights(const Ioss::chain_t &element_chains, + size_t element_count); + + template + void DecompUtils::line_decomp_modify(const Ioss::chain_t &element_chains, + std::vector &elem_to_proc, int proc_count) + { + int debug_level = 0; + // Get a map of all chains and the elements in the chains. Map key will be root. + auto chains = string_chains(element_chains); + + // Delta: elements added/removed from each processor... + std::vector delta(proc_count); + + // Now, for each chain... + for (const auto &[chain_root, chain_elements] : chains) { + if ((debug_level & 16) != 0) { + fmt::print("Chain Root: {} contains: {}\n", chain_root, fmt::join(chain_elements, ", ")); + } + + std::vector chain_proc_count(proc_count); + + // * get processors used by elements in the chain... + for (const auto &element : chain_elements) { + auto proc = elem_to_proc[element - 1]; + chain_proc_count[proc]++; + } + + // * Now, subtract the `delta` from each count + for (int i = 0; i < proc_count; i++) { + chain_proc_count[i] -= delta[i]; + } + + // * Assign all elements in the chain to processor at chain root + // * Update the deltas for all processors that gain/lose elements... + auto root_proc = elem_to_proc[chain_root - 1]; + for (const auto &element : chain_elements) { + if (elem_to_proc[element - 1] != root_proc) { + auto old_proc = elem_to_proc[element - 1]; + elem_to_proc[element - 1] = root_proc; + delta[root_proc]++; + delta[old_proc]--; + } + } + } + + std::vector proc_element_count(proc_count); + for (auto proc : elem_to_proc) { + proc_element_count[proc]++; + } + if ((debug_level & 32) != 0) { + fmt::print("\nElements/Processor: {}\n", fmt::join(proc_element_count, ", ")); + fmt::print("Delta/Processor: {}\n", fmt::join(delta, ", ")); + } + } + + template IOSS_EXPORT void + DecompUtils::line_decomp_modify(const Ioss::chain_t &element_chains, + std::vector &elem_to_proc, int proc_count); + template IOSS_EXPORT void + DecompUtils::line_decomp_modify(const Ioss::chain_t &element_chains, + std::vector &elem_to_proc, int proc_count); + + void DecompUtils::output_decomposition_statistics(const std::vector &elem_to_proc, + int proc_count) + { + // Output histogram of elements / rank... + std::vector elem_per_rank(proc_count); + for (int proc : elem_to_proc) { + elem_per_rank[proc]++; + } + + size_t number_elements = elem_to_proc.size(); + size_t proc_width = Ioss::Utils::number_width(proc_count, false); + size_t work_width = Ioss::Utils::number_width(number_elements, true); + + auto min_work = *std::min_element(elem_per_rank.begin(), elem_per_rank.end()); + auto max_work = *std::max_element(elem_per_rank.begin(), elem_per_rank.end()); + size_t median = 0; + { + auto pw_copy(elem_per_rank); + std::nth_element(pw_copy.begin(), pw_copy.begin() + pw_copy.size() / 2, pw_copy.end()); + median = pw_copy[pw_copy.size() / 2]; + fmt::print("\nElements per processor:\n\tMinimum = {}, Maximum = {}, Median = {}, Ratio = " + "{:.3}\n\n", + fmt::group_digits(min_work), fmt::group_digits(max_work), + fmt::group_digits(median), (double)(max_work) / min_work); + } + if (min_work == max_work) { + fmt::print("Work on all processors is {}\n\n", fmt::group_digits(min_work)); + } + else { + int max_star = 40; + int min_star = max_star * ((double)min_work / (double)(max_work)); + min_star = std::max(1, min_star); + int delta = max_star - min_star; + + double avg_work = (double)number_elements / (double)proc_count; + for (size_t i = 0; i < elem_per_rank.size(); i++) { + int star_cnt = + (double)(elem_per_rank[i] - min_work) / (max_work - min_work) * delta + min_star; + std::string stars(star_cnt, '*'); + std::string format = "\tProcessor {:{}}, work = {:{}} ({:.2f})\t{}\n"; + if (elem_per_rank[i] == max_work) { + fmt::print( +#if !defined __NVCC__ + fg(fmt::color::red), +#endif + format, i, proc_width, fmt::group_digits(elem_per_rank[i]), work_width, + (double)elem_per_rank[i] / avg_work, stars); + } + else if (elem_per_rank[i] == min_work) { + fmt::print( +#if !defined __NVCC__ + fg(fmt::color::green), +#endif + format, i, proc_width, fmt::group_digits(elem_per_rank[i]), work_width, + elem_per_rank[i] / avg_work, stars); + } + else { + fmt::print(format, i, proc_width, fmt::group_digits(elem_per_rank[i]), work_width, + elem_per_rank[i] / avg_work, stars); + } + } + + // Output Histogram... + output_histogram(elem_per_rank, (size_t)avg_work, median); + } + } + + template + std::tuple, std::vector, std::vector> + DecompUtils::get_element_centroid(const Ioss::Region ®ion, IOSS_MAYBE_UNUSED INT dummy) + { + size_t element_count = region.get_property("element_count").get_int(); + + // The zoltan methods supported in slice are all geometry based + // and use the element centroid. + std::vector x(element_count); + std::vector y(element_count); + std::vector z(element_count); + + const auto *nb = region.get_node_blocks()[0]; + std::vector coor; + nb->get_field_data("mesh_model_coordinates", coor); + + const auto &blocks = region.get_element_blocks(); + size_t el = 0; + for (auto &eb : blocks) { + std::vector connectivity; + eb->get_field_data("connectivity_raw", connectivity); + size_t blk_element_count = eb->entity_count(); + size_t blk_element_nodes = eb->topology()->number_nodes(); + + for (size_t j = 0; j < blk_element_count; j++) { + for (size_t k = 0; k < blk_element_nodes; k++) { + auto node = connectivity[j * blk_element_nodes + k] - 1; + x[el] += coor[node * 3 + 0]; + y[el] += coor[node * 3 + 1]; + z[el] += coor[node * 3 + 2]; + } + x[el] /= blk_element_nodes; + y[el] /= blk_element_nodes; + z[el] /= blk_element_nodes; + el++; + } + } + return {x, y, z}; + } + + template IOSS_EXPORT std::tuple, std::vector, std::vector> + DecompUtils::get_element_centroid(const Ioss::Region ®ion, IOSS_MAYBE_UNUSED int dummy); + + template IOSS_EXPORT std::tuple, std::vector, std::vector> + DecompUtils::get_element_centroid(const Ioss::Region ®ion, IOSS_MAYBE_UNUSED int64_t dummy); + +} // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.h b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.h new file mode 100644 index 0000000000..30b1b35a2c --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.h @@ -0,0 +1,48 @@ +/* + * Copyright(C) 2024 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 + */ +#pragma once + +#include "Ioss_ChainGenerator.h" +#include "Ioss_CodeTypes.h" +#include "Ioss_Region.h" +#include +#include + +#include "ioss_export.h" + +namespace Ioss { + class IOSS_EXPORT DecompUtils + { + public: + template + static void line_decomp_modify(const Ioss::chain_t &element_chains, + std::vector &element_to_proc, int proc_count); + + static void output_decomposition_statistics(const std::vector &element_to_proc, + int proc_count); + + template + static std::vector line_decomp_weights(const Ioss::chain_t &element_chains, + size_t element_count); + + template + static int line_decompose(Region ®ion, size_t num_ranks, const std::string &method, + const std::string &surface_list, std::vector &element_to_proc, + INT dummy); + + template + static void decompose_zoltan(const Ioss::Region ®ion, int ranks, const std::string &method, + std::vector &elem_to_proc, const std::vector &weights, + bool ignore_x, bool ignore_y, bool ignore_z, + IOSS_MAYBE_UNUSED INT dummy); + + template + static std::tuple, std::vector, std::vector> + get_element_centroid(const Ioss::Region ®ion, IOSS_MAYBE_UNUSED INT dummy); + }; +} // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C index 0badfc563b..b167d1aae8 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2024 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. // @@ -327,6 +327,12 @@ namespace Ioss { FaceGenerator::FaceGenerator(Ioss::Region ®ion) : region_(region) {} + FaceUnorderedSet &FaceGenerator::faces(const Ioss::ElementBlock *block) + { + auto name = block->name(); + return faces_[name]; + } + template IOSS_EXPORT void FaceGenerator::generate_faces(int, bool, bool); template IOSS_EXPORT void FaceGenerator::generate_faces(int64_t, bool, bool); @@ -334,7 +340,8 @@ namespace Ioss { void FaceGenerator::generate_faces(INT /*dummy*/, bool block_by_block, bool local_ids) { if (block_by_block) { - generate_block_faces(INT(0), local_ids); + const auto &ebs = region_.get_element_blocks(); + generate_block_faces(ebs, INT(0), local_ids); } else { generate_model_faces(INT(0), local_ids); @@ -349,7 +356,14 @@ namespace Ioss { } } - template void FaceGenerator::generate_block_faces(INT /*dummy*/, bool local_ids) + template IOSS_EXPORT void FaceGenerator::generate_block_faces(const Ioss::ElementBlockContainer &, + int, bool); + template IOSS_EXPORT void FaceGenerator::generate_block_faces(const Ioss::ElementBlockContainer &, + int64_t, bool); + + template + void FaceGenerator::generate_block_faces(const Ioss::ElementBlockContainer &ebs, INT /*dummy*/, + bool local_ids) { // Convert ids into hashed-ids Ioss::NodeBlock *nb = region_.get_node_blocks()[0]; @@ -370,7 +384,6 @@ namespace Ioss { auto endh = std::chrono::steady_clock::now(); #endif - const auto &ebs = region_.get_element_blocks(); for (const auto &eb : ebs) { const std::string &name = eb->name(); size_t numel = eb->entity_count(); diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h index d88745ffff..2881b08f43 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h @@ -14,6 +14,7 @@ #include #include +#include "Ioss_Region.h" #include "ioss_export.h" #define FG_USE_ROBIN @@ -28,8 +29,7 @@ #include namespace Ioss { - class Region; - + class ElementBlock; class IOSS_EXPORT Face { public: @@ -123,14 +123,18 @@ namespace Ioss { template void generate_faces(INT /*dummy*/, bool block_by_block = false, bool local_ids = false); + template + void generate_block_faces(const ElementBlockContainer &ebs, INT /*dummy*/, + bool local_ids = false); + FaceUnorderedSet &faces(const std::string &name = "ALL") { return faces_[name]; } + FaceUnorderedSet &faces(const ElementBlock *block); //! Given a local node id (0-based), return the hashed value. size_t node_id_hash(size_t local_node_id) const { return hashIds_[local_node_id]; } private: template void hash_node_ids(const std::vector &node_ids); - template void generate_block_faces(INT /*dummy*/, bool local_ids); template void generate_model_faces(INT /*dummy*/, bool local_ids); Ioss::Region ®ion_; diff --git a/packages/seacas/libraries/ioss/src/Ioss_Property.h b/packages/seacas/libraries/ioss/src/Ioss_Property.h index 2ef5656778..fdb065e695 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Property.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Property.h @@ -97,6 +97,7 @@ namespace Ioss { IOSS_NODISCARD bool operator!=(const Ioss::Property &rhs) const; IOSS_NODISCARD bool operator==(const Ioss::Property &rhs) const; +#if 0 friend void swap(Ioss::Property &first, Ioss::Property &second) noexcept { using std::swap; @@ -105,7 +106,7 @@ namespace Ioss { swap(first.origin_, second.origin_); swap(first.data_, second.data_); } - +#endif private: std::string name_{}; BasicType type_{INVALID}; diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C index 9a38458021..5e0540cc7e 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C @@ -282,8 +282,8 @@ namespace Ioex { double t_end = Ioss::Utils::timer(); double duration = util().global_minmax(t_end - t_begin, Ioss::ParallelUtils::DO_MAX); if (myProcessor == 0) { - fmt::print(Ioss::DebugOut(), "Input File Open Time = {} ({})\n", duration, - decoded_filename()); + fmt::print(Ioss::DebugOut(), "Input File Open Time = {} ({})\n", duration, + decoded_filename()); } } diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C index 0c4ab1e54b..734316cf2c 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C @@ -5,12 +5,16 @@ // See packages/seacas/LICENSE for details #include "Ioss_CodeTypes.h" +#include "Ioss_DecompositionUtils.h" #include "exodus/Ioex_DecompositionData.h" + #if defined PARALLEL_AWARE_EXODUS -#include "Ioss_ElementTopology.h" // for ElementTopology -#include "Ioss_Field.h" // for Field, etc -#include "Ioss_Map.h" // for Map, MapContainer -#include "Ioss_PropertyManager.h" // for PropertyManager +#include "Ioss_ElementTopology.h" +#include "Ioss_Field.h" +#include "Ioss_IOFactory.h" +#include "Ioss_Map.h" +#include "Ioss_PropertyManager.h" +#include "Ioss_Region.h" #include "Ioss_SmartAssert.h" #include "Ioss_Sort.h" #include "Ioss_Utils.h" @@ -39,8 +43,8 @@ #endif namespace { - // ZOLTAN Callback functions... + // ZOLTAN Callback functions... #if !defined(NO_ZOLTAN_SUPPORT) int zoltan_num_dim(void *data, int *ierr) { @@ -79,7 +83,12 @@ namespace { } if (wdim != 0) { - std::fill(wgts, wgts + element_count, 1.0); + if (zdata->weights().empty()) { + std::fill(wgts, wgts + element_count, 1.0); + } + else { + std::copy(zdata->weights().begin(), zdata->weights().end(), &wgts[0]); + } } if (ngid_ent == 1) { @@ -107,6 +116,7 @@ namespace { *ierr = ZOLTAN_OK; } #endif + } // namespace namespace Ioex { @@ -125,7 +135,8 @@ namespace Ioex { m_processorCount = pu.parallel_size(); } - template void DecompositionData::decompose_model(int filePtr) + template + void DecompositionData::decompose_model(int filePtr, const std::string &filename) { m_decomposition.show_progress(__func__); // Initial decomposition is linear where processor #p contains @@ -191,8 +202,16 @@ namespace Ioex { for (int i = 0; i < map_count; i++) { if (std::string(names[i]) == map_name) { m_decomposition.m_elementToProc.resize(decomp_elem_count()); - ex_get_partial_num_map(filePtr, EX_ELEM_MAP, i + 1, decomp_elem_offset() + 1, - decomp_elem_count(), Data(m_decomposition.m_elementToProc)); + if (sizeof(INT) == 4) { + ex_get_partial_num_map(filePtr, EX_ELEM_MAP, i + 1, decomp_elem_offset() + 1, + decomp_elem_count(), Data(m_decomposition.m_elementToProc)); + } + else { + std::vector tmp_map(decomp_elem_count()); + ex_get_partial_num_map(filePtr, EX_ELEM_MAP, i + 1, decomp_elem_offset() + 1, + decomp_elem_count(), Data(tmp_map)); + std::copy(tmp_map.begin(), tmp_map.end(), m_decomposition.m_elementToProc.begin()); + } map_read = true; break; } @@ -256,6 +275,55 @@ namespace Ioex { } } + if (m_decomposition.m_lineDecomp) { + // For first iteration of this, we do the line-decomp modified decomposition on a single rank + // and then communicate the m_elementToProc vector to each of the ranks. This is then used + // do do the parallel distributions/decomposition of the elements assuming a "guided" + // decomposition. + std::vector element_to_proc_global{}; + + m_decomposition.show_progress("***LINE_DECOMPOSE BEGIN***"); + if (m_processor == 0) { + Ioss::PropertyManager properties; + Ioss::DatabaseIO *dbi = Ioss::IOFactory::create( + "exodus", filename, Ioss::READ_RESTART, Ioss::ParallelUtils::comm_self(), properties); + Ioss::Region region(dbi, "line_decomp_region"); + + int status = Ioss::DecompUtils::line_decompose( + region, m_processorCount, m_decomposition.m_method, m_decomposition.m_decompExtra, + element_to_proc_global, INT(0)); + + if (m_decomposition.m_showHWM || m_decomposition.m_showProgress) { + Ioss::DecompUtils::output_decomposition_statistics(element_to_proc_global, m_processorCount); + } + } + // Now broadcast the parts of the `element_to_proc_global` + // vector to the owning ranks in the initial linear + // decomposition... + + std::vector sendcounts(m_processorCount); + std::vector displs(m_processorCount); + m_decomposition.m_elementToProc.resize(decomp_elem_count()); + + // calculate send counts and displacements + int sum = 0; + int rem = globalElementCount % m_processorCount; + for (int i = 0; i < m_processorCount; i++) { + sendcounts[i] = globalElementCount / m_processorCount; + if (rem > 0) { + sendcounts[i]++; + rem--; + } + displs[i] = sum; + sum += sendcounts[i]; + } + MPI_Scatterv(Data(element_to_proc_global), Data(sendcounts), Data(displs), MPI_INT, + Data(m_decomposition.m_elementToProc), decomp_elem_count(), MPI_INT, 0, + m_decomposition.m_comm); + m_decomposition.m_method = "SPECIFIED"; + m_decomposition.show_progress("***LINE_DECOMPOSE END***"); + } + #if !defined(NO_ZOLTAN_SUPPORT) float version = 0.0; Zoltan_Initialize(0, nullptr, &version); diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.h index 651476b517..5faa8dc3c2 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.h @@ -50,11 +50,11 @@ namespace Ioex { DecompositionDataBase(const DecompositionDataBase &) = delete; DecompositionDataBase &operator=(const DecompositionDataBase &) = delete; - virtual ~DecompositionDataBase() = default; - IOSS_NODISCARD virtual int int_size() const = 0; - virtual void decompose_model(int filePtr) = 0; - IOSS_NODISCARD virtual size_t ioss_node_count() const = 0; - IOSS_NODISCARD virtual size_t ioss_elem_count() const = 0; + virtual ~DecompositionDataBase() = default; + IOSS_NODISCARD virtual int int_size() const = 0; + virtual void decompose_model(int filePtr, const std::string &filename) = 0; + IOSS_NODISCARD virtual size_t ioss_node_count() const = 0; + IOSS_NODISCARD virtual size_t ioss_elem_count() const = 0; IOSS_NODISCARD virtual int spatial_dimension() const = 0; IOSS_NODISCARD virtual size_t global_node_count() const = 0; @@ -66,6 +66,7 @@ namespace Ioex { IOSS_NODISCARD virtual size_t decomp_elem_count() const = 0; IOSS_NODISCARD virtual std::vector ¢roids() = 0; + IOSS_NODISCARD virtual std::vector &weights() = 0; Ioss_MPI_Comm comm_; @@ -120,7 +121,7 @@ namespace Ioex { IOSS_NODISCARD int int_size() const { return sizeof(INT); } - void decompose_model(int filePtr); + void decompose_model(int filePtr, const std::string &filename); IOSS_NODISCARD int spatial_dimension() const { return m_decomposition.m_spatialDimension; } @@ -136,6 +137,7 @@ namespace Ioex { IOSS_NODISCARD size_t decomp_elem_count() const { return m_decomposition.file_elem_count(); } IOSS_NODISCARD std::vector ¢roids() { return m_decomposition.m_centroids; } + IOSS_NODISCARD std::vector &weights() { return m_decomposition.m_weights; } template void communicate_element_data(T *file_data, T *ioss_data, size_t comp_count) const diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C index f819428074..b0d9f4304d 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C @@ -356,6 +356,14 @@ namespace { return total_data_size; } + void add_processor_id_map(Ioss::Region *region) + { + const auto &blocks = region->get_element_blocks(); + for (const auto &block : blocks) { + block->field_add(Ioss::Field("proc_id", block->field_int_type(), "scalar", Ioss::Field::MAP)); + } + } + } // namespace namespace Ioex { @@ -779,11 +787,11 @@ namespace Ioex { decomp = std::make_unique>(properties, util().communicator()); } assert(decomp != nullptr); - decomp->decompose_model(exoid); + decomp->decompose_model(exoid, get_filename()); read_region(); - Ioex::read_exodus_basis(get_file_pointer()); - Ioex::read_exodus_quadrature(get_file_pointer()); + Ioex::read_exodus_basis(exoid); + Ioex::read_exodus_quadrature(exoid); get_elemblocks(); @@ -4797,6 +4805,16 @@ namespace Ioex { return num_to_get; } + template + void ParallelDatabaseIO::output_processor_id_map(Ioss::Region *region, INT /*dummy*/) + { + std::vector proc_id(elementCount, myProcessor); + const auto &blocks = region->get_element_blocks(); + for (const auto &block : blocks) { + put_field_internal(block, block->get_field("proc_id"), Data(proc_id), -1); + } + } + void ParallelDatabaseIO::write_meta_data(Ioss::IfDatabaseExistsBehavior behavior) { Ioss::Region *region = get_region(); @@ -4849,7 +4867,14 @@ namespace Ioex { if (behavior != Ioss::DB_APPEND && behavior != Ioss::DB_MODIFY) { output_node_map(); + add_processor_id_map(region); output_other_metadata(); + if (int_byte_size_api() == 8) { + output_processor_id_map(region, int64_t(0)); + } + else { + output_processor_id_map(region, int(0)); + } } } diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.h index b61bead2ef..2ab679de54 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.h @@ -197,6 +197,8 @@ namespace Ioex { void write_entity_transient_field(const Ioss::Field &field, const Ioss::GroupingEntity *ge, int64_t count, void *variables) const; void write_meta_data(Ioss::IfDatabaseExistsBehavior behavior) override; + template + void output_processor_id_map(Ioss::Region *region, INT /*dummy*/); // Read related metadata and store it in the region... void read_region(); diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.C b/packages/seacas/libraries/ioss/src/main/shell_interface.C index ce293550ea..02724f4781 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.C +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.C @@ -207,12 +207,10 @@ void IOShell::Interface::enroll_options() "Files are decomposed externally into a file-per-processor in a parallel run.", nullptr); -#if defined(SEACAS_HAVE_CGNS) options_.enroll( "add_processor_id_field", Ioss::GetLongOption::NoValue, - "For CGNS, add a cell-centered field whose value is the processor id of that cell", nullptr); -#endif - + "Add a cell-centered field whose value is the processor id of that cell", nullptr); + options_.enroll("serialize_io_size", Ioss::GetLongOption::MandatoryValue, "Number of processors that can perform simultaneous IO operations in " "a parallel run; 0 to disable", @@ -510,9 +508,7 @@ bool IOShell::Interface::parse_options(int argc, char **argv, int my_processor) } #if defined(SEACAS_HAVE_MPI) -#if defined(SEACAS_HAVE_CGNS) add_processor_id_field = (options_.retrieve("add_processor_id_field") != nullptr); -#endif #if !defined(NO_ZOLTAN_SUPPORT) if (options_.retrieve("rcb") != nullptr) { From be88dca91491f8a78031f302f111263fe8e74e10 Mon Sep 17 00:00:00 2001 From: akimler <160887023+akimler@users.noreply.github.com> Date: Tue, 11 Jun 2024 16:46:59 -0600 Subject: [PATCH 28/59] Correct Jamfile to match Seacas's CMake Library Names (#460) * Correct Jamfile to match Seacas's CMake Library Names Jamfile change for Sierra's build system to support a spack built version of Seacas. This change correctly matches Seacas library names to CMake's version of Seacas's libraries and changes the path name to 'seacas' rather than 'sierra_seacas' Signed-off-by: akimler <160887023+akimler@users.noreply.github.com> * Update Jamfile Fixed accidently removed Jamfile changes Signed-off-by: akimler <160887023+akimler@users.noreply.github.com> * Update Jamfile Remove whitespace Signed-off-by: akimler <160887023+akimler@users.noreply.github.com> --------- Signed-off-by: akimler <160887023+akimler@users.noreply.github.com> --- packages/seacas/Jamfile | 246 ++++++++++++++++++++-------------------- 1 file changed, 124 insertions(+), 122 deletions(-) diff --git a/packages/seacas/Jamfile b/packages/seacas/Jamfile index b9010fe49d..52e6d8693e 100644 --- a/packages/seacas/Jamfile +++ b/packages/seacas/Jamfile @@ -174,7 +174,7 @@ install install-user-include0 explicit install-user-include1 ; install install-user-include1 : [ glob $(seacas-root)/libraries/suplib_c/*.h ] - : $(install-root)/sierra_seacas/include + : $(install-root)/seacas/include ; # This rule copies all headers it finds in the named directory of the @@ -183,14 +183,14 @@ install install-user-include1 explicit install-user-include2 ; install install-user-include2 : [ path.glob-tree $(seacas-root)/libraries/exodus/include : *.h ] - : $(install-root)/sierra_seacas/include/exodus + : $(install-root)/seacas/include/exodus $(seacas-root)/libraries/exodus/include ; explicit install-user-include2b ; install install-user-include2b : [ path.glob-tree $(seacas-root)/libraries/exodus/sierra : *.h ] - : $(install-root)/sierra_seacas/include/exodus + : $(install-root)/seacas/include/exodus $(seacas-root)/libraries/exodus/sierra ; @@ -200,21 +200,21 @@ install install-user-include2b explicit install-user-include3 ; install install-user-include3 : [ path.glob-tree $(seacas-root)/libraries/nemesis : *.h ] - : $(install-root)/sierra_seacas/include/nemesis + : $(install-root)/seacas/include/nemesis $(seacas-root)/libraries/nemesis ; explicit install-user-include4 ; install install-user-include4 : [ path.glob-tree $(seacas-root)/libraries/exodus_for/include : *.inc ] - : $(install-root)/sierra_seacas/include/exodus + : $(install-root)/seacas/include/exodus $(seacas-root)/libraries/exodus_for/include ; explicit install-user-include5 ; install install-user-include5 : [ glob $(seacas-root)/libraries/suplib_cpp/*.h ] - : $(install-root)/sierra_seacas/include + : $(install-root)/seacas/include ; explicit install-user-include6 ; @@ -229,7 +229,7 @@ install install-user-include6 explicit install-user-include7 ; install install-user-include7 : [ glob $(seacas-root)/libraries/aprepro_lib/*.h ] - : $(install-root)/sierra_seacas/include + : $(install-root)/seacas/include ; @@ -241,7 +241,7 @@ alias install-user-env : install-user-jamfile explicit install-user-jamfile ; install install-user-jamfile : [ glob $(seacas-root)/Jamfile ] - : $(install-root)/sierra_seacas + : $(install-root)/seacas $(seacas-root) ; @@ -262,21 +262,21 @@ install install-user-lib fastqlib mapvarlib aprepro_lib - ioex - ioexnl + Ioex + Ioexnl io_info_lib - iocgns - iopg - iohb - iogn - iogs - ionull - ioinit - ioss - iotr - iotm - iovs - : $(install-root)/sierra_seacas/lib + Iocgns + Iopg + Iohb + Iogn + Iogs + Ionull + Ionit + Ioss + Iotr + Iotm + Iovs + : $(install-root)/seacas/lib ; @@ -305,14 +305,14 @@ alias install-exe-targets : ; # # Libs in seacas/ioss.... -lib ioex +lib Ioex : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/exodus/*.[Cc] ] ] - ioss + Ioss exodus /tpl/netcdf-c//netcdf /tpl/trilinos//zoltan @@ -321,24 +321,24 @@ lib ioex [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libioex.a + $(seacas-root)/lib/libIoex.a ] ; -lib ioexnl +lib Ioexnl : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/exonull/*.[C] ] ] - ioss + Ioss exodus : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libioexnl.a + $(seacas-root)/lib/libIoexnl.a ] ; @@ -351,8 +351,8 @@ lib io_info_lib $(seacas-root)/libraries/ioss/src/main/volume.C $(seacas-root)/libraries/ioss/src/main/info_interface.C ] - ioinit - iocgns + Ionit + Iocgns /mpi//mpi : [ ifuserbuild @@ -362,152 +362,152 @@ lib io_info_lib ] ; -lib iopg +lib Iopg : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/pamgen/*.C ] ] - ioss + Ioss /mpi//mpi /tpl/trilinos//pamgen : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libiopg.a + $(seacas-root)/lib/libIopg.a ] ; -lib iocgns +lib Iocgns : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/cgns/*.C ] ] - ioss + Ioss /mpi//mpi /tpl/cgns//cgns : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libiocgns.a + $(seacas-root)/lib/libIocgns.a ] ; -lib iohb +lib Iohb : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/heartbeat/*.C ] ] - ioss + Ioss /mpi//mpi : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libiohb.a + $(seacas-root)/lib/libIohb.a ] ; -lib iogn +lib Iogn : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/generated/*.C ] ] - ioss + Ioss /mpi//mpi : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libiogn.a + $(seacas-root)/lib/libIogn.a ] ; -lib ionull +lib Ionull : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/null/*.C ] ] - ioss + Ioss /mpi//mpi : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libionull.a + $(seacas-root)/lib/libIonull.a ] ; -lib iotm +lib Iotm : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/text_mesh/*.C ] ] - ioss + Ioss /mpi//mpi : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libiotm.a + $(seacas-root)/lib/libIotm.a ] ; -lib iogs +lib Iogs : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/gen_struc/*.C ] ] - ioss + Ioss /mpi//mpi : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libiogs.a + $(seacas-root)/lib/libIogs.a ] ; -lib ioinit +lib Ionit : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/init/*.C ] ] - iocgns - ioex - ioexnl - ionull - iopg - iohb - iogn - iogs - iotm - iotr - iovs + Iocgns + Ioex + Ioexnl + Ionull + Iopg + Iohb + Iogn + Iogs + Iotm + Iotr + Iovs /mpi//mpi : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libioinit.a + $(seacas-root)/lib/libIonit.a ] ; -lib ioss +lib Ioss : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development @@ -523,30 +523,30 @@ lib ioss [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libioss.a + $(seacas-root)/lib/libIoss.a ] : : BUILT_IN_SIERRA ; -lib iotr +lib Iotr : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. [ glob $(seacas-root)/libraries/ioss/src/transform/*.C ] ] - ioss + Ioss /mpi//mpi : [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libiotr.a + $(seacas-root)/lib/libIotr.a ] ; -lib iovs +lib Iovs : [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development @@ -555,7 +555,7 @@ lib iovs $(seacas-root)/libraries/ioss/src/visualization/cgns/*.C $(seacas-root)/libraries/ioss/src/visualization/utils/*.C ] ] - ioss + Ioss : shared:IOSS_DLOPEN_ENABLED [ ifdevbuild @@ -569,7 +569,7 @@ lib iovs [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(seacas-root)/lib/libiovs.a + $(seacas-root)/lib/libIovs.a ] ; @@ -624,7 +624,7 @@ lib exodus [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libexodus.a + $(sierra-root)/seacas/lib/libexodus.a ] : : BUILT_IN_SIERRA @@ -637,7 +637,7 @@ lib exodus [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include/exodus + $(sierra-root)/seacas/include/exodus ] ; @@ -680,7 +680,7 @@ lib exodus_for [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libexodus_for.a + $(sierra-root)/seacas/lib/libexodus_for.a ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -703,7 +703,7 @@ lib exodus_for [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include/exodus + $(sierra-root)/seacas/include/exodus ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -737,7 +737,7 @@ lib exoIIv2for32 [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libexoIIv2for32.a + $(sierra-root)/seacas/lib/libexoIIv2for32.a ] DEFAULT_REAL_INT : @@ -751,7 +751,7 @@ lib exoIIv2for32 [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include/exodus + $(sierra-root)/seacas/include/exodus ] DEFAULT_REAL_INT ; @@ -774,7 +774,7 @@ lib nemesis [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libnemesis.a + $(sierra-root)/seacas/lib/libnemesis.a ] : : @@ -786,7 +786,7 @@ lib nemesis [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include/nemesis + $(sierra-root)/seacas/include/nemesis ] ; @@ -810,7 +810,7 @@ lib suplib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -833,7 +833,7 @@ lib suplib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -865,7 +865,7 @@ lib suplib_cpp [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] : : @@ -877,7 +877,7 @@ lib suplib_cpp [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] ; @@ -899,7 +899,7 @@ lib suplib_c [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] : : @@ -911,7 +911,7 @@ lib suplib_c [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] ; @@ -932,7 +932,7 @@ lib plt [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libplt.a + $(sierra-root)/seacas/lib/libplt.a ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -955,7 +955,7 @@ lib plt [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -989,7 +989,7 @@ lib svdi_cdr [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libsvdi_cdr.a + $(sierra-root)/seacas/lib/libsvdi_cdr.a ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1012,7 +1012,7 @@ lib svdi_cdr [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1056,7 +1056,7 @@ lib svdi_cgi [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libsvdi_cgi.a + $(sierra-root)/seacas/lib/libsvdi_cgi.a ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1079,7 +1079,7 @@ lib svdi_cgi [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1172,7 +1172,7 @@ lib blotlib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libblotlib.a + $(sierra-root)/seacas/lib/libblotlib.a ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1190,7 +1190,7 @@ lib blotlib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1222,7 +1222,7 @@ lib fastqlib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libfastqlib.a + $(sierra-root)/seacas/lib/libfastqlib.a ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1240,7 +1240,7 @@ lib fastqlib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1274,7 +1274,7 @@ lib mapvarlib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libmapvarlib.a + $(sierra-root)/seacas/lib/libmapvarlib.a ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1297,7 +1297,7 @@ lib mapvarlib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1331,7 +1331,7 @@ lib supes [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libsupes.a + $(sierra-root)/seacas/lib/libsupes.a ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1354,7 +1354,7 @@ lib supes [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] darwin,64:"-fdefault-real-8 -fdefault-integer-8" gcc,64:"-fdefault-real-8 -fdefault-integer-8" @@ -1385,6 +1385,7 @@ lib aprepro_lib /tpl/netcdf-c//netcdf /tpl/fmt//fmt : EXODUS_SUPPORT + FMT_SUPPORT [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. @@ -1393,10 +1394,11 @@ lib aprepro_lib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libaprepro_lib.a + $(sierra-root)/seacas/lib/libaprepro_lib.a ] : : EXODUS_SUPPORT + FMT_SUPPORT [ ifdevbuild # Any parameters within this 'ifdevbuild' block apply to development # builds only and will not be present for user builds. @@ -1405,7 +1407,7 @@ lib aprepro_lib [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] ; @@ -1446,7 +1448,7 @@ lib chaco [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/lib/libchaco.a + $(sierra-root)/seacas/lib/libchaco.a ] : : @@ -1458,7 +1460,7 @@ lib chaco [ ifuserbuild # Any parameters within this 'ifuserbuild' block apply to user # builds only and will not be present for developer builds. - $(sierra-root)/sierra_seacas/include + $(sierra-root)/seacas/include ] ; @@ -1498,7 +1500,7 @@ exe slice $(seacas-root)/applications/slice/SL_Decompose.C suplib_cpp suplib_c - ioinit + Ionit : $(seacas-root)/applications/slice USE_METIS USE_ZOLTAN @@ -1515,7 +1517,7 @@ exe ejoin $(seacas-root)/applications/ejoin/EJ_vector3d.C suplib_cpp suplib_c - ioinit + Ionit : $(seacas-root)/applications/ejoin @utility-exec-tag ; @@ -1552,7 +1554,7 @@ exe exomatlab $(seacas-root)/applications/exomatlab/EML_SystemInterface.C suplib_cpp suplib_c - ioinit + Ionit exodus : $(seacas-root)/applications/exomatlab @utility-exec-tag @@ -1758,7 +1760,7 @@ exe io_shell : $(seacas-root)/libraries/ioss/src/main/io_shell.C $(seacas-root)/libraries/ioss/src/main/shell_interface.C - ioinit + Ionit /mpi//mpi : @sierra-exec-tag @@ -1767,7 +1769,7 @@ exe io_shell exe cgns_decomp : $(seacas-root)/libraries/ioss/src/main/cgns_decomp.C - ioinit + Ionit /mpi//mpi : @sierra-exec-tag @@ -1786,8 +1788,8 @@ exe io_modify : $(seacas-root)/libraries/ioss/src/main/io_modify.C $(seacas-root)/libraries/ioss/src/main/modify_interface.C - ioinit - ioex + Ionit + Ioex /mpi//mpi : @sierra-exec-tag @@ -1797,7 +1799,7 @@ exe shell_to_hex : $(seacas-root)/libraries/ioss/src/main/shell_to_hex.C $(seacas-root)/libraries/ioss/src/main/vector3d.C - ioinit + Ionit /mpi//mpi : @sierra-exec-tag ; @@ -1806,7 +1808,7 @@ exe cth_pressure_map : $(seacas-root)/libraries/ioss/src/main/cth_pressure_map.C $(seacas-root)/libraries/ioss/src/main/vector3d.C - ioinit + Ionit /mpi//mpi : @sierra-exec-tag ; @@ -1814,7 +1816,7 @@ exe cth_pressure_map exe struc_to_unstruc : $(seacas-root)/libraries/ioss/src/main/struc_to_unstruc.C - ioinit + Ionit /mpi//mpi : @sierra-exec-tag ; @@ -1822,7 +1824,7 @@ exe struc_to_unstruc exe Utst_ioel : $(seacas-root)/libraries/ioss/src/utest/Utst_ioel.C - ioss + Ioss /mpi//mpi : @sierra-exec-tag ; @@ -1830,7 +1832,7 @@ exe Utst_ioel exe Utst_sort : $(seacas-root)/libraries/ioss/src/utest/Utst_sort.C - ioss + Ioss /mpi//mpi : @sierra-exec-tag ; @@ -1838,7 +1840,7 @@ exe Utst_sort exe Utst_map : $(seacas-root)/libraries/ioss/src/utest/Utst_map.C - ioss + Ioss /mpi//mpi : @sierra-exec-tag ; @@ -1846,7 +1848,7 @@ exe Utst_map exe Utst_structured_decomp : $(seacas-root)/libraries/ioss/src/utest/Utst_structured_decomp.C - iocgns + Iocgns /mpi//mpi : off @sierra-exec-tag ; @@ -1854,7 +1856,7 @@ exe Utst_structured_decomp exe Utst_superelement : $(seacas-root)/libraries/ioss/src/utest/Utst_superelement.C - ioex + Ioex : @sierra-exec-tag ; @@ -1862,7 +1864,7 @@ exe Utst_superelement exe Utst_database_io : [ glob $(seacas-root)/libraries/ioss/src/utest/Utst_IofxDatabaseIO.C ] - ioinit + Ionit /tpl/netcdf-c//netcdf /tpl/googletest//gtest : @utility-exec-tag @@ -1871,8 +1873,8 @@ exe Utst_database_io exe Utst_textmesh : [ glob $(seacas-root)/libraries/ioss/src/unit_tests/*.C ] - ioinit - iotm + Ionit + Iotm /tpl/netcdf-c//netcdf /tpl/googletest//gtest /mpi//mpi From cbaeaa28752c84505c745fe2245ee7326b2c0326 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 12 Jun 2024 12:23:59 -0600 Subject: [PATCH 29/59] IOSS: Reduce memory usage for line decomp --- .../ioss/src/exodus/Ioex_DecompositionData.C | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C index 734316cf2c..7acf965b69 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C @@ -154,7 +154,9 @@ namespace Ioex { // processor p contains all elements/nodes from X_dist[p] .. X_dist[p+1] m_decomposition.generate_entity_distributions(globalNodeCount, globalElementCount); - generate_adjacency_list(filePtr, m_decomposition); + if (!m_decomposition.m_lineDecomp) { + generate_adjacency_list(filePtr, m_decomposition); + } #if IOSS_DEBUG_OUTPUT fmt::print(Ioss::DebugOut(), "Processor {} has {} elements; offset = {}\n", m_processor, @@ -163,7 +165,7 @@ namespace Ioex { fmt::group_digits(decomp_node_count()), fmt::group_digits(decomp_node_offset())); #endif - if (m_decomposition.needs_centroids()) { + if (!m_decomposition.m_lineDecomp && m_decomposition.needs_centroids()) { // Get my coordinate data using direct exodus calls size_t size = decomp_node_count(); if (size == 0) { @@ -324,6 +326,12 @@ namespace Ioex { m_decomposition.show_progress("***LINE_DECOMPOSE END***"); } + if (m_decomposition.m_lineDecomp) { + // Do not combine into previous if block since we want to release memory for + // the local vectors in that block before allocating the large adjacency vector. + generate_adjacency_list(filePtr, m_decomposition); + } + #if !defined(NO_ZOLTAN_SUPPORT) float version = 0.0; Zoltan_Initialize(0, nullptr, &version); From 5693dde3e39089287514a2e65ae55e84f081bf71 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 12 Jun 2024 13:07:30 -0600 Subject: [PATCH 30/59] IOSS: Add line_decomp io_shell option; clean up output --- .../libraries/ioss/src/Ioss_Decomposition.C | 10 +++++--- .../ioss/src/Ioss_DecompositionUtils.C | 4 ++-- .../ioss/src/exodus/Ioex_DecompositionData.C | 2 +- .../seacas/libraries/ioss/src/main/io_shell.C | 3 +++ .../libraries/ioss/src/main/shell_interface.C | 24 ++++++++++++++----- .../libraries/ioss/src/main/shell_interface.h | 1 + 6 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C index 48df8029e0..1deac79a93 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C @@ -426,6 +426,10 @@ namespace Ioss { "\nIOSS: Using decomposition method '{}' for {} elements on {} mpi ranks.\n", m_method, fmt::group_digits(m_globalElementCount), m_processorCount); + if (!m_decompExtra.empty()) { + fmt::print(Ioss::OUTPUT(), "\tDecomposition extra data: '{}'.\n", m_decompExtra); + } + if ((size_t)m_processorCount > m_globalElementCount) { fmt::print(Ioss::WarnOut(), "Decomposing {} elements across {} mpi ranks will " @@ -459,7 +463,7 @@ namespace Ioss { if (m_method == "MAP") { guided_decompose(); } - if (m_method == "SPECIFIED") { + if (m_method == "LINE_DECOMP") { // Currently used for line decomposition with another decomposition type. // The line-modified decomposition is done prior to this and builds the // `m_elementToProc` which is then used here to decompose the elements... @@ -679,7 +683,7 @@ namespace Ioss { template void Decomposition::guided_decompose() { show_progress(__func__); - assert(m_method == "MAP" || m_method == "VARIABLE" || m_method == "SPECIFIED"); + assert(m_method == "MAP" || m_method == "VARIABLE" || m_method == "LINE_DECOMP"); // - Read my portion of the map / variable. // - count # of exports to each rank // -- exportElementCount[proc] @@ -706,7 +710,7 @@ namespace Ioss { // [0..m_processorCount). double scale = 1.0; auto pos = m_decompExtra.find(","); - if (m_method != "SPECIFIED" && pos != std::string::npos) { + if (m_method != "LINE_DECOMP" && pos != std::string::npos) { // Extract the string following the comma... auto scale_str = m_decompExtra.substr(pos + 1); if (scale_str == "AUTO" || scale_str == "auto") { diff --git a/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C index b065b56154..bec6473e01 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C +++ b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C @@ -270,7 +270,7 @@ namespace Ioss { float ver = 0.0; Zoltan_Initialize(argc, argv, &ver); - fmt::print("Using Zoltan version {:.2}, method {}\n", static_cast(ver), method); + fmt::print("\tUsing Zoltan version {:.2}, method {}\n", static_cast(ver), method); Zoltan zz(Ioss::ParallelUtils::comm_self()); @@ -354,7 +354,7 @@ namespace Ioss { decompose_zoltan(region, num_ranks, method, element_to_proc, weights, false, false, false, dummy); double end = Ioss::Utils::timer(); - fmt::print(stderr, "Decompose elements = {:.5}\n", end - start); + fmt::print(stderr, "\tDecompose elements = {:.5}\n", end - start); region.get_database()->progress("exit decompose_elements"); // Make sure all elements on a chain are on the same processor rank... diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C index 7acf965b69..ee5a8b68bd 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DecompositionData.C @@ -322,7 +322,7 @@ namespace Ioex { MPI_Scatterv(Data(element_to_proc_global), Data(sendcounts), Data(displs), MPI_INT, Data(m_decomposition.m_elementToProc), decomp_elem_count(), MPI_INT, 0, m_decomposition.m_comm); - m_decomposition.m_method = "SPECIFIED"; + m_decomposition.m_method = "LINE_DECOMP"; m_decomposition.show_progress("***LINE_DECOMPOSE END***"); } diff --git a/packages/seacas/libraries/ioss/src/main/io_shell.C b/packages/seacas/libraries/ioss/src/main/io_shell.C index 1673575195..bba91946c7 100644 --- a/packages/seacas/libraries/ioss/src/main/io_shell.C +++ b/packages/seacas/libraries/ioss/src/main/io_shell.C @@ -715,6 +715,9 @@ namespace { if (interFace.decomp_method == "MAP" || interFace.decomp_method == "VARIABLE") { properties.add(Ioss::Property("DECOMPOSITION_EXTRA", interFace.decomp_extra)); } + if (interFace.line_decomp) { + properties.add(Ioss::Property("LINE_DECOMPOSITION", interFace.decomp_extra)); + } } if (interFace.retain_empty_blocks) { diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.C b/packages/seacas/libraries/ioss/src/main/shell_interface.C index 02724f4781..1bd0003843 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.C +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.C @@ -203,6 +203,14 @@ void IOShell::Interface::enroll_options() "\t\t(if auto) by `int((max_entry+1)/proc_count)`.", nullptr); + options_.enroll("line_decomp", Ioss::GetLongOption::OptionalValue, + "Generate the `lines` or `columns` of elements from the specified surface(s).\n" + "\t\tSpecify a comma-separated list of surface/sideset names from which the " + "lines will grow.\n" + "\t\tDo not split a line/column across processors.\n" + "\t\tOmit or enter 'ALL' for all surfaces in model.", + nullptr, "ALL"); + options_.enroll("external", Ioss::GetLongOption::NoValue, "Files are decomposed externally into a file-per-processor in a parallel run.", nullptr); @@ -213,7 +221,7 @@ void IOShell::Interface::enroll_options() options_.enroll("serialize_io_size", Ioss::GetLongOption::MandatoryValue, "Number of processors that can perform simultaneous IO operations in " - "a parallel run; 0 to disable", + "a parallel run;\n\t\t0 to disable", nullptr, nullptr, true); #endif @@ -226,7 +234,7 @@ void IOShell::Interface::enroll_options() nullptr); options_.enroll("split_cyclic", Ioss::GetLongOption::MandatoryValue, - "If non-zero, then the `split_times` timesteps will be put into <$val> files and " + "If non-zero, then the `split_times` timesteps will be put into <$val> files\n\t\tand " "then recycle filenames.", nullptr); @@ -274,7 +282,7 @@ void IOShell::Interface::enroll_options() options_.enroll("field_suffix_separator", Ioss::GetLongOption::MandatoryValue, "Character used to separate a field suffix from the field basename\n" - "\t\t when recognizing vector, tensor fields. Enter '0' for no separator", + "\t\twhen recognizing vector, tensor fields. Enter '0' for no separator", "_"); options_.enroll("disable_field_recognition", Ioss::GetLongOption::NoValue, @@ -311,9 +319,8 @@ void IOShell::Interface::enroll_options() nullptr); options_.enroll("omit_sets", Ioss::GetLongOption::MandatoryValue, - "comma-separated list of nodeset/edgeset/faceset/elemset/sideset names that " - "should NOT be transferred to " - "output database", + "comma-separated list of nodeset/edgeset/faceset/elemset/sideset names\n" + "\t\tthat should NOT be transferred to output database", nullptr); options_.enroll("boundary_sideset", Ioss::GetLongOption::NoValue, @@ -552,6 +559,11 @@ bool IOShell::Interface::parse_options(int argc, char **argv, int my_processor) decomp_extra = options_.get_option_value("variable", decomp_extra); } + if (options_.retrieve("line_decomp") != nullptr) { + line_decomp = true; + decomp_extra = options_.get_option_value("line_decomp", decomp_extra); + } + if (options_.retrieve("cyclic") != nullptr) { decomp_method = "CYCLIC"; } diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.h b/packages/seacas/libraries/ioss/src/main/shell_interface.h index 36c77f38e1..a391174c7a 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.h +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.h @@ -113,6 +113,7 @@ namespace IOShell { bool ignore_face_map{false}; bool delete_qa{false}; bool delete_info{false}; + bool line_decomp{false}; char fieldSuffixSeparator{'_'}; }; } // namespace IOShell From fc44446d51ac21c03a3c654246bd2db31575fa30 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 12 Jun 2024 17:28:00 -0600 Subject: [PATCH 31/59] IOSS: Reduce memory usage of line decomp --- .../libraries/ioss/src/Ioss_ChainGenerator.C | 3 +- .../libraries/ioss/src/Ioss_FaceGenerator.C | 43 ++++++++++++------- .../libraries/ioss/src/Ioss_FaceGenerator.h | 12 ++++-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C index 92d762eae9..c2d976f04c 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C @@ -195,7 +195,6 @@ namespace Ioss { { debug = debug_level; size_t numel = region.get_property("element_count").get_int(); - Ioss::chain_t element_chains(numel); // Determine which element block(s) are adjacent to the faceset specifying "lines" // The `adjacent_blocks` contains the names of all element blocks that are adjacent to the @@ -225,6 +224,7 @@ namespace Ioss { Ioss::FaceGenerator face_generator(region); face_generator.generate_block_faces(adjacent_blocks, (INT)0, true); + Ioss::chain_t element_chains(numel); for (const auto *block : adjacent_blocks) { // Get the offset into the element_chains vector... auto offset = block->get_offset() + 1; @@ -239,6 +239,7 @@ namespace Ioss { connectivity_t face_connectivity(count); generate_face_connectivity(face_generator.faces(block), static_cast(offset), face_connectivity); + face_generator.clear(block); // For each face on the "front" (at the beginning the boundary sideset faces) // Set `element_chains` to the `face` "ID" diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C index b167d1aae8..04bd1effab 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C @@ -69,11 +69,7 @@ namespace { eb->get_field_data("connectivity_raw", connectivity); std::vector elem_ids; - if (local_ids) { - elem_ids.resize(eb->entity_count()); - std::iota(elem_ids.begin(), elem_ids.end(), static_cast(eb->get_offset() + 1)); - } - else { + if (!local_ids) { eb->get_field_data("ids", elem_ids); } @@ -90,6 +86,7 @@ namespace { size_t num_elem = eb->entity_count(); for (size_t elem = 0, offset = 0; elem < num_elem; elem++, offset += num_node_per_elem) { + auto elem_id = local_ids ? eb->get_offset() + elem + 1 : elem_ids[elem]; for (int face = 0; face < num_face_per_elem; face++) { size_t id = 0; assert(face_node_count[face] <= 4); @@ -97,10 +94,10 @@ namespace { for (int j = 0; j < face_node_count[face]; j++) { size_t fnode = offset + face_conn[face][j]; size_t lnode = connectivity[fnode]; // local since "connectivity_raw" - conn[j] = ids[lnode - 1]; // Convert to global + conn[j] = local_ids ? lnode : ids[lnode - 1]; // Convert to global id += hash_ids[lnode - 1]; } - create_face(faces, id, conn, elem_ids[elem], face); + create_face(faces, id, conn, elem_id, face); } } } @@ -333,6 +330,12 @@ namespace Ioss { return faces_[name]; } + void FaceGenerator::clear(const Ioss::ElementBlock *block) + { + auto name = block->name(); + faces_[name].clear(); + } + template IOSS_EXPORT void FaceGenerator::generate_faces(int, bool, bool); template IOSS_EXPORT void FaceGenerator::generate_faces(int64_t, bool, bool); @@ -356,6 +359,14 @@ namespace Ioss { } } + void FaceGenerator::hash_local_node_ids(size_t count) + { + hashIds_.reserve(count); + for (size_t i = 1; i <= count; i++) { + hashIds_.push_back(id_hash(i)); + } + } + template IOSS_EXPORT void FaceGenerator::generate_block_faces(const Ioss::ElementBlockContainer &, int, bool); template IOSS_EXPORT void FaceGenerator::generate_block_faces(const Ioss::ElementBlockContainer &, @@ -368,27 +379,27 @@ namespace Ioss { // Convert ids into hashed-ids Ioss::NodeBlock *nb = region_.get_node_blocks()[0]; +#if DO_TIMING + auto starth = std::chrono::steady_clock::now(); +#endif std::vector ids; if (local_ids) { - ids.resize(nb->entity_count()); - std::iota(ids.begin(), ids.end(), 1); + hash_local_node_ids(nb->entity_count()); } else { nb->get_field_data("ids", ids); + hash_node_ids(ids); } #if DO_TIMING - auto starth = std::chrono::steady_clock::now(); -#endif - hash_node_ids(ids); -#if DO_TIMING - auto endh = std::chrono::steady_clock::now(); + auto endh = std::chrono::steady_clock::now(); #endif for (const auto &eb : ebs) { const std::string &name = eb->name(); size_t numel = eb->entity_count(); - size_t reserve = 3.2 * numel; + size_t reserve = 2.0 * numel; faces_[name].reserve(reserve); + faces_[name].max_load_factor(0.9); internal_generate_faces(eb, faces_[name], ids, hashIds_, local_ids, (INT)0); } @@ -426,6 +437,7 @@ namespace Ioss { fmt::print("Total time: \t{:.6} ms\n\n", std::chrono::duration(endp - starth).count()); #endif + hashIds_.clear(); } template void FaceGenerator::generate_model_faces(INT /*dummy*/, bool local_ids) @@ -487,6 +499,7 @@ namespace Ioss { fmt::print("Total time: \t{:.3f} ms\n\n", std::chrono::duration(endp - starth).count()); #endif + hashIds_.clear(); } } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h index 2881b08f43..ed9ada94a1 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h @@ -108,11 +108,11 @@ namespace Ioss { #if defined FG_USE_STD using FaceUnorderedSet = std::unordered_set; #elif defined FG_USE_HOPSCOTCH - using FaceUnorderedSet = tsl::hopscotch_set; - // using FaceUnorderedSet = tsl::hopscotch_pg_set; + //using FaceUnorderedSet = tsl::hopscotch_set; + using FaceUnorderedSet = tsl::hopscotch_pg_set; #elif defined FG_USE_ROBIN - using FaceUnorderedSet = tsl::robin_set; - // using FaceUnorderedSet = tsl::robin_pg_set; + // using FaceUnorderedSet = tsl::robin_set; + using FaceUnorderedSet = tsl::robin_pg_set; #endif class IOSS_EXPORT FaceGenerator { @@ -130,11 +130,15 @@ namespace Ioss { FaceUnorderedSet &faces(const std::string &name = "ALL") { return faces_[name]; } FaceUnorderedSet &faces(const ElementBlock *block); + void clear(const std::string &name) {faces_[name].clear();} + void clear(const ElementBlock *block); + //! Given a local node id (0-based), return the hashed value. size_t node_id_hash(size_t local_node_id) const { return hashIds_[local_node_id]; } private: template void hash_node_ids(const std::vector &node_ids); + void hash_local_node_ids(size_t count); template void generate_model_faces(INT /*dummy*/, bool local_ids); Ioss::Region ®ion_; From 7781505d03d14fff8dd7f4db774e5cb2b8c3b1bc Mon Sep 17 00:00:00 2001 From: tjotaha Date: Wed, 12 Jun 2024 17:44:15 -0600 Subject: [PATCH 32/59] Catalyst API 2 (#461) Fixed crash bug when IOSS field suffix separator is empty. Added tests for case when reading from Conduit files written by Catalyst IOSS database. Changed time step selection when reading from Conduit files to use the IOSS integer property CATALYST_READER_TIME_STEP instead of IOSS database filename. --- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.C | 147 +++++++++--------- .../ioss/src/catalyst_tests/CMakeLists.txt | 73 ++++++--- .../Iocatalyst_ConduitReadTest.C | 72 +++++++++ .../Iocatalyst_DatabaseIOTest.C | 99 ++++++------ .../Iocatalyst_DatabaseIOTest.h | 43 ++--- 5 files changed, 270 insertions(+), 164 deletions(-) create mode 100644 packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C index e4c0b8f35d..77f223fac3 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C @@ -70,6 +70,7 @@ namespace Iocatalyst { inline static const std::string BOUNDARYCONDS = "boundaryconditions"; inline static const std::string CATCONDNODE = "CATALYST_CONDUIT_NODE"; inline static const std::string CATDUMPDIR = "CATALYST_DATA_DUMP_DIRECTORY"; + inline static const std::string CATREADTIMESTEP = "CATALYST_READER_TIME_STEP"; inline static const std::string COMPONENTCOUNT = "component_count"; inline static const std::string COMPONENTDEGREE = "component_degree"; inline static const std::string COUNT = "count"; @@ -115,7 +116,6 @@ namespace Iocatalyst { inline static const std::string ROLE = "role"; inline static const std::string SHALLOWCOPYFIELDS = "SHALLOW_COPY_FIELDS"; inline static const std::string SIDEBLOCKS = "sideblocks"; - inline static const std::string STATETIME = "state_time"; inline static const std::string STORAGE = "storage"; inline static const std::string TIME = "time"; inline static const std::string TOPOLOGYTYPE = "topology_type"; @@ -289,6 +289,7 @@ namespace Iocatalyst { conduit_cpp::Node DBNode; mutable Ioss::Map NodeMap; std::map sideBlocks; + char read_db_field_separator; public: conduit_cpp::Node &databaseNode() { return this->DBNode; } @@ -348,9 +349,8 @@ namespace Iocatalyst { bool readTime(Ioss::Region *region) { auto &node = this->DBNode; - if (node.has_child(detail::STATETIME)) { - const auto time = node[detail::STATETIME].as_float64(); - region->add_state(time); + if (node.has_path(getTimePath())) { + region->add_state(node[getTimePath()].as_float64()); } return true; } @@ -585,6 +585,8 @@ namespace Iocatalyst { return retVal; } + std::string getTimePath() { return detail::DATABASE + detail::FS + detail::TIME; } + Ioss::Map &get_node_map(const Ioss::DatabaseIO *dbase) const { if (this->NodeMap.defined()) { @@ -818,96 +820,93 @@ namespace Iocatalyst { std::vector getScalarNamesFromNonScalarField(const Ioss::Field &field) const { - int ncomp = field.get_component_count(Ioss::Field::InOut::INPUT); + int ncomp = field.get_component_count(Ioss::Field::InOut::INPUT); std::vector fnames; - for(int i=1; i<=ncomp; i++){ - fnames.push_back(field.get_component_name(i, Ioss::Field::InOut::INPUT)); + for (int i = 1; i <= ncomp; i++) { + if (read_db_field_separator == '\0') { + fnames.push_back( + field.get_component_name(i, Ioss::Field::InOut::INPUT, read_db_field_separator)); + } + else { + fnames.push_back(field.get_component_name(i, Ioss::Field::InOut::INPUT)); + } } return fnames; } template - std::vector getInterweavedScalarDataFromConduitNode(const std::vector fnames, conduit_cpp::Node &node) const + std::vector getInterweavedScalarDataFromConduitNode(const std::vector fnames, + conduit_cpp::Node &node) const { - int ncomp = fnames.size(); - auto &&t_node = node[fnames[0] + detail::FS + detail::VALUE]; - int num_get = t_node.number_of_elements(); - std::vector vals(ncomp*num_get); - - for(int i=0; i vals(ncomp * num_get); + + for (int i = 0; i < num_get; i++) { + for (int j = 0; j < ncomp; j++) { std::string path_to_value = fnames[j] + detail::FS + detail::VALUE; - auto &&child_value = node[path_to_value]; - vals[i*ncomp + j] = - reinterpret_cast(child_value.element_ptr(0))[i]; + auto &&child_value = node[path_to_value]; + vals[i * ncomp + j] = reinterpret_cast(child_value.element_ptr(0))[i]; } } return vals; } template - void addFieldNodeAndDataToConduitNode(const Ioss::Field &field, void *data, conduit_cpp::Node &node) const + void addFieldNodeAndDataToConduitNode(const Ioss::Field &field, void *data, + conduit_cpp::Node &node) const { - int ncomp = field.get_component_count(Ioss::Field::InOut::INPUT); - int num_get = field.raw_count(); + int ncomp = field.get_component_count(Ioss::Field::InOut::INPUT); + int num_get = field.raw_count(); node[field.get_name()] = conduit_cpp::Node(); - auto &&f_node = node[field.get_name()]; + auto &&f_node = node[field.get_name()]; f_node[detail::ROLE].set(static_cast(field.get_role())); f_node[detail::TYPE].set(static_cast(field.get_type())); f_node[detail::COUNT].set(static_cast(field.raw_count())); f_node[detail::INDEX].set(static_cast(field.get_index())); f_node[detail::COMPONENTCOUNT].set(static_cast(ncomp)); f_node[detail::STORAGE].set(field.raw_storage()->name()); - f_node[detail::VALUE].set(static_cast< T *>(data), ncomp * num_get); + f_node[detail::VALUE].set(static_cast(data), ncomp * num_get); } - void combineScalarFieldsInConduitNodeToNonScalarField( - const Ioss::Field &field, conduit_cpp::Node &node) const + void combineScalarFieldsInConduitNodeToNonScalarField(const Ioss::Field &field, + conduit_cpp::Node &node) const { - std::vector fnames = - this->getScalarNamesFromNonScalarField(field); + std::vector fnames = this->getScalarNamesFromNonScalarField(field); switch (field.get_type()) { - case Ioss::Field::BasicType::DOUBLE: - this->addFieldNodeAndDataToConduitNode( - field, - this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), - node - ); - break; + case Ioss::Field::BasicType::DOUBLE: + this->addFieldNodeAndDataToConduitNode( + field, this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), + node); + break; - case Ioss::Field::BasicType::INT32: - this->addFieldNodeAndDataToConduitNode( - field, - this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), - node - ); - break; + case Ioss::Field::BasicType::INT32: + this->addFieldNodeAndDataToConduitNode( + field, this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), + node); + break; - case Ioss::Field::BasicType::INT64: - this->addFieldNodeAndDataToConduitNode( - field, - this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), - node - ); - break; + case Ioss::Field::BasicType::INT64: + this->addFieldNodeAndDataToConduitNode( + field, this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), + node); + break; - case Ioss::Field::BasicType::CHARACTER: - this->addFieldNodeAndDataToConduitNode( - field, - this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), - node - ); - break; - default: - std::ostringstream errmsg; - fmt::print(errmsg, "ERROR in {} on read: {}, unsupported field type: {}\n", __func__, - field.get_name(), field.type_string()); - IOSS_ERROR(errmsg); + case Ioss::Field::BasicType::CHARACTER: + this->addFieldNodeAndDataToConduitNode( + field, this->getInterweavedScalarDataFromConduitNode(fnames, node).data(), node); + break; + default: + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR in {} on read: {}, unsupported field type: {}\n", __func__, + field.get_name(), field.type_string()); + IOSS_ERROR(errmsg); } - - //Remove Related Scalars from Conduit Node - for(int i=0; iname() != IOSS_SCALAR()) { + if (field.raw_storage()->name() != IOSS_SCALAR()) { this->combineScalarFieldsInConduitNodeToNonScalarField(field, parent); } } @@ -992,6 +991,7 @@ namespace Iocatalyst { { for (conduit_index_t idx = 0, max = parent.number_of_children(); idx < max; ++idx) { auto &&child = parent[idx]; + this->readProperties(child[detail::PROPERTIES], region); // read fields (meta-data only) @@ -1193,10 +1193,10 @@ namespace Iocatalyst { if (write_split_type != region->get_database()->get_surface_split_type()) { static_cast(region->get_database())->set_split_type_changed(true); } + read_db_field_separator = region->get_database()->get_field_separator(); - auto tpath = detail::REGION + detail::FS + detail::TIME; - if (node.has_path(tpath)) { - region->add_state(node[tpath].to_float64()); + if (node.has_path(getTimePath())) { + region->add_state(node[getTimePath()].to_float64()); } this->readEntityGroup(node[detail::REGION], region); this->readEntityGroup(node[detail::NODEBLOCKS], region); @@ -1239,14 +1239,17 @@ namespace Iocatalyst { if (is_input()) { auto &pm = get_property_manager(); if (pm.exists(detail::CATCONDNODE)) { - auto c_node_ptr = reinterpret_cast( - get_property_manager().get(detail::CATCONDNODE).get_pointer()); + auto c_node_ptr = + reinterpret_cast(pm.get(detail::CATCONDNODE).get_pointer()); this->Impl->setDatabaseNode(c_node_ptr); } else { - // we'll use filename as the location for the data dumps and read those. + int timestep = 1; + if (pm.exists(detail::CATREADTIMESTEP)) { + timestep = pm.get(detail::CATREADTIMESTEP).get_int(); + } std::ostringstream path; - path << get_catalyst_dump_dir() << detail::EXECUTE_INVC << filename + path << get_catalyst_dump_dir() << detail::EXECUTE_INVC << timestep << detail::PARAMS_CONDUIT_BIN << util().parallel_size() << detail::DOT << util().parallel_rank(); auto &root = this->Impl->root(); @@ -1333,7 +1336,7 @@ namespace Iocatalyst { // invoke catalyst. auto &impl = (*this->Impl.get()); auto &dbaseNode = this->Impl->databaseNode(); - dbaseNode[detail::REGION + detail::FS + detail::TIME].set_float64(time); + dbaseNode[this->Impl->getTimePath()].set_float64(time); conduit_cpp::Node node; CatalystManager::getInstance().execute(catPipeID, state, time, impl.databaseNode()); } diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/CMakeLists.txt b/packages/seacas/libraries/ioss/src/catalyst_tests/CMakeLists.txt index 123f70c850..4587a74657 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/CMakeLists.txt +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/CMakeLists.txt @@ -113,11 +113,11 @@ TRIBITS_ADD_ADVANCED_TEST(${test_name} NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 TEST_2 EXEC io_shell ARGS -out_type catalyst ${ioshell_output_file_name} - ${test_time_step} + catalyst.bin DIRECTORY ../main NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS ${num_procs} - TEST_3 EXEC io_shell ARGS -in_type catalyst ${test_time_step} + TEST_3 EXEC io_shell ARGS -in_type catalyst catalyst.bin ${CATALYST_FNAME} DIRECTORY ../main NOEXEPREFIX NOEXESUFFIX @@ -137,6 +137,7 @@ TRIBITS_ADD_ADVANCED_TEST(${test_name} NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 ENVIRONMENT CATALYST_DATA_DUMP_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}/${test_name} + IOSS_PROPERTIES=CATALYST_READER_TIME_STEP=${test_time_step} ) endfunction() @@ -145,32 +146,32 @@ foreach(NUM_PROCS 1 4) catalyst_test_ioshell_generated( "Iocatalyst_10x10x10_hex_MPI_${NUM_PROCS}" "10x10x10+times:4+variables:element,2,nodal,3" - "ioshell_10x10x10.g" "3" "3" ${NUM_PROCS}) + "ioshell_10x10x10.g" "3" 3 ${NUM_PROCS}) catalyst_test_ioshell_generated( "Iocatalyst_10x10x10_tets_MPI_${NUM_PROCS}" "10x10x10+tets:+times:2+variables:element,2,nodal,3" - "ioshell_10x10x10_tets.g" "1" "1" ${NUM_PROCS}) + "ioshell_10x10x10_tets.g" "1" 1 ${NUM_PROCS}) catalyst_test_ioshell_generated( "Iocatalyst_10x10x10_pyramids_MPI_${NUM_PROCS}" "10x10x10+pyramids:+times:2+variables:element,2,nodal,3" - "ioshell_10x10x10_pyramids.g" "1" "1" ${NUM_PROCS}) + "ioshell_10x10x10_pyramids.g" "1" 1 ${NUM_PROCS}) catalyst_test_ioshell_generated( "Iocatalyst_10x10x10_shell_MPI_${NUM_PROCS}" "10x10x10+shell:xX:+times:2+variables:element,2,nodal,3" - "ioshell_10x10x10_shell.g" "1" "1" ${NUM_PROCS}) + "ioshell_10x10x10_shell.g" "1" 1 ${NUM_PROCS}) catalyst_test_ioshell_generated( "Iocatalyst_10x10x10_nodeset_MPI_${NUM_PROCS}" "10x10x10+nodeset:xX:+times:2+variables:element,2,nodal,3,nodeset,4" - "ioshell_10x10x10_nodeset.g" "1" "1" ${NUM_PROCS}) + "ioshell_10x10x10_nodeset.g" "1" 1 ${NUM_PROCS}) catalyst_test_ioshell_generated( "Iocatalyst_10x10x10_sideset_MPI_${NUM_PROCS}" "10x10x10+sideset:xX:+times:2+variables:element,2,nodal,3,sideset,4" - "ioshell_10x10x10_sideset.g" "1" "1" ${NUM_PROCS}) + "ioshell_10x10x10_sideset.g" "1" 1 ${NUM_PROCS}) endforeach() @@ -196,12 +197,12 @@ TRIBITS_ADD_ADVANCED_TEST(${test_name} NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 TEST_1 EXEC io_shell ARGS -out_type catalyst - ${input_file} ${test_time_step} + ${input_file} catalyst.bin DIRECTORY ../main NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS ${num_procs} TEST_2 EXEC io_shell ARGS -in_type catalyst - ${test_time_step} ${CATALYST_FNAME} + catalyst.bin ${CATALYST_FNAME} DIRECTORY ../main NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS ${num_procs} @@ -220,7 +221,13 @@ TRIBITS_ADD_ADVANCED_TEST(${test_name} NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 ENVIRONMENT CATALYST_DATA_DUMP_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}/${test_name} + IOSS_PROPERTIES=CATALYST_READER_TIME_STEP=${test_time_step} + ADDED_TEST_NAME_OUT FileTestExodus ) + +set_tests_properties(${FileTestExodus} + PROPERTIES FIXTURES_SETUP WriteTestConduit) + endfunction() set(EXO_TEST_FILES @@ -237,16 +244,16 @@ endforeach() foreach(NUM_PROCS 1 4) catalyst_test_ioshell_exodus_file( "Iocatalyst_cube_g_MPI_${NUM_PROCS}" - "cube.g" "1" "1" ${NUM_PROCS}) + "cube.g" "1" 1 ${NUM_PROCS}) catalyst_test_ioshell_exodus_file( "Iocatalyst_two_block_g_MPI_${NUM_PROCS}" - "two-block.g" "1" "1" ${NUM_PROCS}) + "two-block.g" "1" 1 ${NUM_PROCS}) catalyst_test_ioshell_exodus_file( "Iocatalyst_eight_block_g_MPI_${NUM_PROCS}" - "8-block.g" "0.05" "5" ${NUM_PROCS}) + "8-block.g" "0.05" 5 ${NUM_PROCS}) catalyst_test_ioshell_exodus_file( "Iocatalyst_can_ex2_MPI_${NUM_PROCS}" - "can.ex2" "0.00219992" "22" ${NUM_PROCS}) + "can.ex2" "0.00219992" 22 ${NUM_PROCS}) endforeach() if ( NETCDF_NCDUMP_BINARY ) @@ -267,11 +274,11 @@ TRIBITS_ADD_ADVANCED_TEST(${test_name} DIRECTORY ../../../exodus/test NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 - TEST_1 EXEC io_shell ARGS -out_type catalyst ${INPUT_FILE_PATH} ${test_time_step} + TEST_1 EXEC io_shell ARGS -out_type catalyst ${INPUT_FILE_PATH} catalyst.bin DIRECTORY ../main NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 - TEST_2 EXEC io_shell ARGS -in_type catalyst ${test_time_step} ${CATALYST_FNAME} + TEST_2 EXEC io_shell ARGS -in_type catalyst catalyst.bin ${CATALYST_FNAME} DIRECTORY ../main NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 @@ -284,14 +291,16 @@ TRIBITS_ADD_ADVANCED_TEST(${test_name} NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 ENVIRONMENT CATALYST_DATA_DUMP_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}/${test_name} + IOSS_PROPERTIES=CATALYST_READER_TIME_STEP=${test_time_step} ) + endfunction() catalyst_test_ioshell_generated_exodus_file("Iocatalyst_test_blob_exo_MPI_1" - "testwt-blob" "" "test-blob.exo" "0.02" "1") + "testwt-blob" "" "test-blob.exo" "0.02" 1) catalyst_test_ioshell_generated_exodus_file("Iocatalyst_test_seacas_exodus_exoIIC_MPI_1" - "SEACASExodus_ExoIICTests.exe" "CreateEdgeFace" "edgeFace.exo" "1" "0") + "SEACASExodus_ExoIICTests.exe" "CreateEdgeFace" "edgeFace.exo" "1" 0) endif() @@ -325,11 +334,11 @@ set(IOSHELL_FNAME ioshell_time_${test_time_step}_${input_file}) set(INPUT_FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../main/test/${input_file}) TRIBITS_ADD_ADVANCED_TEST(${test_name} - TEST_0 EXEC io_shell ARGS -debug -out_type catalyst ${INPUT_FILE_PATH} ${test_time_step} + TEST_0 EXEC io_shell ARGS -debug -out_type catalyst ${INPUT_FILE_PATH} catalyst.bin DIRECTORY ../main NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 - TEST_1 EXEC io_shell ARGS -in_type catalyst ${test_time_step} ${CATALYST_FNAME} + TEST_1 EXEC io_shell ARGS -in_type catalyst catalyst.bin ${CATALYST_FNAME} DIRECTORY ../main NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 @@ -341,11 +350,33 @@ TRIBITS_ADD_ADVANCED_TEST(${test_name} NOEXEPREFIX NOEXESUFFIX NUM_MPI_PROCS 1 ENVIRONMENT CATALYST_DATA_DUMP_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}/${test_name} + IOSS_PROPERTIES=CATALYST_READER_TIME_STEP=${test_time_step} + ADDED_TEST_NAME_OUT FileTestCGNS ) + +set_tests_properties(${FileTestCGNS} + PROPERTIES FIXTURES_SETUP WriteTestConduit) + endfunction() catalyst_test_ioshell_cgns_file( - "Iocatalyst_sparc1_cgns_MPI_1" "sparc1.cgns" "15.992" "1") + "Iocatalyst_sparc1_cgns_MPI_1" "sparc1.cgns" "15.992" 1) + +TRIBITS_ADD_EXECUTABLE( + Iocatalyst_ConduitReadTest + SOURCES + Iocatalyst_ConduitReadTest.C +) + +TRIBITS_ADD_TEST( + Iocatalyst_ConduitReadTest + NAME Iocatalyst_ConduitReadTest + NUM_MPI_PROCS 1 + ADDED_TESTS_NAMES_OUT ConduitReadTest +) + +set_tests_properties(${ConduitReadTest} + PROPERTIES FIXTURES_REQUIRED WriteTestConduit) endif() endif() diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C new file mode 100644 index 0000000000..128fc09e53 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C @@ -0,0 +1,72 @@ +// Copyright(C) 1999-2020 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 +#include +#include +#include +#include + +TEST_F(Iocatalyst_DatabaseIOTest, ReadConduitCanExo) +{ + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("FIELD_SUFFIX_SEPARATOR", "")); + + auto db = getCatalystDatabaseFromConduitFiles("Iocatalyst_can_ex2_MPI_1", iossProp); + ASSERT_TRUE(db != nullptr); + Ioss::Region reg(db); + + auto eb = reg.get_element_block("block_1"); + EXPECT_TRUE(eb->field_exists("eqps")); + + auto nb = reg.get_node_block("nodeblock_1"); + EXPECT_TRUE(nb->field_exists("accl")); + EXPECT_TRUE(nb->field_exists("vel")); + EXPECT_TRUE(nb->field_exists("displ")); +} + +TEST_F(Iocatalyst_DatabaseIOTest, ReadConduitSPARCOneCGNS) +{ + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("FIELD_SUFFIX_SEPARATOR", "")); + + auto db = getCatalystDatabaseFromConduitFiles("Iocatalyst_sparc1_cgns_MPI_1", iossProp); + ASSERT_TRUE(db != nullptr); + Ioss::Region reg(db); + + auto sb = reg.get_structured_block("blk-1"); + EXPECT_TRUE(sb != nullptr); + EXPECT_TRUE(sb->field_exists("Density1")); + EXPECT_TRUE(sb->field_exists("Temperature1")); + EXPECT_TRUE(sb->field_exists("Velocity")); + + auto nb = sb->get_node_block(); + EXPECT_TRUE(nb.field_exists("mesh_model_coordinates")); + EXPECT_TRUE(nb.field_exists("mesh_model_coordinates_x")); + EXPECT_TRUE(nb.field_exists("mesh_model_coordinates_y")); + EXPECT_TRUE(nb.field_exists("mesh_model_coordinates_z")); +} + +TEST_F(Iocatalyst_DatabaseIOTest, SetReaderTimeStepWithIOSSProp) +{ + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("CATALYST_READER_TIME_STEP", 12)); + + auto db = getCatalystDatabaseFromConduitFiles("Iocatalyst_can_ex2_MPI_1", iossProp); + ASSERT_TRUE(db != nullptr); + + Iocatalyst::DatabaseIO *catdb = static_cast(db); + + Ioss::Region reg(db); + + auto mint = reg.get_min_time(); + EXPECT_EQ(mint.first, 1); + EXPECT_DOUBLE_EQ(mint.second, 0.0011999331181868911); + + auto maxt = reg.get_max_time(); + EXPECT_EQ(maxt.first, 1); + EXPECT_DOUBLE_EQ(maxt.second, 0.0011999331181868911); +} \ No newline at end of file diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.C index bdf3d43227..ac2bafb855 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_DatabaseIOTest.C @@ -12,9 +12,10 @@ #include #include #include +#include #include #include -#include +#include Iocatalyst_DatabaseIOTest::Iocatalyst_DatabaseIOTest() { @@ -92,30 +93,33 @@ void Iocatalyst_DatabaseIOTest::runUnstructuredTest(const std::string &testName) EXPECT_TRUE(regionsAreEqual(exodusFileName, catalystFileName, EXODUS_DATABASE_TYPE)); } -Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::writeAndGetExodusDatabaseOnRead(const std::string &testName, - Ioss::PropertyManager dbProps) +Ioss::DatabaseIO * +Iocatalyst_DatabaseIOTest::writeAndGetExodusDatabaseOnRead(const std::string &testName, + Ioss::PropertyManager dbProps) { std::string exodusFileName = testName + CATALYST_TEST_FILE_NP + std::to_string(part.size) + EXODUS_FILE_EXTENSION; Iocatalyst::BlockMeshSet::IOSSparams iop(exodusFileName, EXODUS_DATABASE_TYPE); bmSet.writeIOSSFile(iop); - Ioss::DatabaseIO* exo_db = getDatabaseOnReadFromFileName(exodusFileName, EXODUS_DATABASE_TYPE, dbProps); - if(exo_db == nullptr) - { + Ioss::DatabaseIO *exo_db = + getDatabaseOnReadFromFileName(exodusFileName, EXODUS_DATABASE_TYPE, dbProps); + if (exo_db == nullptr) { EXPECT_TRUE(false) << "Exodus db unable to initialize on read"; } return exo_db; } -Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getExodusDatabaseFromFile(std::string &filename, - Ioss::PropertyManager dbProps) { +Ioss::DatabaseIO * +Iocatalyst_DatabaseIOTest::getExodusDatabaseFromFile(std::string &filename, + Ioss::PropertyManager dbProps) +{ Ioss::PropertyManager edbProps(dbProps); - + std::string inputFileName = filename; Ioss::DatabaseIO *edbi = - Ioss::IOFactory::create(EXODUS_DATABASE_TYPE, inputFileName, Ioss::READ_RESTART, - Ioss::ParallelUtils::comm_self(), edbProps); + Ioss::IOFactory::create(EXODUS_DATABASE_TYPE, inputFileName, Ioss::READ_RESTART, + Ioss::ParallelUtils::comm_self(), edbProps); if (edbi == nullptr || !edbi->ok(true)) { return nullptr; } @@ -123,7 +127,22 @@ Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getExodusDatabaseFromFile(std::stri return edbi; } -conduit_cpp::Node Iocatalyst_DatabaseIOTest::getConduitFromExodusFile(std::string &filename, +Ioss::DatabaseIO * +Iocatalyst_DatabaseIOTest::getCatalystDatabaseFromConduitFiles(const std::string &dirName, + Ioss::PropertyManager dbProps) +{ + setenv("CATALYST_DATA_DUMP_DIRECTORY", dirName.c_str(), 1); + Ioss::DatabaseIO *cdbi = + Ioss::IOFactory::create(CATALYST_DATABASE_TYPE, "catalyst.bin", Ioss::READ_RESTART, + Ioss::ParallelUtils::comm_self(), dbProps); + if (cdbi == nullptr || !cdbi->ok(true)) { + return nullptr; + } + + return cdbi; +} + +conduit_cpp::Node Iocatalyst_DatabaseIOTest::getConduitFromExodusFile(std::string &filename, Ioss::PropertyManager dbProps) { Iocatalyst::CatalystManager::getInstance().reset(); @@ -131,16 +150,16 @@ conduit_cpp::Node Iocatalyst_DatabaseIOTest::getConduitFromExodusFile(std::strin Ioss::PropertyManager edbProps; edbProps.add(Ioss::Property("SURFACE_SPLIT_TYPE", "TOPOLOGY")); Ioss::DatabaseIO *edbi = getExodusDatabaseFromFile(filename, edbProps); - - //Create Cat Db on write + + // Create Cat Db on write Ioss::PropertyManager cdbwProps(edbi->get_property_manager()); - Ioss::DatabaseIO *cdb_on_write = + Ioss::DatabaseIO *cdb_on_write = Ioss::IOFactory::create(CATALYST_DATABASE_TYPE, CATALYST_DUMMY_DATABASE, Ioss::WRITE_RESULTS, Ioss::ParallelUtils::comm_world(), cdbwProps); if (cdb_on_write == nullptr || !cdb_on_write->ok(true)) { return conduit_cpp::Node(); } - + Ioss::Region cor(edbi); Ioss::Region cir(cdb_on_write); Ioss::MeshCopyOptions options; @@ -148,22 +167,22 @@ conduit_cpp::Node Iocatalyst_DatabaseIOTest::getConduitFromExodusFile(std::strin Ioss::copy_database(cor, cir, options); auto c_node = reinterpret_cast( - ((Iocatalyst::DatabaseIO *)cdb_on_write)->get_catalyst_conduit_node()); + ((Iocatalyst::DatabaseIO *)cdb_on_write)->get_catalyst_conduit_node()); conduit_cpp::Node conduitNode; - auto cpp_node = conduit_cpp::cpp_node(c_node); + auto cpp_node = conduit_cpp::cpp_node(c_node); conduitNode.set(cpp_node); return conduitNode; - } -Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getCatalystDatabaseFromConduit(conduit_cpp::Node &conduitNode, - Ioss::PropertyManager dbProps) +Ioss::DatabaseIO * +Iocatalyst_DatabaseIOTest::getCatalystDatabaseFromConduit(conduit_cpp::Node &conduitNode, + Ioss::PropertyManager dbProps) { Ioss::PropertyManager cdbrProps = Ioss::PropertyManager(dbProps); cdbrProps.add(Ioss::Property("CATALYST_CONDUIT_NODE", conduit_cpp::c_node(&conduitNode))); - - //Give to Cat Db on read + + // Give to Cat Db on read Ioss::DatabaseIO *cdb_on_read = Ioss::IOFactory::create(CATALYST_DATABASE_TYPE, CATALYST_DUMMY_DATABASE, Ioss::READ_RESTART, Ioss::ParallelUtils::comm_world(), cdbrProps); @@ -174,16 +193,14 @@ Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getCatalystDatabaseFromConduit(cond return cdb_on_read; } -Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getDatabaseOnReadFromFileName(const std::string &fileName, - const std::string &iossDatabaseType, - Ioss::PropertyManager dbProps) +Ioss::DatabaseIO *Iocatalyst_DatabaseIOTest::getDatabaseOnReadFromFileName( + const std::string &fileName, const std::string &iossDatabaseType, Ioss::PropertyManager dbProps) { Ioss::PropertyManager dbaseProps = Ioss::PropertyManager(dbProps); - //dbProps.add(Ioss::Property("ENABLE_FIELD_RECOGNITION", "OFF")); - auto inputFileName = fileName; - Ioss::ParallelUtils pu; - int numRanks = pu.parallel_size(); - int rank = pu.parallel_rank(); + auto inputFileName = fileName; + Ioss::ParallelUtils pu; + int numRanks = pu.parallel_size(); + int rank = pu.parallel_rank(); if (iossDatabaseType == EXODUS_DATABASE_TYPE && numRanks > 1) { inputFileName += "." + std::to_string(numRanks) + "." + std::to_string(rank); } @@ -196,26 +213,6 @@ Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::getDatabaseOnReadFromFileName(const return dbi; } -/*Ioss::DatabaseIO* Iocatalyst_DatabaseIOTest::writeAndGetCatalystDatabaseOnRead(Ioss::PropertyManager dbProps) -{ - std::string exodusFileName = - "test_eb_1_enable_field_recog" + CATALYST_TEST_FILE_NP + std::to_string(part.size) + EXODUS_FILE_EXTENSION; - Iocatalyst::BlockMeshSet::IOSSparams iop(exodusFileName, EXODUS_DATABASE_TYPE); - - Ioss::DatabaseIO *cat_d = bmSet.getCatalystDatabase(iop); - - //Ioss::Region cir(cat_d); - //std::cout<<"Done Region!"<get_field_data(name, &data, &dataSize); - ASSERT_GT(dataSize, 0) << "DataSize is not greater than 0 for field "<(data); std::vector zcBuffer(b, b + field.get_size()); EXPECT_EQ(dcBuffer, zcBuffer); @@ -76,12 +79,12 @@ class IOCATALYST_EXPORT Iocatalyst_DatabaseIOTest : public ::testing::Test void setOrigin(unsigned int i, unsigned int j, unsigned int k); void addBlockMesh(Iocatalyst::BlockMesh &blockMesh); - const std::string CGNS_DATABASE_TYPE = "cgns"; - const std::string CGNS_FILE_EXTENSION = ".cgns"; - const std::string EXODUS_DATABASE_TYPE = "exodus"; - const std::string EXODUS_FILE_EXTENSION = ".ex2"; - const std::string CATALYST_TEST_FILE_PREFIX = "catalyst_"; - const std::string CATALYST_TEST_FILE_NP = "_np_"; - inline static const std::string CATALYST_DATABASE_TYPE = "catalyst"; - inline static const std::string CATALYST_DUMMY_DATABASE = "dummy.db"; + const std::string CGNS_DATABASE_TYPE = "cgns"; + const std::string CGNS_FILE_EXTENSION = ".cgns"; + const std::string EXODUS_DATABASE_TYPE = "exodus"; + const std::string EXODUS_FILE_EXTENSION = ".ex2"; + const std::string CATALYST_TEST_FILE_PREFIX = "catalyst_"; + const std::string CATALYST_TEST_FILE_NP = "_np_"; + inline static const std::string CATALYST_DATABASE_TYPE = "catalyst"; + inline static const std::string CATALYST_DUMMY_DATABASE = "dummy.db"; }; From f02f0f39deac484692de643469238d60f7f8f27c Mon Sep 17 00:00:00 2001 From: tjotaha Date: Thu, 13 Jun 2024 16:45:48 -0600 Subject: [PATCH 33/59] IOSS: Catalyst API 2 (#462) Allow CATALYST_READER_TIME_STEP to be defined as an environment variable. Changed default timestep to 0 for reading from Conduit files. --- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.C | 6 +++++- .../Iocatalyst_ConduitReadTest.C | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C index 77f223fac3..e17738a9c6 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -1244,10 +1245,13 @@ namespace Iocatalyst { this->Impl->setDatabaseNode(c_node_ptr); } else { - int timestep = 1; + int timestep = 0; if (pm.exists(detail::CATREADTIMESTEP)) { timestep = pm.get(detail::CATREADTIMESTEP).get_int(); } + else if(const char* ts = std::getenv(detail::CATREADTIMESTEP.c_str())) { + timestep = std::stoi(std::string(ts)); + } std::ostringstream path; path << get_catalyst_dump_dir() << detail::EXECUTE_INVC << timestep << detail::PARAMS_CONDUIT_BIN << util().parallel_size() << detail::DOT diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C index 128fc09e53..766a8dc8c1 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C @@ -69,4 +69,22 @@ TEST_F(Iocatalyst_DatabaseIOTest, SetReaderTimeStepWithIOSSProp) auto maxt = reg.get_max_time(); EXPECT_EQ(maxt.first, 1); EXPECT_DOUBLE_EQ(maxt.second, 0.0011999331181868911); +} + +TEST_F(Iocatalyst_DatabaseIOTest, SetReaderTimeStepWithIOSSEnvVar) +{ + setenv("CATALYST_READER_TIME_STEP", "24", 1); + + auto db = getCatalystDatabaseFromConduitFiles("Iocatalyst_can_ex2_MPI_1"); + ASSERT_TRUE(db != nullptr); + + Ioss::Region reg(db); + + auto mint = reg.get_min_time(); + EXPECT_EQ(mint.first, 1); + EXPECT_DOUBLE_EQ(mint.second, 0.0024000538978725672); + + auto maxt = reg.get_max_time(); + EXPECT_EQ(maxt.first, 1); + EXPECT_DOUBLE_EQ(maxt.second, 0.0024000538978725672); } \ No newline at end of file From 8ec59c9e9b5d35b20f42f879c07fb35766c350ea Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 17 Jun 2024 07:55:01 -0600 Subject: [PATCH 34/59] Fix errors detected in Trilinos integration --- packages/seacas/libraries/exodus/src/ex_field_utils.c | 4 ++-- packages/seacas/libraries/exodus/test/testrd-field-metadata.c | 4 ++-- .../seacas/libraries/ioss/src/Ioss_CompositeVariableType.h | 4 ++-- packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C | 4 ++++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/seacas/libraries/exodus/src/ex_field_utils.c b/packages/seacas/libraries/exodus/src/ex_field_utils.c index f054e84935..df99363788 100644 --- a/packages/seacas/libraries/exodus/src/ex_field_utils.c +++ b/packages/seacas/libraries/exodus/src/ex_field_utils.c @@ -15,7 +15,7 @@ #define SIZE(X) sizeof(X) / sizeof(X[0]) -char *my_strsep(char **stringp, const char *delim) +static char *my_strsep(char **stringp, const char *delim) { char *rv = *stringp; if (rv) { @@ -27,7 +27,7 @@ char *my_strsep(char **stringp, const char *delim) } return rv; } -size_t my_strlcat(char *restrict dst, const char *restrict src, size_t maxlen) +static size_t my_strlcat(char *restrict dst, const char *restrict src, size_t maxlen) { const size_t srclen = strlen(src); const size_t dstlen = strlen(dst); diff --git a/packages/seacas/libraries/exodus/test/testrd-field-metadata.c b/packages/seacas/libraries/exodus/test/testrd-field-metadata.c index 21011c26a3..67d140e453 100644 --- a/packages/seacas/libraries/exodus/test/testrd-field-metadata.c +++ b/packages/seacas/libraries/exodus/test/testrd-field-metadata.c @@ -15,7 +15,7 @@ #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) -char *my_strdup(const char *s) +static char *my_strdup(const char *s) { size_t slen = strlen(s); char *result = malloc(slen + 1); @@ -27,7 +27,7 @@ char *my_strdup(const char *s) return result; } -char *my_strsep(char **stringp, const char *delim) +static char *my_strsep(char **stringp, const char *delim) { assert(delim != NULL); char *rv = *stringp; diff --git a/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h b/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h index 53f0bb9b64..7a445264f0 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h +++ b/packages/seacas/libraries/ioss/src/Ioss_CompositeVariableType.h @@ -32,11 +32,11 @@ namespace Ioss { IOSS_NODISCARD int get_num_copies() const { return copies_; } // Kept for backward compatibility... - IOSS_NODISCARD [[deprecated("Use get_base_type")]] const VariableType *getBaseType() const + IOSS_NODISCARD [[deprecated("Use get_base_type")]] const VariableType *GetBaseType() const { return baseType; } - IOSS_NODISCARD [[deprecated("Use get_num_copies")]] int getNumCopies() const { return copies_; } + IOSS_NODISCARD [[deprecated("Use get_num_copies")]] int GetNumCopies() const { return copies_; } private: const VariableType *baseType; diff --git a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C index bf6d2092a2..a1862313b7 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C +++ b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C @@ -1272,6 +1272,10 @@ namespace { else { } +#ifdef SEACAS_HAVE_KOKKOS + int basic_type = ige->get_field(field_name).get_type(); +#endif + assert(pool.data.size() >= isize); switch (options.data_storage_type) { From 9060aadeb59845714d12af7d01e931d93f1a2440 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 17 Jun 2024 09:06:48 -0600 Subject: [PATCH 35/59] EXOMERGE: Handle non-3D models more correctly --- packages/seacas/scripts/exomerge3.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/seacas/scripts/exomerge3.py b/packages/seacas/scripts/exomerge3.py index 76779addcb..19cb128204 100644 --- a/packages/seacas/scripts/exomerge3.py +++ b/packages/seacas/scripts/exomerge3.py @@ -57,11 +57,11 @@ import exodus3 as exodus # informal version number of this module -__version__ = "8.6.1" +__version__ = "8.6.2" VERSION = __version__ # contact person for issues -CONTACT = "Tim Kostka " +CONTACT = "Sierra Help " # show the banner on first use SHOW_BANNER = True @@ -555,6 +555,7 @@ def __init__(self): self.qa_records = [] # title of the database self.title = None + self.num_dimension = 3 def __getattr__(self, name): """ @@ -7797,6 +7798,9 @@ def import_model( exodus_file = exodus.exodus(filename, mode="r") if SUPPRESS_EXODUS_OUTPUT: sys.stdout = save_stdout + + self.num_dimension = exodus_file.num_dimensions() + # format timesteps to retrieve file_timesteps = list(exodus_file.get_times()) if timesteps == "last_if_any": @@ -8232,7 +8236,7 @@ def export_model( "w", "ctype", self.title, - 3, + self.num_dimension, len(self.nodes), self.get_element_count(element_block_ids), len(element_block_ids), From d4cfa71dc7cc6344df2c7b7b8a0a5f8807638441 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 17 Jun 2024 09:07:14 -0600 Subject: [PATCH 36/59] GREPOS: Remove debug output --- packages/seacas/applications/grepos/gp_mapvar.f | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/seacas/applications/grepos/gp_mapvar.f b/packages/seacas/applications/grepos/gp_mapvar.f index 49858aec65..d55dd4d391 100644 --- a/packages/seacas/applications/grepos/gp_mapvar.f +++ b/packages/seacas/applications/grepos/gp_mapvar.f @@ -49,7 +49,6 @@ subroutine mapvar(nold, nnew, nvar, map, vars, scr) do 30 ivar = 1, nvar do 10 i = 1, nnew - write (*,*) i, map(i) scr(i) = vars(map(i) + nold * (ivar-1) ) 10 continue From deec345c8d1c8067b6c34b252bfb99c561b13448 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 18 Jun 2024 09:50:02 -0600 Subject: [PATCH 37/59] EXODUS: Fix output of map with zero entries --- .../seacas/libraries/exodus/src/ex_put_partial_num_map.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/seacas/libraries/exodus/src/ex_put_partial_num_map.c b/packages/seacas/libraries/exodus/src/ex_put_partial_num_map.c index 00b991927d..1f111503f2 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_partial_num_map.c +++ b/packages/seacas/libraries/exodus/src/ex_put_partial_num_map.c @@ -159,7 +159,11 @@ int ex_put_partial_num_map(int exoid, ex_entity_type map_type, ex_entity_id map_ } /* Check input parameters for a valid range of numbers */ - if (ent_start <= 0 || ent_start > num_mobj) { + if (ent_count == 0) { + ent_start = 0; + } + + if (ent_start < 0 || ent_start > num_mobj) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: start count is invalid in file id %d", exoid); ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM); EX_FUNC_LEAVE(EX_FATAL); @@ -169,7 +173,7 @@ int ex_put_partial_num_map(int exoid, ex_entity_type map_type, ex_entity_id map_ ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM); EX_FUNC_LEAVE(EX_FATAL); } - if (ent_start + ent_count - 1 > num_mobj) { + if (ent_count > 0 && (ent_start + ent_count - 1 > num_mobj)) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: start+count-1 is larger than mesh object count in file id %d", exoid); ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM); From 936da1b6688c9b3338d23dbc744237cc2a1e15fb Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 19 Jun 2024 10:12:24 -0600 Subject: [PATCH 38/59] Address coverity issues --- .../seacas/libraries/ioss/src/Ioss_ChainGenerator.C | 6 +++--- .../seacas/libraries/ioss/src/Ioss_FaceGenerator.C | 12 ++++++------ packages/seacas/libraries/ioss/src/Ioss_Glob.h | 2 +- .../libraries/ioss/src/text_mesh/Iotm_TextMesh.C | 1 + 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C index c2d976f04c..365fee7b65 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C @@ -86,7 +86,7 @@ namespace { void get_line_front(Ioss::SideSet *fs, const Ioss::ElementBlock *block, Ioss::chain_t &element_chains, front_t &front) { - const auto adj_block_name = block->name(); + const auto &adj_block_name = block->name(); Ioss::NameList blocks; fs->block_membership(blocks); for (const auto &fs_block : blocks) { @@ -193,8 +193,8 @@ namespace Ioss { Ioss::chain_t generate_element_chains(Ioss::Region ®ion, const std::string &surface_list, int debug_level, INT /*dummy*/) { - debug = debug_level; - size_t numel = region.get_property("element_count").get_int(); + debug = debug_level; + size_t numel = region.get_property("element_count").get_int(); // Determine which element block(s) are adjacent to the faceset specifying "lines" // The `adjacent_blocks` contains the names of all element blocks that are adjacent to the diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C index 04bd1effab..e230989bd8 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C @@ -93,8 +93,8 @@ namespace { std::array conn = {{0, 0, 0, 0}}; for (int j = 0; j < face_node_count[face]; j++) { size_t fnode = offset + face_conn[face][j]; - size_t lnode = connectivity[fnode]; // local since "connectivity_raw" - conn[j] = local_ids ? lnode : ids[lnode - 1]; // Convert to global + size_t lnode = connectivity[fnode]; // local since "connectivity_raw" + conn[j] = local_ids ? lnode : ids[lnode - 1]; // Convert to global id += hash_ids[lnode - 1]; } create_face(faces, id, conn, elem_id, face); @@ -326,13 +326,13 @@ namespace Ioss { FaceUnorderedSet &FaceGenerator::faces(const Ioss::ElementBlock *block) { - auto name = block->name(); + const auto &name = block->name(); return faces_[name]; } void FaceGenerator::clear(const Ioss::ElementBlock *block) { - auto name = block->name(); + const auto &name = block->name(); faces_[name].clear(); } @@ -380,7 +380,7 @@ namespace Ioss { Ioss::NodeBlock *nb = region_.get_node_blocks()[0]; #if DO_TIMING - auto starth = std::chrono::steady_clock::now(); + auto starth = std::chrono::steady_clock::now(); #endif std::vector ids; if (local_ids) { @@ -391,7 +391,7 @@ namespace Ioss { hash_node_ids(ids); } #if DO_TIMING - auto endh = std::chrono::steady_clock::now(); + auto endh = std::chrono::steady_clock::now(); #endif for (const auto &eb : ebs) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_Glob.h b/packages/seacas/libraries/ioss/src/Ioss_Glob.h index 58f65a3e56..6d84314f30 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Glob.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Glob.h @@ -225,7 +225,7 @@ namespace Ioss::glob { } std::vector>> states_; - size_t match_state_; + size_t match_state_{}; size_t start_state_{0}; }; diff --git a/packages/seacas/libraries/ioss/src/text_mesh/Iotm_TextMesh.C b/packages/seacas/libraries/ioss/src/text_mesh/Iotm_TextMesh.C index 270b20da68..51d6490033 100644 --- a/packages/seacas/libraries/ioss/src/text_mesh/Iotm_TextMesh.C +++ b/packages/seacas/libraries/ioss/src/text_mesh/Iotm_TextMesh.C @@ -938,6 +938,7 @@ namespace Iotm { for (const std::string &assemblyName : m_data.assemblies.get_part_names()) { const AssemblyData *assembly = m_data.assemblies.get_group_data(assemblyName); + ThrowRequireMsg(nullptr != assembly, "Could not find assembly with name" << assemblyName); bool omitAssembly = std::binary_search(assemblyOmissions.begin(), assemblyOmissions.end(), assembly->name); From 9a67af6487c437e2d4de1ec4d2d386cca9213c7c Mon Sep 17 00:00:00 2001 From: tjotaha Date: Thu, 20 Jun 2024 14:42:15 -0600 Subject: [PATCH 39/59] IOSS: Catalyst API 2 (#463) * IOSS: Catalyst API 2 Added support to database for using my_processor and processor_count IOSS properties when operating in serial parallel mode and reading Conduit files. * IOSS: Catalyst API 2 Changed Conduit serial parallel test to use files from the one MPI rank can.ex2 test. --------- Co-authored-by: Greg Sjaardema --- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.C | 14 ++++++++++---- .../catalyst_tests/Iocatalyst_ConduitReadTest.C | 10 ++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C index e17738a9c6..7531cff0d6 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -1249,13 +1248,20 @@ namespace Iocatalyst { if (pm.exists(detail::CATREADTIMESTEP)) { timestep = pm.get(detail::CATREADTIMESTEP).get_int(); } - else if(const char* ts = std::getenv(detail::CATREADTIMESTEP.c_str())) { + else if (const char *ts = std::getenv(detail::CATREADTIMESTEP.c_str())) { timestep = std::stoi(std::string(ts)); } + + int proc_count = util().parallel_size(); + int my_proc = util().parallel_rank(); + if (properties.exists("processor_count") && properties.exists("my_processor")) { + proc_count = properties.get("processor_count").get_int(); + my_proc = properties.get("my_processor").get_int(); + } + std::ostringstream path; path << get_catalyst_dump_dir() << detail::EXECUTE_INVC << timestep - << detail::PARAMS_CONDUIT_BIN << util().parallel_size() << detail::DOT - << util().parallel_rank(); + << detail::PARAMS_CONDUIT_BIN << proc_count << detail::DOT << my_proc; auto &root = this->Impl->root(); auto &dbase = this->Impl->databaseNode(); conduit_node_load(conduit_cpp::c_node(&root), path.str().c_str(), "conduit_bin"); diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C index 766a8dc8c1..4283115b50 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C @@ -87,4 +87,14 @@ TEST_F(Iocatalyst_DatabaseIOTest, SetReaderTimeStepWithIOSSEnvVar) auto maxt = reg.get_max_time(); EXPECT_EQ(maxt.first, 1); EXPECT_DOUBLE_EQ(maxt.second, 0.0024000538978725672); +} + +TEST_F(Iocatalyst_DatabaseIOTest, SetRankNumRanksSerialParallel) +{ + Ioss::PropertyManager iossProp; + iossProp.add(Ioss::Property("my_processor", 0)); + iossProp.add(Ioss::Property("processor_count", 1)); + + auto db = getCatalystDatabaseFromConduitFiles("Iocatalyst_can_ex2_MPI_1", iossProp); + ASSERT_TRUE(db != nullptr); } \ No newline at end of file From 60f737a998136c241a0baa239eac779e4df8d8b9 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Tue, 25 Jun 2024 11:08:30 -0400 Subject: [PATCH 40/59] Fix export symbols (#465) --- .../libraries/exodus/include/exodusII_cfg.h.in | 12 +++++++----- .../seacas/libraries/exodus/include/exodusII_int.h | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/seacas/libraries/exodus/include/exodusII_cfg.h.in b/packages/seacas/libraries/exodus/include/exodusII_cfg.h.in index f407c0ed79..304c65b4c7 100644 --- a/packages/seacas/libraries/exodus/include/exodusII_cfg.h.in +++ b/packages/seacas/libraries/exodus/include/exodusII_cfg.h.in @@ -1,8 +1,10 @@ -# Copyright(C) 1999-2022 National Technology & Engineering Solutions -# of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with -# NTESS, the U.S. Government retains certain rights in this software. -# -# See packages/seacas/LICENSE for details +/* + * Copyright(C) 1999-2022 National Technology & Engineering Solutions + * of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with + * NTESS, the U.S. Government retains certain rights in this software. + * + * See packages/seacas/LICENSE for details + */ #pragma once #cmakedefine EXODUSII_BUILD_SHARED_LIBS diff --git a/packages/seacas/libraries/exodus/include/exodusII_int.h b/packages/seacas/libraries/exodus/include/exodusII_int.h index 8657fa6755..c6e6e7e4e4 100644 --- a/packages/seacas/libraries/exodus/include/exodusII_int.h +++ b/packages/seacas/libraries/exodus/include/exodusII_int.h @@ -91,7 +91,7 @@ extern "C" { /* Utility function to find variable to store entity attribute on */ int exi_get_varid(int exoid, ex_entity_type obj_type, ex_entity_id id); -void exi_reset_error_status(void); +EXODUS_EXPORT void exi_reset_error_status(void); #if defined(EXODUS_THREADSAFE) #if !defined(exerrval) @@ -796,7 +796,7 @@ extern struct exi_obj_stats *exoII_edm; extern struct exi_obj_stats *exoII_fam; extern struct exi_obj_stats *exoII_nm; -struct exi_file_item *exi_find_file_item(int exoid); +EXODUS_EXPORT struct exi_file_item *exi_find_file_item(int exoid); struct exi_file_item *exi_add_file_item(int exoid); struct exi_obj_stats *exi_get_stat_ptr(int exoid, struct exi_obj_stats **obj_ptr); From 1b5f4c81d10ab139e7e298001a894211cfe147fc Mon Sep 17 00:00:00 2001 From: tjotaha Date: Tue, 25 Jun 2024 11:45:55 -0600 Subject: [PATCH 41/59] IOSS: Catalyst API 2 (#466) Create the cell_ids and cell_node_ids fields for a StructuredBlock when asked for by a reading application, if these fields are not already stored in the Conduit data. Uses the get_cell_ids() and get_cell_node_ids() methods on StructuredBlock. --- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.C | 40 +++++++++++++++++++ .../Iocatalyst_ConduitReadTest.C | 34 ++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C index 7531cff0d6..379fc673a5 100644 --- a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C @@ -71,6 +71,8 @@ namespace Iocatalyst { inline static const std::string CATCONDNODE = "CATALYST_CONDUIT_NODE"; inline static const std::string CATDUMPDIR = "CATALYST_DATA_DUMP_DIRECTORY"; inline static const std::string CATREADTIMESTEP = "CATALYST_READER_TIME_STEP"; + inline static const std::string CELLIDS = "cell_ids"; + inline static const std::string CELLNODEIDS = "cell_node_ids"; inline static const std::string COMPONENTCOUNT = "component_count"; inline static const std::string COMPONENTDEGREE = "component_degree"; inline static const std::string COUNT = "count"; @@ -587,6 +589,41 @@ namespace Iocatalyst { std::string getTimePath() { return detail::DATABASE + detail::FS + detail::TIME; } + int64_t getStructuredBlockIDS(const Ioss::StructuredBlock *sb, const Ioss::Field &field, + void *data, size_t data_size) + { + auto num_to_get = field.verify(data_size); + + if (num_to_get > 0) { + switch (field.get_type()) { + case Ioss::Field::BasicType::INT32: + copyIDS(sb, field, reinterpret_cast(data)); + break; + + case Ioss::Field::BasicType::INT64: + copyIDS(sb, field, reinterpret_cast(data)); + break; + default: + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR in {}: {} ({}), unsupported field type: {}\n", __func__, + field.get_name(), num_to_get, field.type_string()); + IOSS_ERROR(errmsg); + } + } + return num_to_get; + } + + template + void copyIDS(const Ioss::StructuredBlock *sb, const Ioss::Field &field, INT_t *data) + { + if (field.get_name() == detail::CELLIDS) { + sb->get_cell_ids(data, true); + } + else { + sb->get_cell_node_ids(data, true); + } + } + Ioss::Map &get_node_map(const Ioss::DatabaseIO *dbase) const { if (this->NodeMap.defined()) { @@ -1679,6 +1716,9 @@ namespace Iocatalyst { if (impl.hasField(blockPath, sb, field.get_name())) { return impl.getField(blockPath, sb, field, data, data_size); } + else if (field.get_name() == detail::CELLIDS || field.get_name() == detail::CELLNODEIDS) { + return impl.getStructuredBlockIDS(sb, field, data, data_size); + } else if ((field.get_name() == detail::MESHMODCO) && (impl.hasField(blockPath, sb, detail::MESHMODCOX) && impl.hasField(blockPath, sb, detail::MESHMODCOY) && diff --git a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C index 4283115b50..48bc674704 100644 --- a/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C +++ b/packages/seacas/libraries/ioss/src/catalyst_tests/Iocatalyst_ConduitReadTest.C @@ -9,6 +9,7 @@ #include #include #include +#include TEST_F(Iocatalyst_DatabaseIOTest, ReadConduitCanExo) { @@ -97,4 +98,37 @@ TEST_F(Iocatalyst_DatabaseIOTest, SetRankNumRanksSerialParallel) auto db = getCatalystDatabaseFromConduitFiles("Iocatalyst_can_ex2_MPI_1", iossProp); ASSERT_TRUE(db != nullptr); +} + +TEST_F(Iocatalyst_DatabaseIOTest, CellIdsAndCellNodeIds) +{ + setenv("CATALYST_READER_TIME_STEP", "1", 1); + + auto db = getCatalystDatabaseFromConduitFiles("Iocatalyst_sparc1_cgns_MPI_1"); + ASSERT_TRUE(db != nullptr); + + Ioss::Region reg(db); + + auto sb = reg.get_structured_block("blk-1"); + + EXPECT_TRUE(sb->field_exists("cell_ids")); + EXPECT_TRUE(sb->field_exists("cell_node_ids")); + + auto cids = sb->get_fieldref("cell_ids"); + EXPECT_TRUE(cids.get_type() == Ioss::Field::INTEGER); + std::vector cidBuff(cids.raw_count()); + EXPECT_EQ(sb->get_field_data("cell_ids", Data(cidBuff), sizeof(int32_t) * cidBuff.size()), + cids.raw_count()); + EXPECT_EQ(cidBuff[0], 1); + EXPECT_EQ(cidBuff[cids.raw_count() - 1], 256); + + + auto cnids = sb->get_fieldref("cell_node_ids"); + EXPECT_TRUE(cnids.get_type() == Ioss::Field::INTEGER); + std::vector cnidsBuff(cnids.raw_count()); + EXPECT_EQ( + sb->get_field_data("cell_node_ids", Data(cnidsBuff), sizeof(int32_t) * cnidsBuff.size()), + cnids.raw_count()); + EXPECT_EQ(cnidsBuff[0], 1); + EXPECT_EQ(cnidsBuff[cnids.raw_count() - 1], 594); } \ No newline at end of file From ea2fe086ca8ee5e8dbadb246f5f9e96d78179b74 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 25 Jun 2024 12:59:44 -0600 Subject: [PATCH 42/59] CI: See if can figure out intel build failure Signed-off-by: Greg Sjaardema --- .github/workflows/intel-build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/intel-build.yml b/.github/workflows/intel-build.yml index faa93ccc00..25757032e2 100644 --- a/.github/workflows/intel-build.yml +++ b/.github/workflows/intel-build.yml @@ -149,13 +149,13 @@ jobs: mkdir build cd build source /opt/intel/oneapi/setvars.sh - # echo "------------" - # ls /opt/intel/oneapi/ - # echo "------------" - # ls /opt/intel/oneapi/2024.1/ - # echo "------------" - # ls /opt/intel/oneapi/2024.1/bin/ - # echo "------------" + echo "------------" + ls /opt/intel/oneapi/ + echo "------------" + ls /opt/intel/oneapi/2024.1/ + echo "------------" + ls /opt/intel/oneapi/2024.1/bin/ + echo "------------" export PATH=/opt/intel/oneapi/2024.1/bin/:$PATH export LD_LIBRARY_PATH=/opt/intel/oneapi/2024.1/lib/:$LD_LIBRARY_PATH printenv >> $GITHUB_ENV From fdf2339fb362d29c827e77d9d9ee2951a136c816 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 25 Jun 2024 13:02:48 -0600 Subject: [PATCH 43/59] CI: Another try with syntax fixed --- .github/workflows/intel-build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/intel-build.yml b/.github/workflows/intel-build.yml index 25757032e2..6a7083fd4d 100644 --- a/.github/workflows/intel-build.yml +++ b/.github/workflows/intel-build.yml @@ -149,13 +149,13 @@ jobs: mkdir build cd build source /opt/intel/oneapi/setvars.sh - echo "------------" - ls /opt/intel/oneapi/ - echo "------------" - ls /opt/intel/oneapi/2024.1/ - echo "------------" - ls /opt/intel/oneapi/2024.1/bin/ - echo "------------" + echo "------------" + ls /opt/intel/oneapi/ + echo "------------" + ls /opt/intel/oneapi/2024.1/ + echo "------------" + ls /opt/intel/oneapi/2024.1/bin/ + echo "------------" export PATH=/opt/intel/oneapi/2024.1/bin/:$PATH export LD_LIBRARY_PATH=/opt/intel/oneapi/2024.1/lib/:$LD_LIBRARY_PATH printenv >> $GITHUB_ENV From 57dab19efc5c82fc3c9f07af7eb6752452b9fe22 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 25 Jun 2024 13:12:34 -0600 Subject: [PATCH 44/59] CI: Use newer release of intel compiler 2024.2 --- .github/workflows/intel-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/intel-build.yml b/.github/workflows/intel-build.yml index 6a7083fd4d..012a105446 100644 --- a/.github/workflows/intel-build.yml +++ b/.github/workflows/intel-build.yml @@ -152,12 +152,12 @@ jobs: echo "------------" ls /opt/intel/oneapi/ echo "------------" - ls /opt/intel/oneapi/2024.1/ + ls /opt/intel/oneapi/2024.2/ echo "------------" - ls /opt/intel/oneapi/2024.1/bin/ + ls /opt/intel/oneapi/2024.2/bin/ echo "------------" - export PATH=/opt/intel/oneapi/2024.1/bin/:$PATH - export LD_LIBRARY_PATH=/opt/intel/oneapi/2024.1/lib/:$LD_LIBRARY_PATH + export PATH=/opt/intel/oneapi/2024.2/bin/:$PATH + export LD_LIBRARY_PATH=/opt/intel/oneapi/2024.2/lib/:$LD_LIBRARY_PATH printenv >> $GITHUB_ENV NUMPROCS=2 COMPILER=${{ matrix.compiler }} INSTALL_PATH=${HOME}/environments/${{ matrix.compiler }}-${{ matrix.hdf5 }}-${{ matrix.netcdf }}-${{ matrix.cgns }} bash ../cmake-config From ef1bdc6e6a9594a5e48682171fe6c44a5d609c7a Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Mon, 8 Jul 2024 16:52:31 -0600 Subject: [PATCH 45/59] IOSS: Fix so latest fmt version works --- packages/seacas/applications/zellij/Cell.C | 4 ++-- packages/seacas/libraries/ioss/src/Ioss_Field.C | 1 + packages/seacas/libraries/ioss/src/Ioss_VariableType.C | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/seacas/applications/zellij/Cell.C b/packages/seacas/applications/zellij/Cell.C index 30d13d8014..53b0d9f347 100644 --- a/packages/seacas/applications/zellij/Cell.C +++ b/packages/seacas/applications/zellij/Cell.C @@ -1,4 +1,4 @@ -// Copyright(C) 2021, 2022, 2023 National Technology & Engineering Solutions +// Copyright(C) 2021, 2022, 2023, 2024 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. // @@ -21,7 +21,7 @@ extern unsigned int debug_level; template <> struct fmt::formatter : formatter { // parse is inherited from formatter. - template auto format(Loc l, FormatContext &ctx) + template auto format(Loc l, FormatContext &ctx) const { std::string name = "unknown"; switch (l) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_Field.C b/packages/seacas/libraries/ioss/src/Ioss_Field.C index c110b825c5..0e0b33a8be 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Field.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Field.C @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/packages/seacas/libraries/ioss/src/Ioss_VariableType.C b/packages/seacas/libraries/ioss/src/Ioss_VariableType.C index c14a7cfa15..21a21216f5 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_VariableType.C +++ b/packages/seacas/libraries/ioss/src/Ioss_VariableType.C @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include From 07ae2a4b6c4f17843e2caf04fd57f494dbc75b0b Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 9 Jul 2024 07:59:45 -0600 Subject: [PATCH 46/59] NEM_SPREAD: Add missing include file --- packages/seacas/applications/nem_spread/ps_pario_const.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/seacas/applications/nem_spread/ps_pario_const.h b/packages/seacas/applications/nem_spread/ps_pario_const.h index 43442abd6d..916a299fbe 100644 --- a/packages/seacas/applications/nem_spread/ps_pario_const.h +++ b/packages/seacas/applications/nem_spread/ps_pario_const.h @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2020, 2022, 2023 National Technology & Engineering Solutions + * Copyright(C) 1999-2020, 2022, 2023, 2024 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. * @@ -8,6 +8,7 @@ #pragma once #include "rf_io_const.h" +#include #include /* Global variables. */ From ff688ee74b9a02d5e6de9ce349b2f2269c554cce Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 9 Jul 2024 08:59:34 -0600 Subject: [PATCH 47/59] CI: Remove HDF5-1.8 support --- .github/workflows/build_test.yml | 4 ++-- README.md | 2 +- install-tpl.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 6061efd5b8..e015620ee3 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -21,7 +21,7 @@ jobs: strategy: matrix: compiler: [ gnu, clang, mpi ] - hdf5: [ V18, V110, V114 ] + hdf5: [ V110, V114 ] netcdf: [ 4.9.2 ] cgns: [ 4.4.0 ] steps: @@ -62,7 +62,7 @@ jobs: strategy: matrix: compiler: [ gnu, clang, mpi ] - hdf5: [ V18, V110, V114 ] + hdf5: [ V110, V114 ] netcdf: [ 4.9.2 ] cgns: [ 4.4.0 ] steps: diff --git a/README.md b/README.md index 9e8fb3d2b5..98b3134eb3 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ manually as detailed in | GNU_PARALLEL | YES, NO | YES | Should GNU parallel script be built. | | FMT | YES, NO | YES | Should Lib::FMT TPL be built. | | CATCH2 | YES, NO | YES | Should Catch2 be built (used for testing). | -| H5VERSION | V114, V110, V18 | V114 | Use HDF5-1.14.X, HDF5-1.10.X or HDF5-1.8.X | +| H5VERSION | V114, V110 | V114 | Use HDF5-1.14.X or HDF5-1.10.X | | H5CPP | YES, NO | NO | Should the HDF5 C++ library be built/installed | | BB | YES, NO | NO | Enable Burst Buffer support in PnetCDF | | JOBS | {count} | 2 | Number of "jobs" used for simultaneous compiles | diff --git a/install-tpl.sh b/install-tpl.sh index f57a0502b8..500196867d 100755 --- a/install-tpl.sh +++ b/install-tpl.sh @@ -427,7 +427,7 @@ then elif [ "${H5VERSION}" == "develop" ]; then hdf_version="develop" else - echo 1>&2 ${txtred}Invalid HDF5 version specified: ${H5VERSION}. Must be one of V18, V110, V112. exiting.${txtrst} + echo 1>&2 ${txtred}Invalid HDF5 version specified: ${H5VERSION}. Must be one of V110, V112, or V114. exiting.${txtrst} exit 1 fi From 11a7abec5e35a7ecc09ffc3e64cdee2f5a64fc6a Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 9 Jul 2024 10:48:11 -0600 Subject: [PATCH 48/59] IOSS: Fix reading db with inconsistent timestep count --- .../ioss/src/exodus/Ioex_BaseDatabaseIO.C | 3 +++ .../ioss/src/exodus/Ioex_DatabaseIO.C | 23 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index 9a0cc0949f..53f6e8e07f 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -1623,6 +1623,9 @@ namespace Ioex { // common int64_t BaseDatabaseIO::add_results_fields(Ioss::GroupingEntity *entity, int64_t position) { + auto timestep_count = get_region()->get_optional_property("state_count", 0); + if (timestep_count == 0) return 0; + ex_entity_type type = Ioex::map_exodus_type(entity->type()); return internal_add_results_fields(type, entity, position, m_groupCount[type], m_truthTable[type], m_variables[type]); diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C index 5e0540cc7e..83ac5b8419 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C @@ -682,6 +682,24 @@ namespace Ioex { { Ioss::SerializeIO serializeIO_(this); timestep_count = ex_inquire_int(get_file_pointer(), EX_INQ_TIME); + // Need to sync timestep count across ranks if parallel... + if (isParallel) { + auto min_timestep_count = util().global_minmax(timestep_count, Ioss::ParallelUtils::DO_MIN); + if (min_timestep_count == 0) { + auto max_timestep_count = util().global_minmax(timestep_count, Ioss::ParallelUtils::DO_MAX); + if (max_timestep_count != 0) { + if (myProcessor == 0) { + // NOTE: Don't want to warn on all processors if the + // timestep count is zero on some, but not all ranks. + fmt::print(Ioss::WarnOut(), + "At least one database has no timesteps. No times will be read on ANY" + " database for consistency.\n"); + } + } + } + timestep_count = min_timestep_count; + } + if (timestep_count <= 0) { return; } @@ -1125,12 +1143,11 @@ namespace Ioex { Ioss::Int64Vector counts(m_groupCount[entity_type] * 4); Ioss::Int64Vector local_X_count(m_groupCount[entity_type]); Ioss::Int64Vector global_X_count(m_groupCount[entity_type]); - int iblk; { Ioss::SerializeIO serializeIO_(this); - for (iblk = 0; iblk < m_groupCount[entity_type]; iblk++) { + for (int iblk = 0; iblk < m_groupCount[entity_type]; iblk++) { int index = 4 * iblk; int64_t id = X_block_ids[iblk]; @@ -1184,7 +1201,7 @@ namespace Ioex { // querying if none. int nmap = std::numeric_limits::max(); // Number of 'block' vars on database. Used to skip // querying if none. - for (iblk = 0; iblk < m_groupCount[entity_type]; iblk++) { + for (int iblk = 0; iblk < m_groupCount[entity_type]; iblk++) { int index = 4 * iblk; int64_t nodes_per_X = counts[index + 0]; int64_t edges_per_X = counts[index + 1]; From 48af1ba70d6f88f89a99fd87d88177d5974c7ca2 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 9 Jul 2024 11:28:58 -0600 Subject: [PATCH 49/59] IOSS: Clean up timestep count fix --- .../ioss/src/exodus/Ioex_BaseDatabaseIO.C | 4 +-- .../ioss/src/exodus/Ioex_BaseDatabaseIO.h | 1 + .../ioss/src/exodus/Ioex_DatabaseIO.C | 27 +++++++++---------- .../ioss/src/exodus/Ioex_ParallelDatabaseIO.C | 4 +-- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index 53f6e8e07f..dddad93efd 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -1623,9 +1623,7 @@ namespace Ioex { // common int64_t BaseDatabaseIO::add_results_fields(Ioss::GroupingEntity *entity, int64_t position) { - auto timestep_count = get_region()->get_optional_property("state_count", 0); - if (timestep_count == 0) return 0; - + if (m_timestepCount == 0) return 0; ex_entity_type type = Ioex::map_exodus_type(entity->type()); return internal_add_results_fields(type, entity, position, m_groupCount[type], m_truthTable[type], m_variables[type]); diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h index bf6484e9c7..f391f78170 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h @@ -324,6 +324,7 @@ namespace Ioex { time_t timeLastFlush{0}; int flushInterval{-1}; + int m_timestepCount{0}; mutable bool fileExists{false}; // False if file has never been opened/created mutable bool minimizeOpenFiles{false}; diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C index 83ac5b8419..fb3f0ac6c0 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C @@ -642,13 +642,12 @@ namespace Ioex { { bool exists = false; double last_time = DBL_MAX; - int timestep_count = 0; std::vector tsteps(0); if (dbUsage == Ioss::WRITE_HISTORY) { if (myProcessor == 0) { - timestep_count = ex_inquire_int(get_file_pointer(), EX_INQ_TIME); - if (timestep_count <= 0) { + m_timestepCount = ex_inquire_int(get_file_pointer(), EX_INQ_TIME); + if (m_timestepCount <= 0) { return; } @@ -657,15 +656,15 @@ namespace Ioex { // Read the timesteps and add them to the region. // Since we can't access the Region's stateCount directly, we just add // all of the steps and assume the Region is dealing with them directly... - tsteps.resize(timestep_count); + tsteps.resize(m_timestepCount); int error = ex_get_all_times(get_file_pointer(), Data(tsteps)); if (error < 0) { Ioex::exodus_error(get_file_pointer(), __LINE__, __func__, __FILE__); } - int max_step = properties.get_optional("APPEND_OUTPUT_AFTER_STEP", timestep_count); - max_step = std::min(max_step, timestep_count); + int max_step = properties.get_optional("APPEND_OUTPUT_AFTER_STEP", m_timestepCount); + max_step = std::min(max_step, m_timestepCount); double max_time = properties.get_optional("APPEND_OUTPUT_AFTER_TIME", std::numeric_limits::max()); @@ -681,12 +680,12 @@ namespace Ioex { else { { Ioss::SerializeIO serializeIO_(this); - timestep_count = ex_inquire_int(get_file_pointer(), EX_INQ_TIME); + m_timestepCount = ex_inquire_int(get_file_pointer(), EX_INQ_TIME); // Need to sync timestep count across ranks if parallel... if (isParallel) { - auto min_timestep_count = util().global_minmax(timestep_count, Ioss::ParallelUtils::DO_MIN); + auto min_timestep_count = util().global_minmax(m_timestepCount, Ioss::ParallelUtils::DO_MIN); if (min_timestep_count == 0) { - auto max_timestep_count = util().global_minmax(timestep_count, Ioss::ParallelUtils::DO_MAX); + auto max_timestep_count = util().global_minmax(m_timestepCount, Ioss::ParallelUtils::DO_MAX); if (max_timestep_count != 0) { if (myProcessor == 0) { // NOTE: Don't want to warn on all processors if the @@ -697,16 +696,16 @@ namespace Ioex { } } } - timestep_count = min_timestep_count; + m_timestepCount = min_timestep_count; } - if (timestep_count <= 0) { + if (m_timestepCount <= 0) { return; } // For an exodus file, timesteps are global and are stored in the region. // Read the timesteps and add to the region - tsteps.resize(timestep_count, -std::numeric_limits::max()); + tsteps.resize(m_timestepCount, -std::numeric_limits::max()); // The `EXODUS_CALL_GET_ALL_TIMES=NO` is typically only used in // isSerialParallel mode and the client is responsible for @@ -752,8 +751,8 @@ namespace Ioex { // One use case is that job is restarting at a time prior to what has been // written to the results file, so want to start appending after // restart time instead of at end time on database. - int max_step = properties.get_optional("APPEND_OUTPUT_AFTER_STEP", timestep_count); - max_step = std::min(max_step, timestep_count); + int max_step = properties.get_optional("APPEND_OUTPUT_AFTER_STEP", m_timestepCount); + max_step = std::min(max_step, m_timestepCount); double max_time = properties.get_optional("APPEND_OUTPUT_AFTER_TIME", std::numeric_limits::max()); diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C index b0d9f4304d..8ce9dd59b7 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C @@ -793,10 +793,9 @@ namespace Ioex { Ioex::read_exodus_basis(exoid); Ioex::read_exodus_quadrature(exoid); - get_elemblocks(); - get_step_times_nl(); + get_elemblocks(); get_nodeblocks(); get_edgeblocks(); get_faceblocks(); @@ -922,6 +921,7 @@ namespace Ioex { { timestep_count = ex_inquire_int(get_file_pointer(), EX_INQ_TIME); + m_timestepCount = timestep_count; if (timestep_count <= 0) { return; } From f6fc7727d92b6bda670002e516aeba1190c53c33 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 10 Jul 2024 14:06:41 -0600 Subject: [PATCH 50/59] CI: Spack build should use develop --- .github/workflows/spack.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/spack.yml b/.github/workflows/spack.yml index 8979e8742c..bee277d22d 100644 --- a/.github/workflows/spack.yml +++ b/.github/workflows/spack.yml @@ -6,9 +6,6 @@ on: push: branches: - master - pull_request: - branches: - - master concurrency: group: ${{ github.workflow}}-${{ github.head_ref }} @@ -28,8 +25,8 @@ jobs: - run: | spack external find spack compiler find - spack spec seacas~mpi - spack install seacas~mpi + spack spec seacas@develop~mpi + spack install seacas@develop~mpi spack find # spack spec seacas+mpi # spack install seacas+mpi From 8fc2cc54df22164d5acd3f48ad5d94f03d5854f5 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 10 Jul 2024 14:17:54 -0600 Subject: [PATCH 51/59] CI: See if this uses commit sha --- .github/workflows/spack.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/spack.yml b/.github/workflows/spack.yml index bee277d22d..97babb223d 100644 --- a/.github/workflows/spack.yml +++ b/.github/workflows/spack.yml @@ -6,6 +6,9 @@ on: push: branches: - master + pull_request: + branches: + - master concurrency: group: ${{ github.workflow}}-${{ github.head_ref }} @@ -25,8 +28,8 @@ jobs: - run: | spack external find spack compiler find - spack spec seacas@develop~mpi - spack install seacas@develop~mpi + spack spec seacasd@${{ github.sha }}~mpi + spack install seacas@${{ github.sha }}~mpi spack find # spack spec seacas+mpi # spack install seacas+mpi From 86a670ba547c47da9844bdd7177e4f773baf8a8c Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 10 Jul 2024 14:20:37 -0600 Subject: [PATCH 52/59] CI: Remove typo --- .github/workflows/spack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spack.yml b/.github/workflows/spack.yml index 97babb223d..f5899463fd 100644 --- a/.github/workflows/spack.yml +++ b/.github/workflows/spack.yml @@ -28,7 +28,7 @@ jobs: - run: | spack external find spack compiler find - spack spec seacasd@${{ github.sha }}~mpi + spack spec seacas@${{ github.sha }}~mpi spack install seacas@${{ github.sha }}~mpi spack find # spack spec seacas+mpi From 5c1f1f009c2df458dca83eec6e1b16a60edf804c Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 10 Jul 2024 14:48:00 -0600 Subject: [PATCH 53/59] CI: Spack build on master only [ci skip] --- .github/workflows/spack.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/spack.yml b/.github/workflows/spack.yml index f5899463fd..b06265879d 100644 --- a/.github/workflows/spack.yml +++ b/.github/workflows/spack.yml @@ -6,9 +6,6 @@ on: push: branches: - master - pull_request: - branches: - - master concurrency: group: ${{ github.workflow}}-${{ github.head_ref }} @@ -28,8 +25,8 @@ jobs: - run: | spack external find spack compiler find - spack spec seacas@${{ github.sha }}~mpi - spack install seacas@${{ github.sha }}~mpi + spack spec seacas@master~mpi + spack install seacas@$master~mpi spack find # spack spec seacas+mpi # spack install seacas+mpi From c3d57f7b80c61725a38896ed85d42ab40f96fc9a Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 19 Jun 2024 14:33:25 -0600 Subject: [PATCH 54/59] IOSS: Add progress output to chain/face generation --- .../seacas/libraries/ioss/src/Ioss_ChainGenerator.C | 8 ++++++-- .../seacas/libraries/ioss/src/Ioss_FaceGenerator.C | 12 ++++++++++++ .../seacas/libraries/ioss/src/Ioss_FaceGenerator.h | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C index 365fee7b65..82b78b0cbd 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C @@ -193,8 +193,10 @@ namespace Ioss { Ioss::chain_t generate_element_chains(Ioss::Region ®ion, const std::string &surface_list, int debug_level, INT /*dummy*/) { - debug = debug_level; - size_t numel = region.get_property("element_count").get_int(); + region.get_database()->progress(__func__); + + debug = debug_level; + size_t numel = region.get_property("element_count").get_int(); // Determine which element block(s) are adjacent to the faceset specifying "lines" // The `adjacent_blocks` contains the names of all element blocks that are adjacent to the @@ -223,6 +225,7 @@ namespace Ioss { // Generate the faces for use later... (only generate on the blocks touching the front) Ioss::FaceGenerator face_generator(region); face_generator.generate_block_faces(adjacent_blocks, (INT)0, true); + region.get_database()->progress("\tAfter generate_block_faces"); Ioss::chain_t element_chains(numel); for (const auto *block : adjacent_blocks) { @@ -296,6 +299,7 @@ namespace Ioss { next_front.clear(); } } // End of block loop + region.get_database()->progress("\tAfter generating chains"); return element_chains; } } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C index e230989bd8..63f8e9c027 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C @@ -330,6 +330,11 @@ namespace Ioss { return faces_[name]; } + void FaceGenerator::progress(const std::string &output) const + { + region_.get_database()->progress(output); + } + void FaceGenerator::clear(const Ioss::ElementBlock *block) { const auto &name = block->name(); @@ -342,6 +347,7 @@ namespace Ioss { template void FaceGenerator::generate_faces(INT /*dummy*/, bool block_by_block, bool local_ids) { + progress(__func__); if (block_by_block) { const auto &ebs = region_.get_element_blocks(); generate_block_faces(ebs, INT(0), local_ids); @@ -353,6 +359,7 @@ namespace Ioss { template void FaceGenerator::hash_node_ids(const std::vector &node_ids) { + progress(__func__); hashIds_.reserve(node_ids.size()); for (auto &id : node_ids) { hashIds_.push_back(id_hash(id)); @@ -376,6 +383,7 @@ namespace Ioss { void FaceGenerator::generate_block_faces(const Ioss::ElementBlockContainer &ebs, INT /*dummy*/, bool local_ids) { + progress(__func__); // Convert ids into hashed-ids Ioss::NodeBlock *nb = region_.get_node_blocks()[0]; @@ -395,6 +403,7 @@ namespace Ioss { #endif for (const auto &eb : ebs) { + progress("\tgenerate_block_faces: " + eb->name()); const std::string &name = eb->name(); size_t numel = eb->entity_count(); size_t reserve = 2.0 * numel; @@ -402,6 +411,7 @@ namespace Ioss { faces_[name].max_load_factor(0.9); internal_generate_faces(eb, faces_[name], ids, hashIds_, local_ids, (INT)0); } + progress("\tgenerate_block_faces: end of blocks"); #if DO_TIMING auto endf = std::chrono::steady_clock::now(); @@ -437,11 +447,13 @@ namespace Ioss { fmt::print("Total time: \t{:.6} ms\n\n", std::chrono::duration(endp - starth).count()); #endif + progress("\tgenerate_block_faces: end of routine"); hashIds_.clear(); } template void FaceGenerator::generate_model_faces(INT /*dummy*/, bool local_ids) { + progress(__func__); // Convert ids into hashed-ids Ioss::NodeBlock *nb = region_.get_node_blocks()[0]; diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h index ed9ada94a1..133ec3f266 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h @@ -136,6 +136,8 @@ namespace Ioss { //! Given a local node id (0-based), return the hashed value. size_t node_id_hash(size_t local_node_id) const { return hashIds_[local_node_id]; } + void progress(const std::string &output) const; + private: template void hash_node_ids(const std::vector &node_ids); void hash_local_node_ids(size_t count); From a5dc7bb0ad12bf9e7410ac00cc06adaabb188c66 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 19 Jun 2024 14:34:12 -0600 Subject: [PATCH 55/59] IOSS: Reduce Face memory --- packages/seacas/applications/zellij/UnitCell.C | 2 +- .../libraries/ioss/src/Ioss_ChainGenerator.C | 8 ++++---- .../seacas/libraries/ioss/src/Ioss_CopyDatabase.C | 2 +- .../seacas/libraries/ioss/src/Ioss_FaceGenerator.C | 6 +++--- .../seacas/libraries/ioss/src/Ioss_FaceGenerator.h | 14 +++++++++++--- .../seacas/libraries/ioss/src/cgns/Iocgns_Utils.C | 4 ++-- packages/seacas/libraries/ioss/src/main/skinner.C | 2 +- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/seacas/applications/zellij/UnitCell.C b/packages/seacas/applications/zellij/UnitCell.C index ccd98e996a..5ecc781ba1 100644 --- a/packages/seacas/applications/zellij/UnitCell.C +++ b/packages/seacas/applications/zellij/UnitCell.C @@ -279,7 +279,7 @@ void UnitCell::generate_boundary_faces(unsigned int which_faces) auto &faces = face_generator.faces("ALL"); Ioss::ElementBlock *block = nullptr; for (const auto &face : faces) { - if (face.elementCount_ == 1) { + if (face.element_count() == 1) { block = get_element_block(block, face.element[0] / 10, m_region); auto block_offset = block->get_offset(); for (int i = 0; i < 6; i++) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C index 82b78b0cbd..27bb94759e 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C @@ -148,7 +148,7 @@ namespace { connectivity_t &face_connectivity) { for (const auto &face : faces) { - for (int i = 0; i < face.elementCount_; i++) { + for (int i = 0; i < face.element_count(); i++) { auto element = face.element[i] / 10 - offset; auto side = face.element[i] % 10; // 0-based side face_connectivity[element][side] = &face; @@ -162,11 +162,11 @@ namespace { for (size_t j = 0; j < 6; j++) { const auto *face = face_connectivity[i][j]; assert(face != nullptr); - int k = (face->elementCount_ > 1 && face->element[0] / 10 - offset != i) ? 1 : 0; + int k = (face->element_count() > 1 && face->element[0] / 10 - offset != i) ? 1 : 0; auto element = face->element[k] / 10; auto side = face->element[k] % 10; assert(side == j); - if (face->elementCount_ > 1) { + if (face->element_count() > 1) { fmt::print( "[{:3}] Element {}, Side {}/{} is Face {}.\tAdjacent to Element {}, Side {}.\n", l++, element, side, j, face->hashId_, face->element[1 - k] / 10, @@ -261,7 +261,7 @@ namespace Ioss { assert(opp_side >= 0); auto *opp_face = face_connectivity[element - offset][opp_side]; // See if there is an element attached to the `opp_side` - if (opp_face->elementCount_ > 1) { + if (opp_face->element_count() > 1) { // Determine which is current element and which is adjacent element... int index = (opp_face->element[0] / 10 == static_castelement)::value_type>(element)) diff --git a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C index a1862313b7..e9f61a7e12 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C +++ b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C @@ -417,7 +417,7 @@ namespace { // Get vector of all boundary faces which will be output as the skin... const auto &faces = face_generator.faces("ALL"); for (const auto &face : faces) { - if (face.elementCount_ == 1) { + if (face.element_count() == 1) { boundary.push_back(face); } } diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C index 63f8e9c027..f8bb48abec 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C @@ -160,7 +160,7 @@ namespace { std::vector potential_count(proc_count); std::vector shared_nodes(proc_count); for (auto &face : faces) { - if (face.elementCount_ == 1) { + if (face.element_count() == 1) { // On 'boundary' -- try to determine whether on processor or exterior // boundary int face_node_count = 0; @@ -193,7 +193,7 @@ namespace { std::vector potential_faces(6 * potential); for (auto &face : faces) { - if (face.elementCount_ == 1) { + if (face.element_count() == 1) { // On 'boundary' -- try to determine whether on processor or exterior // boundary int face_node_count = 0; @@ -219,7 +219,7 @@ namespace { potential_faces[6 * offset + 3] = face.connectivity_[2]; potential_faces[6 * offset + 4] = face.connectivity_[3]; potential_faces[6 * offset + 5] = face.element[0]; - assert(face.elementCount_ == 1); + assert(face.element_count() == 1); potential_offset[i]++; } shared_nodes[i] = 0; // Reset for next trip through face.connectivity_ loop diff --git a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h index 133ec3f266..84f1e328e8 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h @@ -39,14 +39,23 @@ namespace Ioss { void add_element(size_t element_id) const { - if (elementCount_ < 2) { - element[elementCount_++] = element_id; + assert(element_id != 0); + if (element[0] == 0) { + element[0] = element_id; + } + else if (element[1] == 0) { + element[1] = element_id; } else { face_element_error(element_id); } } + int element_count() const + { + return (element[0] != 0) + (element[1] != 0); + } + void add_element(size_t element_id, size_t face_ordinal) const { add_element(element_id * 10 + face_ordinal); @@ -70,7 +79,6 @@ namespace Ioss { // parallel communication maps. May need to save the proc it is // shared with also (which is available in git history) mutable std::array element{}; - mutable int elementCount_{0}; // Should be max of 2 solid elements... std::array connectivity_{}; }; diff --git a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C index f4fa782a21..01a7c4ba55 100644 --- a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C +++ b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C @@ -2101,7 +2101,7 @@ void Iocgns::Utils::generate_boundary_faces( auto &boundary = boundary_faces[name]; auto &faces = face_generator.faces(name); for (auto &face : faces) { - if (face.elementCount_ == 1) { + if (face.element_count() == 1) { boundary.insert(face); } } @@ -3102,7 +3102,7 @@ void Iocgns::Utils::generate_block_faces(Ioss::ElementTopology *topo, size_t num // All faces generated for this element block; now extract boundary faces... for (auto &face : all_faces) { - if (face.elementCount_ == 1) { + if (face.element_count() == 1) { boundary.insert(face); } } diff --git a/packages/seacas/libraries/ioss/src/main/skinner.C b/packages/seacas/libraries/ioss/src/main/skinner.C index 6ab0a4f0bc..bda07b2181 100644 --- a/packages/seacas/libraries/ioss/src/main/skinner.C +++ b/packages/seacas/libraries/ioss/src/main/skinner.C @@ -230,7 +230,7 @@ namespace { auto &boundary = boundary_faces[name]; auto &faces = face_generator.faces(name); for (auto &face : faces) { - if (face.elementCount_ == 1) { + if (face.element_count() == 1) { boundary.push_back(face); } } From 18afdd26efb9aa6f0a82f923c5578a38b23b83f6 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Thu, 11 Jul 2024 09:03:13 -0600 Subject: [PATCH 56/59] EXODUS: Better control over exodus verbosity --- packages/seacas/libraries/exodus/src/ex_utils.c | 12 +++++++++--- .../libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/seacas/libraries/exodus/src/ex_utils.c b/packages/seacas/libraries/exodus/src/ex_utils.c index e23c6007b1..d56969c62f 100644 --- a/packages/seacas/libraries/exodus/src/ex_utils.c +++ b/packages/seacas/libraries/exodus/src/ex_utils.c @@ -2090,9 +2090,15 @@ int exi_handle_mode(unsigned int my_mode, int is_parallel, int run_version) * unless specified differently via environment. */ { - char *option = getenv("EXODUS_VERBOSE"); - if (option != NULL) { - exoptval = EX_VERBOSE; + if (exoptval != EX_VERBOSE) { + /* Avoid getenv call if already in verbose mode */ + char *option = getenv("EXODUS_VERBOSE"); + if (option != NULL) { + exoptval = EX_VERBOSE; + if (option[0] != 'q') { + fprintf(stderr, "EXODUS: Setting EX_VERBOSE mode since EXODUS_VERBOSE environment variable is set.\n"); + } + } } ex_opts(exoptval); /* call required to set ncopts first time through */ } diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index dddad93efd..6749fd7332 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -230,6 +230,13 @@ namespace Ioex { "IOEX: Setting EX_VERBOSE|EX_DEBUG because EX_DEBUG environment variable is set.\n"); ex_opts(EX_VERBOSE | EX_DEBUG); } + // This is also done down in the exodus library, but helps logic to do it here... + if (util().get_environment("EXODUS_VERBOSE", isParallel)) { + fmt::print( + Ioss::DebugOut(), + "IOEX: Exodus error reporting set to VERBOSE because EXODUS_VERBOSE environment variable is set.\n"); + ex_opts(EX_VERBOSE); + } if (!is_input()) { if (util().get_environment("EX_MODE", exodusMode, isParallel)) { From 7f68c86449139f219c14647734991facccf26887 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Thu, 11 Jul 2024 09:19:52 -0600 Subject: [PATCH 57/59] CI: Fix spack workflow syntax [ci skip] --- .github/workflows/spack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spack.yml b/.github/workflows/spack.yml index b06265879d..5044d7710e 100644 --- a/.github/workflows/spack.yml +++ b/.github/workflows/spack.yml @@ -26,7 +26,7 @@ jobs: spack external find spack compiler find spack spec seacas@master~mpi - spack install seacas@$master~mpi + spack install seacas@master~mpi spack find # spack spec seacas+mpi # spack install seacas+mpi From 9157ee337fdefd28722c55631710acacabd35816 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Thu, 11 Jul 2024 14:32:36 -0600 Subject: [PATCH 58/59] IOSS: Add property to disable field metadata output --- .../ioss/src/exodus/Ioex_BaseDatabaseIO.C | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index 6749fd7332..a42c86c7c9 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -230,13 +230,6 @@ namespace Ioex { "IOEX: Setting EX_VERBOSE|EX_DEBUG because EX_DEBUG environment variable is set.\n"); ex_opts(EX_VERBOSE | EX_DEBUG); } - // This is also done down in the exodus library, but helps logic to do it here... - if (util().get_environment("EXODUS_VERBOSE", isParallel)) { - fmt::print( - Ioss::DebugOut(), - "IOEX: Exodus error reporting set to VERBOSE because EXODUS_VERBOSE environment variable is set.\n"); - ex_opts(EX_VERBOSE); - } if (!is_input()) { if (util().get_environment("EX_MODE", exodusMode, isParallel)) { @@ -267,6 +260,7 @@ namespace Ioex { // See if there are any properties that need to (or can) be // handled prior to opening/creating database... +#if NC_HAS_HDF5 bool compress = ((properties.exists("COMPRESSION_LEVEL") && properties.get("COMPRESSION_LEVEL").get_int() > 0) || (properties.exists("COMPRESSION_SHUFFLE") && @@ -275,24 +269,31 @@ namespace Ioex { if (compress) { exodusMode |= EX_NETCDF4; } +#endif if (properties.exists("FILE_TYPE")) { std::string type = properties.get("FILE_TYPE").get_string(); if (type == "netcdf3" || type == "netcdf-3") { exodusMode = EX_CLOBBER; // Reset back to default... } +#if NC_HAS_HDF5 if (type == "netcdf4" || type == "netcdf-4" || type == "hdf5") { exodusMode |= EX_NETCDF4; } +#endif +#if NC_HAS_CDF5 else if (type == "netcdf5" || type == "netcdf-5" || type == "cdf5") { exodusMode |= EX_64BIT_DATA; } +#endif } +#if NC_HAS_HDF5 if (properties.exists("ENABLE_FILE_GROUPS")) { exodusMode |= EX_NETCDF4; exodusMode |= EX_NOCLASSIC; } +#endif if (properties.exists("MAXIMUM_NAME_LENGTH")) { maximumNameLength = properties.get("MAXIMUM_NAME_LENGTH").get_int(); @@ -1630,7 +1631,6 @@ namespace Ioex { // common int64_t BaseDatabaseIO::add_results_fields(Ioss::GroupingEntity *entity, int64_t position) { - if (m_timestepCount == 0) return 0; ex_entity_type type = Ioex::map_exodus_type(entity->type()); return internal_add_results_fields(type, entity, position, m_groupCount[type], m_truthTable[type], m_variables[type]); @@ -2036,7 +2036,11 @@ namespace Ioex { } // Output field metadata - output_field_metadata(); + bool do_metadata = true; + Ioss::Utils::check_set_bool_property(properties, "OUTPUT_FIELD_METADATA", do_metadata); + if (do_metadata) { + output_field_metadata(); + } } } From 71e3b0ba102bebefcb557d708c68567e2a48e6c7 Mon Sep 17 00:00:00 2001 From: Tolu Okusanya Date: Wed, 17 Jul 2024 18:04:29 -0600 Subject: [PATCH 59/59] Modifications based on review --- .../libraries/ioss/src/Ioss_DatabaseIO.h | 6 ++-- .../libraries/ioss/src/Ioss_DynamicTopology.C | 31 ++++++------------- .../libraries/ioss/src/Ioss_DynamicTopology.h | 18 ++--------- .../seacas/libraries/ioss/src/Ioss_Region.C | 30 +++++++++++++----- .../seacas/libraries/ioss/src/Ioss_Region.h | 2 +- .../ioss/src/exodus/Ioex_BaseDatabaseIO.C | 29 ++++++++--------- .../ioss/src/exodus/Ioex_BaseDatabaseIO.h | 2 +- .../src/unit_tests/UnitTestDynamicTopology.C | 25 ++++++--------- 8 files changed, 65 insertions(+), 78 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h index 3c0f348b55..5d193ed64f 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h @@ -285,10 +285,10 @@ namespace Ioss { return open_root_group_nl(); } - bool groups_describe(Ioss::NameList& names, Ioss::NameList *full_names = nullptr) + Ioss::NameList groups_describe(bool return_full_names = false) { IOSS_FUNC_ENTER(m_); - return groups_describe_nl(names, full_names); + return groups_describe_nl(return_full_names); } /** \brief Set the database to the given State. @@ -781,7 +781,7 @@ namespace Ioss { virtual bool open_root_group_nl() { return false; } virtual bool open_group_nl(const std::string & /* group_name */) { return false; } virtual bool create_subgroup_nl(const std::string & /* group_name */) { return false; } - virtual bool groups_describe_nl(Ioss::NameList& /* names */, Ioss::NameList * /* full_names */) { return false; } + virtual Ioss::NameList groups_describe_nl(bool /* return_full_names */) { return Ioss::NameList(); } virtual bool begin_nl(Ioss::State state) = 0; virtual bool end_nl(Ioss::State state) = 0; diff --git a/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C index 85f7d6e783..ac5af4849e 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C +++ b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2024 National Technology & Engineering Solutions +// Copyright(C) 2024 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. // @@ -8,7 +8,6 @@ #include "Ioss_Blob.h" #include "Ioss_CodeTypes.h" #include "Ioss_CommSet.h" -#include "Ioss_CoordinateFrame.h" #include "Ioss_DBUsage.h" #include "Ioss_DatabaseIO.h" #include "Ioss_DynamicTopology.h" @@ -26,32 +25,23 @@ #include "Ioss_IOFactory.h" #include "Ioss_NodeBlock.h" #include "Ioss_NodeSet.h" -#include "Ioss_Property.h" -#include "Ioss_PropertyManager.h" #include "Ioss_Region.h" #include "Ioss_SideBlock.h" #include "Ioss_SideSet.h" -#include "Ioss_SmartAssert.h" -#include "Ioss_Sort.h" -#include "Ioss_State.h" #include "Ioss_StructuredBlock.h" -#include + #include #include #include #include #include -#include #include -#include -#include #include #include #include #include -#include "Ioss_MeshType.h" #include "Ioss_ParallelUtils.h" namespace Ioss { @@ -162,17 +152,17 @@ int DynamicTopologyObserver::get_cumulative_topology_modification_field() return ivalue; } -void DynamicTopologyObserver::define_model(Region& region) +void DynamicTopologyObserver::define_model(IOSS_MAYBE_UNUSED Region& region) { } -void DynamicTopologyObserver::write_model(Region& region) +void DynamicTopologyObserver::write_model(IOSS_MAYBE_UNUSED Region& region) { } -void DynamicTopologyObserver::define_transient(Region& region) +void DynamicTopologyObserver::define_transient(IOSS_MAYBE_UNUSED Region& region) { } @@ -259,13 +249,13 @@ std::string DynamicTopologyFileControl::construct_database_filename(int& step, I // number. Assume maximum of 9999 steps (will do more, but won't have // good lineup of step numbers. // Check database for validity (filename and a type) - if(m_ioDB == "" || m_dbType == "") + if(m_ioDB.empty() || m_dbType.empty()) { std::string error_message; - if(m_dbType == "") + if(m_dbType.empty()) error_message += "The database TYPE has not been defined\n"; - if(m_ioDB == "") + if(m_ioDB.empty()) { error_message += "The database FILENAME has not been defined\n"; } @@ -273,8 +263,8 @@ std::string DynamicTopologyFileControl::construct_database_filename(int& step, I fmt::print(errmsg, error_message); IOSS_ERROR(errmsg); } - assert(m_ioDB != ""); - assert(m_dbType != ""); + assert(!m_ioDB.empty()); + assert(!m_dbType.empty()); std::string filename = m_ioDB; if(m_fileCyclicCount > 0) { @@ -513,7 +503,6 @@ bool DynamicTopologyFileControl::replace_output_database(Ioss::DatabaseIO *db) return false; current_db->finalize_database(); - current_db->flush_database(); current_db->closeDatabase(); delete current_db; diff --git a/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h index 376faa714b..be25390e3c 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h +++ b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2024 National Technology & Engineering Solutions +// Copyright(C) 2024 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. // @@ -8,31 +8,19 @@ #include "Ioss_DatabaseIO.h" // for DatabaseIO #include "Ioss_DBUsage.h" -#include "Ioss_EntityType.h" // for EntityType, etc -#include "Ioss_Field.h" -#include "Ioss_GroupingEntity.h" // for GroupingEntity -#include "Ioss_MeshType.h" #include "Ioss_ParallelUtils.h" // for ParallelUtils -#include "Ioss_Property.h" // for Property -#include "Ioss_State.h" // for State +#include "Ioss_PropertyManager.h" // for PropertyManager #include #include // for size_t, nullptr #include // for int64_t #include "Ioss_CodeTypes.h" #include "Ioss_Utils.h" -#include "Ioss_VariableType.h" #include "ioss_export.h" -#if !defined BUILT_IN_SIERRA -#include -#endif -#include // for less + #include // for ostream -#include // for map, map<>::value_compare #include #include // for string, operator< -#include // for pair -#include // for vector namespace Ioss { class Region; diff --git a/packages/seacas/libraries/ioss/src/Ioss_Region.C b/packages/seacas/libraries/ioss/src/Ioss_Region.C index c58a2e4ce0..82850ffaa1 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Region.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Region.C @@ -378,7 +378,7 @@ namespace Ioss { // Region owns all sub-grouping entities it contains... try { IOSS_FUNC_ENTER(m_); - release_memory(); + reset_region(); // Region owns the database pointer even though other entities use it. GroupingEntity::really_delete_database(); @@ -387,7 +387,7 @@ namespace Ioss { } } - void Region::release_memory() + void Region::reset_region() { for (const auto &nb : nodeBlocks) { delete (nb); @@ -2855,11 +2855,25 @@ namespace Ioss { if (get_database()->is_input()) return; - if(!topologyObserver) - return; + const Ioss::PropertyManager &properties = get_database()->get_property_manager(); + if(!properties.exists("ENABLE_FILE_GROUPS")) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: File groups are not enabled in the database file '{}'.\n", + get_database()->get_filename()); + IOSS_ERROR(errmsg); + } + + if(topologyObserver && (topologyObserver->get_control_option() == FileControlOption::CONTROL_AUTO_MULTI_FILE)) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: TopologyObserver for database file '{}' does not support file groups.\n", + get_database()->get_filename()); + IOSS_ERROR(errmsg); + } int state = steps; - if (topologyObserver->is_topology_modified() || force_addition) { + if ((topologyObserver && topologyObserver->is_topology_modified()) || force_addition) { // Determine how many steps have been written already... state = get_property("state_count").get_int(); @@ -2872,7 +2886,7 @@ namespace Ioss { state++; // For the state we are going to write. - release_memory(); + reset_region(); DynamicTopologyFileControl fileControl(this, fileCyclicCount, ifDatabaseExists, dbChangeCount); fileControl.add_output_database_group(state); } @@ -2910,7 +2924,7 @@ namespace Ioss { state++; // For the state we are going to write. - release_memory(); + reset_region(); DynamicTopologyFileControl fileControl(this, fileCyclicCount, ifDatabaseExists, dbChangeCount); fileControl.clone_and_replace_output_database(state); } @@ -2938,7 +2952,7 @@ namespace Ioss { if(!iodatabase->open_group(group_name)) return false; - release_memory(); + reset_region(); iodatabase->release_memory(); Region::set_state(STATE_CLOSED); diff --git a/packages/seacas/libraries/ioss/src/Ioss_Region.h b/packages/seacas/libraries/ioss/src/Ioss_Region.h index 7d04b9b154..9a7f27754b 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Region.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Region.h @@ -331,7 +331,7 @@ namespace Ioss { bool end_mode_nl(State current_state); void delete_database() override; - void release_memory(); + void reset_region(); mutable std::map aliases_; ///< Stores alias mappings diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index 9a5b38027b..5733bdf694 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -88,7 +88,7 @@ namespace { template void write_attribute_names(int exoid, ex_entity_type type, const std::vector &entities); - bool add_groups(int exoid, Ioss::NameList& names, Ioss::NameList *full_names = nullptr); + void query_groups(int exoid, Ioss::NameList& names, bool return_full_names); class AssemblyTreeFilter { @@ -3219,12 +3219,15 @@ namespace Ioex { } - bool BaseDatabaseIO::groups_describe_nl(Ioss::NameList& names, Ioss::NameList *full_names) + Ioss::NameList BaseDatabaseIO::groups_describe_nl(bool return_full_names) { Ioss::SerializeIO serializeIO_(this); + Ioss::NameList names; int group_root = ex_inquire_int(get_file_pointer(), EX_INQ_GROUP_ROOT); - return add_groups(group_root, names, full_names); + query_groups(group_root, names, return_full_names); + + return names; } void BaseDatabaseIO::release_memory_nl() @@ -3551,7 +3554,7 @@ namespace { #endif } - bool add_groups(int exoid, Ioss::NameList& names, Ioss::NameList *full_names) + void query_groups(int exoid, Ioss::NameList& names, bool return_full_names) { int idum; float rdum; @@ -3562,31 +3565,29 @@ namespace { // Get name of this group... int ierr = ex_inquire(exoid, EX_INQ_GROUP_NAME, &idum, &rdum, group_name.data()); if (ierr < 0) { - return false; + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); } - names.push_back(std::string(group_name.data())); - if(nullptr != full_names) { + if(return_full_names) { std::fill(group_name.begin(), group_name.end(), '\0'); ierr = ex_inquire(exoid, EX_INQ_FULL_GROUP_NAME, &idum, &rdum, group_name.data()); if (ierr < 0) { - return false; + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); } - full_names->push_back(std::string(group_name.data())); + names.push_back(std::string(group_name.data())); + } else { + names.push_back(std::string(group_name.data())); } int num_children = ex_inquire_int(exoid, EX_INQ_NUM_CHILD_GROUPS); std::vector children(num_children); ierr = ex_get_group_ids(exoid, nullptr, Data(children)); if (ierr < 0) { - return false; + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); } - bool rtn = true; for (int i = 0; i < num_children; i++) { - rtn |= add_groups(children[i], names, full_names); + query_groups(children[i], names, return_full_names); } - - return rtn; } } // namespace diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h index f391f78170..11174e6052 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h @@ -104,7 +104,7 @@ namespace Ioex { bool open_root_group_nl() override; bool open_group_nl(const std::string &group_name) override; bool create_subgroup_nl(const std::string &group_name) override; - bool groups_describe_nl(Ioss::NameList& names, Ioss::NameList *full_names = nullptr) override; + Ioss::NameList groups_describe_nl(bool return_full_names) override; bool begin_nl(Ioss::State state) override; bool end_nl(Ioss::State state) override; diff --git a/packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C b/packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C index b8ece953a8..150ff08038 100644 --- a/packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C +++ b/packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C @@ -361,9 +361,8 @@ void run_single_file_simple_topology_change(const std::string &elemFieldName, co run_simple_topology_change(i_region, o_region, observer, elemFieldName); - Ioss::NameList names; - Ioss::NameList full_names; - o_database->groups_describe(names, &full_names); + Ioss::NameList names = o_database->groups_describe(false); + Ioss::NameList full_names = o_database->groups_describe(true); std::vector gold_names{"/", "STEP-1", "STEP-2"}; std::vector gold_full_names{"/", "/STEP-1", "/STEP-2"}; @@ -471,9 +470,8 @@ TEST(TestDynamicWrite, create_subgroup_with_file_reopen) // Group pointer is still at root level o_database->create_subgroup("GROUP_2"); - Ioss::NameList names; - Ioss::NameList full_names; - o_database->groups_describe(names, &full_names); + Ioss::NameList names = o_database->groups_describe(false); + Ioss::NameList full_names = o_database->groups_describe(true); std::vector gold_names{"/", "GROUP_1", "GROUP_2"}; std::vector gold_full_names{"/", "/GROUP_1", "/GROUP_2"}; @@ -526,9 +524,8 @@ TEST(TestDynamicWrite, create_subgroup_with_file_persistence_and_child_group) // Group pointer is at "GROUP_1" ... "GROUP_2" is a child o_database->create_subgroup("GROUP_2"); - Ioss::NameList names; - Ioss::NameList full_names; - o_database->groups_describe(names, &full_names); + Ioss::NameList names = o_database->groups_describe(false); + Ioss::NameList full_names = o_database->groups_describe(true); std::vector gold_names{"/", "GROUP_1", "GROUP_2"}; std::vector gold_full_names{"/", "/GROUP_1", "/GROUP_1/GROUP_2"}; @@ -582,9 +579,8 @@ TEST(TestDynamicWrite, create_subgroup_with_file_persistence_and_no_child_group) EXPECT_TRUE(o_database->open_root_group()); o_database->create_subgroup("GROUP_2"); - Ioss::NameList names; - Ioss::NameList full_names; - o_database->groups_describe(names, &full_names); + Ioss::NameList names = o_database->groups_describe(false); + Ioss::NameList full_names = o_database->groups_describe(true); std::vector gold_names{"/", "GROUP_1", "GROUP_2"}; std::vector gold_full_names{"/", "/GROUP_1", "/GROUP_2"}; @@ -624,9 +620,8 @@ void read_and_test_single_file_simple_topology_change(const std::string& elemFie Ioss::ParallelUtils::comm_world(), propertyManager); - Ioss::NameList names; - Ioss::NameList full_names; - i_database->groups_describe(names, &full_names); + Ioss::NameList names = i_database->groups_describe(false); + Ioss::NameList full_names = i_database->groups_describe(true); std::vector gold_names{"/", "STEP-1", "STEP-2"}; std::vector gold_full_names{"/", "/STEP-1", "/STEP-2"};