From a912641c85f93ae9bc746e12067c2181825ad33a Mon Sep 17 00:00:00 2001 From: kdorheim Date: Mon, 27 Oct 2025 15:13:13 -0400 Subject: [PATCH 1/8] use the v35 data in leeyabot reporting --- scripts/leeyabot.Rmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/leeyabot.Rmd b/scripts/leeyabot.Rmd index dd617380a..1ad9addbb 100644 --- a/scripts/leeyabot.Rmd +++ b/scripts/leeyabot.Rmd @@ -140,14 +140,14 @@ oldnew_commit <- unique(oldnew_data$commit) # Read in the comparison data associated with the official releases minted on Zenodo -read.csv("https://zenodo.org/records/13286176/files/leeyabot-data.csv?download=1") %>% +read.csv("https://zenodo.org/records/17458844/files/leeyabot-data.csv?download=1") %>% filter(variable %in% vars) -> zenodo_data #TODO eventually we will probably need the ability to determine what the most recent # release version is since the zenodo archive might include results from multiple zenodo_data %>% - filter(version == "3.2.0") -> + filter(version == "3.5.0") -> release_data # Save oldnew version and commit tag From d9fb1e67eb19f6d3bd2a4ef69336f0b34e1d6e78 Mon Sep 17 00:00:00 2001 From: "Kalyn R. Dorheim" Date: Fri, 12 Dec 2025 06:10:05 -0500 Subject: [PATCH 2/8] Fix RH CH4 returned by csv output visitor to be from permafrost (#798) --- src/csv_outputstream_visitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csv_outputstream_visitor.cpp b/src/csv_outputstream_visitor.cpp index 25d05afdd..2b736ecca 100644 --- a/src/csv_outputstream_visitor.cpp +++ b/src/csv_outputstream_visitor.cpp @@ -175,7 +175,7 @@ void CSVOutputStreamVisitor::visit(SimpleNbox *c) { STREAM_UNITVAL(csvFile, c, D_RH, c->final_rh[SNBOX_DEFAULT_BIOME]); STREAM_UNITVAL(csvFile, c, D_RH_DETRITUS, c->final_rh_detritus[SNBOX_DEFAULT_BIOME]); STREAM_UNITVAL(csvFile, c, D_RH_SOIL, c->final_rh_soil[SNBOX_DEFAULT_BIOME]); - STREAM_UNITVAL(csvFile, c, D_RH_CH4, c->final_rh[SNBOX_DEFAULT_BIOME]); + STREAM_UNITVAL(csvFile, c, D_RH_CH4, c->RH_ch4[SNBOX_DEFAULT_BIOME]); STREAM_MESSAGE_DATE(csvFile, c, D_CO2_CONC, current_date); STREAM_MESSAGE(csvFile, c, D_ATMOSPHERIC_CO2); STREAM_MESSAGE(csvFile, c, D_ATMOSPHERIC_C_RESIDUAL); From f856e98abe31fc5242b22e6ef307976c169741e9 Mon Sep 17 00:00:00 2001 From: kdorheim Date: Tue, 30 Dec 2025 22:01:22 -0500 Subject: [PATCH 3/8] precision fix --- src/csv_outputstream_visitor.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/csv_outputstream_visitor.cpp b/src/csv_outputstream_visitor.cpp index 2b736ecca..87ce7ebb5 100644 --- a/src/csv_outputstream_visitor.cpp +++ b/src/csv_outputstream_visitor.cpp @@ -146,7 +146,7 @@ void CSVOutputStreamVisitor::visit(Core *c) { void CSVOutputStreamVisitor::visit(ForcingComponent *c) { if (!core->outputEnabled(c->getComponentName())) return; - streamsize oldPrecision = csvFile.precision(4); + csvFile.precision(15); if (c->currentYear < c->baseyear) return; @@ -158,7 +158,6 @@ void CSVOutputStreamVisitor::visit(ForcingComponent *c) { STREAM_UNITVAL(csvFile, c, f.first, f.second); } - csvFile.precision(oldPrecision); } //------------------------------------------------------------------------------ From 8f61403d19f557b8f1eda1398adee76a6344ecd5 Mon Sep 17 00:00:00 2001 From: kdorheim Date: Wed, 31 Dec 2025 08:42:49 -0500 Subject: [PATCH 4/8] address inline TODO for outputstream_visitor --- src/csv_outputstream_visitor.cpp | 57 ++++++++++---------------------- 1 file changed, 17 insertions(+), 40 deletions(-) diff --git a/src/csv_outputstream_visitor.cpp b/src/csv_outputstream_visitor.cpp index 87ce7ebb5..1e6a8f57e 100644 --- a/src/csv_outputstream_visitor.cpp +++ b/src/csv_outputstream_visitor.cpp @@ -109,18 +109,6 @@ void CSVOutputStreamVisitor::visit(Core *c) { core = c; } -// TODO: consolidate these macros into the two MESSAGE ones, -// and shift string literals to D_xxxx definitions - -// Macro to send a variable with associated unitval units to some output stream -// Takes s (stream), c (component), xname (variable name), x (output variable) -#define STREAM_UNITVAL(s, c, xname, x) \ - { \ - s << linestamp() << c->getComponentName() << DELIMITER << xname \ - << DELIMITER << x.value(x.units()) << DELIMITER << x.unitsName() \ - << std::endl; \ - } - // Macro to send a variable with associated unitval units to some output stream // This uses new sendMessage interface in imodel_component // Takes s (stream), c (component), xname (variable name) @@ -155,7 +143,7 @@ void CSVOutputStreamVisitor::visit(ForcingComponent *c) { // Walk through the forcings map, outputting everything for (auto f : forcings) { - STREAM_UNITVAL(csvFile, c, f.first, f.second); + STREAM_MESSAGE_DATE(csvFile, c, f.first, current_date); } } @@ -170,11 +158,11 @@ void CSVOutputStreamVisitor::visit(SimpleNbox *c) { // Note if there are multiple biomes, these values will be totals, summed // across all biomes STREAM_MESSAGE(csvFile, c, D_NBP); - STREAM_UNITVAL(csvFile, c, D_NPP, c->final_npp[SNBOX_DEFAULT_BIOME]); - STREAM_UNITVAL(csvFile, c, D_RH, c->final_rh[SNBOX_DEFAULT_BIOME]); - STREAM_UNITVAL(csvFile, c, D_RH_DETRITUS, c->final_rh_detritus[SNBOX_DEFAULT_BIOME]); - STREAM_UNITVAL(csvFile, c, D_RH_SOIL, c->final_rh_soil[SNBOX_DEFAULT_BIOME]); - STREAM_UNITVAL(csvFile, c, D_RH_CH4, c->RH_ch4[SNBOX_DEFAULT_BIOME]); + STREAM_MESSAGE(csvFile, c, D_NPP); + STREAM_MESSAGE(csvFile, c, D_RH); + STREAM_MESSAGE(csvFile, c, D_RH_DETRITUS); + STREAM_MESSAGE(csvFile, c, D_RH_SOIL); + STREAM_MESSAGE(csvFile, c, D_RH_CH4); STREAM_MESSAGE_DATE(csvFile, c, D_CO2_CONC, current_date); STREAM_MESSAGE(csvFile, c, D_ATMOSPHERIC_CO2); STREAM_MESSAGE(csvFile, c, D_ATMOSPHERIC_C_RESIDUAL); @@ -191,28 +179,17 @@ void CSVOutputStreamVisitor::visit(SimpleNbox *c) { SimpleNbox::fluxpool_stringmap::const_iterator it; for (auto b : c->veg_c) { std::string biome = b.first; - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_NPP, - c->final_npp[biome]); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_RH, - c->final_rh[biome]); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_RH_CH4, - c->RH_ch4[biome]); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_VEGC, - c->veg_c[biome]); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_DETRITUSC, - c->detritus_c[biome]); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_SOILC, - c->soil_c[biome]); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_PERMAFROSTC, - c->permafrost_c[biome]); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_THAWEDPC, - c->thawed_permafrost_c[biome]); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_F_FROZEN, - unitval(c->f_frozen[biome], U_UNITLESS)); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_TEMPFERTD, - unitval(c->tempfertd[biome], U_UNITLESS)); - STREAM_UNITVAL(csvFile, c, biome + SNBOX_PARSECHAR + D_TEMPFERTS, - unitval(c->tempferts[biome], U_UNITLESS)); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_NPP); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_RH); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_RH_CH4); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_VEGC); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_DETRITUSC); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_SOILC); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_PERMAFROSTC); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_THAWEDPC); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_F_FROZEN); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_TEMPFERTD); + STREAM_MESSAGE(csvFile, c, biome + SNBOX_PARSECHAR + D_TEMPFERTS); } } } From 1b4837e7d3b5d52c01016e1a3accfb3fb20d6a05 Mon Sep 17 00:00:00 2001 From: kdorheim Date: Mon, 12 Jan 2026 16:42:26 -0500 Subject: [PATCH 5/8] set csv precision level in output stream visitor class constructor --- src/csv_outputstream_visitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csv_outputstream_visitor.cpp b/src/csv_outputstream_visitor.cpp index 1e6a8f57e..b4ef32ba9 100644 --- a/src/csv_outputstream_visitor.cpp +++ b/src/csv_outputstream_visitor.cpp @@ -74,6 +74,7 @@ CSVOutputStreamVisitor::CSVOutputStreamVisitor(ostream &outputStream, current_date = 0; datestring = ""; spinupstring = ""; + csvFile.precision(15); } //------------------------------------------------------------------------------ @@ -134,7 +135,6 @@ void CSVOutputStreamVisitor::visit(Core *c) { void CSVOutputStreamVisitor::visit(ForcingComponent *c) { if (!core->outputEnabled(c->getComponentName())) return; - csvFile.precision(15); if (c->currentYear < c->baseyear) return; From e3f73f2c34f053766761c0fd89c695b56ac292bc Mon Sep 17 00:00:00 2001 From: kdorheim Date: Mon, 2 Feb 2026 12:33:28 -0500 Subject: [PATCH 6/8] use explicit csv parser for header --- inst/include/csv_table_reader.hpp | 5 ++- src/csv_table_reader.cpp | 55 +++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/inst/include/csv_table_reader.hpp b/inst/include/csv_table_reader.hpp index b21d89aa5..786495599 100644 --- a/inst/include/csv_table_reader.hpp +++ b/inst/include/csv_table_reader.hpp @@ -56,8 +56,11 @@ class CSVTableReader { //! Current line (that has just been read) int lineNum; - // Helper function to find next non-commented line + //! Helper function to find next non-commented line std::string csv_getline(); + + //! Helper function that reads the header row and returns all column names as a vector + std::vector read_header(); }; } // namespace Hector diff --git a/src/csv_table_reader.cpp b/src/csv_table_reader.cpp index 2d818dcd6..c784571cd 100644 --- a/src/csv_table_reader.cpp +++ b/src/csv_table_reader.cpp @@ -22,6 +22,7 @@ #pragma clang diagnostic pop #include +#include #include #include @@ -32,6 +33,8 @@ namespace Hector { using namespace std; +using CsvTok = boost::tokenizer>; + //------------------------------------------------------------------------------ /*! \brief Constructor @@ -91,6 +94,33 @@ string CSVTableReader::csv_getline() { return s; } +//------------------------------------------------------------------------------ +/*! \brief Helper function + * + * Read in and format the header of a hector input csv table. + */ +std::vector Hector::CSVTableReader::read_header() { + using namespace boost; + + // Skip all the comments at the start of the hector CSV input table + std::string line = csv_getline(); + H_ASSERT(!line.empty(), "Header line is empty"); + + // Parse variable names from the CSV + std::vector headerRow; + CsvTok tok(line); + headerRow.assign(tok.begin(), tok.end()); + + // Tim any whitespace included in the header + for (auto& colName : headerRow) { + trim(colName); + } + + return headerRow; +} + + + //------------------------------------------------------------------------------ /*! \brief Process the CSV file looking for the given varName and route the data * into the core. @@ -129,21 +159,18 @@ void CSVTableReader::process(Core *core, const string &componentName, // read the header line and attempt to find varName. The first column is // not considered because that should be the index column. - line = csv_getline(); - H_ASSERT(!line.empty(), "line empty"); - split(row, line, is_any_of(",")); - // const int headerRowSize = row.size(); - for (size_t col = 1; col < row.size() && columnIndex == 0; ++col) { - // ignore white space before comparing variable names - trim(row[col]); - if (row[col] == varName) { - columnIndex = col; + row = read_header(); + + for (size_t col = 1; col < row.size(); ++col) { + if (row[col] == varName) { + columnIndex = col; + break; + } + } + if (columnIndex == 0) { + H_THROW("Could not find a column for " + varName + + " in " + fileName); } - } - if (columnIndex == 0) { - H_THROW("Could not find a column for " + varName + " in " + fileName + - " header=" + line); - } // we are all set to process the table // note that getline sets the fail bit when it hits eof which is not what From 5a9fa94cb57ce4e2e24e0c0ae03c474b8ffcac2a Mon Sep 17 00:00:00 2001 From: kdorheim Date: Thu, 12 Mar 2026 14:03:05 -0400 Subject: [PATCH 7/8] increase the size of the earth c pool to work with idealized runs --- src/simpleNbox.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/simpleNbox.cpp b/src/simpleNbox.cpp index 91b9481b8..96c039fbf 100644 --- a/src/simpleNbox.cpp +++ b/src/simpleNbox.cpp @@ -69,9 +69,9 @@ SimpleNbox::SimpleNbox() : CarbonCycleModel(8), masstot(0.0) { // earth_c keeps track of how much fossil C is pulled out // so that we can do a mass-balance check throughout the run // 2020-02-05 With the introduction of non-negative 'fluxpool' class - // we can't start earth_c at zero. Value of 5500 is set to avoid - // overdrawing in RCP 8.5 - earth_c.set(5500, U_PGC, false, D_EARTHC); + // we can't start earth_c at zero. Value of 6400 is set to avoid + // overdrawing in esm-flat20 + earth_c.set(6400, U_PGC, false, D_EARTHC); // We keep a running total of LUC emissions from (and uptake to) vegetation // This is used in slowparameval() to calculate npp_luc_adjust From 8a7d19670b37a0f293b9cdbb5a928117b4a66fb8 Mon Sep 17 00:00:00 2001 From: kdorheim Date: Fri, 13 Mar 2026 11:19:56 -0400 Subject: [PATCH 8/8] remove atmos C pool --- data-raw/input_params.csv | 1 - data/inputstable.rda | Bin 4078 -> 4072 bytes inst/input/hector_picontrol.ini | 1 - inst/input/hector_ssp119.ini | 3 +-- inst/input/hector_ssp126.ini | 3 +-- inst/input/hector_ssp245.ini | 3 +-- inst/input/hector_ssp370.ini | 3 +-- inst/input/hector_ssp434.ini | 3 +-- inst/input/hector_ssp460.ini | 3 +-- inst/input/hector_ssp534-over.ini | 3 +-- inst/input/hector_ssp585.ini | 3 +-- src/simpleNbox.cpp | 12 +----------- tests/testthat/input/luc_pulse.ini | 1 - 13 files changed, 9 insertions(+), 30 deletions(-) diff --git a/data-raw/input_params.csv b/data-raw/input_params.csv index 26bfd6354..c32367e32 100644 --- a/data-raw/input_params.csv +++ b/data-raw/input_params.csv @@ -13,7 +13,6 @@ ocean,twi,n,n,y,1.25E+07,m3 s-1,Ocean warm-intermediate exchange ocean,tid,n,n,y,2.00E+08,m3 s-1,Ocean intermediate-deep exchange ocean,preind_surface_c,n,n,n,900,Pg C,Initial surface ocean carbon ocean,preind_interdeep_c,n,n,n,37100,Pg C,Initial intermediate + deep ocean carbon -simpleNbox,atmos_co2,n,n,y,588.071,Pg C,Initial atmospheric CO2 simpleNbox,C0,n,n,y,277.15,Pg C,Initial atmospheric C pool simpleNbox,pf.veg_c,y,n,n,55,Pg C,Initial vegetation C pool simpleNbox,nonpf.veg_c,y,n,n,495,Pg C,Initial vegetation C pool diff --git a/data/inputstable.rda b/data/inputstable.rda index c7df81fca22f8aea1166cccfd156f1c4c690e680..454460acaab7a45a8d8eea77613e2120eb25c63b 100644 GIT binary patch literal 4072 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