From 930fa8faef7b0c4b7a3a953f6a1dec673ec9c780 Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Fri, 22 Sep 2023 00:01:35 -0700 Subject: [PATCH 01/24] Prepare development for v6.0.3 --- DESCRIPTION | 2 +- NEWS.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4e009134..b477b3c0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Package: rSOILWAT2 -Version: 6.0.2 +Version: 6.0.3-9000 Title: An Ecohydrological Ecosystem-Scale Water Balance Simulation Model Description: Access to the C-based SOILWAT2 v7.1.0 and functionality for SQLite-database of weather data. diff --git a/NEWS.md b/NEWS.md index f8ed79ea..50032bc3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,6 @@ +# rSOILWAT2 v6.0.3 + + # rSOILWAT2 v6.0.2 * This version produces the same output as the previous version. * Update `SOILWAT2` to v7.1.0 which prepares for thread-safety and reentrancy From 358271044a0bfd14248e81c532cbde5b60315c19 Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Fri, 22 Sep 2023 00:02:26 -0700 Subject: [PATCH 02/24] Update submodule --- .gitmodules | 2 +- src/SOILWAT2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 39058bbd..8486a60c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/SOILWAT2"] path = src/SOILWAT2 url = https://github.com/DrylandEcology/SOILWAT2 - branch = master + branch = feature_ErrorHandling diff --git a/src/SOILWAT2 b/src/SOILWAT2 index 2c5900db..0b0d97a8 160000 --- a/src/SOILWAT2 +++ b/src/SOILWAT2 @@ -1 +1 @@ -Subproject commit 2c5900dbecd7140599609dd7acbf47de82c5a1c4 +Subproject commit 0b0d97a82e0c4e6c6b8eb66e22e91814b3ff1d6a From 86730f8499178f7d3c40aa7ab3b3d68c1a55a7ae Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Fri, 22 Sep 2023 00:22:45 -0700 Subject: [PATCH 03/24] Use SOILWAT2's new error-handling - No longer use LOGFATAL or LOGNOTE - Use new functions `sw_write_logs()` and `sw_check_exit()` to write logs and check if the program needs to crash with an error - Initialize pointers and logs at the beginning of the program - Update function calls --- src/SW_R_lib.c | 23 ++++++++++++++++++++--- src/rSW_Carbon.c | 2 +- src/rSW_Markov.c | 4 ++-- src/rSW_Model.c | 10 +++++----- src/rSW_Output.c | 9 ++++++++- src/rSW_Site.c | 12 ++++++------ src/rSW_SoilWater.c | 8 +++++--- src/rSW_VegEstab.c | 8 ++++---- src/rSW_VegProd.c | 16 +++++++++++++--- src/rSW_Weather.c | 14 +++++++++----- 10 files changed, 73 insertions(+), 33 deletions(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index 94fded68..c8e1907a 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -135,7 +135,7 @@ void setupSOILWAT2(SEXP inputOptions) { if (argc > 7) { // fatal condition because argv is hard-coded to be of length 7; increase size of // argv if more command-line options are added to SOILWAT2 in the future - sw_error(-1, "length(inputOptions) must be <= 7."); + LogError(&LogInfo, LOGERROR, "length(inputOptions) must be <= 7."); } for (i = 0; i < argc; i++) { argv[i] = (char *) CHAR(STRING_ELT(inputOptions, i)); @@ -145,6 +145,7 @@ void setupSOILWAT2(SEXP inputOptions) { if (debug) swprintf("Set call arguments\n"); #endif + SW_CTL_init_ptrs(&SoilWatAll, PathInfo.InFiles); sw_init_args(argc, argv, &QuietMode, &EchoInits, &PathInfo.InFiles[eFirst], &LogInfo); @@ -166,7 +167,7 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { int debug = 0; #endif - LogInfo.logged = FALSE; + sw_init_logs(NULL, &LogInfo); sw_quiet(quiet); #ifdef RSWDEBUG @@ -301,6 +302,12 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { #endif UNPROTECT(5); + + if(LogInfo.logged) { + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + } + return SW_DataList; } @@ -318,7 +325,7 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { int debug = 0; #endif - LogInfo.logged = FALSE; + sw_init_logs(NULL, &LogInfo); sw_quiet(quiet); if (isNull(inputData)) { @@ -391,6 +398,11 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { #endif SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + if(LogInfo.logged) { + sw_write_logs(current_sw_quiet, &LogInfo); // Note: `FALSE` is not used + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + } + #ifdef RSWDEBUG if (debug) swprintf(" completed.\n"); #endif @@ -488,6 +500,11 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { PROTECT(res = onGet_WTH_DATA()); UNPROTECT(1); + + if(LogInfo.logged) { + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + } return res; } diff --git a/src/rSW_Carbon.c b/src/rSW_Carbon.c index fa731fe3..cb741655 100644 --- a/src/rSW_Carbon.c +++ b/src/rSW_Carbon.c @@ -164,7 +164,7 @@ void onSet_swCarbon(SEXP object) { // Check that we have enough data if (i - 1 + n_sim > n_input) { - LogError(&LogInfo, LOGFATAL, "CO2ppm object does not contain data for every year"); + LogError(&LogInfo, LOGERROR, "CO2ppm object does not contain data for every year"); } // Copy CO2 concentration values to SOILWAT variable diff --git a/src/rSW_Markov.c b/src/rSW_Markov.c index 667e30f3..ae0a0f66 100644 --- a/src/rSW_Markov.c +++ b/src/rSW_Markov.c @@ -72,7 +72,7 @@ void onSet_MKV(SEXP MKV) { if (!onSet_MKV_prob(MKV_prob) && SoilWatAll.Weather.generateWeatherMethod == 2) { LogError( &LogInfo, - LOGFATAL, + LOGERROR, "Markov weather generator: " "rSOILWAT2 failed to pass `MKV_prob` values to SOILWAT2.\n" ); @@ -81,7 +81,7 @@ void onSet_MKV(SEXP MKV) { if (!onSet_MKV_conv(MKV_conv) && SoilWatAll.Weather.generateWeatherMethod == 2) { LogError( &LogInfo, - LOGFATAL, + LOGERROR, "Markov weather generator: " "rSOILWAT2 failed to pass `MKV_conv` values to SOILWAT2.\n" ); diff --git a/src/rSW_Model.c b/src/rSW_Model.c index 783040b8..9d971b73 100644 --- a/src/rSW_Model.c +++ b/src/rSW_Model.c @@ -106,24 +106,24 @@ void onSet_SW_MDL(SEXP SW_MDL) { MyFileName = PathInfo.InFiles[eModel]; if (!IS_S4_OBJECT(SW_MDL)) { - LogError(&LogInfo, LOGFATAL, "%s: No input.", MyFileName); + LogError(&LogInfo, LOGERROR, "%s: No input.", MyFileName); } PROTECT(StartYear = GET_SLOT(SW_MDL, install("StartYear"))); if (INTEGER(StartYear)[0] < 0) { - LogError(&LogInfo, LOGFATAL, "%s: Negative start year (%d)", MyFileName, INTEGER(StartYear)[0]); + LogError(&LogInfo, LOGERROR, "%s: Negative start year (%d)", MyFileName, INTEGER(StartYear)[0]); } m->startyr = INTEGER(StartYear)[0]; PROTECT(EndYear = GET_SLOT(SW_MDL, install("EndYear"))); if (isNull(EndYear) || INTEGER(EndYear)[0] == NA_INTEGER) { - LogError(&LogInfo, LOGFATAL, "%s: Ending year not found.", MyFileName); + LogError(&LogInfo, LOGERROR, "%s: Ending year not found.", MyFileName); } if (INTEGER(EndYear)[0] < 0) { - LogError(&LogInfo, LOGFATAL, "%s: Negative ending year (%d)", MyFileName, INTEGER(EndYear)[0]); + LogError(&LogInfo, LOGERROR, "%s: Negative ending year (%d)", MyFileName, INTEGER(EndYear)[0]); } m->endyr = INTEGER(EndYear)[0]; if (m->endyr < m->startyr) { - LogError(&LogInfo, LOGFATAL, "%s: Start Year > End Year", MyFileName); + LogError(&LogInfo, LOGERROR, "%s: Start Year > End Year", MyFileName); } PROTECT(StartStart = GET_SLOT(SW_MDL, install("FDOFY"))); diff --git a/src/rSW_Output.c b/src/rSW_Output.c index bfa73fa3..bedebdb1 100644 --- a/src/rSW_Output.c +++ b/src/rSW_Output.c @@ -27,6 +27,7 @@ #include "SOILWAT2/include/SW_Defines.h" #include "SOILWAT2/include/SW_Files.h" #include "SOILWAT2/include/SW_Site.h" +#include "SOILWAT2/include/SW_Main_lib.h" #include "SOILWAT2/include/SW_Output.h" // externs many variables #include "SOILWAT2/include/SW_Output_outarray.h" // for function `SW_OUT_set_nrow` @@ -116,7 +117,7 @@ void onSet_SW_OUT(SEXP OUT) { } if (EchoInits) - _echo_outputs(&SoilWatAll, &LogInfo); + _echo_outputs(&SoilWatAll); UNPROTECT(3); @@ -367,5 +368,11 @@ SEXP onGetOutput(SEXP inputData) { if (debug) swprintf(" ... done. \n"); #endif + if(LogInfo.stopRun) { + // The only message could be an error from this function, + // so no need to use `sw_write_logs()` + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + } + return swOutput_Object; } diff --git a/src/rSW_Site.c b/src/rSW_Site.c index 6e7957ec..de68702b 100644 --- a/src/rSW_Site.c +++ b/src/rSW_Site.c @@ -129,7 +129,7 @@ static void onSet_SW_LYR(SEXP SW_LYR) { if (columns != 12) { LogError( &LogInfo, - LOGFATAL, + LOGERROR, "%s : Too few columns in layers specified (%d).\n", MyFileName, columns ); @@ -167,7 +167,7 @@ static void onSet_SW_LYR(SEXP SW_LYR) { if (lyrno >= MAX_LAYERS) { LogError( &LogInfo, - LOGFATAL, + LOGERROR, "%s : Too many layers specified (%d).\n" "Maximum number of layers is %d\n", MyFileName, lyrno + 1, MAX_LAYERS @@ -218,7 +218,7 @@ static void onSet_SW_SWRCp(SEXP SW_SWRCp) { if (ncols(SW_SWRCp) != SWRC_PARAM_NMAX) { LogError( &LogInfo, - LOGFATAL, + LOGERROR, "%s : Bad number of SWRC parameters %d -- must be %d.\n", MyFileName, ncols(SW_SWRCp), SWRC_PARAM_NMAX ); @@ -228,7 +228,7 @@ static void onSet_SW_SWRCp(SEXP SW_SWRCp) { if (nrows(SW_SWRCp) != SoilWatAll.Site.n_layers) { LogError( &LogInfo, - LOGFATAL, + LOGERROR, "%s : Number of layers with SWRC parameters (%d) " "must match number of soil layers (%d)\n", MyFileName, nrows(SW_SWRCp), SoilWatAll.Site.n_layers @@ -630,7 +630,7 @@ void onSet_SW_SIT(SEXP SW_SIT) { } } if (too_many_regions) { - LogError(&LogInfo, LOGFATAL, "siteparam.in : Number of transpiration regions" + LogError(&LogInfo, LOGERROR, "siteparam.in : Number of transpiration regions" " exceeds maximum allowed (%d > %d)\n", v->n_transp_rgn, MAX_TRANSP_REGIONS); } #ifdef RSWDEBUG @@ -640,7 +640,7 @@ void onSet_SW_SIT(SEXP SW_SIT) { /* check for any discontinuities (reversals) in the transpiration regions */ for (r = 1; r < v->n_transp_rgn; r++) { if (v->_TranspRgnBounds[r - 1] >= v->_TranspRgnBounds[r]) { - LogError(&LogInfo, LOGFATAL, "siteparam.in : Discontinuity/reversal in transpiration regions.\n"); + LogError(&LogInfo, LOGERROR, "siteparam.in : Discontinuity/reversal in transpiration regions.\n"); } } diff --git a/src/rSW_SoilWater.c b/src/rSW_SoilWater.c index f241e0d3..370d7b06 100644 --- a/src/rSW_SoilWater.c +++ b/src/rSW_SoilWater.c @@ -121,7 +121,7 @@ void onSet_SW_SWC(SEXP SWC) { v->hist.method = INTEGER(swcMethod)[0]; if (v->hist.method < 1 || v->hist.method > 2) { - LogError(&LogInfo, LOGFATAL, "swcsetup.in : Invalid swc adjustment method."); + LogError(&LogInfo, LOGERROR, "swcsetup.in : Invalid swc adjustment method."); } v->hist.yr.last = SoilWatAll.Model.endyr; v->hist.yr.total = v->hist.yr.last - v->hist.yr.first + 1; @@ -153,7 +153,8 @@ SEXP onGet_SW_SWC_hists(void) { } SEXP onGet_SW_SWC_hist(TimeInt year) { - sw_error(-1, "'onGet_SW_SWC_hist' is currently not functional.\n"); + LogError(&LogInfo, LOGERROR, + "'onGet_SW_SWC_hist' is currently not functional.\n"); int i, j = 0; SW_SOILWAT *v = &SoilWatAll.SoilWat; @@ -190,7 +191,8 @@ SEXP onGet_SW_SWC_hist(TimeInt year) { } void onSet_SW_SWC_hist(void) { - sw_error(-1, "'onSet_SW_SWC_hist' is currently not functional.\n"); + LogError(&LogInfo, LOGERROR, + "'onSet_SW_SWC_hist' is currently not functional.\n"); int i, j = 0; SW_SOILWAT *v = &SoilWatAll.SoilWat; diff --git a/src/rSW_VegEstab.c b/src/rSW_VegEstab.c index 170ddd6f..e216e4b0 100644 --- a/src/rSW_VegEstab.c +++ b/src/rSW_VegEstab.c @@ -82,7 +82,7 @@ void onSet_SW_VES(SEXP VES) { PROTECT(count = GET_SLOT(VES,install("count"))); if (LOGICAL(use)[0] == FALSE) { - //LogError(logfp, LOGNOTE, "Establishment not used.\n"); + //LogError(logfp, LOGWARN, "Establishment not used.\n"); SoilWatAll.VegEstab.use = FALSE; } else { nSPPS = INTEGER(count)[0]; @@ -97,13 +97,13 @@ void onSet_SW_VES(SEXP VES) { } if (EchoInits) - LogError(&LogInfo, LOGNOTE, "Establishment not used.\n"); + LogError(&LogInfo, LOGWARN, "Establishment not used.\n"); SW_VegEstab_construct(&SoilWatAll.VegEstab, &LogInfo); if (EchoInits) _echo_VegEstab(SoilWatAll.Site.width, SoilWatAll.VegEstab.parms, - SoilWatAll.VegEstab.count, &LogInfo); + SoilWatAll.VegEstab.count); UNPROTECT(2); } @@ -203,7 +203,7 @@ void onSet_SW_VES_spp(SEXP SPP, IntU i) { strcpy(v->sppFileName, CHAR(STRING_ELT(fileName,i)) ); /* check for valid name first */ if (strlen(CHAR(STRING_ELT(Name,i))) > MAX_SPECIESNAMELEN) { - LogError(&LogInfo, LOGFATAL, "Species name too long (> 4 chars).\n\tTry again.\n"); + LogError(&LogInfo, LOGERROR, "Species name too long (> 4 chars).\n\tTry again.\n"); } else { strcpy(v->sppname, CHAR(STRING_ELT(Name,i)) ); } diff --git a/src/rSW_VegProd.c b/src/rSW_VegProd.c index 43037b97..7c7928da 100644 --- a/src/rSW_VegProd.c +++ b/src/rSW_VegProd.c @@ -25,6 +25,7 @@ vegetation production parameter information. #include "SOILWAT2/include/SW_Defines.h" #include "SOILWAT2/include/SW_Files.h" +#include "SOILWAT2/include/SW_Main_lib.h" #include "SOILWAT2/include/SW_VegProd.h" #include "rSW_VegProd.h" @@ -627,8 +628,7 @@ void onSet_SW_VPD(SEXP SW_VPD) { SW_VPD_fix_cover(&SoilWatAll.VegProd, &LogInfo); if (EchoInits) - _echo_VegProd(SoilWatAll.VegProd.veg, SoilWatAll.VegProd.bare_cov, - &LogInfo); + _echo_VegProd(SoilWatAll.VegProd.veg, SoilWatAll.VegProd.bare_cov); UNPROTECT(18); } @@ -641,6 +641,8 @@ SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_mont SEXP Trees_Fraction, SEXP BareGround_Fraction) { double RelAbundanceL0[8], RelAbundanceL1[5], grasses[3]; + LOG_INFO local_LogInfo; + sw_init_logs(LogInfo.logfp, &local_LogInfo); // "final_" in the beginning meaning it's the final R -> conversion double final_MAP_cm = asReal(MAP_mm) / 10, final_MAT_C = asReal(MAT_C), final_MonPPT_cm[MAX_MONTHS], @@ -723,7 +725,7 @@ SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_mont estimatePotNatVegComposition(final_MAT_C, final_MAP_cm, final_MonTemp_C, final_MonPPT_cm, inputValues_D, final_shrubLimit, final_SumGrassesFraction, C4Variables, final_fill_empty_with_BareGround, final_isNorth, final_warn_extrapolation, - final_fix_bareGround, grasses, RelAbundanceL0, RelAbundanceL1, &LogInfo); + final_fix_bareGround, grasses, RelAbundanceL0, RelAbundanceL1, &local_LogInfo); for(index = 0; index < 8; index++) { REAL(final_RelAbundanceL0)[index] = RelAbundanceL0[index]; @@ -741,6 +743,14 @@ SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_mont UNPROTECT(8); + if(local_LogInfo.numWarnings > 0) { + sw_write_logs(FALSE, &local_LogInfo); // Note: `FALSE` is not used + } + + if(local_LogInfo.stopRun) { + sw_check_exit(FALSE, &local_LogInfo); // Note: `FALSE` is not used + } + return res; } diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index 768d9d29..5cbc703c 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -24,6 +24,7 @@ #include "SOILWAT2/include/SW_Model.h" #include "SOILWAT2/include/SW_Markov.h" #include "SOILWAT2/include/SW_Sky.h" +#include "SOILWAT2/include/SW_Main_lib.h" #include "SOILWAT2/include/SW_Weather.h" #include "rSW_Weather.h" @@ -279,7 +280,7 @@ void onSet_SW_WTH_setup(SEXP SW_WTH) { if (SW_Weather.generateWeatherMethod != 2 && SW_Model.startyr < w->yr.first) { LogError( logfp, - LOGFATAL, + LOGERROR, "%s : Model year (%d) starts before weather files (%d)" " and weather generator turned off.\n" " Please synchronize the years or set up the weather generator files", @@ -447,7 +448,7 @@ void onSet_WTH_DATA(void) { SoilWatAll.Weather.startYear = SoilWatAll.Model.startyr; // Allocate new `allHist` (based on current `SW_Weather.n_years`) - allocateAllWeather(&SoilWatAll.Weather); + allocateAllWeather(&SoilWatAll.Weather, &LogInfo); // Equivalent to `readAllWeather()`: @@ -567,7 +568,7 @@ static void rSW2_set_weather_hist( if (nrows(yrWData) != numDaysYear) { LogError( &LogInfo, - LOGFATAL, + LOGERROR, "Weather data (year %d): " "expected %d rows (had %d).\n", year, @@ -606,7 +607,7 @@ static void rSW2_set_weather_hist( if (doy != p_yrWData[doy + numDaysYear * 0] - 1) { LogError( &LogInfo, - LOGFATAL, + LOGERROR, "Weather data (year %d): " "day of year out of range (%d), expected: %d.\n", year, @@ -868,7 +869,8 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, ); // Allocate memory of structs for climate on SOILWAT side - allocateClimateStructs(numYears, &climateOutput, &climateAverages); + allocateClimateStructs(numYears, &climateOutput, &climateAverages, + &LogInfo); // Calculate climate variables calcSiteClimate(allHist, SW_Model->cum_monthdays, SW_Model->days_in_month, @@ -957,6 +959,8 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, } free(allHist); + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + return res; } From d18c6b54a5a1ef2fae1077893484c4ae574e72be Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Mon, 25 Sep 2023 20:16:36 -0700 Subject: [PATCH 04/24] Initialize `LOG_INFO` in `rSW2_processAllWeather()` --- src/SW_R_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index c8e1907a..e53ca83a 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -424,7 +424,7 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { #ifdef RSWDEBUG int debug = 0; #endif - + sw_init_logs(NULL, &LogInfo); #ifdef RSWDEBUG if (debug) swprintf("\n'rSW2_processAllWeather': data preparation: "); From faba9ecffe43d03d3147d60fad415dd0ee9b070b Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Mon, 25 Sep 2023 20:53:07 -0700 Subject: [PATCH 05/24] Handle warn/error logging individually - No longer use `logged` as a way to determine if something was stored - Individually check for logged warnings and logged errors - Update one call to `sw_write_logs()` to use FALSE instead of a global variable --- src/SW_R_lib.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index e53ca83a..c85c6e6e 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -303,8 +303,11 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { UNPROTECT(5); - if(LogInfo.logged) { + if(LogInfo.numWarnings > 0) { sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + } + + if(LogInfo.stopRun) { sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } @@ -398,8 +401,11 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { #endif SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - if(LogInfo.logged) { - sw_write_logs(current_sw_quiet, &LogInfo); // Note: `FALSE` is not used + if(LogInfo.numWarnings > 0) { + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + } + + if(LogInfo.stopRun) { sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } @@ -501,8 +507,11 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { UNPROTECT(1); - if(LogInfo.logged) { + if(LogInfo.numWarnings > 0) { sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + } + + if(LogInfo.stopRun) { sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } return res; From 811f373dffbbab137b02463cbbf76a8369733b3d Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Tue, 26 Sep 2023 23:56:55 -0700 Subject: [PATCH 06/24] Update submodule --- .gitmodules | 2 +- src/SOILWAT2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8486a60c..f24aa691 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/SOILWAT2"] path = src/SOILWAT2 url = https://github.com/DrylandEcology/SOILWAT2 - branch = feature_ErrorHandling + branch = feature_IndivErrorHandling diff --git a/src/SOILWAT2 b/src/SOILWAT2 index 0b0d97a8..f2416fbd 160000 --- a/src/SOILWAT2 +++ b/src/SOILWAT2 @@ -1 +1 @@ -Subproject commit 0b0d97a82e0c4e6c6b8eb66e22e91814b3ff1d6a +Subproject commit f2416fbd8bbf3c97767d4aa0ec98556607a87c37 From bdf5082b6b815c1fa2c353f1be448e2f57ad3ac2 Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Wed, 27 Sep 2023 00:16:24 -0700 Subject: [PATCH 07/24] Add premature returns to C functions - SOILWAT2 no longer immediately writes to file and instead * Writes warnings at the end of the program * Exits functions early and writes an error at the end of the program (including warnings if any were generated) - C functions that have the ability to fail, now check to see if a function call resulted in an error and will then fail - If an error is thrown inside a function, the function will immediately return after the fact - Some functions may use `goto` to skip to a specific part within the function to crash and deallocate memory if needed to prevent a memory leak --- src/SW_R_lib.c | 76 ++++++++++++++++++++++++++++++++++++--------- src/rSW_Carbon.c | 1 + src/rSW_Control.c | 1 + src/rSW_Files.c | 3 ++ src/rSW_Markov.c | 2 ++ src/rSW_Model.c | 5 +++ src/rSW_Output.c | 22 ++++++++++--- src/rSW_Site.c | 9 ++++++ src/rSW_SoilWater.c | 11 +++++++ src/rSW_VegEstab.c | 7 +++++ src/rSW_VegProd.c | 19 +++++++++--- src/rSW_Weather.c | 16 ++++++++-- 12 files changed, 146 insertions(+), 26 deletions(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index c85c6e6e..daee1650 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -136,6 +136,7 @@ void setupSOILWAT2(SEXP inputOptions) { // fatal condition because argv is hard-coded to be of length 7; increase size of // argv if more command-line options are added to SOILWAT2 in the future LogError(&LogInfo, LOGERROR, "length(inputOptions) must be <= 7."); + return; // Exit function prematurely due to error } for (i = 0; i < argc; i++) { argv[i] = (char *) CHAR(STRING_ELT(inputOptions, i)); @@ -148,12 +149,19 @@ void setupSOILWAT2(SEXP inputOptions) { SW_CTL_init_ptrs(&SoilWatAll, PathInfo.InFiles); sw_init_args(argc, argv, &QuietMode, &EchoInits, &PathInfo.InFiles[eFirst], &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } #ifdef RSWDEBUG if (debug) swprintf("Initialize SOILWAT ..."); #endif SW_CTL_setup_model(&SoilWatAll, SoilWatOutputPtrs, &PathInfo, &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } + rSW_CTL_setup_model2(); } @@ -192,12 +200,18 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { #endif SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); + if(LogInfo.stopRun) { + goto report; + } // initialize simulation run (based on user inputs) #ifdef RSWDEBUG if (debug) swprintf(" init simulation run ...\n"); #endif SW_CTL_init_run(&SoilWatAll, &LogInfo); + if(LogInfo.stopRun) { + goto report; + } #ifdef RSWDEBUG if (debug) { @@ -295,7 +309,18 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { #ifdef RSWDEBUG if (debug) swprintf(" > de-allocate most memory; \n"); #endif - SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + + report: { + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + + if(LogInfo.numWarnings > 0) { + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + } + + if(LogInfo.stopRun) { + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + } + } #ifdef RSWDEBUG if (debug) swprintf(" onGetInputDataFromFiles completed.\n"); @@ -303,14 +328,6 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { UNPROTECT(5); - if(LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used - } - - if(LogInfo.stopRun) { - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } - return SW_DataList; } @@ -371,12 +388,18 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { #endif SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); + if(LogInfo.stopRun) { + goto report; + } // initialize simulation run (based on user inputs) #ifdef RSWDEBUG if (debug) swprintf(" init simulation run ..."); #endif SW_CTL_init_run(&SoilWatAll, &LogInfo); + if(LogInfo.stopRun) { + goto report; + } // initialize output #ifdef RSWDEBUG @@ -386,6 +409,10 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { SoilWatAll.VegEstab.count, SoilWatAll.GenOutput.ncol_OUT); SW_OUT_set_colnames(SoilWatAll.Site.n_layers, SoilWatAll.VegEstab.parms, SoilWatAll.GenOutput.ncol_OUT, SoilWatAll.GenOutput.colnames_OUT, &LogInfo); + if(LogInfo.stopRun) { + goto report; + } + PROTECT(outputData = onGetOutput(inputData)); setGlobalrSOILWAT2_OutputVariables(outputData); @@ -399,6 +426,8 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { #ifdef RSWDEBUG if (debug) swprintf(" clean up ..."); #endif + + report: { SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); if(LogInfo.numWarnings > 0) { @@ -408,6 +437,7 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { if(LogInfo.stopRun) { sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } + } #ifdef RSWDEBUG if (debug) swprintf(" completed.\n"); @@ -455,6 +485,9 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { } SW_CTL_setup_model(&SoilWatAll, SoilWatOutputPtrs, &PathInfo, &LogInfo); + if(LogInfo.stopRun) { + goto report; + } // `onSet_WTH_DATA()` requires correct `endyr` and `startyr` of `SW_Model` #ifdef RSWDEBUG @@ -501,19 +534,23 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); + report: { + if(LogInfo.numWarnings > 0) { + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + } + + if(LogInfo.stopRun) { + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + } + } // Return processed weather data PROTECT(res = onGet_WTH_DATA()); UNPROTECT(1); - if(LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used - } - if(LogInfo.stopRun) { - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } return res; } @@ -591,6 +628,9 @@ SEXP rSW2_readAllWeatherFromDisk( SoilWatAll.Weather.dailyInputFlags, &LogInfo ); + if(LogInfo.stopRun) { + return NULL; // Exit function prematurely due to error + } // no monthly scaling for (i = 0; i < MAX_MONTHS; i++) { @@ -611,6 +651,9 @@ SEXP rSW2_readAllWeatherFromDisk( #endif // using global variables: SW_Weather, SW_Model, SW_Sky SW_WTH_read(&SoilWatAll.Weather, &SoilWatAll.Sky, &SoilWatAll.Model, &LogInfo); + if(LogInfo.stopRun) { + return NULL; // Exit function prematurely due to error + } // Finalize daily weather (weather generator & monthly scaling) #ifdef RSWDEBUG @@ -618,6 +661,9 @@ SEXP rSW2_readAllWeatherFromDisk( #endif SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); + if(LogInfo.stopRun) { + return NULL; // Exit function prematurely due to error + } // Return processed weather data diff --git a/src/rSW_Carbon.c b/src/rSW_Carbon.c index cb741655..68f8ca5a 100644 --- a/src/rSW_Carbon.c +++ b/src/rSW_Carbon.c @@ -165,6 +165,7 @@ void onSet_swCarbon(SEXP object) { if (i - 1 + n_sim > n_input) { LogError(&LogInfo, LOGERROR, "CO2ppm object does not contain data for every year"); + return; // Exit function prematurely due to error } // Copy CO2 concentration values to SOILWAT variable diff --git a/src/rSW_Control.c b/src/rSW_Control.c index 1b9d3c05..cae1c828 100644 --- a/src/rSW_Control.c +++ b/src/rSW_Control.c @@ -60,6 +60,7 @@ void rSW_CTL_obtain_inputs(Bool from_files) { if (from_files) { SW_CTL_read_inputs_from_disk(&SoilWatAll, &PathInfo, &LogInfo); + return; // Exit function prematurely due to error } else { //Use R data to set the data #ifdef RSWDEBUG diff --git a/src/rSW_Files.c b/src/rSW_Files.c index a45c1228..fa0f252b 100644 --- a/src/rSW_Files.c +++ b/src/rSW_Files.c @@ -90,6 +90,9 @@ void onSet_SW_F(SEXP SW_F_construct) { } for (i = 0; i < j; i++) { PathInfo.InFiles[i] = Str_Dup(CHAR(STRING_ELT(FilesIn,i)), &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely + } } PROTECT(Rweather_prefix = GET_SLOT(SW_F_construct, install("WeatherPrefix"))); diff --git a/src/rSW_Markov.c b/src/rSW_Markov.c index ae0a0f66..2dd7a75c 100644 --- a/src/rSW_Markov.c +++ b/src/rSW_Markov.c @@ -76,6 +76,7 @@ void onSet_MKV(SEXP MKV) { "Markov weather generator: " "rSOILWAT2 failed to pass `MKV_prob` values to SOILWAT2.\n" ); + return; // Exit function prematurely due to error } if (!onSet_MKV_conv(MKV_conv) && SoilWatAll.Weather.generateWeatherMethod == 2) { @@ -85,6 +86,7 @@ void onSet_MKV(SEXP MKV) { "Markov weather generator: " "rSOILWAT2 failed to pass `MKV_conv` values to SOILWAT2.\n" ); + return; // Exit function prematurely due to error } UNPROTECT(2); diff --git a/src/rSW_Model.c b/src/rSW_Model.c index 9d971b73..94c569d5 100644 --- a/src/rSW_Model.c +++ b/src/rSW_Model.c @@ -107,23 +107,28 @@ void onSet_SW_MDL(SEXP SW_MDL) { if (!IS_S4_OBJECT(SW_MDL)) { LogError(&LogInfo, LOGERROR, "%s: No input.", MyFileName); + return; // Exit function prematurely due to error } PROTECT(StartYear = GET_SLOT(SW_MDL, install("StartYear"))); if (INTEGER(StartYear)[0] < 0) { LogError(&LogInfo, LOGERROR, "%s: Negative start year (%d)", MyFileName, INTEGER(StartYear)[0]); + return; // Exit function prematurely due to error } m->startyr = INTEGER(StartYear)[0]; PROTECT(EndYear = GET_SLOT(SW_MDL, install("EndYear"))); if (isNull(EndYear) || INTEGER(EndYear)[0] == NA_INTEGER) { LogError(&LogInfo, LOGERROR, "%s: Ending year not found.", MyFileName); + return; // Exit function prematurely due to error } if (INTEGER(EndYear)[0] < 0) { LogError(&LogInfo, LOGERROR, "%s: Negative ending year (%d)", MyFileName, INTEGER(EndYear)[0]); + return; // Exit function prematurely due to error } m->endyr = INTEGER(EndYear)[0]; if (m->endyr < m->startyr) { LogError(&LogInfo, LOGERROR, "%s: Start Year > End Year", MyFileName); + return; // Exit function prematurely due to error } PROTECT(StartStart = GET_SLOT(SW_MDL, install("FDOFY"))); diff --git a/src/rSW_Output.c b/src/rSW_Output.c index bedebdb1..7b16a293 100644 --- a/src/rSW_Output.c +++ b/src/rSW_Output.c @@ -104,11 +104,17 @@ void onSet_SW_OUT(SEXP OUT) { if (msg_type > 0) { LogError(&LogInfo, msg_type, "%s", msg); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } continue; } if (SoilWatAll.Output[k].use) { SoilWatAll.Output[k].outfile = Str_Dup(CHAR(STRING_ELT(outfile, k)), &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } ForEachOutPeriod(i) { SoilWatAll.GenOutput.timeSteps[k][i] = timePeriods[k + i * SW_OUTNKEYS]; @@ -300,6 +306,10 @@ SEXP onGetOutput(SEXP inputData) { PROTECT(stemp_KEY = NEW_OBJECT(swOutput_KEY)); SET_SLOT(stemp_KEY, install("Title"), mkString(Str_Dup(CHAR(STRING_ELT(outfile, k)), &LogInfo))); + if(LogInfo.stopRun) { + goto report; + } + SET_SLOT(stemp_KEY, install("Columns"), ScalarInteger(GenOut->ncol_OUT[k])); PROTECT(rTimeStep = NEW_INTEGER(GenOut->used_OUTNPERIODS)); @@ -368,10 +378,14 @@ SEXP onGetOutput(SEXP inputData) { if (debug) swprintf(" ... done. \n"); #endif - if(LogInfo.stopRun) { - // The only message could be an error from this function, - // so no need to use `sw_write_logs()` - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + report: { + if(LogInfo.stopRun) { + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + + // The only message could be an error from this function, + // so no need to use `sw_write_logs()` + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + } } return swOutput_Object; diff --git a/src/rSW_Site.c b/src/rSW_Site.c index de68702b..10bf2e02 100644 --- a/src/rSW_Site.c +++ b/src/rSW_Site.c @@ -133,6 +133,7 @@ static void onSet_SW_LYR(SEXP SW_LYR) { "%s : Too few columns in layers specified (%d).\n", MyFileName, columns ); + return; // Exit function prematurely due to error } for (i = 0; i < j; i++) { @@ -172,6 +173,7 @@ static void onSet_SW_LYR(SEXP SW_LYR) { "Maximum number of layers is %d\n", MyFileName, lyrno + 1, MAX_LAYERS ); + return; // Exit function prematurely due to error } } } @@ -222,6 +224,7 @@ static void onSet_SW_SWRCp(SEXP SW_SWRCp) { "%s : Bad number of SWRC parameters %d -- must be %d.\n", MyFileName, ncols(SW_SWRCp), SWRC_PARAM_NMAX ); + return; // Exit function prematurely due to error } /* Check that we have `SW_Site.n_layers` */ @@ -233,6 +236,7 @@ static void onSet_SW_SWRCp(SEXP SW_SWRCp) { "must match number of soil layers (%d)\n", MyFileName, nrows(SW_SWRCp), SoilWatAll.Site.n_layers ); + return; // Exit function prematurely due to error } /* Copy values */ @@ -609,6 +613,9 @@ void onSet_SW_SIT(SEXP SW_SIT) { PROTECT(swrc_flags = GET_SLOT(SW_SIT, install("swrc_flags"))); strcpy(v->site_swrc_name, CHAR(STRING_ELT(swrc_flags, 0))); v->site_swrc_type = encode_str2swrc(v->site_swrc_name, &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } strcpy(v->site_ptf_name, CHAR(STRING_ELT(swrc_flags, 1))); v->site_ptf_type = encode_str2ptf(v->site_ptf_name); PROTECT(has_swrcp = GET_SLOT(SW_SIT, install("has_swrcp"))); @@ -632,6 +639,7 @@ void onSet_SW_SIT(SEXP SW_SIT) { if (too_many_regions) { LogError(&LogInfo, LOGERROR, "siteparam.in : Number of transpiration regions" " exceeds maximum allowed (%d > %d)\n", v->n_transp_rgn, MAX_TRANSP_REGIONS); + return; // Exit function prematurely due to error } #ifdef RSWDEBUG if (debug) swprintf(" > 'transp-regions'"); @@ -641,6 +649,7 @@ void onSet_SW_SIT(SEXP SW_SIT) { for (r = 1; r < v->n_transp_rgn; r++) { if (v->_TranspRgnBounds[r - 1] >= v->_TranspRgnBounds[r]) { LogError(&LogInfo, LOGERROR, "siteparam.in : Discontinuity/reversal in transpiration regions.\n"); + return; // Exit function prematurely due to error } } diff --git a/src/rSW_SoilWater.c b/src/rSW_SoilWater.c index 370d7b06..ebc090e0 100644 --- a/src/rSW_SoilWater.c +++ b/src/rSW_SoilWater.c @@ -117,11 +117,16 @@ void onSet_SW_SWC(SEXP SWC) { // Mem_Free(v->hist.file_prefix); //} v->hist.file_prefix = (char *) Str_Dup(CHAR(STRING_ELT(swcFilePrefix,0)), &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } + v->hist.yr.first = INTEGER(swcFirstYear)[0]; v->hist.method = INTEGER(swcMethod)[0]; if (v->hist.method < 1 || v->hist.method > 2) { LogError(&LogInfo, LOGERROR, "swcsetup.in : Invalid swc adjustment method."); + return; // Exit function prematurely due to error } v->hist.yr.last = SoilWatAll.Model.endyr; v->hist.yr.total = v->hist.yr.last - v->hist.yr.first + 1; @@ -141,6 +146,10 @@ SEXP onGet_SW_SWC_hists(void) { for (year = SoilWatAll.Model.startyr; year <= SoilWatAll.Model.endyr; year++) { if (SoilWatAll.SoilWat.hist_use && year >= SoilWatAll.SoilWat.hist.yr.first) { _read_swc_hist(&SoilWatAll.SoilWat.hist, year, &LogInfo); + if(LogInfo.stopRun) { + return NULL; // Exit function prematurely due to error + } + SET_VECTOR_ELT(SWC_hists, i, onGet_SW_SWC_hist(year)); snprintf(cYear, sizeof cYear, "%4d", year); SET_STRING_ELT(SWC_hists_names, i, mkChar(cYear)); @@ -155,6 +164,7 @@ SEXP onGet_SW_SWC_hists(void) { SEXP onGet_SW_SWC_hist(TimeInt year) { LogError(&LogInfo, LOGERROR, "'onGet_SW_SWC_hist' is currently not functional.\n"); + return NULL; // Exit function prematurely due to error int i, j = 0; SW_SOILWAT *v = &SoilWatAll.SoilWat; @@ -193,6 +203,7 @@ SEXP onGet_SW_SWC_hist(TimeInt year) { void onSet_SW_SWC_hist(void) { LogError(&LogInfo, LOGERROR, "'onSet_SW_SWC_hist' is currently not functional.\n"); + return; // Exit function prematurely due to error int i, j = 0; SW_SOILWAT *v = &SoilWatAll.SoilWat; diff --git a/src/rSW_VegEstab.c b/src/rSW_VegEstab.c index e216e4b0..3e1cb894 100644 --- a/src/rSW_VegEstab.c +++ b/src/rSW_VegEstab.c @@ -100,6 +100,9 @@ void onSet_SW_VES(SEXP VES) { LogError(&LogInfo, LOGWARN, "Establishment not used.\n"); SW_VegEstab_construct(&SoilWatAll.VegEstab, &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } if (EchoInits) _echo_VegEstab(SoilWatAll.Site.width, SoilWatAll.VegEstab.parms, @@ -179,6 +182,10 @@ void onSet_SW_VES_spp(SEXP SPP, IntU i) { unsigned int count; count = _new_species(&SoilWatAll.VegEstab, &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } + v = SoilWatAll.VegEstab.parms[count]; v->vegType = INTEGER(GET_SLOT(SPP, install("vegType")))[i]; diff --git a/src/rSW_VegProd.c b/src/rSW_VegProd.c index 7c7928da..d411be5f 100644 --- a/src/rSW_VegProd.c +++ b/src/rSW_VegProd.c @@ -626,6 +626,9 @@ void onSet_SW_VPD(SEXP SW_VPD) { SW_VPD_fix_cover(&SoilWatAll.VegProd, &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } if (EchoInits) _echo_VegProd(SoilWatAll.VegProd.veg, SoilWatAll.VegProd.bare_cov); @@ -726,6 +729,9 @@ SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_mont final_MonPPT_cm, inputValues_D, final_shrubLimit, final_SumGrassesFraction, C4Variables, final_fill_empty_with_BareGround, final_isNorth, final_warn_extrapolation, final_fix_bareGround, grasses, RelAbundanceL0, RelAbundanceL1, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } for(index = 0; index < 8; index++) { REAL(final_RelAbundanceL0)[index] = RelAbundanceL0[index]; @@ -743,12 +749,15 @@ SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_mont UNPROTECT(8); - if(local_LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &local_LogInfo); // Note: `FALSE` is not used - } + report: { + if(local_LogInfo.numWarnings > 0) { + sw_write_logs(FALSE, &local_LogInfo); // Note: `FALSE` is not used + } - if(local_LogInfo.stopRun) { - sw_check_exit(FALSE, &local_LogInfo); // Note: `FALSE` is not used + if(local_LogInfo.stopRun) { + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + sw_check_exit(FALSE, &local_LogInfo); // Note: `FALSE` is not used + } } return res; diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index 5cbc703c..996099a6 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -449,6 +449,9 @@ void onSet_WTH_DATA(void) { // Allocate new `allHist` (based on current `SW_Weather.n_years`) allocateAllWeather(&SoilWatAll.Weather, &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } // Equivalent to `readAllWeather()`: @@ -575,7 +578,7 @@ static void rSW2_set_weather_hist( numDaysYear, nrows(yrWData) ); - return; + return; // Exit function prematurely due to error } // Suitable year among rSOILWAT2 weather list located @@ -614,6 +617,7 @@ static void rSW2_set_weather_hist( p_yrWData[doy + numDaysYear * 0], doy + 1 ); + return; // Exit function prematurely due to error } // Maximum daily temperature [C] @@ -871,6 +875,9 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, // Allocate memory of structs for climate on SOILWAT side allocateClimateStructs(numYears, &climateOutput, &climateAverages, &LogInfo); + if(LogInfo.stopRun) { + goto report; + } // Calculate climate variables calcSiteClimate(allHist, SW_Model->cum_monthdays, SW_Model->days_in_month, @@ -959,7 +966,12 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, } free(allHist); - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + report: { + if(LogInfo.stopRun) { + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + } + } return res; From 258a47830c7dc8ff73900033f42b15377b1ea797 Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Fri, 29 Sep 2023 09:57:20 -0700 Subject: [PATCH 08/24] Remove redundant checks when writing log messages - Currently, the program checks for a valid reason (any warnings or errors) to call `sw_write_logs()` and `sw_check_exit()` - These precautions are done in the functions themselves and do not need to be done in rSOILWAT2 - `rSW2_estimate_PotNatVeg_composition()` remains the same to stop logging errors as warnings for tests --- src/SW_R_lib.c | 27 ++++++--------------------- src/rSW_Output.c | 8 +++----- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index c85c6e6e..196259fd 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -303,13 +303,8 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { UNPROTECT(5); - if(LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used - } - - if(LogInfo.stopRun) { - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used return SW_DataList; } @@ -401,13 +396,8 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { #endif SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - if(LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used - } - - if(LogInfo.stopRun) { - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used #ifdef RSWDEBUG if (debug) swprintf(" completed.\n"); @@ -507,13 +497,8 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { UNPROTECT(1); - if(LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used - } - - if(LogInfo.stopRun) { - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used return res; } diff --git a/src/rSW_Output.c b/src/rSW_Output.c index bedebdb1..2c6757ee 100644 --- a/src/rSW_Output.c +++ b/src/rSW_Output.c @@ -368,11 +368,9 @@ SEXP onGetOutput(SEXP inputData) { if (debug) swprintf(" ... done. \n"); #endif - if(LogInfo.stopRun) { - // The only message could be an error from this function, - // so no need to use `sw_write_logs()` - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } + // The only message could be an error from this function, + // so no need to use `sw_write_logs()` + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used return swOutput_Object; } From 3098381387435a585a68b5d1da43b1cee7c7e2cf Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Fri, 29 Sep 2023 10:38:39 -0700 Subject: [PATCH 09/24] Merge branch 'feature_ErrorHandling' into feature_IndivErrorHandling - `start()` still checks if there are any warnings to report * This allows the tests to read an error instead of a warning version of the error --- src/SW_R_lib.c | 20 ++++++-------------- src/rSW_Output.c | 8 ++++---- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index daee1650..b2295963 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -313,13 +313,8 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { report: { SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - if(LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used - } - - if(LogInfo.stopRun) { - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } #ifdef RSWDEBUG @@ -434,9 +429,7 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used } - if(LogInfo.stopRun) { - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } #ifdef RSWDEBUG @@ -535,14 +528,13 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); report: { - if(LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used - } + sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used if(LogInfo.stopRun) { SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } + + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } // Return processed weather data diff --git a/src/rSW_Output.c b/src/rSW_Output.c index 7b16a293..7c4e1074 100644 --- a/src/rSW_Output.c +++ b/src/rSW_Output.c @@ -381,11 +381,11 @@ SEXP onGetOutput(SEXP inputData) { report: { if(LogInfo.stopRun) { SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - - // The only message could be an error from this function, - // so no need to use `sw_write_logs()` - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } + + // The only message could be an error from this function, + // so no need to use `sw_write_logs()` + sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } return swOutput_Object; From f4238166f3cd5bc131a954b1e6eaa4660a9d39df Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Fri, 29 Sep 2023 10:48:02 -0700 Subject: [PATCH 10/24] Silence "uninitialized variable" warnings --- src/SW_R_lib.c | 4 ++-- src/rSW_Weather.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index b2295963..dcaffdb1 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -170,7 +170,7 @@ void setupSOILWAT2(SEXP inputOptions) { @brief Read inputs from SOILWAT2 input files on disk using SOILWAT2 code */ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { - SEXP swInputData, SW_DataList, swLog, oRlogfile; + SEXP swInputData, SW_DataList = NULL, swLog, oRlogfile; #ifdef RSWDEBUG int debug = 0; #endif @@ -335,7 +335,7 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { - Copies output to R output variable */ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { - SEXP outputData, swLog, oRlogfile; + SEXP outputData = NULL, swLog, oRlogfile; #ifdef RSWDEBUG int debug = 0; #endif diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index 996099a6..7b9dd7b7 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -812,7 +812,7 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, Bool dailyInputFlags[MAX_INPUT_COLUMNS]; double cloudcov[MAX_MONTHS], windspeed[MAX_MONTHS], r_humidity[MAX_MONTHS]; - SEXP res, monthlyMean, monthlyMax, monthlyMin, monthlyPPT, MAT_C, MAP_cm, vectorNames, + SEXP res = NULL, monthlyMean, monthlyMax, monthlyMin, monthlyPPT, MAT_C, MAP_cm, vectorNames, C4Variables, Cheatgrass, cnamesC4SEXP, cnamesCheatgrassSEXP; char *cnames[] = {"meanMonthlyTempC","minMonthlyTempC","maxMonthlyTempC", From 20367cb3120abc8c746f67137ccf0304f088ba7f Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Fri, 29 Sep 2023 11:53:51 -0700 Subject: [PATCH 11/24] Unprotect protected variables before function exits --- src/SW_R_lib.c | 32 +++++++++++++++++++++----------- src/rSW_Carbon.c | 2 ++ src/rSW_Files.c | 1 + src/rSW_Markov.c | 6 +++++- src/rSW_Model.c | 8 ++++++++ src/rSW_Output.c | 16 ++++++++++------ src/rSW_Site.c | 5 +++++ src/rSW_SoilWater.c | 4 ++++ src/rSW_VegEstab.c | 1 + src/rSW_VegProd.c | 5 +++-- src/rSW_Weather.c | 7 ++++--- 11 files changed, 64 insertions(+), 23 deletions(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index dcaffdb1..e0a784f4 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -171,6 +171,7 @@ void setupSOILWAT2(SEXP inputOptions) { */ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { SEXP swInputData, SW_DataList = NULL, swLog, oRlogfile; + int numUnprotects = 5; #ifdef RSWDEBUG int debug = 0; #endif @@ -201,6 +202,7 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); if(LogInfo.stopRun) { + numUnprotects = 3; goto report; } @@ -210,6 +212,7 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { #endif SW_CTL_init_run(&SoilWatAll, &LogInfo); if(LogInfo.stopRun) { + numUnprotects = 3; goto report; } @@ -311,6 +314,8 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { #endif report: { + UNPROTECT(numUnprotects); + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used @@ -321,8 +326,6 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { if (debug) swprintf(" onGetInputDataFromFiles completed.\n"); #endif - UNPROTECT(5); - return SW_DataList; } @@ -423,6 +426,8 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { #endif report: { + UNPROTECT(3); + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); if(LogInfo.numWarnings > 0) { @@ -436,8 +441,6 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { if (debug) swprintf(" completed.\n"); #endif - UNPROTECT(4); - return(outputData); } @@ -449,7 +452,7 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { uses imputation/weather generator to fill missing values */ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { - SEXP res; + SEXP res = NULL; #ifdef RSWDEBUG int debug = 0; #endif @@ -527,6 +530,15 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); + if(LogInfo.stopRun) { + goto report; + } + + // Return processed weather data + PROTECT(res = onGet_WTH_DATA()); + + UNPROTECT(1); + report: { sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used @@ -537,11 +549,6 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used } - // Return processed weather data - PROTECT(res = onGet_WTH_DATA()); - - UNPROTECT(1); - return res; } @@ -562,7 +569,7 @@ SEXP rSW2_readAllWeatherFromDisk( SEXP endYear, SEXP dailyInputFlags ) { - SEXP res; + SEXP res = NULL; int i; #ifdef RSWDEBUG @@ -621,6 +628,7 @@ SEXP rSW2_readAllWeatherFromDisk( &LogInfo ); if(LogInfo.stopRun) { + UNPROTECT(5); // Unprotect the five protected variables before exiting return NULL; // Exit function prematurely due to error } @@ -644,6 +652,7 @@ SEXP rSW2_readAllWeatherFromDisk( // using global variables: SW_Weather, SW_Model, SW_Sky SW_WTH_read(&SoilWatAll.Weather, &SoilWatAll.Sky, &SoilWatAll.Model, &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(5); // Unprotect the five protected variables before exiting return NULL; // Exit function prematurely due to error } @@ -654,6 +663,7 @@ SEXP rSW2_readAllWeatherFromDisk( SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(5); // Unprotect the five protected variables before exiting return NULL; // Exit function prematurely due to error } diff --git a/src/rSW_Carbon.c b/src/rSW_Carbon.c index 68f8ca5a..929a6f2f 100644 --- a/src/rSW_Carbon.c +++ b/src/rSW_Carbon.c @@ -164,6 +164,8 @@ void onSet_swCarbon(SEXP object) { // Check that we have enough data if (i - 1 + n_sim > n_input) { + UNPROTECT(1); + LogError(&LogInfo, LOGERROR, "CO2ppm object does not contain data for every year"); return; // Exit function prematurely due to error } diff --git a/src/rSW_Files.c b/src/rSW_Files.c index fa0f252b..f2f30278 100644 --- a/src/rSW_Files.c +++ b/src/rSW_Files.c @@ -91,6 +91,7 @@ void onSet_SW_F(SEXP SW_F_construct) { for (i = 0; i < j; i++) { PathInfo.InFiles[i] = Str_Dup(CHAR(STRING_ELT(FilesIn,i)), &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(2); // Unprotect the two protected variables before exiting return; // Exit function prematurely } } diff --git a/src/rSW_Markov.c b/src/rSW_Markov.c index 2dd7a75c..9636317b 100644 --- a/src/rSW_Markov.c +++ b/src/rSW_Markov.c @@ -65,6 +65,9 @@ void onSet_MKV(SEXP MKV) { SEXP MKV_prob, MKV_conv; SW_MKV_construct(SoilWatAll.Weather.rng_seed, &SoilWatAll.Markov, &LogInfo); + if(LogInfo.stopRun) { + return; // Exit function prematurely due to error + } PROTECT(MKV_prob = GET_SLOT(MKV, install(cSW_MKV[0]))); PROTECT(MKV_conv = GET_SLOT(MKV, install(cSW_MKV[1]))); @@ -76,6 +79,8 @@ void onSet_MKV(SEXP MKV) { "Markov weather generator: " "rSOILWAT2 failed to pass `MKV_prob` values to SOILWAT2.\n" ); + + UNPROTECT(2); // Unprotect the two protected variables before exiting return; // Exit function prematurely due to error } @@ -86,7 +91,6 @@ void onSet_MKV(SEXP MKV) { "Markov weather generator: " "rSOILWAT2 failed to pass `MKV_conv` values to SOILWAT2.\n" ); - return; // Exit function prematurely due to error } UNPROTECT(2); diff --git a/src/rSW_Model.c b/src/rSW_Model.c index 94c569d5..441809e4 100644 --- a/src/rSW_Model.c +++ b/src/rSW_Model.c @@ -113,21 +113,29 @@ void onSet_SW_MDL(SEXP SW_MDL) { PROTECT(StartYear = GET_SLOT(SW_MDL, install("StartYear"))); if (INTEGER(StartYear)[0] < 0) { LogError(&LogInfo, LOGERROR, "%s: Negative start year (%d)", MyFileName, INTEGER(StartYear)[0]); + + UNPROTECT(1); return; // Exit function prematurely due to error } m->startyr = INTEGER(StartYear)[0]; PROTECT(EndYear = GET_SLOT(SW_MDL, install("EndYear"))); if (isNull(EndYear) || INTEGER(EndYear)[0] == NA_INTEGER) { LogError(&LogInfo, LOGERROR, "%s: Ending year not found.", MyFileName); + + UNPROTECT(2); return; // Exit function prematurely due to error } if (INTEGER(EndYear)[0] < 0) { LogError(&LogInfo, LOGERROR, "%s: Negative ending year (%d)", MyFileName, INTEGER(EndYear)[0]); + + UNPROTECT(2); return; // Exit function prematurely due to error } m->endyr = INTEGER(EndYear)[0]; if (m->endyr < m->startyr) { LogError(&LogInfo, LOGERROR, "%s: Start Year > End Year", MyFileName); + + UNPROTECT(2); return; // Exit function prematurely due to error } diff --git a/src/rSW_Output.c b/src/rSW_Output.c index 7c4e1074..5f2266b0 100644 --- a/src/rSW_Output.c +++ b/src/rSW_Output.c @@ -105,6 +105,7 @@ void onSet_SW_OUT(SEXP OUT) { if (msg_type > 0) { LogError(&LogInfo, msg_type, "%s", msg); if(LogInfo.stopRun) { + UNPROTECT(3); // Unprotect the three protected variables before exiting return; // Exit function prematurely due to error } continue; @@ -113,6 +114,7 @@ void onSet_SW_OUT(SEXP OUT) { if (SoilWatAll.Output[k].use) { SoilWatAll.Output[k].outfile = Str_Dup(CHAR(STRING_ELT(outfile, k)), &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(3); // Unprotect the three protected variables before exiting return; // Exit function prematurely due to error } @@ -247,7 +249,7 @@ void setGlobalrSOILWAT2_OutputVariables(SEXP outputData) { /* Experience has shown that generating the Output Data structure in R is slow compared to C * This will generate the OUTPUT data Structure and Names*/ SEXP onGetOutput(SEXP inputData) { - int i, l, h; + int i, l, h, numUnprotects = 0; OutKey k; OutPeriod pd; int *use; @@ -298,6 +300,7 @@ SEXP onGetOutput(SEXP inputData) { install("outfile"))); ForEachOutKey(k) { + numUnprotects = 4; if (use[k]) { #ifdef RSWDEBUG if (debug) swprintf("%s (ncol = %d):", key2str[k], GenOut->ncol_OUT[k]); @@ -306,6 +309,7 @@ SEXP onGetOutput(SEXP inputData) { PROTECT(stemp_KEY = NEW_OBJECT(swOutput_KEY)); SET_SLOT(stemp_KEY, install("Title"), mkString(Str_Dup(CHAR(STRING_ELT(outfile, k)), &LogInfo))); + numUnprotects++; if(LogInfo.stopRun) { goto report; } @@ -372,13 +376,13 @@ SEXP onGetOutput(SEXP inputData) { } } - UNPROTECT(4); + report: { + UNPROTECT(numUnprotects); - #ifdef RSWDEBUG - if (debug) swprintf(" ... done. \n"); - #endif + #ifdef RSWDEBUG + if (debug) swprintf(" ... done. \n"); + #endif - report: { if(LogInfo.stopRun) { SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); } diff --git a/src/rSW_Site.c b/src/rSW_Site.c index 10bf2e02..82c53638 100644 --- a/src/rSW_Site.c +++ b/src/rSW_Site.c @@ -614,6 +614,7 @@ void onSet_SW_SIT(SEXP SW_SIT) { strcpy(v->site_swrc_name, CHAR(STRING_ELT(swrc_flags, 0))); v->site_swrc_type = encode_str2swrc(v->site_swrc_name, &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(12); // Unprotect the twelve protected variables before exiting return; // Exit function prematurely due to error } strcpy(v->site_ptf_name, CHAR(STRING_ELT(swrc_flags, 1))); @@ -639,6 +640,8 @@ void onSet_SW_SIT(SEXP SW_SIT) { if (too_many_regions) { LogError(&LogInfo, LOGERROR, "siteparam.in : Number of transpiration regions" " exceeds maximum allowed (%d > %d)\n", v->n_transp_rgn, MAX_TRANSP_REGIONS); + + UNPROTECT(14); // Unprotect the fourteen protected variables before exiting return; // Exit function prematurely due to error } #ifdef RSWDEBUG @@ -649,6 +652,8 @@ void onSet_SW_SIT(SEXP SW_SIT) { for (r = 1; r < v->n_transp_rgn; r++) { if (v->_TranspRgnBounds[r - 1] >= v->_TranspRgnBounds[r]) { LogError(&LogInfo, LOGERROR, "siteparam.in : Discontinuity/reversal in transpiration regions.\n"); + + UNPROTECT(14); // Unprotect the fourteen protected variables before exiting return; // Exit function prematurely due to error } } diff --git a/src/rSW_SoilWater.c b/src/rSW_SoilWater.c index ebc090e0..7af992bd 100644 --- a/src/rSW_SoilWater.c +++ b/src/rSW_SoilWater.c @@ -118,6 +118,7 @@ void onSet_SW_SWC(SEXP SWC) { //} v->hist.file_prefix = (char *) Str_Dup(CHAR(STRING_ELT(swcFilePrefix,0)), &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(4); // Unprotect the four protected variables before exiting return; // Exit function prematurely due to error } @@ -126,6 +127,8 @@ void onSet_SW_SWC(SEXP SWC) { if (v->hist.method < 1 || v->hist.method > 2) { LogError(&LogInfo, LOGERROR, "swcsetup.in : Invalid swc adjustment method."); + + UNPROTECT(4); // Unprotect the four protected variables before exiting return; // Exit function prematurely due to error } v->hist.yr.last = SoilWatAll.Model.endyr; @@ -147,6 +150,7 @@ SEXP onGet_SW_SWC_hists(void) { if (SoilWatAll.SoilWat.hist_use && year >= SoilWatAll.SoilWat.hist.yr.first) { _read_swc_hist(&SoilWatAll.SoilWat.hist, year, &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(2); // Unprotect the two protected variables before exiting return NULL; // Exit function prematurely due to error } diff --git a/src/rSW_VegEstab.c b/src/rSW_VegEstab.c index 3e1cb894..fc1ee541 100644 --- a/src/rSW_VegEstab.c +++ b/src/rSW_VegEstab.c @@ -101,6 +101,7 @@ void onSet_SW_VES(SEXP VES) { SW_VegEstab_construct(&SoilWatAll.VegEstab, &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(2); // Unprotect the two protected variables before exiting return; // Exit function prematurely due to error } diff --git a/src/rSW_VegProd.c b/src/rSW_VegProd.c index d411be5f..a683108c 100644 --- a/src/rSW_VegProd.c +++ b/src/rSW_VegProd.c @@ -627,6 +627,7 @@ void onSet_SW_VPD(SEXP SW_VPD) { SW_VPD_fix_cover(&SoilWatAll.VegProd, &LogInfo); if(LogInfo.stopRun) { + UNPROTECT(18); // Unprotect the eighteen protected variables before exiting return; // Exit function prematurely due to error } @@ -747,9 +748,9 @@ SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_mont SET_VECTOR_ELT(res, 1, final_RelAbundanceL1); SET_VECTOR_ELT(res, 2, final_grasses); - UNPROTECT(8); - report: { + UNPROTECT(8); + if(local_LogInfo.numWarnings > 0) { sw_write_logs(FALSE, &local_LogInfo); // Note: `FALSE` is not used } diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index 7b9dd7b7..81ec1942 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -807,7 +807,7 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SW_MODEL *SW_Model = &SoilWatAll.Model; int numYears = asInteger(yearEnd) - asInteger(yearStart) + 1, year, calcSiteOutputNum = 10, - index; + index, numUnprotects = 12; Bool dailyInputFlags[MAX_INPUT_COLUMNS]; double cloudcov[MAX_MONTHS], windspeed[MAX_MONTHS], r_humidity[MAX_MONTHS]; @@ -876,6 +876,7 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, allocateClimateStructs(numYears, &climateOutput, &climateAverages, &LogInfo); if(LogInfo.stopRun) { + numUnprotects = 11; goto report; } @@ -959,14 +960,14 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, deallocateClimateStructs(&climateOutput, &climateAverages); - UNPROTECT(12); - for(year = 0; year < numYears; year++) { free(allHist[year]); } free(allHist); report: { + UNPROTECT(numUnprotects); + if(LogInfo.stopRun) { SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used From 249fd6d71d358d49174beb480b7fb9246113912d Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Sat, 30 Sep 2023 14:19:25 -0700 Subject: [PATCH 12/24] Use `Mem_Malloc` instead of `malloc` --- src/rSW_Weather.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index 5cbc703c..8b54e36f 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -836,10 +836,14 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, C4Variables = PROTECT(allocVector(REALSXP, 6)); Cheatgrass = PROTECT(allocVector(REALSXP, 6)); - allHist = (SW_WEATHER_HIST **)malloc(sizeof(SW_WEATHER_HIST *) * numYears); + allHist = (SW_WEATHER_HIST **)Mem_Malloc(sizeof(SW_WEATHER_HIST *) * numYears, + "rSW2_calc_SiteClimate", + &LogInfo); for(year = 0; year < numYears; year++) { - allHist[year] = (SW_WEATHER_HIST *)malloc(sizeof(SW_WEATHER_HIST)); + allHist[year] = (SW_WEATHER_HIST *)Mem_Malloc(sizeof(SW_WEATHER_HIST), + "rSW2_calc_SiteClimate", + &LogInfo); } Time_init_model(SoilWatAll.Model.days_in_month); From dcd41658219ea0068fbc5916125e589ac95313c1 Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Sat, 30 Sep 2023 14:23:26 -0700 Subject: [PATCH 13/24] New functions - `init_allHist_years` & `free_allHist` - There is a possibility the program does not have enough memory when allocating allHist in `rSW2_calc_SiteClimate()` - Initialize "allHist" to NULL `init_allHist_years` - Initializes all years in "allHist" before they are allocated `free_allHist` - Frees all non-NULL pointers within, and including "allHist" to make sure a NULL pointer is not attempted to be freed --- src/rSW_Weather.c | 31 ++++++++++++++++++++++++++----- src/rSW_Weather.h | 2 ++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index 8b54e36f..3dd4a1b6 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -795,7 +795,7 @@ static void rSW2_set_weather_hist( SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SEXP do_C4vars, SEXP do_Cheatgrass_ClimVars, SEXP latitude) { - SW_WEATHER_HIST **allHist; + SW_WEATHER_HIST **allHist = NULL; SW_CLIMATE_YEARLY climateOutput; SW_CLIMATE_CLIM climateAverages; @@ -840,6 +840,8 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, "rSW2_calc_SiteClimate", &LogInfo); + init_allHist_years(allHist, numYears); + for(year = 0; year < numYears; year++) { allHist[year] = (SW_WEATHER_HIST *)Mem_Malloc(sizeof(SW_WEATHER_HIST), "rSW2_calc_SiteClimate", @@ -958,13 +960,32 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, UNPROTECT(12); - for(year = 0; year < numYears; year++) { - free(allHist[year]); - } - free(allHist); + free_allHist(allHist, numYears); sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used return res; } + +void init_allHist_years(SW_WEATHER_HIST **allHist, int numYears) { + int year; + + for(year = 0; year < numYears; year++) { + allHist[year] = NULL; + } +} + +void free_allHist(SW_WEATHER_HIST **allHist, int numYears) { + int year; + + for(year = 0; year < numYears; year++) { + if(!isnull(allHist[year])) { + free(allHist[year]); + } + } + + if(!isnull(allHist)) { + free(allHist); + } +} diff --git a/src/rSW_Weather.h b/src/rSW_Weather.h index 63272fef..4c79c439 100644 --- a/src/rSW_Weather.h +++ b/src/rSW_Weather.h @@ -12,3 +12,5 @@ SEXP onGet_WTH_DATA(void); void onSet_WTH_DATA(void); SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SEXP do_C4vars, SEXP do_Cheatgrass_ClimVars, SEXP latitude); +void init_allHist_years(SW_WEATHER_HIST **allHist, int numYears); +void free_allHist(SW_WEATHER_HIST **allHist, int numYears); From 38564d5616fc55bf52df18b75434eb313271753d Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Sat, 30 Sep 2023 14:42:00 -0700 Subject: [PATCH 14/24] Update early exits in `rSW2_calc_SiteClimate()` - Check for early exit after memory allocations - Remove unnecessary `UNPROTECT()`, which will be handled in an instance of `UNPROTECT()` that uses a variable for the number of unprotects - Free "allHist" after the unprotects --- src/rSW_Weather.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index cdf8d548..292d46e4 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -843,6 +843,9 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, allHist = (SW_WEATHER_HIST **)Mem_Malloc(sizeof(SW_WEATHER_HIST *) * numYears, "rSW2_calc_SiteClimate", &LogInfo); + if(LogInfo.stopRun) { + goto report; + } init_allHist_years(allHist, numYears); @@ -850,6 +853,9 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, allHist[year] = (SW_WEATHER_HIST *)Mem_Malloc(sizeof(SW_WEATHER_HIST), "rSW2_calc_SiteClimate", &LogInfo); + if(LogInfo.stopRun) { + goto report; + } } Time_init_model(SoilWatAll.Model.days_in_month); @@ -966,13 +972,11 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, deallocateClimateStructs(&climateOutput, &climateAverages); - UNPROTECT(12); - - free_allHist(allHist, numYears); - report: { UNPROTECT(numUnprotects); + free_allHist(allHist, numYears); + if(LogInfo.stopRun) { SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used From bfa9599e2dc6b2617a4adb87fbdc926b9df0e545 Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Sat, 30 Sep 2023 14:57:15 -0700 Subject: [PATCH 15/24] Simplify number of variables to unprotect in early-exiting functions --- src/SW_R_lib.c | 7 ++++--- src/rSW_Output.c | 2 +- src/rSW_Weather.c | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index e0a784f4..511b6811 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -171,7 +171,7 @@ void setupSOILWAT2(SEXP inputOptions) { */ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { SEXP swInputData, SW_DataList = NULL, swLog, oRlogfile; - int numUnprotects = 5; + int numUnprotects = 3; #ifdef RSWDEBUG int debug = 0; #endif @@ -202,7 +202,6 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); if(LogInfo.stopRun) { - numUnprotects = 3; goto report; } @@ -212,7 +211,6 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { #endif SW_CTL_init_run(&SoilWatAll, &LogInfo); if(LogInfo.stopRun) { - numUnprotects = 3; goto report; } @@ -228,6 +226,9 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { PROTECT(swInputData = MAKE_CLASS("swInputData")); PROTECT(SW_DataList = NEW_OBJECT(swInputData)); + // Include the above protects in the number of variables to unprotect + numUnprotects += 2; + SET_SLOT(SW_DataList, install("files"), onGet_SW_F()); #ifdef RSWDEBUG diff --git a/src/rSW_Output.c b/src/rSW_Output.c index 5f2266b0..e3d8f137 100644 --- a/src/rSW_Output.c +++ b/src/rSW_Output.c @@ -307,9 +307,9 @@ SEXP onGetOutput(SEXP inputData) { #endif PROTECT(stemp_KEY = NEW_OBJECT(swOutput_KEY)); + numUnprotects++; SET_SLOT(stemp_KEY, install("Title"), mkString(Str_Dup(CHAR(STRING_ELT(outfile, k)), &LogInfo))); - numUnprotects++; if(LogInfo.stopRun) { goto report; } diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index 292d46e4..936369b7 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -807,7 +807,7 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SW_MODEL *SW_Model = &SoilWatAll.Model; int numYears = asInteger(yearEnd) - asInteger(yearStart) + 1, year, calcSiteOutputNum = 10, - index, numUnprotects = 12; + index, numUnprotects = 11; Bool dailyInputFlags[MAX_INPUT_COLUMNS]; double cloudcov[MAX_MONTHS], windspeed[MAX_MONTHS], r_humidity[MAX_MONTHS]; @@ -888,7 +888,6 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, allocateClimateStructs(numYears, &climateOutput, &climateAverages, &LogInfo); if(LogInfo.stopRun) { - numUnprotects = 11; goto report; } @@ -900,6 +899,7 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, averageClimateAcrossYears(&climateOutput, numYears, &climateAverages); res = PROTECT(allocVector(VECSXP, calcSiteOutputNum)); + numUnprotects++; double *xmonthlyMean = REAL(monthlyMean), *xmonthlyMax = REAL(monthlyMax), *xmonthlyMin = REAL(monthlyMin), *xmontlyPPT = REAL(monthlyPPT); From bd4350adff54610c9c0810f0c147a3ee1165ab39 Mon Sep 17 00:00:00 2001 From: Daniel Schlaepfer Date: Thu, 5 Oct 2023 09:58:20 -0400 Subject: [PATCH 16/24] Fully implement early recoil on error and message propagation - update SOILWAT2 on branch feature_ErrorHandling ** `sw_write_logs()` was renamed to `sw_write_warnings()` and lost argument "QuietMode" ** `sw_check_exit()` was renamed to `sw_fail_on_error()` and lost argument "QuietMode" - `start()` renamed to `sw_start()` -> each R access point to SOILWAT2 structures (rSOILWAT2 global variables SoilWatAll etc) must initialize and clean up each time (so that the global variables are ready for a next call possibly via another access point); and no other function should clean up SoilWatAll ** these access points now are `sw_start()`, `onGetInputDataFromFiles()`, `rSW2_processAllWeather()`, `rSW2_readAllWeatherFromDisk()`, and `sw_outputData()` ** deprecate R function `sw_outputData()` (which now calls new `onGetOutputDeprecated()` instead of `onGetOutput()` which is used by `sw_start()`) ** `onGetOutput()` no longer cleans up SoilWatAll ** `tempError()` no longer queries the rSOILWAT2 global variable "SoilWataAll" (because it should be cleaned up by then) but instead directly the new rSOILWAT2 global "sw_has_soiltemp_error"; instead, `sw_start()` now writes the soil temperature error status for each run to the new rSOILWAT2 global variable "sw_has_soiltemp_error" via new `setGlobal_soiltempError()` -> functions don't use rSOILWAT2 global variables (e.g., SoilWatAll) unless definitively needed ** `rSW2_calc_SiteClimate()` no longer uses "SoilWatAll" and instead gains local variables "days_in_month" and "cum_monthdays" -> "LogInfo" is no longer a rSOILWAT2 global variable ** R user control of verbosity (i.e., issue warnings etc) is now controlled only via `sw_verbose()` and the new rSOILWAT2 global variable "current_sw_verbosity" ** each function has a local instance that is initialized with "current_sw_verbosity" ** `sw_start()` and `onGetInputDataFromFiles()` lose argument "quiet" -> "InputData", "WeatherList", "bWeatherList", "useFiles", "Rlogfile" are no longer rSOILWAT2 global variables ** these are replaced by function arguments, see `rSW_CTL_obtain_inputs()` which gained arguments "InputData" and "weatherList" ** "useFiles" is now a local variable to `sw_start()` ** "Rlogfile" has been unused -> C functions in rSOILWAT2 gain argument "LogInfo" and early returns on error: ** `setupSOILWAT2()`, `rSW_CTL_obtain_inputs()`, `onGet_SW_SWC()`, `onGetOutput()`, `onSet_swCarbon()`, `onSet_SW_F()`, `onSet_MKV()`, `onSet_SW_MDL()`, `onSet_SW_OUT()`, `onSet_SW_SIT()`, `onSet_SW_SOILS()`, `onSet_SW_LYR()`, `onSet_SW_SWRCp()`, `onGet_SW_SWC()`, `onGet_SW_SWC_hists()`, `onGet_SW_SWC_hist()`, `onSet_SW_SWC_hist()`, `onSet_SW_VES()`, `onSet_SW_VES_spp()`, `onSet_SW_VPD()`, `onSet_SW_WTH_setup()`, `onSet_WTH_DATA()`, `rSW2_setAllWeather()`, `rSW2_set_weather_hist()` -> vegetation unit tests: gained regex to check for matching warning messages --- .gitignore | 2 + .gitmodules | 2 +- R/Rsw.R | 35 +-- R/data.R | 3 +- R/rSOILWAT2_deprecated.R | 25 ++ man/sw_exampleData.Rd | 3 +- man/sw_outputData.Rd | 2 +- src/SOILWAT2 | 2 +- src/SW_R_init.c | 16 +- src/SW_R_lib.c | 466 ++++++++++++++++++++----------- src/SW_R_lib.h | 14 +- src/rSW_Carbon.c | 4 +- src/rSW_Carbon.h | 2 +- src/rSW_Control.c | 83 ++++-- src/rSW_Control.h | 2 +- src/rSW_Files.c | 6 +- src/rSW_Files.h | 2 +- src/rSW_Markov.c | 10 +- src/rSW_Markov.h | 2 +- src/rSW_Model.c | 14 +- src/rSW_Model.h | 2 +- src/rSW_Output.c | 26 +- src/rSW_Output.h | 5 +- src/rSW_Site.c | 28 +- src/rSW_Site.h | 4 +- src/rSW_SoilWater.c | 32 +-- src/rSW_SoilWater.h | 10 +- src/rSW_VegEstab.c | 31 +- src/rSW_VegEstab.h | 4 +- src/rSW_VegProd.c | 22 +- src/rSW_VegProd.h | 2 +- src/rSW_Weather.c | 101 ++++--- src/rSW_Weather.h | 4 +- tests/testthat/test_Vegetation.R | 19 +- 34 files changed, 600 insertions(+), 385 deletions(-) diff --git a/.gitignore b/.gitignore index e8512fee..111e5ec5 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,5 @@ doc vignettes/*.pdf vignettes/*.html vignettes/*.log +/doc/ +/Meta/ diff --git a/.gitmodules b/.gitmodules index f24aa691..8486a60c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/SOILWAT2"] path = src/SOILWAT2 url = https://github.com/DrylandEcology/SOILWAT2 - branch = feature_IndivErrorHandling + branch = feature_ErrorHandling diff --git a/R/Rsw.R b/R/Rsw.R index a0d5b454..bd4e2e70 100644 --- a/R/Rsw.R +++ b/R/Rsw.R @@ -44,7 +44,7 @@ sw_args <- function(dir, files.in, echo, quiet) { #' #' @export sw_verbosity <- function(verbose = TRUE) { - invisible(!.Call(C_sw_quiet, !as.logical(verbose))) + invisible(.Call(C_sw_verbose, as.logical(verbose))) } @@ -243,6 +243,8 @@ sw_exec <- function( on.exit(setwd(dir_prev), add = TRUE) quiet <- as.logical(quiet) + prev_verbosity <- sw_verbosity(!quiet) + on.exit(sw_verbosity(prev_verbosity), add = TRUE) input <- sw_args(dir, files.in, echo, quiet) @@ -310,7 +312,7 @@ sw_exec <- function( # Run SOILWAT2 - res <- .Call(C_start, input, inputData, weatherList, quiet) + res <- .Call(C_sw_start, input, inputData, weatherList) slot(res, "version") <- rSW2_version() slot(res, "timestamp") <- rSW2_timestamp() @@ -380,30 +382,12 @@ sw_inputDataFromFiles <- function( on.exit(setwd(dir_prev), add = TRUE) quiet <- as.logical(quiet) + prev_verbosity <- sw_verbosity(!quiet) + on.exit(sw_verbosity(prev_verbosity), add = TRUE) input <- sw_args(dir, files.in, echo = FALSE, quiet = quiet) - res <- .Call(C_onGetInputDataFromFiles, input, quiet) - - slot(res, "version") <- rSW2_version() - slot(res, "timestamp") <- rSW2_timestamp() - - res -} - - -#' Return output data -#' -#' @inheritParams sw_exec -#' -#' @return An object of class \code{\linkS4class{swOutput}}. -#' @export -sw_outputData <- function(inputData) { - - dir_prev <- getwd() - on.exit(setwd(dir_prev), add = TRUE) - - res <- .Call(C_onGetOutput, inputData) + res <- .Call(C_onGetInputDataFromFiles, input) slot(res, "version") <- rSW2_version() slot(res, "timestamp") <- rSW2_timestamp() @@ -439,9 +423,6 @@ sw_outputData <- function(inputData) { #' #' @export sw_inputData <- function() { - dir_prev <- getwd() - on.exit(setwd(dir_prev), add = TRUE) - tmp <- swInputData() # default values (minus some deleted slots) utils::data(package = "rSOILWAT2", "weatherData", envir = environment()) slot(tmp, "weatherHistory") <- get("weatherData", envir = environment()) @@ -455,7 +436,7 @@ sw_inputData <- function() { #' @return A logical value #' @export has_soilTemp_failed <- function() { - .Call(C_tempError) + isTRUE(.Call(C_tempError)) } diff --git a/R/data.R b/R/data.R index 18baf221..b3c016bd 100644 --- a/R/data.R +++ b/R/data.R @@ -39,7 +39,8 @@ #' utils::data(sw_exampleData) #' path_demo <- system.file("extdata", "example1", package = "rSOILWAT2") #' x <- sw_inputDataFromFiles(dir = path_demo, files.in = "files.in") -#' identical(x, sw_exampleData) ## TRUE +#' ## expect differences in slots "timestamp" and possibly "version" +#' all.equal(x, sw_exampleData) "sw_exampleData" diff --git a/R/rSOILWAT2_deprecated.R b/R/rSOILWAT2_deprecated.R index 12956ab1..ca9e4ed4 100644 --- a/R/rSOILWAT2_deprecated.R +++ b/R/rSOILWAT2_deprecated.R @@ -1255,3 +1255,28 @@ readNumerics <- function(text,expectedArgs,showWarnings=FALSE) { } return(tmp) } + + + +#------ Other deprecated functions ------ + + +#' Return output data +#' +#' @inheritParams sw_exec +#' +#' @return An object of class \code{\linkS4class{swOutput}}. +#' @export +sw_outputData <- function(inputData) { + .Deprecated("Store return value of `rSOILWAT2::sw_exec()`.") + + dir_prev <- getwd() + on.exit(setwd(dir_prev), add = TRUE) + + res <- .Call(C_onGetOutputDeprecated, inputData) + + slot(res, "version") <- rSW2_version() + slot(res, "timestamp") <- rSW2_timestamp() + + res +} diff --git a/man/sw_exampleData.Rd b/man/sw_exampleData.Rd index a88a8b62..390a96a9 100644 --- a/man/sw_exampleData.Rd +++ b/man/sw_exampleData.Rd @@ -28,6 +28,7 @@ A dataset containing complete input data for an unspecified location. utils::data(sw_exampleData) path_demo <- system.file("extdata", "example1", package = "rSOILWAT2") x <- sw_inputDataFromFiles(dir = path_demo, files.in = "files.in") -identical(x, sw_exampleData) ## TRUE +## expect differences in slots "timestamp" and possibly "version" +all.equal(x, sw_exampleData) } \keyword{datasets} diff --git a/man/sw_outputData.Rd b/man/sw_outputData.Rd index 27d0486a..26416f65 100644 --- a/man/sw_outputData.Rd +++ b/man/sw_outputData.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/Rsw.R +% Please edit documentation in R/rSOILWAT2_deprecated.R \name{sw_outputData} \alias{sw_outputData} \title{Return output data} diff --git a/src/SOILWAT2 b/src/SOILWAT2 index f2416fbd..6423e4f2 160000 --- a/src/SOILWAT2 +++ b/src/SOILWAT2 @@ -1 +1 @@ -Subproject commit f2416fbd8bbf3c97767d4aa0ec98556607a87c37 +Subproject commit 6423e4f2d14504bd9a19e7612d6ecc34cfb37d9a diff --git a/src/SW_R_init.c b/src/SW_R_init.c index 6e311255..7c5a8945 100644 --- a/src/SW_R_init.c +++ b/src/SW_R_init.c @@ -5,14 +5,14 @@ /* .Call calls */ -extern SEXP start(SEXP, SEXP, SEXP, SEXP); +extern SEXP sw_start(SEXP, SEXP, SEXP); extern SEXP tempError(void); -extern SEXP onGetInputDataFromFiles(SEXP, SEXP); -extern SEXP onGetOutput(SEXP); +extern SEXP onGetInputDataFromFiles(SEXP); +extern SEXP onGetOutputDeprecated(SEXP); extern SEXP rSW2_processAllWeather(SEXP, SEXP); extern SEXP rSW2_readAllWeatherFromDisk(SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP sw_consts(void); -extern SEXP sw_quiet(SEXP); +extern SEXP sw_verbose(SEXP); extern SEXP rSW2_SWRC_PTF_estimate_parameters(SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP sw_check_SWRC_vs_PTF(SEXP, SEXP); extern SEXP rSW2_SWRC_check_parameters(SEXP, SEXP); @@ -23,14 +23,14 @@ extern SEXP rSW2_estimate_PotNatVeg_composition(SEXP, SEXP, SEXP, SEXP,SEXP, SEX extern SEXP rSW2_calc_SiteClimate(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); static const R_CallMethodDef CallEntries[] = { - {"start", (DL_FUNC) &start, 4}, + {"sw_start", (DL_FUNC) &sw_start, 3}, {"tempError", (DL_FUNC) &tempError, 0}, - {"onGetInputDataFromFiles", (DL_FUNC) &onGetInputDataFromFiles, 2}, - {"onGetOutput", (DL_FUNC) &onGetOutput, 1}, + {"onGetInputDataFromFiles", (DL_FUNC) &onGetInputDataFromFiles, 1}, + {"onGetOutputDeprecated", (DL_FUNC) &onGetOutputDeprecated, 1}, {"rSW2_processAllWeather", (DL_FUNC) &rSW2_processAllWeather, 2}, {"rSW2_readAllWeatherFromDisk",(DL_FUNC) &rSW2_readAllWeatherFromDisk,5}, {"sw_consts", (DL_FUNC) &sw_consts, 0}, - {"sw_quiet", (DL_FUNC) &sw_quiet, 1}, + {"sw_verbose", (DL_FUNC) &sw_verbose, 1}, {"rSW2_SWRC_PTF_estimate_parameters", (DL_FUNC) &rSW2_SWRC_PTF_estimate_parameters, 5}, {"sw_check_SWRC_vs_PTF", (DL_FUNC) &sw_check_SWRC_vs_PTF, 2}, {"rSW2_SWRC_check_parameters", (DL_FUNC) &rSW2_SWRC_check_parameters, 2}, diff --git a/src/SW_R_lib.c b/src/SW_R_lib.c index 511b6811..508c406d 100644 --- a/src/SW_R_lib.c +++ b/src/SW_R_lib.c @@ -46,26 +46,19 @@ /* Global Variables */ /* --------------------------------------------------- */ -SEXP InputData; -SEXP WeatherList; -Bool useFiles; -Bool bWeatherList; - SW_ALL SoilWatAll; SW_OUTPUT_POINTERS SoilWatOutputPtrs[SW_OUTNKEYS]; -LOG_INFO LogInfo; PATH_INFO PathInfo; + Bool EchoInits; -Bool QuietMode; +FILE *current_sw_verbosity = (FILE *) TRUE; // quiet = FALSE; verbose = TRUE (show SOILWAT2 warnings) /* =================================================== */ /* Local Variables */ /* --------------------------------------------------- */ -static SEXP Rlogfile; -static Bool current_sw_quiet = swFALSE; - +static SEXP sw_has_soiltemp_error = FALSE; /* =================================================== */ @@ -74,52 +67,58 @@ static Bool current_sw_quiet = swFALSE; /** - * Turn on/off `SOILWAT2` messages including errors, notes, and warnings - * - * @param verbose A logical value. - * @return The previous logical value. - */ -SEXP sw_quiet(SEXP quiet) { - SEXP prev_quiet; + Turn on/off `SOILWAT2` messages including errors, notes, and warnings - PROTECT(prev_quiet = NEW_LOGICAL(1)); - LOGICAL_POINTER(prev_quiet)[0] = current_sw_quiet; + `sw_verbosity()` is R interface to sw_verbose(). - if (LOGICAL(coerceVector(quiet, LGLSXP))[0]) { - // tell `LogError()` that R should NOT print messages to the console - LogInfo.logfp = NULL; - current_sw_quiet = swTRUE; + @param verbose A logical value. + @return The previous logical value. +*/ +SEXP sw_verbose(SEXP verbose) { + SEXP prev_verbose; + + PROTECT(prev_verbose = NEW_LOGICAL(1)); + LOGICAL_POINTER(prev_verbose)[0] = !isnull(current_sw_verbosity); + + if (LOGICAL(coerceVector(verbose, LGLSXP))[0]) { + // verbose: tell `LogError()` that R should print messages to the console + current_sw_verbosity = (FILE *) TRUE; // any non-NULL file pointer } else { - // tell `LogError()` that R should print messages to the console - LogInfo.logfp = (FILE *) swTRUE; // any non-NULL file pointer - current_sw_quiet = swFALSE; + // quiet: tell `LogError()` that R should NOT print messages to the console + current_sw_verbosity = NULL; } UNPROTECT(1); - return prev_quiet; + return prev_verbose; } /** - * Determines if a constant in the Parton equation 2.21 is invalid and would - * thus cause extreme soil temperature values (see SW_Flow_lib.c ~1770) + * Queries error status of soil temperature from the most previous simulation run (`sw_start()`) + * + * `has_soilTemp_failed()` is R interface to tempError() * * @param none * @return an R boolean that denotes an error (TRUE) or lack of (FALSE) * */ SEXP tempError(void) { - SEXP swR_temp_error; - PROTECT(swR_temp_error = NEW_LOGICAL(1)); - LOGICAL_POINTER(swR_temp_error)[0] = SoilWatAll.SoilWat.soiltempError; + return sw_has_soiltemp_error; +} + +static void setGlobal_soiltempError(Bool soiltempError) { + PROTECT(sw_has_soiltemp_error = NEW_LOGICAL(1)); + LOGICAL_POINTER(sw_has_soiltemp_error)[0] = soiltempError; UNPROTECT(1); - return swR_temp_error; } -/** Setup and construct model (independent of inputs) + +/** Setup and construct global variables for SOILWAT2 + + Global variables managed by rSOILWAT2: SoilWatAll, SoilWatOutputPtrs, PathInfo. */ -void setupSOILWAT2(SEXP inputOptions) { +void setupSOILWAT2(SEXP inputOptions, LOG_INFO* LogInfo) { int i, argc; char *argv[7]; #ifdef RSWDEBUG @@ -135,7 +134,7 @@ void setupSOILWAT2(SEXP inputOptions) { if (argc > 7) { // fatal condition because argv is hard-coded to be of length 7; increase size of // argv if more command-line options are added to SOILWAT2 in the future - LogError(&LogInfo, LOGERROR, "length(inputOptions) must be <= 7."); + LogError(LogInfo, LOGERROR, "length(inputOptions) must be <= 7."); return; // Exit function prematurely due to error } for (i = 0; i < argc; i++) { @@ -147,18 +146,17 @@ void setupSOILWAT2(SEXP inputOptions) { #endif SW_CTL_init_ptrs(&SoilWatAll, PathInfo.InFiles); - sw_init_args(argc, argv, &QuietMode, &EchoInits, - &PathInfo.InFiles[eFirst], &LogInfo); - if(LogInfo.stopRun) { + sw_init_args(argc, argv, &EchoInits, &PathInfo.InFiles[eFirst], LogInfo); + if(LogInfo->stopRun) { return; // Exit function prematurely due to error } #ifdef RSWDEBUG if (debug) swprintf("Initialize SOILWAT ..."); - #endif + #endif - SW_CTL_setup_model(&SoilWatAll, SoilWatOutputPtrs, &PathInfo, &LogInfo); - if(LogInfo.stopRun) { + SW_CTL_setup_model(&SoilWatAll, SoilWatOutputPtrs, &PathInfo, LogInfo); + if(LogInfo->stopRun) { return; // Exit function prematurely due to error } @@ -168,40 +166,47 @@ void setupSOILWAT2(SEXP inputOptions) { /** @brief Read inputs from SOILWAT2 input files on disk using SOILWAT2 code + + `sw_inputDataFromFiles()` is R interface to onGetInputDataFromFiles() */ -SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { +SEXP onGetInputDataFromFiles(SEXP inputOptions) { SEXP swInputData, SW_DataList = NULL, swLog, oRlogfile; - int numUnprotects = 3; + int numUnprotects = 2; #ifdef RSWDEBUG int debug = 0; #endif - sw_init_logs(NULL, &LogInfo); - sw_quiet(quiet); + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); #ifdef RSWDEBUG if (debug) swprintf("Set log\n"); #endif PROTECT(swLog = MAKE_CLASS("swLog")); PROTECT(oRlogfile = NEW_OBJECT(swLog)); - PROTECT(Rlogfile = GET_SLOT(oRlogfile,install("LogData"))); // setup and construct model (independent of inputs) - setupSOILWAT2(inputOptions); + setupSOILWAT2(inputOptions, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } // read user inputs: from files #ifdef RSWDEBUG if (debug) swprintf("Read input from disk files into SOILWAT2 variables\n"); #endif - rSW_CTL_obtain_inputs(TRUE); + rSW_CTL_obtain_inputs(TRUE, NULL, NULL, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } // finalize daily weather #ifdef RSWDEBUG if (debug) swprintf(" finalize daily weather ...\n"); #endif SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, - SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); - if(LogInfo.stopRun) { + SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } @@ -209,8 +214,8 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { #ifdef RSWDEBUG if (debug) swprintf(" init simulation run ...\n"); #endif - SW_CTL_init_run(&SoilWatAll, &LogInfo); - if(LogInfo.stopRun) { + SW_CTL_init_run(&SoilWatAll, &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } @@ -302,14 +307,16 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { if (debug) swprintf(" > 'CO2'"); #endif - SET_SLOT(SW_DataList, install("swc"), onGet_SW_SWC()); + SET_SLOT(SW_DataList, install("swc"), onGet_SW_SWC(&local_LogInfo)); #ifdef RSWDEBUG if (debug) swprintf(" > 'swc'"); #endif + if(local_LogInfo.stopRun) { + goto report; + } SET_SLOT(SW_DataList, install("log"), oRlogfile); - // de-allocate all memory, but `p_OUT` #ifdef RSWDEBUG if (debug) swprintf(" > de-allocate most memory; \n"); #endif @@ -317,10 +324,11 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { report: { UNPROTECT(numUnprotects); + // de-allocate SOILWAT2 memory, but let R handle `p_OUT` SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); } #ifdef RSWDEBUG @@ -337,57 +345,53 @@ SEXP onGetInputDataFromFiles(SEXP inputOptions, SEXP quiet) { - Copies R inputs to C variables - Executes a SOILWAT2 simulation - Copies output to R output variable + + `sw_exec()` is R interface to sw_start() */ -SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { +SEXP sw_start(SEXP inputOptions, SEXP inputData, SEXP weatherList) { SEXP outputData = NULL, swLog, oRlogfile; + Bool useFiles; + #ifdef RSWDEBUG int debug = 0; #endif - sw_init_logs(NULL, &LogInfo); - sw_quiet(quiet); + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); - if (isNull(inputData)) { - useFiles = TRUE; - } else { - useFiles = FALSE; - InputData = inputData; - } - - //This is used to minimize copying weather data between similiar runs. - if (isNull(weatherList)) { - bWeatherList = FALSE; - } else { - bWeatherList = TRUE; - WeatherList = weatherList; - } #ifdef RSWDEBUG if (debug) swprintf("'start': create log ..."); #endif PROTECT(swLog = MAKE_CLASS("swLog")); PROTECT(oRlogfile = NEW_OBJECT(swLog)); - PROTECT(Rlogfile = GET_SLOT(oRlogfile,install("LogData"))); // setup and construct model (independent of inputs) #ifdef RSWDEBUG if (debug) swprintf(" input arguments & setup model ..."); #endif - setupSOILWAT2(inputOptions); + setupSOILWAT2(inputOptions, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } // read user inputs: either from files or from memory (depending on useFiles) #ifdef RSWDEBUG if (debug) swprintf(" obtain inputs ..."); #endif - rSW_CTL_obtain_inputs(useFiles); + useFiles = isNull(inputData) ? swTRUE : swFALSE; + rSW_CTL_obtain_inputs(useFiles, inputData, weatherList, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } // finalize daily weather #ifdef RSWDEBUG if (debug) swprintf(" finalize daily weather ...\n"); #endif SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, - SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); - if(LogInfo.stopRun) { + SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } @@ -395,8 +399,8 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { #ifdef RSWDEBUG if (debug) swprintf(" init simulation run ..."); #endif - SW_CTL_init_run(&SoilWatAll, &LogInfo); - if(LogInfo.stopRun) { + SW_CTL_init_run(&SoilWatAll, &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } @@ -407,35 +411,42 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { SW_OUT_set_ncol(SoilWatAll.Site.n_layers, SoilWatAll.Site.n_evap_lyrs, SoilWatAll.VegEstab.count, SoilWatAll.GenOutput.ncol_OUT); SW_OUT_set_colnames(SoilWatAll.Site.n_layers, SoilWatAll.VegEstab.parms, - SoilWatAll.GenOutput.ncol_OUT, SoilWatAll.GenOutput.colnames_OUT, &LogInfo); - if(LogInfo.stopRun) { + SoilWatAll.GenOutput.ncol_OUT, SoilWatAll.GenOutput.colnames_OUT, &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } - PROTECT(outputData = onGetOutput(inputData)); + PROTECT(outputData = onGetOutput(inputData, &local_LogInfo)); + if(local_LogInfo.stopRun) { + goto report; + } setGlobalrSOILWAT2_OutputVariables(outputData); // run simulation: loop through each year #ifdef RSWDEBUG if (debug) swprintf(" run SOILWAT2 ..."); #endif - SW_CTL_main(&SoilWatAll, SoilWatOutputPtrs, &LogInfo); + SW_CTL_main(&SoilWatAll, SoilWatOutputPtrs, &local_LogInfo); + - // de-allocate all memory, but let R handle `p_OUT` #ifdef RSWDEBUG if (debug) swprintf(" clean up ..."); #endif report: { - UNPROTECT(3); + UNPROTECT(2); - SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - - if(LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + if (local_LogInfo.stopRun) { + setGlobal_soiltempError(TRUE); + } else { + setGlobal_soiltempError(SoilWatAll.SoilWat.soiltempError); } - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + // de-allocate SOILWAT2 memory, but let R handle `p_OUT` + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); } #ifdef RSWDEBUG @@ -446,57 +457,110 @@ SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet) { } + + +// `sw_outputData()` is R interface to onGetOutputDeprecated() +SEXP onGetOutputDeprecated(SEXP inputData) { + SEXP inputOptions, swOutput_Object = NULL; + int numUnprotects = 0; + + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } + + /* Setup global variables including SoilWatAll */ + PROTECT(inputOptions = allocVector(STRSXP, 1)); + numUnprotects++; + SET_STRING_ELT(inputOptions, 0, mkChar("SOILWAT2")); + + setupSOILWAT2(inputOptions, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } + + // Create output object + swOutput_Object = onGetOutput(inputData, &local_LogInfo); + + + report: { + UNPROTECT(numUnprotects); + + // de-allocate SOILWAT2 memory + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); + } + + return swOutput_Object; +} + + + + /** @brief Process daily driving (weather) variables using SOILWAT2 code Applies additive/multiplicative scaling parameters and uses imputation/weather generator to fill missing values + + `dbW_generateWeather()` is R interface to rSW2_processAllWeather() */ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { - SEXP res = NULL; + SEXP res = NULL, inputOptions; + int numUnprotects = 0; #ifdef RSWDEBUG int debug = 0; #endif - sw_init_logs(NULL, &LogInfo); #ifdef RSWDEBUG if (debug) swprintf("\n'rSW2_processAllWeather': data preparation: "); #endif - // Copy `swInputData` to global variable `InputData` which is used - // by `onSet_XXX()` functions - InputData = inputData; + if (isNull(weatherList)) { + error("'weatherList' is NULL."); + } - // Copy `weatherList` to global variable `WeatherList` which is used - // by `onSet_WTH_DATA()` if `bWeatherList` - bWeatherList = TRUE; - WeatherList = weatherList; + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); - // setup and construct model (independent of inputs) + // setup and construct model #ifdef RSWDEBUG if (debug) swprintf("'setup' > "); #endif - if(PathInfo.InFiles[eFirst] == NULL) { - PathInfo.InFiles[eFirst] = DFLT_FIRSTFILE; - } - SW_CTL_setup_model(&SoilWatAll, SoilWatOutputPtrs, &PathInfo, &LogInfo); - if(LogInfo.stopRun) { + // values of `inputOptions` are not used + PROTECT(inputOptions = allocVector(STRSXP, 1)); + numUnprotects++; + SET_STRING_ELT(inputOptions, 0, mkChar("SOILWAT2")); + + setupSOILWAT2(inputOptions, &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } + + // rSW_CTL_obtain_inputs(): // `onSet_WTH_DATA()` requires correct `endyr` and `startyr` of `SW_Model` #ifdef RSWDEBUG if (debug) swprintf("'model' > "); #endif - onSet_SW_MDL(GET_SLOT(inputData, install("years"))); + onSet_SW_MDL(GET_SLOT(inputData, install("years")), &local_LogInfo); + if (local_LogInfo.stopRun) { + goto report; + } // `onSet_WTH_DATA()` requires additive/multiplicative scaling parameters #ifdef RSWDEBUG if (debug) swprintf(" > 'weather-setup'"); #endif - onSet_SW_WTH_setup(GET_SLOT(inputData, install("weather"))); + onSet_SW_WTH_setup(GET_SLOT(inputData, install("weather")), &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } // `onSet_WTH_DATA()` requires ready-to-go weather generator if ( @@ -510,10 +574,13 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { ) )[0] ) { - onSet_MKV(GET_SLOT(inputData, install("markov"))); + onSet_MKV(GET_SLOT(inputData, install("markov")), &local_LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'weather generator'.\n"); #endif + if(local_LogInfo.stopRun) { + goto report; + } } @@ -521,33 +588,35 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { #ifdef RSWDEBUG if (debug) swprintf("'rSW2_processAllWeather': process weather data"); #endif - onSet_WTH_DATA(); - + onSet_WTH_DATA(weatherList, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } // Finalize daily weather (weather generator & monthly scaling) #ifdef RSWDEBUG if (debug) swprintf(" > finalize daily weather.\n"); #endif SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, - SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); + SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &local_LogInfo); - if(LogInfo.stopRun) { + if(local_LogInfo.stopRun) { goto report; } // Return processed weather data PROTECT(res = onGet_WTH_DATA()); + numUnprotects++; - UNPROTECT(1); report: { - sw_write_logs(FALSE, &LogInfo); // Note: `FALSE` is not used + UNPROTECT(numUnprotects); - if(LogInfo.stopRun) { - SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - } + // de-allocate SOILWAT2 memory + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); } @@ -562,6 +631,8 @@ SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData) { Applies additive/multiplicative scaling parameters and uses imputation/weather generator to fill missing values + + `getWeatherData_folders()` is R interface to rSW2_readAllWeatherFromDisk() */ SEXP rSW2_readAllWeatherFromDisk( SEXP path, @@ -570,27 +641,44 @@ SEXP rSW2_readAllWeatherFromDisk( SEXP endYear, SEXP dailyInputFlags ) { - SEXP res = NULL; - int i; + SEXP res = NULL, inputOptions; + int i, numUnprotects = 0; #ifdef RSWDEBUG int debug = 0; #endif + #ifdef RSWDEBUG + if (debug) swprintf("\n'rSW2_readAllWeatherFromDisk': data preparation: "); + #endif + + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); + /* Convert inputs to correct type */ path = PROTECT(AS_CHARACTER(path)); name_prefix = PROTECT(AS_CHARACTER(name_prefix)); startYear = PROTECT(coerceVector(startYear, INTSXP)); endYear = PROTECT(coerceVector(endYear, INTSXP)); dailyInputFlags = PROTECT(coerceVector(dailyInputFlags, LGLSXP)); + numUnprotects += 5; + /* Create convenience pointers */ int *xdif = LOGICAL(dailyInputFlags); /* LGLSXP are internally coded as int */ - #ifdef RSWDEBUG - if (debug) swprintf("\n'rSW2_readAllWeatherFromDisk': data preparation: "); - #endif + /* Setup global variables including SoilWatAll */ + PROTECT(inputOptions = allocVector(STRSXP, 1)); + numUnprotects++; + SET_STRING_ELT(inputOptions, 0, mkChar("SOILWAT2")); + + setupSOILWAT2(inputOptions, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; + } + + /* Copy relevant data to global variable SoilWatAll */ SoilWatAll.Model.startyr = INTEGER(startYear)[0]; SoilWatAll.Model.endyr = INTEGER(endYear)[0]; @@ -626,11 +714,10 @@ SEXP rSW2_readAllWeatherFromDisk( SoilWatAll.Weather.use_humidityMonthly, SoilWatAll.Weather.use_windSpeedMonthly, SoilWatAll.Weather.dailyInputFlags, - &LogInfo + &local_LogInfo ); - if(LogInfo.stopRun) { - UNPROTECT(5); // Unprotect the five protected variables before exiting - return NULL; // Exit function prematurely due to error + if(local_LogInfo.stopRun) { + goto report; // Exit function prematurely due to error } // no monthly scaling @@ -646,15 +733,13 @@ SEXP rSW2_readAllWeatherFromDisk( } - // Process weather data + // Read weather data #ifdef RSWDEBUG - if (debug) swprintf("'rSW2_readAllWeatherFromDisk': process weather data"); + if (debug) swprintf("'rSW2_readAllWeatherFromDisk': read weather data"); #endif - // using global variables: SW_Weather, SW_Model, SW_Sky - SW_WTH_read(&SoilWatAll.Weather, &SoilWatAll.Sky, &SoilWatAll.Model, &LogInfo); - if(LogInfo.stopRun) { - UNPROTECT(5); // Unprotect the five protected variables before exiting - return NULL; // Exit function prematurely due to error + SW_WTH_read(&SoilWatAll.Weather, &SoilWatAll.Sky, &SoilWatAll.Model, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; // Exit function prematurely due to error } // Finalize daily weather (weather generator & monthly scaling) @@ -662,18 +747,28 @@ SEXP rSW2_readAllWeatherFromDisk( if (debug) swprintf(" > finalize daily weather.\n"); #endif SW_WTH_finalize_all_weather(&SoilWatAll.Markov, &SoilWatAll.Weather, - SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &LogInfo); - if(LogInfo.stopRun) { - UNPROTECT(5); // Unprotect the five protected variables before exiting - return NULL; // Exit function prematurely due to error + SoilWatAll.Model.cum_monthdays, SoilWatAll.Model.days_in_month, &local_LogInfo); + if(local_LogInfo.stopRun) { + goto report; // Exit function prematurely due to error } // Return processed weather data - // using global variables: SW_Weather + // using global variable SoilWatAll.Weather res = PROTECT(onGet_WTH_DATA()); + numUnprotects++; + + + report: { + UNPROTECT(numUnprotects); + + // de-allocate SOILWAT2 memory + SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); + + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); + } - UNPROTECT(6); return res; } @@ -681,6 +776,9 @@ SEXP rSW2_readAllWeatherFromDisk( /** Expose SOILWAT2 constants and defines to internal R code of rSOILWAT2 + + `C_sw_consts()` is R interface to sw_consts() + @return A list with six elements: one element `kINT` for integer constants; other elements contain vegetation keys, `VegTypes`; @@ -935,6 +1033,8 @@ SEXP sw_consts(void) { See SOILWAT2's `SWRC_PTF_estimate_parameters()`, `swrc2str[]` and `ptf2str[]`. + `ptf_estimate()` is R interface to rSW2_SWRC_PTF_estimate_parameters(). + @param[in] ptf_type Identification number of selected PTF @param[in] sand Sand content of the matric soil (< 2 mm fraction) [g/g] @param[in] clay Clay content of the matric soil (< 2 mm fraction) [g/g] @@ -953,6 +1053,9 @@ SEXP rSW2_SWRC_PTF_estimate_parameters( SEXP fcoarse, SEXP bdensity ) { + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); + int nlyrs = length(sand); Rboolean has_bd = !isNull(bdensity); @@ -1014,15 +1117,26 @@ SEXP rSW2_SWRC_PTF_estimate_parameters( xclay[k1], xcoarse[k1], xbd[k1], - &LogInfo + &local_LogInfo ); + if (local_LogInfo.stopRun) { + goto report; + } + + for (k2 = 0; k2 < SWRC_PARAM_NMAX; k2++) { xres[k1 + nlyrs * k2] = REAL(swrcpk)[k2]; } } - UNPROTECT(7); + report: { + // Note: no SOILWAT2 memory was allocated + UNPROTECT(7); + + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); + } return res_swrcp; } @@ -1031,6 +1145,8 @@ SEXP rSW2_SWRC_PTF_estimate_parameters( /** @brief Check whether PTF and SWRC are compatible and implemented in `SOILWAT2` + `check_SWRC_vs_PTF()` is R interface to sw_check_SWRC_vs_PTF(). + @param[in] swrc_name Name of SWRC @param[in] ptf_name Name of PTF @@ -1070,6 +1186,8 @@ SEXP sw_check_SWRC_vs_PTF(SEXP swrc_name, SEXP ptf_name) { See SOILWAT2 function `SWRC_check_parameters()`. + `check_swrcp()` is R interface to rSW2_SWRC_check_parameters(). + @param[in] swrc_type Identification number of selected SWRC @param[in] *swrcp SWRC parameters; matrix (one row per set of parameters) or vector (treated as one set) @@ -1077,10 +1195,14 @@ SEXP sw_check_SWRC_vs_PTF(SEXP swrc_name, SEXP ptf_name) { @return A logical vector indicating if parameters passed the checks. */ SEXP rSW2_SWRC_check_parameters(SEXP swrc_type, SEXP swrcp) { + int numUnprotects = 0; + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); + /* Convert inputs to correct type */ swrcp = PROTECT(coerceVector(swrcp, REALSXP)); swrc_type = PROTECT(coerceVector(swrc_type, INTSXP)); - + numUnprotects += 2; /* Check SWRC parameters */ int @@ -1099,19 +1221,19 @@ SEXP rSW2_SWRC_check_parameters(SEXP swrc_type, SEXP swrcp) { } if (nlyrs != nrp) { - UNPROTECT(2); /* unprotect: swrcp, swrc_type */ + UNPROTECT(numUnprotects); /* unprotect: swrcp, swrc_type */ error("`nrows(swrcp)` disagrees with length of `swrc_type`."); } if (ncp != SWRC_PARAM_NMAX) { - UNPROTECT(2); /* unprotect: swrcp, swrc_type */ + UNPROTECT(numUnprotects); /* unprotect: swrcp, swrc_type */ error("`ncols(swrcp)` disagrees with required number of SWRC parameters."); } /* Allocate memory for result */ SEXP res = PROTECT(allocVector(LGLSXP, nlyrs)); - + numUnprotects++; /* Create convenience pointers */ unsigned int *xswrc_type = (unsigned int *) INTEGER(swrc_type); @@ -1132,10 +1254,20 @@ SEXP rSW2_SWRC_check_parameters(SEXP swrc_type, SEXP swrcp) { swrcpk[k2] = xswrcp[k1 + nlyrs * k2]; } - xres[k1] = SWRC_check_parameters(xswrc_type[k1], swrcpk, &LogInfo); + xres[k1] = SWRC_check_parameters(xswrc_type[k1], swrcpk, &local_LogInfo); + + if (local_LogInfo.stopRun) { + goto report; + } } - UNPROTECT(3); + report: { + // Note: no SOILWAT2 memory was allocated + UNPROTECT(numUnprotects); + + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); + } return res; } @@ -1148,6 +1280,8 @@ SEXP rSW2_SWRC_check_parameters(SEXP swrc_type, SEXP swrcp) { See SOILWAT2 function `SWRC_SWCtoSWP()` and `SWRC_SWPtoSWC()`. + `swrc_conversion()` via `swrc_conversion_1d()` is R interface to rSW2_SWRC(). + @param[in] x Soil water content in the layer [cm] or soil water potential [-bar]\ @param[in] direction Direction of conversion, 1: SWP->SWC; 2: SWC->SWP @@ -1167,7 +1301,12 @@ SEXP rSW2_SWRC( SEXP fcoarse, SEXP width ) { - int xdirection = asInteger(direction); + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); + + int + xdirection = asInteger(direction), + numUnprotects = 0; if (xdirection != 1 && xdirection != 2) { error("`direction` must be either SWP->SWC(1) or SWC->SWP(2)."); @@ -1195,7 +1334,7 @@ SEXP rSW2_SWRC( width = PROTECT(coerceVector(width, REALSXP)); swrcp = PROTECT(coerceVector(swrcp, REALSXP)); swrc_type = PROTECT(coerceVector(swrc_type, INTSXP)); - + numUnprotects += 5; /* Check SWRC parameters */ int nrp, ncp; @@ -1212,19 +1351,19 @@ SEXP rSW2_SWRC( } if (nlyrs != nrp) { - UNPROTECT(5); /* unprotect: swrcp, width, fcoarse, x, swrc_type */ + UNPROTECT(numUnprotects); /* unprotect: swrcp, width, fcoarse, x, swrc_type */ error("`nrows(swrcp)` disagrees with number of soil layers."); } if (ncp != SWRC_PARAM_NMAX) { - UNPROTECT(5); /* unprotect: swrcp, width, fcoarse, x, swrc_type */ + UNPROTECT(numUnprotects); /* unprotect: swrcp, width, fcoarse, x, swrc_type */ error("`ncols(swrcp)` disagrees with required number of SWRC parameters."); } /* Allocate memory for result */ SEXP res = PROTECT(allocVector(REALSXP, nlyrs)); - + numUnprotects++; /* Create convenience pointers */ unsigned int @@ -1262,7 +1401,7 @@ SEXP rSW2_SWRC( xcoarse[k1], xwidth[k1], LOGWARN, - &LogInfo + &local_LogInfo ); break; @@ -1275,7 +1414,7 @@ SEXP rSW2_SWRC( xcoarse[k1], xwidth[k1], LOGWARN, - &LogInfo + &local_LogInfo ); break; } @@ -1289,9 +1428,20 @@ SEXP rSW2_SWRC( // Input values are not finite xres[k1] = NA_REAL; } + + if (local_LogInfo.stopRun) { + goto report; // Exit function prematurely due to error + } } - UNPROTECT(6); + + report: { + // Note: no SOILWAT2 memory was allocated + UNPROTECT(numUnprotects); + + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); + } return res; } diff --git a/src/SW_R_lib.h b/src/SW_R_lib.h index bd8aa75a..d18cf479 100644 --- a/src/SW_R_lib.h +++ b/src/SW_R_lib.h @@ -19,25 +19,21 @@ /* =================================================== */ /* Externed Global Variables */ /* --------------------------------------------------- */ -extern SEXP InputData; -extern SEXP WeatherList; -extern Bool useFiles; -extern Bool bWeatherList; - extern SW_ALL SoilWatAll; extern SW_OUTPUT_POINTERS SoilWatOutputPtrs[SW_OUTNKEYS]; -extern LOG_INFO LogInfo; extern PATH_INFO PathInfo; + extern Bool EchoInits; +extern FILE *current_sw_verbosity; /* =================================================== */ /* Global Function Declarations */ /* --------------------------------------------------- */ -SEXP sw_quiet(SEXP quiet); +SEXP sw_verbose(SEXP verbose); SEXP tempError(void); -SEXP onGetInputDataFromFiles(SEXP input, SEXP quiet); -SEXP start(SEXP inputOptions, SEXP inputData, SEXP weatherList, SEXP quiet); +SEXP onGetInputDataFromFiles(SEXP input); +SEXP sw_start(SEXP inputOptions, SEXP inputData, SEXP weatherList); SEXP rSW2_processAllWeather(SEXP weatherList, SEXP inputData); SEXP rSW2_readAllWeatherFromDisk( SEXP path, diff --git a/src/rSW_Carbon.c b/src/rSW_Carbon.c index 929a6f2f..aef75812 100644 --- a/src/rSW_Carbon.c +++ b/src/rSW_Carbon.c @@ -120,7 +120,7 @@ SEXP onGet_SW_CARBON(void) { * * @param object An instance of the swCarbon class. */ -void onSet_swCarbon(SEXP object) { +void onSet_swCarbon(SEXP object, LOG_INFO* LogInfo) { SW_CARBON *c = &SoilWatAll.Carbon; // Extract the slots from our object into our structure @@ -166,7 +166,7 @@ void onSet_swCarbon(SEXP object) { { UNPROTECT(1); - LogError(&LogInfo, LOGERROR, "CO2ppm object does not contain data for every year"); + LogError(LogInfo, LOGERROR, "CO2ppm object does not contain data for every year"); return; // Exit function prematurely due to error } diff --git a/src/rSW_Carbon.h b/src/rSW_Carbon.h index a75e2fd4..8fa5db23 100644 --- a/src/rSW_Carbon.h +++ b/src/rSW_Carbon.h @@ -4,5 +4,5 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_SW_CARBON(void); -void onSet_swCarbon(SEXP object); +void onSet_swCarbon(SEXP object, LOG_INFO* LogInfo); diff --git a/src/rSW_Control.c b/src/rSW_Control.c index cae1c828..c60fae61 100644 --- a/src/rSW_Control.c +++ b/src/rSW_Control.c @@ -52,15 +52,27 @@ void rSW_CTL_setup_model2(void) { } - -void rSW_CTL_obtain_inputs(Bool from_files) { +// uses global variable `InputData` if not `from_files` +/** Prepare inputs for SOILWAT2 + + Side effect is that SOILWAT2 structures contain input values + (i.e., rSOILWAT2 global variable SoilWatAll). + + @param[in] from_files If TRUE, then read inputs from disk and copy into + SoilWatAll. + @param[in] InputData If from_files is FALSE, then copy values from + InputData to SoilWatAll. + @param[in] weatherList If from_files is FALSE, then copy values from + weatherList to SoilWatAll + (unless weatherList is NULL, then slot weatherHistory of InputData is used). +*/ +void rSW_CTL_obtain_inputs(Bool from_files, SEXP InputData, SEXP weatherList, LOG_INFO* LogInfo) { #ifdef RSWDEBUG int debug = 0; #endif if (from_files) { - SW_CTL_read_inputs_from_disk(&SoilWatAll, &PathInfo, &LogInfo); - return; // Exit function prematurely due to error + SW_CTL_read_inputs_from_disk(&SoilWatAll, &PathInfo, LogInfo); } else { //Use R data to set the data #ifdef RSWDEBUG @@ -72,20 +84,29 @@ void rSW_CTL_obtain_inputs(Bool from_files) { } #endif - onSet_SW_F(GET_SLOT(InputData, install("files"))); + onSet_SW_F(GET_SLOT(InputData, install("files")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" 'files'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_SW_MDL(GET_SLOT(InputData, install("years"))); + onSet_SW_MDL(GET_SLOT(InputData, install("years")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'model'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_SW_WTH_setup(GET_SLOT(InputData, install("weather"))); + onSet_SW_WTH_setup(GET_SLOT(InputData, install("weather")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'weather-setup'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } onSet_SW_SKY(GET_SLOT(InputData, install("cloud"))); #ifdef RSWDEBUG @@ -95,51 +116,81 @@ void rSW_CTL_obtain_inputs(Bool from_files) { if ( LOGICAL(GET_SLOT(GET_SLOT(InputData, install("weather")), install("use_weathergenerator")))[0] ) { - onSet_MKV(GET_SLOT(InputData, install("markov"))); + onSet_MKV(GET_SLOT(InputData, install("markov")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'weather generator'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } } - onSet_WTH_DATA(); + if (isNull(weatherList)) { + weatherList = GET_SLOT(InputData, install("weatherHistory")); + } + onSet_WTH_DATA(weatherList, LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'weather-history'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_SW_VPD(GET_SLOT(InputData, install("prod"))); + onSet_SW_VPD(GET_SLOT(InputData, install("prod")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'veg'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_SW_SIT(GET_SLOT(InputData, install("site"))); + onSet_SW_SIT(GET_SLOT(InputData, install("site")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'site'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_SW_SOILS(GET_SLOT(InputData, install("soils"))); + onSet_SW_SOILS(GET_SLOT(InputData, install("soils")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'soils' + 'swrc parameters'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_SW_VES(GET_SLOT(InputData, install("estab"))); + onSet_SW_VES(GET_SLOT(InputData, install("estab")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'establishment'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_SW_OUT(GET_SLOT(InputData, install("output"))); + onSet_SW_OUT(GET_SLOT(InputData, install("output")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'ouput'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_swCarbon(GET_SLOT(InputData, install("carbon"))); + onSet_swCarbon(GET_SLOT(InputData, install("carbon")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'CO2'"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } - onSet_SW_SWC(GET_SLOT(InputData, install("swc"))); + onSet_SW_SWC(GET_SLOT(InputData, install("swc")), LogInfo); #ifdef RSWDEBUG if (debug) swprintf(" > 'swc'"); if (debug) swprintf(" completed.\n"); #endif + if (LogInfo->stopRun) { + return; // Exit function prematurely due to error + } } } diff --git a/src/rSW_Control.h b/src/rSW_Control.h index cf10e79e..145cca09 100644 --- a/src/rSW_Control.h +++ b/src/rSW_Control.h @@ -2,4 +2,4 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ void rSW_CTL_setup_model2(void); -void rSW_CTL_obtain_inputs(Bool from_files); +void rSW_CTL_obtain_inputs(Bool from_files, SEXP InputData, SEXP weatherList, LOG_INFO* LogInfo); diff --git a/src/rSW_Files.c b/src/rSW_Files.c index f2f30278..a05889dc 100644 --- a/src/rSW_Files.c +++ b/src/rSW_Files.c @@ -72,7 +72,7 @@ SEXP onGet_SW_F(void) { return SW_F_construct; } -void onSet_SW_F(SEXP SW_F_construct) { +void onSet_SW_F(SEXP SW_F_construct, LOG_INFO* LogInfo) { int i, j; SEXP ProjDir; SEXP FilesIn; @@ -89,8 +89,8 @@ void onSet_SW_F(SEXP SW_F_construct) { Mem_Free(PathInfo.InFiles[i]); } for (i = 0; i < j; i++) { - PathInfo.InFiles[i] = Str_Dup(CHAR(STRING_ELT(FilesIn,i)), &LogInfo); - if(LogInfo.stopRun) { + PathInfo.InFiles[i] = Str_Dup(CHAR(STRING_ELT(FilesIn,i)), LogInfo); + if(LogInfo->stopRun) { UNPROTECT(2); // Unprotect the two protected variables before exiting return; // Exit function prematurely } diff --git a/src/rSW_Files.h b/src/rSW_Files.h index b96655bc..3bdc79ad 100644 --- a/src/rSW_Files.h +++ b/src/rSW_Files.h @@ -5,4 +5,4 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_SW_F(void); -void onSet_SW_F(SEXP SW_F_construct); +void onSet_SW_F(SEXP SW_F_construct, LOG_INFO* LogInfo); diff --git a/src/rSW_Markov.c b/src/rSW_Markov.c index 9636317b..9b8b3d48 100644 --- a/src/rSW_Markov.c +++ b/src/rSW_Markov.c @@ -61,11 +61,11 @@ SEXP onGet_MKV(void) { return MKV; } -void onSet_MKV(SEXP MKV) { +void onSet_MKV(SEXP MKV, LOG_INFO* LogInfo) { SEXP MKV_prob, MKV_conv; - SW_MKV_construct(SoilWatAll.Weather.rng_seed, &SoilWatAll.Markov, &LogInfo); - if(LogInfo.stopRun) { + SW_MKV_construct(SoilWatAll.Weather.rng_seed, &SoilWatAll.Markov, LogInfo); + if(LogInfo->stopRun) { return; // Exit function prematurely due to error } @@ -74,7 +74,7 @@ void onSet_MKV(SEXP MKV) { if (!onSet_MKV_prob(MKV_prob) && SoilWatAll.Weather.generateWeatherMethod == 2) { LogError( - &LogInfo, + LogInfo, LOGERROR, "Markov weather generator: " "rSOILWAT2 failed to pass `MKV_prob` values to SOILWAT2.\n" @@ -86,7 +86,7 @@ void onSet_MKV(SEXP MKV) { if (!onSet_MKV_conv(MKV_conv) && SoilWatAll.Weather.generateWeatherMethod == 2) { LogError( - &LogInfo, + LogInfo, LOGERROR, "Markov weather generator: " "rSOILWAT2 failed to pass `MKV_conv` values to SOILWAT2.\n" diff --git a/src/rSW_Markov.h b/src/rSW_Markov.h index feea76ef..a19a3691 100644 --- a/src/rSW_Markov.h +++ b/src/rSW_Markov.h @@ -4,7 +4,7 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_MKV(void); -void onSet_MKV(SEXP MKV); +void onSet_MKV(SEXP MKV, LOG_INFO* LogInfo); SEXP onGet_MKV_prob(void); Bool onSet_MKV_prob(SEXP MKV_prob); SEXP onGet_MKV_conv(void); diff --git a/src/rSW_Model.c b/src/rSW_Model.c index 441809e4..22ca609c 100644 --- a/src/rSW_Model.c +++ b/src/rSW_Model.c @@ -90,7 +90,7 @@ SEXP onGet_SW_MDL(void) { return SW_MDL; } -void onSet_SW_MDL(SEXP SW_MDL) { +void onSet_SW_MDL(SEXP SW_MDL, LOG_INFO* LogInfo) { SW_MODEL *m = &SoilWatAll.Model; SEXP StartYear; @@ -106,13 +106,13 @@ void onSet_SW_MDL(SEXP SW_MDL) { MyFileName = PathInfo.InFiles[eModel]; if (!IS_S4_OBJECT(SW_MDL)) { - LogError(&LogInfo, LOGERROR, "%s: No input.", MyFileName); + LogError(LogInfo, LOGERROR, "%s: No input.", MyFileName); return; // Exit function prematurely due to error } PROTECT(StartYear = GET_SLOT(SW_MDL, install("StartYear"))); if (INTEGER(StartYear)[0] < 0) { - LogError(&LogInfo, LOGERROR, "%s: Negative start year (%d)", MyFileName, INTEGER(StartYear)[0]); + LogError(LogInfo, LOGERROR, "%s: Negative start year (%d)", MyFileName, INTEGER(StartYear)[0]); UNPROTECT(1); return; // Exit function prematurely due to error @@ -120,20 +120,20 @@ void onSet_SW_MDL(SEXP SW_MDL) { m->startyr = INTEGER(StartYear)[0]; PROTECT(EndYear = GET_SLOT(SW_MDL, install("EndYear"))); if (isNull(EndYear) || INTEGER(EndYear)[0] == NA_INTEGER) { - LogError(&LogInfo, LOGERROR, "%s: Ending year not found.", MyFileName); + LogError(LogInfo, LOGERROR, "%s: Ending year not found.", MyFileName); UNPROTECT(2); return; // Exit function prematurely due to error } if (INTEGER(EndYear)[0] < 0) { - LogError(&LogInfo, LOGERROR, "%s: Negative ending year (%d)", MyFileName, INTEGER(EndYear)[0]); + LogError(LogInfo, LOGERROR, "%s: Negative ending year (%d)", MyFileName, INTEGER(EndYear)[0]); UNPROTECT(2); return; // Exit function prematurely due to error } m->endyr = INTEGER(EndYear)[0]; if (m->endyr < m->startyr) { - LogError(&LogInfo, LOGERROR, "%s: Start Year > End Year", MyFileName); + LogError(LogInfo, LOGERROR, "%s: Start Year > End Year", MyFileName); UNPROTECT(2); return; // Exit function prematurely due to error @@ -168,7 +168,7 @@ void onSet_SW_MDL(SEXP SW_MDL) { m->isnorth = TRUE; } strcat(errstr, "Continuing.\n"); - LogError(&LogInfo, LOGWARN, errstr); + LogError(LogInfo, LOGWARN, errstr); } m->startstart += ((m->isnorth) ? DAYFIRST_NORTH : DAYFIRST_SOUTH) - 1; diff --git a/src/rSW_Model.h b/src/rSW_Model.h index 0ed76d39..d6b546f6 100644 --- a/src/rSW_Model.h +++ b/src/rSW_Model.h @@ -4,4 +4,4 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_SW_MDL(void); -void onSet_SW_MDL(SEXP SW_MDL); +void onSet_SW_MDL(SEXP SW_MDL, LOG_INFO* LogInfo); diff --git a/src/rSW_Output.c b/src/rSW_Output.c index e3d8f137..32c1acd8 100644 --- a/src/rSW_Output.c +++ b/src/rSW_Output.c @@ -54,7 +54,7 @@ static char *MyFileName; "Output": Updated global variables SW_Output, used_OUTNPERIODS, and timeSteps which are defined in SW_Output.c */ -void onSet_SW_OUT(SEXP OUT) { +void onSet_SW_OUT(SEXP OUT, LOG_INFO* LogInfo) { int i, msg_type; OutKey k; SEXP sep, outfile, tp_convert; @@ -103,8 +103,8 @@ void onSet_SW_OUT(SEXP OUT) { ); if (msg_type > 0) { - LogError(&LogInfo, msg_type, "%s", msg); - if(LogInfo.stopRun) { + LogError(LogInfo, msg_type, "%s", msg); + if(LogInfo->stopRun) { UNPROTECT(3); // Unprotect the three protected variables before exiting return; // Exit function prematurely due to error } @@ -112,8 +112,8 @@ void onSet_SW_OUT(SEXP OUT) { } if (SoilWatAll.Output[k].use) { - SoilWatAll.Output[k].outfile = Str_Dup(CHAR(STRING_ELT(outfile, k)), &LogInfo); - if(LogInfo.stopRun) { + SoilWatAll.Output[k].outfile = Str_Dup(CHAR(STRING_ELT(outfile, k)), LogInfo); + if(LogInfo->stopRun) { UNPROTECT(3); // Unprotect the three protected variables before exiting return; // Exit function prematurely due to error } @@ -248,7 +248,7 @@ void setGlobalrSOILWAT2_OutputVariables(SEXP outputData) { /* Experience has shown that generating the Output Data structure in R is slow compared to C * This will generate the OUTPUT data Structure and Names*/ -SEXP onGetOutput(SEXP inputData) { +SEXP onGetOutput(SEXP inputData, LOG_INFO* LogInfo) { int i, l, h, numUnprotects = 0; OutKey k; OutPeriod pd; @@ -309,8 +309,8 @@ SEXP onGetOutput(SEXP inputData) { PROTECT(stemp_KEY = NEW_OBJECT(swOutput_KEY)); numUnprotects++; - SET_SLOT(stemp_KEY, install("Title"), mkString(Str_Dup(CHAR(STRING_ELT(outfile, k)), &LogInfo))); - if(LogInfo.stopRun) { + SET_SLOT(stemp_KEY, install("Title"), mkString(Str_Dup(CHAR(STRING_ELT(outfile, k)), LogInfo))); + if(LogInfo->stopRun) { goto report; } @@ -378,19 +378,11 @@ SEXP onGetOutput(SEXP inputData) { report: { UNPROTECT(numUnprotects); + } #ifdef RSWDEBUG if (debug) swprintf(" ... done. \n"); #endif - if(LogInfo.stopRun) { - SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - } - - // The only message could be an error from this function, - // so no need to use `sw_write_logs()` - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } - return swOutput_Object; } diff --git a/src/rSW_Output.h b/src/rSW_Output.h index 74a78653..ad561987 100644 --- a/src/rSW_Output.h +++ b/src/rSW_Output.h @@ -4,6 +4,7 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_SW_OUT(void); -void onSet_SW_OUT(SEXP OUT); +void onSet_SW_OUT(SEXP OUT, LOG_INFO* LogInfo); void setGlobalrSOILWAT2_OutputVariables(SEXP outputData); -SEXP onGetOutput(SEXP inputData); +SEXP onGetOutput(SEXP inputData, LOG_INFO* LogInfo); +SEXP onGetOutputDeprecated(SEXP inputData); diff --git a/src/rSW_Site.c b/src/rSW_Site.c index 82c53638..573d7188 100644 --- a/src/rSW_Site.c +++ b/src/rSW_Site.c @@ -109,7 +109,7 @@ static SEXP onGet_SW_LYR(void) { /* Function `onSet_SW_LYR()` corresponds to SOILWAT2's `SW_LYR_read()`, previously named `_read_layers()` */ -static void onSet_SW_LYR(SEXP SW_LYR) { +static void onSet_SW_LYR(SEXP SW_LYR, LOG_INFO* LogInfo) { SW_SITE *v = &SoilWatAll.Site; LyrIndex lyrno; @@ -128,7 +128,7 @@ static void onSet_SW_LYR(SEXP SW_LYR) { /* Adjust number if new variables are added */ if (columns != 12) { LogError( - &LogInfo, + LogInfo, LOGERROR, "%s : Too few columns in layers specified (%d).\n", MyFileName, columns @@ -167,7 +167,7 @@ static void onSet_SW_LYR(SEXP SW_LYR) { if (lyrno >= MAX_LAYERS) { LogError( - &LogInfo, + LogInfo, LOGERROR, "%s : Too many layers specified (%d).\n" "Maximum number of layers is %d\n", @@ -207,7 +207,7 @@ static SEXP onGet_SW_SWRCp(void) { } /* Function `onSet_SW_SWRCp()` corresponds to SOILWAT2's `SW_SWRC_read()` */ -static void onSet_SW_SWRCp(SEXP SW_SWRCp) { +static void onSet_SW_SWRCp(SEXP SW_SWRCp, LOG_INFO* LogInfo) { SW_SITE *v = &SoilWatAll.Site; int i, k; @@ -219,7 +219,7 @@ static void onSet_SW_SWRCp(SEXP SW_SWRCp) { /* Check that we have n = `SWRC_PARAM_NMAX` values per layer */ if (ncols(SW_SWRCp) != SWRC_PARAM_NMAX) { LogError( - &LogInfo, + LogInfo, LOGERROR, "%s : Bad number of SWRC parameters %d -- must be %d.\n", MyFileName, ncols(SW_SWRCp), SWRC_PARAM_NMAX @@ -230,7 +230,7 @@ static void onSet_SW_SWRCp(SEXP SW_SWRCp) { /* Check that we have `SW_Site.n_layers` */ if (nrows(SW_SWRCp) != SoilWatAll.Site.n_layers) { LogError( - &LogInfo, + LogInfo, LOGERROR, "%s : Number of layers with SWRC parameters (%d) " "must match number of soil layers (%d)\n", @@ -269,14 +269,14 @@ SEXP onGet_SW_SOILS(void) { } /* Copy S4 class "swSoils" into SOILWAT2 soil properties and SWRC parameters */ -void onSet_SW_SOILS(SEXP SW_SOILS) { +void onSet_SW_SOILS(SEXP SW_SOILS, LOG_INFO* LogInfo) { SEXP SW_LYR, SW_SWRCp; PROTECT(SW_LYR = GET_SLOT(SW_SOILS, install("Layers"))); - onSet_SW_LYR(SW_LYR); + onSet_SW_LYR(SW_LYR, LogInfo); PROTECT(SW_SWRCp = GET_SLOT(SW_SOILS, install("SWRCp"))); - onSet_SW_SWRCp(SW_SWRCp); + onSet_SW_SWRCp(SW_SWRCp, LogInfo); UNPROTECT(2); } @@ -480,7 +480,7 @@ SEXP onGet_SW_SIT(void) { return SW_SIT; } -void onSet_SW_SIT(SEXP SW_SIT) { +void onSet_SW_SIT(SEXP SW_SIT, LOG_INFO* LogInfo) { int i; SW_SITE *v = &SoilWatAll.Site; @@ -612,8 +612,8 @@ void onSet_SW_SIT(SEXP SW_SIT) { PROTECT(swrc_flags = GET_SLOT(SW_SIT, install("swrc_flags"))); strcpy(v->site_swrc_name, CHAR(STRING_ELT(swrc_flags, 0))); - v->site_swrc_type = encode_str2swrc(v->site_swrc_name, &LogInfo); - if(LogInfo.stopRun) { + v->site_swrc_type = encode_str2swrc(v->site_swrc_name, LogInfo); + if(LogInfo->stopRun) { UNPROTECT(12); // Unprotect the twelve protected variables before exiting return; // Exit function prematurely due to error } @@ -638,7 +638,7 @@ void onSet_SW_SIT(SEXP SW_SIT) { } } if (too_many_regions) { - LogError(&LogInfo, LOGERROR, "siteparam.in : Number of transpiration regions" + LogError(LogInfo, LOGERROR, "siteparam.in : Number of transpiration regions" " exceeds maximum allowed (%d > %d)\n", v->n_transp_rgn, MAX_TRANSP_REGIONS); UNPROTECT(14); // Unprotect the fourteen protected variables before exiting @@ -651,7 +651,7 @@ void onSet_SW_SIT(SEXP SW_SIT) { /* check for any discontinuities (reversals) in the transpiration regions */ for (r = 1; r < v->n_transp_rgn; r++) { if (v->_TranspRgnBounds[r - 1] >= v->_TranspRgnBounds[r]) { - LogError(&LogInfo, LOGERROR, "siteparam.in : Discontinuity/reversal in transpiration regions.\n"); + LogError(LogInfo, LOGERROR, "siteparam.in : Discontinuity/reversal in transpiration regions.\n"); UNPROTECT(14); // Unprotect the fourteen protected variables before exiting return; // Exit function prematurely due to error diff --git a/src/rSW_Site.h b/src/rSW_Site.h index 65a3b3d0..5f455333 100644 --- a/src/rSW_Site.h +++ b/src/rSW_Site.h @@ -4,6 +4,6 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_SW_SIT(void); -void onSet_SW_SIT(SEXP SW_SIT); +void onSet_SW_SIT(SEXP SW_SIT, LOG_INFO* LogInfo); SEXP onGet_SW_SOILS(void); -void onSet_SW_SOILS(SEXP SW_SOILS); +void onSet_SW_SOILS(SEXP SW_SOILS, LOG_INFO* LogInfo); diff --git a/src/rSW_SoilWater.c b/src/rSW_SoilWater.c index 7af992bd..89872191 100644 --- a/src/rSW_SoilWater.c +++ b/src/rSW_SoilWater.c @@ -57,7 +57,7 @@ void rSW_SWC_construct(void) { } -SEXP onGet_SW_SWC(void) { +SEXP onGet_SW_SWC(LOG_INFO* LogInfo) { SW_SOILWAT *v = &SoilWatAll.SoilWat; SEXP swSWC; SEXP SWC; @@ -87,7 +87,7 @@ SEXP onGet_SW_SWC(void) { SET_SLOT(SWC, install(cSWC[3]), swcMethod); if(v->hist_use) - SET_SLOT(SWC,install(cSWC[4]),onGet_SW_SWC_hists()); + SET_SLOT(SWC,install(cSWC[4]),onGet_SW_SWC_hists(LogInfo)); else SET_SLOT(SWC,install(cSWC[4]),NEW_LIST(0)); @@ -95,7 +95,7 @@ SEXP onGet_SW_SWC(void) { return SWC; } -void onSet_SW_SWC(SEXP SWC) { +void onSet_SW_SWC(SEXP SWC, LOG_INFO* LogInfo) { SW_SOILWAT *v = &SoilWatAll.SoilWat; SEXP swcUseData; SEXP swcFilePrefix; @@ -116,8 +116,8 @@ void onSet_SW_SWC(SEXP SWC) { //if (!isnull(v->hist.file_prefix)) {//Clear memory before setting it // Mem_Free(v->hist.file_prefix); //} - v->hist.file_prefix = (char *) Str_Dup(CHAR(STRING_ELT(swcFilePrefix,0)), &LogInfo); - if(LogInfo.stopRun) { + v->hist.file_prefix = (char *) Str_Dup(CHAR(STRING_ELT(swcFilePrefix,0)), LogInfo); + if(LogInfo->stopRun) { UNPROTECT(4); // Unprotect the four protected variables before exiting return; // Exit function prematurely due to error } @@ -126,7 +126,7 @@ void onSet_SW_SWC(SEXP SWC) { v->hist.method = INTEGER(swcMethod)[0]; if (v->hist.method < 1 || v->hist.method > 2) { - LogError(&LogInfo, LOGERROR, "swcsetup.in : Invalid swc adjustment method."); + LogError(LogInfo, LOGERROR, "swcsetup.in : Invalid swc adjustment method."); UNPROTECT(4); // Unprotect the four protected variables before exiting return; // Exit function prematurely due to error @@ -137,7 +137,7 @@ void onSet_SW_SWC(SEXP SWC) { } -SEXP onGet_SW_SWC_hists(void) { +SEXP onGet_SW_SWC_hists(LOG_INFO* LogInfo) { TimeInt year; SEXP SWC_hists, SWC_hists_names; int years = ((SoilWatAll.Model.endyr + 1) - SoilWatAll.Model.startyr), i = 0; @@ -148,13 +148,13 @@ SEXP onGet_SW_SWC_hists(void) { for (year = SoilWatAll.Model.startyr; year <= SoilWatAll.Model.endyr; year++) { if (SoilWatAll.SoilWat.hist_use && year >= SoilWatAll.SoilWat.hist.yr.first) { - _read_swc_hist(&SoilWatAll.SoilWat.hist, year, &LogInfo); - if(LogInfo.stopRun) { + _read_swc_hist(&SoilWatAll.SoilWat.hist, year, LogInfo); + if(LogInfo->stopRun) { UNPROTECT(2); // Unprotect the two protected variables before exiting return NULL; // Exit function prematurely due to error } - SET_VECTOR_ELT(SWC_hists, i, onGet_SW_SWC_hist(year)); + SET_VECTOR_ELT(SWC_hists, i, onGet_SW_SWC_hist(year, LogInfo)); snprintf(cYear, sizeof cYear, "%4d", year); SET_STRING_ELT(SWC_hists_names, i, mkChar(cYear)); } @@ -165,8 +165,8 @@ SEXP onGet_SW_SWC_hists(void) { return SWC_hists; } -SEXP onGet_SW_SWC_hist(TimeInt year) { - LogError(&LogInfo, LOGERROR, +SEXP onGet_SW_SWC_hist(TimeInt year, LOG_INFO* LogInfo) { + LogError(LogInfo, LOGERROR, "'onGet_SW_SWC_hist' is currently not functional.\n"); return NULL; // Exit function prematurely due to error @@ -204,15 +204,15 @@ SEXP onGet_SW_SWC_hist(TimeInt year) { return lyrs; } -void onSet_SW_SWC_hist(void) { - LogError(&LogInfo, LOGERROR, - "'onSet_SW_SWC_hist' is currently not functional.\n"); +void onSet_SW_SWC_hist(LOG_INFO* LogInfo) { + LogError(LogInfo, LOGERROR, + "'onSet_SW_SWC_hist' is currently not functional (see `_read_swc_hist()`).\n"); return; // Exit function prematurely due to error int i, j = 0; SW_SOILWAT *v = &SoilWatAll.SoilWat; RealD *p_lyrs; - SEXP lyrs = VECTOR_ELT(VECTOR_ELT(VECTOR_ELT(InputData,7),4),swcdataIndex); + SEXP lyrs; // lyrs = VECTOR_ELT(VECTOR_ELT(VECTOR_ELT(InputData,7),4),swcdataIndex); swcdataIndex++; p_lyrs = REAL(lyrs); diff --git a/src/rSW_SoilWater.h b/src/rSW_SoilWater.h index 926361ee..b37fce54 100644 --- a/src/rSW_SoilWater.h +++ b/src/rSW_SoilWater.h @@ -5,8 +5,8 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ void rSW_SWC_construct(void); -SEXP onGet_SW_SWC(void); -void onSet_SW_SWC(SEXP SWC); -SEXP onGet_SW_SWC_hists(void); -SEXP onGet_SW_SWC_hist(TimeInt year); -void onSet_SW_SWC_hist(void); +SEXP onGet_SW_SWC(LOG_INFO* LogInfo); +void onSet_SW_SWC(SEXP SWC, LOG_INFO* LogInfo); +SEXP onGet_SW_SWC_hists(LOG_INFO* LogInfo); +SEXP onGet_SW_SWC_hist(TimeInt year, LOG_INFO* LogInfo); +void onSet_SW_SWC_hist(LOG_INFO* LogInfo); diff --git a/src/rSW_VegEstab.c b/src/rSW_VegEstab.c index fc1ee541..beca5769 100644 --- a/src/rSW_VegEstab.c +++ b/src/rSW_VegEstab.c @@ -71,7 +71,7 @@ SEXP onGet_SW_VES(void) { return VES; } -void onSet_SW_VES(SEXP VES) { +void onSet_SW_VES(SEXP VES, LOG_INFO* LogInfo) { IntU i; int nSPPS; SoilWatAll.VegEstab.use = TRUE; @@ -87,29 +87,30 @@ void onSet_SW_VES(SEXP VES) { } else { nSPPS = INTEGER(count)[0]; if (nSPPS == 0) { - LogError(&LogInfo, LOGWARN, "Establishment is TRUE but no data. Setting False."); + LogError(LogInfo, LOGWARN, "Establishment is TRUE but no data. Setting False."); SoilWatAll.VegEstab.use = FALSE; } else { SoilWatAll.VegEstab.use = TRUE; for (i = 0; i < nSPPS; i++) - onSet_SW_VES_spp(VES, i); // sets `SW_VegEstab.count` incrementally + onSet_SW_VES_spp(VES, i, LogInfo); // sets `SW_VegEstab.count` incrementally + if (LogInfo->stopRun) { + goto report; // Exit function prematurely due to error + } } } - if (EchoInits) - LogError(&LogInfo, LOGWARN, "Establishment not used.\n"); - - SW_VegEstab_construct(&SoilWatAll.VegEstab, &LogInfo); - if(LogInfo.stopRun) { - UNPROTECT(2); // Unprotect the two protected variables before exiting - return; // Exit function prematurely due to error + SW_VegEstab_construct(&SoilWatAll.VegEstab, LogInfo); + if(LogInfo->stopRun) { + goto report; // Exit function prematurely due to error } if (EchoInits) _echo_VegEstab(SoilWatAll.Site.width, SoilWatAll.VegEstab.parms, SoilWatAll.VegEstab.count); - UNPROTECT(2); + report: { + UNPROTECT(2); + } } void onGet_SW_VES_spps(SEXP SPP) { @@ -177,13 +178,13 @@ void onGet_SW_VES_spps(SEXP SPP) { UNPROTECT(17); } -void onSet_SW_VES_spp(SEXP SPP, IntU i) { +void onSet_SW_VES_spp(SEXP SPP, IntU i, LOG_INFO* LogInfo) { SW_VEGESTAB_INFO *v; SEXP fileName, Name; unsigned int count; - count = _new_species(&SoilWatAll.VegEstab, &LogInfo); - if(LogInfo.stopRun) { + count = _new_species(&SoilWatAll.VegEstab, LogInfo); + if(LogInfo->stopRun) { return; // Exit function prematurely due to error } @@ -211,7 +212,7 @@ void onSet_SW_VES_spp(SEXP SPP, IntU i) { strcpy(v->sppFileName, CHAR(STRING_ELT(fileName,i)) ); /* check for valid name first */ if (strlen(CHAR(STRING_ELT(Name,i))) > MAX_SPECIESNAMELEN) { - LogError(&LogInfo, LOGERROR, "Species name too long (> 4 chars).\n\tTry again.\n"); + LogError(LogInfo, LOGERROR, "Species name too long (> 4 chars).\n\tTry again.\n"); } else { strcpy(v->sppname, CHAR(STRING_ELT(Name,i)) ); } diff --git a/src/rSW_VegEstab.h b/src/rSW_VegEstab.h index 189fc09f..bd02db60 100644 --- a/src/rSW_VegEstab.h +++ b/src/rSW_VegEstab.h @@ -6,6 +6,6 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_SW_VES(void); -void onSet_SW_VES(SEXP VES); +void onSet_SW_VES(SEXP VES, LOG_INFO* LogInfo); void onGet_SW_VES_spps(SEXP SPP); -void onSet_SW_VES_spp(SEXP SPP, IntU i); +void onSet_SW_VES_spp(SEXP SPP, IntU i, LOG_INFO* LogInfo); diff --git a/src/rSW_VegProd.c b/src/rSW_VegProd.c index a683108c..cb79b326 100644 --- a/src/rSW_VegProd.c +++ b/src/rSW_VegProd.c @@ -415,7 +415,7 @@ SEXP onGet_SW_VPD(void) { return VegProd; } -void onSet_SW_VPD(SEXP SW_VPD) { +void onSet_SW_VPD(SEXP SW_VPD, LOG_INFO* LogInfo) { int i; SW_VEGPROD *v = &SoilWatAll.VegProd; @@ -625,8 +625,8 @@ void onSet_SW_VPD(SEXP SW_VPD) { v->veg[SW_FORBS].co2_wue_coeff2 = REAL(CO2Coefficients)[15]; - SW_VPD_fix_cover(&SoilWatAll.VegProd, &LogInfo); - if(LogInfo.stopRun) { + SW_VPD_fix_cover(&SoilWatAll.VegProd, LogInfo); + if(LogInfo->stopRun) { UNPROTECT(18); // Unprotect the eighteen protected variables before exiting return; // Exit function prematurely due to error } @@ -637,6 +637,7 @@ void onSet_SW_VPD(SEXP SW_VPD) { UNPROTECT(18); } +// `estimate_PotNatVeg_composition()` is R interface to rSW2_estimate_PotNatVeg_composition() SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_monthly_ppt_mm, SEXP mean_monthly_Temp_C, SEXP shrub_limit, SEXP SumGrasses_Fraction, SEXP fill_empty_with_BareGround, SEXP warn_extrapolation, SEXP dailyC4vars, @@ -645,8 +646,10 @@ SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_mont SEXP Trees_Fraction, SEXP BareGround_Fraction) { double RelAbundanceL0[8], RelAbundanceL1[5], grasses[3]; + LOG_INFO local_LogInfo; - sw_init_logs(LogInfo.logfp, &local_LogInfo); + sw_init_logs(current_sw_verbosity, &local_LogInfo); + // "final_" in the beginning meaning it's the final R -> conversion double final_MAP_cm = asReal(MAP_mm) / 10, final_MAT_C = asReal(MAT_C), final_MonPPT_cm[MAX_MONTHS], @@ -749,16 +752,11 @@ SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_mont SET_VECTOR_ELT(res, 2, final_grasses); report: { + // Note: no SOILWAT2 memory was allocated, nothing to deallocate UNPROTECT(8); - if(local_LogInfo.numWarnings > 0) { - sw_write_logs(FALSE, &local_LogInfo); // Note: `FALSE` is not used - } - - if(local_LogInfo.stopRun) { - SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - sw_check_exit(FALSE, &local_LogInfo); // Note: `FALSE` is not used - } + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); } return res; diff --git a/src/rSW_VegProd.h b/src/rSW_VegProd.h index b1d0d559..fffa1f97 100644 --- a/src/rSW_VegProd.h +++ b/src/rSW_VegProd.h @@ -4,7 +4,7 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_SW_VPD(void); -void onSet_SW_VPD(SEXP SW_VPD); +void onSet_SW_VPD(SEXP SW_VPD, LOG_INFO* LogInfo); SEXP rSW2_estimate_PotNatVeg_composition(SEXP MAP_mm, SEXP MAT_C, SEXP mean_monthly_ppt_mm, SEXP mean_monthly_Temp_C, SEXP shrub_limit, SEXP SumGrasses_Fraction, SEXP fill_empty_with_BareGround, SEXP warn_extrapolation, SEXP dailyC4vars, diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index 936369b7..a5485bee 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -28,7 +28,7 @@ #include "SOILWAT2/include/SW_Weather.h" #include "rSW_Weather.h" -#include "SW_R_lib.h" // externs `InputData`, `WeatherList`, `bWeatherList` +#include "SW_R_lib.h" // externs `SoilWatAll` #include #include @@ -74,14 +74,16 @@ static void rSW2_setAllWeather( Bool *dailyInputFlags, RealD *cloudcov, RealD *windspeed, - RealD *r_humidity + RealD *r_humidity, + LOG_INFO* LogInfo ); static void rSW2_set_weather_hist( SEXP listAllW, TimeInt year, SW_WEATHER_HIST *yearWeather, - Bool *dailyInputFlags + Bool *dailyInputFlags, + LOG_INFO* LogInfo ); static double value_or_missing(double x) { @@ -202,7 +204,7 @@ SEXP onGet_SW_WTH_setup(void) { Called by `rSW_CTL_obtain_inputs()` if `from_files` is `FALSE`. */ -void onSet_SW_WTH_setup(SEXP SW_WTH) { +void onSet_SW_WTH_setup(SEXP SW_WTH, LOG_INFO* LogInfo) { int i; SW_WEATHER *w = &SoilWatAll.Weather; SEXP @@ -294,7 +296,7 @@ void onSet_SW_WTH_setup(SEXP SW_WTH) { w->use_humidityMonthly, w->use_windSpeedMonthly, w->dailyInputFlags, - &LogInfo + LogInfo ); UNPROTECT(11); @@ -428,14 +430,7 @@ SEXP onGet_WTH_DATA_YEAR(TimeInt year) { `SW_Sky` (via `onSet_SW_SKY()`) must be set/updated before this function is called. */ -void onSet_WTH_DATA(void) { - - SEXP listAllWeather; - - // Determine which `rSOILWAT2` list of `swWeatherData` we are using - listAllWeather = bWeatherList ? - WeatherList : - GET_SLOT(InputData, install("weatherHistory")); +void onSet_WTH_DATA(SEXP weatherList, LOG_INFO* LogInfo) { // Deallocate (previous, if any) `allHist` @@ -448,8 +443,8 @@ void onSet_WTH_DATA(void) { SoilWatAll.Weather.startYear = SoilWatAll.Model.startyr; // Allocate new `allHist` (based on current `SW_Weather.n_years`) - allocateAllWeather(&SoilWatAll.Weather, &LogInfo); - if(LogInfo.stopRun) { + allocateAllWeather(&SoilWatAll.Weather, LogInfo); + if(LogInfo->stopRun) { return; // Exit function prematurely due to error } @@ -457,7 +452,7 @@ void onSet_WTH_DATA(void) { // Equivalent to `readAllWeather()`: // fill `SOILWAT2` `allHist` with values from `rSOILWAT2` rSW2_setAllWeather( - listAllWeather, + weatherList, SoilWatAll.Weather.allHist, SoilWatAll.Weather.startYear, SoilWatAll.Weather.n_years, @@ -468,7 +463,8 @@ void onSet_WTH_DATA(void) { SoilWatAll.Weather.dailyInputFlags, SoilWatAll.Sky.cloudcov, SoilWatAll.Sky.windspeed, - SoilWatAll.Sky.r_humidity + SoilWatAll.Sky.r_humidity, + LogInfo ); } @@ -487,7 +483,8 @@ static void rSW2_setAllWeather( Bool *dailyInputFlags, RealD *cloudcov, RealD *windspeed, - RealD *r_humidity + RealD *r_humidity, + LOG_INFO* LogInfo ) { unsigned int yearIndex, year; @@ -530,8 +527,13 @@ static void rSW2_setAllWeather( listAllW, year, allHist[yearIndex], - dailyInputFlags + dailyInputFlags, + LogInfo ); + + if(LogInfo->stopRun) { + return; // Exit function prematurely due to error + } } } } @@ -543,7 +545,8 @@ static void rSW2_set_weather_hist( SEXP listAllW, TimeInt year, SW_WEATHER_HIST *yearWeather, - Bool *dailyInputFlags + Bool *dailyInputFlags, + LOG_INFO* LogInfo ) { // Locate suitable year among rSOILWAT2 list of `swWeatherData` @@ -570,7 +573,7 @@ static void rSW2_set_weather_hist( numDaysYear = Time_get_lastdoy_y(year); if (nrows(yrWData) != numDaysYear) { LogError( - &LogInfo, + LogInfo, LOGERROR, "Weather data (year %d): " "expected %d rows (had %d).\n", @@ -609,7 +612,7 @@ static void rSW2_set_weather_hist( if (doy != p_yrWData[doy + numDaysYear * 0] - 1) { LogError( - &LogInfo, + LogInfo, LOGERROR, "Weather data (year %d): " "day of year out of range (%d), expected: %d.\n", @@ -796,6 +799,7 @@ static void rSW2_set_weather_hist( } } +// `calc_SiteClimate()` is R interface to rSW2_calc_SiteClimate() SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SEXP do_C4vars, SEXP do_Cheatgrass_ClimVars, SEXP latitude) { @@ -804,7 +808,13 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SW_CLIMATE_YEARLY climateOutput; SW_CLIMATE_CLIM climateAverages; - SW_MODEL *SW_Model = &SoilWatAll.Model; + LOG_INFO local_LogInfo; + sw_init_logs(current_sw_verbosity, &local_LogInfo); + + TimeInt + days_in_month[MAX_MONTHS], + cum_monthdays[MAX_MONTHS]; + int numYears = asInteger(yearEnd) - asInteger(yearStart) + 1, year, calcSiteOutputNum = 10, index, numUnprotects = 11; @@ -842,8 +852,8 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, allHist = (SW_WEATHER_HIST **)Mem_Malloc(sizeof(SW_WEATHER_HIST *) * numYears, "rSW2_calc_SiteClimate", - &LogInfo); - if(LogInfo.stopRun) { + &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } @@ -852,13 +862,13 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, for(year = 0; year < numYears; year++) { allHist[year] = (SW_WEATHER_HIST *)Mem_Malloc(sizeof(SW_WEATHER_HIST), "rSW2_calc_SiteClimate", - &LogInfo); - if(LogInfo.stopRun) { + &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } } - Time_init_model(SoilWatAll.Model.days_in_month); + Time_init_model(days_in_month); // Set `dailyInputFlags`: currently, `calcSiteClimate()` use only tmax, tmin, ppt for (index = 0; index < MAX_INPUT_COLUMNS; index++) { @@ -881,18 +891,22 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, dailyInputFlags, cloudcov, windspeed, - r_humidity + r_humidity, + &local_LogInfo ); + if(local_LogInfo.stopRun) { + goto report; + } // Allocate memory of structs for climate on SOILWAT side allocateClimateStructs(numYears, &climateOutput, &climateAverages, - &LogInfo); - if(LogInfo.stopRun) { + &local_LogInfo); + if(local_LogInfo.stopRun) { goto report; } // Calculate climate variables - calcSiteClimate(allHist, SW_Model->cum_monthdays, SW_Model->days_in_month, + calcSiteClimate(allHist, cum_monthdays, days_in_month, numYears, asInteger(yearStart), inNorthHem, &climateOutput); // Average climate variables @@ -970,17 +984,14 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SET_VECTOR_ELT(res, 8, C4Variables); SET_VECTOR_ELT(res, 9, Cheatgrass); - deallocateClimateStructs(&climateOutput, &climateAverages); - report: { + // Note: no SOILWAT2 memory was allocated UNPROTECT(numUnprotects); - + deallocateClimateStructs(&climateOutput, &climateAverages); free_allHist(allHist, numYears); - if(LogInfo.stopRun) { - SW_CTL_clear_model(FALSE, &SoilWatAll, &PathInfo); - sw_check_exit(FALSE, &LogInfo); // Note: `FALSE` is not used - } + sw_write_warnings(&local_LogInfo); + sw_fail_on_error(&local_LogInfo); } return res; @@ -998,13 +1009,15 @@ void init_allHist_years(SW_WEATHER_HIST **allHist, int numYears) { void free_allHist(SW_WEATHER_HIST **allHist, int numYears) { int year; - for(year = 0; year < numYears; year++) { - if(!isnull(allHist[year])) { - free(allHist[year]); + if(!isnull(allHist)) { + + for(year = 0; year < numYears; year++) { + if(!isnull(allHist[year])) { + free(allHist[year]); + } } - } - if(!isnull(allHist)) { free(allHist); + allHist = NULL; } } diff --git a/src/rSW_Weather.h b/src/rSW_Weather.h index 4c79c439..ed930f5c 100644 --- a/src/rSW_Weather.h +++ b/src/rSW_Weather.h @@ -7,9 +7,9 @@ /* Global Function Declarations */ /* --------------------------------------------------- */ SEXP onGet_SW_WTH_setup(void); -void onSet_SW_WTH_setup(SEXP SW_WTH); +void onSet_SW_WTH_setup(SEXP SW_WTH, LOG_INFO* LogInfo); SEXP onGet_WTH_DATA(void); -void onSet_WTH_DATA(void); +void onSet_WTH_DATA(SEXP weatherList, LOG_INFO* LogInfo); SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SEXP do_C4vars, SEXP do_Cheatgrass_ClimVars, SEXP latitude); void init_allHist_years(SW_WEATHER_HIST **allHist, int numYears); diff --git a/tests/testthat/test_Vegetation.R b/tests/testthat/test_Vegetation.R index 5273b630..6eb6506f 100644 --- a/tests/testthat/test_Vegetation.R +++ b/tests/testthat/test_Vegetation.R @@ -284,7 +284,8 @@ test_that("Vegetation: estimate land cover composition", { mean_monthly_ppt_mm = rep(0, 12), mean_monthly_Temp_C = rep(0, 12), fix_succulents = TRUE, Succulents_Fraction = 10 - ) + ), + regexp = "relative abundance values sum to more than 1 = full land cover" ) # issue 219: output incorrectly contained negative cover @@ -301,7 +302,8 @@ test_that("Vegetation: estimate land cover composition", { Shrubs_Fraction = 0.5, fix_sumgrasses = TRUE, SumGrasses_Fraction = 0.7 - ) + ), + regexp = "relative abundance values sum to more than 1 = full land cover" ) # (ii) all fixed but sum is less than 1 and !fill_empty_with_BareGround @@ -320,7 +322,8 @@ test_that("Vegetation: estimate land cover composition", { fix_trees = TRUE, Trees_Fraction = 0, fix_BareGround = TRUE, BareGround_Fraction = 0, fill_empty_with_BareGround = FALSE - ) + ), + regexp = "all fixed, but their sum is smaller than 1" ) # The last errors are avoided if `fill_empty_with_BareGround = TRUE` @@ -345,9 +348,8 @@ test_that("Vegetation: estimate land cover composition", { expect_pnv(pnv[1:2]) expect_equal(pnv[["Rel_Abundance_L0"]][ibar], 1, ignore_attr = "names") - # Make sure `SOILWAT2` throws a warning that R, we use `sw_verbosity()` - # to do that - prev_quiet <- sw_verbosity(TRUE) + # Make sure `SOILWAT2` throws a warning + prev_verbosity <- sw_verbosity(TRUE) # Expecting warning because MAT_C is outside of formulas domain expect_warning( @@ -357,11 +359,12 @@ test_that("Vegetation: estimate land cover composition", { mean_monthly_ppt_mm = c(0, 0, rep(100, 9), 0), mean_monthly_Temp_C = rep(-10, 12), fill_empty_with_BareGround = FALSE - ) + ), + regexp = "Equations used outside supported range" ) # Undo what the previous call to `sw_verbosity()` did - sw_verbosity(prev_quiet) + sw_verbosity(prev_verbosity) }) From e6ee3fe882c2da3625673c9151ba85e0e3273a94 Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Sat, 7 Oct 2023 00:18:28 -0700 Subject: [PATCH 17/24] Silence compiler warning [-Wmisleading-indentation] --- src/rSW_VegEstab.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rSW_VegEstab.c b/src/rSW_VegEstab.c index beca5769..194e06b3 100644 --- a/src/rSW_VegEstab.c +++ b/src/rSW_VegEstab.c @@ -93,9 +93,10 @@ void onSet_SW_VES(SEXP VES, LOG_INFO* LogInfo) { SoilWatAll.VegEstab.use = TRUE; for (i = 0; i < nSPPS; i++) onSet_SW_VES_spp(VES, i, LogInfo); // sets `SW_VegEstab.count` incrementally - if (LogInfo->stopRun) { - goto report; // Exit function prematurely due to error - } + + if (LogInfo->stopRun) { + goto report; // Exit function prematurely due to error + } } } From 2986765041e48758fe1f0715d8ba29e6d6c92c51 Mon Sep 17 00:00:00 2001 From: Daniel Schlaepfer Date: Fri, 13 Oct 2023 18:02:00 -0400 Subject: [PATCH 18/24] SOILWAT2 devel on v7.2.0: RNG abstracted in new type - RNG functionality is now encapsulated in type "sw_random_t" --> rSOILWAT2 no longer requires that PCG is present (despite it was never actually used by rSOILWAT2) - remove SOILWAT2 submodules that are not used by rSOILWAT2 --- .gitmodules | 2 +- src/Makevars | 4 ++-- src/SOILWAT2 | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 39058bbd..0ab16695 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/SOILWAT2"] path = src/SOILWAT2 url = https://github.com/DrylandEcology/SOILWAT2 - branch = master + branch = release/devel_v7.2.0 diff --git a/src/Makevars b/src/Makevars index 70a22a67..491bcbb4 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,5 +1,5 @@ target = SOILWAT2 -lib_target = lib$(target).a +lib_target = libr$(target).a path_target = SOILWAT2 path_lib = $(path_target)/bin sw_sources = src/SW_Output_outarray.c @@ -21,7 +21,7 @@ $(path_lib)/$(lib_target): # Note: `-I..` is required for rSOILWAT2 headers that are included by # SOILWAT2 headers (such as `#include "rSW_SoilWater.h"`) # -@(cd $(path_target) && $(MAKE) compiler_version) - cd $(path_target) && $(MAKE) lib \ + cd $(path_target) && $(MAKE) libr \ CC="$(CC)" CPPFLAGS="$(ALL_CPPFLAGS) -I.." CFLAGS="$(ALL_CFLAGS)" \ AR="$(AR)" sw_sources="$(sw_sources)" diff --git a/src/SOILWAT2 b/src/SOILWAT2 index 2c5900db..d05beebe 160000 --- a/src/SOILWAT2 +++ b/src/SOILWAT2 @@ -1 +1 @@ -Subproject commit 2c5900dbecd7140599609dd7acbf47de82c5a1c4 +Subproject commit d05beebe72bf57919646395c3d2d329822bb12fc From e185e629b4f381f70f1cc71518f653267bf96bad Mon Sep 17 00:00:00 2001 From: Daniel Schlaepfer Date: Mon, 16 Oct 2023 14:25:04 -0400 Subject: [PATCH 19/24] Fix early return on error for `onSet_SW_VES()` - now, check for error inside for loop (instead of afterwards) - see commit e6ee3fe882c2da3625673c9151ba85e0e3273a94 --- src/rSW_VegEstab.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rSW_VegEstab.c b/src/rSW_VegEstab.c index 194e06b3..53212759 100644 --- a/src/rSW_VegEstab.c +++ b/src/rSW_VegEstab.c @@ -91,12 +91,13 @@ void onSet_SW_VES(SEXP VES, LOG_INFO* LogInfo) { SoilWatAll.VegEstab.use = FALSE; } else { SoilWatAll.VegEstab.use = TRUE; - for (i = 0; i < nSPPS; i++) + for (i = 0; i < nSPPS; i++) { onSet_SW_VES_spp(VES, i, LogInfo); // sets `SW_VegEstab.count` incrementally if (LogInfo->stopRun) { goto report; // Exit function prematurely due to error } + } } } From 2ff36ef5e13452dd6b197560efb6911fa79fa593 Mon Sep 17 00:00:00 2001 From: Daniel Schlaepfer Date: Mon, 16 Oct 2023 15:38:22 -0400 Subject: [PATCH 20/24] Update SOILWAT2 devel: generalized (de)allocate weather data --- src/SOILWAT2 | 2 +- src/rSW_Weather.c | 47 +++++------------------------------------------ src/rSW_Weather.h | 2 -- 3 files changed, 6 insertions(+), 45 deletions(-) diff --git a/src/SOILWAT2 b/src/SOILWAT2 index 6423e4f2..43da1ab2 160000 --- a/src/SOILWAT2 +++ b/src/SOILWAT2 @@ -1 +1 @@ -Subproject commit 6423e4f2d14504bd9a19e7612d6ecc34cfb37d9a +Subproject commit 43da1ab23132089913673c64cff2a58c4b8b3bd9 diff --git a/src/rSW_Weather.c b/src/rSW_Weather.c index a5485bee..e0e23bad 100644 --- a/src/rSW_Weather.c +++ b/src/rSW_Weather.c @@ -436,14 +436,14 @@ void onSet_WTH_DATA(SEXP weatherList, LOG_INFO* LogInfo) { // Deallocate (previous, if any) `allHist` // (using value of `SW_Weather.n_years` previously used to allocate) // `SW_WTH_construct()` sets `n_years` to zero - deallocateAllWeather(&SoilWatAll.Weather); + deallocateAllWeather(SoilWatAll.Weather.allHist, SoilWatAll.Weather.n_years); // Update number of years and first calendar year represented SoilWatAll.Weather.n_years = SoilWatAll.Model.endyr - SoilWatAll.Model.startyr + 1; SoilWatAll.Weather.startYear = SoilWatAll.Model.startyr; // Allocate new `allHist` (based on current `SW_Weather.n_years`) - allocateAllWeather(&SoilWatAll.Weather, LogInfo); + allocateAllWeather(&SoilWatAll.Weather.allHist, SoilWatAll.Weather.n_years, LogInfo); if(LogInfo->stopRun) { return; // Exit function prematurely due to error } @@ -816,7 +816,7 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, cum_monthdays[MAX_MONTHS]; - int numYears = asInteger(yearEnd) - asInteger(yearStart) + 1, year, calcSiteOutputNum = 10, + int numYears = asInteger(yearEnd) - asInteger(yearStart) + 1, calcSiteOutputNum = 10, index, numUnprotects = 11; Bool dailyInputFlags[MAX_INPUT_COLUMNS]; @@ -850,24 +850,11 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, C4Variables = PROTECT(allocVector(REALSXP, 6)); Cheatgrass = PROTECT(allocVector(REALSXP, 6)); - allHist = (SW_WEATHER_HIST **)Mem_Malloc(sizeof(SW_WEATHER_HIST *) * numYears, - "rSW2_calc_SiteClimate", - &local_LogInfo); + allocateAllWeather(&allHist, numYears, &local_LogInfo); if(local_LogInfo.stopRun) { goto report; } - init_allHist_years(allHist, numYears); - - for(year = 0; year < numYears; year++) { - allHist[year] = (SW_WEATHER_HIST *)Mem_Malloc(sizeof(SW_WEATHER_HIST), - "rSW2_calc_SiteClimate", - &local_LogInfo); - if(local_LogInfo.stopRun) { - goto report; - } - } - Time_init_model(days_in_month); // Set `dailyInputFlags`: currently, `calcSiteClimate()` use only tmax, tmin, ppt @@ -988,7 +975,7 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, // Note: no SOILWAT2 memory was allocated UNPROTECT(numUnprotects); deallocateClimateStructs(&climateOutput, &climateAverages); - free_allHist(allHist, numYears); + deallocateAllWeather(allHist, numYears); sw_write_warnings(&local_LogInfo); sw_fail_on_error(&local_LogInfo); @@ -997,27 +984,3 @@ SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, return res; } - -void init_allHist_years(SW_WEATHER_HIST **allHist, int numYears) { - int year; - - for(year = 0; year < numYears; year++) { - allHist[year] = NULL; - } -} - -void free_allHist(SW_WEATHER_HIST **allHist, int numYears) { - int year; - - if(!isnull(allHist)) { - - for(year = 0; year < numYears; year++) { - if(!isnull(allHist[year])) { - free(allHist[year]); - } - } - - free(allHist); - allHist = NULL; - } -} diff --git a/src/rSW_Weather.h b/src/rSW_Weather.h index ed930f5c..31f38ff6 100644 --- a/src/rSW_Weather.h +++ b/src/rSW_Weather.h @@ -12,5 +12,3 @@ SEXP onGet_WTH_DATA(void); void onSet_WTH_DATA(SEXP weatherList, LOG_INFO* LogInfo); SEXP rSW2_calc_SiteClimate(SEXP weatherList, SEXP yearStart, SEXP yearEnd, SEXP do_C4vars, SEXP do_Cheatgrass_ClimVars, SEXP latitude); -void init_allHist_years(SW_WEATHER_HIST **allHist, int numYears); -void free_allHist(SW_WEATHER_HIST **allHist, int numYears); From 05b53b80a7115894982dc8295890f226c316df5b Mon Sep 17 00:00:00 2001 From: Daniel Schlaepfer Date: Mon, 16 Oct 2023 15:42:00 -0400 Subject: [PATCH 21/24] Switch to SOILWAT2 release/devel_v7.2.0 to prepare merge --- .gitmodules | 2 +- src/SOILWAT2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8486a60c..0ab16695 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/SOILWAT2"] path = src/SOILWAT2 url = https://github.com/DrylandEcology/SOILWAT2 - branch = feature_ErrorHandling + branch = release/devel_v7.2.0 diff --git a/src/SOILWAT2 b/src/SOILWAT2 index 43da1ab2..dc7b17b3 160000 --- a/src/SOILWAT2 +++ b/src/SOILWAT2 @@ -1 +1 @@ -Subproject commit 43da1ab23132089913673c64cff2a58c4b8b3bd9 +Subproject commit dc7b17b332c286731d68f4038b9109996a2e64d0 From 64486082a20210ad62a9c1c0899170aae9e73f94 Mon Sep 17 00:00:00 2001 From: Daniel Schlaepfer Date: Mon, 16 Oct 2023 16:42:07 -0400 Subject: [PATCH 22/24] Update NEWS for v6.0.3-devel - also: clarify contributions for v6.0.2 --- NEWS.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 50032bc3..b064cda7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,12 +1,18 @@ -# rSOILWAT2 v6.0.3 +# rSOILWAT2 v6.0.3-9000 +* This version produces the same output as the previous version. +* Update `SOILWAT2` to v7.2.0 which improves error handling and fixes + memory leaks on error (#239; @dschlaep, @N1ckP3rsl3y). +* Code no longer requires (unused) `pcg` header (@dschlaep). # rSOILWAT2 v6.0.2 * This version produces the same output as the previous version. * Update `SOILWAT2` to v7.1.0 which prepares for thread-safety and reentrancy + (@N1ckP3rsl3y, @dschlaep). * C-side of `rSOILWAT2` gains new globals of the four main abstract types in `SOILWAT2` - SW_ALL, SW_OUTPUT_POINTERS, LOG_INFO, and PATH_INFO - - to interact with `SOILWAT2`. + to interact with `SOILWAT2`(@N1ckP3rsl3y, @dschlaep). + # rSOILWAT2 v6.0.1 From c035a73478e01c16018fa59854f04beee197d2ef Mon Sep 17 00:00:00 2001 From: Daniel Schlaepfer Date: Tue, 17 Oct 2023 10:37:51 -0400 Subject: [PATCH 23/24] New tests for vegetation establishment module --- tests/testthat/test_class_swEstab.R | 92 +++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 tests/testthat/test_class_swEstab.R diff --git a/tests/testthat/test_class_swEstab.R b/tests/testthat/test_class_swEstab.R new file mode 100644 index 00000000..3057979d --- /dev/null +++ b/tests/testthat/test_class_swEstab.R @@ -0,0 +1,92 @@ + +dir_test_data <- file.path("..", "test_data") +temp <- list.files(dir_test_data, pattern = "Ex") +temp <- sapply(strsplit(temp, "_", fixed = TRUE), function(x) x[[1]]) +tests <- unique(temp) + +test_that("Test data availability", { + expect_gt(length(tests), 0) +}) + + +test_that("Plant establishment: input files", { + expect_gte(length(tests), 6L) + it <- tests[[6L]] + + swin <- readRDS(file.path(dir_test_data, paste0(it, "_input.rds"))) + sw_weather <- readRDS(file.path(dir_test_data, paste0(it, "_weather.rds"))) + + # Execute the simulation run + swout <- rSOILWAT2::sw_exec( + inputData = swin, + weatherList = sw_weather, + quiet = TRUE + ) + res_estab <- swout@ESTABL@Year + + # Expect species columns + expect_gt(nrow(res_estab), 1L) + + # Expect some non-zero output for each species + expect_true(all(colSums(res_estab[, -1L, drop = FALSE]) > 0)) +}) + + +test_that("Plant establishment: user specified", { + swin <- rSOILWAT2::sw_exampleData + + # Turn on output for establishment module + activate_swOUT_OutKey(swin) <- "ESTABL" + + # turn on sumtype for every output (for simplicity) + # actual sumtype doesn't matter for establishment + slot(slot(swin, "output"), "sumtype")[] <- 1L + # ideally, this should be (but it uses `:::` operator): + # nolint start: commented_code_linter. + # tmp <- rSOILWAT2:::rSW2_glovars[["kSOILWAT2"]][["OutKeys"]] %in% "ESTABL" + # slot(slot(swin, "output"), "sumtype")[tmp] <- 1L + # nolint end: commented_code_linter. + + # Turn on establishment module + swin@estab@useEstab <- TRUE + + # Provide establishment parameter values + test_names <- c("sp1", "sp2", "sp3") + n <- length(test_names) + + swin@estab <- new( + "swEstab", + useEstab = TRUE, + count = n, + fileName = test_names, + Name = test_names, + vegType = rep(3L, n), + estab_lyrs = c(2L, 3L, 1L), + barsGERM = c(10, 10, 5L), + barsESTAB = c(15, 15, 10L), + min_pregerm_days = c(60L, 200L, 0L), + max_pregerm_days = c(180L, 365L, 365L), + min_wetdays_for_germ = c(2L, 6L, 1L), + max_drydays_postgerm = c(40L, 45L, 5L), + min_wetdays_for_estab = c(5L, 6L, 10L), + min_days_germ2estab = c(15L, 15L, 0L), + max_days_germ2estab = c(75L, 90L, 100L), + min_temp_germ = c(5, 10, 0), + max_temp_germ = c(20, 30, 35), + min_temp_estab = c(0, 3, 0), + max_temp_estab = c(20, 30, 15) + ) + + # Execute the simulation run + swout <- rSOILWAT2::sw_exec(inputData = swin, quiet = TRUE) + res_estab <- swout@ESTABL@Year + + # Expect species columns + expect_gt(nrow(res_estab), 1L) + + # Expect species names as columns + expect_equal(colnames(res_estab)[-1L], expected = test_names) + + # Expect some non-zero output for each species + expect_true(all(colSums(res_estab[, -1L, drop = FALSE]) > 0)) +}) From e94589b5036e0bfecb09d352b70a33bcf88b7fc6 Mon Sep 17 00:00:00 2001 From: Daniel Schlaepfer Date: Fri, 27 Oct 2023 09:22:27 -0400 Subject: [PATCH 24/24] Finalize v6.0.3 -- update to SOILWAT2 v7.2.0 --- .gitmodules | 2 +- DESCRIPTION | 4 ++-- NEWS.md | 2 +- src/SOILWAT2 | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index 0ab16695..39058bbd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/SOILWAT2"] path = src/SOILWAT2 url = https://github.com/DrylandEcology/SOILWAT2 - branch = release/devel_v7.2.0 + branch = master diff --git a/DESCRIPTION b/DESCRIPTION index b477b3c0..0602316c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: rSOILWAT2 -Version: 6.0.3-9000 +Version: 6.0.3 Title: An Ecohydrological Ecosystem-Scale Water Balance Simulation Model -Description: Access to the C-based SOILWAT2 v7.1.0 and functionality for +Description: Access to the C-based SOILWAT2 v7.2.0 and functionality for SQLite-database of weather data. Authors@R: c( person( diff --git a/NEWS.md b/NEWS.md index b064cda7..9bf4de01 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# rSOILWAT2 v6.0.3-9000 +# rSOILWAT2 v6.0.3 * This version produces the same output as the previous version. * Update `SOILWAT2` to v7.2.0 which improves error handling and fixes memory leaks on error (#239; @dschlaep, @N1ckP3rsl3y). diff --git a/src/SOILWAT2 b/src/SOILWAT2 index dc7b17b3..5b236ec6 160000 --- a/src/SOILWAT2 +++ b/src/SOILWAT2 @@ -1 +1 @@ -Subproject commit dc7b17b332c286731d68f4038b9109996a2e64d0 +Subproject commit 5b236ec64b1c4b822428898480e6278db35ceda2