diff --git a/external/rummy b/external/rummy index 6734a06292b70..212d34dab0e85 160000 --- a/external/rummy +++ b/external/rummy @@ -1 +1 @@ -Subproject commit 6734a06292b70fc2cbb756e66a073fc7c060013c +Subproject commit 212d34dab0e85ffaf5c9b8f695fae681ffd87483 diff --git a/src/outputs/output_parameters.hpp b/src/outputs/output_parameters.hpp index fad0f40fe547c..cb93d20893a7c 100644 --- a/src/outputs/output_parameters.hpp +++ b/src/outputs/output_parameters.hpp @@ -40,6 +40,7 @@ struct OutputParameters { int block_number = 0; std::string block_name; + std::string state_key; std::string file_basename; int file_number_width; bool file_label_final; diff --git a/src/outputs/outputs.cpp b/src/outputs/outputs.cpp index 1d35462dab64a..5bf5f3239f37d 100644 --- a/src/outputs/outputs.cpp +++ b/src/outputs/outputs.cpp @@ -84,6 +84,21 @@ namespace parthenon { +namespace { +int IsLegacyOutputBlock(const std::string &block_name) { + constexpr const char *prefix = "parthenon/output"; + if (block_name.rfind(prefix, 0) != 0) return false; + const std::string suffix = block_name.substr(std::char_traits::length(prefix)); + const bool is_legacy = !suffix.empty() && + std::all_of(suffix.begin(), suffix.end(), + [](unsigned char c) { return std::isdigit(c) != 0; }); + if (is_legacy) { + return std::atoi(suffix.c_str()); + } + return -1; +} +} // namespace + //---------------------------------------------------------------------------------------- // OutputType constructor @@ -111,14 +126,21 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { // `pinput` again here as we're actually processing (potentially even modifying) // `pinput`. auto output_blocks = pin->GetBlockNamesWithPrefix("parthenon/output"); + int named_output_ordinal = 0; for (const auto &block_name : output_blocks) { std::shared_ptr pnew_type; // the new output we will create bool restart = false; // we track restart outputs separately so we // need this temp variable to check OutputParameters op; // define temporary OutputParameters struct op.block_name = block_name; - const auto outn_str = block_name.substr(16); // 16 because counting starts at 0! - op.block_number = atoi(outn_str.c_str()); + const auto slash = block_name.find_last_of('/'); + const auto outn_str = + (slash == std::string::npos) ? block_name : block_name.substr(slash + 1); + op.state_key = outn_str; + const int legacy_number = IsLegacyOutputBlock(block_name); + const bool legacy_output = (legacy_number >= 0); + op.block_number = + legacy_output ? legacy_number : named_output_ordinal++; auto *pfile_number = pkg->MutableParam(outn_str + "/file_number"); auto *plast_time = pkg->MutableParam(outn_str + "/last_time"); auto *plast_n = pkg->MutableParam(outn_str + "/last_n"); @@ -215,10 +237,9 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { op.include_in_final = pin->GetOrAddBoolean(op.block_name, "include_in_final", true, "include output when triggered on final signal"); - char define_id[10]; - std::snprintf(define_id, sizeof(define_id), "out%d", - op.block_number); // default id="outN" - op.file_id = pin->GetOrAddString(op.block_name, "id", define_id); + const std::string default_id = + legacy_output ? "out" + std::to_string(op.block_number) : op.state_key; + op.file_id = pin->GetOrAddString(op.block_name, "id", default_id); op.file_type = pin->GetString(op.block_name, "file_type", "output type"); // read ghost cell option @@ -547,10 +568,9 @@ void Outputs::MakeOutputs(Mesh *pm, ParameterInput *pin, SimTime *tm, void OutputType::UpdateNextOutput_(Mesh *pm, SimTime *tm) { output_params.file_number++; auto pkg = pm->packages.Get("Outputs"); - const auto outn_str = std::to_string(output_params.block_number); - auto *pfile_number = pkg->MutableParam(outn_str + "/file_number"); - auto *plast_time = pkg->MutableParam(outn_str + "/last_time"); - auto *plast_n = pkg->MutableParam(outn_str + "/last_n"); + auto *pfile_number = pkg->MutableParam(output_params.state_key + "/file_number"); + auto *plast_time = pkg->MutableParam(output_params.state_key + "/last_time"); + auto *plast_n = pkg->MutableParam(output_params.state_key + "/last_n"); *pfile_number = output_params.file_number; if (tm != nullptr) { // JMM: Do NOT use the current time to update these, as that can diff --git a/src/outputs/outputs_package.cpp b/src/outputs/outputs_package.cpp index e24c36e3e809f..192122ef84e87 100644 --- a/src/outputs/outputs_package.cpp +++ b/src/outputs/outputs_package.cpp @@ -42,7 +42,9 @@ std::shared_ptr Initialize(ParameterInput *pin) { // from restart files or are cleanly initialized). auto output_blocks = pin->GetBlockNamesWithPrefix("parthenon/output"); for (const auto &block_name : output_blocks) { - std::string outn = block_name.substr(16); // 16 because counting starts at 0! + const auto slash = block_name.find_last_of('/'); + const std::string outn = + (slash == std::string::npos) ? block_name : block_name.substr(slash + 1); // These will be updated later or restarted from int file_number = 0; diff --git a/src/outputs/parthenon_hdf5.cpp b/src/outputs/parthenon_hdf5.cpp index c553e6e2288dd..49160ac0627b6 100644 --- a/src/outputs/parthenon_hdf5.cpp +++ b/src/outputs/parthenon_hdf5.cpp @@ -45,6 +45,7 @@ #include "outputs/parthenon_xdmf.hpp" #include "outputs/restart.hpp" #include "pack/default_names.hpp" +#include "parameter_parsers/rummy_parser.hpp" #include "provenance.hpp" #include "utils/string_utils.hpp" @@ -137,14 +138,19 @@ void PHDF5Output::WriteOutputFileImpl(Mesh *pm, ParameterInput *pin, SimTime *tm Kokkos::Profiling::pushRegion("write Attributes"); { Kokkos::Profiling::pushRegion("write input"); - // write input key-value pairs - std::ostringstream oss; - pin->ParameterDump(oss); - - // Mesh information const H5G input_group = MakeGroup(file, "/Input"); - - HDF5WriteAttribute("File", oss.str().c_str(), input_group); + if (const auto *deck = pin->GetRummyDeck(); deck != nullptr) { + const auto state = MakeRummyRestartState(*pin, *deck); + HDF5WriteAttribute("File", state.source, input_group); + HDF5WriteAttribute("InputParser", "rummy", input_group); + HDF5WriteAttribute("RummyMode", state.mode, input_group); + HDF5WriteAttribute("RummyStateVersion", state.version, input_group); + HDF5WriteAttribute("RummyState", state.source, input_group); + } else { + std::ostringstream oss; + pin->ParameterDump(oss); + HDF5WriteAttribute("File", oss.str(), input_group); + } Kokkos::Profiling::popRegion(); // write input } // Input section diff --git a/src/outputs/parthenon_opmd.cpp b/src/outputs/parthenon_opmd.cpp index c70cfc647aac6..7ff09154d1937 100644 --- a/src/outputs/parthenon_opmd.cpp +++ b/src/outputs/parthenon_opmd.cpp @@ -56,6 +56,7 @@ #include "outputs/outputs.hpp" #include "outputs/parthenon_opmd.hpp" #include "pack/default_names.hpp" +#include "parameter_parsers/rummy_parser.hpp" #include "parthenon_array_generic.hpp" #include "provenance.hpp" #include "utils/error_checking.hpp" @@ -507,10 +508,18 @@ void OpenPMDOutput::WriteOutputFileImpl(Mesh *pm, ParameterInput *pin, SimTime * // Then our own if (!is_slice) { PARTHENON_INSTRUMENT_REGION("write input"); - // write input key-value pairs - std::ostringstream oss; - pin->ParameterDump(oss); - it.setAttribute("InputFile", oss.str()); + if (const auto *deck = pin->GetRummyDeck(); deck != nullptr) { + const auto state = MakeRummyRestartState(*pin, *deck); + it.setAttribute("InputFile", state.source); + it.setAttribute("InputParser", std::string("rummy")); + it.setAttribute("RummyMode", state.mode); + it.setAttribute("RummyStateVersion", state.version); + it.setAttribute("RummyState", state.source); + } else { + std::ostringstream oss; + pin->ParameterDump(oss); + it.setAttribute("InputFile", oss.str()); + } } if (!is_slice) { diff --git a/src/outputs/restart.hpp b/src/outputs/restart.hpp index e61cfe2dd6d44..8f977d49e9074 100644 --- a/src/outputs/restart.hpp +++ b/src/outputs/restart.hpp @@ -98,6 +98,14 @@ class RestartReader { [[nodiscard]] virtual std::string GetInputString() const = 0; + struct RummyInputState { + bool present = false; + int version = 0; + std::string mode; + std::string source; + }; + [[nodiscard]] virtual RummyInputState GetRummyInputState() const = 0; + // Return output format version number. Return -1 if not existent. [[nodiscard]] virtual int GetOutputFormatVersion() const = 0; diff --git a/src/outputs/restart_hdf5.cpp b/src/outputs/restart_hdf5.cpp index 67780f54fd756..19970cfb19aa7 100644 --- a/src/outputs/restart_hdf5.cpp +++ b/src/outputs/restart_hdf5.cpp @@ -84,6 +84,23 @@ int RestartReaderHDF5::GetOutputFormatVersion() const { #endif // ENABLE_HDF5 } +RestartReader::RummyInputState RestartReaderHDF5::GetRummyInputState() const { +#ifndef ENABLE_HDF5 + PARTHENON_FAIL("Restart functionality is not available because HDF5 is disabled"); +#else + RummyInputState state; + const H5O input = H5O::FromHIDCheck(H5Oopen(fh_, "Input", H5P_DEFAULT)); + auto status = PARTHENON_HDF5_CHECK(H5Aexists(input, "InputParser")); + if (status <= 0 || GetAttr("Input", "InputParser") != "rummy") + return state; + state.present = true; + state.version = GetAttr("Input", "RummyStateVersion"); + state.mode = GetAttr("Input", "RummyMode"); + state.source = GetAttr("Input", "RummyState"); + return state; +#endif +} + RestartReaderHDF5::SparseInfo RestartReaderHDF5::GetSparseInfo() const { #ifndef ENABLE_HDF5 PARTHENON_FAIL("Restart functionality is not available because HDF5 is disabled"); diff --git a/src/outputs/restart_hdf5.hpp b/src/outputs/restart_hdf5.hpp index fe14cacb152f5..13d95e203c9af 100644 --- a/src/outputs/restart_hdf5.hpp +++ b/src/outputs/restart_hdf5.hpp @@ -55,6 +55,7 @@ class RestartReaderHDF5 : public RestartReader { [[nodiscard]] std::string GetInputString() const override { return GetAttr("Input", "File"); }; + [[nodiscard]] RummyInputState GetRummyInputState() const override; // Return output format version number. Return -1 if not existent. [[nodiscard]] int GetOutputFormatVersion() const override; diff --git a/src/outputs/restart_opmd.hpp b/src/outputs/restart_opmd.hpp index 953c440e9d70f..a21588d700c36 100644 --- a/src/outputs/restart_opmd.hpp +++ b/src/outputs/restart_opmd.hpp @@ -41,6 +41,17 @@ class RestartReaderOPMD : public RestartReader { [[nodiscard]] std::string GetInputString() const override { return it->getAttribute("InputFile").get(); }; + [[nodiscard]] RummyInputState GetRummyInputState() const override { + RummyInputState state; + if (!it->containsAttribute("InputParser") || + it->getAttribute("InputParser").get() != "rummy") + return state; + state.present = true; + state.version = it->getAttribute("RummyStateVersion").get(); + state.mode = it->getAttribute("RummyMode").get(); + state.source = it->getAttribute("RummyState").get(); + return state; + } // Return output format version number. Return -1 if not existent. [[nodiscard]] int GetOutputFormatVersion() const override; diff --git a/src/parameter_input.cpp b/src/parameter_input.cpp index 39bb3fd03d8be..12213bf67d09e 100644 --- a/src/parameter_input.cpp +++ b/src/parameter_input.cpp @@ -56,7 +56,6 @@ #include #include #include -#include #include #include #include @@ -128,9 +127,9 @@ void ParameterInput::LoadFromStream(std::istream &is) { [](char c) { return std::isspace(c) && c != ' '; }), line.end()); - if (line.empty()) continue; // skip blank line - first_char = line.find_first_not_of(" "); // skip white space - if (first_char == std::string::npos) continue; // line is all white space + if (line.empty()) continue; // skip blank line + first_char = line.find_first_not_of(" "); // skip white space + if (first_char == std::string::npos) continue; // line is all white space if (line.compare(first_char, 1, "#") == 0) continue; // skip comments if (line.compare(first_char, 9, "") == 0) break; // stop on @@ -278,8 +277,7 @@ Block *ParameterInput::FindOrAddBlock_(const std::string &name) { // Not found - create new block in vector and index it size_t new_idx = param_storage_.size(); - param_storage_.emplace_back( - Block{name, {}, {}}); // name, params vector, param_index map + param_storage_.emplace_back(Block{name, {}, {}, {}, {}}); block_index_[name] = new_idx; // Index it return ¶m_storage_[new_idx]; } @@ -832,6 +830,21 @@ std::string Parameter::ToString() const { if (std::holds_alternative(value)) { ss << std::get(value).value; + } else if (std::holds_alternative(value)) { + const auto &vec = std::get(value).values; + for (size_t i = 0; i < vec.size(); ++i) { + if (i > 0) ss << ", "; + std::visit( + [&](const auto &element) { + using Element = std::decay_t; + if constexpr (std::is_same_v) { + ss << element.value; + } else { + ss << element; + } + }, + vec[i]); + } } else if (std::holds_alternative(value)) { ss << std::get(value); } else if (std::holds_alternative(value)) { @@ -912,6 +925,18 @@ void ParameterInput::AddParsedParameter(const std::string &block, const std::str AddParameter_(block, name, value, comment); } +void ParameterInput::AddParsedBlock(const std::string &block, + const std::string &class_name, + const std::string &instance_name, + const std::string &canonical_path) { + PARTHENON_REQUIRE_THROWS(!parsing_finalized_, + "Can't add new blocks after parsing is resolved."); + auto *parsed_block = FindOrAddBlock_(block); + if (!class_name.empty()) parsed_block->class_name = class_name; + if (!instance_name.empty()) parsed_block->instance_name = instance_name; + if (!canonical_path.empty()) parsed_block->canonical_path = canonical_path; +} + //---------------------------------------------------------------------------------------- //! \fn void ParameterInput::FinalizeParsing() // \brief Finalize the parsing phase - no more parsing allowed (GetOrAdd/Set still work) @@ -940,7 +965,8 @@ ParameterInput::GetBlockNamesWithPrefix(const std::string &prefix) const { std::vector matching_blocks; for (const auto &block : param_storage_) { - if (block.name.compare(0, prefix.length(), prefix) == 0) { + if ((block.canonical_path == prefix) || + (block.name.compare(0, prefix.length(), prefix) == 0)) { matching_blocks.push_back(block.name); } } @@ -948,6 +974,34 @@ ParameterInput::GetBlockNamesWithPrefix(const std::string &prefix) const { return matching_blocks; } +//---------------------------------------------------------------------------------------- +//! \fn std::vector ParameterInput::GetBlocksOfClass() +// \brief Return all block names whose rummy class metadata equals `class_name`. + +std::vector +ParameterInput::GetBlocksOfClass(const std::string &class_name) const { + std::vector matching_blocks; + for (const auto &block : param_storage_) { + if (block.class_name == class_name) matching_blocks.push_back(block.name); + } + return matching_blocks; +} + +//---------------------------------------------------------------------------------------- +//! \fn void ParameterInput::SetBlockClassMetadata() +// \brief Record the rummy class/instance metadata for an existing block. + +void ParameterInput::SetBlockClassMetadata(const std::string &block, + const std::string &class_name, + const std::string &instance_name, + const std::string &canonical_path) { + auto it = block_index_.find(block); + if (it == block_index_.end()) return; + param_storage_[it->second].class_name = class_name; + param_storage_[it->second].instance_name = instance_name; + if (!canonical_path.empty()) param_storage_[it->second].canonical_path = canonical_path; +} + //---------------------------------------------------------------------------------------- //! \fn std::vector ParameterInput::GetParameterNames() // \brief Return all parameter names in the given block @@ -1003,9 +1057,12 @@ std::optional ParameterInput::GetFromStorage_(const std::string &block, return std::nullopt; // Not in storage } - // If it's an UnresolvedString, convert and cache - if (std::holds_alternative(param->value)) { - if (!param->original_string.has_value()) { + // Parser-preserved unresolved values convert on first use and then cache + // the requested concrete type. + if (std::holds_alternative(param->value) || + std::holds_alternative(param->value)) { + if (std::holds_alternative(param->value) && + !param->original_string.has_value()) { param->original_string = std::get(param->value); } T typed_val = ConvertParamValue(param->value, block, name); @@ -1077,19 +1134,43 @@ T ParameterInput::ConvertParamValue(const ParamValue &value, const std::string & return std::get(value); } + constexpr bool is_vector_type = + std::is_same_v> || std::is_same_v> || + std::is_same_v> || std::is_same_v>; + + if (std::holds_alternative(value)) { + if constexpr (is_vector_type) { + using ElemType = typename T::value_type; + T result; + for (const auto &element : std::get(value).values) { + ParamValue scalar = + std::visit([](const auto &item) -> ParamValue { return item; }, element); + result.push_back(ConvertParamValue(scalar, block, name)); + } + return result; + } + } + // If it's an unresolved string, convert it if (std::holds_alternative(value)) { const std::string &str_val = std::get(value).value; - constexpr bool is_vector_type = std::is_same_v> || - std::is_same_v> || - std::is_same_v> || - std::is_same_v>; - if constexpr (std::is_same_v) { - return stoi(str_val); + const std::string trimmed = SanitizeString(str_val); + std::size_t pos = 0; + int parsed = std::stoi(trimmed, &pos); + if (pos != trimmed.size()) { + Real d_parsed = static_cast(std::stod(trimmed, &pos)); + if (pos != trimmed.size()) throw std::invalid_argument("trailing characters"); + if ( static_cast(parsed) != d_parsed) throw std::invalid_argument("Integer type parameter is not parsing correctly from the string value"); + } + return parsed; } else if constexpr (std::is_same_v) { - return static_cast(atof(str_val.c_str())); + const std::string trimmed = SanitizeString(str_val); + std::size_t pos = 0; + Real parsed = static_cast(std::stod(trimmed, &pos)); + if (pos != trimmed.size()) throw std::invalid_argument("trailing characters"); + return parsed; } else if constexpr (std::is_same_v) { return stob(str_val); } else if constexpr (std::is_same_v) { @@ -1100,15 +1181,8 @@ T ParameterInput::ConvertParamValue(const ParamValue &value, const std::string & T result; for (const auto &field : fields) { - if constexpr (std::is_same_v) { - result.push_back(stoi(field)); - } else if constexpr (std::is_same_v) { - result.push_back(static_cast(atof(field.c_str()))); - } else if constexpr (std::is_same_v) { - result.push_back(stob(field)); - } else if constexpr (std::is_same_v) { - result.push_back(field); - } + result.push_back(ConvertParamValue(ParamValue(UnresolvedString(field)), + block, name)); } return result; } diff --git a/src/parameter_input.hpp b/src/parameter_input.hpp index a1c7d4e3ef91f..2da8dacd4df50 100644 --- a/src/parameter_input.hpp +++ b/src/parameter_input.hpp @@ -36,6 +36,7 @@ #include #include #include // for std::forward, std::pair +#include #include #include "config.hpp" @@ -48,6 +49,10 @@ #include "utils/type_list.hpp" #include "utils/utils.hpp" +namespace Rummy { +class DeckBase; +} + namespace parthenon { std::string SanitizeString(const std::string &input); @@ -166,9 +171,17 @@ struct UnresolvedString { explicit UnresolvedString(std::string &&v) : value(std::move(v)) {} }; -// Build ParamValue variant from SupportedParamTypes + UnresolvedString -using ParamValue = - type_list_to_variant_t>; +// A parser-provided vector whose elements retain their original scalar kind. +// This avoids treating commas in string elements as separators while deferring +// conversion until the consumer requests a concrete vector type. +using UnresolvedScalar = std::variant; +struct UnresolvedVector { + std::vector values; +}; + +// Build ParamValue variant from SupportedParamTypes + parser-preserved values. +using ParamValue = type_list_to_variant_t, 0>>; // This can be used to tell the params infrastructure that the default // value of one parameter depends on another one @@ -214,6 +227,13 @@ struct Parameter { struct Block { std::string name; + // Optional metadata describing the rummy class backing this block. Only + // populated by the FullDeck parser (e.g. a `` + // header records class_name="output", instance_name="output1"). Empty + // for the SimpleDeck parser and the legacy text parser. + std::string class_name; + std::string instance_name; + std::string canonical_path; std::vector params; // Ordered storage (for iteration) std::unordered_map param_index; // Fast lookup within block (stores indices) @@ -251,6 +271,9 @@ class ParameterInput { void AddParsedParameter(const std::string &block, const std::string &name, const ParamValue &value, const std::string &comment = "# From parser"); + void AddParsedBlock(const std::string &block, const std::string &class_name = "", + const std::string &instance_name = "", + const std::string &canonical_path = ""); // Finalize the parsing phase - no more parsing allowed (but GetOrAdd/Set still work) void FinalizeParsing(); @@ -258,9 +281,24 @@ class ParameterInput { // === QUERY INTERFACE (parser-agnostic) === std::vector GetBlockNames() const; std::vector GetBlockNamesWithPrefix(const std::string &prefix) const; + // Return the names of all blocks whose rummy class metadata matches + // `class_name`. Only meaningful for inputs parsed by the FullDeck parser; + // returns an empty vector for SimpleDeck/legacy inputs. + std::vector GetBlocksOfClass(const std::string &class_name) const; std::vector GetParameterNames(const std::string &block) const; + // Set the rummy class/instance metadata for a parsed block. Used by the + // rummy parser when populating from a FullDeck; ignored for callers that + // do not need class introspection. + void SetBlockClassMetadata(const std::string &block, const std::string &class_name, + const std::string &instance_name, + const std::string &canonical_path = ""); + void ParameterDump(std::ostream &os); + // Non-owning link to the input deck retained by ParthenonManager. Output + // writers use it to generate current Rummy restart state. + void SetRummyDeck(Rummy::DeckBase *deck) { rummy_deck_ = deck; } + Rummy::DeckBase *GetRummyDeck() const { return rummy_deck_; } // TODO(JMM): Make this more general? void OutputParameterTable(std::ostream &os, const std::regex &block_regex = std::regex("(.*)")) const; @@ -473,6 +511,7 @@ class ParameterInput { std::unordered_map block_index_; // Fast O(1) block lookup (stores indices) bool parsing_finalized_ = false; // Track if parsing phase is complete + Rummy::DeckBase *rummy_deck_ = nullptr; std::string last_filename_; // last input file opened, to prevent duplicate reads // We will want to iterate through the record in lexicographic diff --git a/src/parameter_parsers/rummy_parser.cpp b/src/parameter_parsers/rummy_parser.cpp index 16200a628559e..4328e8960ba76 100644 --- a/src/parameter_parsers/rummy_parser.cpp +++ b/src/parameter_parsers/rummy_parser.cpp @@ -13,6 +13,9 @@ // This file was made in part with generative AI. #include +#include +#include +#include #include #include #include @@ -21,13 +24,26 @@ #include #include -#include +#include +#include #include "parameter_input.hpp" #include "rummy_parser.hpp" namespace parthenon { +InputDeckType ToInputDeckType(RummyMode mode) { + switch (mode) { + case RummyMode::Simple: + return InputDeckType::RummySimple; + case RummyMode::FullLoose: + return InputDeckType::RummyFullLoose; + case RummyMode::FullStrict: + return InputDeckType::RummyFullStrict; + } + PARTHENON_FAIL("Unknown RummyMode"); +} + //! \fn ParameterInput::ParamValue RummyCardToParamValue(const Rummy::Card &card) // \brief Convert a Rummy Card to a ParameterInput::ParamValue for storage in // ParameterInput. @@ -42,6 +58,35 @@ ParamValue RummyCardToParamValue(const Rummy::Card &card) { } } +UnresolvedScalar RummyCardToUnresolvedScalar(const Rummy::Card &card) { + if (card.isBool()) return card.Get(); + if (card.isString()) return card.Get(); + return UnresolvedString(card.GetString(std::numeric_limits::max_digits10)); +} + +UnresolvedVector RummyVectorToParamValue(const Rummy::DeckBase &deck, + const std::string &suit, + const std::string &name) { + UnresolvedVector result; + const auto &cards = deck.GetSuit(suit); + auto direct = cards.find(name); + if (direct != cards.end() && + direct->second.GetValue().type == pips::ValueType::VECTOR && + direct->second.GetValue().as.vector != nullptr) { + for (const auto &element : direct->second.GetValue().as.vector->elements) { + result.values.emplace_back(RummyCardToUnresolvedScalar(Rummy::Card("", name, element, ""))); + } + return result; + } + for (std::size_t i = 0;; ++i) { + const std::string indexed = name + "[" + std::to_string(i) + "]"; + auto it = cards.find(indexed); + if (it == cards.end()) break; + result.values.emplace_back(RummyCardToUnresolvedScalar(it->second)); + } + return result; +} + //! \fn Rummy::Card ParamValueToRummyCard(suit, name, v) // \brief Convert a scalar ParamValue to a Rummy::Card. Rummy::Card ParamValueToRummyCard(const std::string &suit, const std::string &name, @@ -66,85 +111,231 @@ Rummy::Card ParamValueToRummyCard(const std::string &suit, const std::string &na try { std::size_t pos; double d = std::stod(trimmed, &pos); - return Rummy::Card(suit, name, d, ""); + if (pos == trimmed.size()) return Rummy::Card(suit, name, d, ""); } catch (...) { } return Rummy::Card(suit, name, trimmed, ""); } -void LoadParameterFromRummy(ParameterInput &pin, std::istream &ss, const bool sync) { - Rummy::Deck deck; +namespace { + +std::unique_ptr MakeDeck(InputDeckType /*deck_type*/, + std::istream &schema_stream) { + std::string schema_text((std::istreambuf_iterator(schema_stream)), + std::istreambuf_iterator()); + auto schema = Rummy::Schema::FromString(schema_text); + return std::make_unique(Rummy::FullDeck::Mode::Strict, + std::move(schema)); +} + +std::unique_ptr MakeRestartDeck(InputDeckType deck_type) { + switch (deck_type) { + case InputDeckType::RummyFullLoose: + return std::make_unique(Rummy::FullDeck::Mode::Loose); + case InputDeckType::RummyFullStrict: + case InputDeckType::RummyFullSchema: + // Schema-generated declarations are embedded in the restart source. + return std::make_unique(Rummy::FullDeck::Mode::Strict); + case InputDeckType::RummySimple: + return std::make_unique(); + default: + PARTHENON_FAIL("A Rummy restart requires a Rummy input deck type"); + } +} + +std::vector +MakeSources(const std::string *restart_source, const std::vector &files, + const std::vector &mods) { + std::vector sources; + if (restart_source != nullptr) + sources.push_back({"", *restart_source, ""}); + for (const auto &file : files) { + std::ifstream input_file(file); + if (!input_file.is_open()) { + std::stringstream msg; + msg << "Could not open file '" << file << "'"; + PARTHENON_FAIL(msg); + } + std::stringstream contents; + contents << input_file.rdbuf(); + sources.push_back( + {file, contents.str(), std::filesystem::path(file).parent_path().string()}); + } + if (!mods.empty()) { + std::stringstream contents; + for (const auto &mod : mods) contents << mod << " # From command line\n"; + sources.push_back({"", contents.str(), ""}); + } + return sources; +} + +// Construct a deck of the requested type +std::unique_ptr MakeDeck(InputDeckType deck_type, + const std::string &schema_path = "") { + switch (deck_type) { + case InputDeckType::RummyFullLoose: + return std::make_unique(Rummy::FullDeck::Mode::Loose); + case InputDeckType::RummyFullStrict: { + if (schema_path.empty()) { + PARTHENON_FAIL("InputDeckType::RummyFullStrict requires a non-empty schema_path"); + } + std::ifstream f(schema_path); + if (!f.is_open()) { + std::stringstream msg; + msg << "Could not open schema file '" << schema_path << "'"; + PARTHENON_FAIL(msg); + } + return MakeDeck(deck_type, static_cast(f)); + } + case InputDeckType::RummyFullSchema: + PARTHENON_FAIL("InputDeckType::RummyFullSchema is unsupported. Use " + "InputDeckOptions{..., RummyMode::FullStrict, schema_path}."); + case InputDeckType::RummySimple: + default: + return std::make_unique(); + } +} + +} // namespace + +std::unique_ptr +LoadParameterFromRummy(ParameterInput &pin, std::istream &ss, const bool sync, + InputDeckType deck_type, const std::string &schema_path) { + auto deck = MakeDeck(deck_type, schema_path); if (sync) { - SyncDeckFromStorage(pin, deck); + SyncDeckFromStorage(pin, *deck); } - deck.Build(ss); - AddRummyParameters(pin, deck); + deck->Build(ss); + AddRummyParameters(pin, *deck); + return deck; } -void LoadParameterFromRummy(ParameterInput &pin, const std::vector &files, - const std::vector &mods, const bool is_restart) { - Rummy::Deck deck; +std::unique_ptr +LoadParameterFromRummy(ParameterInput &pin, const std::vector &files, + const std::vector &mods, const bool is_restart, + InputDeckType deck_type, const std::string &schema_path) { + auto deck = MakeDeck(deck_type, schema_path); const bool no_inputs = files.empty() && mods.empty(); if (no_inputs) { - return; + return deck; } if (is_restart) { // If this is a restart, we need to sync the deck with the existing parameters - SyncDeckFromStorage(pin, deck); + SyncDeckFromStorage(pin, *deck); } - // concatenate all input files and mods into a single stream for parsing - std::stringstream contents; - for (const auto &file : files) { - std::ifstream input_file(file); - if (input_file.is_open()) { - contents << input_file.rdbuf() << "\n"; - } else { - std::stringstream msg; - msg << "Could not open file '" << file << "'"; - PARTHENON_FAIL(msg); - } + auto sources = MakeSources(nullptr, files, mods); + deck->BuildSources(sources); + AddRummyParameters(pin, *deck); + return deck; +} + +std::unique_ptr +LoadParameterFromRummy(ParameterInput &pin, std::istream &ss, const bool sync, + InputDeckType deck_type, std::istream &schema_stream) { + auto deck = MakeDeck(deck_type, schema_stream); + if (sync) { + SyncDeckFromStorage(pin, *deck); } - for (const auto &mod : mods) { - contents << mod << " # From command line\n"; + deck->Build(ss); + AddRummyParameters(pin, *deck); + return deck; +} + +std::unique_ptr +LoadParameterFromRummy(ParameterInput &pin, const std::vector &files, + const std::vector &mods, const bool is_restart, + InputDeckType deck_type, std::istream &schema_stream) { + auto deck = MakeDeck(deck_type, schema_stream); + + const bool no_inputs = files.empty() && mods.empty(); + if (no_inputs) { + return deck; + } + + if (is_restart) { + SyncDeckFromStorage(pin, *deck); } - deck.Build(contents); + auto sources = MakeSources(nullptr, files, mods); + deck->BuildSources(sources); + AddRummyParameters(pin, *deck); + return deck; +} + +std::unique_ptr +LoadParameterFromRummyRestart(ParameterInput &pin, const std::string &restart_source, + const std::vector &files, + const std::vector &mods, + InputDeckType deck_type) { + auto deck = MakeRestartDeck(deck_type); + auto sources = MakeSources(&restart_source, files, mods); + deck->BuildSources(sources); + AddRummyParameters(pin, *deck); + return deck; +} + +InputDeckType RummyRestartModeToDeckType(const std::string &mode) { + if (mode == "simple") return InputDeckType::RummySimple; + if (mode == "full-loose") return InputDeckType::RummyFullLoose; + if (mode == "full-strict") return InputDeckType::RummyFullStrict; + PARTHENON_FAIL("Unsupported Rummy restart mode '" + mode + "'"); +} - AddRummyParameters(pin, deck); +RummyRestartState MakeRummyRestartState(const ParameterInput &pin, + const Rummy::DeckBase &deck) { + RummyRestartState state; + std::ostringstream source; + if (const auto *full = dynamic_cast(&deck); full != nullptr) { + auto snapshot = *full; + SyncDeckFromStorage(pin, snapshot); + snapshot.SaveRestartState(source); + state.mode = full->GetMode() == Rummy::FullDeck::Mode::Strict ? "full-strict" + : "full-loose"; + } else if (const auto *simple = dynamic_cast(&deck); + simple != nullptr) { + auto snapshot = *simple; + SyncDeckFromStorage(pin, snapshot); + snapshot.SaveRestartState(source); + state.mode = "simple"; + } else { + PARTHENON_FAIL("Unsupported Rummy deck implementation in restart output"); + } + state.source = source.str(); + return state; } -void AddRummyParameters(ParameterInput &pin, Rummy::Deck &deck) { - static const std::regex kVectorCardPattern(R"(^(.+)\[(\d+)\]$)"); +void AddRummyParameters(ParameterInput &pin, Rummy::DeckBase &deck) { + // If the deck is a FullDeck, capture per-suit class metadata so callers can + // query blocks by their pips class via ParameterInput::GetBlocksOfClass. + auto *full_deck = dynamic_cast(&deck); for (const auto &suit_name : deck.GetSuitsInOrder()) { const std::string &block_name = suit_name; + std::string class_name; + std::string canonical_path; + if (full_deck != nullptr) { + class_name = full_deck->GetClassName(suit_name); + canonical_path = full_deck->GetCanonicalPath(suit_name); + } + std::string instance_name = suit_name; + const auto last_slash = suit_name.find_last_of('/'); + if (last_slash != std::string::npos) instance_name = suit_name.substr(last_slash + 1); + pin.AddParsedBlock(block_name, class_name, instance_name, canonical_path); const auto &suit_cards = deck.GetCardsInOrder(suit_name); for (const auto &card_name : suit_cards) { // match for vector if (deck.IsCardVector(suit_name, card_name)) { - std::vector comments; - auto elements = deck.GetVector(suit_name, card_name, comments); - std::string joined; - std::string joined_comments; - for (std::size_t i = 0; i < elements.size(); ++i) { - if (comments[i] != "") { - if (i > 0) { - joined_comments += " "; - } - joined_comments += comments[i]; - } - if (i > 0) { - joined += ","; - } - joined += elements[i]; - } - // Rummy stores comments without '#' std::string comment; - if (!joined_comments.empty()) comment = "# " + joined_comments; - pin.AddParsedParameter(block_name, card_name, UnresolvedString(joined), comment); + const auto &cards = deck.GetSuit(suit_name); + auto first = cards.find(card_name + "[0]"); + if (first == cards.end()) first = cards.find(card_name); + if (first != cards.end() && !first->second.GetComment().empty()) + comment = "# " + first->second.GetComment(); + pin.AddParsedParameter(block_name, card_name, + RummyVectorToParamValue(deck, suit_name, card_name), comment); } else { auto &card = deck.GetCard(suit_name, card_name); std::string comment; @@ -202,12 +393,24 @@ bool IsRummyFormat(std::istream &is, const bool command_line) { } if (line.compare(first_char, 1, "<") == 0) { - if (line.size() > first_char + 2 && line.compare(first_char + 1, 2, "..") == 0) { + const auto close = line.find('>', first_char + 1); + const std::string header = line.substr(first_char + 1, + close == std::string::npos + ? std::string::npos + : close - first_char - 1); + if (header.rfind("./", 0) == 0 || header.rfind("../", 0) == 0 || + header.find('(') != std::string::npos) { return restore_and_return(true); } found_block = true; continue; } + const auto token_end = line.find_first_of(" \t{"); + const std::string token = line.substr(first_char, token_end - first_char); + if (token == "include" || token == "setattr" || token == "for" || token == "while" || + token == "if" || token == "fn" || token == "class") { + return restore_and_return(true); + } // Non-comment, non-blank content before the first block = Rummy global variable // Disable for command line modifications @@ -253,7 +456,7 @@ bool IsRummyFormat(const std::string &filename) { //---------------------------------------------------------------------------------------- //! \fn void ParameterInput::SyncDeckFromStorage() // \brief Seed the Rummy Deck from the current param_storage_ contents. -void SyncDeckFromStorage(ParameterInput &pin, Rummy::Deck &deck) { +void SyncDeckFromStorage(const ParameterInput &pin, Rummy::DeckBase &deck) { std::map> new_cards; std::vector new_suits; std::map> new_card_map; @@ -269,6 +472,14 @@ void SyncDeckFromStorage(ParameterInput &pin, Rummy::Deck &deck) { new_cards[suit][card_name] = std::move(card); }; + auto register_suit = [&](const std::string &suit) { + if (new_cards.find(suit) == new_cards.end()) { + new_cards[suit] = {}; + new_suits.push_back(suit); + new_card_map[suit] = {}; + } + }; + for (const auto &block : pin.GetBlocks()) { // Collapse the block name into a Rummy suit: non-empty '/' segments joined by '/'. // A block that is only "/" (global scope) maps to suit "/". @@ -286,6 +497,8 @@ void SyncDeckFromStorage(ParameterInput &pin, Rummy::Deck &deck) { if (!assembled.empty()) suit = assembled; } + register_suit(suit); + for (const auto ¶m : block.params) { // Vector variants expand to one card per element: name[0], name[1], ... if (std::holds_alternative>(param.value)) { @@ -312,6 +525,14 @@ void SyncDeckFromStorage(ParameterInput &pin, Rummy::Deck &deck) { std::string cn = param.name + "[" + std::to_string(i) + "]"; register_card(suit, cn, Rummy::Card(suit, cn, vec[i], "")); } + } else if (std::holds_alternative(param.value)) { + const auto &vec = std::get(param.value).values; + for (size_t i = 0; i < vec.size(); ++i) { + std::string cn = param.name + "[" + std::to_string(i) + "]"; + ParamValue element = std::visit( + [](const auto &item) -> ParamValue { return item; }, vec[i]); + register_card(suit, cn, ParamValueToRummyCard(suit, cn, element)); + } } else { register_card(suit, param.name, ParamValueToRummyCard(suit, param.name, param.value)); @@ -320,6 +541,14 @@ void SyncDeckFromStorage(ParameterInput &pin, Rummy::Deck &deck) { } deck.SeedGlobals(new_cards, new_suits, new_card_map); + if (auto *full_deck = dynamic_cast(&deck); full_deck != nullptr) { + for (const auto &block : pin.GetBlocks()) { + std::string suit = block.name; + while (!suit.empty() && suit.front() == '/') suit.erase(suit.begin()); + if (suit.empty()) suit = "/"; + full_deck->SeedSuitMetadata(suit, block.class_name, block.canonical_path); + } + } } } // namespace parthenon diff --git a/src/parameter_parsers/rummy_parser.hpp b/src/parameter_parsers/rummy_parser.hpp index 890b3f654a35d..7901130296168 100644 --- a/src/parameter_parsers/rummy_parser.hpp +++ b/src/parameter_parsers/rummy_parser.hpp @@ -15,22 +15,75 @@ #ifndef PARAMETER_PARSERS_RUMMY_PARSER_HPP_ #define PARAMETER_PARSERS_RUMMY_PARSER_HPP_ +#include #include #include #include #include "parameter_input.hpp" -// Foward declare Rummy::Deck to avoid including the full header in this file +// Forward declare Rummy deck classes to avoid including full headers here. namespace Rummy { -class Deck; -} +class DeckBase; +class SimpleDeck; +class FullDeck; +} // namespace Rummy namespace parthenon { -void LoadParameterFromRummy(ParameterInput &input, const std::vector &files, - const std::vector &mods, const bool is_restart); -void LoadParameterFromRummy(ParameterInput &pin, std::istream &ss, const bool sync); -void AddRummyParameters(ParameterInput &pin, Rummy::Deck &deck); -void SyncDeckFromStorage(ParameterInput &pin, Rummy::Deck &deck); +enum class InputDeckType { + Native = 0, + RummySimple = 1, + RummyFullLoose = 2, + RummyFullStrict = 3, + RummyFullSchema = 4, +}; + +enum class InputParserPolicy { Auto, NativeOnly, RummyOnly }; +enum class RummyMode { Simple, FullLoose, FullStrict }; + +// Explicit parser selection. Unlike the legacy InputDeckType API this keeps +// format detection separate from the Rummy implementation selected after a +// Rummy deck has been chosen. +struct InputDeckOptions { + InputParserPolicy parser = InputParserPolicy::Auto; + RummyMode rummy_mode = RummyMode::Simple; + std::string schema_path; +}; + +struct RummyRestartState { + static constexpr int VERSION = 1; + int version = VERSION; + std::string mode; + std::string source; +}; + +InputDeckType ToInputDeckType(RummyMode mode); + +std::unique_ptr +LoadParameterFromRummy(ParameterInput &input, const std::vector &files, + const std::vector &mods, const bool is_restart, + InputDeckType deck_type = InputDeckType::RummySimple, + const std::string &schema_path = ""); +std::unique_ptr +LoadParameterFromRummy(ParameterInput &input, const std::vector &files, + const std::vector &mods, const bool is_restart, + InputDeckType deck_type, std::istream &schema_stream); +std::unique_ptr +LoadParameterFromRummy(ParameterInput &pin, std::istream &ss, const bool sync, + InputDeckType deck_type = InputDeckType::RummySimple, + const std::string &schema_path = ""); +std::unique_ptr +LoadParameterFromRummy(ParameterInput &pin, std::istream &ss, const bool sync, + InputDeckType deck_type, std::istream &schema_stream); +std::unique_ptr +LoadParameterFromRummyRestart(ParameterInput &pin, const std::string &restart_source, + const std::vector &files, + const std::vector &mods, + InputDeckType deck_type); +RummyRestartState MakeRummyRestartState(const ParameterInput &pin, + const Rummy::DeckBase &deck); +InputDeckType RummyRestartModeToDeckType(const std::string &mode); +void AddRummyParameters(ParameterInput &pin, Rummy::DeckBase &deck); +void SyncDeckFromStorage(const ParameterInput &pin, Rummy::DeckBase &deck); bool IsRummyFormat(const std::string &filename); bool IsRummyFormat(std::istream &is, const bool command_line); } // namespace parthenon diff --git a/src/parthenon_manager.cpp b/src/parthenon_manager.cpp index f77fc8b3d2d0b..e35dd1d26ab49 100644 --- a/src/parthenon_manager.cpp +++ b/src/parthenon_manager.cpp @@ -18,6 +18,9 @@ #include "parthenon_manager.hpp" +#include +#include + #include #include #include @@ -53,7 +56,39 @@ namespace fs = FS_NAMESPACE; namespace parthenon { -ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[]) { +ParthenonManager::ParthenonManager() { app_input = std::make_unique(); } + +ParthenonManager::~ParthenonManager() = default; + +Rummy::FullDeck *ParthenonManager::GetRummyFullDeck() const { + return dynamic_cast(input_deck.get()); +} + +ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[], + InputDeckType deck_type, + const std::string &schema_path) { + return ParthenonInitEnvCore_(argc, argv, deck_type, InputParserPolicy::Auto, + schema_path, nullptr); +} + +ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[], + InputDeckType deck_type, + std::istream &schema_stream) { + return ParthenonInitEnvCore_(argc, argv, deck_type, InputParserPolicy::Auto, "", + &schema_stream); +} + +ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[], + const InputDeckOptions &options) { + return ParthenonInitEnvCore_(argc, argv, ToInputDeckType(options.rummy_mode), + options.parser, options.schema_path, nullptr); +} + +ParthenonStatus ParthenonManager::ParthenonInitEnvCore_(int argc, char *argv[], + InputDeckType deck_type, + InputParserPolicy parser_policy, + const std::string &schema_path, + std::istream *schema_stream) { if (called_init_env_) { PARTHENON_THROW("ParthenonInitEnv called twice!"); } @@ -109,6 +144,7 @@ ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[]) { // Populate the ParameterInput object. // If restart, then ParameterInput in the restart file takes precedence. + RestartReader::RummyInputState rummy_restart; if (arg.is_restart) { // Read input from restart file if (fs::path(arg.restart_filename).extension() == ".rhdf") { @@ -132,28 +168,34 @@ ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[]) { PARTHENON_FAIL("Unsupported restart file format."); } - // Load input stream + rummy_restart = restartReader->GetRummyInputState(); pinput = std::make_unique(); - auto inputString = restartReader->GetInputString(); - std::istringstream is(inputString); - pinput->LoadFromStream(is); - } - // Determine what parser to use - bool is_rummy = false; - for (const auto &input_filename : arg.input_filenames) { - if (IsRummyFormat(input_filename)) { - is_rummy = true; - break; + if (!rummy_restart.present) { + auto inputString = restartReader->GetInputString(); + std::istringstream is(inputString); + pinput->LoadFromStream(is); } } - if (!is_rummy) { - for (const auto &mod : arg.modifiers) { - std::stringstream ss(mod); - if (IsRummyFormat(ss, true)) { + // Determine what parser to use + bool is_rummy = parser_policy == InputParserPolicy::RummyOnly; + if (parser_policy == InputParserPolicy::Auto) { + is_rummy = rummy_restart.present; + for (const auto &input_filename : arg.input_filenames) { + if (is_rummy) break; + if (IsRummyFormat(input_filename)) { is_rummy = true; break; } } + if (!is_rummy) { + for (const auto &mod : arg.modifiers) { + std::stringstream ss(mod); + if (IsRummyFormat(ss, true)) { + is_rummy = true; + break; + } + } + } } // read the parameters @@ -161,8 +203,24 @@ ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[]) { pinput = std::make_unique(); } if (is_rummy) { - LoadParameterFromRummy(*pinput, arg.input_filenames, arg.modifiers, arg.is_restart); + if (rummy_restart.present) { + PARTHENON_REQUIRE_THROWS( + rummy_restart.version == RummyRestartState::VERSION, + "Unsupported Rummy restart state version " + + std::to_string(rummy_restart.version)); + input_deck = LoadParameterFromRummyRestart( + *pinput, rummy_restart.source, arg.input_filenames, arg.modifiers, + RummyRestartModeToDeckType(rummy_restart.mode)); + } else if (schema_stream != nullptr) { + input_deck = LoadParameterFromRummy(*pinput, arg.input_filenames, arg.modifiers, + arg.is_restart, deck_type, *schema_stream); + } else { + input_deck = LoadParameterFromRummy(*pinput, arg.input_filenames, arg.modifiers, + arg.is_restart, deck_type, schema_path); + } + pinput->SetRummyDeck(input_deck.get()); } else { + input_deck.reset(); for (const auto &input_filename : arg.input_filenames) { pinput->ReadFile(input_filename); } diff --git a/src/parthenon_manager.hpp b/src/parthenon_manager.hpp index 34fe5458e710f..faf415f7abcb6 100644 --- a/src/parthenon_manager.hpp +++ b/src/parthenon_manager.hpp @@ -30,6 +30,7 @@ #include "mesh/mesh.hpp" #include "outputs/restart.hpp" #include "parameter_input.hpp" +#include "parameter_parsers/rummy_parser.hpp" #include "utils/error_checking.hpp" #include "utils/utils.hpp" @@ -39,8 +40,15 @@ enum class ParthenonStatus { ok, complete, error }; class ParthenonManager { public: - ParthenonManager() { app_input.reset(new ApplicationInput()); } - ParthenonStatus ParthenonInitEnv(int argc, char *argv[]); + ParthenonManager(); + ~ParthenonManager(); + ParthenonStatus ParthenonInitEnv(int argc, char *argv[], + InputDeckType deck_type = InputDeckType::Native, + const std::string &schema_path = ""); + ParthenonStatus ParthenonInitEnv(int argc, char *argv[], InputDeckType deck_type, + std::istream &schema_stream); + ParthenonStatus ParthenonInitEnv(int argc, char *argv[], + const InputDeckOptions &options); void ParthenonInitPackagesAndMesh(std::optional forest_def = {}); ParthenonStatus ParthenonFinalize(); @@ -56,12 +64,24 @@ class ParthenonManager { std::unique_ptr pmesh; std::unique_ptr restartReader; std::unique_ptr app_input; + // Retains full Rummy state (graphs and packed device functions) for the + // complete Parthenon lifetime. Null when the native parser is selected. + std::unique_ptr input_deck; + + Rummy::DeckBase *GetRummyDeck() const { return input_deck.get(); } + Rummy::FullDeck *GetRummyFullDeck() const; private: ArgParse arg; bool called_init_env_ = false; bool called_init_packages_and_mesh_ = false; + // Shared implementation for both ParthenonInitEnv overloads. + ParthenonStatus ParthenonInitEnvCore_(int argc, char *argv[], InputDeckType deck_type, + InputParserPolicy parser_policy, + const std::string &schema_path, + std::istream *schema_stream); + template void ReadSwarmVars_(const SP_Swarm &pswarm, const BlockList_t &block_list, const std::size_t count_on_rank, const std::size_t offset) {