From d5d0fdf0eb340a8d6be1ccb5c57aa0363d52ef5d Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 20 Aug 2024 16:05:07 -0600 Subject: [PATCH 1/7] SEACAS: Snapshot 2024-08-20 * APREPRO: Better parsing of strings starting with digits on command line * Conjoin, Ejoin, EPU: Easier to specify "last N steps" on the time selection command * NemSlice, Slice: Handle decomposition of 1D models * IOSS: Reduce memory associated with face generation code/data * EXODUS: Persistent redef mode to improve performance * EXODUS: Number width routine no longer needs math functions * IOSS: Exodus - support for lossy compression (QUANTIZE_NSD) * IOSS: Exodus - support for additional compression algorithms * IOSS: Initial support for better dynamic topology handling * IOSS: Fmt support for fmt-11.X Signed-off-by: Greg Sjaardema --- .../seacas/applications/aprepro/aprepro.C | 20 +- packages/seacas/applications/blot/linthc.blk | 4 +- .../applications/cpup/CP_SystemInterface.C | 20 +- .../seacas/applications/cpup/CP_Version.h | 4 +- packages/seacas/applications/cpup/cpup.C | 10 +- .../applications/ejoin/EJ_SystemInterface.C | 27 +- .../seacas/applications/ejoin/EJ_Version.h | 4 +- packages/seacas/applications/ejoin/EJoin.C | 8 +- .../applications/epu/EP_SystemInterface.C | 25 +- packages/seacas/applications/epu/EP_Version.h | 4 +- packages/seacas/applications/epu/epu.C | 8 +- packages/seacas/applications/exodiff/map.C | 24 +- packages/seacas/applications/nem_slice/elb.h | 2 +- .../seacas/applications/nem_slice/elb_elem.C | 32 +- .../applications/nem_slice/elb_loadbal.C | 661 +++++----- .../applications/nem_slice/elb_output.C | 6 +- .../applications/nem_spread/ps_pario_const.h | 3 +- .../seacas/applications/slice/SL_Version.h | 4 +- packages/seacas/applications/slice/Slice.C | 27 +- packages/seacas/applications/zellij/Cell.C | 4 +- packages/seacas/applications/zellij/Cell.h | 2 +- .../seacas/applications/zellij/UnitCell.C | 2 +- .../seacas/cmake/tpls/FindTPLADIOS2.cmake | 90 -- .../libraries/aprepro_lib/CMakeLists.txt | 2 - .../libraries/exodus/include/exodusII.h | 11 +- .../exodus/include/exodusII_cfg.h.in | 12 +- .../libraries/exodus/include/exodusII_int.h | 42 +- .../src/ex__put_homogenous_block_params.c | 2 +- .../seacas/libraries/exodus/src/ex_add_attr.c | 2 +- .../seacas/libraries/exodus/src/ex_close.c | 7 +- .../seacas/libraries/exodus/src/ex_conv.c | 2 + .../seacas/libraries/exodus/src/ex_copy.c | 4 +- .../libraries/exodus/src/ex_create_group.c | 4 +- packages/seacas/libraries/exodus/src/ex_err.c | 4 +- .../libraries/exodus/src/ex_field_utils.c | 13 +- .../exodus/src/ex_get_field_metadata.c | 16 +- .../libraries/exodus/src/ex_get_group_id.c | 11 +- .../seacas/libraries/exodus/src/ex_open.c | 9 +- .../seacas/libraries/exodus/src/ex_open_par.c | 10 +- .../exodus/src/ex_put_all_var_param_ext.c | 2 +- .../libraries/exodus/src/ex_put_assemblies.c | 2 +- .../libraries/exodus/src/ex_put_attr_param.c | 2 +- .../libraries/exodus/src/ex_put_attribute.c | 6 +- .../libraries/exodus/src/ex_put_blobs.c | 2 +- .../exodus/src/ex_put_block_params.c | 2 +- .../libraries/exodus/src/ex_put_cmap_params.c | 2 +- .../exodus/src/ex_put_cmap_params_cc.c | 2 +- .../exodus/src/ex_put_concat_all_blocks.c | 2 +- .../exodus/src/ex_put_concat_elem_block.c | 2 +- .../libraries/exodus/src/ex_put_concat_sets.c | 2 +- .../exodus/src/ex_put_coordinate_frames.c | 2 +- .../exodus/src/ex_put_field_metadata.c | 71 +- .../libraries/exodus/src/ex_put_id_map.c | 2 +- .../seacas/libraries/exodus/src/ex_put_info.c | 4 +- .../libraries/exodus/src/ex_put_init_ext.c | 2 +- .../libraries/exodus/src/ex_put_init_global.c | 2 +- .../libraries/exodus/src/ex_put_init_info.c | 2 +- .../exodus/src/ex_put_loadbal_param.c | 2 +- .../exodus/src/ex_put_loadbal_param_cc.c | 2 +- .../seacas/libraries/exodus/src/ex_put_map.c | 2 +- .../libraries/exodus/src/ex_put_map_param.c | 2 +- .../seacas/libraries/exodus/src/ex_put_name.c | 2 +- .../libraries/exodus/src/ex_put_num_map.c | 2 +- .../exodus/src/ex_put_partial_id_map.c | 2 +- .../libraries/exodus/src/ex_put_partial_var.c | 2 +- .../seacas/libraries/exodus/src/ex_put_prop.c | 2 +- .../libraries/exodus/src/ex_put_prop_array.c | 2 +- .../libraries/exodus/src/ex_put_prop_names.c | 2 +- .../seacas/libraries/exodus/src/ex_put_qa.c | 4 +- .../src/ex_put_reduction_variable_param.c | 2 +- .../exodus/src/ex_put_reduction_vars.c | 2 +- .../seacas/libraries/exodus/src/ex_put_sets.c | 2 +- .../libraries/exodus/src/ex_put_truth_table.c | 2 +- .../exodus/src/ex_put_var_multi_time.c | 2 +- .../exodus/src/ex_put_variable_param.c | 2 +- .../seacas/libraries/exodus/src/ex_update.c | 9 +- .../seacas/libraries/exodus/src/ex_utils.c | 121 +- .../seacas/libraries/exodus/test/testall.in | 4 +- .../exodus/test/testwt-field-metadata.dmp | 4 - .../exodus/test/testwt-field-metadata2.c | 214 ---- .../ioss/cmake/SEACASIoss_config.h.in | 2 + .../seacas/libraries/ioss/src/CMakeLists.txt | 9 +- .../ioss/src/Ioss_BasisVariableType.C | 26 - .../libraries/ioss/src/Ioss_ChainGenerator.C | 18 +- .../libraries/ioss/src/Ioss_CodeTypes.h | 1 + .../seacas/libraries/ioss/src/Ioss_Compare.C | 5 + .../libraries/ioss/src/Ioss_CopyDatabase.C | 2 +- .../libraries/ioss/src/Ioss_DatabaseIO.C | 4 +- .../libraries/ioss/src/Ioss_DatabaseIO.h | 48 +- .../libraries/ioss/src/Ioss_Decomposition.C | 5 +- .../ioss/src/Ioss_DecompositionUtils.C | 34 +- .../ioss/src/Ioss_DecompositionUtils.h | 8 +- .../seacas/libraries/ioss/src/Ioss_Doxygen.h | 6 +- .../libraries/ioss/src/Ioss_DynamicTopology.C | 728 +++++++++++ .../libraries/ioss/src/Ioss_DynamicTopology.h | 238 ++++ .../libraries/ioss/src/Ioss_FaceGenerator.C | 30 +- .../libraries/ioss/src/Ioss_FaceGenerator.h | 19 +- .../seacas/libraries/ioss/src/Ioss_Field.C | 1 + .../seacas/libraries/ioss/src/Ioss_Field.h | 13 + .../seacas/libraries/ioss/src/Ioss_Glob.h | 2 +- .../libraries/ioss/src/Ioss_IOFactory.C | 4 +- .../libraries/ioss/src/Ioss_ParallelUtils.h | 1 + .../seacas/libraries/ioss/src/Ioss_Region.C | 406 +++++- .../seacas/libraries/ioss/src/Ioss_Region.h | 65 +- .../libraries/ioss/src/Ioss_StructuredBlock.C | 36 +- .../libraries/ioss/src/Ioss_StructuredBlock.h | 12 + .../seacas/libraries/ioss/src/Ioss_Utils.C | 4 +- .../libraries/ioss/src/Ioss_VariableType.C | 1 + .../ioss/src/Ioss_ZoneConnectivity.h | 14 + .../libraries/ioss/src/adios/AdiosWrapper.C | 13 +- .../libraries/ioss/src/adios/AdiosWrapper.hpp | 4 +- .../ioss/src/adios/Ioad_DatabaseIO.h | 24 +- .../ioss/src/catalyst/Iocatalyst_DatabaseIO.C | 54 +- .../Iocatalyst_ConduitReadTest.C | 44 + .../libraries/ioss/src/cgns/Iocgns_Utils.C | 4 +- .../ioss/src/exodus/Ioex_BaseDatabaseIO.C | 276 +++- .../ioss/src/exodus/Ioex_BaseDatabaseIO.h | 7 + .../ioss/src/exodus/Ioex_DatabaseIO.C | 47 +- .../ioss/src/exodus/Ioex_ParallelDatabaseIO.C | 4 +- .../libraries/ioss/src/exodus/Ioex_Utils.C | 3 +- .../src/exonull/Ioexnl_DecompositionData.C | 12 +- .../src/exonull/Ioexnl_DecompositionData.h | 14 +- .../src/faodel/Iofaodel_FieldSerialization.h | 4 +- .../faodel/Iofaodel_PropertySerialization.h | 4 +- .../ioss/src/init/Ionit_Initializer.C | 9 +- .../seacas/libraries/ioss/src/main/io_info.C | 8 + .../libraries/ioss/src/main/shell_interface.C | 26 +- .../seacas/libraries/ioss/src/main/skinner.C | 2 +- .../seacas/libraries/ioss/src/main/sphgen.C | 9 +- .../ioss/src/text_mesh/Iotm_TextMesh.C | 1 + .../ioss/src/unit_tests/CMakeLists.txt | 11 + .../src/unit_tests/UnitTestDynamicTopology.C | 1111 +++++++++++++++++ .../seacas/libraries/ioss/src/utest/Ut_ioad.C | 2 +- packages/seacas/scripts/exomerge3.py | 10 +- 134 files changed, 3996 insertions(+), 1092 deletions(-) delete mode 100644 packages/seacas/cmake/tpls/FindTPLADIOS2.cmake delete mode 100644 packages/seacas/libraries/exodus/test/testwt-field-metadata2.c delete mode 100644 packages/seacas/libraries/ioss/src/Ioss_BasisVariableType.C 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/applications/aprepro/aprepro.C b/packages/seacas/applications/aprepro/aprepro.C index 378ae87c6a23..1b74a9c00fdf 100644 --- a/packages/seacas/applications/aprepro/aprepro.C +++ b/packages/seacas/applications/aprepro/aprepro.C @@ -1,4 +1,4 @@ -// Copyright(C) 1999-2021, 2023 National Technology & Engineering Solutions +// Copyright(C) 1999-2021, 2023, 2024, 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. // @@ -14,6 +14,17 @@ #include "aprepro.h" +namespace { + bool is_double(const std::string &myString) + { + std::istringstream iss(myString); + double f; + iss >> std::noskipws >> f; // noskipws considers leading whitespace invalid + // Check the entire string was consumed and if either failbit or badbit is set + return iss.eof() && !iss.fail(); + } +} // namespace + int main(int argc, char *argv[]) { SEAMS::Aprepro aprepro; @@ -42,7 +53,9 @@ int main(int argc, char *argv[]) value = value.substr(1, value.length() - 2); aprepro.add_variable(var, value, true); // Make it immutable } - else { + // See if `value` contains any characters that are invalid for a number... + + else if (is_double(value)) { try { double dval = std::stod(value); aprepro.add_variable(var, dval, true); @@ -58,6 +71,9 @@ int main(int argc, char *argv[]) } } } + else { + aprepro.add_variable(var, value, true); // Make it immutable + } } else { input_files.emplace_back(argv[ai]); diff --git a/packages/seacas/applications/blot/linthc.blk b/packages/seacas/applications/blot/linthc.blk index 0e53deb870dd..72def06804b4 100644 --- a/packages/seacas/applications/blot/linthc.blk +++ b/packages/seacas/applications/blot/linthc.blk @@ -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 @@ -8,7 +8,7 @@ C See packages/seacas/LICENSE for details REAL MSHBND, BLKBND, ELEBND C -- Line thickness specification for lines appearing C -- on mesh plots. Specification is a real value in the -C -- range 0. - 1000., with 0. being the thinest line and +C -- range 0. - 1000., with 0. being the thinnest line and C -- 1000. being the thickest. C -- MSHBND - Thickness of lines forming the mesh boundary. C -- BLKBND - Thickness of lines forming element block boundaries. diff --git a/packages/seacas/applications/cpup/CP_SystemInterface.C b/packages/seacas/applications/cpup/CP_SystemInterface.C index dd9d21973134..38007c582c36 100644 --- a/packages/seacas/applications/cpup/CP_SystemInterface.C +++ b/packages/seacas/applications/cpup/CP_SystemInterface.C @@ -64,8 +64,12 @@ void Cpup::SystemInterface::enroll_options() options_.enroll("steps", GetLongOption::MandatoryValue, "Specify subset of timesteps to transfer to output file.\n" "\t\tFormat is beg:end:step. 1:10:2 --> 1,3,5,7,9\n" + "\t\tIf the 'beg' or 'end' is < 0, then it is the \"-Nth\" step...\n" + "\t\t-1 is \"first last\" or last, -3 is \"third last\"\n" + "\t\tTo copy just the last 3 steps, do: `-steps -3:-1`\n" "\t\tEnter LAST for last step", "1:", nullptr, true); + options_.enroll("extension", GetLongOption::MandatoryValue, "CGNS database extension for the input files", "cgns"); @@ -466,7 +470,11 @@ void Cpup::SystemInterface::parse_step_option(const char *tokens) //:
  • ":Y" -- 1 to Y by 1
  • //:
  • "::Z" -- 1 to oo by Z
  • //: - //: The count and step must always be >= 0 + //: The step must always be > 0 + //: If the 'from' or 'to' is < 0, then it is the "-Nth" step... + //: -1 is "first last" or last + //: -4 is "fourth last step" + //: To copy just the last 3 steps, do: `-steps -3:-1` // Break into tokens separated by ":" @@ -482,24 +490,24 @@ void Cpup::SystemInterface::parse_step_option(const char *tokens) for (auto &val : vals) { // Parse 'i'th field char tmp_str[128]; - int k = 0; + int k = 0; while (tokens[j] != '\0' && tokens[j] != ':') { tmp_str[k++] = tokens[j++]; } tmp_str[k] = '\0'; if (strlen(tmp_str) > 0) { - val = strtoul(tmp_str, nullptr, 0); + val = strtol(tmp_str, nullptr, 0); } if (tokens[j++] == '\0') { break; // Reached end of string } } - stepMin_ = abs(vals[0]); - stepMax_ = abs(vals[1]); - stepInterval_ = abs(vals[2]); + stepMin_ = vals[0]; + stepMax_ = vals[1]; + stepInterval_ = abs(vals[2]); // step is always positive... } else if (str_equal("LAST", tokens)) { stepMin_ = stepMax_ = -1; diff --git a/packages/seacas/applications/cpup/CP_Version.h b/packages/seacas/applications/cpup/CP_Version.h index 8ba908eaf3a6..9ddb0b5d1402 100644 --- a/packages/seacas/applications/cpup/CP_Version.h +++ b/packages/seacas/applications/cpup/CP_Version.h @@ -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. * @@ -8,4 +8,4 @@ #pragma once #include #include -static const std::array qainfo{"cpup", "0.95 beta", "2023/04/25"}; +static const std::array qainfo{"cpup", "0.96 beta", "2024/08/06"}; diff --git a/packages/seacas/applications/cpup/cpup.C b/packages/seacas/applications/cpup/cpup.C index a382b488ca50..c88c46703bda 100644 --- a/packages/seacas/applications/cpup/cpup.C +++ b/packages/seacas/applications/cpup/cpup.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. // @@ -406,9 +406,11 @@ template void cpup(Cpup::SystemInterface &interFace, INT /*dummy* int ts_max = interFace.step_max(); int ts_step = interFace.step_interval(); - if (ts_min == -1 && ts_max == -1) { - ts_min = num_time_steps; - ts_max = num_time_steps; + if (ts_min < 0) { + ts_min = num_time_steps + 1 + ts_min; + } + if (ts_max < 0) { + ts_max = num_time_steps + 1 + ts_max; } // Time steps for output file diff --git a/packages/seacas/applications/ejoin/EJ_SystemInterface.C b/packages/seacas/applications/ejoin/EJ_SystemInterface.C index a551af6ced4d..f1aeecd6d920 100644 --- a/packages/seacas/applications/ejoin/EJ_SystemInterface.C +++ b/packages/seacas/applications/ejoin/EJ_SystemInterface.C @@ -143,8 +143,11 @@ void SystemInterface::enroll_options() options_.enroll("steps", GetLongOption::MandatoryValue, "Specify subset of timesteps to transfer to output file.\n" "\t\tFormat is beg:end:step. 1:10:2 --> 1,3,5,7,9\n" - "\t\tTo only transfer last step, use '-steps LAST'", - "1:"); + "\t\tIf the 'beg' or 'end' is < 0, then it is the \"-Nth\" step...\n" + "\t\t-1 is \"first last\" or last, -3 is \"third last\"\n" + "\t\tTo copy just the last 3 steps, do: `-steps -3:-1`\n" + "\t\tEnter LAST for last step", + "1:", nullptr, true); options_.enroll("gvar", GetLongOption::MandatoryValue, "Comma-separated list of global variables to be joined or ALL or NONE.", nullptr); @@ -478,15 +481,17 @@ void SystemInterface::parse_step_option(const char *tokens) //: The defined formats for the count attribute are:
    //:
      //:
    • -- default -- 1 <= count <= oo (all steps)
    • - //:
    • "X" -- X <= count <= X (just step X). If X == LAST, last step - // only
    • + //:
    • "X" -- X <= count <= X (just step X) LAST for last step.
    • //:
    • "X:Y" -- X to Y by 1
    • //:
    • "X:" -- X to oo by 1
    • //:
    • ":Y" -- 1 to Y by 1
    • //:
    • "::Z" -- 1 to oo by Z
    • - //:
    • "LAST" -- last step only
    • //:
    - //: The count and step must always be >= 0 + //: The step must always be > 0 + //: If the 'from' or 'to' is < 0, then it is the "-Nth" step... + //: -1 is "first last" or last + //: -4 is "fourth last step" + //: To copy just the last 3 steps, do: `-steps -3:-1` // Break into tokens separated by ":" @@ -502,24 +507,24 @@ void SystemInterface::parse_step_option(const char *tokens) for (auto &val : vals) { // Parse 'i'th field char tmp_str[128]; - int k = 0; + int k = 0; while (tokens[j] != '\0' && tokens[j] != ':') { tmp_str[k++] = tokens[j++]; } tmp_str[k] = '\0'; if (strlen(tmp_str) > 0) { - val = strtoul(tmp_str, nullptr, 0); + val = strtol(tmp_str, nullptr, 0); } if (tokens[j++] == '\0') { break; // Reached end of string } } - stepMin_ = abs(vals[0]); - stepMax_ = abs(vals[1]); - stepInterval_ = abs(vals[2]); + stepMin_ = vals[0]; + stepMax_ = vals[1]; + stepInterval_ = abs(vals[2]); // step is always positive... } else if (str_equal("LAST", tokens)) { stepMin_ = stepMax_ = -1; diff --git a/packages/seacas/applications/ejoin/EJ_Version.h b/packages/seacas/applications/ejoin/EJ_Version.h index 8fad82651f41..9389859444c1 100644 --- a/packages/seacas/applications/ejoin/EJ_Version.h +++ b/packages/seacas/applications/ejoin/EJ_Version.h @@ -9,6 +9,6 @@ static const std::array qainfo{ "ejoin", - "2024/02/21", - "1.6.3", + "2024/08/06", + "1.6.4", }; diff --git a/packages/seacas/applications/ejoin/EJoin.C b/packages/seacas/applications/ejoin/EJoin.C index ef15d73cc1c3..8cbbe7315c41 100644 --- a/packages/seacas/applications/ejoin/EJoin.C +++ b/packages/seacas/applications/ejoin/EJoin.C @@ -528,9 +528,11 @@ double ejoin(SystemInterface &interFace, std::vector &part_mesh, int ts_step = interFace.step_interval(); int num_steps = static_cast(global_times.size()); - if (ts_min == -1 && ts_max == -1) { - ts_min = num_steps; - ts_max = num_steps; + if (ts_min < 0) { + ts_min = num_steps + 1 + ts_min; + } + if (ts_max < 0) { + ts_max = num_steps + 1 + ts_max; } ts_max = ts_max < num_steps ? ts_max : num_steps; diff --git a/packages/seacas/applications/epu/EP_SystemInterface.C b/packages/seacas/applications/epu/EP_SystemInterface.C index 21ff8d183e3c..ca97eb217f02 100644 --- a/packages/seacas/applications/epu/EP_SystemInterface.C +++ b/packages/seacas/applications/epu/EP_SystemInterface.C @@ -175,6 +175,9 @@ void Excn::SystemInterface::enroll_options() options_.enroll("steps", GetLongOption::MandatoryValue, "Specify subset of timesteps to transfer to output file.\n" "\t\tFormat is beg:end:step. 1:10:2 --> 1,3,5,7,9\n" + "\t\tIf the 'beg' or 'end' is < 0, then it is the \"-Nth\" step...\n" + "\t\t-1 is \"first last\" or last, -3 is \"third last\"\n" + "\t\tTo copy just the last 3 steps, do: `-steps -3:-1`\n" "\t\tEnter LAST for last step", "1:", nullptr, true); @@ -545,7 +548,11 @@ void Excn::SystemInterface::parse_step_option(const char *tokens) //:
  • ":Y" -- 1 to Y by 1
  • //:
  • "::Z" -- 1 to oo by Z
  • //: - //: The count and step must always be >= 0 + //: The step must always be > 0 + //: If the 'from' or 'to' is < 0, then it is the "-Nth" step... + //: -1 is "first last" or last + //: -4 is "fourth last step" + //: To copy just the last 3 steps, do: `-steps -3:-1` // Break into tokens separated by ":" @@ -555,34 +562,30 @@ void Excn::SystemInterface::parse_step_option(const char *tokens) if (strchr(tokens, ':') != nullptr) { // The string contains a separator - int vals[3]; - vals[0] = stepMin_; - vals[1] = stepMax_; - vals[2] = stepInterval_; + std::array vals{stepMin_, stepMax_, stepInterval_}; int j = 0; for (auto &val : vals) { // Parse 'i'th field char tmp_str[128]; - ; - int k = 0; + int k = 0; while (tokens[j] != '\0' && tokens[j] != ':') { tmp_str[k++] = tokens[j++]; } tmp_str[k] = '\0'; if (strlen(tmp_str) > 0) { - val = strtoul(tmp_str, nullptr, 0); + val = strtol(tmp_str, nullptr, 0); } if (tokens[j++] == '\0') { break; // Reached end of string } } - stepMin_ = abs(vals[0]); - stepMax_ = abs(vals[1]); - stepInterval_ = abs(vals[2]); + stepMin_ = vals[0]; + stepMax_ = vals[1]; + stepInterval_ = abs(vals[2]); // step is always positive... } else if (str_equal("LAST", tokens)) { stepMin_ = stepMax_ = -1; diff --git a/packages/seacas/applications/epu/EP_Version.h b/packages/seacas/applications/epu/EP_Version.h index aca0e9ff6557..44cabbaaa476 100644 --- a/packages/seacas/applications/epu/EP_Version.h +++ b/packages/seacas/applications/epu/EP_Version.h @@ -11,6 +11,6 @@ static const std::array qainfo{ "epu -- E Pluribus Unum", - "6.09", - "2024/04/18", + "6.10", + "2024/08/06", }; diff --git a/packages/seacas/applications/epu/epu.C b/packages/seacas/applications/epu/epu.C index 89fcb8cf97c9..30a11cefd1a1 100644 --- a/packages/seacas/applications/epu/epu.C +++ b/packages/seacas/applications/epu/epu.C @@ -1325,9 +1325,11 @@ int epu(SystemInterface &interFace, int start_part, int part_count, int cycle, T int ts_max = interFace.step_max(); int ts_step = interFace.step_interval(); - if (ts_min == -1 && ts_max == -1) { - ts_min = num_time_steps; - ts_max = num_time_steps; + if (ts_min < 0) { + ts_min = num_time_steps + 1 + ts_min; + } + if (ts_max < 0) { + ts_max = num_time_steps + 1 + ts_max; } // Time steps for output file diff --git a/packages/seacas/applications/exodiff/map.C b/packages/seacas/applications/exodiff/map.C index 69b1fbae44de..62b872ccc7c5 100644 --- a/packages/seacas/applications/exodiff/map.C +++ b/packages/seacas/applications/exodiff/map.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. // @@ -23,9 +23,9 @@ namespace { double find_range(const double *x, size_t num_nodes); template - INT Find(double x0, double y0, double z0, const std::vector &x, - const std::vector &y, const std::vector &z, const std::vector &id, - int dim, bool ignore_dups); + int64_t Find(double x0, double y0, double z0, const std::vector &x, + const std::vector &y, const std::vector &z, + const std::vector &id, int dim, bool ignore_dups); template void Compute_Node_Map(std::vector &node_map, ExoII_Read &file1, ExoII_Read &file2); @@ -164,7 +164,7 @@ void Compute_Maps(std::vector &node_map, std::vector &elmt_map, ExoII_ } // Locate midpoint in sorted array. - INT sort_idx = Find(mid_x, mid_y, mid_z, x2, y2, z2, id, dim, interFace.ignore_dups); + int64_t sort_idx = Find(mid_x, mid_y, mid_z, x2, y2, z2, id, dim, interFace.ignore_dups); if (sort_idx < 0) { Error(fmt::format("Files are different (couldn't match element {} from block {} from first " @@ -457,7 +457,7 @@ void Compute_Partial_Maps(std::vector &node_map, std::vector &elmt_map } // Locate midpoint in sorted array. - INT sort_idx = Find(mid_x, mid_y, mid_z, x2, y2, z2, id2, dim, interFace.ignore_dups); + int64_t sort_idx = Find(mid_x, mid_y, mid_z, x2, y2, z2, id2, dim, interFace.ignore_dups); if (sort_idx < 0) { unmatched++; if (first && interFace.show_unmatched) { @@ -826,9 +826,9 @@ namespace { } template - INT Find(double x0, double y0, double z0, const std::vector &x, - const std::vector &y, const std::vector &z, const std::vector &id, - int dim, bool ignore_dups) + int64_t Find(double x0, double y0, double z0, const std::vector &x, + const std::vector &y, const std::vector &z, + const std::vector &id, int dim, bool ignore_dups) { if (x.empty()) { return -1; @@ -856,7 +856,7 @@ namespace { } } - INT i = low == N ? N - 1 : low; // Make sure index falls within array bounds. + int64_t i = low == N ? N - 1 : low; // Make sure index falls within array bounds. if (i == 0 && interFace.coord_tol.Diff(x[id[i]], x0)) { // Could not find an index within tolerance on x coordinate. @@ -871,7 +871,7 @@ namespace { // Search until tolerance between the x coordinate fails or a match is found. // If a match is found, the loop continues in order to check for dups. - INT index = -1; + int64_t index = -1; do { if (dim == 1 || (dim == 2 && !interFace.coord_tol.Diff(y[id[i]], y0)) || (dim == 3 && !interFace.coord_tol.Diff(y[id[i]], y0) && @@ -900,7 +900,7 @@ namespace { index = i; } - } while (++i < (INT)N && !interFace.coord_tol.Diff(x[id[i]], x0)); + } while (++i < (int64_t)N && !interFace.coord_tol.Diff(x[id[i]], x0)); interFace.coord_tol.type = save_tolerance_type; return index; diff --git a/packages/seacas/applications/nem_slice/elb.h b/packages/seacas/applications/nem_slice/elb.h index 815c873e6514..6255820f5106 100644 --- a/packages/seacas/applications/nem_slice/elb.h +++ b/packages/seacas/applications/nem_slice/elb.h @@ -14,7 +14,7 @@ #include #include -#define ELB_VERSION "5.00" +#define ELB_VERSION "5.04 (2024/08/19)" #define UTIL_NAME "nem_slice" #define ELB_FALSE 0 #define ELB_TRUE 1 diff --git a/packages/seacas/applications/nem_slice/elb_elem.C b/packages/seacas/applications/nem_slice/elb_elem.C index cd56ccc4d82e..f706c25dee7b 100644 --- a/packages/seacas/applications/nem_slice/elb_elem.C +++ b/packages/seacas/applications/nem_slice/elb_elem.C @@ -326,8 +326,8 @@ int get_elem_info(const int req, const E_Type etype) case BAR2: switch (req) { case NNODES: answer = 2; break; - case NSIDE_NODES: answer = 2; break; - case NSIDES: answer = 1; break; + case NSIDE_NODES: answer = 1; break; + case NSIDES: answer = 2; break; case NDIM: /* number of physical dimensions */ answer = 1; break; default: Gen_Error(0, "fatal: unknown quantity"); @@ -365,8 +365,8 @@ int get_elem_info(const int req, const E_Type etype) case BAR3: switch (req) { case NNODES: answer = 3; break; - case NSIDE_NODES: answer = 2; break; - case NSIDES: answer = 1; break; + case NSIDE_NODES: answer = 1; break; + case NSIDES: answer = 2; break; case NDIM: /* number of physical dimensions */ answer = 1; break; default: Gen_Error(0, "fatal: unknown quantity"); @@ -945,6 +945,15 @@ int get_side_id(const E_Type etype, const INT *connect, const int nsnodes, INT s switch (etype) { case BAR2: case BAR3: + /* SIDE 1 */ + if (side_nodes[0] == connect[0]) { + return 1; + } + if (side_nodes[0] == connect[1]) { + return 2; + } + break; + case SHELL2: case SHELL3: /* SIDE 1 */ @@ -1661,26 +1670,33 @@ int ss_to_node_list(const E_Type etype, /* The element type */ {1, 4, 3, 2, 9, 8, 7, 6, 14} // side 5 (quad) }; - static int bar_table[1][3] = {{1, 2, 3}}; + static int shell_bar_table[1][3] = {{1, 2, 3}}; + static int bar_table[2][1] = {{1}, {2}}; /* Locally decrement side_num */ side_num--; /* Switch over the element type. */ switch (etype) { + case BAR3: case BAR2: + /* Bar1 has 2 sides, each is a single node */ + for (i = 0; i < 1; i++) { + ss_node_list[i] = connect[(bar_table[side_num][i] - 1)]; + } + break; + case SHELL2: /* Bar1 has 1 side */ for (i = 0; i < 2; i++) { - ss_node_list[i] = connect[(bar_table[side_num][i] - 1)]; + ss_node_list[i] = connect[(shell_bar_table[side_num][i] - 1)]; } break; - case BAR3: case SHELL3: /* Bar has 1 side */ for (i = 0; i < 3; i++) { - ss_node_list[i] = connect[(bar_table[side_num][i] - 1)]; + ss_node_list[i] = connect[(shell_bar_table[side_num][i] - 1)]; } break; diff --git a/packages/seacas/applications/nem_slice/elb_loadbal.C b/packages/seacas/applications/nem_slice/elb_loadbal.C index 0d65ba204a41..2b42dbd5b5cf 100644 --- a/packages/seacas/applications/nem_slice/elb_loadbal.C +++ b/packages/seacas/applications/nem_slice/elb_loadbal.C @@ -1535,358 +1535,435 @@ namespace { /* Find the internal and border elements */ double time1 = get_time(); - for (size_t ecnt = 0; ecnt < mesh->num_elems; ecnt++) { - int proc = lb->vertex2proc[ecnt]; - assert(proc < machine->num_procs); - bool internal = true; - int flag = 0; - E_Type etype = mesh->elem_type[ecnt]; - int dim1 = get_elem_info(NDIM, etype); - - /* need to check for hex's or tet's */ - hflag1 = is_hex(etype); + int mesh_dim = mesh->num_dims; + if (mesh_dim == 1) { + std::vector categorized(mesh->num_elems); + for (size_t ecnt = 0; ecnt < mesh->num_elems; ecnt++) { + int proce = lb->vertex2proc[ecnt]; + auto etype = mesh->elem_type[ecnt]; + int nsides = get_elem_info(NSIDES, etype); + assert(nsides == 2); + + /* check each side of this element */ + for (int nscnt = 0; nscnt < nsides; nscnt++) { + + /* get the node on this element side (should only be one)*/ + side_cnt = ss_to_node_list(etype, mesh->connect[ecnt], (nscnt + 1), side_nodes); + assert(side_cnt == 1); + + size_t nhold = graph->sur_elem[side_nodes[0]].size(); + assert(nhold == 1 || nhold == 2); + if (nhold == 2) { + // 2 elements connected to this node -- `ecnt` and the other one... + for (size_t ncnt = 0; ncnt < nhold; ncnt++) { + size_t elem = graph->sur_elem[side_nodes[0]][ncnt]; + if (elem == ecnt) { + continue; + } + int proc2 = lb->vertex2proc[elem]; + if (proce == proc2) { + continue; + } + else { + // Processors of the two elements are different, so we are + // at a processor boundary... + if (!categorized[ecnt]) { + lb->bor_elems[proce].push_back(ecnt); + categorized[ecnt] = 1; + } + lb->e_cmap_elems[proc2].push_back(elem); + lb->e_cmap_sides[proc2].push_back(2 - ncnt); + lb->e_cmap_procs[proc2].push_back(proce); + lb->e_cmap_neigh[proc2].push_back(ecnt); + } + } + } + } + if (!categorized[ecnt]) { + lb->int_elems[proce].push_back(ecnt); + categorized[ecnt] = 1; + } + } + } + else { + for (size_t ecnt = 0; ecnt < mesh->num_elems; ecnt++) { + int proc = lb->vertex2proc[ecnt]; + assert(proc < machine->num_procs); + bool internal = true; + int flag = 0; + auto etype = mesh->elem_type[ecnt]; + int dim1 = get_elem_info(NDIM, etype); - /* a TET10 cannot connect to a HEX */ - tflag1 = is_tet(etype); + /* need to check for hex's or tet's */ + hflag1 = is_hex(etype); - int nsides = get_elem_info(NSIDES, etype); + /* a tet10 cannot connect to a hex */ + tflag1 = is_tet(etype); - /* check each side of this element */ - for (int nscnt = 0; nscnt < nsides; nscnt++) { + int nsides = get_elem_info(NSIDES, etype); - /* get the list of nodes on this element side */ - side_cnt = ss_to_node_list(etype, mesh->connect[ecnt], (nscnt + 1), side_nodes); + /* check each side of this element */ + for (int nscnt = 0; nscnt < nsides; nscnt++) { - /* - * now determine how many side set nodes are needed to - * determine if there is an element connected to this side. - * - * 2-D - need two nodes, so find one intersection - * 3-D - need three nodes, so find two intersections - * NOTE: must check to make sure that this number is not - * larger than the number of nodes on the sides (ie - SHELL). - */ + /* get the list of nodes on this element side */ + side_cnt = ss_to_node_list(etype, mesh->connect[ecnt], (nscnt + 1), side_nodes); - int nnodes = mesh->num_dims; - if (side_cnt < nnodes) { - nnodes = side_cnt; - } - nnodes--; /* decrement to find the number of intersections needed */ + /* + * now determine how many side set nodes are needed to + * determine if there is an element connected to this side. + * + * 1-d - need one node + * 2-d - need two nodes, so find one intersection + * 3-d - need three nodes, so find two intersections + * note: must check to make sure that this number is not + * larger than the number of nodes on the sides (ie - shell). + */ - nelem = 0; /* reset this in case no intersections are needed */ + int nnodes = mesh->num_dims; + if (side_cnt < nnodes) { + nnodes = side_cnt; + } + if (nnodes > 1) + nnodes--; /* decrement to find the number of intersections needed */ - /* - * need to handle hex's differently because of - * the tet/hex combination - */ + nelem = 0; /* reset this in case no intersections are needed */ - if (!hflag1) { /* Not a hex */ + /* + * need to handle hex's differently because of + * the tet/hex combination + */ - /* ignore degenerate bars */ + if (!hflag1) { /* not a hex */ - if (!((etype == BAR2 || etype == SHELL2) && side_nodes[0] == side_nodes[1])) { + if (etype == BAR2 || etype == BAR3) { + size_t nhold = graph->sur_elem[side_nodes[0]].size(); + if (nhold > 1) { + for (size_t ncnt = 0; ncnt < nhold; ncnt++) { + hold_elem[ncnt] = graph->sur_elem[side_nodes[0]][ncnt]; + } - size_t nhold = graph->sur_elem[side_nodes[0]].size(); - for (size_t ncnt = 0; ncnt < nhold; ncnt++) { - hold_elem[ncnt] = graph->sur_elem[side_nodes[0]][ncnt]; - } + for (int ncnt = 0; ncnt < nnodes; ncnt++) { + /* Find elements connected to both node '0' and node 'ncnt+1' */ + nelem = find_inter(Data(hold_elem), Data(graph->sur_elem[side_nodes[(ncnt + 1)]]), + nhold, graph->sur_elem[side_nodes[(ncnt + 1)]].size(), + Data(pt_list)); - for (int ncnt = 0; ncnt < nnodes; ncnt++) { - /* Find elements connected to both node '0' and node 'ncnt+1' */ - nelem = - find_inter(Data(hold_elem), Data(graph->sur_elem[side_nodes[(ncnt + 1)]]), nhold, - graph->sur_elem[side_nodes[(ncnt + 1)]].size(), Data(pt_list)); + if (nelem < 2) { + break; + } - if (nelem < 2) { - break; + nhold = nelem; + for (int ncnt2 = 0; ncnt2 < nelem; ncnt2++) { + hold_elem[ncnt2] = hold_elem[pt_list[ncnt2]]; + } + } } + } + else if (!((etype == BAR2 || etype == SHELL2) && side_nodes[0] == side_nodes[1])) { - nhold = nelem; - for (int ncnt2 = 0; ncnt2 < nelem; ncnt2++) { - hold_elem[ncnt2] = hold_elem[pt_list[ncnt2]]; + size_t nhold = graph->sur_elem[side_nodes[0]].size(); + for (size_t ncnt = 0; ncnt < nhold; ncnt++) { + hold_elem[ncnt] = graph->sur_elem[side_nodes[0]][ncnt]; } - } - } - else { - fmt::print("WARNING: Element = {} is a DEGENERATE BAR\n", ecnt + 1); - } - } - else { /* Is a hex */ - /* - * Hex faces are fairly complicated now. There are two - * exceptions to the standard case: - * 1. it is a degenerate hex (mimics a wedge). this has - * two special faces, the first is a triangle, and the - * second is a 2d line - * 2. two tets are connected to this hex face - */ + for (int ncnt = 0; ncnt < nnodes; ncnt++) { + /* Find elements connected to both node '0' and node 'ncnt+1' */ + nelem = find_inter(Data(hold_elem), Data(graph->sur_elem[side_nodes[(ncnt + 1)]]), + nhold, graph->sur_elem[side_nodes[(ncnt + 1)]].size(), + Data(pt_list)); - /* first need to check for a degenerate element */ - dflag = 0; - if (side_nodes[0] == side_nodes[1] || side_nodes[0] == side_nodes[3]) { - dflag++; - } - if (side_nodes[2] == side_nodes[1] || side_nodes[2] == side_nodes[3]) { - dflag++; - } + if (nelem < 2) { + break; + } - /* - * if both flags are set, then this face is the 2d line, - * and should be ignored with respect to elemental - * communication maps - */ - if (dflag == 2) { - nelem = 1; + nhold = nelem; + for (int ncnt2 = 0; ncnt2 < nelem; ncnt2++) { + hold_elem[ncnt2] = hold_elem[pt_list[ncnt2]]; + } + } + } + else { + fmt::print("WARNING: Element = {} is a DEGENERATE BAR\n", ecnt + 1); + } } - else { + else { /* Is a hex */ + /* - * In order to check for two tets connected to this face, - * check the intersection of opposite corners of this face. - * Both tets should show up in the intersection of one of the - * sets of opposite corners (nothing should show up in the - * other). + * Hex faces are fairly complicated now. There are two + * exceptions to the standard case: + * 1. it is a degenerate hex (mimics a wedge). this has + * two special faces, the first is a triangle, and the + * second is a 2d line + * 2. two tets are connected to this hex face */ + /* first need to check for a degenerate element */ + dflag = 0; + if (side_nodes[0] == side_nodes[1] || side_nodes[0] == side_nodes[3]) { + dflag++; + } + if (side_nodes[2] == side_nodes[1] || side_nodes[2] == side_nodes[3]) { + dflag++; + } + /* - * Initial check is side nodes 0 and 2 which are - * diagonally opposite + * if both flags are set, then this face is the 2d line, + * and should be ignored with respect to elemental + * communication maps */ - int inode = 0; - int node = 2; - size_t nhold = 0; - for (int ncnt = 0; ncnt < nnodes; ncnt++) { - /* Find elements connected to both node 'inode' and node 'node' */ - nelem = find_inter(Data(graph->sur_elem[side_nodes[inode]]), - Data(graph->sur_elem[side_nodes[node]]), - graph->sur_elem[side_nodes[inode]].size(), - graph->sur_elem[side_nodes[node]].size(), Data(pt_list)); - - if (nelem > 1) { - if (ncnt == 0) { - nhold = nelem; - for (int ncnt2 = 0; ncnt2 < nelem; ncnt2++) { - hold_elem[ncnt2] = graph->sur_elem[side_nodes[inode]][pt_list[ncnt2]]; - } + if (dflag == 2) { + nelem = 1; + } + else { + /* + * In order to check for two tets connected to this face, + * check the intersection of opposite corners of this face. + * Both tets should show up in the intersection of one of the + * sets of opposite corners (nothing should show up in the + * other). + */ - if (dflag) { - /* - * in this case, need to get an intersection with - * another (unique) point since nodes 0 and 2 - * may represent an edge and not the diagonal - */ - if (side_nodes[1] != side_nodes[0] && side_nodes[1] != side_nodes[2]) { - node = 1; + /* + * Initial check is side nodes 0 and 2 which are + * diagonally opposite + */ + int inode = 0; + int node = 2; + size_t nhold = 0; + for (int ncnt = 0; ncnt < nnodes; ncnt++) { + /* Find elements connected to both node 'inode' and node 'node' */ + nelem = find_inter(Data(graph->sur_elem[side_nodes[inode]]), + Data(graph->sur_elem[side_nodes[node]]), + graph->sur_elem[side_nodes[inode]].size(), + graph->sur_elem[side_nodes[node]].size(), Data(pt_list)); + + if (nelem > 1) { + if (ncnt == 0) { + nhold = nelem; + for (int ncnt2 = 0; ncnt2 < nelem; ncnt2++) { + hold_elem[ncnt2] = graph->sur_elem[side_nodes[inode]][pt_list[ncnt2]]; + } + + if (dflag) { + /* + * in this case, need to get an intersection with + * another (unique) point since nodes 0 and 2 + * may represent an edge and not the diagonal + */ + if (side_nodes[1] != side_nodes[0] && side_nodes[1] != side_nodes[2]) { + node = 1; + } + else { + node = 3; + } } else { - node = 3; + /* + * in the non-degenerate case, if an element is connected + * to two opposite nodes, then it must share a face. + */ + break; } } else { - /* - * in the non-degenerate case, if an element is connected - * to two opposite nodes, then it must share a face. - */ - break; - } - } - else { - /* This is the second or later time through this - loop and each time through, there have been two - or more elements that are connected to 'node' - (which changes) and 'inode'. We want to make - sure that the elements matched this time through - were also in the list the first time through so - that the elements contain nodes 0 1 2 of the face - and not just 0 1 and 0 2... - So, this time, only put an element in the list if - it was in the list before. - */ - for (size_t ncnt2 = 0; ncnt2 < nhold; ncnt2++) { - hold_elem[ncnt2] = -hold_elem[ncnt2]; - } - for (int ncnt3 = 0; ncnt3 < nelem; ncnt3++) { + /* This is the second or later time through this + loop and each time through, there have been two + or more elements that are connected to 'node' + (which changes) and 'inode'. We want to make + sure that the elements matched this time through + were also in the list the first time through so + that the elements contain nodes 0 1 2 of the face + and not just 0 1 and 0 2... + So, this time, only put an element in the list if + it was in the list before. + */ for (size_t ncnt2 = 0; ncnt2 < nhold; ncnt2++) { - if (-hold_elem[ncnt2] == graph->sur_elem[side_nodes[inode]][pt_list[ncnt3]]) { - hold_elem[ncnt2] = graph->sur_elem[side_nodes[inode]][pt_list[ncnt3]]; - break; + hold_elem[ncnt2] = -hold_elem[ncnt2]; + } + for (int ncnt3 = 0; ncnt3 < nelem; ncnt3++) { + for (size_t ncnt2 = 0; ncnt2 < nhold; ncnt2++) { + if (-hold_elem[ncnt2] == + graph->sur_elem[side_nodes[inode]][pt_list[ncnt3]]) { + hold_elem[ncnt2] = graph->sur_elem[side_nodes[inode]][pt_list[ncnt3]]; + break; + } } } - } - /* Now, go through list and cull out element < 0 */ - size_t ncnt3 = 0; - for (size_t ncnt2 = 0; ncnt2 < nhold; ncnt2++) { - if (hold_elem[ncnt2] >= 0) { - hold_elem[ncnt3] = hold_elem[ncnt2]; - ncnt3++; + /* Now, go through list and cull out element < 0 */ + size_t ncnt3 = 0; + for (size_t ncnt2 = 0; ncnt2 < nhold; ncnt2++) { + if (hold_elem[ncnt2] >= 0) { + hold_elem[ncnt3] = hold_elem[ncnt2]; + ncnt3++; + } + } + nelem = ncnt3; + if (!dflag && nelem > 2) { + fmt::print( + stderr, + "Possible corrupted mesh detected at element {}, strange connectivity.\n", + ecnt); } - } - nelem = ncnt3; - if (!dflag && nelem > 2) { - fmt::print( - stderr, - "Possible corrupted mesh detected at element {}, strange connectivity.\n", - ecnt); } } - } - else { /* nelem == 1 or 0 */ - if (!dflag) { - nhold = graph->sur_elem[side_nodes[1]].size(); - for (size_t ncnt2 = 0; ncnt2 < nhold; ncnt2++) { - hold_elem[ncnt2] = graph->sur_elem[side_nodes[1]][ncnt2]; + else { /* nelem == 1 or 0 */ + if (!dflag) { + nhold = graph->sur_elem[side_nodes[1]].size(); + for (size_t ncnt2 = 0; ncnt2 < nhold; ncnt2++) { + hold_elem[ncnt2] = graph->sur_elem[side_nodes[1]][ncnt2]; + } } + inode = 1; + node = 3; /* The node diagonally opposite node 1 */ } - inode = 1; - node = 3; /* The node diagonally opposite node 1 */ } } - } - } /* "if (!hflag1)" */ - - /* - * if there is an element on this side of ecnt, then there - * will be at least two elements in the intersection (one - * will be ecnt) - */ - if (nelem > 1) { + } /* "if (!hflag1)" */ /* - * now go through and check each element in the list to see - * if it on a different processor than ecnt. Don't need to - * worry about ecnt (which is in the list) since it is on - * the same processor as itself. Note that due to filtering - * done above, we are guaranteed to either have an element - * on a different processor or elem==ecnt. + * if there is an element on this side of ecnt, then there + * will be at least two elements in the intersection (one + * will be ecnt) */ - for (int ncnt = 0; ncnt < nelem; ncnt++) { + if (nelem > 1) { + + /* + * now go through and check each element in the list to see + * if it on a different processor than ecnt. Don't need to + * worry about ecnt (which is in the list) since it is on + * the same processor as itself. Note that due to filtering + * done above, we are guaranteed to either have an element + * on a different processor or elem==ecnt. + */ + for (int ncnt = 0; ncnt < nelem; ncnt++) { - INT elem = hold_elem[ncnt]; - int proc2 = lb->vertex2proc[elem]; - assert(proc2 < machine->num_procs); + INT elem = hold_elem[ncnt]; + int proc2 = lb->vertex2proc[elem]; + assert(proc2 < machine->num_procs); - if (proc != proc2) { + if (proc != proc2) { - E_Type etype2 = mesh->elem_type[elem]; + E_Type etype2 = mesh->elem_type[elem]; - int dim2 = get_elem_info(NDIM, etype2); + int dim2 = get_elem_info(NDIM, etype2); - int diff = abs(dim1 - dim2); + int diff = abs(dim1 - dim2); - /* - * hex's to shells - ok - * shells to bar - ok - * hex to bar - BAD since a BAR will see a HEX but a HEX will not - * see a BAR - */ + /* + * hex's to shells - ok + * shells to bar - ok + * hex to bar - BAD since a BAR will see a HEX but a HEX will not + * see a BAR + */ - if (diff < 2) { - - /* need to check for hex's */ - hflag2 = is_hex(etype2); - tflag2 = is_tet(etype2); - - /* check here for tet/hex combinations */ - if ((tflag1 && hflag2) || (hflag1 && tflag2)) { - /* - * have to call a special function to get the side id - * in these cases. In both cases, the number of side - * nodes for the element will not be consistent with - * side_cnt, and: - * - * TET/HEX - side_nodes only contains three of the - * the side nodes of the hex. - * - * HEX/TET - Have to check that this tet shares a side - * with the hex. - */ - sid = get_side_id_hex_tet(mesh->elem_type[elem], mesh->connect[elem], side_cnt, - side_nodes); - } - else { - /* - * get the side id of elem. Make sure that ecnt is - * trying to communicate to a valid side of elem - */ - side_cnt = get_ss_mirror(etype, side_nodes, (nscnt + 1), mirror_nodes); - - /* - * small kludge to handle 6 node faces butted up against - * 4 node faces - */ - if (etype == HEXSHELL && side_cnt == 6) { - side_cnt = 4; - } + if (diff < 2) { - /* - * in order to get the correct side order for elem, - * get the mirror of the side of ecnt - */ - sid = get_side_id(mesh->elem_type[elem], mesh->connect[elem], side_cnt, - mirror_nodes, problem->skip_checks, problem->partial_adj); - } + /* need to check for hex's */ + hflag2 = is_hex(etype2); + tflag2 = is_tet(etype2); - if (sid > 0) { - /* Element is a border element */ - internal = false; - if (!flag) { - flag = 1; - lb->bor_elems[proc].push_back(ecnt); + /* check here for tet/hex combinations */ + if ((tflag1 && hflag2) || (hflag1 && tflag2)) { + /* + * have to call a special function to get the side id + * in these cases. In both cases, the number of side + * nodes for the element will not be consistent with + * side_cnt, and: + * + * TET/HEX - side_nodes only contains three of the + * the side nodes of the hex. + * + * HEX/TET - Have to check that this tet shares a side + * with the hex. + */ + sid = get_side_id_hex_tet(mesh->elem_type[elem], mesh->connect[elem], side_cnt, + side_nodes); } + else { + /* + * get the side id of elem. Make sure that ecnt is + * trying to communicate to a valid side of elem + */ + side_cnt = get_ss_mirror(etype, side_nodes, (nscnt + 1), mirror_nodes); - /* now put ecnt into proc2's communications map */ - lb->e_cmap_elems[proc2].push_back(elem); - lb->e_cmap_sides[proc2].push_back(sid); - lb->e_cmap_procs[proc2].push_back(proc); - lb->e_cmap_neigh[proc2].push_back(ecnt); - } - else if ((sid < 0) && (!problem->skip_checks)) { - /* - * too many errors with bad meshes, print out - * more information here for diagnostics - */ - cmesg = - fmt::format("Error returned while getting side id for communication map."); - Gen_Error(0, cmesg); - cmesg = fmt::format("Element 1: {}", (ecnt + 1)); - Gen_Error(0, cmesg); - nnodes = get_elem_info(NNODES, etype); - cmesg = "connect table:"; - for (int i = 0; i < nnodes; i++) { - tmpstr = fmt::format(" {}", (size_t)(mesh->connect[ecnt][i] + 1)); - cmesg += tmpstr; - } - Gen_Error(0, cmesg); - cmesg = fmt::format("side id: {}", static_cast(nscnt + 1)); - Gen_Error(0, cmesg); - cmesg = "side nodes:"; - for (int i = 0; i < side_cnt; i++) { - tmpstr = fmt::format(" {}", (size_t)(side_nodes[i] + 1)); - cmesg += tmpstr; + /* + * small kludge to handle 6 node faces butted up against + * 4 node faces + */ + if (etype == HEXSHELL && side_cnt == 6) { + side_cnt = 4; + } + + /* + * in order to get the correct side order for elem, + * get the mirror of the side of ecnt + */ + sid = get_side_id(mesh->elem_type[elem], mesh->connect[elem], side_cnt, + mirror_nodes, problem->skip_checks, problem->partial_adj); } - Gen_Error(0, cmesg); - cmesg = fmt::format("Element 2: {}", (size_t)(elem + 1)); - Gen_Error(0, cmesg); - nnodes = get_elem_info(NNODES, etype2); - cmesg = "connect table:"; - for (int i = 0; i < nnodes; i++) { - tmpstr = fmt::format(" {}", (size_t)(mesh->connect[elem][i] + 1)); - cmesg += tmpstr; + + if (sid > 0) { + /* Element is a border element */ + internal = false; + if (!flag) { + flag = 1; + lb->bor_elems[proc].push_back(ecnt); + } + + /* now put ecnt into proc2's communications map */ + lb->e_cmap_elems[proc2].push_back(elem); + lb->e_cmap_sides[proc2].push_back(sid); + lb->e_cmap_procs[proc2].push_back(proc); + lb->e_cmap_neigh[proc2].push_back(ecnt); } - Gen_Error(0, cmesg); - return 0; /* and get out of here */ + else if ((sid < 0) && (!problem->skip_checks)) { + /* + * too many errors with bad meshes, print out + * more information here for diagnostics + */ + cmesg = + fmt::format("Error returned while getting side id for communication map."); + Gen_Error(0, cmesg); + cmesg = fmt::format("Element 1: {}", (ecnt + 1)); + Gen_Error(0, cmesg); + nnodes = get_elem_info(NNODES, etype); + cmesg = "connect table:"; + for (int i = 0; i < nnodes; i++) { + tmpstr = fmt::format(" {}", (size_t)(mesh->connect[ecnt][i] + 1)); + cmesg += tmpstr; + } + Gen_Error(0, cmesg); + cmesg = fmt::format("side id: {}", static_cast(nscnt + 1)); + Gen_Error(0, cmesg); + cmesg = "side nodes:"; + for (int i = 0; i < side_cnt; i++) { + tmpstr = fmt::format(" {}", (size_t)(side_nodes[i] + 1)); + cmesg += tmpstr; + } + Gen_Error(0, cmesg); + cmesg = fmt::format("Element 2: {}", (size_t)(elem + 1)); + Gen_Error(0, cmesg); + nnodes = get_elem_info(NNODES, etype2); + cmesg = "connect table:"; + for (int i = 0; i < nnodes; i++) { + tmpstr = fmt::format(" {}", (size_t)(mesh->connect[elem][i] + 1)); + cmesg += tmpstr; + } + Gen_Error(0, cmesg); + return 0; /* and get out of here */ - } /* End "if sid < 0 && !problem>skip_checks" */ - } /* End "if (sid > 0)" */ - } /* End "if (proc != proc2)" */ - } /* End "for (ncnt = 0; ncnt < nelem; ncnt++)" */ - } /* End "if (nelem > 1)" */ - } /* End "for (nscnt = 0; nscnt < nsides; nscnt++)" */ + } /* End "if sid < 0 && !problem>skip_checks" */ + } /* End "if (sid > 0)" */ + } /* End "if (proc != proc2)" */ + } /* End "for (ncnt = 0; ncnt < nelem; ncnt++)" */ + } /* End "if (nelem > 1)" */ + } /* End "for (nscnt = 0; nscnt < nsides; nscnt++)" */ - if (internal) { - lb->int_elems[proc].push_back(ecnt); - } - } /* End "for(ecnt=0; ecnt < mesh->num_elems; ecnt++)" */ + if (internal) { + lb->int_elems[proc].push_back(ecnt); + } + } /* End "for(ecnt=0; ecnt < mesh->num_elems; ecnt++)" */ + } time2 = get_time(); fmt::print("Time for elemental categorization: {}s\n", time2 - time1); diff --git a/packages/seacas/applications/nem_slice/elb_output.C b/packages/seacas/applications/nem_slice/elb_output.C index 12557f5b1ff9..7e0869e38b04 100644 --- a/packages/seacas/applications/nem_slice/elb_output.C +++ b/packages/seacas/applications/nem_slice/elb_output.C @@ -174,10 +174,10 @@ int write_nemesis(std::string &nemI_out_file, Machine_Description *machine, std::string time = fmt::format("{:%H:%M:%S}", *lt); std::string date = fmt::format("{:%Y/%m/%d}", *lt); - char qa_date[15]; - char qa_time[10]; + char qa_date[32]; + char qa_time[32]; char qa_name[MAX_STR_LENGTH]; - char qa_vers[10]; + char qa_vers[32]; copy_string(qa_time, time); copy_string(qa_date, date); diff --git a/packages/seacas/applications/nem_spread/ps_pario_const.h b/packages/seacas/applications/nem_spread/ps_pario_const.h index 43442abd6de4..916a299fbeaf 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. */ diff --git a/packages/seacas/applications/slice/SL_Version.h b/packages/seacas/applications/slice/SL_Version.h index 1560a65a3c8e..ca02cc306020 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/06/10", - "2.3.00", + "2024/07/17", + "2.3.01", }; diff --git a/packages/seacas/applications/slice/Slice.C b/packages/seacas/applications/slice/Slice.C index be73865ceb9e..2bba93b462ed 100644 --- a/packages/seacas/applications/slice/Slice.C +++ b/packages/seacas/applications/slice/Slice.C @@ -912,6 +912,8 @@ namespace { size_t proc_size) { progress(__func__); + size_t spatial_dimension = region.get_property("spatial_dimension").get_int(); + std::vector glob_coord_x; std::vector glob_coord_y; std::vector glob_coord_z; @@ -968,8 +970,12 @@ namespace { } else { gnb->get_field_data("mesh_model_coordinates_x", glob_coord_x); - gnb->get_field_data("mesh_model_coordinates_y", glob_coord_y); - gnb->get_field_data("mesh_model_coordinates_z", glob_coord_z); + if (spatial_dimension > 1) { + gnb->get_field_data("mesh_model_coordinates_y", glob_coord_y); + } + if (spatial_dimension > 2) { + gnb->get_field_data("mesh_model_coordinates_z", glob_coord_z); + } progress("\tRead global mesh_model_coordinates"); for (size_t i = 0; i < node_count; i++) { @@ -993,8 +999,12 @@ namespace { for (size_t p = proc_begin; p < proc_begin + proc_size; p++) { Ioss::NodeBlock *nb = proc_region[p]->get_node_blocks()[0]; nb->put_field_data("mesh_model_coordinates_x", coordinates_x[p]); - nb->put_field_data("mesh_model_coordinates_y", coordinates_y[p]); - nb->put_field_data("mesh_model_coordinates_z", coordinates_z[p]); + if (spatial_dimension > 1) { + nb->put_field_data("mesh_model_coordinates_y", coordinates_y[p]); + } + if (spatial_dimension > 2) { + nb->put_field_data("mesh_model_coordinates_z", coordinates_z[p]); + } proc_progress(p, processor_count); } progress("\tOutput processor coordinate vectors"); @@ -1028,7 +1038,9 @@ namespace { } progress("\tReserve processor coordinate vectors"); - for (size_t comp = 0; comp < 3; comp++) { + size_t spatial_dimension = region.get_property("spatial_dimension").get_int(); + + for (size_t comp = 0; comp < spatial_dimension; comp++) { for (size_t p = proc_begin; p < proc_begin + proc_size; p++) { coordinates[p].resize(0); } @@ -1292,9 +1304,10 @@ namespace { offset += element_count; } } + size_t spatial_dimension = region.get_property("spatial_dimension").get_int(); for (size_t p = 0; p < proc_count; p++) { - auto *nb = - new Ioss::NodeBlock(proc_region[p]->get_database(), "node_block1", on_proc_count[p], 3); + auto *nb = new Ioss::NodeBlock(proc_region[p]->get_database(), "node_block1", + on_proc_count[p], spatial_dimension); proc_region[p]->add(nb); if (debug_level & 2) { fmt::print(stderr, "\tProcessor {} has {} nodes.\n", fmt::group_digits(p), diff --git a/packages/seacas/applications/zellij/Cell.C b/packages/seacas/applications/zellij/Cell.C index 30d13d8014ac..53b0d9f34710 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/applications/zellij/Cell.h b/packages/seacas/applications/zellij/Cell.h index 8db0dac91646..5006063f04d3 100644 --- a/packages/seacas/applications/zellij/Cell.h +++ b/packages/seacas/applications/zellij/Cell.h @@ -150,7 +150,7 @@ class Cell double m_offY{0.0}; private: - //! The UnitCell that occupies this location in the grid / latice + //! The UnitCell that occupies this location in the grid / lattice std::shared_ptr m_unitCell; //! The MPI ranks of all surrounding cells in order: diff --git a/packages/seacas/applications/zellij/UnitCell.C b/packages/seacas/applications/zellij/UnitCell.C index ccd98e996a7d..5ecc781ba198 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/cmake/tpls/FindTPLADIOS2.cmake b/packages/seacas/cmake/tpls/FindTPLADIOS2.cmake deleted file mode 100644 index bddbe96e9ca4..000000000000 --- a/packages/seacas/cmake/tpls/FindTPLADIOS2.cmake +++ /dev/null @@ -1,90 +0,0 @@ -# @HEADER -# ************************************************************************ -# -# Trilinos: An Object-Oriented Solver Framework -# Copyright (2001) Sandia Corporation -# -# -# Copyright (2001) Sandia Corporation. Under the terms of Contract -# DE-AC04-94AL85000, there is a non-exclusive license for use of this -# work by or on behalf of the U.S. Government. Export of this program -# may require a license from the United States Government. -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the Corporation nor the names of the -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# NOTICE: The United States Government is granted for itself and others -# acting on its behalf a paid-up, nonexclusive, irrevocable worldwide -# license in this data to reproduce, prepare derivative works, and -# perform publicly and display publicly. Beginning five (5) years from -# July 25, 2001, the United States Government is granted for itself and -# others acting on its behalf a paid-up, nonexclusive, irrevocable -# worldwide license in this data to reproduce, prepare derivative works, -# distribute copies to the public, perform publicly and display -# publicly, and to permit others to do so. -# -# NEITHER THE UNITED STATES GOVERNMENT, NOR THE UNITED STATES DEPARTMENT -# OF ENERGY, NOR SANDIA CORPORATION, NOR ANY OF THEIR EMPLOYEES, MAKES -# ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LEGAL LIABILITY OR -# RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY -# INFORMATION, APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS -# THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS. -# -# ************************************************************************ -# @HEADER - - -MESSAGE("-- Using FIND_PACKAGE(ADIOS2 ...) ...") - -FIND_PACKAGE(ADIOS2) - -IF (ADIOS2_FOUND) - # Tell TriBITS that we found ADIOS2 and there no need to look any further! - GET_TARGET_PROPERTY(_ADIOS2_INCLUDE_DIRS adios2::adios2 INTERFACE_INCLUDE_DIRECTORIES) - GET_TARGET_PROPERTY(_ADIOS2_LIBRARY_LOCATION adios2::adios2 LOCATION) - GET_FILENAME_COMPONENT(ADIOS2_LIBRARY_DIRS ${_ADIOS2_LIBRARY_LOCATION} DIRECTORY) - -# For compatibility with TriBITS: -SET(DOCSTR "List of semi-colon separated paths to look for the TPL ADIOS2") - -GET_TARGET_PROPERTY(ADIOS2_LOCATION adios2::adios2 LOCATION) - -SET(TPL_ADIOS2_LIBRARIES ${ADIOS2_LOCATION} CACHE PATH ${DOCSTR}) -SET(TPL_ADIOS2_INCLUDE_DIRS ${_ADIOS2_INCLUDE_DIRS} CACHE PATH ${DOCSTR}) -SET(TPL_ADIOS2_LIBRARY_DIRS ${_ADIOS2_LIBRARY_DIRS} CACHE PATH ${DOCSTR}) - -ENDIF() - -# -# Third, call TRIBITS_TPL_FIND_INCLUDE_DIRS_AND_LIBRARIES() -# -TRIBITS_TPL_FIND_INCLUDE_DIRS_AND_LIBRARIES( ADIOS2 - REQUIRED_HEADERS adios2.h - REQUIRED_LIBS_NAMES adios2_cxx11_mpi,adios2_cxx11,adios2_core_mpi,adios2_core - ) - -# NOTE: If FIND_PACKAGE(ADIOS2 ...) was called and successfully found ADIOS2, then -# TRIBITS_TPL_FIND_INCLUDE_DIRS_AND_LIBRARIES() will use the already-set -# variables TPL_ADIOS2_INCLUDE_DIRS and TPL_ADIOS2_LIBRARIES and then print them -# out (and set some other standard variables as well). This is the final -# "hook" into the TriBITS TPL system. diff --git a/packages/seacas/libraries/aprepro_lib/CMakeLists.txt b/packages/seacas/libraries/aprepro_lib/CMakeLists.txt index b1665f2b28b5..13fcf791dda3 100644 --- a/packages/seacas/libraries/aprepro_lib/CMakeLists.txt +++ b/packages/seacas/libraries/aprepro_lib/CMakeLists.txt @@ -15,8 +15,6 @@ IF (${CMAKE_PROJECT_NAME}_ENABLE_SEACASExodus) ADD_DEFINITIONS(-DEXODUS_SUPPORT) ENDIF() -ADD_DEFINITIONS(-DFMT_SUPPORT) - TRIBITS_INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/packages/seacas/libraries/exodus/include/exodusII.h b/packages/seacas/libraries/exodus/include/exodusII.h index dd545b5ec7c1..8c978d67aa2a 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 "9.00" +#define EXODUS_VERSION "9.01" #define EXODUS_VERSION_MAJOR 9 -#define EXODUS_VERSION_MINOR 0 -#define EXODUS_RELEASE_DATE "May 30, 2024" +#define EXODUS_VERSION_MINOR 1 +#define EXODUS_RELEASE_DATE "July 17, 2024" -#define EX_API_VERS 9.00f +#define EX_API_VERS 9.01f #define EX_API_VERS_NODOT (100 * EXODUS_VERSION_MAJOR + EXODUS_VERSION_MINOR) #define EX_VERS EX_API_VERS @@ -1045,6 +1045,8 @@ 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_multi_field_metadata(int exoid, const ex_field *field, + const int field_count); 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); @@ -1953,6 +1955,7 @@ enum ex_error_return_code { EX_LASTERR = -1003, /**< in ex_err, use existing err_num value */ EX_NULLENTITY = -1006, /**< null entity found */ EX_NOENTITY = -1007, /**< no entities of that type on database */ + EX_INTSIZEMISMATCH = -1008, /**< integer sizes do not match on input/output databases in ex_copy */ EX_NOTFOUND = -1008, /**< could not find requested variable on database */ EX_FATAL = -1, /**< fatal error flag def */ diff --git a/packages/seacas/libraries/exodus/include/exodusII_cfg.h.in b/packages/seacas/libraries/exodus/include/exodusII_cfg.h.in index f407c0ed7964..304c65b4c734 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 8657fa6755dc..e8bb5aca4ebc 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) @@ -686,28 +686,31 @@ typedef enum exi_element_type exi_element_type; struct exi_file_item { int file_id; - nc_type netcdf_type_code; + nc_type netcdf_type_code; /**< NC_FLOAT or NC_DOUBLE */ int int64_status; int maximum_name_length; int time_varid; /* Store to avoid lookup each timestep */ unsigned int assembly_count; unsigned int blob_count; + unsigned int compression_level; /**< 0 (disabled) to 9 (maximum) compression level for + gzip, 4..32 and even for szip; NetCDF-4 only */ + unsigned int persist_define_mode : 10; /**< Stay in define mode until exi_persist_leavedef is + called. Set by exi_persist_redef... */ unsigned int - compression_algorithm : 2; /**< GZIP/ZLIB, SZIP, more may be supported by NetCDF soon */ - unsigned int compression_level : 6; /**< 0 (disabled) to 9 (maximum) compression level for - gzip, 4..32 and even for szip; NetCDF-4 only */ + compression_algorithm : 4; /**< GZIP/ZLIB, SZIP, more may be supported by NetCDF soon */ + unsigned int shuffle : 1; /**< 1 true, 0 false */ unsigned int user_compute_wordsize : 1; /**< 0 for 4 byte or 1 for 8 byte reals */ - unsigned int shuffle : 1; /**< 1 true, 0 false */ unsigned int file_type : 2; /**< 0 - classic, 1 -- 64 bit classic, 2 --NetCDF4, 3 --NetCDF4 classic */ - unsigned int is_write : 1; /**< for output or append */ - unsigned int is_parallel : 1; /**< 1 true, 0 false */ - unsigned int is_hdf5 : 1; /**< 1 true, 0 false */ - unsigned int is_pnetcdf : 1; /**< 1 true, 0 false */ - unsigned int has_nodes : 1; /**< for input only at this time */ - unsigned int has_edges : 1; /**< for input only at this time */ - unsigned int has_faces : 1; /**< for input only at this time */ - unsigned int has_elems : 1; /**< for input only at this time */ + unsigned int is_write : 1; /**< for output or append */ + unsigned int is_parallel : 1; /**< 1 true, 0 false */ + unsigned int is_hdf5 : 1; /**< 1 true, 0 false */ + unsigned int is_pnetcdf : 1; /**< 1 true, 0 false */ + unsigned int has_nodes : 1; /**< for input only at this time */ + unsigned int has_edges : 1; /**< for input only at this time */ + unsigned int has_faces : 1; /**< for input only at this time */ + unsigned int has_elems : 1; /**< for input only at this time */ + unsigned int in_define_mode : 1; /**< Is the file in nc define mode... */ struct exi_file_item *next; }; @@ -796,9 +799,9 @@ 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); -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); +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); EXODUS_EXPORT void exi_rm_stat_ptr(int exoid, struct exi_obj_stats **obj_ptr); @@ -854,9 +857,14 @@ EXODUS_EXPORT int exi_put_names(int exoid, int varid, size_t num_entity, char * ex_entity_type obj_type, const char *subtype, const char *routine); EXODUS_EXPORT void exi_trim(char *name); EXODUS_EXPORT void exi_update_max_name_length(int exoid, int length); +EXODUS_EXPORT int exi_redef(int exoid, const char *call_func); +EXODUS_EXPORT int exi_persist_redef(int exoid, const char *call_func); EXODUS_EXPORT int exi_leavedef(int exoid, /* NemesisI file ID */ const char *call_rout /* Name of calling function */ ); +EXODUS_EXPORT int exi_persist_leavedef(int exoid, /* NemesisI file ID */ + const char *call_rout /* Name of calling function */ + ); EXODUS_EXPORT int exi_check_version(int run_version); EXODUS_EXPORT int exi_handle_mode(unsigned int my_mode, int is_parallel, int run_version); diff --git a/packages/seacas/libraries/exodus/src/ex__put_homogenous_block_params.c b/packages/seacas/libraries/exodus/src/ex__put_homogenous_block_params.c index ed4e31d7200b..0e16d4a9e0f8 100644 --- a/packages/seacas/libraries/exodus/src/ex__put_homogenous_block_params.c +++ b/packages/seacas/libraries/exodus/src/ex__put_homogenous_block_params.c @@ -118,7 +118,7 @@ int exi_put_homogenous_block_params(int exoid, size_t block_count, const struct /* ======================================================================== */ /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); return (EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_add_attr.c b/packages/seacas/libraries/exodus/src/ex_add_attr.c index 5d10849df2ce..a0f0e94776d3 100644 --- a/packages/seacas/libraries/exodus/src/ex_add_attr.c +++ b/packages/seacas/libraries/exodus/src/ex_add_attr.c @@ -123,7 +123,7 @@ int ex_add_attr(int exoid, ex_entity_type obj_type, ex_entity_id obj_id, int64_t /* element attribute array */ /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_close.c b/packages/seacas/libraries/exodus/src/ex_close.c index 171f9559c901..33ac81497e08 100644 --- a/packages/seacas/libraries/exodus/src/ex_close.c +++ b/packages/seacas/libraries/exodus/src/ex_close.c @@ -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. * @@ -57,6 +57,11 @@ int ex_close(int exoid) EX_FUNC_LEAVE(EX_FATAL); } +#ifndef NDEBUG + struct exi_file_item *file = exi_find_file_item(exoid); + assert(!file->in_define_mode && file->persist_define_mode == 0); +#endif + /* * NOTE: If using netcdf-4, exoid must refer to the root group. * Need to determine whether there are any groups and if so, diff --git a/packages/seacas/libraries/exodus/src/ex_conv.c b/packages/seacas/libraries/exodus/src/ex_conv.c index bbb545e9c3ed..f053f185d37c 100644 --- a/packages/seacas/libraries/exodus/src/ex_conv.c +++ b/packages/seacas/libraries/exodus/src/ex_conv.c @@ -249,6 +249,8 @@ int exi_conv_init(int exoid, int *comp_wordsize, int *io_wordsize, int file_word new_file->has_edges = 1; new_file->has_faces = 1; new_file->has_elems = 1; + new_file->in_define_mode = 0; + new_file->persist_define_mode = 0; new_file->is_write = is_write; new_file->next = file_list; diff --git a/packages/seacas/libraries/exodus/src/ex_copy.c b/packages/seacas/libraries/exodus/src/ex_copy.c index e7807dc9d4ac..8ccb00505f2b 100644 --- a/packages/seacas/libraries/exodus/src/ex_copy.c +++ b/packages/seacas/libraries/exodus/src/ex_copy.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. * @@ -108,7 +108,7 @@ static int ex_copy_internal(int in_exoid, int out_exoid, int mesh_only) } /* put output file into define mode */ - EXCHECK(nc_redef(out_exoid)); + EXCHECK(exi_redef(out_exoid, __func__)); /* copy global attributes */ EXCHECK(cpy_global_att(in_exoid, out_exoid)); diff --git a/packages/seacas/libraries/exodus/src/ex_create_group.c b/packages/seacas/libraries/exodus/src/ex_create_group.c index 3d05f0ce541f..0adc0c3595b4 100644 --- a/packages/seacas/libraries/exodus/src/ex_create_group.c +++ b/packages/seacas/libraries/exodus/src/ex_create_group.c @@ -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. * @@ -26,7 +26,7 @@ int ex_create_group(int parent_id, const char *group_name) EX_FUNC_LEAVE(EX_FATAL); } - if ((status = nc_redef(parent_id)) != NC_NOERR) { + if ((status = exi_redef(parent_id, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", parent_id); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_err.c b/packages/seacas/libraries/exodus/src/ex_err.c index e3d95e4f0113..a2400ab978c9 100644 --- a/packages/seacas/libraries/exodus/src/ex_err.c +++ b/packages/seacas/libraries/exodus/src/ex_err.c @@ -333,7 +333,8 @@ const char *ex_strerror(int err_num) case EX_MEMFAIL: return "Memory allocation failure"; case EX_BADFILEMODE: return "Bad file mode -- cannot specify both EX_READ and EX_WRITE"; case EX_BADFILEID: return "Bad file id. Could not find exodus file associated with file id."; - case EX_WRONGFILETYPE: return "Integer sizes must match for input and output file in ex_copy."; + case EX_WRONGFILETYPE: + return "File does not exist or is not of a supported type (netcdf3, netcdf4, netcdf5)."; case EX_LOOKUPFAIL: return "Id lookup failed for specified entity type. Could not find entity with specified id."; case EX_BADFILENAME: return "Empty or null filename specified."; @@ -344,6 +345,7 @@ const char *ex_strerror(int err_num) case EX_INTERNAL: return "Internal logic error in exodus library."; case EX_NOTROOTID: return "File id is not the root id; it is a subgroup id."; case EX_NULLENTITY: return "Null entity found."; + case EX_INTSIZEMISMATCH: return "Integer sizes must match for input and output file in ex_copy."; case EX_MSG: return "Message printed; no error implied."; default: return nc_strerror(err_num); } diff --git a/packages/seacas/libraries/exodus/src/ex_field_utils.c b/packages/seacas/libraries/exodus/src/ex_field_utils.c index df99363788d5..098e3c589f26 100644 --- a/packages/seacas/libraries/exodus/src/ex_field_utils.c +++ b/packages/seacas/libraries/exodus/src/ex_field_utils.c @@ -9,7 +9,6 @@ #include "exodusII.h" // for ex_err, etc #include "exodusII_int.h" // for EX_FATAL, etc #include -#include #define _GNU_SOURCE #include @@ -43,13 +42,19 @@ static size_t my_strlcat(char *restrict dst, const char *restrict src, size_t ma return dstlen + srclen; } -static int number_width(const size_t number) +static int number_width(size_t number) { + /* Could use `(int)floor(log10(number)) + 1`, but that requires math library... */ if (number == 0) { return 1; } - int width = (int)floor(log10(number)) + 1; - return width; + int count = 0; + // Iterate till n has digits remaining + while (number != 0) { + number /= 10; + ++count; + } + return count; } static void verify_valid_component(int component, size_t cardinality, size_t suffix_size) diff --git a/packages/seacas/libraries/exodus/src/ex_get_field_metadata.c b/packages/seacas/libraries/exodus/src/ex_get_field_metadata.c index 7d1dbdd70e7f..1d25ed3c2956 100644 --- a/packages/seacas/libraries/exodus/src/ex_get_field_metadata.c +++ b/packages/seacas/libraries/exodus/src/ex_get_field_metadata.c @@ -122,11 +122,11 @@ 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); + int att_count = exi_get_attribute_count(exoid, field[0].entity_type, field[0].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); + att_count, ex_name_of_object(field[0].entity_type), field[0].entity_id); ex_err_fn(exoid, __func__, errmsg, EX_INTERNAL); EX_FUNC_LEAVE(EX_FATAL); } @@ -140,7 +140,7 @@ int ex_get_field_metadata(int exoid, ex_field *field) 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_name_of_object(field[0].entity_type), field[0].entity_id); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); } @@ -165,6 +165,10 @@ int ex_get_field_metadata(int exoid, ex_field *field) if (found == -1) { which = count; strcpy(field[count].name, fld_name); + /* Set default separator type... */ + field[count].component_separator[0] = '_'; + field[count].component_separator[1] = '\0'; + count++; } @@ -174,7 +178,7 @@ int ex_get_field_metadata(int exoid, ex_field *field) 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); + attr_name, ex_name_of_object(field[0].entity_type), field[0].entity_id); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); } @@ -205,7 +209,7 @@ int ex_get_field_metadata(int exoid, ex_field *field) 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); + fld_type, fld_name, ex_name_of_object(field[0].entity_type), field[0].entity_id); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); } @@ -214,7 +218,7 @@ int ex_get_field_metadata(int exoid, ex_field *field) 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); + fld_type, fld_name, ex_name_of_object(field[0].entity_type), field[0].entity_id); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); } diff --git a/packages/seacas/libraries/exodus/src/ex_get_group_id.c b/packages/seacas/libraries/exodus/src/ex_get_group_id.c index d98c8ca401d6..c07ac1f1b7c4 100644 --- a/packages/seacas/libraries/exodus/src/ex_get_group_id.c +++ b/packages/seacas/libraries/exodus/src/ex_get_group_id.c @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2020 National Technology & Engineering Solutions + * Copyright(C) 1999-2020, 2024, 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. * @@ -23,12 +23,15 @@ int ex_get_group_id(int parent_id, const char *group_name, int *group_id) char errmsg[MAX_ERR_LENGTH]; #if NC_HAS_HDF5 EX_FUNC_ENTER(); - /* See if name contains "/" indicating it is a full path name... */ - if (group_name == NULL) { + /* If the `group_name` is NULL, or is the single character '/', then + * return the root id. + */ + if (group_name == NULL || (group_name[0] == '/' && group_name[1] == '\0')) { /* Return root */ *group_id = (unsigned)parent_id & EX_FILE_ID_MASK; } - else if (strchr(group_name, '/') == NULL) { + /* If the name does not start with "/" then it is a relative name from current location... */ + else if (group_name[0] != '/') { /* Local child */ int status = nc_inq_grp_ncid(parent_id, group_name, group_id); if (status != NC_NOERR) { diff --git a/packages/seacas/libraries/exodus/src/ex_open.c b/packages/seacas/libraries/exodus/src/ex_open.c index f8014692d4ba..baac050f4a14 100644 --- a/packages/seacas/libraries/exodus/src/ex_open.c +++ b/packages/seacas/libraries/exodus/src/ex_open.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. * @@ -253,8 +253,8 @@ int ex_open_int(const char *path, int mode, int *comp_ws, int *io_ws, float *ver EX_FUNC_LEAVE(EX_FATAL); } snprintf(errmsg, MAX_ERR_LENGTH, - "ERROR: failed to open %s of type %d for reading.\n\tEither " - "the file does not exist,\n\tor there is a permission or file " + "ERROR: failed to open %s of type %d for reading.\n\t\tThe " + "file does not exist, or there is a permission or file " "format issue.", canon_path, type); ex_err(__func__, errmsg, status); @@ -298,6 +298,7 @@ int ex_open_int(const char *path, int mode, int *comp_ws, int *io_ws, float *ver int dim_str_name = 0; int stat_dim = nc_inq_dimid(exoid, DIM_STR_NAME, &dim_str_name); if (stat_att != NC_NOERR || stat_dim != NC_NOERR) { + /* This must still be nc_redef */ if ((status = nc_redef(exoid)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d named %s into define mode", exoid, canon_path); @@ -332,7 +333,7 @@ int ex_open_int(const char *path, int mode, int *comp_ws, int *io_ws, float *ver EX_FUNC_LEAVE(EX_FATAL); } } - if ((status = exi_leavedef(exoid, __func__)) != NC_NOERR) { + if ((status = nc_enddef(exoid)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to exit define mode in file id %d", exoid); ex_err_fn(exoid, __func__, errmsg, status); free(canon_path); diff --git a/packages/seacas/libraries/exodus/src/ex_open_par.c b/packages/seacas/libraries/exodus/src/ex_open_par.c index 724d301eee76..5bfad214c030 100644 --- a/packages/seacas/libraries/exodus/src/ex_open_par.c +++ b/packages/seacas/libraries/exodus/src/ex_open_par.c @@ -291,9 +291,9 @@ int ex_open_par_int(const char *path, int mode, int *comp_ws, int *io_ws, float } snprintf(errmsg, MAX_ERR_LENGTH, - "ERROR: failed to open %s for read/write.\n\tEither the file " - "does not exist,\n\tor there is a permission or file format " - "issue.", + "ERROR: failed to open %s of type %d for reading.\n\t\tThe " + "file does not exist, or there is a permission or file " + "format issue.", canon_path); ex_err(__func__, errmsg, status); free(canon_path); @@ -313,7 +313,7 @@ int ex_open_par_int(const char *path, int mode, int *comp_ws, int *io_ws, float if (mode & EX_WRITE) { /* Appending */ /* turn off automatic filling of netCDF variables */ if (is_pnetcdf) { - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); free(canon_path); @@ -359,7 +359,7 @@ int ex_open_par_int(const char *path, int mode, int *comp_ws, int *io_ws, float } if (in_redef) { - if ((status = exi_leavedef(exoid, __func__)) != NC_NOERR) { + if ((status = nc_enddef(exoid)) != NC_NOERR) { free(canon_path); EX_FUNC_LEAVE(EX_FATAL); } 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 074a79d84f88..7bcf30eb1ef9 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 @@ -144,7 +144,7 @@ int ex_put_all_var_param_ext(int exoid, const ex_var_params *vp) EX_ELEM_SET, VAR_ELS_STAT, elset_stat); /* put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); goto error_ret; diff --git a/packages/seacas/libraries/exodus/src/ex_put_assemblies.c b/packages/seacas/libraries/exodus/src/ex_put_assemblies.c index c3e1f2c398c9..a731215b546a 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_assemblies.c +++ b/packages/seacas/libraries/exodus/src/ex_put_assemblies.c @@ -45,7 +45,7 @@ int ex_put_assemblies(int exoid, size_t count, const struct ex_assembly *assembl /* Assembly has not already been defined */ /* put netcdf file into define mode */ if (!in_define) { - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); diff --git a/packages/seacas/libraries/exodus/src/ex_put_attr_param.c b/packages/seacas/libraries/exodus/src/ex_put_attr_param.c index 498d019c2df1..dd7847c7da5a 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_attr_param.c +++ b/packages/seacas/libraries/exodus/src/ex_put_attr_param.c @@ -149,7 +149,7 @@ int ex_put_attr_param(int exoid, ex_entity_type obj_type, ex_entity_id obj_id, i } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_attribute.c b/packages/seacas/libraries/exodus/src/ex_put_attribute.c index 370cfe31ef5e..a63b09cc813f 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_attribute.c +++ b/packages/seacas/libraries/exodus/src/ex_put_attribute.c @@ -38,7 +38,7 @@ int ex_put_double_attribute(int exoid, ex_entity_type obj_type, ex_entity_id id, } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); @@ -84,7 +84,7 @@ int ex_put_integer_attribute(int exoid, ex_entity_type obj_type, ex_entity_id id } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); @@ -137,7 +137,7 @@ int ex_put_text_attribute(int exoid, ex_entity_type obj_type, ex_entity_id id, c } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_blobs.c b/packages/seacas/libraries/exodus/src/ex_put_blobs.c index 5bd70b3fdd05..89c22537bb60 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_blobs.c +++ b/packages/seacas/libraries/exodus/src/ex_put_blobs.c @@ -29,7 +29,7 @@ int ex_put_blobs(int exoid, size_t count, const struct ex_blob *blobs) int *entlst_id = (int *)calloc(count, sizeof(int)); - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); free(entlst_id); diff --git a/packages/seacas/libraries/exodus/src/ex_put_block_params.c b/packages/seacas/libraries/exodus/src/ex_put_block_params.c index 3f3801513056..b6d16dd3ccd8 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_block_params.c +++ b/packages/seacas/libraries/exodus/src/ex_put_block_params.c @@ -253,7 +253,7 @@ int ex_put_block_params(int exoid, size_t block_count, const struct ex_block *bl } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); free(blocks_to_define); diff --git a/packages/seacas/libraries/exodus/src/ex_put_cmap_params.c b/packages/seacas/libraries/exodus/src/ex_put_cmap_params.c index bf70211e32ad..196d1966bf7c 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_cmap_params.c +++ b/packages/seacas/libraries/exodus/src/ex_put_cmap_params.c @@ -87,7 +87,7 @@ int ex_put_cmap_params(int exoid, const void_int *node_cmap_ids, /* Put NetCDF file into define mode */ int status; - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to file ID %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_cmap_params_cc.c b/packages/seacas/libraries/exodus/src/ex_put_cmap_params_cc.c index 6c6809380835..1df47f1eee80 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_cmap_params_cc.c +++ b/packages/seacas/libraries/exodus/src/ex_put_cmap_params_cc.c @@ -215,7 +215,7 @@ file ID %d", } /* "if (num_e_comm_maps >0)" */ /* Put NetCDF file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file ID %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_concat_all_blocks.c b/packages/seacas/libraries/exodus/src/ex_put_concat_all_blocks.c index 53573ac67c65..f4b4a997bc78 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_concat_all_blocks.c +++ b/packages/seacas/libraries/exodus/src/ex_put_concat_all_blocks.c @@ -205,7 +205,7 @@ int ex_put_concat_all_blocks(int exoid, const ex_block_params *param) EX_FUNC_LEAVE(EX_NOERR); } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_concat_elem_block.c b/packages/seacas/libraries/exodus/src/ex_put_concat_elem_block.c index f5c7ad17bfc4..92ce78b781b0 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_concat_elem_block.c +++ b/packages/seacas/libraries/exodus/src/ex_put_concat_elem_block.c @@ -144,7 +144,7 @@ int ex_put_concat_elem_block(int exoid, const void_int *elem_blk_id, char *const } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); free(eb_array); diff --git a/packages/seacas/libraries/exodus/src/ex_put_concat_sets.c b/packages/seacas/libraries/exodus/src/ex_put_concat_sets.c index 43a8807b6361..31c59c187c80 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_concat_sets.c +++ b/packages/seacas/libraries/exodus/src/ex_put_concat_sets.c @@ -171,7 +171,7 @@ int ex_put_concat_sets(int exoid, ex_entity_type set_type, const struct ex_set_s } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); free(set_stat); diff --git a/packages/seacas/libraries/exodus/src/ex_put_coordinate_frames.c b/packages/seacas/libraries/exodus/src/ex_put_coordinate_frames.c index b11c10c42a47..9e2edda44278 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_coordinate_frames.c +++ b/packages/seacas/libraries/exodus/src/ex_put_coordinate_frames.c @@ -57,7 +57,7 @@ int ex_put_coordinate_frames(int exoid, int nframes, const void_int *cf_ids, /* make the definitions */ /* go into define mode. define num_frames, num_frames9 */ int status; - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_field_metadata.c b/packages/seacas/libraries/exodus/src/ex_put_field_metadata.c index 36ad8a30f8b7..ff388de656db 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_field_metadata.c +++ b/packages/seacas/libraries/exodus/src/ex_put_field_metadata.c @@ -32,6 +32,28 @@ static int exi_print_attribute_error(int status, const char *name, const char *a return EX_FATAL; } +int ex_put_multi_field_metadata(int exoid, const ex_field field[], const int count) +{ + exi_persist_redef(exoid, __func__); + for (int i = 0; i < count; i++) { + if (field[i].type[0] != EX_SCALAR) { + int status = ex_put_field_metadata(exoid, field[i]); + if (status != EX_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to store field metadata for field '%s' on %s with id %" PRId64 + " in file id %d", + field[i].name, ex_name_of_object(field[i].entity_type), field[i].entity_id, exoid); + ex_err_fn(exoid, __func__, errmsg, status); + exi_persist_leavedef(exoid, __func__); + return EX_FATAL; + } + } + } + exi_persist_leavedef(exoid, __func__); + return EX_NOERR; +} + int ex_put_field_metadata(int exoid, const ex_field field) { /* @@ -53,28 +75,38 @@ int ex_put_field_metadata(int exoid, const ex_field field) field.entity_id); #endif + exi_persist_redef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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__); + /* Do not write if empty... */ + if (field.type_name[0] != '\0') { + 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) { + exi_persist_leavedef(exoid, __func__); + 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__); + /* Default component_separator is '_'. Avoid writing if that is what it is... */ + if (field.component_separator[0] != '_' || field.nesting > 1) { + 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) { + exi_persist_leavedef(exoid, __func__); + return exi_print_attribute_error(status, field.name, "separator", field.entity_type, + field.entity_id, exoid, __func__); + } } bool needs_cardinality = false; @@ -89,11 +121,13 @@ int ex_put_field_metadata(int exoid, const ex_field field) if ((status = ex_put_integer_attribute(exoid, field.entity_type, field.entity_id, attribute_name, field.nesting, field.cardinality)) != EX_NOERR) { + exi_persist_leavedef(exoid, __func__); return exi_print_attribute_error(status, field.name, "cardinality", field.entity_type, field.entity_id, exoid, __func__); } } + exi_persist_leavedef(exoid, __func__); return EX_NOERR; } @@ -145,46 +179,56 @@ int ex_put_basis(int exoid, const ex_basis basis) * } ex_basis; */ + exi_persist_redef(exoid, __func__); int status; if ((status = exi_put_basis_attribute(exoid, basis.name, "cardinality", EX_INTEGER, 1, &basis.cardinality)) != EX_NOERR) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); return exi_print_type_error(status, basis.name, "basis", "zeta", exoid, __func__); } + exi_persist_leavedef(exoid, __func__); return EX_NOERR; } @@ -204,31 +248,38 @@ int ex_put_quadrature(int exoid, const ex_quadrature quad) * } ex_quad; */ + exi_persist_redef(exoid, __func__); int status; if ((status = exi_put_quad_attribute(exoid, quad.name, "cardinality", EX_INTEGER, 1, &quad.cardinality)) != EX_NOERR) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); 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) { + exi_persist_leavedef(exoid, __func__); return exi_print_type_error(status, quad.name, "quad", "weight", exoid, __func__); } + exi_persist_leavedef(exoid, __func__); return EX_NOERR; } diff --git a/packages/seacas/libraries/exodus/src/ex_put_id_map.c b/packages/seacas/libraries/exodus/src/ex_put_id_map.c index eaae70e01318..fb38531d5578 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_id_map.c +++ b/packages/seacas/libraries/exodus/src/ex_put_id_map.c @@ -83,7 +83,7 @@ int ex_put_id_map(int exoid, ex_entity_type map_type, const void_int *map) /* put netcdf file into define mode */ if (nc_inq_varid(exoid, vmap, &mapid) != NC_NOERR) { - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_info.c b/packages/seacas/libraries/exodus/src/ex_put_info.c index da69df0d83fd..4878d1d884f4 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_info.c +++ b/packages/seacas/libraries/exodus/src/ex_put_info.c @@ -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. * @@ -95,7 +95,7 @@ int ex_put_info(int exoid, int num_info, char *const info[]) if (status != NC_NOERR) { /* put file into define mode */ - if ((status = nc_redef(rootid)) != NC_NOERR) { + if ((status = exi_redef(rootid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed put file id %d into define mode", rootid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_init_ext.c b/packages/seacas/libraries/exodus/src/ex_put_init_ext.c index 6d1ecb301a56..e07e8434444b 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_init_ext.c +++ b/packages/seacas/libraries/exodus/src/ex_put_init_ext.c @@ -247,7 +247,7 @@ int ex_put_init_ext(int exoid, const ex_init_params *model) } /* put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { char errmsg[MAX_ERR_LENGTH]; snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); diff --git a/packages/seacas/libraries/exodus/src/ex_put_init_global.c b/packages/seacas/libraries/exodus/src/ex_put_init_global.c index 5053d37b27ec..f6731cd1640c 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_init_global.c +++ b/packages/seacas/libraries/exodus/src/ex_put_init_global.c @@ -60,7 +60,7 @@ int ex_put_init_global(int exoid, int64_t num_nodes_g, int64_t num_elems_g, int6 id_type = NC_INT64; } /* Put NetCDF file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file ID %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_init_info.c b/packages/seacas/libraries/exodus/src/ex_put_init_info.c index 0c88892b4364..29c55db5eb3e 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_init_info.c +++ b/packages/seacas/libraries/exodus/src/ex_put_init_info.c @@ -68,7 +68,7 @@ int ex_put_init_info(int exoid, int num_proc, int num_proc_in_f, const char *fty } /* Put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file ID %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_loadbal_param.c b/packages/seacas/libraries/exodus/src/ex_put_loadbal_param.c index f5463e863747..9307850593b9 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_loadbal_param.c +++ b/packages/seacas/libraries/exodus/src/ex_put_loadbal_param.c @@ -91,7 +91,7 @@ int ex_put_loadbal_param(int exoid, int64_t num_int_nodes, int64_t num_bor_nodes } /* Put NetCDF file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_loadbal_param_cc.c b/packages/seacas/libraries/exodus/src/ex_put_loadbal_param_cc.c index ed64526b4934..dc1d9dc35865 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_loadbal_param_cc.c +++ b/packages/seacas/libraries/exodus/src/ex_put_loadbal_param_cc.c @@ -120,7 +120,7 @@ int ex_put_loadbal_param_cc(int exoid, const void_int *num_int_nodes, const void } /* Put NetCDF file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_map.c b/packages/seacas/libraries/exodus/src/ex_put_map.c index 9861092f0553..f03881a42f21 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_map.c +++ b/packages/seacas/libraries/exodus/src/ex_put_map.c @@ -75,7 +75,7 @@ int ex_put_map(int exoid, const void_int *elem_map) } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_map_param.c b/packages/seacas/libraries/exodus/src/ex_put_map_param.c index f9d30aa53e45..8347e5c4b273 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_map_param.c +++ b/packages/seacas/libraries/exodus/src/ex_put_map_param.c @@ -73,7 +73,7 @@ int ex_put_map_param(int exoid, int num_node_maps, int num_elem_maps) } /* put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_name.c b/packages/seacas/libraries/exodus/src/ex_put_name.c index 0c161ab38533..c0dc4a93af31 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_name.c +++ b/packages/seacas/libraries/exodus/src/ex_put_name.c @@ -39,7 +39,7 @@ int exi_put_assembly_name(int exoid, ex_entity_type obj_type, ex_entity_id entit char errmsg[MAX_ERR_LENGTH]; if (nc_inq_varid(exoid, VAR_ENTITY_ASSEMBLY(entity_id), &entlst_id) == NC_NOERR) { int status; - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_num_map.c b/packages/seacas/libraries/exodus/src/ex_put_num_map.c index b4aa483fdc06..85a5c9d73f67 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_num_map.c +++ b/packages/seacas/libraries/exodus/src/ex_put_num_map.c @@ -190,7 +190,7 @@ int ex_put_num_map(int exoid, ex_entity_type map_type, ex_entity_id map_id, cons EX_FUNC_LEAVE(EX_FATAL); } - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_partial_id_map.c b/packages/seacas/libraries/exodus/src/ex_put_partial_id_map.c index 7c1ffcdab5f6..6a97fd8ee04a 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_partial_id_map.c +++ b/packages/seacas/libraries/exodus/src/ex_put_partial_id_map.c @@ -106,7 +106,7 @@ int ex_put_partial_id_map(int exoid, ex_entity_type map_type, int64_t start_enti /* define the map if it doesn't already exist... */ if (nc_inq_varid(exoid, vmap, &mapid) != NC_NOERR) { - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_partial_var.c b/packages/seacas/libraries/exodus/src/ex_put_partial_var.c index 8eabaae831ed..bc62bca91732 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_partial_var.c +++ b/packages/seacas/libraries/exodus/src/ex_put_partial_var.c @@ -127,7 +127,7 @@ static int exi_look_up_var(int exoid, ex_entity_type var_type, int var_index, ex ex_name_of_object(var_type), &num_entity, &numobjdim, __func__); /* variable doesn't exist so put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); return (EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_prop.c b/packages/seacas/libraries/exodus/src/ex_put_prop.c index 6b1a86598a32..9300d2fb3026 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_prop.c +++ b/packages/seacas/libraries/exodus/src/ex_put_prop.c @@ -152,7 +152,7 @@ int ex_put_prop(int exoid, ex_entity_type obj_type, ex_entity_id obj_id, const c name_length = ex_inquire_int(exoid, EX_INQ_DB_MAX_ALLOWED_NAME_LENGTH) + 1; /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_prop_array.c b/packages/seacas/libraries/exodus/src/ex_put_prop_array.c index 1e35e2197ab8..c49cedcc5381 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_prop_array.c +++ b/packages/seacas/libraries/exodus/src/ex_put_prop_array.c @@ -144,7 +144,7 @@ int ex_put_prop_array(int exoid, ex_entity_type obj_type, const char *prop_name, if (!found) { /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_prop_names.c b/packages/seacas/libraries/exodus/src/ex_put_prop_names.c index d88b2aeccb76..bcf063f05012 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_prop_names.c +++ b/packages/seacas/libraries/exodus/src/ex_put_prop_names.c @@ -130,7 +130,7 @@ int ex_put_prop_names(int exoid, ex_entity_type obj_type, int num_props, char ** nc_set_fill(exoid, NC_FILL, &oldfill); /* fill with zeros per routine spec */ /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to place file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_qa.c b/packages/seacas/libraries/exodus/src/ex_put_qa.c index 1b66067c9e63..a4efa3793670 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_qa.c +++ b/packages/seacas/libraries/exodus/src/ex_put_qa.c @@ -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. * @@ -86,7 +86,7 @@ int ex_put_qa(int exoid, int num_qa_records, char *qa_record[][4]) if (status != NC_NOERR) { /* put file into define mode */ - if ((status = nc_redef(rootid)) != NC_NOERR) { + if ((status = exi_redef(rootid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", rootid); ex_err_fn(exoid, __func__, errmsg, status); diff --git a/packages/seacas/libraries/exodus/src/ex_put_reduction_variable_param.c b/packages/seacas/libraries/exodus/src/ex_put_reduction_variable_param.c index 2f2f4ac8a661..d3d28c85685d 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_reduction_variable_param.c +++ b/packages/seacas/libraries/exodus/src/ex_put_reduction_variable_param.c @@ -165,7 +165,7 @@ int ex_put_reduction_variable_param(int exoid, ex_entity_type obj_type, int num_ } /* put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_reduction_vars.c b/packages/seacas/libraries/exodus/src/ex_put_reduction_vars.c index b8b97e905628..f39948bcd026 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_reduction_vars.c +++ b/packages/seacas/libraries/exodus/src/ex_put_reduction_vars.c @@ -84,7 +84,7 @@ static int exi_look_up_var(int exoid, ex_entity_type var_type, ex_entity_id obj_ } /* variable doesn't exist so put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); return (EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_put_sets.c b/packages/seacas/libraries/exodus/src/ex_put_sets.c index 0a81e9439bec..772994646260 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_sets.c +++ b/packages/seacas/libraries/exodus/src/ex_put_sets.c @@ -96,7 +96,7 @@ int ex_put_sets(int exoid, size_t set_count, const struct ex_set *sets) if (needs_define > 0) { /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); free(sets_to_define); diff --git a/packages/seacas/libraries/exodus/src/ex_put_truth_table.c b/packages/seacas/libraries/exodus/src/ex_put_truth_table.c index ccc89a1b2155..09cb4e6973d8 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_truth_table.c +++ b/packages/seacas/libraries/exodus/src/ex_put_truth_table.c @@ -236,7 +236,7 @@ int ex_put_truth_table(int exoid, ex_entity_type obj_type, int num_blk, int num_ } /* put netcdf file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { free(stat_vals); snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); diff --git a/packages/seacas/libraries/exodus/src/ex_put_var_multi_time.c b/packages/seacas/libraries/exodus/src/ex_put_var_multi_time.c index 821aec23ef59..a34123f5fb8c 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_var_multi_time.c +++ b/packages/seacas/libraries/exodus/src/ex_put_var_multi_time.c @@ -128,7 +128,7 @@ static int exi_look_up_var(int exoid, ex_entity_type var_type, int var_index, ex ex_name_of_object(var_type), &num_entity, &numobjdim, __func__); /* variable doesn't exist so put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); return EX_FATAL; diff --git a/packages/seacas/libraries/exodus/src/ex_put_variable_param.c b/packages/seacas/libraries/exodus/src/ex_put_variable_param.c index ed896f4a8c67..e19a10833f5f 100644 --- a/packages/seacas/libraries/exodus/src/ex_put_variable_param.c +++ b/packages/seacas/libraries/exodus/src/ex_put_variable_param.c @@ -174,7 +174,7 @@ int ex_put_variable_param(int exoid, ex_entity_type obj_type, int num_vars) } /* put file into define mode */ - if ((status = nc_redef(exoid)) != NC_NOERR) { + if ((status = exi_redef(exoid, __func__)) != NC_NOERR) { snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to put file id %d into define mode", exoid); ex_err_fn(exoid, __func__, errmsg, status); EX_FUNC_LEAVE(EX_FATAL); diff --git a/packages/seacas/libraries/exodus/src/ex_update.c b/packages/seacas/libraries/exodus/src/ex_update.c index 02e2f8a93003..52b0d9101391 100644 --- a/packages/seacas/libraries/exodus/src/ex_update.c +++ b/packages/seacas/libraries/exodus/src/ex_update.c @@ -1,5 +1,5 @@ /* - * Copyright(C) 1999-2021 National Technology & Engineering Solutions + * Copyright(C) 1999-2021, 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. * @@ -20,7 +20,7 @@ #include "exodusII.h" // for ex_err, etc #include "exodusII_int.h" // for EX_FATAL, EX_NOERR - +#include /*! * \ingroup Utilities * updates an opened EXODUS file (or EXODUS history file) @@ -35,6 +35,11 @@ int ex_update(int exoid) EX_FUNC_LEAVE(EX_FATAL); } +#ifndef NDEBUG + struct exi_file_item *file = exi_find_file_item(exoid); + assert(!file->in_define_mode && file->persist_define_mode == 0); +#endif + int status; if ((status = nc_sync(exoid)) != NC_NOERR) { char errmsg[MAX_ERR_LENGTH]; diff --git a/packages/seacas/libraries/exodus/src/ex_utils.c b/packages/seacas/libraries/exodus/src/ex_utils.c index e23c6007b10f..0c33d625a32f 100644 --- a/packages/seacas/libraries/exodus/src/ex_utils.c +++ b/packages/seacas/libraries/exodus/src/ex_utils.c @@ -1777,16 +1777,114 @@ void exi_compress_variable(int exoid, int varid, int type) \internal \undoc */ -int exi_leavedef(int exoid, const char *call_rout) +int exi_leavedef(int exoid, const char *call_func) { int status; - if ((status = nc_enddef(exoid)) != NC_NOERR) { + struct exi_file_item *file = exi_find_file_item(exoid); + if (!file) { char errmsg[MAX_ERR_LENGTH]; - snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", exoid); - ex_err_fn(exoid, call_rout, errmsg, status); + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for %s called from %s.", exoid, + __func__, call_func); + ex_err_fn(exoid, __func__, errmsg, EX_BADFILEID); + return EX_FATAL; + } - return (EX_FATAL); + if (!file->persist_define_mode && file->in_define_mode) { + if ((status = nc_enddef(exoid)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", + exoid); + ex_err_fn(exoid, call_func, errmsg, status); + + return (EX_FATAL); + } + file->in_define_mode = 0; + } + return (EX_NOERR); +} + +int exi_redef(int exoid, const char *call_func) +{ + int status; + + struct exi_file_item *file = exi_find_file_item(exoid); + + if (!file) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for %s called from %s.", exoid, + __func__, call_func); + ex_err_fn(exoid, __func__, errmsg, EX_BADFILEID); + return EX_FATAL; + } + + if (!file->in_define_mode) { + if ((status = nc_redef(exoid)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, + "ERROR: failed to put file %d into definition mode in exi_redef called from %s", + exoid, call_func); + ex_err_fn(exoid, __func__, errmsg, status); + return EX_FATAL; + } + file->in_define_mode = 1; + } + return EX_NOERR; +} + +int exi_persist_redef(int exoid, const char *call_func) +{ + int status; + + struct exi_file_item *file = exi_find_file_item(exoid); + + if (!file) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for %s called from %s.", exoid, + __func__, call_func); + ex_err_fn(exoid, __func__, errmsg, EX_BADFILEID); + return EX_FATAL; + } + + if ((++file->persist_define_mode == 1) && !file->in_define_mode) { + if ((status = nc_redef(exoid)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf( + errmsg, MAX_ERR_LENGTH, + "ERROR: failed to put file %d into definition mode in exi_persist_redef called from %s", + exoid, call_func); + ex_err_fn(exoid, __func__, errmsg, status); + return EX_FATAL; + } + file->in_define_mode = 1; + } + return EX_NOERR; +} + +int exi_persist_leavedef(int exoid, const char *call_func) +{ + int status; + + struct exi_file_item *file = exi_find_file_item(exoid); + if (!file) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for %s called from %s.", exoid, + __func__, call_func); + ex_err_fn(exoid, __func__, errmsg, EX_BADFILEID); + return EX_FATAL; + } + + if ((file->persist_define_mode-- == 1) && file->in_define_mode) { + if ((status = nc_enddef(exoid)) != NC_NOERR) { + char errmsg[MAX_ERR_LENGTH]; + snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", + exoid); + ex_err_fn(exoid, call_func, errmsg, status); + + return (EX_FATAL); + } + file->in_define_mode = 0; + file->persist_define_mode = 0; } return (EX_NOERR); } @@ -2090,9 +2188,16 @@ 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/exodus/test/testall.in b/packages/seacas/libraries/exodus/test/testall.in index 1e28a043b67f..889cf15434a0 100755 --- a/packages/seacas/libraries/exodus/test/testall.in +++ b/packages/seacas/libraries/exodus/test/testall.in @@ -357,8 +357,8 @@ 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]})) +${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 diff --git a/packages/seacas/libraries/exodus/test/testwt-field-metadata.dmp b/packages/seacas/libraries/exodus/test/testwt-field-metadata.dmp index 9b9e075aa8b9..37e75ddeb6e9 100644 --- a/packages/seacas/libraries/exodus/test/testwt-field-metadata.dmp +++ b/packages/seacas/libraries/exodus/test/testwt-field-metadata.dmp @@ -25,10 +25,8 @@ :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" ; @@ -42,10 +40,8 @@ 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", diff --git a/packages/seacas/libraries/exodus/test/testwt-field-metadata2.c b/packages/seacas/libraries/exodus/test/testwt-field-metadata2.c deleted file mode 100644 index ffa23d9513d0..000000000000 --- a/packages/seacas/libraries/exodus/test/testwt-field-metadata2.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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 -#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 = 7, - .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$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", "Gradient-X$9", "Gradient-Y$9", "Gradient-Z$9", "Curl@1", "Curl@2", - "Curl@3", "Curl@4", "Curl@5", "Curl@6", "Curl@7", - "Curl@8", "User_h2o", "User_gas", "User_ch4", "User_methane"}; - 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); - } - } - - { - struct ex_field field = (ex_field){.entity_type = EX_ELEM_BLOCK, - .entity_id = blocks[1].id, - .name = "Gradient", - .type = {EX_VECTOR_3D, EX_FIELD_TYPE_SEQUENCE}, - .nesting = 2, - .cardinality = {0,9}, - .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 = {EX_FIELD_TYPE_SEQUENCE}, - .nesting = 1, - .cardinality = {9}, - .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 = "User", - .type = {EX_FIELD_TYPE_USER_DEFINED}, - .nesting = 1, - .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/ioss/cmake/SEACASIoss_config.h.in b/packages/seacas/libraries/ioss/cmake/SEACASIoss_config.h.in index 15b62c0beaa2..808f36ece452 100644 --- a/packages/seacas/libraries/ioss/cmake/SEACASIoss_config.h.in +++ b/packages/seacas/libraries/ioss/cmake/SEACASIoss_config.h.in @@ -11,6 +11,8 @@ #cmakedefine SEACAS_HAVE_EXODUS +#cmakedefine SEACAS_HAVE_EXONULL + #cmakedefine SEACAS_HAVE_CGNS #cmakedefine SEACAS_HAVE_PAMGEN diff --git a/packages/seacas/libraries/ioss/src/CMakeLists.txt b/packages/seacas/libraries/ioss/src/CMakeLists.txt index ee0779d4fdca..3ab10755d3e3 100644 --- a/packages/seacas/libraries/ioss/src/CMakeLists.txt +++ b/packages/seacas/libraries/ioss/src/CMakeLists.txt @@ -28,8 +28,12 @@ ENDIF() IF (${PACKAGE_NAME}_ENABLE_SEACASExodus) SET(SEACAS_HAVE_EXODUS ON) + IF (ENABLE_ExoNull) + SET(SEACAS_HAVE_EXONULL ON) + ENDIF() ENDIF() + TRIBITS_CONFIGURE_FILE(${PACKAGE_NAME}_config.h) SET(HEADERS "") @@ -111,9 +115,12 @@ endif() IF (${PACKAGE_NAME}_ENABLE_SEACASExodus) ADD_SUBDIRECTORY(exodus) - ADD_SUBDIRECTORY(exonull) + IF (ENABLE_ExoNull) + ADD_SUBDIRECTORY(exonull) + ENDIF() ENDIF() + IF (TPL_ENABLE_Pamgen OR Trilinos_ENABLE_Pamgen ) ADD_SUBDIRECTORY(pamgen) ENDIF() diff --git a/packages/seacas/libraries/ioss/src/Ioss_BasisVariableType.C b/packages/seacas/libraries/ioss/src/Ioss_BasisVariableType.C deleted file mode 100644 index 7515f145e624..000000000000 --- a/packages/seacas/libraries/ioss/src/Ioss_BasisVariableType.C +++ /dev/null @@ -1,26 +0,0 @@ -// 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 "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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Ioss_CodeTypes.h" diff --git a/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_ChainGenerator.C index c2d976f04c92..07c9ca156d0c 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) { @@ -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, @@ -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) { @@ -258,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)) @@ -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_CodeTypes.h b/packages/seacas/libraries/ioss/src/Ioss_CodeTypes.h index f799bad41fbd..a88d7816b5d6 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_CodeTypes.h +++ b/packages/seacas/libraries/ioss/src/Ioss_CodeTypes.h @@ -35,6 +35,7 @@ inline std::string IOSS_SYM_TENSOR() { return {"sym_tensor_33"}; } /* #undef IOSS_THREADSAFE */ /* #undef SEACAS_HAVE_KOKKOS */ #define SEACAS_HAVE_EXODUS +#define SEACAS_HAVE_EXONULL #define SEACAS_HAVE_CGNS /* #undef SEACAS_HAVE_FAODEL */ #define SEACAS_HAVE_PAMGEN diff --git a/packages/seacas/libraries/ioss/src/Ioss_Compare.C b/packages/seacas/libraries/ioss/src/Ioss_Compare.C index 9d99ecb35d42..440622159d29 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Compare.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Compare.C @@ -959,6 +959,11 @@ namespace { // the filenames match. continue; } + if (property == "base_filename") { + // IGNORE the base_filename. This is generally the base portion of the filename; we don't + // care whether the filenames match. + continue; + } // ALLOW the regions to have different names (when copying between databases, io_shell // will create "region_1" (input) and "region_2" (output)) diff --git a/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C b/packages/seacas/libraries/ioss/src/Ioss_CopyDatabase.C index a1862313b702..e9f61a7e1268 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_DatabaseIO.C b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C index bee5f80d1d0d..f16b426753cd 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.C @@ -379,6 +379,8 @@ 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) { @@ -1633,7 +1635,7 @@ namespace { else { char sep = (util.parallel_size() > 1) ? ':' : ' '; for (auto &p_time : all_times) { - fmt::print(strm, "{:8d}{}", p_time, sep); + fmt::print(strm, "{:8}{}", p_time, sep); } } if (util.parallel_size() > 1) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h index f5edcdb3b45d..44db7cfe0778 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/Ioss_DatabaseIO.h @@ -279,6 +279,30 @@ namespace Ioss { return create_subgroup_nl(group_name); } + bool open_root_group() + { + IOSS_FUNC_ENTER(m_); + return open_root_group_nl(); + } + + int num_child_group() + { + IOSS_FUNC_ENTER(m_); + return num_child_group_nl(); + } + + bool open_child_group(int index) + { + IOSS_FUNC_ENTER(m_); + return open_child_group_nl(index); + } + + Ioss::NameList groups_describe(bool return_full_names = false) + { + IOSS_FUNC_ENTER(m_); + return groups_describe_nl(return_full_names); + } + /** \brief Set the database to the given State. * * All transitions must begin from the 'STATE_CLOSED' state or be to @@ -502,6 +526,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. @@ -737,6 +762,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 @@ -757,16 +790,15 @@ 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 int num_child_group_nl() { return 0; } + virtual bool open_child_group_nl(int /* index */) { return false; } + 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 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_Decomposition.C b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C index 1deac79a93d2..5d41b9069e28 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Decomposition.C @@ -15,6 +15,7 @@ #include #include #include +#include #include #if !defined(NO_ZOLTAN_SUPPORT) @@ -427,9 +428,9 @@ namespace Ioss { m_method, fmt::group_digits(m_globalElementCount), m_processorCount); if (!m_decompExtra.empty()) { - fmt::print(Ioss::OUTPUT(), "\tDecomposition extra data: '{}'.\n", m_decompExtra); + 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 " diff --git a/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C index bec6473e015c..d6d5fdd097b4 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C +++ b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.C @@ -337,9 +337,9 @@ namespace Ioss { } 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) + void 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 = @@ -359,20 +359,18 @@ namespace Ioss { // 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 IOSS_EXPORT void 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 void 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, @@ -466,7 +464,7 @@ namespace Ioss { std::vector &elem_to_proc, int proc_count); void DecompUtils::output_decomposition_statistics(const std::vector &elem_to_proc, - int proc_count) + int proc_count) { // Output histogram of elements / rank... std::vector elem_per_rank(proc_count); @@ -475,8 +473,8 @@ namespace Ioss { } 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); + 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()); diff --git a/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.h b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.h index 30b1b35a2ca0..728f40f4ed47 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.h +++ b/packages/seacas/libraries/ioss/src/Ioss_DecompositionUtils.h @@ -24,16 +24,16 @@ namespace Ioss { std::vector &element_to_proc, int proc_count); static void output_decomposition_statistics(const std::vector &element_to_proc, - int proc_count); + 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); + static void 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, diff --git a/packages/seacas/libraries/ioss/src/Ioss_Doxygen.h b/packages/seacas/libraries/ioss/src/Ioss_Doxygen.h index d3c20468da6a..6206f622dd9a 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Doxygen.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Doxygen.h @@ -198,9 +198,9 @@ file. Then, the first file will be reopened and steps 0.7, 0.8, and Property | Value | Description -----------------------|:------:|----------------------------------------------------------- -MEMORY_READ | on/[off] | experimental -MEMORY_WRITE | on/[off] | experimental -ENABLE_FILE_GROUPS | on/[off] | experimental +MEMORY_READ | on/[off] | experimental. Read a file into memory at open time, operate on it without disk accesses. +MEMORY_WRITE | on/[off] | experimental. Open and read a file into memory or create and optionally write it back out to disk when nc_close() is called. +ENABLE_FILE_GROUPS | on/[off] | experimental. Opens database in netcdf-4 non-classic mode which is what is required to support groups at netCDF level. MINIMAL_NEMESIS_INFO | on/[off] | special case, omit all nemesis data except for nodal communication map OMIT_EXODUS_NUM_MAPS | on/[off] | special case, do not output the node and element numbering map. EXODUS_CALL_GET_ALL_TIMES| [on] / off | special case -- should the `ex_get_all_times()` function be called. See below. 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 000000000000..639f6ba47a59 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.C @@ -0,0 +1,728 @@ +// 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 "Ioss_Assembly.h" +#include "Ioss_Blob.h" +#include "Ioss_CodeTypes.h" +#include "Ioss_CommSet.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_Region.h" +#include "Ioss_SideBlock.h" +#include "Ioss_SideSet.h" +#include "Ioss_StructuredBlock.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 != region && 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::register_notifier(DynamicTopologyNotifier *notifier) +{ + if(nullptr != notifier && nullptr != m_notifier && notifier != m_notifier) { + std::ostringstream errmsg; + fmt::print(errmsg, + "ERROR: Attempt to re-register different notifier on " + "Dynamic Topology Observer.\n\n"); + IOSS_ERROR(errmsg); + } + + m_notifier = notifier; +} + +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_nl(unsigned int type) +{ + m_topologyModification |= type; + m_cumulativeTopologyModification |= type; +} + +void DynamicTopologyObserver::set_topology_modification(unsigned int type) +{ + if(!(m_topologyModification & type)) { + set_topology_modification_nl(type); + + if(nullptr != m_notifier) { + for(auto observer : m_notifier->get_observers()) { + observer->set_topology_modification_nl(type); + } + } + } +} + +void DynamicTopologyObserver::reset_topology_modification() +{ + m_topologyModification = TOPOLOGY_SAME; +} + +void DynamicTopologyObserver::reset_topology_modification_all() +{ + if(m_topologyModification != TOPOLOGY_SAME) { + reset_topology_modification(); + + if(nullptr != m_notifier) { + for(auto observer : m_notifier->get_observers()) { + observer->reset_topology_modification(); + } + } + } +} + +bool DynamicTopologyObserver::is_topology_modified() const +{ return m_topologyModification != TOPOLOGY_SAME; } + +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() +{ + +} + +void DynamicTopologyObserver::write_model() +{ + +} + +void DynamicTopologyObserver::define_transient() +{ + +} + + +DynamicTopologyBroker* DynamicTopologyBroker::broker() +{ + static DynamicTopologyBroker broker_; + return &broker_; +} + +void DynamicTopologyBroker::register_model(const std::string& model_name) +{ + auto iter = m_notifiers.find(model_name); + if(iter != m_notifiers.end()) { + return; + } + + m_notifiers[model_name] = std::make_shared(model_name); +} + +std::shared_ptr DynamicTopologyBroker::get_notifier(const std::string& model_name) const +{ + auto iter = m_notifiers.find(model_name); + if(iter != m_notifiers.end()) { + return iter->second; + } + + return {}; +} + +std::vector> DynamicTopologyBroker::get_observers(const std::string& model_name) const +{ + std::vector> observers; + + auto notifier = get_notifier(model_name); + + if(notifier) { + return notifier->get_observers(); + } + + return observers; +} + +void DynamicTopologyBroker::remove_model(const std::string& model_name) +{ + auto iter = m_notifiers.find(model_name); + if(iter != m_notifiers.end()) { + m_notifiers.erase(iter); + } +} + +void DynamicTopologyBroker::clear_models() +{ + m_notifiers.clear(); +} + +void DynamicTopologyBroker::register_observer(const std::string& model_name, + std::shared_ptr observer) +{ + auto notifier = get_notifier(model_name); + + if(!notifier) { + register_model(model_name); + notifier = get_notifier(model_name); + } + + notifier->register_observer(observer); +} + +void DynamicTopologyBroker::register_observer(const std::string& model_name, + std::shared_ptr observer, + Region& region) +{ + region.register_mesh_modification_observer(observer); + register_observer(model_name, observer); +} + +void DynamicTopologyBroker::reset_topology_modification(const std::string& model_name) +{ + auto notifier = get_notifier(model_name); + + if(!notifier) return; + + notifier->reset_topology_modification(); +} + +void DynamicTopologyBroker::set_topology_modification(const std::string& model_name, unsigned int type) +{ + auto notifier = get_notifier(model_name); + + if(!notifier) return; + + notifier->set_topology_modification(type); +} + + +struct DynamicTopologyObserverCompare { + bool operator()(const std::shared_ptr & lhs, + const std::shared_ptr & rhs) const { + assert(lhs && (lhs->get_region() != nullptr)); + assert(rhs && (rhs->get_region() != nullptr)); + return (lhs->get_region() < rhs->get_region()); + } +}; + +void DynamicTopologyNotifier::register_observer(std::shared_ptr observer) +{ + observer->register_notifier(this); + m_observers.push_back(observer); + std::sort(m_observers.begin(), m_observers.end(), DynamicTopologyObserverCompare()); +} + +void DynamicTopologyNotifier::unregister_observer(std::shared_ptr observer) +{ + auto iter = std::find(m_observers.begin(), m_observers.end(), observer); + if (iter != m_observers.end()) { + (*iter)->register_notifier(nullptr); + m_observers.erase(iter); + } +} + +void DynamicTopologyNotifier::reset_topology_modification() +{ + for(std::shared_ptr& observer : m_observers) { + observer->reset_topology_modification(); + } +} + +void DynamicTopologyNotifier::set_topology_modification(unsigned int type) +{ + for(std::shared_ptr& observer : m_observers) { + observer->set_topology_modification(type); + } +} + + +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.empty() || m_dbType.empty()) + { + std::string error_message; + if(m_dbType.empty()) + error_message += "The database TYPE has not been defined\n"; + + if(m_ioDB.empty()) + { + error_message += "The database FILENAME has not been defined\n"; + } + std::ostringstream errmsg; + fmt::print(errmsg, error_message); + IOSS_ERROR(errmsg); + } + assert(!m_ioDB.empty()); + assert(!m_dbType.empty()); + 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->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 << group_prefix(); + oss << m_dbChangeCount; + + current_db->release_memory(); + current_db->open_root_group(); + current_db->create_subgroup(oss.str()); + + m_dbChangeCount++; +} + +} + + + + 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 000000000000..76de048fd491 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/Ioss_DynamicTopology.h @@ -0,0 +1,238 @@ +// 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_DBUsage.h" +#include "Ioss_DatabaseIO.h" // for DatabaseIO +#include "Ioss_ParallelUtils.h" // for ParallelUtils +#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_export.h" + +#include // for ostream +#include +#include // for string, operator< +#include + +namespace Ioss { + class Region; + class DynamicTopologyNotifier; + + /*! The TopologyModified enumeration is used as an argument to the + * topology_modified() functions in io to + * specify the type of topology modification that has occurred. 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_GEOMETRY = (1U << 3), //!< Geometry (mesh coordinates) modified. Restart needs to know this. + TOPOLOGY_CREATEFACE = (1U << 4), //!< Face/Edge are created/deleted. + TOPOLOGY_CREATEELEM = (1U << 5), //!< Elements are created/deleted. + TOPOLOGY_CREATENODE = (1U << 6), //!< Nodes are created/deleted. + TOPOLOGY_CREATEASSEMBLY = (1U << 7), //!< Assemblies are created/deleted. + TOPOLOGY_UNKNOWN = (1U << 8), //!< Unknown change, recreate from scratch. + }; + + enum class FileControlOption { CONTROL_NONE, CONTROL_AUTO_MULTI_FILE, CONTROL_AUTO_GROUP_FILE }; + + class IOSS_EXPORT DynamicTopologyObserver + { + public: + DynamicTopologyObserver(Region *region) + : m_region(region) {} + + virtual ~DynamicTopologyObserver() {} + + virtual void reset_topology_modification_all(); + 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; } + + void register_notifier(DynamicTopologyNotifier *notifier); + DynamicTopologyNotifier* get_notifier() const { return m_notifier; } + + virtual void define_model(); + virtual void write_model(); + virtual void define_transient(); + + virtual FileControlOption get_control_option() const { return FileControlOption::CONTROL_NONE; } + + 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}; + + DynamicTopologyNotifier *m_notifier{nullptr}; + + void check_region() const; + IOSS_NODISCARD const ParallelUtils &util() const; + void synchronize_topology_modified_flags(); + + void set_topology_modification_nl(unsigned int type); + + private: + DynamicTopologyObserver(); + }; + + + class IOSS_EXPORT DynamicTopologyNotifier + { + public: + DynamicTopologyNotifier(const std::string& model_name) + : m_modelName(model_name) {} + + virtual ~DynamicTopologyNotifier() = default; + + std::string name() const { return m_modelName; } + + std::vector> get_observers() const { return m_observers; } + + void register_observer(std::shared_ptr observer); + + void unregister_observer(std::shared_ptr observer); + + void reset_topology_modification(); + + void set_topology_modification(unsigned int type); + + template + bool has_observer_type() const + { + bool found = false; + + for(const std::shared_ptr& observer : m_observers) { + if (dynamic_cast(observer.get()) != nullptr) { + found = true; + break; + } + } + return found; + } + + template + std::vector> get_observer_type() const + { + std::vector> typed_observers; + + for(const std::shared_ptr &observer : m_observers) { + ObserverType* typed_observer = dynamic_cast(observer.get()); + if (typed_observer != nullptr) { + typed_observers.push_back(std::dynamic_pointer_cast(observer)); + } + } + + return typed_observers; + } + + private: + const std::string m_modelName; + std::vector> m_observers; + }; + + + class IOSS_EXPORT DynamicTopologyBroker + { + public: + static DynamicTopologyBroker *broker(); + + void register_model(const std::string& model_name); + void remove_model(const std::string& model_name); + void clear_models(); + + std::shared_ptr get_notifier(const std::string& model_name) const; + std::vector> get_observers(const std::string& model_name) const; + + void register_observer(const std::string& model_name, std::shared_ptr observer); + void register_observer(const std::string& model_name, std::shared_ptr observer, Region& region); + + void reset_topology_modification(const std::string& model_name); + void set_topology_modification(const std::string& model_name, unsigned int type); + + private: + DynamicTopologyBroker() {}; + DynamicTopologyBroker(DynamicTopologyBroker&); + + std::map> m_notifiers; + }; + + + 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); + + static std::string group_prefix() { return "IOSS_FILE_GROUP-"; } + + 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_FaceGenerator.C b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.C index 04bd1effabf9..f8bb48abecb2 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); @@ -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 @@ -326,13 +326,18 @@ namespace Ioss { FaceUnorderedSet &FaceGenerator::faces(const Ioss::ElementBlock *block) { - auto name = block->name(); + const auto &name = block->name(); return faces_[name]; } + void FaceGenerator::progress(const std::string &output) const + { + region_.get_database()->progress(output); + } + void FaceGenerator::clear(const Ioss::ElementBlock *block) { - auto name = block->name(); + const auto &name = block->name(); faces_[name].clear(); } @@ -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,11 +383,12 @@ 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]; #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,10 +399,11 @@ 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) { + 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 ed9ada94a129..b658e66cd508 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h +++ b/packages/seacas/libraries/ioss/src/Ioss_FaceGenerator.h @@ -39,14 +39,20 @@ 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 +76,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_{}; }; @@ -108,7 +113,7 @@ 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_set; using FaceUnorderedSet = tsl::hopscotch_pg_set; #elif defined FG_USE_ROBIN // using FaceUnorderedSet = tsl::robin_set; @@ -130,15 +135,17 @@ 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 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]; } + 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); + void hash_local_node_ids(size_t count); template void generate_model_faces(INT /*dummy*/, bool local_ids); Ioss::Region ®ion_; diff --git a/packages/seacas/libraries/ioss/src/Ioss_Field.C b/packages/seacas/libraries/ioss/src/Ioss_Field.C index c110b825c5e9..0e0b33a8bee4 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_Field.h b/packages/seacas/libraries/ioss/src/Ioss_Field.h index 4fd2146c72fb..80c7aae15654 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 @@ -244,3 +247,13 @@ namespace Ioss { }; 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_Glob.h b/packages/seacas/libraries/ioss/src/Ioss_Glob.h index 58f65a3e561c..6d84314f30f7 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/Ioss_IOFactory.C b/packages/seacas/libraries/ioss/src/Ioss_IOFactory.C index e020d8dd37d8..80d30ac63c6a 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_IOFactory.C +++ b/packages/seacas/libraries/ioss/src/Ioss_IOFactory.C @@ -93,7 +93,9 @@ Ioss::DatabaseIO *Ioss::IOFactory::create(const std::string &type, const std::st } } else { - auto my_props(properties); + 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_ParallelUtils.h b/packages/seacas/libraries/ioss/src/Ioss_ParallelUtils.h index 58f5926a9504..24cc545535bd 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ParallelUtils.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ParallelUtils.h @@ -18,6 +18,7 @@ #if IOSS_DEBUG_OUTPUT #include #include +#include #endif #ifdef SEACAS_HAVE_MPI diff --git a/packages/seacas/libraries/ioss/src/Ioss_Region.C b/packages/seacas/libraries/ioss/src/Ioss_Region.C index a3660c18f66c..725d07e9cb12 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,10 @@ 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 +379,96 @@ namespace Ioss { // Region owns all sub-grouping entities it contains... try { IOSS_FUNC_ENTER(m_); - for (const auto &nb : nodeBlocks) { - delete (nb); - } + reset_region(); - 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); - } + if(topologyObserver) { + topologyObserver->register_region(nullptr); + } + } - for (const auto &eb : elementBlocks) { - delete (eb); - } + void Region::reset_region() + { + for (const auto &nb : nodeBlocks) { + delete (nb); + } + nodeBlocks.clear(); - for (const auto &sb : structuredBlocks) { - delete (sb); - } + for (const auto &eb : edgeBlocks) { + delete (eb); + } + edgeBlocks.clear(); - for (const auto &ss : sideSets) { - delete (ss); - } + for (const auto &fb : faceBlocks) { + delete (fb); + } + faceBlocks.clear(); - for (const auto &ns : nodeSets) { - delete (ns); - } + for (const auto &eb : elementBlocks) { + delete (eb); + } + elementBlocks.clear(); - for (const auto &es : edgeSets) { - delete (es); - } + for (const auto &sb : structuredBlocks) { + delete (sb); + } + structuredBlocks.clear(); - for (const auto &fs : faceSets) { - delete (fs); - } + for (const auto &ss : sideSets) { + delete (ss); + } + sideSets.clear(); - for (const auto &es : elementSets) { - delete (es); - } + for (const auto &ns : nodeSets) { + delete (ns); + } + nodeSets.clear(); - for (const auto &cs : commSets) { - delete (cs); - } + for (const auto &es : edgeSets) { + delete (es); + } + edgeSets.clear(); - for (const auto &as : assemblies) { - delete (as); - } + for (const auto &fs : faceSets) { + delete (fs); + } + faceSets.clear(); - for (const auto &bl : blobs) { - delete (bl); - } + for (const auto &es : elementSets) { + delete (es); + } + elementSets.clear(); - // Region owns the database pointer even though other entities use it. - GroupingEntity::really_delete_database(); + for (const auto &cs : commSets) { + delete (cs); } - catch (...) { + commSets.clear(); + + for (const auto &as : assemblies) { + delete (as); } + assemblies.clear(); + + for (const auto &bl : blobs) { + delete (bl); + } + blobs.clear(); + + stateTimes.clear(); + + currentState = -1; + stateCount = 0; + + modelDefined = false; + transientDefined = false; + + // Ioex:DatabaseIO::read_communication_metadata() adds comm fields that need to be cleared + erase_fields(Field::COMMUNICATION); } void Region::delete_database() { GroupingEntity::really_delete_database(); } @@ -652,6 +706,23 @@ 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_GROUP_FILE)) { + if (!fileGroupsStarted) { + int steps = get_property("state_count").get_int(); + bool force_addition = true; + add_output_database_group(steps, force_addition); + + fileGroupsStarted = true; + } + } + } + else if (new_state == STATE_TRANSIENT) { + update_dynamic_topology(); + } switch (get_state()) { case STATE_CLOSED: // Make sure we can go to the specified state. @@ -785,6 +856,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; } @@ -1002,6 +1079,9 @@ namespace Ioss { } currentState = state; } + + update_dynamic_topology(); + DatabaseIO *db = get_database(); db->begin_state(state, time); } @@ -2741,4 +2821,238 @@ namespace Ioss { } } + void + Region::register_mesh_modification_observer(std::shared_ptr observer) + { + if (observer) { + if (observer->get_control_option() == FileControlOption::CONTROL_AUTO_GROUP_FILE) { + const Ioss::PropertyManager &db_properties = get_database()->get_property_manager(); + if (!db_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_GROUP_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; + + const Ioss::PropertyManager &db_properties = get_database()->get_property_manager(); + if (!db_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 && 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. + + reset_region(); + 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 continuation database... + if (property_exists("state_offset")) + state += get_property("state_offset").get_int(); + + state++; // For the state we are going to write. + + reset_region(); + DynamicTopologyFileControl fileControl(this, fileCyclicCount, ifDatabaseExists, + dbChangeCount); + fileControl.clone_and_replace_output_database(state); + } + } + + void Region::reset_topology_modification() + { + if(topologyObserver) { + topologyObserver->reset_topology_modification(); + } + } + + void Region::set_topology_modification(unsigned int type) + { + if(topologyObserver) { + topologyObserver->set_topology_modification(type); + } + } + + unsigned int Region::get_topology_modification() const + { + if(topologyObserver) { + return topologyObserver->get_topology_modification(); + } + + return TOPOLOGY_SAME; + } + + bool Region::load_group_mesh(const std::string &child_group_name) + { + // Check name for '/' which is not allowed since it is the + // separator character in a full group path + if (child_group_name.find('/') != std::string::npos) { + std::ostringstream errmsg; + fmt::print(errmsg, "ERROR: Invalid group name '{}' contains a '/' which is not allowed.\n", + child_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(child_group_name)) + return false; + + reset_region(); + 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; + } + + bool Region::load_group_mesh(const int child_group_index) + { + DatabaseIO *iodatabase = get_database(); + + if (!iodatabase->is_input()) + return false; + + if (!iodatabase->open_root_group()) + return false; + + if (!iodatabase->open_child_group(child_group_index)) + return false; + + reset_region(); + 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; + } + + void Region::update_dynamic_topology() + { + auto topologyObserver = get_mesh_modification_observer(); + + bool has_output_observer = topologyObserver && !get_database()->is_input(); + if (has_output_observer && topologyObserver->is_topology_modified()) { + if(topologyObserver->get_control_option() != FileControlOption::CONTROL_NONE) { + int steps = get_property("state_count").get_int(); + start_new_output_database_entry(steps); + + topologyObserver->define_model(); + topologyObserver->write_model(); + topologyObserver->define_transient(); + } + topologyObserver->reset_topology_modification(); + } + } } // namespace Ioss diff --git a/packages/seacas/libraries/ioss/src/Ioss_Region.h b/packages/seacas/libraries/ioss/src/Ioss_Region.h index 2b65b19ae4cb..6210b37b2698 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Region.h +++ b/packages/seacas/libraries/ioss/src/Ioss_Region.h @@ -7,8 +7,10 @@ #pragma once #include "Ioss_CoordinateFrame.h" // for CoordinateFrame -#include "Ioss_DatabaseIO.h" // for DatabaseIO -#include "Ioss_EntityType.h" // for EntityType, etc +#include "Ioss_DBUsage.h" +#include "Ioss_DatabaseIO.h" // for DatabaseIO +#include "Ioss_DynamicTopology.h" +#include "Ioss_EntityType.h" // for EntityType, etc #include "Ioss_Field.h" #include "Ioss_GroupingEntity.h" // for GroupingEntity #include "Ioss_MeshType.h" @@ -22,9 +24,13 @@ #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 #include // for string, operator< #include // for pair @@ -52,6 +58,7 @@ namespace Ioss { namespace Ioss { class CoordinateFrame; + enum class MeshType; using AssemblyContainer = std::vector; @@ -277,7 +284,40 @@ 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 reset_topology_modification(); + void set_topology_modification(unsigned int type); + unsigned int get_topology_modification() const; + + 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 &child_group_name); + bool load_group_mesh(const int child_group_index); + protected: + void update_dynamic_topology(); + 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; @@ -303,6 +343,7 @@ namespace Ioss { bool end_mode_nl(State current_state); void delete_database() override; + void reset_region(); mutable std::map aliases_; ///< Stores alias mappings @@ -329,6 +370,18 @@ 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}; + bool fileGroupsStarted{false}; + }; } // namespace Ioss @@ -431,9 +484,17 @@ namespace Ioss { if (found && field.get_role() != role) { std::ostringstream errmsg; +#if defined BUILT_IN_SIERRA errmsg << "ERROR: Field " << field.get_name() << " with role " << field.role_string() << " on entity " << entity->name() << " does not match previously found role " << Ioss::Field::role_string(role) << ".\n", +#else + fmt::print(errmsg, + "ERROR: Field {} with role {} on entity {} does not match previously found " + "role {}.\n", + field.get_name(), field.role_string(), entity->name(), + Ioss::Field::role_string(role)); +#endif IOSS_ERROR(errmsg); } diff --git a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.C b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.C index 7e06fcb72174..7c8891124a27 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.C +++ b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.C @@ -11,6 +11,7 @@ #include "Ioss_Hex8.h" #include "Ioss_Property.h" // for Property #include "Ioss_SmartAssert.h" +#include "Ioss_Sort.h" #include "Ioss_StructuredBlock.h" #include #include // for size_t @@ -533,25 +534,38 @@ namespace Ioss { same = false; } - // NOTE: this comparison assumes that the elements of this vector will - // appear in the same order in two databases that are equivalent. if (quiet && this->m_zoneConnectivity != rhs.m_zoneConnectivity) { return false; } - if (!vec_equal(this->m_zoneConnectivity, rhs.m_zoneConnectivity)) { - fmt::print(Ioss::OUTPUT(), "StructuredBlock: Zone Connectivity mismatch (size {} vs {})\n", - this->m_zoneConnectivity.size(), rhs.m_zoneConnectivity.size()); - same = false; + { + auto lhzc = this->m_zoneConnectivity; + auto rhzc = rhs.m_zoneConnectivity; + Ioss::sort(lhzc.begin(), lhzc.end(), [](const ZoneConnectivity &l, const ZoneConnectivity &r) { + return l.m_connectionName < r.m_connectionName;}); + Ioss::sort(rhzc.begin(), rhzc.end(), [](const ZoneConnectivity &l, const ZoneConnectivity &r) { + return l.m_connectionName < r.m_connectionName;}); + if (!vec_equal(lhzc, rhzc)) { + fmt::print(Ioss::OUTPUT(), "StructuredBlock: Zone Connectivity mismatch (size {} vs {})\n", + this->m_zoneConnectivity.size(), rhs.m_zoneConnectivity.size()); + same = false; + } } - // NOTE: this comparison assumes that the elements of this vector will - // appear in the same order in two databases that are equivalent. if (quiet && this->m_boundaryConditions != rhs.m_boundaryConditions) { return false; } - if (!vec_equal(this->m_boundaryConditions, rhs.m_boundaryConditions)) { - fmt::print(Ioss::OUTPUT(), "StructuredBlock: Boundary Conditions mismatch\n"); - same = false; + + { + auto lhbc = this->m_boundaryConditions; + auto rhbc = rhs.m_boundaryConditions; + Ioss::sort(lhbc.begin(), lhbc.end(), [](const BoundaryCondition &l, const BoundaryCondition &r) { + return l.m_bcName < r.m_bcName;}); + Ioss::sort(rhbc.begin(), rhbc.end(), [](const BoundaryCondition &l, const BoundaryCondition &r) { + return l.m_bcName < r.m_bcName;}); + if (!vec_equal(lhbc, rhbc)) { + fmt::print(Ioss::OUTPUT(), "StructuredBlock: Boundary Conditions mismatch\n"); + same = false; + } } if (!quiet) { diff --git a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h index 640e37b8bd45..283b3af21b06 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h +++ b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h @@ -12,6 +12,9 @@ #include "Ioss_NodeBlock.h" #include "Ioss_Property.h" #include "Ioss_ZoneConnectivity.h" +#if !defined BUILT_IN_SIERRA +#include +#endif #include #include #include @@ -368,3 +371,12 @@ namespace Ioss { }; } // 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_Utils.C b/packages/seacas/libraries/ioss/src/Ioss_Utils.C index eb458f12de08..a61e1d5bd3ca 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_Utils.C +++ b/packages/seacas/libraries/ioss/src/Ioss_Utils.C @@ -1369,7 +1369,7 @@ void Ioss::Utils::info_fields(const Ioss::GroupingEntity *ige, Ioss::Field::Role for (const auto &field_name : fields) { if (detail) { const auto &field_ref = ige->get_fieldref(field_name); - std::cout << field_ref << suffix; + fmt::print("{}{}", field_ref, suffix); } else { const Ioss::VariableType *var_type = ige->get_field(field_name).raw_storage(); @@ -1377,7 +1377,7 @@ void Ioss::Utils::info_fields(const Ioss::GroupingEntity *ige, Ioss::Field::Role 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); + fmt::print("{}", suffix); cur_out = 8; } } diff --git a/packages/seacas/libraries/ioss/src/Ioss_VariableType.C b/packages/seacas/libraries/ioss/src/Ioss_VariableType.C index c14a7cfa15bf..21a21216f558 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 diff --git a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h index dda947eb6ee9..1df3ded9432b 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h @@ -10,6 +10,10 @@ #include #include #include +#if !defined BUILT_IN_SIERRA +#include +#include +#endif #include #include #include @@ -151,3 +155,13 @@ 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 + { + }; +} // namespace fmt +#endif +#endif diff --git a/packages/seacas/libraries/ioss/src/adios/AdiosWrapper.C b/packages/seacas/libraries/ioss/src/adios/AdiosWrapper.C index 63de1523f77a..6efb3353dfd2 100644 --- a/packages/seacas/libraries/ioss/src/adios/AdiosWrapper.C +++ b/packages/seacas/libraries/ioss/src/adios/AdiosWrapper.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. // @@ -12,9 +12,14 @@ namespace Ioad { AdiosWrapper::AdiosWrapper(Ioss_MPI_Comm comm, const std::string &filename, bool is_input, unsigned long rank, const Ioss::PropertyManager &properties) - : adios2::ADIOS(comm), adios2::IO(IOInit(properties, is_input)), - adios2::Engine(EngineInit(filename, is_input)), m_Rank(rank), m_Communicator(comm), - m_OpenStep(false) +#if ADIOS2_USE_MPI + : adios2::ADIOS(comm), +#else + : adios2::ADIOS(), +#endif + adios2::IO(IOInit(properties, is_input)), + adios2::Engine(EngineInit(filename, is_input)), m_Rank(rank), m_Communicator(comm), + m_OpenStep(false) { } diff --git a/packages/seacas/libraries/ioss/src/adios/AdiosWrapper.hpp b/packages/seacas/libraries/ioss/src/adios/AdiosWrapper.hpp index 1705d955480a..3cc759cc1d26 100644 --- a/packages/seacas/libraries/ioss/src/adios/AdiosWrapper.hpp +++ b/packages/seacas/libraries/ioss/src/adios/AdiosWrapper.hpp @@ -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. // @@ -15,7 +15,7 @@ namespace Ioad { template void AdiosWrapper::DefineVariable(const std::string &name, const adios2::Dims &shape, const adios2::Dims &start, const adios2::Dims &count, - const bool constantDims) + const bool /*constantDims*/) { adios2::Variable var = this->IO::InquireVariable(name); if (!var) { diff --git a/packages/seacas/libraries/ioss/src/adios/Ioad_DatabaseIO.h b/packages/seacas/libraries/ioss/src/adios/Ioad_DatabaseIO.h index 632f5332d2b7..54b0caef8b36 100644 --- a/packages/seacas/libraries/ioss/src/adios/Ioad_DatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/adios/Ioad_DatabaseIO.h @@ -60,18 +60,18 @@ namespace Ioad { size_t data_size) const override; int64_t get_field_internal(const Ioss::ElementBlock *eb, const Ioss::Field &field, void *data, size_t data_size) const override; - int64_t get_field_internal(const Ioss::StructuredBlock *sb, const Ioss::Field &field, - void *data, size_t data_size) const override + int64_t get_field_internal(const Ioss::StructuredBlock */* sb */, const Ioss::Field &/*field*/, + void */*data*/, size_t /*data_size*/) const override { return -1; } - int64_t get_field_internal(const Ioss::Assembly *sb, const Ioss::Field &field, void *data, - size_t data_size) const override + int64_t get_field_internal(const Ioss::Assembly */*sb*/, const Ioss::Field &/*field*/, void */*data*/, + size_t /*data_size*/) const override { return -1; } - int64_t get_field_internal(const Ioss::Blob *sb, const Ioss::Field &field, void *data, - size_t data_size) const override + int64_t get_field_internal(const Ioss::Blob */*sb*/, const Ioss::Field &/*field*/, void */*data*/, + size_t /*data_size*/) const override { return -1; } @@ -120,18 +120,18 @@ namespace Ioad { size_t data_size) const override; int64_t put_field_internal(const Ioss::CommSet *cs, const Ioss::Field &field, void *data, size_t data_size) const override; - int64_t put_field_internal(const Ioss::StructuredBlock *sb, const Ioss::Field &field, - void *data, size_t data_size) const override + int64_t put_field_internal(const Ioss::StructuredBlock */*sb*/, const Ioss::Field &/*field*/, + void */*data*/, size_t /*data_size*/) const override { return -1; } - int64_t put_field_internal(const Ioss::Assembly *sb, const Ioss::Field &field, void *data, - size_t data_size) const override + int64_t put_field_internal(const Ioss::Assembly */*sb*/, const Ioss::Field &/*field*/, void */*data*/, + size_t /*data_size*/) const override { return -1; } - int64_t put_field_internal(const Ioss::Blob *sb, const Ioss::Field &field, void *data, - size_t data_size) const override + int64_t put_field_internal(const Ioss::Blob */*sb*/, const Ioss::Field &/*field*/, void */*data*/, + size_t /*data_size*/) const override { return -1; } diff --git a/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C b/packages/seacas/libraries/ioss/src/catalyst/Iocatalyst_DatabaseIO.C index e17738a9c63a..379fc673a5bf 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 @@ -72,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"; @@ -588,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()) { @@ -1249,13 +1285,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"); @@ -1673,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 766a8dc8c16d..48bc67470448 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) { @@ -87,4 +88,47 @@ 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); +} + +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 diff --git a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C index f4fa782a21d5..01a7c4ba557c 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/exodus/Ioex_BaseDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C index 1e04863d48e7..5cb2caec64e8 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.C @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include "Ioex_Utils.h" #include "Ioss_Assembly.h" @@ -86,6 +88,8 @@ namespace { template void write_attribute_names(int exoid, ex_entity_type type, const std::vector &entities); + void query_groups(int exoid, Ioss::NameList& names, bool return_full_names); + class AssemblyTreeFilter { public: @@ -257,6 +261,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") && @@ -265,24 +270,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(); @@ -490,11 +502,47 @@ namespace Ioex { ex_set_max_name_length(m_exodusFilePtr, maximumNameLength); } + bool BaseDatabaseIO::open_root_group_nl() + { + // Get existing file pointer... + bool success = false; + + Ioss::SerializeIO serializeIO_(this); + 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... bool success = false; + Ioss::SerializeIO serializeIO_(this); int exoid = get_file_pointer(); m_groupName = group_name; @@ -515,6 +563,7 @@ namespace Ioex { bool success = false; if (!is_input()) { // Get existing file pointer... + Ioss::SerializeIO serializeIO_(this); int exoid = get_file_pointer(); // Check name for '/' which is not allowed since it is the @@ -1994,7 +2043,7 @@ namespace Ioex { bool do_metadata = true; Ioss::Utils::check_set_bool_property(properties, "OUTPUT_FIELD_METADATA", do_metadata); if (do_metadata) { - output_field_metadata(); + output_field_metadata(); } } } @@ -2006,107 +2055,112 @@ namespace Ioex { // 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) { + + std::vector exo_fields(results_fields.size()); + for (const auto &[i, field_name] : Ioss::enumerate(results_fields)) { + exo_fields[i].type[0] = EX_SCALAR; + 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); + Ioss::Utils::copy_string(exo_fields[i].name, field_name); + exo_fields[i].entity_type = type; + exo_fields[i].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; + exo_fields[i].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; + exo_fields[i].type[0] = Ioex::map_ioss_field_type(composed->get_base_type()); + exo_fields[i].cardinality[0] = composed->get_base_type()->component_count(); + char separator0 = field.get_suffix_separator(); + exo_fields[i].component_separator[0] = separator0 == 1 ? default_separator : separator0; - if (exo_field.type[0] == EX_FIELD_TYPE_USER_DEFINED) { + if (exo_fields[i].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) { + for (int ii = 0; ii < nsvt->component_count(); ii++) { + if (ii > 0) { suffices += ","; } - suffices += nsvt->label(i + 1, 0); + suffices += nsvt->label(ii + 1, 0); } - Ioss::Utils::copy_string(exo_field.suffices, suffices.c_str(), EX_MAX_NAME + 1); + Ioss::Utils::copy_string(exo_fields[i].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], + exo_fields[i].type[1] = Ioex::map_ioss_field_type(composed->get_secondary_type()); + exo_fields[i].cardinality[1] = composed->get_secondary_type()->component_count(); + char separator1 = field.get_suffix_separator(1); + exo_fields[i].component_separator[1] = separator1 == 1 ? default_separator : separator1; + if (exo_fields[i].type[1] == EX_BASIS || exo_fields[i].type[1] == EX_QUADRATURE) { + exo_fields[i].type_name[0] = ','; + Ioss::Utils::copy_string(&exo_fields[i].type_name[1], composed->get_secondary_type()->name(), EX_MAX_NAME); } } else if (storage_type == Ioss::VariableType::Type::COMPOSITE) { - exo_field.nesting = 2; + exo_fields[i].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_fields[i].type[0] = Ioex::map_ioss_field_type(composite->get_base_type()); + exo_fields[i].cardinality[0] = composite->get_base_type()->component_count(); + char separator0 = field.get_suffix_separator(); + exo_fields[i].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; + exo_fields[i].type[1] = EX_FIELD_TYPE_SEQUENCE; + exo_fields[i].cardinality[1] = composite->get_num_copies(); + char separator1 = field.get_suffix_separator(1); + exo_fields[i].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(); + exo_fields[i].nesting = 1; + exo_fields[i].type[0] = Ioex::map_ioss_field_type(storage); + if (exo_fields[i].type[0] == EX_FIELD_TYPE_SEQUENCE) { + exo_fields[i].cardinality[0] = storage->component_count(); } - if (exo_field.type[0] == EX_BASIS) { + if (exo_fields[i].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()); + exo_fields[i].cardinality[0] = storage->component_count(); + Ioss::Utils::copy_string(exo_fields[i].type_name, basis->name()); } - if (exo_field.type[0] == EX_QUADRATURE) { + if (exo_fields[i].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()); + exo_fields[i].cardinality[0] = storage->component_count(); + Ioss::Utils::copy_string(exo_fields[i].type_name, quad->name()); } - if (exo_field.type[0] == EX_FIELD_TYPE_USER_DEFINED) { + if (exo_fields[i].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(); + exo_fields[i].cardinality[0] = nsvt->component_count(); std::string suffices{}; - for (int i = 0; i < nsvt->component_count(); i++) { - if (i > 0) { + for (int ii = 0; ii < nsvt->component_count(); ii++) { + if (ii > 0) { suffices += ","; } - suffices += nsvt->label(i + 1, 0); + suffices += nsvt->label(ii + 1, 0); } - Ioss::Utils::copy_string(exo_field.suffices, suffices.c_str(), EX_MAX_NAME + 1); + Ioss::Utils::copy_string(exo_fields[i].suffices, suffices.c_str(), EX_MAX_NAME + 1); } - char separator = field.get_suffix_separator(); - exo_field.component_separator[0] = separator == 1 ? default_separator : separator; + char separator = field.get_suffix_separator(); + exo_fields[i].component_separator[0] = separator == 1 ? default_separator : separator; } + } + ex_put_multi_field_metadata(exoid, Data(exo_fields), exo_fields.size()); + for (const auto &exo_field : exo_fields) { 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); } @@ -2182,6 +2236,7 @@ namespace Ioex { { Ioss::SerializeIO serializeIO_(this); // Output the 'basis' and 'quadrature' type metadata... + exi_persist_redef(get_file_pointer(), __func__); output_type_metadata(get_file_pointer()); const Ioss::NodeBlockContainer &node_blocks = get_region()->get_node_blocks(); @@ -2217,6 +2272,7 @@ namespace Ioex { const Ioss::SideSetContainer &sidesets = get_region()->get_sidesets(); internal_output_field_metadata(get_file_pointer(), EX_SIDE_SET, sidesets); + exi_persist_leavedef(get_file_pointer(), __func__); } // common @@ -3164,6 +3220,87 @@ namespace Ioex { // Write coordinate frame data... write_coordinate_frames(get_file_pointer(), get_region()->get_coordinate_frames()); } + + + 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); + query_groups(group_root, names, return_full_names); + + return 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(); + } + + int BaseDatabaseIO::num_child_group_nl() + { + Ioss::SerializeIO serializeIO_(this); + int exoid = get_file_pointer(); + exoid = ex_inquire_int(exoid, EX_INQ_GROUP_ROOT); + int num_children = ex_inquire_int(exoid, EX_INQ_NUM_CHILD_GROUPS); + return num_children; + } + + bool BaseDatabaseIO::open_child_group_nl(int index) + { + if(index < 0) return false; + Ioss::SerializeIO serializeIO_(this); + int exoid = get_file_pointer(); + int num_children = ex_inquire_int(exoid, EX_INQ_NUM_CHILD_GROUPS); + if(num_children == 0) return true; + + if(index >= num_children) return false; + + std::vector children(num_children); + + int ierr = ex_get_group_ids(exoid, nullptr, Data(children)); + if (ierr < 0) { + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); + } + + exoid = children[index]; + + 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; + ierr = ex_inquire(exoid, EX_INQ_GROUP_NAME, &idum, &rdum, group_name.data()); + if (ierr < 0) { + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); + } + + m_exodusFilePtr = exoid; + m_groupName = std::string(group_name.data()); + + return true; + } + } // namespace Ioex namespace { @@ -3465,4 +3602,41 @@ namespace { } #endif } + + void query_groups(int exoid, Ioss::NameList& names, bool return_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) { + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); + } + + 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) { + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); + } + 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) { + Ioex::exodus_error(exoid, __LINE__, __func__, __FILE__); + } + + for (int i = 0; i < num_children; i++) { + query_groups(children[i], names, return_full_names); + } + } } // namespace diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h index 1d05234f8a90..b6f18fc75653 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_BaseDatabaseIO.h @@ -99,8 +99,14 @@ 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; + + int num_child_group_nl() override; + bool open_child_group_nl(int index) 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; + Ioss::NameList groups_describe_nl(bool return_full_names) override; bool begin_nl(Ioss::State state) override; bool end_nl(Ioss::State state) override; @@ -320,6 +326,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 5e0540cc7ef5..d368bd2ff93d 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_DatabaseIO.C @@ -171,6 +171,9 @@ namespace Ioex { IOSS_ERROR(errmsg); } } + + open_root_group_nl(); + open_child_group_nl(0); } bool DatabaseIO::check_valid_file_ptr(bool write_message, std::string *error_msg, int *bad_count, @@ -642,13 +645,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 +659,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,14 +683,32 @@ namespace Ioex { else { { Ioss::SerializeIO serializeIO_(this); - 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); + // Need to sync timestep count across ranks if parallel... + if (isParallel) { + 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(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 + // 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"); + } + } + } + m_timestepCount = min_timestep_count; + } + + 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 @@ -734,8 +754,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()); @@ -1125,12 +1145,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 +1203,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]; diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_ParallelDatabaseIO.C index b0d9f4304d36..8ce9dd59b7bd 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; } diff --git a/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C b/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C index d922e6fafd35..12ac92d9e6b0 100644 --- a/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C +++ b/packages/seacas/libraries/ioss/src/exodus/Ioex_Utils.C @@ -17,6 +17,7 @@ #include #include #include +#include #include "Ioss_BasisVariableType.h" #include "Ioss_CoordinateFrame.h" @@ -616,7 +617,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/exonull/Ioexnl_DecompositionData.C b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.C index 84848ee2bc0c..2354423d1774 100644 --- a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.C +++ b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.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. // @@ -7,26 +7,16 @@ #include "Ioss_CodeTypes.h" #include "exonull/Ioexnl_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_SmartAssert.h" -#include "Ioss_Sort.h" #include "Ioss_Utils.h" #include "exonull/Ioexnl_Utils.h" #include -#include #include -#include #include -#include -#include -#include #include -#include -#include #include namespace Ioexnl { diff --git a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.h b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.h index 603627bca150..4ac731d1e70e 100644 --- a/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.h +++ b/packages/seacas/libraries/ioss/src/exonull/Ioexnl_DecompositionData.h @@ -11,24 +11,18 @@ #if defined PARALLEL_AWARE_EXODUS #include "Ioss_CodeTypes.h" +#include #include #include "ioexnl_export.h" -#if !defined(NO_PARMETIS_SUPPORT) -#include -#endif -#undef MPICPP -#if !defined(NO_ZOLTAN_SUPPORT) -#include -#endif #include "Ioss_Decomposition.h" -#include "Ioss_Map.h" -#include "Ioss_PropertyManager.h" namespace Ioss { class Field; -} + class Map; + class PropertyManager; +} // namespace Ioss namespace Ioexnl { class IOEXNL_EXPORT DecompositionDataBase diff --git a/packages/seacas/libraries/ioss/src/faodel/Iofaodel_FieldSerialization.h b/packages/seacas/libraries/ioss/src/faodel/Iofaodel_FieldSerialization.h index 50ab629d1bdf..28bbc4229fe4 100644 --- a/packages/seacas/libraries/ioss/src/faodel/Iofaodel_FieldSerialization.h +++ b/packages/seacas/libraries/ioss/src/faodel/Iofaodel_FieldSerialization.h @@ -1,4 +1,4 @@ -// 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. // @@ -40,7 +40,7 @@ namespace Iofaodel { IOFAODEL_EXPORT size_t data_size(const Ioss::Field &f); // Caller should write their own version of this - // FieldFunction should return a function or a lamba that matches the + // FieldFunction should return a function or a lambda that matches the // signature below. The function it returns may or may not capture variables // that are given to the user-defined function. // Some examples are given in this file and are also useful diff --git a/packages/seacas/libraries/ioss/src/faodel/Iofaodel_PropertySerialization.h b/packages/seacas/libraries/ioss/src/faodel/Iofaodel_PropertySerialization.h index 68ac3d42372d..50c2fefaa21d 100644 --- a/packages/seacas/libraries/ioss/src/faodel/Iofaodel_PropertySerialization.h +++ b/packages/seacas/libraries/ioss/src/faodel/Iofaodel_PropertySerialization.h @@ -1,4 +1,4 @@ -// 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. // @@ -23,7 +23,7 @@ namespace Iofaodel { IOFAODEL_EXPORT size_t data_size(const Ioss::Property &p); // Caller should write their own version of this - // PropertyFunction should return a function or a lamba that matches the + // PropertyFunction should return a function or a lambda that matches the // signature below. The function it returns may or may not capture variables // that are given to the user-defined function. // Some examples are given in this file and are also useful diff --git a/packages/seacas/libraries/ioss/src/init/Ionit_Initializer.C b/packages/seacas/libraries/ioss/src/init/Ionit_Initializer.C index 9018a7af82aa..47be56adea03 100644 --- a/packages/seacas/libraries/ioss/src/init/Ionit_Initializer.C +++ b/packages/seacas/libraries/ioss/src/init/Ionit_Initializer.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. // @@ -10,8 +10,10 @@ #if defined(SEACAS_HAVE_EXODUS) #include "exodus/Ioex_IOFactory.h" +#if defined(SEACAS_HAVE_EXONULL) #include "exonull/Ioexnl_IOFactory.h" #endif +#endif #include "gen_struc/Iogs_DatabaseIO.h" #include "generated/Iogn_DatabaseIO.h" @@ -47,6 +49,8 @@ #include "Ioss_IOFactory.h" +#include "Ioss_DynamicTopology.h" + namespace { #if defined(IOSS_THREADSAFE) std::mutex m_; @@ -72,8 +76,10 @@ namespace Ioss::Init { #if defined(SEACAS_HAVE_EXODUS) Ioex::IOFactory::factory(); // Exodus +#if defined(SEACAS_HAVE_EXONULL) Ioexnl::IOFactory::factory(); #endif +#endif #if defined(SEACAS_HAVE_PAMGEN) Iopg::IOFactory::factory(); // Pamgen #endif @@ -94,6 +100,7 @@ namespace Ioss::Init { Iogs::IOFactory::factory(); // Structured Mesh Generator Ionull::IOFactory::factory(); Ioss::StorageInitializer(); + Ioss::DynamicTopologyBroker::broker(); Ioss::Initializer(); Iotr::Initializer(); #ifdef HAVE_SEACASIOSS_ADIOS2 diff --git a/packages/seacas/libraries/ioss/src/main/io_info.C b/packages/seacas/libraries/ioss/src/main/io_info.C index 1483423816ee..c8f25eaf6094 100644 --- a/packages/seacas/libraries/ioss/src/main/io_info.C +++ b/packages/seacas/libraries/ioss/src/main/io_info.C @@ -317,7 +317,11 @@ namespace { if (!sb->m_zoneConnectivity.empty()) { fmt::print("\tConnectivity with other blocks:\n"); for (const auto &zgc : sb->m_zoneConnectivity) { +#if defined __NVCC__ std::cout << zgc << "\n"; +#else + fmt::print("{}\n", zgc); +#endif } } if (!sb->m_boundaryConditions.empty()) { @@ -331,7 +335,11 @@ namespace { }); for (const auto &bc : sb_bc) { +#if defined __NVCC__ std::cout << bc << "\n"; +#else + fmt::print("{}\n", bc); +#endif } } if (interFace.compute_bbox()) { diff --git a/packages/seacas/libraries/ioss/src/main/shell_interface.C b/packages/seacas/libraries/ioss/src/main/shell_interface.C index 1bd000384359..40fad4c4e6b3 100644 --- a/packages/seacas/libraries/ioss/src/main/shell_interface.C +++ b/packages/seacas/libraries/ioss/src/main/shell_interface.C @@ -48,6 +48,9 @@ void IOShell::Interface::enroll_options() "Database type for output file:" #if defined(SEACAS_HAVE_EXODUS) " exodus" +#if defined(SEACAS_HAVE_EXONULL) + " exonull" +#endif #endif #if defined(SEACAS_HAVE_CGNS) " cgns" @@ -55,7 +58,7 @@ void IOShell::Interface::enroll_options() #if defined(SEACAS_HAVE_FAODEL) " faodel" #endif - ".\n\t\tIf not specified, guess from extension or exodus is the default.", + " null.\n\t\tIf not specified, guess from extension or exodus is the default.", "unknown"); options_.enroll("compare", Ioss::GetLongOption::NoValue, "Compare the contents of the INPUT and OUTPUT files.", nullptr); @@ -215,10 +218,10 @@ void IOShell::Interface::enroll_options() "Files are decomposed externally into a file-per-processor in a parallel run.", nullptr); - options_.enroll( - "add_processor_id_field", Ioss::GetLongOption::NoValue, - "Add a cell-centered field whose value is the processor id of that cell", nullptr); - + options_.enroll("add_processor_id_field", Ioss::GetLongOption::NoValue, + "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;\n\t\t0 to disable", @@ -233,10 +236,11 @@ void IOShell::Interface::enroll_options() "If non-zero, then put <$val> timesteps in each file. Then close file and start new file.", nullptr); - options_.enroll("split_cyclic", Ioss::GetLongOption::MandatoryValue, - "If non-zero, then the `split_times` timesteps will be put into <$val> files\n\t\tand " - "then recycle filenames.", - nullptr); + options_.enroll( + "split_cyclic", Ioss::GetLongOption::MandatoryValue, + "If non-zero, then the `split_times` timesteps will be put into <$val> files\n\t\tand " + "then recycle filenames.", + nullptr); options_.enroll("file_per_state", Ioss::GetLongOption::NoValue, "put transient data for each timestep in separate file (EXPERIMENTAL)", nullptr); @@ -320,7 +324,7 @@ void IOShell::Interface::enroll_options() options_.enroll("omit_sets", Ioss::GetLongOption::MandatoryValue, "comma-separated list of nodeset/edgeset/faceset/elemset/sideset names\n" - "\t\tthat should NOT be transferred to output database", + "\t\tthat should NOT be transferred to output database", nullptr); options_.enroll("boundary_sideset", Ioss::GetLongOption::NoValue, @@ -560,7 +564,7 @@ bool IOShell::Interface::parse_options(int argc, char **argv, int my_processor) } if (options_.retrieve("line_decomp") != nullptr) { - line_decomp = true; + line_decomp = true; decomp_extra = options_.get_option_value("line_decomp", decomp_extra); } diff --git a/packages/seacas/libraries/ioss/src/main/skinner.C b/packages/seacas/libraries/ioss/src/main/skinner.C index 6ab0a4f0bc03..bda07b2181c2 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); } } diff --git a/packages/seacas/libraries/ioss/src/main/sphgen.C b/packages/seacas/libraries/ioss/src/main/sphgen.C index b41d5b98320d..054e6e2823c8 100644 --- a/packages/seacas/libraries/ioss/src/main/sphgen.C +++ b/packages/seacas/libraries/ioss/src/main/sphgen.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. // @@ -232,12 +232,12 @@ namespace { Ioss::ElementBlock *eb = *I; ++I; std::string type = eb->topology()->name(); + std::string name = eb->name(); if (type == Ioss::Hex8::name) { sph_node_count += eb->entity_count(); // Add the element block... - int num_elem = eb->entity_count(); - std::string name = eb->name(); + int num_elem = eb->entity_count(); auto *ebn = new Ioss::ElementBlock(output_region.get_database(), name, "sphere", num_elem); int id = eb->get_property("id").get_int(); ebn->property_add(Ioss::Property("id", id)); @@ -253,6 +253,9 @@ namespace { ns->property_add(Ioss::Property("id", id)); output_region.add(ns); } + else { + fmt::print("Skipping block {} which contains {} elements.\n", name, type); + } } // Define a node block... 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 270b20da68c0..51d6490033a8 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); diff --git a/packages/seacas/libraries/ioss/src/unit_tests/CMakeLists.txt b/packages/seacas/libraries/ioss/src/unit_tests/CMakeLists.txt index 17a7bbb4403e..db5ff09522f7 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 000000000000..b42cc43027d1 --- /dev/null +++ b/packages/seacas/libraries/ioss/src/unit_tests/UnitTestDynamicTopology.C @@ -0,0 +1,1111 @@ +// 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 + +#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_DatabaseIO.h" // for DatabaseIO +#include "Ioss_DBUsage.h" +#include "Ioss_ElementBlock.h" +#include "Ioss_Field.h" // for Field, etc +#include "Ioss_FileInfo.h" +#include "Ioss_IOFactory.h" +#include "Ioss_NodeBlock.h" +#include "Ioss_ParallelUtils.h" +#include "Ioss_Property.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_) + : Ioss::DynamicTopologyObserver(nullptr) + , inputRegion(inputRegion_) + , elemFieldName(elemFieldName_) + , fileControlOption(fileControlOption_) + {} + + virtual ~Observer() {} + + void define_model() override + { + ::define_model(inputRegion, *get_region()); + } + + void write_model() override + { + ::write_model(inputRegion, *get_region()); + } + + void define_transient() override + { + ::define_transient(inputRegion, *get_region(), elemFieldName); + } + + Ioss::FileControlOption get_control_option() const + { + return fileControlOption; + } + +private: + Observer(); + + Ioss::Region& inputRegion; + const std::string elemFieldName; + Ioss::FileControlOption fileControlOption; +}; + +struct OutputParams { + OutputParams(const std::string& outFile_) + : outFile(outFile_) {} + + OutputParams(const std::string& outFile_, const std::string& elemFieldName_) + : outFile(outFile_) + , elemFieldName(elemFieldName_) {} + + void set_data(const std::vector& output_times_, + const std::vector& output_steps_, + const std::vector& modification_steps_) + { + ASSERT_EQ(output_times_.size(), output_steps_.size()); + ASSERT_EQ(output_times_.size(), modification_steps_.size()); + + size_t numSteps = output_times_.size(); + for(auto i=1; i output_times_[i-1]); + } + + output_times = output_times_; + output_steps = output_steps_; + modification_steps = modification_steps_; + } + + void set_data(const std::vector& output_steps_, + const std::vector& modification_steps_) + { + ASSERT_EQ(output_steps_.size(), modification_steps_.size()); + + size_t numSteps = output_steps_.size(); + for(size_t i=0; i 0) { + // Monotone increasing + EXPECT_TRUE(time > output_times[numSteps-1]); + } + + output_times.push_back(time); + output_steps.push_back(do_output); + modification_steps.push_back(do_modification); + + return *this; + } + + void clear() + { + output_times.clear(); + output_steps.clear(); + modification_steps.clear(); + } + + std::string outFile{"file.g"}; + std::string elemFieldName{"elem_field"}; + std::vector output_times; + std::vector output_steps; + std::vector modification_steps; +}; + +void do_output(Ioss::Region &o_region, + const OutputParams& params, + size_t step, + double& minTime, + int& maxStep, + bool& doneOutputAfterModification) +{ + if(params.output_steps[step]) { + if(!doneOutputAfterModification) { + minTime = params.output_times[step]; + } + + write_transient(o_region, params.elemFieldName, params.output_times[step]); + + auto min_result = o_region.get_min_time(); + EXPECT_EQ(1, min_result.first); + EXPECT_NEAR(minTime, min_result.second, 1.0e-6); + + auto max_result = o_region.get_max_time(); + EXPECT_EQ(maxStep, max_result.first); + EXPECT_NEAR(params.output_times[step], max_result.second, 1.0e-6); + + maxStep++; + doneOutputAfterModification = true; + } +} + +void run_topology_change(const Ioss::Region& i_region, + Ioss::Region &o_region, + const OutputParams& params) +{ + auto observer = o_region.get_mesh_modification_observer(); + + define_model(i_region, o_region); + write_model(i_region, o_region); + + define_transient(i_region, o_region, params.elemFieldName); + + auto numSteps = params.output_steps.size(); + + int maxStep = 1; + + double minTime = numSteps > 0 ? params.output_times[0] : 0.0; + double maxTime = numSteps > 0 ? params.output_times[0] : 0.0; + + bool doneOutputAfterModification = true; + + for(size_t i=0; iset_topology_modification(Ioss::TOPOLOGY_UNKNOWN); + maxStep = 1; + doneOutputAfterModification = false; + } + + do_output(o_region, params, i, minTime, maxStep, doneOutputAfterModification); + } +} + +void cleanup_simple_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()); + + std::string file3 = Ioss::Utils::decode_filename(outFile + "-s0003", util.parallel_rank(), util.parallel_size()); + unlink(file3.c_str()); + + std::string file4 = Ioss::Utils::decode_filename(outFile + "-s0004", util.parallel_rank(), util.parallel_size()); + unlink(file4.c_str()); +} + +void run_multi_file_simple_topology_change(const OutputParams& params) +{ + 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", params.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, params.elemFieldName, fileControlOption); + o_region.register_mesh_modification_observer(observer); + + run_topology_change(i_region, o_region, params); +} + +TEST(TestDynamicWrite, multi_file_simple_topology_modification) +{ + std::string outFile("multiFileManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + OutputParams params(outFile, elemFieldName); + + std::vector output_steps{true , true, true, true, true, true}; + std::vector modification_steps{false, true, false, true, true, false}; + + params.set_data(output_steps, modification_steps); + + cleanup_simple_multi_files(outFile); + run_multi_file_simple_topology_change(params); + cleanup_simple_multi_files(outFile); +} + +void cleanup_cyclic_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 + "-A", util.parallel_rank(), util.parallel_size()); + unlink(file2.c_str()); + + std::string file3 = Ioss::Utils::decode_filename(outFile + "-B", util.parallel_rank(), util.parallel_size()); + unlink(file3.c_str()); + + std::string file4 = Ioss::Utils::decode_filename(outFile + "-C", util.parallel_rank(), util.parallel_size()); + unlink(file4.c_str()); +} + +void run_multi_file_cyclic_topology_change(const OutputParams& params) +{ + 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", params.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, params.elemFieldName, fileControlOption); + o_region.register_mesh_modification_observer(observer); + + o_region.set_file_cyclic_count(3); + run_topology_change(i_region, o_region, params); +} + +TEST(TestDynamicWrite, multi_file_cyclic_topology_modification) +{ + std::string outFile("cyclicMultiFileManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + OutputParams params(outFile, elemFieldName); + + std::vector output_times{0.0 , 0.5 , 1.5 , 1.75, 2.0 , 3.0}; + std::vector output_steps{true , true, true , true, true, true }; + std::vector modification_steps{false, true, false, true, true, false}; + + params.set_data(output_times, output_steps, modification_steps); + + cleanup_cyclic_multi_files(outFile); + run_multi_file_cyclic_topology_change(params); + cleanup_cyclic_multi_files(outFile); +} + +void fill_group_gold_names(const int numFileGroups, + std::vector& gold_names, + std::vector& gold_full_names) +{ + gold_names.clear(); + gold_full_names.clear(); + + gold_names.push_back("/"); + gold_full_names.push_back("/"); + + for(int i=1; i<=numFileGroups; i++) { + std::ostringstream oss; + oss << Ioss::DynamicTopologyFileControl::group_prefix(); + oss << i; + + gold_names.push_back(oss.str()); + gold_full_names.push_back("/" + oss.str()); + } +} + +void test_group_names(Ioss::DatabaseIO *database) +{ + Ioss::NameList names = database->groups_describe(false); + Ioss::NameList full_names = database->groups_describe(true); + + std::vector gold_names; + std::vector gold_full_names; + + fill_group_gold_names(database->num_child_group(), gold_names, gold_full_names); + + EXPECT_EQ(gold_names, names); + EXPECT_EQ(gold_full_names, full_names); +} + +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 OutputParams& params) +{ + 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", params.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_GROUP_FILE; + auto observer = std::make_shared(i_region, params.elemFieldName, fileControlOption); + o_region.register_mesh_modification_observer(observer); + + run_topology_change(i_region, o_region, params); + test_group_names(o_database); +} + +TEST(TestDynamicWrite, single_file_simple_topology_modification) +{ + std::string outFile("singleFileManyBlocks.g"); + std::string elemFieldName = "elem_field"; + + OutputParams params(outFile, elemFieldName); + + params.add(0.0, true, false) + .add(1.0, true, true) + .add(2.0, true, false) + .add(3.0, true, true) + .add(4.0, true, true) + .add(5.0, true, false); + + cleanup_single_file(outFile); + run_single_file_simple_topology_change(params); + 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_GROUP_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 automatically at first child + o_database->create_subgroup("GROUP_2"); + + 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"}; + + 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 = 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"}; + + 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 = 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"}; + + EXPECT_EQ(gold_names, names); + EXPECT_EQ(gold_full_names, full_names); + } + + unlink(file1.c_str()); +} + + +void run_topology_change_with_multiple_output(const Ioss::Region& i_region, + Ioss::Region &o_region1, + Ioss::Region &o_region2, + const OutputParams& params1, + const OutputParams& params2) +{ + ASSERT_EQ(params1.modification_steps, params2.modification_steps); + + auto observer1 = o_region1.get_mesh_modification_observer(); + auto observer2 = o_region2.get_mesh_modification_observer(); + + define_model(i_region, o_region1); + write_model(i_region, o_region1); + define_transient(i_region, o_region1, params1.elemFieldName); + + define_model(i_region, o_region2); + write_model(i_region, o_region2); + define_transient(i_region, o_region2, params2.elemFieldName); + + auto numSteps = params1.output_steps.size(); + + int maxStep1 = 1; + int maxStep2 = 1; + + double minTime1 = numSteps > 0 ? params1.output_times[0] : 0.0; + double minTime2 = numSteps > 0 ? params2.output_times[0] : 0.0; + + bool doneOutputAfterModification1 = true; + bool doneOutputAfterModification2 = true; + + for(size_t i=0; iset_topology_modification(Ioss::TOPOLOGY_UNKNOWN); + maxStep1 = 1; + maxStep2 = 1; + + EXPECT_EQ(Ioss::TOPOLOGY_UNKNOWN, observer1->get_topology_modification()); + EXPECT_EQ(Ioss::TOPOLOGY_UNKNOWN, observer2->get_topology_modification()); + + doneOutputAfterModification1 = false; + doneOutputAfterModification2 = false; + } + + do_output(o_region1, params1, i, minTime1, maxStep1, doneOutputAfterModification1); + do_output(o_region2, params2, i, minTime2, maxStep2, doneOutputAfterModification2); + } +} + +void run_single_file_simple_topology_change_with_multiple_output(const std::string& model, + const OutputParams& params1, + const OutputParams& params2) +{ + Ioss::Init::Initializer io; + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + auto broker = Ioss::DynamicTopologyBroker::broker(); + broker->register_model(model); + + 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_database1 = Ioss::IOFactory::create("exodus", params1.outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region1(o_database1, "region1"); + EXPECT_TRUE(o_database1 != nullptr); + EXPECT_TRUE(o_database1->ok(true)); + + auto fileControlOption = Ioss::FileControlOption::CONTROL_AUTO_GROUP_FILE; + auto observer1 = std::make_shared(i_region, params1.elemFieldName, fileControlOption); + broker->register_observer(model, observer1, o_region1); + + Ioss::DatabaseIO *o_database2 = Ioss::IOFactory::create("exodus", params2.outFile, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region2(o_database2, "region2"); + EXPECT_TRUE(o_database2 != nullptr); + EXPECT_TRUE(o_database2->ok(true)); + + auto observer2 = std::make_shared(i_region, params2.elemFieldName, fileControlOption); + broker->register_observer(model, observer2, o_region2); + + run_topology_change_with_multiple_output(i_region, o_region1, o_region2, params1, params2); + + test_group_names(o_database1); + test_group_names(o_database2); +} + +TEST(TestDynamicWrite, single_file_simple_topology_modification_with_multiple_output) +{ + std::string outFile1("singleFileManyBlocks1.g"); + std::string outFile2("singleFileManyBlocks2.g"); + std::string elemFieldName = "elem_field"; + std::string model = "multiple-output"; + + OutputParams params1(outFile1, elemFieldName); + + params1.add(0.0, true , false) + .add(1.0, true , true) + .add(2.0, false, false) + .add(3.0, true , true) + .add(4.0, true , false) + .add(5.0, true , true); + + OutputParams params2(outFile2, elemFieldName); + + params2.add(0.0, true , false) + .add(1.0, true , true) + .add(2.0, true , false) + .add(3.0, false, true) + .add(4.0, true , false) + .add(5.0, true , true); + + cleanup_single_file(outFile1); + cleanup_single_file(outFile2); + run_single_file_simple_topology_change_with_multiple_output(model, params1, params2); + cleanup_single_file(outFile1); + cleanup_single_file(outFile2); +} + +TEST(TestDynamicWrite, same_model_triggers_same_modification_for_all_observers) +{ + Ioss::Init::Initializer io; + Ioss::ParallelUtils util(Ioss::ParallelUtils::comm_world()); + + std::string outFile1("sameModelManyBlocks1.g"); + std::string outFile2("sameModelManyBlocks2.g"); + std::string elemFieldName("elem_field"); + std::string model("same-model"); + + auto broker = Ioss::DynamicTopologyBroker::broker(); + broker->register_model(model); + + std::string file1 = Ioss::Utils::decode_filename(outFile1, util.parallel_rank(), util.parallel_size()); + std::string file2 = Ioss::Utils::decode_filename(outFile2, util.parallel_rank(), util.parallel_size()); + + unlink(file1.c_str()); + unlink(file2.c_str()); + + int numBlocks = util.parallel_size(); + if(numBlocks > 1) GTEST_SKIP(); + + 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_database1 = Ioss::IOFactory::create("exodus", outFile1, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region1(o_database1, "region1"); + EXPECT_TRUE(o_database1 != nullptr); + EXPECT_TRUE(o_database1->ok(true)); + + auto fileControlOption = Ioss::FileControlOption::CONTROL_AUTO_GROUP_FILE; + auto observer1 = std::make_shared(i_region, elemFieldName, fileControlOption); + broker->register_observer(model, observer1, o_region1); + + Ioss::DatabaseIO *o_database2 = Ioss::IOFactory::create("exodus", outFile2, Ioss::WRITE_RESULTS, + Ioss::ParallelUtils::comm_world(), + propertyManager); + Ioss::Region o_region2(o_database2, "region2"); + EXPECT_TRUE(o_database2 != nullptr); + EXPECT_TRUE(o_database2->ok(true)); + + auto observer2 = std::make_shared(i_region, elemFieldName, fileControlOption); + broker->register_observer(model, observer2, o_region2); + + EXPECT_EQ(Ioss::TOPOLOGY_SAME, observer1->get_topology_modification()); + EXPECT_EQ(Ioss::TOPOLOGY_SAME, observer2->get_topology_modification()); + + observer1->set_topology_modification(Ioss::TOPOLOGY_UNKNOWN); + + EXPECT_EQ(Ioss::TOPOLOGY_UNKNOWN, observer1->get_topology_modification()); + EXPECT_EQ(Ioss::TOPOLOGY_UNKNOWN, observer2->get_topology_modification()); + } + + unlink(file1.c_str()); + unlink(file2.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 OutputParams& params) +{ + Ioss::PropertyManager propertyManager; + + Ioss::DatabaseIO *i_database = Ioss::IOFactory::create("exodus", params.outFile, Ioss::READ_RESTART, + Ioss::ParallelUtils::comm_world(), + propertyManager); + + test_group_names(i_database); + + Ioss::Region i_region(i_database, "input_model"); + EXPECT_TRUE(i_database != nullptr); + EXPECT_TRUE(i_database->ok(true)); + + auto numSteps = params.output_steps.size(); + + int numMods = 0; + + int maxStep = 1; + + double minTime = numSteps > 0 ? params.output_times[0] : 0.0; + double maxTime = numSteps > 0 ? params.output_times[0] : 0.0; + + bool doneOutputAfterModification = true; + + Ioss::NameList names = i_database->groups_describe(false); + + for(size_t i=0; i //////// Global constants //////////// -std::vector ignored_properties = {"database_name"}; +std::vector ignored_properties = {"database_name", "base_filename", "database_type"}; std::vector ignored_fields = {"implicit_ids"}; std::vector ignore_errors = {"owning_processor"}; diff --git a/packages/seacas/scripts/exomerge3.py b/packages/seacas/scripts/exomerge3.py index 76779addcb45..19cb12820444 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 6e11226f16164960f9db2e7b76e556d10e9d7a0d Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 20 Aug 2024 16:40:21 -0600 Subject: [PATCH 2/7] IOSS: Remove build_in_sierra ifdefs around fmt code --- packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h | 4 ---- packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h index 283b3af21b06..9b510a31546c 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h +++ b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h @@ -12,9 +12,7 @@ #include "Ioss_NodeBlock.h" #include "Ioss_Property.h" #include "Ioss_ZoneConnectivity.h" -#if !defined BUILT_IN_SIERRA #include -#endif #include #include #include @@ -371,7 +369,6 @@ namespace Ioss { }; } // namespace Ioss -#if !defined BUILT_IN_SIERRA #if FMT_VERSION >= 90000 namespace fmt { template <> struct formatter : ostream_formatter @@ -379,4 +376,3 @@ 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 1df3ded9432b..9927e1b3d93a 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h @@ -10,10 +10,8 @@ #include #include #include -#if !defined BUILT_IN_SIERRA #include #include -#endif #include #include #include @@ -156,7 +154,6 @@ 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 @@ -164,4 +161,3 @@ namespace fmt { }; } // namespace fmt #endif -#endif From f169391a05e1e998bf92b525582c529b1c3ba4b6 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 20 Aug 2024 16:40:56 -0600 Subject: [PATCH 3/7] IOSS: Remove need for build_in_sierra ifdefs around cgns includes --- .../libraries/ioss/src/Ioss_StructuredBlock.h | 19 ------------------- .../ioss/src/Ioss_ZoneConnectivity.C | 4 ++-- .../ioss/src/Ioss_ZoneConnectivity.h | 13 +------------ .../libraries/ioss/src/cgns/Iocgns_Utils.C | 4 ---- 4 files changed, 3 insertions(+), 37 deletions(-) diff --git a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h index 9b510a31546c..7fb1c87c8073 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h +++ b/packages/seacas/libraries/ioss/src/Ioss_StructuredBlock.h @@ -24,17 +24,6 @@ #include "Ioss_EntityType.h" #include "ioss_export.h" -#if defined(SEACAS_HAVE_CGNS) && !defined(BUILT_IN_SIERRA) -#include - -using IOSS_SB_INT = cgsize_t; -#else -// If this is not being built with CGNS, then default to using 32-bit integers. -// Currently there is no way to input/output a structured mesh without CGNS, -// so this block is simply to get things to compile and probably has no use. -using IOSS_SB_INT = int; -#endif - namespace Ioss { class Region; class Field; @@ -237,14 +226,6 @@ namespace Ioss { return get_local_node_offset(index[0], index[1], index[2]); } - IOSS_NODISCARD std::vector get_cell_node_ids(bool add_offset) const - { - size_t node_count = get_property("node_count").get_int(); - std::vector ids(node_count); - get_cell_node_ids(Data(ids), add_offset); - return ids; - } - template size_t get_cell_node_ids(INT_t *idata, bool add_offset) const { // Fill 'idata' with the cell node ids which are the diff --git a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.C b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.C index b0f9706b7cf5..331758116984 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.C +++ b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.C @@ -389,9 +389,9 @@ namespace Ioss { return range; } - std::array ZoneConnectivity::transform_matrix() const + std::array ZoneConnectivity::transform_matrix() const { - std::array t_matrix{}; + std::array t_matrix{}; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { t_matrix[3 * i + j] = sign(m_transform[j]) * del(m_transform[j], i + 1); diff --git a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h index 9927e1b3d93a..765da23d8492 100644 --- a/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h +++ b/packages/seacas/libraries/ioss/src/Ioss_ZoneConnectivity.h @@ -19,17 +19,6 @@ #include "ioss_export.h" -#if defined(SEACAS_HAVE_CGNS) && !defined(BUILT_IN_SIERRA) -#include - -using IOSS_ZC_INT = cgsize_t; -#else -// If this is not being built with CGNS, then default to using 32-bit integers. -// Currently there is no way to input/output a structured mesh without CGNS, -// so this block is simply to get things to compile and probably has no use. -using IOSS_ZC_INT = int; -#endif - namespace Ioss { class Region; @@ -88,7 +77,7 @@ namespace Ioss { IOSS_NODISCARD bool has_faces() const; IOSS_NODISCARD bool retain_original() const; // True if need to retain in parallel decomp - IOSS_NODISCARD std::array transform_matrix() const; + IOSS_NODISCARD std::array transform_matrix() const; IOSS_NODISCARD Ioss::IJK_t transform(const Ioss::IJK_t &index_1) const; IOSS_NODISCARD Ioss::IJK_t inverse_transform(const Ioss::IJK_t &index_1) const; diff --git a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C index 01a7c4ba557c..84bbd54c37e1 100644 --- a/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C +++ b/packages/seacas/libraries/ioss/src/cgns/Iocgns_Utils.C @@ -1939,7 +1939,6 @@ Iocgns::Utils::resolve_processor_shared_nodes(Ioss::Region ®ion, int my_proce for (auto &owner_block : blocks) { int owner_zone = owner_block->get_property("zone").get_int(); - auto owner_ids = owner_block->get_cell_node_ids(true); for (const auto &zgc : owner_block->m_zoneConnectivity) { assert(zgc.m_donorProcessor >= 0); assert(zgc.m_ownerProcessor >= 0); @@ -1952,9 +1951,6 @@ Iocgns::Utils::resolve_processor_shared_nodes(Ioss::Region ®ion, int my_proce auto donor_block = region.get_structured_block(zgc.m_donorName); assert(donor_block != nullptr); int donor_zone = donor_block->get_property("zone").get_int(); - - auto donor_ids = donor_block->get_cell_node_ids(true); - std::vector i_range = zgc.get_range(1); std::vector j_range = zgc.get_range(2); std::vector k_range = zgc.get_range(3); From 4a3ac6bb5c2b857c983ca524531d29e5ce4c2294 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 21 Aug 2024 08:06:32 -0600 Subject: [PATCH 4/7] IOSS: Disable exonull tests if exonull not enabled --- packages/seacas/libraries/ioss/src/main/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/seacas/libraries/ioss/src/main/CMakeLists.txt b/packages/seacas/libraries/ioss/src/main/CMakeLists.txt index d2905ff5f856..dc447fa7d40e 100644 --- a/packages/seacas/libraries/ioss/src/main/CMakeLists.txt +++ b/packages/seacas/libraries/ioss/src/main/CMakeLists.txt @@ -306,12 +306,14 @@ TRIBITS_ADD_TEST( COMM mpi serial ) +IF (ENABLE_ExoNull) TRIBITS_ADD_TEST( io_shell ARGS "${DECOMP_ARG} ${JOIN_ARG} --out_type=exonull --in_type=generated 10x10x10+shell:xXyYzZ+sideset:xXyY+times:2+variables:element,2,nodal,3,sideset,4 null_file" NAME generated_to_exonull NOEXEPREFIX NOEXESUFFIX COMM mpi serial ) +ENDIF() if (TPL_ENABLE_MPI) set(EXTERNAL "--compose=external") @@ -324,12 +326,14 @@ TRIBITS_ADD_TEST( COMM mpi serial ) +IF (ENABLE_ExoNull) TRIBITS_ADD_TEST( io_shell ARGS "${DECOMP_ARG} ${JOIN_ARG} ${EXTERNAL} --out_type=exonull --in_type=generated 10x10x10+shell:xXyYzZ+sideset:xXyY+times:2+variables:element,2,nodal,3,sideset,4 null_file" NAME generated_to_exonull_fpp NOEXEPREFIX NOEXESUFFIX COMM mpi serial ) +ENDIF() if (${CMAKE_PROJECT_NAME}_ENABLE_SEACASExodiff) TRIBITS_ADD_ADVANCED_TEST(exodus_to_unstructured_cgns_to_exodus From 6a2a559a9aa104fb9116cd08204b35cf6a0012ed Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 21 Aug 2024 08:31:17 -0600 Subject: [PATCH 5/7] EXODUS_FOR: Fix test syntax --- packages/seacas/libraries/exodus_for/test/testall.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/seacas/libraries/exodus_for/test/testall.in b/packages/seacas/libraries/exodus_for/test/testall.in index b50717ef20e0..f49dbc9c79c5 100644 --- a/packages/seacas/libraries/exodus_for/test/testall.in +++ b/packages/seacas/libraries/exodus_for/test/testall.in @@ -1,5 +1,5 @@ #! /usr/bin/env bash -# 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. # @@ -111,7 +111,7 @@ ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[3]})) # test_nem - nemesis routines echo "test_nem ..." ${PREFIX} ${BINDIR}/f_test_nem${SUFFIX} | ${DIFF} -w - ${SRCDIR}/test_nem.dmp | tee test_nem.res -ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[1})) +ret_status=$((ret_status+${PIPESTATUS[0]}+${PIPESTATUS[1]})) echo "end test_nem, status = $ret_status" >> test.output # test partial read/write. Uses 'test.exo' from testwt. From 798220c3357ff88b8db37e845e3f12df243c7e86 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 21 Aug 2024 08:40:01 -0600 Subject: [PATCH 6/7] EXODUS: Disable tests with ordering issues temporarily --- .../libraries/exodus/test/CMakeLists.txt | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/seacas/libraries/exodus/test/CMakeLists.txt b/packages/seacas/libraries/exodus/test/CMakeLists.txt index fc3596d89435..6b1641c53c77 100644 --- a/packages/seacas/libraries/exodus/test/CMakeLists.txt +++ b/packages/seacas/libraries/exodus/test/CMakeLists.txt @@ -168,14 +168,15 @@ IF ( NETCDF_NCDUMP_BINARY ) ) IF (NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - TRIBITS_ADD_TEST( - testall - NOEXEPREFIX - NOEXESUFFIX - NAME exodus_unit_tests - COMM mpi serial - NUM_MPI_PROCS 1 - ) + +# TRIBITS_ADD_TEST( +# testall +# NOEXEPREFIX +# NOEXESUFFIX +# NAME exodus_unit_tests +# COMM mpi serial +# NUM_MPI_PROCS 1 +# ) if ( TPL_Netcdf_Enables_Netcdf4 ) TRIBITS_ADD_TEST( @@ -199,15 +200,15 @@ IF ( NETCDF_NCDUMP_BINARY ) endif() # if ( TPL_Netcdf_Enables_Netcdf5 ) - TRIBITS_ADD_TEST( - testall - NOEXEPREFIX - NOEXESUFFIX - NAME exodus_unit_tests_nc5_env - ARGS netcdf5 - COMM mpi serial - NUM_MPI_PROCS 1 - ) +# TRIBITS_ADD_TEST( +# testall +# NOEXEPREFIX +# NOEXESUFFIX +# NAME exodus_unit_tests_nc5_env +# ARGS netcdf5 +# COMM mpi serial +# NUM_MPI_PROCS 1 +# ) # endif() endif() ELSE() From 28d63a240f01b39dfc9dff8b6199916001c49cab Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Wed, 21 Aug 2024 15:43:06 -0600 Subject: [PATCH 7/7] NEM_SLICE: Another fix for non-1-D bars --- .../seacas/applications/nem_slice/elb_loadbal.C | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/packages/seacas/applications/nem_slice/elb_loadbal.C b/packages/seacas/applications/nem_slice/elb_loadbal.C index 2b42dbd5b5cf..f716bccf9f93 100644 --- a/packages/seacas/applications/nem_slice/elb_loadbal.C +++ b/packages/seacas/applications/nem_slice/elb_loadbal.C @@ -1641,22 +1641,6 @@ namespace { for (size_t ncnt = 0; ncnt < nhold; ncnt++) { hold_elem[ncnt] = graph->sur_elem[side_nodes[0]][ncnt]; } - - for (int ncnt = 0; ncnt < nnodes; ncnt++) { - /* Find elements connected to both node '0' and node 'ncnt+1' */ - nelem = find_inter(Data(hold_elem), Data(graph->sur_elem[side_nodes[(ncnt + 1)]]), - nhold, graph->sur_elem[side_nodes[(ncnt + 1)]].size(), - Data(pt_list)); - - if (nelem < 2) { - break; - } - - nhold = nelem; - for (int ncnt2 = 0; ncnt2 < nelem; ncnt2++) { - hold_elem[ncnt2] = hold_elem[pt_list[ncnt2]]; - } - } } } else if (!((etype == BAR2 || etype == SHELL2) && side_nodes[0] == side_nodes[1])) {