From d32b2e2402e72294acae41c76fcc23b1b4592a53 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Mon, 30 Jun 2025 12:56:59 -0700 Subject: [PATCH 01/35] Update submodule t-route commit hash --- extern/t-route | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/t-route b/extern/t-route index f36280cb75..32a91742da 160000 --- a/extern/t-route +++ b/extern/t-route @@ -1 +1 @@ -Subproject commit f36280cb75449f2970d56d8a86eb2b149782aab9 +Subproject commit 32a91742da3cd4698bb73f706f9ebef1017422f8 From e1c657fb1bea0d4131a7f4f137d95dfa50a985f7 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Wed, 2 Jul 2025 21:28:21 -0700 Subject: [PATCH 02/35] Update submodules to release-candidate branch --- extern/t-route | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/t-route b/extern/t-route index 32a91742da..70cfc5654b 160000 --- a/extern/t-route +++ b/extern/t-route @@ -1 +1 @@ -Subproject commit 32a91742da3cd4698bb73f706f9ebef1017422f8 +Subproject commit 70cfc5654bef014479f8f8dcf5400988597f308e From 716f2b8ee5ce57942d125ad6dc83812317409715 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 9 Sep 2025 11:31:13 -0700 Subject: [PATCH 03/35] Update submodule references to release-candidate branches --- extern/LASAM | 2 +- extern/SoilFreezeThaw/SoilFreezeThaw | 2 +- extern/SoilMoistureProfiles/SoilMoistureProfiles | 2 +- extern/bmi-cxx | 2 +- extern/cfe/cfe | 2 +- extern/evapotranspiration/evapotranspiration | 2 +- extern/lstm | 2 +- extern/noah-owp-modular/noah-owp-modular | 2 +- extern/sac-sma/sac-sma | 2 +- extern/sloth | 2 +- extern/snow17 | 2 +- extern/t-route | 2 +- extern/topmodel/topmodel | 2 +- extern/ueb-bmi | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/extern/LASAM b/extern/LASAM index 889ebef625..7e5405866d 160000 --- a/extern/LASAM +++ b/extern/LASAM @@ -1 +1 @@ -Subproject commit 889ebef62577fcd8e03efb36b3c855e1ba0794fe +Subproject commit 7e5405866dd66747b0db7d0351f67ad3e5b22f12 diff --git a/extern/SoilFreezeThaw/SoilFreezeThaw b/extern/SoilFreezeThaw/SoilFreezeThaw index dd0a439c17..6ea238f346 160000 --- a/extern/SoilFreezeThaw/SoilFreezeThaw +++ b/extern/SoilFreezeThaw/SoilFreezeThaw @@ -1 +1 @@ -Subproject commit dd0a439c1765d7d6a2e743c491ce0b77dfb4aff2 +Subproject commit 6ea238f346c2f2bf2a7b3dba9bb0e65c75a63afa diff --git a/extern/SoilMoistureProfiles/SoilMoistureProfiles b/extern/SoilMoistureProfiles/SoilMoistureProfiles index 385189c0f8..aaa505dc39 160000 --- a/extern/SoilMoistureProfiles/SoilMoistureProfiles +++ b/extern/SoilMoistureProfiles/SoilMoistureProfiles @@ -1 +1 @@ -Subproject commit 385189c0f88ae5e71eabbc470130f449ed182d8b +Subproject commit aaa505dc39492cf755bb5792ba5762ae16d1b198 diff --git a/extern/bmi-cxx b/extern/bmi-cxx index 77aa8f0774..ca3bea02f9 160000 --- a/extern/bmi-cxx +++ b/extern/bmi-cxx @@ -1 +1 @@ -Subproject commit 77aa8f0774ea60ea96d0c834b14e82591a5b699c +Subproject commit ca3bea02f989e351ddd1700743105cd72983036b diff --git a/extern/cfe/cfe b/extern/cfe/cfe index 4717734939..6eea6d86bc 160000 --- a/extern/cfe/cfe +++ b/extern/cfe/cfe @@ -1 +1 @@ -Subproject commit 4717734939ec4dc547156448326a38f9ee7a3585 +Subproject commit 6eea6d86bcf47c26c89718fe23cf35aac04aaa6b diff --git a/extern/evapotranspiration/evapotranspiration b/extern/evapotranspiration/evapotranspiration index 6b5f9d57d9..7ec90d137f 160000 --- a/extern/evapotranspiration/evapotranspiration +++ b/extern/evapotranspiration/evapotranspiration @@ -1 +1 @@ -Subproject commit 6b5f9d57d9eb0100b0afd5d4a722c0fc7a8376dc +Subproject commit 7ec90d137fc7d3ebaaf0dd16a8af34ca669d34f0 diff --git a/extern/lstm b/extern/lstm index 341be45ed8..ac56cbcd86 160000 --- a/extern/lstm +++ b/extern/lstm @@ -1 +1 @@ -Subproject commit 341be45ed854442459ad2f4ed05532b5eb5406fe +Subproject commit ac56cbcd86c8738c766f2b8288b968b2eb8977f6 diff --git a/extern/noah-owp-modular/noah-owp-modular b/extern/noah-owp-modular/noah-owp-modular index 3d5b1b0e79..3757b2889d 160000 --- a/extern/noah-owp-modular/noah-owp-modular +++ b/extern/noah-owp-modular/noah-owp-modular @@ -1 +1 @@ -Subproject commit 3d5b1b0e794ba878bc59472df77f0c9ce4138a69 +Subproject commit 3757b2889da37ebcd3ade617880705eb91176a54 diff --git a/extern/sac-sma/sac-sma b/extern/sac-sma/sac-sma index 857e364571..c08f021c12 160000 --- a/extern/sac-sma/sac-sma +++ b/extern/sac-sma/sac-sma @@ -1 +1 @@ -Subproject commit 857e36457141e100f5eef54218c6842877ef863b +Subproject commit c08f021c121e587272fe520adca8fb5074e46329 diff --git a/extern/sloth b/extern/sloth index 4e4d43d2b0..cf9030413d 160000 --- a/extern/sloth +++ b/extern/sloth @@ -1 +1 @@ -Subproject commit 4e4d43d2b0dc2d7753828397f446f82d3960a6a5 +Subproject commit cf9030413d36045a01d4b903cc61d425d2057289 diff --git a/extern/snow17 b/extern/snow17 index 0747f9a2ee..a2d3ec49da 160000 --- a/extern/snow17 +++ b/extern/snow17 @@ -1 +1 @@ -Subproject commit 0747f9a2eeaf2a7e191a5d064aca40cae72c375e +Subproject commit a2d3ec49daf8ecf6d59f00b4e78e9f6e2fc3f676 diff --git a/extern/t-route b/extern/t-route index be2f1bbcca..61174a7757 160000 --- a/extern/t-route +++ b/extern/t-route @@ -1 +1 @@ -Subproject commit be2f1bbcca2f2832763aa64fdf81fac606f94f58 +Subproject commit 61174a7757da44e07ad1c1a007bbdf5b1e1a984d diff --git a/extern/topmodel/topmodel b/extern/topmodel/topmodel index 76c3140840..e7ace73a07 160000 --- a/extern/topmodel/topmodel +++ b/extern/topmodel/topmodel @@ -1 +1 @@ -Subproject commit 76c31408406d8fdafad2b1e99592619f45212a19 +Subproject commit e7ace73a0700bd2c174e3f1939b899f7a1b23bca diff --git a/extern/ueb-bmi b/extern/ueb-bmi index 00352d3b88..55177c55a1 160000 --- a/extern/ueb-bmi +++ b/extern/ueb-bmi @@ -1 +1 @@ -Subproject commit 00352d3b88a5b0198d647b0e8270e567df359b06 +Subproject commit 55177c55a18f96ee8e1b0bfdab1bef1fde000898 From baf99d1211cd6f2d37df23cdd786e16fdee6fb58 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Wed, 10 Sep 2025 12:39:35 -0700 Subject: [PATCH 04/35] Update snow 17 submod ref for swe/snow depth unit conversion --- extern/snow17 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/snow17 b/extern/snow17 index a2d3ec49da..0087c515a7 160000 --- a/extern/snow17 +++ b/extern/snow17 @@ -1 +1 @@ -Subproject commit a2d3ec49daf8ecf6d59f00b4e78e9f6e2fc3f676 +Subproject commit 0087c515a798975a9073b8a0283b577a166e795d From 2a52522c0692f51352f29f7ef67fc7852ad51dce Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Thu, 11 Sep 2025 14:06:08 -0700 Subject: [PATCH 05/35] Improve unit conversion error logging --- .../catchment/Bmi_Module_Formulation.hpp | 22 +++++- .../catchment/Bmi_Module_Formulation.cpp | 68 +++++++++++-------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/include/realizations/catchment/Bmi_Module_Formulation.hpp b/include/realizations/catchment/Bmi_Module_Formulation.hpp index b8604adb20..c512aa2251 100644 --- a/include/realizations/catchment/Bmi_Module_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Module_Formulation.hpp @@ -486,8 +486,26 @@ namespace realization { /** A configured mapping of BMI model variable names to standard names for use inside the framework. */ std::map bmi_var_names_map; bool model_initialized = false; - bool unitGetValueErrLogged = false; - bool unitGetValuesErrLogged = false; + + struct unit_error_log_key { + std::string requester_name; + std::string requester_variable; + std::string provider_name; + std::string provider_variable; + std::string failure_message; + + bool operator<(unit_error_log_key const& rhs) const { + return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) + < std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); + } + + bool operator==(unit_error_log_key const& rhs) const { + return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) + == std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); + } + }; + + static std::set unit_errors_reported; std::vector OPTIONAL_PARAMETERS = { BMI_REALIZATION_CFG_PARAM_OPT__USES_FORCINGS diff --git a/src/realizations/catchment/Bmi_Module_Formulation.cpp b/src/realizations/catchment/Bmi_Module_Formulation.cpp index 335cafde19..24a3958682 100644 --- a/src/realizations/catchment/Bmi_Module_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Module_Formulation.cpp @@ -134,6 +134,13 @@ namespace realization { throw std::runtime_error("Bmi_Singular_Formulation does not yet implement get_ts_index_for_time"); } + struct unit_conversion_exception : public std::runtime_error { + unit_conversion_exception(std::string message) : std::runtime_error(message) {} + std::string provider_model_name; + std::string provider_bmi_var_name; + std::vector unconverted_values; + }; + std::vector Bmi_Module_Formulation::get_values(const CatchmentAggrDataSelector& selector, data_access::ReSampleMethod m) { std::string output_name = selector.get_variable_name(); @@ -175,19 +182,11 @@ namespace realization { return values; } catch (const std::runtime_error& e) { - // Log at least one error - if (!unitGetValuesErrLogged) { - bmiform_ss << "BMI Module Formulation: get_values Unit conversion unsuccessful - Returning unconverted value! (" << e.what() << ")" << std::endl; - unitGetValuesErrLogged = true; - LOG(bmiform_ss.str(), LogLevel::WARNING); bmiform_ss.str(""); - } - else { - #ifndef UDUNITS_QUIET - bmiform_ss << "BMI Module Formulation: get_values Unit conversion unsuccessful - Returning unconverted value! (" << e.what() << ")" << std::endl; - LOG(bmiform_ss.str(), LogLevel::WARNING); bmiform_ss.str(""); - #endif - } - return values; + unit_conversion_exception uce(e.what()); + uce.provider_model_name = get_id(); + uce.provider_bmi_var_name = bmi_var_name; + uce.unconverted_values = std::move(values); + throw uce; } } //This is unlikely (impossible?) to throw since a pre-check on available names is done above. Assert instead? @@ -230,19 +229,11 @@ namespace realization { return UnitsHelper::get_converted_value(native_units, value, output_units); } catch (const std::runtime_error& e){ - // Log at least one error - if (!unitGetValueErrLogged) { - bmiform_ss << "BMI Module Formulation: get_value Unit conversion unsuccessful - Returning unconverted value! (" << e.what() << ")" << std::endl; - unitGetValueErrLogged = true; - LOG(bmiform_ss.str(), LogLevel::WARNING); bmiform_ss.str(""); - } - else { - #ifndef UDUNITS_QUIET - bmiform_ss << "BMI Module Formulation: get_value Unit conversion unsuccessful - Returning unconverted value! (" << e.what() << ")" << std::endl; - LOG(bmiform_ss.str(), LogLevel::WARNING); bmiform_ss.str(""); - #endif - } - return value; + unit_conversion_exception uce(e.what()); + uce.provider_model_name = get_id(); + uce.provider_bmi_var_name = bmi_var_name; + uce.unconverted_values.push_back(value); + throw uce; } } @@ -724,15 +715,32 @@ namespace realization { value_ptr = get_values_as_type( type, values.begin(), values.end() ); } else { - //scalar value - double value = provider->get_value(CatchmentAggrDataSelector(this->get_catchment_id(),var_map_alias, model_epoch_time, t_delta, - get_bmi_model()->GetVarUnits(var_name))); - value_ptr = get_value_as_type(type, value); + try { + //scalar value + double value = provider->get_value(CatchmentAggrDataSelector(this->get_catchment_id(),var_map_alias, model_epoch_time, t_delta, + get_bmi_model()->GetVarUnits(var_name))); + value_ptr = get_value_as_type(type, value); + } catch (unit_conversion_exception &uce) { + unit_error_log_key key{get_id(), var_map_alias, uce.provider_model_name, uce.provider_bmi_var_name, uce.what()}; + auto ret = unit_errors_reported.insert(key); + bool new_error = ret.second; + if (new_error) { + std::stringstream ss; + ss << "Unit conversion failure:" + << " requester '" << get_id() << "' catchment '" << get_catchment_id() << "' variable '" << var_map_alias << "'" + << " provider '" << uce.provider_model_name << "' source variable '" << uce.provider_bmi_var_name << "'" + << " raw value " << uce.unconverted_values[0] + << " message \"" << uce.what() << "\""; + LOG(ss.str(), LogLevel::WARNING); ss.str(""); + } + value_ptr = get_value_as_type(type, uce.unconverted_values[0]); + } } get_bmi_model()->SetValue(var_name, value_ptr.get()); } } + std::set Bmi_Module_Formulation::unit_errors_reported = {}; void Bmi_Module_Formulation::append_model_inputs_to_stream(const double &model_init_time, time_step_t t_delta, std::stringstream &inputs) { std::vector in_var_names = get_bmi_model()->GetInputVarNames(); From 213f407b22c15d86ccd2de56cb242c784501f255 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Thu, 11 Sep 2025 16:03:44 -0700 Subject: [PATCH 06/35] Print model name in unit conversion error message --- src/realizations/catchment/Bmi_Module_Formulation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/realizations/catchment/Bmi_Module_Formulation.cpp b/src/realizations/catchment/Bmi_Module_Formulation.cpp index 24a3958682..521c485ff5 100644 --- a/src/realizations/catchment/Bmi_Module_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Module_Formulation.cpp @@ -183,7 +183,7 @@ namespace realization { } catch (const std::runtime_error& e) { unit_conversion_exception uce(e.what()); - uce.provider_model_name = get_id(); + uce.provider_model_name = get_bmi_model()->get_model_name(); uce.provider_bmi_var_name = bmi_var_name; uce.unconverted_values = std::move(values); throw uce; @@ -727,7 +727,7 @@ namespace realization { if (new_error) { std::stringstream ss; ss << "Unit conversion failure:" - << " requester '" << get_id() << "' catchment '" << get_catchment_id() << "' variable '" << var_map_alias << "'" + << " requester '" << get_bmi_model()->get_model_name() << "' catchment '" << get_catchment_id() << "' variable '" << var_map_alias << "'" << " provider '" << uce.provider_model_name << "' source variable '" << uce.provider_bmi_var_name << "'" << " raw value " << uce.unconverted_values[0] << " message \"" << uce.what() << "\""; From 28dfc1b64bad99ea45a4d6f525d171e4cfe3f459 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Fri, 12 Sep 2025 11:11:54 -0700 Subject: [PATCH 07/35] Convert cout and asserts to EWTS log messages --- .../catchment/Catchment_Formulation.hpp | 19 +- .../catchment/Formulation_Manager.hpp | 189 +++++++++++------- src/NGen.cpp | 2 +- .../ForcingsEngineLumpedDataProvider.cpp | 139 +++++++++---- 4 files changed, 241 insertions(+), 108 deletions(-) diff --git a/include/realizations/catchment/Catchment_Formulation.hpp b/include/realizations/catchment/Catchment_Formulation.hpp index dc02a63f45..4892455b98 100644 --- a/include/realizations/catchment/Catchment_Formulation.hpp +++ b/include/realizations/catchment/Catchment_Formulation.hpp @@ -7,6 +7,8 @@ #include #include "GenericDataProvider.hpp" +#include "Logger.hpp" + #define DEFAULT_FORMULATION_OUTPUT_DELIMITER "," namespace realization { @@ -42,16 +44,23 @@ namespace realization { */ static void config_pattern_substitution(geojson::PropertyMap &properties, const std::string &key, const std::string &pattern, const std::string &replacement) { + std::stringstream ss; auto it = properties.find(key); // Do nothing and return if either the key isn't found or the associated property isn't a string if (it == properties.end() || it->second.get_type() != geojson::PropertyType::String) { - std::cout << "[DEBUG] Skipping pattern substitution for key: " << key << " (not found or not a string)" << std::endl; + ss.str(""); + ss << "Skipping pattern substitution for key: " << key << " (not found or not a string)" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); return; } std::string value = it->second.as_string(); - std::cout << "[DEBUG] config_pattern_substitution Performing pattern substitution for key: " << key << ", pattern: " << pattern << ", replacement: " << replacement << std::endl; -// std::cout << "[DEBUG] Original value: " << value << std::endl; + ss.str(""); + ss << "config_pattern_substitution Performing pattern substitution for key: " << key << ", pattern: " << pattern << ", replacement: " << replacement << std::endl; + LOG(ss.str(), LogLevel::DEBUG); +// ss.str(""); +// ss << "Original value: " << value << std::endl; +// LOG(ss.str(), LogLevel::DEBUG); size_t id_index = value.find(pattern); while (id_index != std::string::npos) { @@ -63,7 +72,9 @@ namespace realization { properties.erase(key); properties.emplace(key, geojson::JSONProperty(key, value)); -// std::cout << "[DEBUG] Substitution result for key: " << key << " -> " << value << std::endl; +// ss.str(""); +// ss << "Substitution result for key: " << key << " -> " << value << std::endl; +// LOG(ss.str(), LogLevel::DEBUG); } /** diff --git a/include/realizations/catchment/Formulation_Manager.hpp b/include/realizations/catchment/Formulation_Manager.hpp index e25fa5232f..227e44d24d 100644 --- a/include/realizations/catchment/Formulation_Manager.hpp +++ b/include/realizations/catchment/Formulation_Manager.hpp @@ -32,7 +32,6 @@ namespace realization { class Formulation_Manager { public: - std::shared_ptr Simulation_Time_Object; Formulation_Manager(std::stringstream &data) { @@ -55,7 +54,8 @@ namespace realization { void read(geojson::GeoJSON fabric, utils::StreamHandler output_stream) { std::stringstream ss; - std::cout << "[DEBUG] Entering Formulation_Manager::read()" << std::endl; + ss.str(""); ss << "Entering Formulation_Manager::read()" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); //TODO seperate the parsing of configuration options like time //and routing and other non feature specific tasks from this main function @@ -101,7 +101,8 @@ namespace realization { // add the layer to storage layer_storage.put_layer(layer_desc, layer_desc.id); - std::cout << "[DEBUG] Layer added: ID = " << layer_desc.id << ", Name = " << layer_desc.name << std::endl; + ss.str(""); ss << "Layer added: ID = " << layer_desc.id << ", Name = " << layer_desc.name << std::endl; + LOG(ss.str(), LogLevel::DEBUG); if (layer.has_formulation() && layer.get_domain() == "catchments") { double c_value = UnitsHelper::get_converted_value(layer_desc.time_step_units, layer_desc.time_step, "s"); @@ -136,9 +137,10 @@ namespace realization { using_routing = true; #else using_routing = false; - ss <<"WARNING: Formulation Manager found routing configuration" - << ", but routing support isn't enabled. No routing will occur." << std::endl; - LOG(ss.str(), LogLevel::SEVERE); ss.str(""); + ss.str(""); + ss <<"Formulation Manager found routing configuration" + << ", but routing support isn't enabled. No routing will occur." << std::endl; + LOG(ss.str(), LogLevel::WARNING); #endif // NGEN_WITH_ROUTING } @@ -168,15 +170,17 @@ namespace realization { if (possible_catchment_configs) { for (std::pair catchment_config : *possible_catchment_configs) { - std::cout << "[DEBUG] Processing catchment: " << catchment_config.first << std::endl; + ss.str(""); ss << "Processing catchment: " << catchment_config.first << std::endl; + LOG(ss.str(), LogLevel::DEBUG); int catchment_index = fabric->find(catchment_config.first); if (catchment_index == -1) { #ifndef NGEN_QUIET - ss <<"WARNING: Formulation_Manager::read: Cannot create formulation for catchment " - << catchment_config.first - << " that isn't identified in the hydrofabric or requested subset" << std::endl; - LOG(ss.str(), LogLevel::SEVERE); ss.str(""); + ss.str(""); + ss <<"Formulation_Manager::read: Cannot create formulation for catchment " + << catchment_config.first + << " that isn't identified in the hydrofabric or requested subset" << std::endl; + LOG(ss.str(), LogLevel::WARNING); #endif continue; } @@ -191,7 +195,8 @@ namespace realization { // Parse catchment-specific model_params auto catchment_feature = fabric->get_feature(catchment_index); - std::cout << "[DEBUG] Linking external properties for catchment: " << catchment_config.first << std::endl; + ss.str(""); ss << "Linking external properties for catchment: " << catchment_config.first << std::endl; + LOG(ss.str(), LogLevel::DEBUG); catchment_formulation.formulation.link_external(catchment_feature); this->add_formulation( @@ -202,18 +207,21 @@ namespace realization { output_stream ) ); - std::cout << "[DEBUG] Formulation constructed for catchment: " << catchment_config.first << std::endl; + ss.str(""); ss << "Formulation constructed for catchment: " << catchment_config.first << std::endl; + LOG(ss.str(), LogLevel::DEBUG); } } // Process any catchments not explicitly defined in the realization file for (geojson::Feature location : *fabric) { if (not this->contains(location->get_id())) { - std::cout << "[DEBUG] Creating missing formulation for location: " << location->get_id() << std::endl; + ss.str(""); ss << "Creating missing formulation for location: " << location->get_id() << std::endl; + LOG(ss.str(), LogLevel::DEBUG); std::shared_ptr missing_formulation = this->construct_missing_formulation( location, output_stream, simulation_time_config); this->add_formulation(missing_formulation); -// std::cout << "[DEBUG] Missing formulation created for location: " << location->get_id() << std::endl; +// ss.str(""); ss << "Missing formulation created for location: " << location->get_id() << std::endl; +// LOG(ss.str(), LogLevel::DEBUG); } } } @@ -271,7 +279,9 @@ namespace realization { * @return routing t_route_config_file_with_path */ std::string get_t_route_config_file_with_path() { - std::cout << "[DEBUG] Retrieving t_route config file path" << std::endl; + std::stringstream ss; + ss.str(""); ss << "Retrieving t_route config file path" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); if(this->routing_config != nullptr) return this->routing_config->t_route_config_file_with_path; else @@ -296,7 +306,9 @@ namespace realization { // constituent formulations points to any forcing // object other than the enclosing // Bmi_Multi_Formulation instance itself. - std::cout << "[DEBUG] Finalizing Formulation_Manager" << std::endl; + std::stringstream ss; + ss.str(""); ss << "Finalizing Formulation_Manager" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); for (auto const& fmap: formulations) { fmap.second->finalize(); } @@ -310,7 +322,8 @@ namespace realization { #if NGEN_WITH_PYTHON data_access::detail::ForcingsEngineStorage::instances.clear(); #endif - std::cout << "[DEBUG] Formulation_Manager finalized" << std::endl; + ss.str(""); ss << "Formulation_Manager finalized" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); } /** @@ -364,7 +377,9 @@ namespace realization { * @return a reference to the LayerStorageObject */ ngen::LayerDataStorage& get_layer_metadata() { - std::cout << "[DEBUG] Retrieving layer metadata" << std::endl; + std::stringstream ss; + ss.str(""); ss << "Retrieving layer metadata" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); return layer_storage; } @@ -376,7 +391,9 @@ namespace realization { const realization::config::Config& catchment_formulation, utils::StreamHandler output_stream ) { - std::cout << "[DEBUG] Entering construct_formulation_from_config for identifier: " << identifier << std::endl; + std::stringstream ss; + ss.str(""); ss << "Entering construct_formulation_from_config for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Check if the formulation exists if (!formulation_exists(catchment_formulation.formulation.type)) { @@ -389,18 +406,20 @@ namespace realization { } // Check for missing forcing parameters - std::cout << "[DEBUG] Checking forcing parameters for identifier: " << identifier << std::endl; + ss.str(""); ss << "Checking forcing parameters for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); if (catchment_formulation.forcing.parameters.empty()) { std::string throw_msg; throw_msg.assign("No forcing definition was found for " + identifier); - LOG(throw_msg, LogLevel::WARNING); + LOG(throw_msg, LogLevel::FATAL); throw std::runtime_error(throw_msg); } // Check for missing path std::vector missing_parameters; if (!catchment_formulation.forcing.has_key("path")) { - std::cout << "[DEBUG] Missing path parameter for identifier: " << identifier << std::endl; + ss.str(""); ss << "Missing path parameter for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); missing_parameters.push_back("path"); } @@ -422,30 +441,38 @@ namespace realization { // Extract forcing parameters forcing_params forcing_config = this->get_forcing_params(catchment_formulation.forcing.parameters, identifier, simulation_time_config); - std::cout << "[ngen debug] Forcing parameters extracted for identifier: " << identifier << std::endl; - std::cout << " [ngen debug] Forcing path: " << forcing_config.path << std::endl; - std::cout << " [ngen debug] Forcing provider: " << forcing_config.provider << std::endl; - std::cout << " [ngen debug] Forcing init_config: " << forcing_config.init_config << std::endl; + ss.str(""); + ss << "Forcing parameters extracted for identifier: " << identifier << std::endl; + ss << " Forcing path: " << forcing_config.path << std::endl; + ss << " Forcing provider: " << forcing_config.provider << std::endl; + ss << " Forcing init_config: " << forcing_config.init_config << std::endl; + LOG(ss.str(), LogLevel::DEBUG); std::time_t start_t = static_cast(forcing_config.simulation_start_t); std::time_t end_t = static_cast(forcing_config.simulation_end_t); - std::cout << " [ngen debug] Simulation start time: " << std::put_time(std::gmtime(&start_t), "%Y-%m-%d %H:%M:%S UTC") + ss.str(""); + ss << " Simulation start time: " << std::put_time(std::gmtime(&start_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << forcing_config.simulation_start_t << ")" << std::endl; - std::cout << " [ngen debug] Simulation end time: " << std::put_time(std::gmtime(&end_t), "%Y-%m-%d %H:%M:%S UTC") + ss << " Simulation end time: " << std::put_time(std::gmtime(&end_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << forcing_config.simulation_end_t << ")" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Construct formulation - std::cout << "[DEBUG] Constructing formulation for type: " << catchment_formulation.formulation.type << std::endl; + ss.str(""); ss << "Constructing formulation for type: " << catchment_formulation.formulation.type << std::endl; + LOG(ss.str(), LogLevel::DEBUG); std::shared_ptr constructed_formulation = construct_formulation( catchment_formulation.formulation.type, identifier, forcing_config, output_stream ); - std::cout << "[DEBUG] Formulation constructed successfully for identifier: " << identifier << std::endl; + ss.str(""); ss << "Formulation constructed successfully for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Create formulation instance - std::cout << "[DEBUG] Calling create_formulation for identifier: " << identifier << std::endl; + ss.str(""); ss << "Calling create_formulation for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); constructed_formulation->create_formulation(catchment_formulation.formulation.parameters); - std::cout << "[DEBUG] Formulation creation completed for identifier: " << identifier << std::endl; + ss.str(""); ss << "Formulation creation completed for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); return constructed_formulation; } @@ -455,39 +482,47 @@ namespace realization { utils::StreamHandler output_stream, simulation_time_params &simulation_time_config ) { + std::stringstream ss; const std::string identifier = feature->get_id(); - std::cout << "[DEBUG] Entering construct_missing_formulation for identifier: " << identifier << std::endl; + ss.str(""); ss << "Entering construct_missing_formulation for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Extract forcing parameters from the global config forcing_params forcing_config = this->get_forcing_params(global_config.forcing.parameters, identifier, simulation_time_config); - std::cout << "[ngen debug] Forcing parameters extracted for identifier: " << identifier << std::endl; - std::cout << " [ngen debug] Forcing path: " << forcing_config.path << std::endl; - std::cout << " [ngen debug] Forcing provider: " << forcing_config.provider << std::endl; - std::cout << " [ngen debug] Forcing init_config: " << forcing_config.init_config << std::endl; + ss.str(""); + ss << "Forcing parameters extracted for identifier: " << identifier << std::endl; + ss << " Forcing path: " << forcing_config.path << std::endl; + ss << " Forcing provider: " << forcing_config.provider << std::endl; + ss << " Forcing init_config: " << forcing_config.init_config << std::endl; + LOG(ss.str(), LogLevel::DEBUG); std::time_t start_t = static_cast(forcing_config.simulation_start_t); std::time_t end_t = static_cast(forcing_config.simulation_end_t); - std::cout << " [ngen debug] Simulation start time: " << std::put_time(std::gmtime(&start_t), "%Y-%m-%d %H:%M:%S UTC") + ss.str(""); + ss << " Simulation start time: " << std::put_time(std::gmtime(&start_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << forcing_config.simulation_start_t << ")" << std::endl; - std::cout << " [ngen debug] Simulation end time: " << std::put_time(std::gmtime(&end_t), "%Y-%m-%d %H:%M:%S UTC") + ss << " Simulation end time: " << std::put_time(std::gmtime(&end_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << forcing_config.simulation_end_t << ")" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Construct the formulation object - std::cout << "[DEBUG] Entering construct_formulation for identifier: " << identifier << ", type: " << global_config.formulation.type << std::endl; + ss.str(""); ss << "Entering construct_formulation for identifier: " << identifier << ", type: " << global_config.formulation.type << std::endl; + LOG(ss.str(), LogLevel::DEBUG); std::shared_ptr missing_formulation = construct_formulation( global_config.formulation.type, identifier, forcing_config, output_stream ); - std::cout << "[DEBUG] Formulation object constructed for identifier: " << identifier << std::endl; + ss.str(""); ss << "Formulation object constructed for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Make a copy of the global configuration so parameters don't clash when linking to external data realization::config::Config global_copy = global_config; // // Log parameters before substitution -// std::cout << "[DEBUG] Global config parameters (before substitution) for identifier: " << identifier << std::endl; +// ss.str(""); ss << "Global config parameters (before substitution) for identifier: " << identifier << std::endl; // for (auto it = global_copy.formulation.parameters.begin(); it != global_copy.formulation.parameters.end(); ++it) { // const std::string& key = it->first; // const geojson::JSONProperty& value = it->second; @@ -500,7 +535,8 @@ namespace realization { // } // Substitute {{id}} in the global formulation - std::cout << "[DEBUG] Checking for init_config before substitution for identifier: " << identifier << std::endl; + ss.str(""); ss << "Checking for init_config before substitution for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); auto init_config_it = global_copy.formulation.parameters.find(BMI_REALIZATION_CFG_PARAM_REQ__INIT_CONFIG); if (init_config_it != global_copy.formulation.parameters.end()) { const geojson::JSONProperty& init_config_property = init_config_it->second; @@ -508,9 +544,12 @@ namespace realization { if (init_config_property.get_type() == geojson::PropertyType::String) { std::string original_value = init_config_property.as_string(); if (!original_value.empty()) { - std::cout << "[DEBUG] construct_missing_formulation Performing pattern substitution for key: " << BMI_REALIZATION_CFG_PARAM_REQ__INIT_CONFIG - << ", pattern: {{id}}, replacement: " << identifier << std::endl; -// std::cout << "[DEBUG] Original value: " << original_value << std::endl; + ss.str(""); ss << "construct_missing_formulation Performing pattern substitution for key: " << BMI_REALIZATION_CFG_PARAM_REQ__INIT_CONFIG + << ", pattern: {{id}}, replacement: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); + ss.str(""); +// ss.str(""); ss << "Original value: " << original_value << std::endl; +// LOG(ss.str(), LogLevel::DEBUG); Catchment_Formulation::config_pattern_substitution( global_copy.formulation.parameters, @@ -519,17 +558,21 @@ namespace realization { identifier ); } else { - std::cout << "[WARNING] init_config is present but empty for identifier: " << identifier << std::endl; + ss.str(""); ss << "init_config is present but empty for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::WARNING); } } else { - std::cout << "[WARNING] init_config is present but not a string for identifier: " << identifier << std::endl; + ss.str(""); ss << "init_config is present but not a string for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::WARNING); } } else { - std::cout << "[WARNING] init_config not present in global configuration for identifier: " << identifier << std::endl; + ss.str(""); ss << "[WARNING] init_config not present in global configuration for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::WARNING); } // // Log parameters after substitution -// std::cout << "[DEBUG] Global config parameters (after substitution) for identifier: " << identifier << std::endl; +// ss.str(""); ss << "Global config parameters (after substitution) for identifier: " << identifier << std::endl; +// LOG(ss.str(), LogLevel::DEBUG); // for (auto it = global_copy.formulation.parameters.begin(); it != global_copy.formulation.parameters.end(); ++it) { // const std::string& key = it->first; // const geojson::JSONProperty& value = it->second; @@ -542,33 +585,40 @@ namespace realization { // } // Link external properties - std::cout << "[DEBUG] Linking external properties for identifier: " << identifier << std::endl; + ss.str(""); ss << "Linking external properties for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); auto formulation = realization::config::Formulation(global_copy.formulation); formulation.link_external(feature); // Create the formulation - std::cout << "[DEBUG] Creating formulation for identifier: " << identifier << std::endl; + ss.str(""); ss << "Creating formulation for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); missing_formulation->create_formulation(formulation.parameters); -// std::cout << "[DEBUG] Formulation creation completed for identifier: " << identifier << std::endl; +// ss.str(""); ss << "Formulation creation completed for identifier: " << identifier << std::endl; +// LOG(ss.str(), LogLevel::DEBUG); return missing_formulation; } forcing_params get_forcing_params(const geojson::PropertyMap &forcing_prop_map, std::string identifier, simulation_time_params &simulation_time_config) { - std::cout << "[DEBUG] Entering get_forcing_params for identifier: " << identifier << std::endl; + std::stringstream ss; + ss.str(""); ss << "Entering get_forcing_params for identifier: " << identifier << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Extract the required 'path' parameter std::string path = ""; if (forcing_prop_map.count("path") != 0) { path = forcing_prop_map.at("path").as_string(); - std::cout << " [DEBUG] Forcing path: " << path << std::endl; + ss.str(""); ss << " Forcing path: " << path << std::endl; + LOG(ss.str(), LogLevel::DEBUG); } // Extract the required 'provider' parameter std::string provider; if (forcing_prop_map.count("provider") != 0) { provider = forcing_prop_map.at("provider").as_string(); - std::cout << " [DEBUG] Forcing provider: " << provider << std::endl; + ss.str(""); ss << " Forcing provider: " << provider << std::endl; + LOG(ss.str(), LogLevel::DEBUG); } // Extract the optional 'init_config' parameter from 'params' @@ -579,7 +629,8 @@ namespace realization { const geojson::PropertyMap& params_map = params_property.get_values(); if (params_map.count("init_config") != 0) { init_config = params_map.at("init_config").as_string(); - std::cout << " [DEBUG] Forcing init_config: " << init_config << std::endl; + ss.str(""); ss << " Forcing init_config: " << init_config << std::endl; + LOG(ss.str(), LogLevel::DEBUG); } } else { std::cout << "[WARNING] 'params' is not an object for identifier: " << identifier << std::endl; @@ -779,17 +830,19 @@ namespace realization { case geojson::PropertyType::List: case geojson::PropertyType::Object: default: - ss << "WARNING: property type " << static_cast(catchment_attribute.get_type()) << " not allowed as model parameter. " + ss.str(""); + ss << "property type " << static_cast(catchment_attribute.get_type()) << " not allowed as model parameter. " << "Must be one of: Natural (int), Real (double), Boolean, or String" << '\n'; - LOG(ss.str(), LogLevel::SEVERE); ss.str(""); + LOG(ss.str(), LogLevel::WARNING); ss.str(""); break; } } else { - ss << "WARNING Failed to parse external parameter: catchment `" - << catchment_feature->get_id() - << "` does not contain the property `" - << param_name << "`\n"; - LOG(ss.str(), LogLevel::SEVERE); ss.str(""); + ss.str(""); + ss << " ailed to parse external parameter: catchment `" + << catchment_feature->get_id() + << "` does not contain the property `" + << param_name << "`\n"; + LOG(ss.str(), LogLevel::WARNING); } } @@ -851,9 +904,9 @@ namespace realization { default: // TODO: Should list/object values be passed to model parameters? // Typically, feature properties *should* be scalars. - ss << "WARNING: property type " << static_cast(catchment_attribute.get_type()) << " not allowed as model parameter. " - << "Must be one of: Natural (int), Real (double), Boolean, or String" << '\n'; - LOG(ss.str(), LogLevel::SEVERE); ss.str(""); + ss.str(""); ss << "property type " << static_cast(catchment_attribute.get_type()) << " not allowed as model parameter. " + << "Must be one of: Natural (int), Real (double), Boolean, or String" << '\n'; + LOG(ss.str(), LogLevel::WARNING); break; } } diff --git a/src/NGen.cpp b/src/NGen.cpp index 4aed4cda84..bfc4cebabe 100644 --- a/src/NGen.cpp +++ b/src/NGen.cpp @@ -690,7 +690,7 @@ int main(int argc, char* argv[]) { // next time if (layer_next_time <= next_time && layer_next_time <= prev_layer_time) { if (count % 100 == 0) { - ss << "Updating layer: " << layer->get_name() << "\n"; + ss << "Updating layer: " << layer->get_name() << " Count=" << count << "\n"; LOG(ss.str(), LogLevel::DEBUG); ss.str(""); } diff --git a/src/forcing/ForcingsEngineLumpedDataProvider.cpp b/src/forcing/ForcingsEngineLumpedDataProvider.cpp index b99cfafa43..56e4e8c039 100644 --- a/src/forcing/ForcingsEngineLumpedDataProvider.cpp +++ b/src/forcing/ForcingsEngineLumpedDataProvider.cpp @@ -19,9 +19,11 @@ std::size_t convert_divide_id_stoi(const std::string& divide_id) : ÷_id[separator + 1] ); - std::cout << "[ngen debug] Converting divide ID: " << divide_id - << " -> " << split << std::endl; - + std::stringstream ss; + ss.str(""); + ss << "Converting divide ID: " << divide_id + << " -> " << split << std::endl; + LOG(ss.str(), LogLevel::DEBUG); return std::atol(split); } @@ -34,19 +36,28 @@ Provider::ForcingsEngineLumpedDataProvider( : BaseProvider(init_config, time_begin_seconds, time_end_seconds) { // Add detailed logging of the constructor arguments - std::cout << "\n[ngen debug] Initializing ForcingsEngineLumpedDataProvider:" << std::endl; - std::cout << " init_config: " << init_config << std::endl; - + std::stringstream ss; + ss.str(""); + ss << "Initializing ForcingsEngineLumpedDataProvider:" << std::endl; + ss << " init_config: " << init_config << std::endl; + LOG(ss.str(), LogLevel::DEBUG); + std::time_t tb_t = static_cast(time_begin_seconds); std::time_t te_t = static_cast(time_end_seconds); - std::cout << " Time begin: " << std::put_time(std::gmtime(&tb_t), "%Y-%m-%d %H:%M:%S UTC") - << " (" << time_begin_seconds << ")" << std::endl; + ss.str(""); + ss << " Time begin: " << std::put_time(std::gmtime(&tb_t), "%Y-%m-%d %H:%M:%S UTC") + << " (" << time_begin_seconds << ")" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); - std::cout << " Time end: " << std::put_time(std::gmtime(&te_t), "%Y-%m-%d %H:%M:%S UTC") - << " (" << time_end_seconds << ")" << std::endl; + ss.str(""); + ss << " Time end: " << std::put_time(std::gmtime(&te_t), "%Y-%m-%d %H:%M:%S UTC") + << " (" << time_end_seconds << ")" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); - std::cout << " divide ID: " << divide_id << std::endl; + ss.str(""); + ss << " divide ID: " << divide_id << std::endl; + LOG(ss.str(), LogLevel::DEBUG); divide_id_ = convert_divide_id_stoi(divide_id); @@ -61,14 +72,18 @@ Provider::ForcingsEngineLumpedDataProvider( "Failed to initialize ForcingsEngineLumpedDataProvider: `CAT-ID` is not an output variable of the forcings engine." }; } - std::cout << "[ngen debug] Found CAT-ID in output names" << std::endl; + ss.str(""); + ss << " Found CAT-ID in output names" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); var_output_names_.erase(cat_id_pos); const auto size_id_dimension = static_cast( bmi_->GetVarNbytes("CAT-ID") / bmi_->GetVarItemsize("CAT-ID") ); - std::cout << "[ngen debug] CAT-ID size: " << size_id_dimension << std::endl; + ss.str(""); + ss << " CAT-ID size: " << size_id_dimension << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Copy CAT-ID values into instance vector const auto cat_id_span = boost::span( @@ -78,15 +93,21 @@ Provider::ForcingsEngineLumpedDataProvider( auto divide_id_pos = std::find(cat_id_span.begin(), cat_id_span.end(), divide_id_); if (divide_id_pos == cat_id_span.end()) { - std::cerr << "[ngen error] Unable to find divide ID `" << divide_id - << "` in the given Forcings Engine domain" << std::endl; + ss.str(""); + ss << "Unable to find divide ID `" << divide_id + << "` in the given Forcings Engine domain" << std::endl; + LOG(ss.str(), LogLevel::SEVERE); divide_idx_ = static_cast(-1); } else { divide_idx_ = std::distance(cat_id_span.begin(), divide_id_pos); - std::cout << "[ngen debug] Divide ID found at index: " << divide_idx_ << std::endl; + ss.str(""); + ss << " Divide ID found at index: " << divide_idx_ << std::endl; + LOG(ss.str(), LogLevel::INFO); } - std::cout << "[ngen debug] ForcingsEngineLumpedDataProvider initialization complete" << std::endl; + ss.str(""); + ss << " ForcingsEngineLumpedDataProvider initialization complete" << std::endl; + LOG(LogLevel::DEBUG, ss.str()); } std::size_t Provider::divide() const noexcept @@ -104,7 +125,13 @@ Provider::data_type Provider::get_value( data_access::ReSampleMethod m ) { - assert(divide_id_ == convert_divide_id_stoi(selector.get_id())); + std::stringstream ss; + if (!(divide_id_ == convert_divide_id_stoi(selector.get_id()))) { + ss.str(""); + ss << "divide_id_ " << divide_id_ << " != selector id " << convert_divide_id_stoi(selector.get_id()); + LOG(LogLevel::FATAL, ss.str()); + assert(divide_id_ == convert_divide_id_stoi(selector.get_id())); + } auto variable = ensure_variable(selector.get_variable_name()); @@ -118,19 +145,35 @@ Provider::data_type Provider::get_value( auto s_t = std::chrono::system_clock::to_time_t(start); auto e_t = std::chrono::system_clock::to_time_t(end); - std::cout << "get_value() Time begin: " << std::put_time(std::gmtime(&tb_t), "%Y-%m-%d %H:%M:%S UTC") - << " (" << tb_t << ")" - << " | start: " << std::put_time(std::gmtime(&s_t), "%Y-%m-%d %H:%M:%S UTC") - << " (" << s_t << ")" << std::endl; + ss.str(""); + ss << "get_value() Time begin: " << std::put_time(std::gmtime(&tb_t), "%Y-%m-%d %H:%M:%S UTC") + << " (" << tb_t << ")" + << " | start: " << std::put_time(std::gmtime(&s_t), "%Y-%m-%d %H:%M:%S UTC") + << " (" << s_t << ")" << std::endl; + LOG(LogLevel::INFO, ss.str()); - std::cout << "get_value() Time end: " << std::put_time(std::gmtime(&te_t), "%Y-%m-%d %H:%M:%S UTC") - << " (" << te_t << ")" - << " | end: " << std::put_time(std::gmtime(&e_t), "%Y-%m-%d %H:%M:%S UTC") - << " (" << e_t << ")" << std::endl; + ss.str(""); + ss << "get_value() Time end: " << std::put_time(std::gmtime(&te_t), "%Y-%m-%d %H:%M:%S UTC") + << " (" << te_t << ")" + << " | end: " << std::put_time(std::gmtime(&e_t), "%Y-%m-%d %H:%M:%S UTC") + << " (" << e_t << ")" << std::endl; + LOG(LogLevel::INFO, ss.str()); using namespace std::literals::chrono_literals; - assert(start >= time_begin_); - assert(end < time_end_ + time_step_ + 1s); + if (!(start >= time_begin_)) { + LOG(LogLevel::FATAL, + "Begin time less than start: start=%lld vs time_begin_=%lld", + static_cast(start.time_since_epoch().count()), + static_cast(time_begin_.time_since_epoch().count())); + assert(start >= time_begin_); + } + if (!(end < time_end_ + time_step_ + 1s)) { + LOG(LogLevel::FATAL, + "Next Timestep > end: end=%lld vs limit=%lld", + static_cast(end.time_since_epoch().count()), + static_cast((time_end_ + time_step_ + 1s).time_since_epoch().count())); + assert(end < time_end_ + time_step_ + 1s); + } auto current = start; while (current < end) { @@ -148,7 +191,10 @@ Provider::data_type Provider::get_value( return acc; } - throw std::runtime_error{"Given ReSampleMethod " + std::to_string(m) + " not implemented."}; + ss.str(""); + ss << "Given ReSampleMethod " + std::to_string(m) + " not implemented."; + LOG(LogLevel::FATAL, ss.str()); + throw std::runtime_error{ss.str()}; } std::vector Provider::get_values( @@ -156,8 +202,14 @@ std::vector Provider::get_values( data_access::ReSampleMethod /* unused */ ) { - assert(divide_id_ == convert_divide_id_stoi(selector.get_id())); - + std::stringstream ss; + if (!(divide_id_ == convert_divide_id_stoi(selector.get_id()))) { + ss.str(""); + ss << "divide id " << divide_id_ << "not equal to selector.get_id" << convert_divide_id_stoi(selector.get_id()); + LOG(LogLevel::FATAL, ss.str()); + assert(divide_id_ == convert_divide_id_stoi(selector.get_id())); + } + auto variable = ensure_variable(selector.get_variable_name()); const auto start = clock_type::from_time_t(selector.get_init_time()); @@ -168,20 +220,37 @@ std::vector Provider::get_values( auto s_t = std::chrono::system_clock::to_time_t(start); auto e_t = std::chrono::system_clock::to_time_t(end); - std::cout << "get_values() Time begin: " << std::put_time(std::gmtime(&tb_t), "%Y-%m-%d %H:%M:%S UTC") + ss.str(""); + ss << "get_values() Time begin: " << std::put_time(std::gmtime(&tb_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << tb_t << ")" << " | start: " << std::put_time(std::gmtime(&s_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << s_t << ")" << std::endl; + LOG(LogLevel::INFO, ss.str()); - std::cout << "get_values() Time end: " << std::put_time(std::gmtime(&te_t), "%Y-%m-%d %H:%M:%S UTC") + ss.str(""); + ss << "get_values() Time end: " << std::put_time(std::gmtime(&te_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << te_t << ")" << " | end: " << std::put_time(std::gmtime(&e_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << e_t << ")" << std::endl; + LOG(LogLevel::INFO, ss.str()); using namespace std::literals::chrono_literals; - assert(start >= time_begin_); - assert(end < time_end_ + time_step_ + 1s); + if (!(start >= time_begin_)) { + LOG(LogLevel::FATAL, + "Begin time less than start: start=%lld vs time_begin_=%lld", + static_cast(start.time_since_epoch().count()), + static_cast(time_begin_.time_since_epoch().count())); + assert(start >= time_begin_); + } + if (!(end < time_end_ + time_step_ + 1s)) { + LOG(LogLevel::FATAL, + "Next Timestep > end: end=%lld vs limit=%lld", + static_cast(end.time_since_epoch().count()), + static_cast((time_end_ + time_step_ + 1s).time_since_epoch().count())); + assert(end < time_end_ + time_step_ + 1s); + } + std::vector values; auto current = start; while (current < end) { From cb37ceaaaae5481279334265484b2be43e8ef69c Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Fri, 12 Sep 2025 17:26:39 -0700 Subject: [PATCH 08/35] Fix indentation --- include/realizations/catchment/Formulation_Manager.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/realizations/catchment/Formulation_Manager.hpp b/include/realizations/catchment/Formulation_Manager.hpp index 227e44d24d..07ca80c535 100644 --- a/include/realizations/catchment/Formulation_Manager.hpp +++ b/include/realizations/catchment/Formulation_Manager.hpp @@ -176,11 +176,11 @@ namespace realization { int catchment_index = fabric->find(catchment_config.first); if (catchment_index == -1) { #ifndef NGEN_QUIET - ss.str(""); - ss <<"Formulation_Manager::read: Cannot create formulation for catchment " - << catchment_config.first - << " that isn't identified in the hydrofabric or requested subset" << std::endl; - LOG(ss.str(), LogLevel::WARNING); + ss.str(""); + ss << "Formulation_Manager::read: Cannot create formulation for catchment " + << catchment_config.first + << " that isn't identified in the hydrofabric or requested subset" << std::endl; + LOG(ss.str(), LogLevel::WARNING); #endif continue; } From 2c90142aeda90348fbb1564fadd6a2e7f4645a07 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Fri, 12 Sep 2025 18:04:32 -0700 Subject: [PATCH 09/35] Changed asserts to throws. Moved GetLogger to public. Converted more cout, cerr to log msgs --- .../forcing/ForcingsEngineDataProvider.hpp | 56 ++++++++++++------- .../catchment/Formulation_Constructors.hpp | 17 ++++-- include/utilities/Logger.hpp | 4 +- .../ForcingsEngineLumpedDataProvider.cpp | 39 ++++++++----- 4 files changed, 78 insertions(+), 38 deletions(-) diff --git a/include/forcing/ForcingsEngineDataProvider.hpp b/include/forcing/ForcingsEngineDataProvider.hpp index ca11fc555c..89b49ec664 100644 --- a/include/forcing/ForcingsEngineDataProvider.hpp +++ b/include/forcing/ForcingsEngineDataProvider.hpp @@ -16,6 +16,7 @@ #include "DataProvider.hpp" #include "bmi/Bmi_Py_Adapter.hpp" +#include "Logger.hpp" namespace data_access { @@ -173,31 +174,38 @@ struct ForcingsEngineDataProvider : public DataProvider : time_begin_(std::chrono::system_clock::from_time_t(time_begin_seconds)) , time_end_(std::chrono::system_clock::from_time_t(time_end_seconds)) { + std::stringstream ss; + // Log the constructor arguments - std::cout << "[ngen debug] Entering ForcingsEngineDataProvider constructor" << std::endl; + ss.str(""); + ss << "Entering ForcingsEngineDataProvider constructor" << std::endl; + LOG(LogLevel::DEBUG, ss.str()); std::time_t start_t = static_cast(time_begin_seconds); std::time_t end_t = static_cast(time_end_seconds); - std::cout << " Start time: " << std::put_time(std::gmtime(&start_t), "%Y-%m-%d %H:%M:%S UTC") - << " (" << time_begin_seconds << ")" << std::endl; + ss.str(""); + ss << " Start time: " << std::put_time(std::gmtime(&start_t), "%Y-%m-%d %H:%M:%S UTC") + << " (" << time_begin_seconds << ")" << std::endl; + LOG(LogLevel::INFO, ss.str()); - std::cout << " End time: " << std::put_time(std::gmtime(&end_t), "%Y-%m-%d %H:%M:%S UTC") - << " (" << time_end_seconds << ")" << std::endl; + ss.str(""); + ss << " End time: " << std::put_time(std::gmtime(&end_t), "%Y-%m-%d %H:%M:%S UTC") + << " (" << time_end_seconds << ")" << std::endl; + LOG(LogLevel::INFO, ss.str()); // Attempt to retrieve a previously created BMI instance bmi_ = storage_type::instances.get(init_config); // If it doesn't exist, create it and assign it to the storage map if (bmi_ != nullptr) { - std::cout << "[ngen debug] Reusing existing BMI instance for init_config file: " << init_config << std::endl; + ss.str(""); + ss << "Reusing existing BMI instance for init_config file: " << init_config << std::endl; + LOG(LogLevel::DEBUG, ss.str()); } else { - // Ensure all prior output is flushed before invoking Python - std::cout.flush(); - std::cerr.flush(); - // Log the creation of a new BMI instance - std::cout << "[ngen debug] Creating new BMI instance for init_config file: " << init_config << std::endl; + ss.str(""); ss << "Creating new BMI instance for init_config file: " << init_config << std::endl; + LOG(LogLevel::DEBUG, ss.str()); // Create the BMI instance try { @@ -208,7 +216,8 @@ struct ForcingsEngineDataProvider : public DataProvider /*has_fixed_time_step=*/true ); } catch (const std::exception& ex) { - std::cerr << "[ngen error] Failed to create Bmi_Py_Adapter: " << ex.what() << std::endl; + ss.str("");ss << "Failed to create Bmi_Py_Adapter: " << ex.what() << std::endl; + LOG(LogLevel::FATAL, ss.str()); throw; } @@ -221,19 +230,28 @@ struct ForcingsEngineDataProvider : public DataProvider // NOTE: using std::lround instead of static_cast will prevent potential UB time_step_ = std::chrono::seconds{std::lround(bmi_->GetTimeStep())}; var_output_names_ = bmi_->GetOutputVarNames(); - std::cout << "[ngen debug] BMI instance initialized successfully" << std::endl; - std::cout << "[ngen debug] Time step: " << time_step_.count() << " seconds" << std::endl; - std::cout << "[ngen debug] Available output variable names:" << std::endl; - for (const auto& var_name : var_output_names_) { - std::cout << " - " << var_name << std::endl; + if (Logger::GetLogger()->GetLogLevel() == LogLevel::DEBUG) { + ss.str(""); ss << "BMI instance initialized successfully" << std::endl; + LOG(LogLevel::DEBUG, ss.str()); + ss.str(""); ss << "Time step: " << time_step_.count() << " seconds" << std::endl; + LOG(LogLevel::DEBUG, ss.str()); + ss.str(""); ss << "Available output variable names:" << std::endl; + LOG(LogLevel::DEBUG, ss.str()); + for (const auto& var_name : var_output_names_) { + ss.str(""); ss << " - " << var_name << std::endl; + LOG(LogLevel::DEBUG, ss.str()); + } } } catch (const std::exception& ex) { - std::cerr << "[ngen error] Error initializing BMI instance: " << ex.what() << std::endl; + ss.str(""); ss << "Error initializing BMI instance: " << ex.what() << std::endl; + LOG(LogLevel::FATAL, ss.str()); throw; } // Log successful constructor exit - std::cout << "[ngen debug] Exiting ForcingsEngineDataProvider constructor" << std::endl; + ss.str(""); ss << "Exiting ForcingsEngineDataProvider constructor" << std::endl; + LOG(LogLevel::DEBUG, ss.str()); + } //! Ensure a variable is available, appending the suffix if needed diff --git a/include/realizations/catchment/Formulation_Constructors.hpp b/include/realizations/catchment/Formulation_Constructors.hpp index 9424320f56..8ad1923284 100644 --- a/include/realizations/catchment/Formulation_Constructors.hpp +++ b/include/realizations/catchment/Formulation_Constructors.hpp @@ -44,6 +44,8 @@ namespace realization { ) { constructor formulation_constructor = formulations.at(formulation_type); std::shared_ptr fp; + std::stringstream ss; + if (forcing_config.provider == "CsvPerFeature" || forcing_config.provider == ""){ fp = std::make_shared(forcing_config); } @@ -57,8 +59,11 @@ namespace realization { } #if NGEN_WITH_PYTHON else if (forcing_config.provider == "ForcingsEngineLumpedDataProvider") { - std::cout << "[ngen debug] Using ForcingsEngineLumpedDataProvider for '" << identifier - << "' with init_config = " << forcing_config.init_config << std::endl; + + ss.str(""); + ss << "Using ForcingsEngineLumpedDataProvider for '" << identifier + << "' with init_config = " << forcing_config.init_config << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Confirm requirements like Python module + WGRIB2 are present @@ -68,14 +73,18 @@ namespace realization { auto start = forcing_config.simulation_start_t; auto end = forcing_config.simulation_end_t; - std::cout << "[ngen debug] About to call ForcingsEngineLumpedDataProvider constructor" << std::endl; + ss.str(""); + ss << "About to call ForcingsEngineLumpedDataProvider constructor" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); // Construct the ForcingsEngineLumpedDataProvider fp = std::make_shared( forcing_config.init_config, start, end, identifier ); - std::cout << "[ngen debug] Finished calling ForcingsEngineLumpedDataProvider constructor" << std::endl; + ss.str(""); + ss << "Finished calling ForcingsEngineLumpedDataProvider constructor" << std::endl; + LOG(ss.str(), LogLevel::DEBUG); } #endif else { // Some unknown string in the provider field? diff --git a/include/utilities/Logger.hpp b/include/utilities/Logger.hpp index a1250cf88e..0441e3091f 100644 --- a/include/utilities/Logger.hpp +++ b/include/utilities/Logger.hpp @@ -40,6 +40,8 @@ class Logger { throw std::runtime_error(message); }; + static Logger* GetLogger(); + private: // Methods static std::string ConvertLogLevelToString(LogLevel level); @@ -74,8 +76,6 @@ class Logger { bool openedOnce = false; std::unordered_map moduleLogLevels; - - static Logger* GetLogger(); }; // Placed here to ensure the class is declared before setting this preprocessor symbol diff --git a/src/forcing/ForcingsEngineLumpedDataProvider.cpp b/src/forcing/ForcingsEngineLumpedDataProvider.cpp index 56e4e8c039..c3a38b2fdd 100644 --- a/src/forcing/ForcingsEngineLumpedDataProvider.cpp +++ b/src/forcing/ForcingsEngineLumpedDataProvider.cpp @@ -65,7 +65,8 @@ Provider::ForcingsEngineLumpedDataProvider( // running the correct configuration of the forcings engine for this class. const auto cat_id_pos = std::find(var_output_names_.begin(), var_output_names_.end(), "CAT-ID"); if (cat_id_pos == var_output_names_.end()) { - std::cerr << "[ngen error] Failed to initialize ForcingsEngineLumpedDataProvider: " + + ss << "Failed to initialize ForcingsEngineLumpedDataProvider: " << "`CAT-ID` is not an output variable of the forcings engine." << " Does " << init_config << " have `GRID_TYPE` set to 'hydrofabric'?" << std::endl; throw std::runtime_error{ @@ -128,9 +129,11 @@ Provider::data_type Provider::get_value( std::stringstream ss; if (!(divide_id_ == convert_divide_id_stoi(selector.get_id()))) { ss.str(""); - ss << "divide_id_ " << divide_id_ << " != selector id " << convert_divide_id_stoi(selector.get_id()); + ss << "get_value() divide_id_ " << divide_id_ << " != selector id " << convert_divide_id_stoi(selector.get_id()); LOG(LogLevel::FATAL, ss.str()); - assert(divide_id_ == convert_divide_id_stoi(selector.get_id())); + throw std::runtime_error{ + "Divide ID mismatch in the forcings engine." + }; } auto variable = ensure_variable(selector.get_variable_name()); @@ -162,17 +165,21 @@ Provider::data_type Provider::get_value( using namespace std::literals::chrono_literals; if (!(start >= time_begin_)) { LOG(LogLevel::FATAL, - "Begin time less than start: start=%lld vs time_begin_=%lld", + "get_value() Begin time less than start: start=%lld vs time_begin_=%lld", static_cast(start.time_since_epoch().count()), static_cast(time_begin_.time_since_epoch().count())); - assert(start >= time_begin_); + throw std::runtime_error{ + "Begin time range error." + }; } if (!(end < time_end_ + time_step_ + 1s)) { LOG(LogLevel::FATAL, - "Next Timestep > end: end=%lld vs limit=%lld", + "get_value() Next Timestep > end: end=%lld vs limit=%lld", static_cast(end.time_since_epoch().count()), static_cast((time_end_ + time_step_ + 1s).time_since_epoch().count())); - assert(end < time_end_ + time_step_ + 1s); + throw std::runtime_error{ + "End time range error." + }; } auto current = start; @@ -205,9 +212,11 @@ std::vector Provider::get_values( std::stringstream ss; if (!(divide_id_ == convert_divide_id_stoi(selector.get_id()))) { ss.str(""); - ss << "divide id " << divide_id_ << "not equal to selector.get_id" << convert_divide_id_stoi(selector.get_id()); + ss << "get_values() divide id " << divide_id_ << "not equal to selector.get_id" << convert_divide_id_stoi(selector.get_id()); LOG(LogLevel::FATAL, ss.str()); - assert(divide_id_ == convert_divide_id_stoi(selector.get_id())); + throw std::runtime_error{ + "Divide ID mismatch in the forcings engine." + }; } auto variable = ensure_variable(selector.get_variable_name()); @@ -237,17 +246,21 @@ std::vector Provider::get_values( using namespace std::literals::chrono_literals; if (!(start >= time_begin_)) { LOG(LogLevel::FATAL, - "Begin time less than start: start=%lld vs time_begin_=%lld", + "get_values() Begin time less than start: start=%lld vs time_begin_=%lld", static_cast(start.time_since_epoch().count()), static_cast(time_begin_.time_since_epoch().count())); - assert(start >= time_begin_); + throw std::runtime_error{ + "Start time range error." + }; } if (!(end < time_end_ + time_step_ + 1s)) { LOG(LogLevel::FATAL, - "Next Timestep > end: end=%lld vs limit=%lld", + "get_values() Next Timestep > end: end=%lld vs limit=%lld", static_cast(end.time_since_epoch().count()), static_cast((time_end_ + time_step_ + 1s).time_since_epoch().count())); - assert(end < time_end_ + time_step_ + 1s); + throw std::runtime_error{ + "End time range error." + }; } From 9b67d519b61a8ae51dc28ee66206c78cebd0c95f Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Mon, 15 Sep 2025 10:39:23 -0700 Subject: [PATCH 10/35] Update some INFO msgs to DEBUG --- src/forcing/ForcingsEngineLumpedDataProvider.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/forcing/ForcingsEngineLumpedDataProvider.cpp b/src/forcing/ForcingsEngineLumpedDataProvider.cpp index c3a38b2fdd..68d746b4ce 100644 --- a/src/forcing/ForcingsEngineLumpedDataProvider.cpp +++ b/src/forcing/ForcingsEngineLumpedDataProvider.cpp @@ -153,14 +153,14 @@ Provider::data_type Provider::get_value( << " (" << tb_t << ")" << " | start: " << std::put_time(std::gmtime(&s_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << s_t << ")" << std::endl; - LOG(LogLevel::INFO, ss.str()); + LOG(LogLevel::DEBUG, ss.str()); ss.str(""); ss << "get_value() Time end: " << std::put_time(std::gmtime(&te_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << te_t << ")" << " | end: " << std::put_time(std::gmtime(&e_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << e_t << ")" << std::endl; - LOG(LogLevel::INFO, ss.str()); + LOG(LogLevel::DEBUG, ss.str()); using namespace std::literals::chrono_literals; if (!(start >= time_begin_)) { @@ -234,14 +234,14 @@ std::vector Provider::get_values( << " (" << tb_t << ")" << " | start: " << std::put_time(std::gmtime(&s_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << s_t << ")" << std::endl; - LOG(LogLevel::INFO, ss.str()); + LOG(LogLevel::DEBUG, ss.str()); ss.str(""); ss << "get_values() Time end: " << std::put_time(std::gmtime(&te_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << te_t << ")" << " | end: " << std::put_time(std::gmtime(&e_t), "%Y-%m-%d %H:%M:%S UTC") << " (" << e_t << ")" << std::endl; - LOG(LogLevel::INFO, ss.str()); + LOG(LogLevel::DEBUG, ss.str()); using namespace std::literals::chrono_literals; if (!(start >= time_begin_)) { From 914d103a9a9fec418602d3b17192ded8a2fc4cc3 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Mon, 15 Sep 2025 10:43:22 -0700 Subject: [PATCH 11/35] Fix typo --- include/realizations/catchment/Formulation_Manager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/realizations/catchment/Formulation_Manager.hpp b/include/realizations/catchment/Formulation_Manager.hpp index 07ca80c535..4c97110798 100644 --- a/include/realizations/catchment/Formulation_Manager.hpp +++ b/include/realizations/catchment/Formulation_Manager.hpp @@ -838,7 +838,7 @@ namespace realization { } } else { ss.str(""); - ss << " ailed to parse external parameter: catchment `" + ss << " Failed to parse external parameter: catchment `" << catchment_feature->get_id() << "` does not contain the property `" << param_name << "`\n"; From 8d8fa93352dd71666eb26be6e7ea46d815782195 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Mon, 15 Sep 2025 13:46:12 -0700 Subject: [PATCH 12/35] Update CFE submod ref for UDUNITS2 can parse --- extern/cfe/cfe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/cfe/cfe b/extern/cfe/cfe index 6eea6d86bc..b941a5bc54 160000 --- a/extern/cfe/cfe +++ b/extern/cfe/cfe @@ -1 +1 @@ -Subproject commit 6eea6d86bcf47c26c89718fe23cf35aac04aaa6b +Subproject commit b941a5bc544152464e1afc879df70285de9ac7c7 From c97228ae77e2f1f1024e4f0c07f864b3b141de53 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Fri, 12 Sep 2025 11:48:32 -0700 Subject: [PATCH 13/35] Move unit conversion error instrumentation up to Bmi_Formulation --- .../catchment/Bmi_Formulation.hpp | 27 +++++++++++++++++++ .../catchment/Bmi_Module_Formulation.hpp | 20 -------------- .../catchment/Bmi_Module_Formulation.cpp | 9 ------- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/include/realizations/catchment/Bmi_Formulation.hpp b/include/realizations/catchment/Bmi_Formulation.hpp index a8cbc34bf7..783fa8e813 100644 --- a/include/realizations/catchment/Bmi_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Formulation.hpp @@ -264,6 +264,33 @@ namespace realization { output_variable_names = out_var_names; } + struct unit_error_log_key { + std::string requester_name; + std::string requester_variable; + std::string provider_name; + std::string provider_variable; + std::string failure_message; + + bool operator<(unit_error_log_key const& rhs) const { + return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) + < std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); + } + + bool operator==(unit_error_log_key const& rhs) const { + return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) + == std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); + } + }; + + static std::set unit_errors_reported; + + struct unit_conversion_exception : public std::runtime_error { + unit_conversion_exception(std::string message) : std::runtime_error(message) {} + std::string provider_model_name; + std::string provider_bmi_var_name; + std::vector unconverted_values; + }; + private: std::string bmi_main_output_var; diff --git a/include/realizations/catchment/Bmi_Module_Formulation.hpp b/include/realizations/catchment/Bmi_Module_Formulation.hpp index c512aa2251..d845523c0d 100644 --- a/include/realizations/catchment/Bmi_Module_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Module_Formulation.hpp @@ -487,26 +487,6 @@ namespace realization { std::map bmi_var_names_map; bool model_initialized = false; - struct unit_error_log_key { - std::string requester_name; - std::string requester_variable; - std::string provider_name; - std::string provider_variable; - std::string failure_message; - - bool operator<(unit_error_log_key const& rhs) const { - return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) - < std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); - } - - bool operator==(unit_error_log_key const& rhs) const { - return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) - == std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); - } - }; - - static std::set unit_errors_reported; - std::vector OPTIONAL_PARAMETERS = { BMI_REALIZATION_CFG_PARAM_OPT__USES_FORCINGS BMI_REALIZATION_CFG_PARAM_OPT__FORCING_FILE, diff --git a/src/realizations/catchment/Bmi_Module_Formulation.cpp b/src/realizations/catchment/Bmi_Module_Formulation.cpp index 521c485ff5..5dc82f38be 100644 --- a/src/realizations/catchment/Bmi_Module_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Module_Formulation.cpp @@ -134,13 +134,6 @@ namespace realization { throw std::runtime_error("Bmi_Singular_Formulation does not yet implement get_ts_index_for_time"); } - struct unit_conversion_exception : public std::runtime_error { - unit_conversion_exception(std::string message) : std::runtime_error(message) {} - std::string provider_model_name; - std::string provider_bmi_var_name; - std::vector unconverted_values; - }; - std::vector Bmi_Module_Formulation::get_values(const CatchmentAggrDataSelector& selector, data_access::ReSampleMethod m) { std::string output_name = selector.get_variable_name(); @@ -740,8 +733,6 @@ namespace realization { } } - std::set Bmi_Module_Formulation::unit_errors_reported = {}; - void Bmi_Module_Formulation::append_model_inputs_to_stream(const double &model_init_time, time_step_t t_delta, std::stringstream &inputs) { std::vector in_var_names = get_bmi_model()->GetInputVarNames(); time_t model_epoch_time = convert_model_time(model_init_time) + get_bmi_model_start_time_forcing_offset_s(); From 21f1a6bcc231f193c0afee523151ab0d7125520e Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Fri, 12 Sep 2025 11:49:41 -0700 Subject: [PATCH 14/35] Don't error out every Bmi_Multi_Formulation --- include/realizations/catchment/Bmi_Multi_Formulation.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/realizations/catchment/Bmi_Multi_Formulation.hpp b/include/realizations/catchment/Bmi_Multi_Formulation.hpp index 286c0eeb33..e4192b84f2 100644 --- a/include/realizations/catchment/Bmi_Multi_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Multi_Formulation.hpp @@ -583,6 +583,10 @@ namespace realization { //TODO: After merge PR#405, try re-adding support for index return nested_module->get_value(selector); } + catch (unit_conversion_exception &uce) { + // We asked for it as a dimensionless quantity, "1", just above + return uce.unconverted_values[0]; + } // If there was any problem with the cast and extraction of the value, throw runtime error catch (std::exception &e) { std::string throw_msg; throw_msg.assign("Multi BMI formulation can't use associated data provider as a nested module" From ece81c6fa4c7ba4f769fb829a9f479967ba8488f Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 15:33:17 -0700 Subject: [PATCH 15/35] Move unit conversion error instrumentation up to DataProvider/namespace data_access to support CSV and NetCDF reporting --- include/forcing/DataProvider.hpp | 28 +++++++++++++++++++ .../catchment/Bmi_Formulation.hpp | 27 ------------------ .../catchment/Bmi_Multi_Formulation.hpp | 2 +- .../catchment/Bmi_Formulation.cpp | 4 +++ .../catchment/Bmi_Module_Formulation.cpp | 10 +++---- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/include/forcing/DataProvider.hpp b/include/forcing/DataProvider.hpp index 32cbb36029..4cf0026c33 100644 --- a/include/forcing/DataProvider.hpp +++ b/include/forcing/DataProvider.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include namespace data_access @@ -101,6 +103,32 @@ namespace data_access private: }; + struct unit_error_log_key { + std::string requester_name; + std::string requester_variable; + std::string provider_name; + std::string provider_variable; + std::string failure_message; + + bool operator<(unit_error_log_key const& rhs) const { + return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) + < std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); + } + + bool operator==(unit_error_log_key const& rhs) const { + return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) + == std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); + } + }; + + extern std::set unit_errors_reported; + + struct unit_conversion_exception : public std::runtime_error { + unit_conversion_exception(std::string message) : std::runtime_error(message) {} + std::string provider_model_name; + std::string provider_bmi_var_name; + std::vector unconverted_values; + }; } diff --git a/include/realizations/catchment/Bmi_Formulation.hpp b/include/realizations/catchment/Bmi_Formulation.hpp index 783fa8e813..a8cbc34bf7 100644 --- a/include/realizations/catchment/Bmi_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Formulation.hpp @@ -264,33 +264,6 @@ namespace realization { output_variable_names = out_var_names; } - struct unit_error_log_key { - std::string requester_name; - std::string requester_variable; - std::string provider_name; - std::string provider_variable; - std::string failure_message; - - bool operator<(unit_error_log_key const& rhs) const { - return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) - < std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); - } - - bool operator==(unit_error_log_key const& rhs) const { - return std::tie(requester_name, requester_variable, provider_name, provider_variable, failure_message) - == std::tie(rhs.requester_name, rhs.requester_variable, rhs.provider_name, rhs.provider_variable, rhs.failure_message); - } - }; - - static std::set unit_errors_reported; - - struct unit_conversion_exception : public std::runtime_error { - unit_conversion_exception(std::string message) : std::runtime_error(message) {} - std::string provider_model_name; - std::string provider_bmi_var_name; - std::vector unconverted_values; - }; - private: std::string bmi_main_output_var; diff --git a/include/realizations/catchment/Bmi_Multi_Formulation.hpp b/include/realizations/catchment/Bmi_Multi_Formulation.hpp index e4192b84f2..5e1c37b13b 100644 --- a/include/realizations/catchment/Bmi_Multi_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Multi_Formulation.hpp @@ -583,7 +583,7 @@ namespace realization { //TODO: After merge PR#405, try re-adding support for index return nested_module->get_value(selector); } - catch (unit_conversion_exception &uce) { + catch (data_access::unit_conversion_exception &uce) { // We asked for it as a dimensionless quantity, "1", just above return uce.unconverted_values[0]; } diff --git a/src/realizations/catchment/Bmi_Formulation.cpp b/src/realizations/catchment/Bmi_Formulation.cpp index 2048a47a79..ebb7bb32e2 100644 --- a/src/realizations/catchment/Bmi_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Formulation.cpp @@ -18,3 +18,7 @@ namespace realization BMI_REALIZATION_CFG_PARAM_REQ__MODEL_TYPE, }; } + +namespace data_access { + std::set unit_errors_reported; +} diff --git a/src/realizations/catchment/Bmi_Module_Formulation.cpp b/src/realizations/catchment/Bmi_Module_Formulation.cpp index 5dc82f38be..d376869549 100644 --- a/src/realizations/catchment/Bmi_Module_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Module_Formulation.cpp @@ -175,7 +175,7 @@ namespace realization { return values; } catch (const std::runtime_error& e) { - unit_conversion_exception uce(e.what()); + data_access::unit_conversion_exception uce(e.what()); uce.provider_model_name = get_bmi_model()->get_model_name(); uce.provider_bmi_var_name = bmi_var_name; uce.unconverted_values = std::move(values); @@ -222,7 +222,7 @@ namespace realization { return UnitsHelper::get_converted_value(native_units, value, output_units); } catch (const std::runtime_error& e){ - unit_conversion_exception uce(e.what()); + data_access::unit_conversion_exception uce(e.what()); uce.provider_model_name = get_id(); uce.provider_bmi_var_name = bmi_var_name; uce.unconverted_values.push_back(value); @@ -713,9 +713,9 @@ namespace realization { double value = provider->get_value(CatchmentAggrDataSelector(this->get_catchment_id(),var_map_alias, model_epoch_time, t_delta, get_bmi_model()->GetVarUnits(var_name))); value_ptr = get_value_as_type(type, value); - } catch (unit_conversion_exception &uce) { - unit_error_log_key key{get_id(), var_map_alias, uce.provider_model_name, uce.provider_bmi_var_name, uce.what()}; - auto ret = unit_errors_reported.insert(key); + } catch (data_access::unit_conversion_exception &uce) { + data_access::unit_error_log_key key{get_id(), var_map_alias, uce.provider_model_name, uce.provider_bmi_var_name, uce.what()}; + auto ret = data_access::unit_errors_reported.insert(key); bool new_error = ret.second; if (new_error) { std::stringstream ss; From 7ba19f37a8dd725fc16552b5c9dadf21db4f773a Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 15:33:51 -0700 Subject: [PATCH 16/35] fixup - wrong name in Bmi_Module_Formulation thrown UCE --- src/realizations/catchment/Bmi_Module_Formulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/realizations/catchment/Bmi_Module_Formulation.cpp b/src/realizations/catchment/Bmi_Module_Formulation.cpp index d376869549..070d3f3435 100644 --- a/src/realizations/catchment/Bmi_Module_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Module_Formulation.cpp @@ -223,7 +223,7 @@ namespace realization { } catch (const std::runtime_error& e){ data_access::unit_conversion_exception uce(e.what()); - uce.provider_model_name = get_id(); + uce.provider_model_name = get_bmi_model()->get_model_name(); uce.provider_bmi_var_name = bmi_var_name; uce.unconverted_values.push_back(value); throw uce; From 1e4eff6534fbec08b568eea9057c072e51d2678f Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 15:34:23 -0700 Subject: [PATCH 17/35] CsvPerFeatureForcingProvider: Throw on unit conversion errors rather than printing locally --- include/forcing/CsvPerFeatureForcingProvider.hpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/include/forcing/CsvPerFeatureForcingProvider.hpp b/include/forcing/CsvPerFeatureForcingProvider.hpp index b0d2f85903..799f0990d4 100644 --- a/include/forcing/CsvPerFeatureForcingProvider.hpp +++ b/include/forcing/CsvPerFeatureForcingProvider.hpp @@ -170,13 +170,12 @@ class CsvPerFeatureForcingProvider : public data_access::GenericDataProvider try { return UnitsHelper::get_converted_value(available_forcings_units[output_name], value, output_units); } - catch (const std::runtime_error& e){ - #ifndef UDUNITS_QUIET - std::stringstream ss; - ss <<"WARN: Unit conversion unsuccessful - Returning unconverted value! (\""< Date: Mon, 15 Sep 2025 16:03:09 -0700 Subject: [PATCH 18/35] Give more and better structured information on unit conversion errors --- include/forcing/CsvPerFeatureForcingProvider.hpp | 1 + include/forcing/DataProvider.hpp | 1 + src/realizations/catchment/Bmi_Module_Formulation.cpp | 10 +++++++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/forcing/CsvPerFeatureForcingProvider.hpp b/include/forcing/CsvPerFeatureForcingProvider.hpp index 799f0990d4..03fad6c51c 100644 --- a/include/forcing/CsvPerFeatureForcingProvider.hpp +++ b/include/forcing/CsvPerFeatureForcingProvider.hpp @@ -174,6 +174,7 @@ class CsvPerFeatureForcingProvider : public data_access::GenericDataProvider data_access::unit_conversion_exception uce(e.what()); uce.provider_model_name = "CsvPerFeatureProvider" + catchment_id; uce.provider_bmi_var_name = output_name; + uce.provider_units = available_forcings_units[output_name]; uce.unconverted_values.push_back(value); throw uce; } diff --git a/include/forcing/DataProvider.hpp b/include/forcing/DataProvider.hpp index 4cf0026c33..2cf4573f08 100644 --- a/include/forcing/DataProvider.hpp +++ b/include/forcing/DataProvider.hpp @@ -127,6 +127,7 @@ namespace data_access unit_conversion_exception(std::string message) : std::runtime_error(message) {} std::string provider_model_name; std::string provider_bmi_var_name; + std::string provider_units; std::vector unconverted_values; }; } diff --git a/src/realizations/catchment/Bmi_Module_Formulation.cpp b/src/realizations/catchment/Bmi_Module_Formulation.cpp index 070d3f3435..e869c451eb 100644 --- a/src/realizations/catchment/Bmi_Module_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Module_Formulation.cpp @@ -178,6 +178,7 @@ namespace realization { data_access::unit_conversion_exception uce(e.what()); uce.provider_model_name = get_bmi_model()->get_model_name(); uce.provider_bmi_var_name = bmi_var_name; + uce.provider_units = native_units; uce.unconverted_values = std::move(values); throw uce; } @@ -225,6 +226,7 @@ namespace realization { data_access::unit_conversion_exception uce(e.what()); uce.provider_model_name = get_bmi_model()->get_model_name(); uce.provider_bmi_var_name = bmi_var_name; + uce.provider_units = native_units; uce.unconverted_values.push_back(value); throw uce; } @@ -720,9 +722,11 @@ namespace realization { if (new_error) { std::stringstream ss; ss << "Unit conversion failure:" - << " requester '" << get_bmi_model()->get_model_name() << "' catchment '" << get_catchment_id() << "' variable '" << var_map_alias << "'" - << " provider '" << uce.provider_model_name << "' source variable '" << uce.provider_bmi_var_name << "'" - << " raw value " << uce.unconverted_values[0] + << " requester {'" << get_bmi_model()->get_model_name() << "' catchment '" << get_catchment_id() + << "' variable '" << var_name << "'" << " (alias '" << var_map_alias << "')" + << " units '" << get_bmi_model()->GetVarUnits(var_name) << "'}" + << " provider {'" << uce.provider_model_name << "' source variable '" << uce.provider_bmi_var_name << "'" + << " raw value " << uce.unconverted_values[0] << "}" << " message \"" << uce.what() << "\""; LOG(ss.str(), LogLevel::WARNING); ss.str(""); } From 53f71ecdee42320ed8ea47b71f8c18980c75a7bb Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 16:03:57 -0700 Subject: [PATCH 19/35] Match up units of input and output variables for test_bmi_foo models used in Bmi_Multi_Formulation testing --- extern/test_bmi_c/src/bmi_test_bmi_c.c | 2 +- extern/test_bmi_cpp/include/test_bmi_cpp.hpp | 4 ++-- extern/test_bmi_fortran/src/bmi_test_bmi_fortran.f90 | 4 ++-- extern/test_bmi_py/bmi_model.py | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/extern/test_bmi_c/src/bmi_test_bmi_c.c b/extern/test_bmi_c/src/bmi_test_bmi_c.c index 522fe4651d..187ca666cd 100644 --- a/extern/test_bmi_c/src/bmi_test_bmi_c.c +++ b/extern/test_bmi_c/src/bmi_test_bmi_c.c @@ -21,7 +21,7 @@ static const char *output_var_locations[OUTPUT_VAR_NAME_COUNT] = { "node", "node // Don't forget to update Get_value/Get_value_at_indices (and setter) implementation if these are adjusted static const char *input_var_names[INPUT_VAR_NAME_COUNT] = { "INPUT_VAR_1", "INPUT_VAR_2" }; static const char *input_var_types[INPUT_VAR_NAME_COUNT] = { "double", "double" }; -static const char *input_var_units[INPUT_VAR_NAME_COUNT] = { "m", "m/s" }; +static const char *input_var_units[INPUT_VAR_NAME_COUNT] = { "m", "Pa" }; static const int input_var_item_count[INPUT_VAR_NAME_COUNT] = { 1, 1 }; static const char *input_var_grids[INPUT_VAR_NAME_COUNT] = { 0, 0 }; static const char *input_var_locations[INPUT_VAR_NAME_COUNT] = { "node", "node" }; diff --git a/extern/test_bmi_cpp/include/test_bmi_cpp.hpp b/extern/test_bmi_cpp/include/test_bmi_cpp.hpp index c15896def2..4b2c31a429 100644 --- a/extern/test_bmi_cpp/include/test_bmi_cpp.hpp +++ b/extern/test_bmi_cpp/include/test_bmi_cpp.hpp @@ -172,8 +172,8 @@ class TestBmiCpp : public bmi::Bmi { std::vector input_var_types = { "double", "double" }; std::vector output_var_types = { "double", "double" }; std::vector model_var_types = {}; - std::vector input_var_units = { "m", "m" }; - std::vector output_var_units = { "m", "m/s", "m", "m" }; + std::vector input_var_units = { "mm/s", "Pa" }; + std::vector output_var_units = { "mm/s", "m/s", "m", "m" }; std::vector model_var_units = {}; std::vector input_var_locations = { "node", "node" }; std::vector output_var_locations = { "node", "node" }; diff --git a/extern/test_bmi_fortran/src/bmi_test_bmi_fortran.f90 b/extern/test_bmi_fortran/src/bmi_test_bmi_fortran.f90 index 08a08aa10e..e6c07a5631 100644 --- a/extern/test_bmi_fortran/src/bmi_test_bmi_fortran.f90 +++ b/extern/test_bmi_fortran/src/bmi_test_bmi_fortran.f90 @@ -123,10 +123,10 @@ module bmitestbmi integer :: input_grid(4) = [0, 0, 0, 1] character (len=BMI_MAX_UNITS_NAME) :: & - output_units(6) = [character(BMI_MAX_UNITS_NAME):: 'm', 'm', 's', 'm', 'm', 'm'] + output_units(6) = [character(BMI_MAX_UNITS_NAME):: 'mm s^-1', 'm', 's', 'm', 'm', 'm'] character (len=BMI_MAX_UNITS_NAME) :: & - input_units(4) = [character(BMI_MAX_UNITS_NAME):: 'm', 'm', 's', 'm'] + input_units(4) = [character(BMI_MAX_UNITS_NAME):: 'mm s^-1', 'Pa', 'K', 'mm s^-1'] character (len=BMI_MAX_LOCATION_NAME) :: & output_location(6) = [character(BMI_MAX_LOCATION_NAME):: 'node', 'node', 'node', 'node', 'node', 'node'] diff --git a/extern/test_bmi_py/bmi_model.py b/extern/test_bmi_py/bmi_model.py index 0fbb93e1b7..efbd791dbd 100755 --- a/extern/test_bmi_py/bmi_model.py +++ b/extern/test_bmi_py/bmi_model.py @@ -73,12 +73,12 @@ def __init__(self): # since the input variable names could come from any forcing... #------------------------------------------------------ #_var_name_map_long_first = { - _var_name_units_map = {'INPUT_VAR_1':['INPUT_VAR_1','-'], - 'INPUT_VAR_2':['INPUT_VAR_2','-'], - 'OUTPUT_VAR_1':['OUTPUT_VAR_1','-'], - 'OUTPUT_VAR_2':['OUTPUT_VAR_2','-'], + _var_name_units_map = {'INPUT_VAR_1':['INPUT_VAR_1','mm/s'], + 'INPUT_VAR_2':['INPUT_VAR_2','Pa'], + 'OUTPUT_VAR_1':['OUTPUT_VAR_1','m'], + 'OUTPUT_VAR_2':['OUTPUT_VAR_2','mm/s'], 'OUTPUT_VAR_3':['OUTPUT_VAR_3','-'], - 'GRID_VAR_1':['OUTPUT_VAR_1','-'], + 'GRID_VAR_1':['OUTPUT_VAR_1','mm/s'], 'GRID_VAR_2':['GRID_VAR_2','-'], } @@ -609,4 +609,4 @@ def _parse_config(self, cfg): pass # Add more config parsing if necessary - return cfg \ No newline at end of file + return cfg From 22f7fe4002207c8c671518e3e9a6725e073ab46d Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 16:34:25 -0700 Subject: [PATCH 20/35] Update Bmi_Cpp_Adapter_Test in correspondence to earlier changes --- test/bmi/Bmi_Cpp_Adapter_Test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bmi/Bmi_Cpp_Adapter_Test.cpp b/test/bmi/Bmi_Cpp_Adapter_Test.cpp index 0151360852..d9e7ad6d2d 100644 --- a/test/bmi/Bmi_Cpp_Adapter_Test.cpp +++ b/test/bmi/Bmi_Cpp_Adapter_Test.cpp @@ -51,7 +51,7 @@ class Bmi_Cpp_Adapter_Test : public ::testing::Test { std::vector expected_output_var_names = { "OUTPUT_VAR_1", "OUTPUT_VAR_2" }; std::vector expected_output_var_locations = { "node", "node" }; std::vector expected_output_var_grids = { 0, 0 }; - std::vector expected_output_var_units = { "m", "m" }; + std::vector expected_output_var_units = { "mm/s", "m" }; std::vector expected_output_var_types = { "double", "double" }; int expected_grid_rank = 1; int expected_grid_size = 1; From e197d04261f937162d94f2ddbd45fd1c2d258193 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 16:35:26 -0700 Subject: [PATCH 21/35] NetCDFPerFeatureForcingDataProvider: Throw on unit conversion errors rather than printing locally --- src/forcing/NetCDFPerFeatureDataProvider.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/forcing/NetCDFPerFeatureDataProvider.cpp b/src/forcing/NetCDFPerFeatureDataProvider.cpp index 6c5efccf9e..2a202a9247 100644 --- a/src/forcing/NetCDFPerFeatureDataProvider.cpp +++ b/src/forcing/NetCDFPerFeatureDataProvider.cpp @@ -419,14 +419,12 @@ double NetCDFPerFeatureDataProvider::get_value(const CatchmentAggrDataSelector& } catch (const std::runtime_error& e) { - //minor change to aid debugging (log_stream is an output log stream for messages from the underlying library, therefore logging to both EWTS and library logger) - netcdf_ss << "NetCDFPerFeatureDataProvider: get_converted_value Unit conversion unsuccessful - Returning unconverted value! (" << e.what() << ")" << std::endl; - log_stream << netcdf_ss.str(); - LOG(netcdf_ss.str(), LogLevel::WARNING); netcdf_ss.str(""); - netcdf_ss << "=== Exiting get_value function ===" << std::endl; - log_stream << netcdf_ss.str(); - LOG(netcdf_ss.str(), LogLevel::WARNING); netcdf_ss.str(""); - return rvalue; + data_access::unit_conversion_exception uce(e.what()); + uce.provider_model_name = "NetCDFPerFeatureDataProvider " + catchment_id; + uce.provider_bmi_var_name = selector.get_variable_name(); + uce.provider_units = native_units; + uce.unconverted_values.push_back(rvalue); + throw uce; } return rvalue; From b5c854325f044fa8cd54b1a238dfe3ef4dd7c3c8 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 16:35:56 -0700 Subject: [PATCH 22/35] CsvPerFeatureForcingProvider: add missing space in message --- include/forcing/CsvPerFeatureForcingProvider.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/forcing/CsvPerFeatureForcingProvider.hpp b/include/forcing/CsvPerFeatureForcingProvider.hpp index 03fad6c51c..486d48c220 100644 --- a/include/forcing/CsvPerFeatureForcingProvider.hpp +++ b/include/forcing/CsvPerFeatureForcingProvider.hpp @@ -172,7 +172,7 @@ class CsvPerFeatureForcingProvider : public data_access::GenericDataProvider } catch (const std::runtime_error& e) { data_access::unit_conversion_exception uce(e.what()); - uce.provider_model_name = "CsvPerFeatureProvider" + catchment_id; + uce.provider_model_name = "CsvPerFeatureProvider " + catchment_id; uce.provider_bmi_var_name = output_name; uce.provider_units = available_forcings_units[output_name]; uce.unconverted_values.push_back(value); From c024f30682baae49df0849107491c699a6c5e4b7 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 16:45:54 -0700 Subject: [PATCH 23/35] Fixup diff --- src/realizations/catchment/Bmi_Module_Formulation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/realizations/catchment/Bmi_Module_Formulation.cpp b/src/realizations/catchment/Bmi_Module_Formulation.cpp index e869c451eb..75b6ac2ada 100644 --- a/src/realizations/catchment/Bmi_Module_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Module_Formulation.cpp @@ -737,6 +737,7 @@ namespace realization { } } + void Bmi_Module_Formulation::append_model_inputs_to_stream(const double &model_init_time, time_step_t t_delta, std::stringstream &inputs) { std::vector in_var_names = get_bmi_model()->GetInputVarNames(); time_t model_epoch_time = convert_model_time(model_init_time) + get_bmi_model_start_time_forcing_offset_s(); From cd19153b99455d9e1e38f4132ba94e753e515895 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 17:02:18 -0700 Subject: [PATCH 24/35] Log about output variables not having any unit conversion applied --- include/realizations/catchment/Bmi_Multi_Formulation.hpp | 5 +++++ src/realizations/catchment/Bmi_Module_Formulation.cpp | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/realizations/catchment/Bmi_Multi_Formulation.hpp b/include/realizations/catchment/Bmi_Multi_Formulation.hpp index 5e1c37b13b..7b8090b57c 100644 --- a/include/realizations/catchment/Bmi_Multi_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Multi_Formulation.hpp @@ -585,6 +585,11 @@ namespace realization { } catch (data_access::unit_conversion_exception &uce) { // We asked for it as a dimensionless quantity, "1", just above + static bool no_conversion_message_logged = false; + if (!no_conversion_message_logged) { + no_conversion_message_logged = true; + LOG("Emitting output variables from Bmi_Multi_Formulation without unit conversion - see NGWPC-7604", LogLevel::WARNING); + } return uce.unconverted_values[0]; } // If there was any problem with the cast and extraction of the value, throw runtime error diff --git a/src/realizations/catchment/Bmi_Module_Formulation.cpp b/src/realizations/catchment/Bmi_Module_Formulation.cpp index 75b6ac2ada..883f82d679 100644 --- a/src/realizations/catchment/Bmi_Module_Formulation.cpp +++ b/src/realizations/catchment/Bmi_Module_Formulation.cpp @@ -25,8 +25,14 @@ namespace realization { if (timestep != (next_time_step_index - 1)) { throw std::invalid_argument("Only current time step valid when getting output for BMI C++ formulation"); } - std::string output_str; + static bool no_conversion_message_logged = false; + if (!no_conversion_message_logged) { + no_conversion_message_logged = true; + LOG("Emitting output variables from Bmi_Module_Formulation without unit conversion - see NGWPC-7604", LogLevel::WARNING); + } + + std::string output_str; for (const std::string& name : get_output_variable_names()) { output_str += (output_str.empty() ? "" : ",") + std::to_string(get_var_value_as_double(0, name)); } From 3ac6ca1e425aa5174a5dad8696974ca28f77d276 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 22:08:46 -0700 Subject: [PATCH 25/35] CSV Provider: Correct construction of model name string --- include/forcing/CsvPerFeatureForcingProvider.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/forcing/CsvPerFeatureForcingProvider.hpp b/include/forcing/CsvPerFeatureForcingProvider.hpp index 486d48c220..5b8b73429d 100644 --- a/include/forcing/CsvPerFeatureForcingProvider.hpp +++ b/include/forcing/CsvPerFeatureForcingProvider.hpp @@ -172,7 +172,7 @@ class CsvPerFeatureForcingProvider : public data_access::GenericDataProvider } catch (const std::runtime_error& e) { data_access::unit_conversion_exception uce(e.what()); - uce.provider_model_name = "CsvPerFeatureProvider " + catchment_id; + uce.provider_model_name = "CsvPerFeatureProvider " + std::to_string(catchment_id); uce.provider_bmi_var_name = output_name; uce.provider_units = available_forcings_units[output_name]; uce.unconverted_values.push_back(value); From 9b0c98861e598c00133b359488509c498dfceab1 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 22:09:21 -0700 Subject: [PATCH 26/35] Match BMI Fortran Adapter test to changed units for Multi_Formulation test --- test/bmi/Bmi_Fortran_Adapter_Test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bmi/Bmi_Fortran_Adapter_Test.cpp b/test/bmi/Bmi_Fortran_Adapter_Test.cpp index 1e46792dc6..1e3982dfbe 100644 --- a/test/bmi/Bmi_Fortran_Adapter_Test.cpp +++ b/test/bmi/Bmi_Fortran_Adapter_Test.cpp @@ -52,7 +52,7 @@ class Bmi_Fortran_Adapter_Test : public ::testing::Test { std::vector expected_output_var_locations = { "node", "node", "node", "node", "node"}; std::vector expected_output_var_grids = { 0, 0, 0, 1, 2, 2 }; std::vector expected_input_var_grids = { 0, 0, 0, 1 }; - std::vector expected_output_var_units = { "m", "m", "s", "m", "m", "m" }; + std::vector expected_output_var_units = { "mm s^-1", "m", "s", "m", "m", "m" }; std::vector expected_output_var_types = { "double precision", "real", "integer", "double precision", "double precision", "double precision" }; std::vector expected_output_var_item_sizes = { 8, 4, 4, 8, 8, 8}; std::vector expected_input_var_types = { "double precision", "real", "integer", "double precision" }; From 181a608d4d5745ec5e256430e6aff9925c015fd4 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Mon, 15 Sep 2025 22:55:11 -0700 Subject: [PATCH 27/35] CsvPerFeatureForcingProvider: Log variables and units, request specific units in tests, and disable now-throwing non-conversion cases --- .../forcing/CsvPerFeatureForcingProvider.hpp | 6 +++++- .../CsvPerFeatureForcingProvider_Test.cpp | 21 ++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/include/forcing/CsvPerFeatureForcingProvider.hpp b/include/forcing/CsvPerFeatureForcingProvider.hpp index 5b8b73429d..b8d05cb338 100644 --- a/include/forcing/CsvPerFeatureForcingProvider.hpp +++ b/include/forcing/CsvPerFeatureForcingProvider.hpp @@ -341,12 +341,16 @@ class CsvPerFeatureForcingProvider : public data_access::GenericDataProvider } } + LOG("CsvProvider has variable '" + var_name + "' with units '" + units + "'", LogLevel::DEBUG); + auto wkf = data_access::WellKnownFields.find(var_name); if(wkf != data_access::WellKnownFields.end()){ units = units.empty() ? std::get<1>(wkf->second) : units; + auto wkf_name = std::get<0>(wkf->second); + LOG("CsvProvider has well-known name '" + wkf_name + "' for variable '" + var_name + "' with units '" + units + "'", LogLevel::DEBUG); available_forcings.push_back(var_name); // Allow lookup by non-canonical name available_forcings_units[var_name] = units; // Allow lookup of units by non-canonical name - var_name = std::get<0>(wkf->second); // Use the CSDMS name from here on + var_name = wkf_name; // Use the CSDMS name from here on } forcing_vectors[var_name] = {}; diff --git a/test/forcing/CsvPerFeatureForcingProvider_Test.cpp b/test/forcing/CsvPerFeatureForcingProvider_Test.cpp index 9d292f1f08..774b3e7633 100644 --- a/test/forcing/CsvPerFeatureForcingProvider_Test.cpp +++ b/test/forcing/CsvPerFeatureForcingProvider_Test.cpp @@ -94,24 +94,24 @@ TEST_F(CsvPerFeatureForcingProviderTest, TestForcingDataRead) time_t t = begin+(i*3600); std::cerr << std::ctime(&t) << std::endl; - current_precipitation = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, ""), data_access::SUM); + current_precipitation = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, "mm/s"), data_access::SUM); EXPECT_NEAR(current_precipitation, 7.9999999999999996e-07, 0.00000005); - double temp_k = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_SURFACE_TEMP, begin+(i*3600), 3600, ""), data_access::MEAN); + double temp_k = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_SURFACE_TEMP, begin+(i*3600), 3600, "K"), data_access::MEAN); EXPECT_NEAR(temp_k, 286.9, 0.00001); int current_epoch; i = 387; - current_precipitation = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, ""), data_access::SUM); + current_precipitation = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, "mm/s"), data_access::SUM); EXPECT_NEAR(current_precipitation, 6.9999999999999996e-07, 0.00000005); //Check exceeding the forcing range to retrieve the last forcing precipation rate i = 388; - current_precipitation = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, ""), data_access::SUM); + current_precipitation = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, "mm/s"), data_access::SUM); EXPECT_NEAR(current_precipitation, 6.9999999999999996e-07, 0.00000005); } @@ -127,18 +127,18 @@ TEST_F(CsvPerFeatureForcingProviderTest, TestForcingDataReadAltFormat) time_t t = begin+(i*3600); std::cerr << std::ctime(&t) << std::endl; - current_precipitation = Forcing_Object_2->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, ""), data_access::SUM); + current_precipitation = Forcing_Object_2->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, "mm/s"), data_access::SUM); EXPECT_NEAR(current_precipitation, 0.00032685, 0.00000001); - double temp_k = Forcing_Object_2->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_SURFACE_TEMP, begin+(i*3600), 3600, ""), data_access::MEAN); + double temp_k = Forcing_Object_2->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_SURFACE_TEMP, begin+(i*3600), 3600, "K"), data_access::MEAN); EXPECT_NEAR(temp_k, 265.77, 0.00001); int current_epoch; i = 34; - current_precipitation = Forcing_Object_2->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, ""), data_access::SUM); + current_precipitation = Forcing_Object_2->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, "mm/s"), data_access::SUM); EXPECT_NEAR(current_precipitation, 0.00013539, 0.00000001); @@ -156,7 +156,7 @@ TEST_F(CsvPerFeatureForcingProviderTest, TestForcingDataUnitConversion) time_t t = begin+(i*3600); std::cerr << std::ctime(&t) << std::endl; - current_precipitation = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, ""), data_access::SUM); + current_precipitation = Forcing_Object->get_value(CatchmentAggrDataSelector("", CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE, begin+(i*3600), 3600, "mm/s"), data_access::SUM); EXPECT_NEAR(current_precipitation, 7.9999999999999996e-07, 0.00000005); @@ -188,9 +188,14 @@ TEST_F(CsvPerFeatureForcingProviderTest, TestForcingUnitHeaderParsing) {"U2D", "m s-1", "cm s-1"}, {"V2D", "m/s", "cm s-1"}, {"TEST", "kg", "g"}, + // Disable the cases where units won't be parsed and + // conversion was previously not expected. The non-conversion + // is now being treated as an error, per NGWPC-7604 +#if 0 {"PSFC[Pa)", "Pa", "bar"}, {"SWDOWN(W m-2]", "W m-2", "langley"}, {"LWDOWN [alt]", "W m-2", "langley"} +#endif }; for (auto ite = expected.begin(); ite != expected.end(); ite++) { From cff264702ea306ec010c83b04bd4148831768ac8 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 16 Sep 2025 08:37:40 -0700 Subject: [PATCH 28/35] Update cfe submod ref for udunits2 fix --- extern/cfe/cfe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/cfe/cfe b/extern/cfe/cfe index b941a5bc54..8769f4885b 160000 --- a/extern/cfe/cfe +++ b/extern/cfe/cfe @@ -1 +1 @@ -Subproject commit b941a5bc544152464e1afc879df70285de9ac7c7 +Subproject commit 8769f4885ba1fdf56aea405b0adbd9de2ce18003 From 2a89495f12efc08d80c6ea7d3506fb6aa7756bc6 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 16 Sep 2025 10:19:17 -0700 Subject: [PATCH 29/35] Remove catchment_id from string construction. Not available in method. --- src/forcing/NetCDFPerFeatureDataProvider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forcing/NetCDFPerFeatureDataProvider.cpp b/src/forcing/NetCDFPerFeatureDataProvider.cpp index 2a202a9247..afa4c030a1 100644 --- a/src/forcing/NetCDFPerFeatureDataProvider.cpp +++ b/src/forcing/NetCDFPerFeatureDataProvider.cpp @@ -420,7 +420,7 @@ double NetCDFPerFeatureDataProvider::get_value(const CatchmentAggrDataSelector& catch (const std::runtime_error& e) { data_access::unit_conversion_exception uce(e.what()); - uce.provider_model_name = "NetCDFPerFeatureDataProvider " + catchment_id; + uce.provider_model_name = "NetCDFPerFeatureDataProvider"; uce.provider_bmi_var_name = selector.get_variable_name(); uce.provider_units = native_units; uce.unconverted_values.push_back(rvalue); From faae425438b9dcc1b065c66925f769cac921124b Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Tue, 16 Sep 2025 10:22:24 -0700 Subject: [PATCH 30/35] NetCDF Provider: Reformat constructor initialization --- src/forcing/NetCDFPerFeatureDataProvider.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/forcing/NetCDFPerFeatureDataProvider.cpp b/src/forcing/NetCDFPerFeatureDataProvider.cpp index afa4c030a1..1c52c07552 100644 --- a/src/forcing/NetCDFPerFeatureDataProvider.cpp +++ b/src/forcing/NetCDFPerFeatureDataProvider.cpp @@ -38,9 +38,11 @@ void NetCDFPerFeatureDataProvider::cleanup_shared_providers() shared_providers.clear(); } -NetCDFPerFeatureDataProvider::NetCDFPerFeatureDataProvider(std::string input_path, time_t sim_start, time_t sim_end, utils::StreamHandler log_s) : log_stream(log_s), value_cache(20), - sim_start_date_time_epoch(sim_start), - sim_end_date_time_epoch(sim_end) +NetCDFPerFeatureDataProvider::NetCDFPerFeatureDataProvider(std::string input_path, time_t sim_start, time_t sim_end, utils::StreamHandler log_s) + : log_stream(log_s) + , value_cache(20) + , sim_start_date_time_epoch(sim_start) + , sim_end_date_time_epoch(sim_end) { //size_t sizep = 1073741824, nelemsp = 202481; //float preemptionp = 0.75; From 3d0b897b95acade45aaecac5c0db63f4b4b4a818 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Tue, 16 Sep 2025 10:22:46 -0700 Subject: [PATCH 31/35] NetCDF Provider: Store file path for later use in logging and error reporting --- include/forcing/NetCDFPerFeatureDataProvider.hpp | 2 +- src/forcing/NetCDFPerFeatureDataProvider.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/forcing/NetCDFPerFeatureDataProvider.hpp b/include/forcing/NetCDFPerFeatureDataProvider.hpp index 92ef771fff..b0b782f3f9 100644 --- a/include/forcing/NetCDFPerFeatureDataProvider.hpp +++ b/include/forcing/NetCDFPerFeatureDataProvider.hpp @@ -128,7 +128,7 @@ namespace data_access TimeUnit time_unit; // the unit that time was stored as in the file double time_stride; // the amount of time between stored time values utils::StreamHandler log_stream; - + std::string file_path; std::shared_ptr nc_file; diff --git a/src/forcing/NetCDFPerFeatureDataProvider.cpp b/src/forcing/NetCDFPerFeatureDataProvider.cpp index 1c52c07552..45ce2de210 100644 --- a/src/forcing/NetCDFPerFeatureDataProvider.cpp +++ b/src/forcing/NetCDFPerFeatureDataProvider.cpp @@ -40,6 +40,7 @@ void NetCDFPerFeatureDataProvider::cleanup_shared_providers() NetCDFPerFeatureDataProvider::NetCDFPerFeatureDataProvider(std::string input_path, time_t sim_start, time_t sim_end, utils::StreamHandler log_s) : log_stream(log_s) + , file_path(input_path) , value_cache(20) , sim_start_date_time_epoch(sim_start) , sim_end_date_time_epoch(sim_end) From 54eca657c6592acf5e91d5905ee87c45e6042afa Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Tue, 16 Sep 2025 10:35:20 -0700 Subject: [PATCH 32/35] NetCDF Provider: Report file path in unit conversion exception --- src/forcing/NetCDFPerFeatureDataProvider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forcing/NetCDFPerFeatureDataProvider.cpp b/src/forcing/NetCDFPerFeatureDataProvider.cpp index 45ce2de210..f65784ff5c 100644 --- a/src/forcing/NetCDFPerFeatureDataProvider.cpp +++ b/src/forcing/NetCDFPerFeatureDataProvider.cpp @@ -423,7 +423,7 @@ double NetCDFPerFeatureDataProvider::get_value(const CatchmentAggrDataSelector& catch (const std::runtime_error& e) { data_access::unit_conversion_exception uce(e.what()); - uce.provider_model_name = "NetCDFPerFeatureDataProvider"; + uce.provider_model_name = "NetCDFPerFeatureDataProvider(" + file_path + ")"; uce.provider_bmi_var_name = selector.get_variable_name(); uce.provider_units = native_units; uce.unconverted_values.push_back(rvalue); From e57c8c943b60c3333a5f7ba067a859cb464e2ca7 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Thu, 18 Sep 2025 11:24:34 -0700 Subject: [PATCH 33/35] Add WARNING and INFO mgs to clarify routing module selection --- src/routing/Routing_Py_Adapter.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/routing/Routing_Py_Adapter.cpp b/src/routing/Routing_Py_Adapter.cpp index 4dbb87520d..6569a37e1a 100644 --- a/src/routing/Routing_Py_Adapter.cpp +++ b/src/routing/Routing_Py_Adapter.cpp @@ -19,17 +19,20 @@ Routing_Py_Adapter::Routing_Py_Adapter(std::string t_route_config_file_with_path //Import ngen_main. Will throw error if module isn't available //in the embedded interpreters PYTHON_PATH try { + LOG("Trying t-route module ngen_routing.ngen_main.", LogLevel::INFO); this->t_route_module = utils::ngenPy::InterpreterUtil::getPyModule("ngen_routing.ngen_main"); - LOG("Legacy t-route module detected; use of this version is deprecated!", LogLevel::WARNING); + LOG("Routing module ngen_routing.ngen_main detected and used. Use of this version is deprecated!", LogLevel::WARNING); } catch (const pybind11::error_already_set& e){ try { + LOG("Module ngen_routing.ngen_main not found; Trying different t-route module", LogLevel::WARNING); + LOG("Trying t-route module nwm_routing.__main__.", LogLevel::INFO); // The legacy module has a `nwm_routing.__main__`, so we have to try this one second! this->t_route_module = utils::ngenPy::InterpreterUtil::getPyModule("nwm_routing.__main__"); - LOG("Legacy t-route module nwm_routing.__main__ detected and used.", LogLevel::DEBUG); + LOG("Routing module nwm_routing.__main__ detected and used.", LogLevel::INFO); } catch (const pybind11::error_already_set& e){ - LOG("Unable to import a supported routing module.", LogLevel::FATAL); + LOG("Unable to import a supported routing module.", LogLevel::SEVERE); throw e; } } From 32a353026e12256359a53d3344b1daf67708708c Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Thu, 18 Sep 2025 14:26:19 -0700 Subject: [PATCH 34/35] Removed deprecated code per OWP --- src/routing/Routing_Py_Adapter.cpp | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/routing/Routing_Py_Adapter.cpp b/src/routing/Routing_Py_Adapter.cpp index 6569a37e1a..f003aa4893 100644 --- a/src/routing/Routing_Py_Adapter.cpp +++ b/src/routing/Routing_Py_Adapter.cpp @@ -16,25 +16,15 @@ Routing_Py_Adapter::Routing_Py_Adapter(std::string t_route_config_file_with_path t_route_config_path(t_route_config_file_with_path){ //hold a reference to the interpreter, ensures an interpreter exists as long as the reference is held interpreter = utils::ngenPy::InterpreterUtil::getInstance(); - //Import ngen_main. Will throw error if module isn't available - //in the embedded interpreters PYTHON_PATH + // Import the routing module. An error will be thrown if module isn't available + // in the embedded interpreters PYTHON_PATH try { - LOG("Trying t-route module ngen_routing.ngen_main.", LogLevel::INFO); - this->t_route_module = utils::ngenPy::InterpreterUtil::getPyModule("ngen_routing.ngen_main"); - LOG("Routing module ngen_routing.ngen_main detected and used. Use of this version is deprecated!", LogLevel::WARNING); + this->t_route_module = utils::ngenPy::InterpreterUtil::getPyModule("nwm_routing.__main__"); + LOG("Routing module nwm_routing.__main__ detected and used.", LogLevel::INFO); } - catch (const pybind11::error_already_set& e){ - try { - LOG("Module ngen_routing.ngen_main not found; Trying different t-route module", LogLevel::WARNING); - LOG("Trying t-route module nwm_routing.__main__.", LogLevel::INFO); - // The legacy module has a `nwm_routing.__main__`, so we have to try this one second! - this->t_route_module = utils::ngenPy::InterpreterUtil::getPyModule("nwm_routing.__main__"); - LOG("Routing module nwm_routing.__main__ detected and used.", LogLevel::INFO); - } - catch (const pybind11::error_already_set& e){ - LOG("Unable to import a supported routing module.", LogLevel::SEVERE); - throw e; - } + catch (const pybind11::error_already_set& e) { + LOG("Unable to import a supported routing module.", LogLevel::FATAL); + throw e; } } From f1efab03724f25ac55313239018c40133359dad7 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Mon, 22 Sep 2025 12:21:15 -0700 Subject: [PATCH 35/35] Updates submod refs to RC for noah, sacsma, snow17, ueb --- extern/noah-owp-modular/noah-owp-modular | 2 +- extern/sac-sma/sac-sma | 2 +- extern/snow17 | 2 +- extern/ueb-bmi | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/noah-owp-modular/noah-owp-modular b/extern/noah-owp-modular/noah-owp-modular index 3757b2889d..6def57e360 160000 --- a/extern/noah-owp-modular/noah-owp-modular +++ b/extern/noah-owp-modular/noah-owp-modular @@ -1 +1 @@ -Subproject commit 3757b2889da37ebcd3ade617880705eb91176a54 +Subproject commit 6def57e360f5c27c4453b4bfd61be275c93c2c73 diff --git a/extern/sac-sma/sac-sma b/extern/sac-sma/sac-sma index c08f021c12..5522debfd2 160000 --- a/extern/sac-sma/sac-sma +++ b/extern/sac-sma/sac-sma @@ -1 +1 @@ -Subproject commit c08f021c121e587272fe520adca8fb5074e46329 +Subproject commit 5522debfd216ff9aedd9e4b457e565c9f0266bd7 diff --git a/extern/snow17 b/extern/snow17 index 0087c515a7..989758b4c4 160000 --- a/extern/snow17 +++ b/extern/snow17 @@ -1 +1 @@ -Subproject commit 0087c515a798975a9073b8a0283b577a166e795d +Subproject commit 989758b4c4157a06a10be04632cf2fc77a256383 diff --git a/extern/ueb-bmi b/extern/ueb-bmi index 55177c55a1..0e95f01580 160000 --- a/extern/ueb-bmi +++ b/extern/ueb-bmi @@ -1 +1 @@ -Subproject commit 55177c55a18f96ee8e1b0bfdab1bef1fde000898 +Subproject commit 0e95f015805dfac3cfbfbaf61428179b54108651