From 9a5d9bc8a4260c94555c04a6279718f4fb1baaf5 Mon Sep 17 00:00:00 2001 From: Mohammed Karim Date: Wed, 5 Jun 2024 06:01:39 -0700 Subject: [PATCH 01/50] update submodule url to gitlab --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 0592b6d9..69681f14 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "extern/aorc_bmi"] path = extern/aorc_bmi - url = https://github.com/NOAA-OWP/aorc_bmi.git + url = https://gitlab.sh.nextgenwaterprediction.com/NGWPC/nwm-ngen/aorc_bmi.git [submodule "extern/evapotranspiration"] path = extern/evapotranspiration - url = https://github.com/NOAA-OWP/evapotranspiration.git + url = https://gitlab.sh.nextgenwaterprediction.com/NGWPC/nwm-ngen/evapotranspiration.git From 1deef7dbfa3c255f058d38656022d9758a649325 Mon Sep 17 00:00:00 2001 From: "areg.amirkhanian" Date: Mon, 23 Sep 2024 15:06:38 -0700 Subject: [PATCH 02/50] Implementation of the new Logging mechanism for C-based modules - NGWPC-3889 --- CMakeLists.txt | 14 ++++---- include/logger.h | 64 ++++++++++++++++++++++++++++++++++++ src/Makefile | 2 +- src/logger.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 21 ++++++++++++ 5 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 include/logger.h create mode 100644 src/logger.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c32e22fb..f89143cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ message("CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}") ## cfe + aorc + pet + smp if(AETROOTZONE) -add_executable(${exe_name} ./src/main_cfe_aorc_pet_rz_aet.cxx ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c +add_executable(${exe_name} ./src/main_cfe_aorc_pet_rz_aet.cxx ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c ./extern/evapotranspiration/src/bmi_pet.c) @@ -66,17 +66,17 @@ add_library(cfelib ./extern/SoilMoistureProfiles/src/bmi_soil_moisture_profile.c ./extern/SoilMoistureProfiles/include/soil_moisture_profile.hxx) target_link_libraries(${exe_name} LINK_PUBLIC cfelib) elseif(FORCING) -add_executable(${exe_name} ./src/main_pass_forcings.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c +add_executable(${exe_name} ./src/main_pass_forcings.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c) elseif(FORCINGPET) -add_executable(${exe_name} ./src/main_cfe_aorc_pet.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c +add_executable(${exe_name} ./src/main_cfe_aorc_pet.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c ./extern/evapotranspiration/src/bmi_pet.c) elseif(BASE) -add_executable(${exe_name} ./src/main.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c +add_executable(${exe_name} ./src/main.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c) elseif(UNITTEST) -add_executable(${exe_name} ./test/main_unit_test.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c +add_executable(${exe_name} ./test/main_unit_test.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c) endif() @@ -96,9 +96,9 @@ set(CFE_LIB_DESC_CMAKE "OWP CFE BMI Module Shared Library") add_compile_definitions(BMI_ACTIVE) if(WIN32) - add_library(cfebmi ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c ./src/nash_cascade.c) + add_library(cfebmi ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c) else() - add_library(cfebmi SHARED ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c ./src/nash_cascade.c) + add_library(cfebmi SHARED ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c) endif() target_include_directories(cfebmi PRIVATE include) diff --git a/include/logger.h b/include/logger.h new file mode 100644 index 00000000..b9a6674b --- /dev/null +++ b/include/logger.h @@ -0,0 +1,64 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include +#include + +typedef enum { + NONE = 0, + FATAL, + DEBUG, + INFO, + WARN, + ERROR, +} LogLevel; + +typedef enum { + NGEN, + NOAHOWP, + SNOW17, + UEB, + CFE, + SACSMA, + LASAM, + SMP, + SFT, + TROUTE, + SCHISM, + SFINCS, + GC2D, + TOPOFLOW, + MODULE_COUNT +} LoggingModule; + +static const char* module_name[MODULE_COUNT] = { + "NGEN ", + "NOAHOWP ", + "SNOW17 ", + "UEB ", + "CFE ", + "SACSMA ", + "LASAM ", + "SMP ", + "SFT ", + "TROUTE ", + "SCHISM ", + "SFINCS ", + "GC2D ", + "TOPOFLOW" +}; + +typedef struct { + LogLevel logLevel; + FILE* logFile; +} Logger; + +Logger* GetInstance(); +void SetLogPreferences(Logger* logger, LogLevel level); +void Log(Logger* logger, const char* message, LogLevel messageLevel, LoggingModule module); +LogLevel GetLogLevel(const char* logLevel); +char* createTimestamp(); + +#endif diff --git a/src/Makefile b/src/Makefile index b78e3ebe..4c17e172 100755 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,7 @@ LFLAGS = LIBS = -lm # define the C source files -SRCS = main.c cfe.c bmi_cfe.c +SRCS = main.c cfe.c bmi_cfe.c logger.c # define the C object files OBJS = $(SRCS:.c=.o) diff --git a/src/logger.c b/src/logger.c new file mode 100644 index 00000000..ec20d360 --- /dev/null +++ b/src/logger.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include "logger.h" + +#define MODULE_COUNT 14 + + +Logger* loggerInstance = NULL; + +char* createTimestamp() { + static char buffer[80]; + time_t now = time(NULL); + struct tm* timeinfo = localtime(&now); + strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H:%M:%S", timeinfo); + return buffer; +} + +void SetLogPreferences(Logger* logger, LogLevel level) { + logger->logLevel = level; + const char* logFileDir = "./run_logs/ngen_"; + char fullPath[256]; + snprintf(fullPath, sizeof(fullPath), "%s%s/", logFileDir, createTimestamp()); + + printf("Log File Directory: %s\n", fullPath); + + char mkdir_cmd[512]; + snprintf(mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p %s", fullPath); + int status = system(mkdir_cmd); + if (status == -1) { + fprintf(stderr, "Error: %s\n", strerror(errno)); + } else { + printf("Directories are created\n"); + } + + char logFilePath[512]; + snprintf(logFilePath, sizeof(logFilePath), "%sngen_log.txt", fullPath); + logger->logFile = fopen(logFilePath, "w"); + if (logger->logFile == NULL) { + fprintf(stderr, "Can't Open Log File\n"); + } +} + +Logger* GetInstance() { + if (loggerInstance == NULL) { + loggerInstance = (Logger*)malloc(sizeof(Logger)); + SetLogPreferences(loggerInstance, NONE); + } + return loggerInstance; +} + +void Log(Logger* logger, const char* message, LogLevel messageLevel, LoggingModule module) { + if (messageLevel >= logger->logLevel) { + const char* logType; + switch (messageLevel) { + case FATAL: logType = "FATAL "; break; + case DEBUG: logType = "DEBUG "; break; + case INFO: logType = "INFO "; break; + case WARN: logType = "WARN "; break; + case ERROR: logType = "ERROR "; break; + default: logType = "NONE "; break; + } + + char final_message[1024]; + snprintf(final_message, sizeof(final_message), "%s %s %s%s\n", + createTimestamp(), module_name[module], logType, message); + + fprintf(logger->logFile, "%s", final_message); + fflush(logger->logFile); + } +} + +LogLevel GetLogLevel(const char* logLevel) { + if (strcmp(logLevel, "DEBUG") == 0) return DEBUG; + if (strcmp(logLevel, "INFO") == 0) return INFO; + if (strcmp(logLevel, "WARN") == 0) return ERROR; + if (strcmp(logLevel, "ERROR") == 0) return ERROR; + if (strcmp(logLevel, "FATAL") == 0) return ERROR; + return NONE; +} + diff --git a/src/main.c b/src/main.c index 105bd340..b1bc50cc 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,25 @@ #include "bmi.h" #include "bmi_cfe.h" #include "cfe.h" +#include "logger.h" + +void setup_logger(void) { + Logger* logger = GetInstance(); + + Log(logger, "Sample Log for LogLevel::ERROR", ERROR, NGEN); + Log(logger, "Sample Log for LogLevel::FATAL", FATAL, NGEN); + Log(logger, "Sample Log for LogLevel::WARN", WARN, NGEN); + Log(logger, "Sample Log for LogLevel::INFO", INFO, NGEN); + + const char* multiline_log = + "First line of multiline log:\n" + " Indented second line of multiline log\n" + " Indented third line of multiline log\n" + " Indented fourth line of multiline log"; + Log(logger, multiline_log, INFO, NGEN); + + Log(logger, "Sample Log for LogLevel::DEBUG", DEBUG, NGEN); +} /* This main program is a mock framwork. @@ -12,6 +31,8 @@ This is not part of BMI, but acts as the driver that calls the model. int main(int argc, const char *argv[]) { + // setup the logger + setup_logger(); //////////////////////////////////////////////////////////////// ////////////// USING UPDATE ////////////////////////////// From b7817d04e2a28ba195ab5b7370095ea562d193d5 Mon Sep 17 00:00:00 2001 From: "yuqiong.liu" Date: Fri, 27 Sep 2024 18:00:28 -0400 Subject: [PATCH 03/50] fix to CFE unit test for mass balance check --- src/bmi_cfe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 6efd3aca..e8c32d47 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -3079,7 +3079,8 @@ extern void initialize_volume_trackers(cfe_state_struct* cfe_ptr){ cfe_ptr->vol_struct.vol_out_giuh = 0; cfe_ptr->vol_struct.vol_in_nash = 0; cfe_ptr->vol_struct.vol_out_nash = 0; - cfe_ptr->vol_struct.volstart += cfe_ptr->gw_reservoir.storage_m; // initial mass balance checks in g.w. reservoir + //cfe_ptr->vol_struct.volstart += cfe_ptr->gw_reservoir.storage_m; // initial mass balance checks in g.w. reservoir + cfe_ptr->vol_struct.volstart = cfe_ptr->gw_reservoir.storage_m; // YLiu: initial mass balance checks in g.w. reservoir cfe_ptr->vol_struct.vol_in_gw_start = cfe_ptr->gw_reservoir.storage_m; cfe_ptr->vol_struct.volstart += cfe_ptr->soil_reservoir.storage_m; // initial mass balance checks in soil reservoir cfe_ptr->vol_struct.vol_soil_start = cfe_ptr->soil_reservoir.storage_m; From 0f8d54b1240ad30cc94e638d987dd19978a18b60 Mon Sep 17 00:00:00 2001 From: "areg.amirkhanian" Date: Mon, 30 Sep 2024 15:57:46 -0700 Subject: [PATCH 04/50] More changes including the move of setup_logger call to BMI initialize() function in bmi_cfe.c --- include/logger.h | 1 + src/bmi_cfe.c | 6 ++++++ src/logger.c | 17 +++++++++++++++++ src/main.c | 24 +----------------------- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/include/logger.h b/include/logger.h index b9a6674b..8cdcfaf9 100644 --- a/include/logger.h +++ b/include/logger.h @@ -60,5 +60,6 @@ void SetLogPreferences(Logger* logger, LogLevel level); void Log(Logger* logger, const char* message, LogLevel messageLevel, LoggingModule module); LogLevel GetLogLevel(const char* logLevel); char* createTimestamp(); +void setup_logger(void); #endif diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 276e956e..1ed87ab3 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -5,6 +5,8 @@ #include "bmi_cfe.h" #include #include +#include "logger.h" + #ifndef WATER_SPECIFIC_WEIGHT #define WATER_SPECIFIC_WEIGHT 9810 #define STANDARD_ATMOSPHERIC_PRESSURE_PASCALS 101325 @@ -1290,6 +1292,10 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) static int Initialize (Bmi *self, const char *file) { + // setup the logger + setup_logger(); + printf("In CFE Initialize()"); + //FIXME, we can use the input file to help imply "framework" support or "standalone" //an empty init file string indicates things will come from set_value??? //what happens when both occur, that is we have a config file and framewrok diff --git a/src/logger.c b/src/logger.c index ec20d360..900aa023 100644 --- a/src/logger.c +++ b/src/logger.c @@ -82,3 +82,20 @@ LogLevel GetLogLevel(const char* logLevel) { return NONE; } +void setup_logger(void) { + Logger* logger = GetInstance(); + + Log(logger, "Sample Log for LogLevel::ERROR", ERROR, CFE); + Log(logger, "Sample Log for LogLevel::FATAL", FATAL, CFE); + Log(logger, "Sample Log for LogLevel::WARN", WARN, CFE); + Log(logger, "Sample Log for LogLevel::INFO", INFO, CFE); + + const char* multiline_log = + "First line of multiline log:\n" + " Indented second line of multiline log\n" + " Indented third line of multiline log\n" + " Indented fourth line of multiline log"; + Log(logger, multiline_log, INFO, CFE); + + Log(logger, "Sample Log for LogLevel::DEBUG", DEBUG, CFE); +} diff --git a/src/main.c b/src/main.c index b1bc50cc..e462ebb6 100644 --- a/src/main.c +++ b/src/main.c @@ -4,25 +4,6 @@ #include "bmi.h" #include "bmi_cfe.h" #include "cfe.h" -#include "logger.h" - -void setup_logger(void) { - Logger* logger = GetInstance(); - - Log(logger, "Sample Log for LogLevel::ERROR", ERROR, NGEN); - Log(logger, "Sample Log for LogLevel::FATAL", FATAL, NGEN); - Log(logger, "Sample Log for LogLevel::WARN", WARN, NGEN); - Log(logger, "Sample Log for LogLevel::INFO", INFO, NGEN); - - const char* multiline_log = - "First line of multiline log:\n" - " Indented second line of multiline log\n" - " Indented third line of multiline log\n" - " Indented fourth line of multiline log"; - Log(logger, multiline_log, INFO, NGEN); - - Log(logger, "Sample Log for LogLevel::DEBUG", DEBUG, NGEN); -} /* This main program is a mock framwork. @@ -30,10 +11,7 @@ This is not part of BMI, but acts as the driver that calls the model. */ int main(int argc, const char *argv[]) -{ - // setup the logger - setup_logger(); - +{ //////////////////////////////////////////////////////////////// ////////////// USING UPDATE ////////////////////////////// //////////////////////////////////////////////////////////////// From a97daae9a1521792855899059b3d08070d7b0646 Mon Sep 17 00:00:00 2001 From: "areg.amirkhanian" Date: Mon, 30 Sep 2024 19:52:26 -0700 Subject: [PATCH 05/50] Final change to make it work with environment variable NGEN_LOG_FILE_PATH --- include/logger.h | 4 +- src/bmi_cfe.c | 6 ++- src/logger.c | 96 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/include/logger.h b/include/logger.h index 8cdcfaf9..80ac4baf 100644 --- a/include/logger.h +++ b/include/logger.h @@ -8,9 +8,9 @@ typedef enum { NONE = 0, - FATAL, DEBUG, INFO, + FATAL, WARN, ERROR, } LogLevel; @@ -57,7 +57,7 @@ typedef struct { Logger* GetInstance(); void SetLogPreferences(Logger* logger, LogLevel level); -void Log(Logger* logger, const char* message, LogLevel messageLevel, LoggingModule module); +void Log(Logger* logger, const char* message, LogLevel messageLevel); LogLevel GetLogLevel(const char* logLevel); char* createTimestamp(); void setup_logger(void); diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 1ed87ab3..eebdeb9e 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -1294,8 +1294,9 @@ static int Initialize (Bmi *self, const char *file) { // setup the logger setup_logger(); - printf("In CFE Initialize()"); - + Logger* logger = GetInstance(); + Log(logger, "In CFE Initialize()\n", INFO); + //FIXME, we can use the input file to help imply "framework" support or "standalone" //an empty init file string indicates things will come from set_value??? //what happens when both occur, that is we have a config file and framewrok @@ -1474,6 +1475,7 @@ static int Initialize (Bmi *self, const char *file) #if CFE_DEBUG > 0 printf("At declaration of smc_profile size, soil_reservoir.n_soil_layers = %i\n", cfe_bmi_data_ptr->soil_reservoir.n_soil_layers); #endif + Log(logger, "Success in CFE BMI Initialization\n", INFO); return BMI_SUCCESS; } diff --git a/src/logger.c b/src/logger.c index 900aa023..b6cda190 100644 --- a/src/logger.c +++ b/src/logger.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "logger.h" @@ -11,36 +12,67 @@ Logger* loggerInstance = NULL; +long long timeInMilliseconds(void) { + struct timeval tv; + + gettimeofday(&tv,NULL); + return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); +} + char* createTimestamp() { - static char buffer[80]; + static char buffer[256]; + char buffer1[256]; time_t now = time(NULL); - struct tm* timeinfo = localtime(&now); - strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H:%M:%S", timeinfo); + + long millis = (long)(timeInMilliseconds() % 1000); + + + struct tm* timeinfo = gmtime(&now); + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S:", timeinfo); + snprintf(buffer1, sizeof(buffer1), "%03d/", (int)millis); + strcat(buffer, buffer1); + return buffer; } void SetLogPreferences(Logger* logger, LogLevel level) { logger->logLevel = level; - const char* logFileDir = "./run_logs/ngen_"; - char fullPath[256]; - snprintf(fullPath, sizeof(fullPath), "%s%s/", logFileDir, createTimestamp()); - - printf("Log File Directory: %s\n", fullPath); - - char mkdir_cmd[512]; - snprintf(mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p %s", fullPath); - int status = system(mkdir_cmd); - if (status == -1) { - fprintf(stderr, "Error: %s\n", strerror(errno)); - } else { - printf("Directories are created\n"); - } - char logFilePath[512]; - snprintf(logFilePath, sizeof(logFilePath), "%sngen_log.txt", fullPath); - logger->logFile = fopen(logFilePath, "w"); + // get the log file path + char * log_file_path; + log_file_path = getenv("NGEN_LOG_FILE_PATH"); + + logger->logFile = fopen(log_file_path, "a"); if (logger->logFile == NULL) { - fprintf(stderr, "Can't Open Log File\n"); + printf("Can't Open Log File\n"); + // create local log directory and file + const char* logFileDir = "./run_logs/ngen_"; + char fullPath[256]; + snprintf(fullPath, sizeof(fullPath), "%s%s/", logFileDir, createTimestamp()); + + printf("Log File Directory: %s\n", fullPath); + + char mkdir_cmd[512]; + snprintf(mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p %s", fullPath); + int status = system(mkdir_cmd); + if (status == -1) { + fprintf(stderr, "Error: %s\n", strerror(errno)); + } else { + printf("Directories are created\n"); + } + + char logFilePath[512]; + snprintf(logFilePath, sizeof(logFilePath), "%scfe_log.txt", fullPath); + logger->logFile = fopen(logFilePath, "w"); + if (logger->logFile == NULL) { + fprintf(stderr, "Can't Open local Log File\n"); + } + else { + printf("Log File Path: %s\n", logFilePath); + } + } + else { + printf("Log File Path: %s\n", log_file_path); } } @@ -52,7 +84,7 @@ Logger* GetInstance() { return loggerInstance; } -void Log(Logger* logger, const char* message, LogLevel messageLevel, LoggingModule module) { +void Log(Logger* logger, const char* message, LogLevel messageLevel) { if (messageLevel >= logger->logLevel) { const char* logType; switch (messageLevel) { @@ -66,10 +98,12 @@ void Log(Logger* logger, const char* message, LogLevel messageLevel, LoggingModu char final_message[1024]; snprintf(final_message, sizeof(final_message), "%s %s %s%s\n", - createTimestamp(), module_name[module], logType, message); + createTimestamp(), module_name[CFE], logType, message); - fprintf(logger->logFile, "%s", final_message); - fflush(logger->logFile); + if (logger->logFile != NULL) { + fprintf(logger->logFile, "%s", final_message); + fflush(logger->logFile); + } } } @@ -85,17 +119,17 @@ LogLevel GetLogLevel(const char* logLevel) { void setup_logger(void) { Logger* logger = GetInstance(); - Log(logger, "Sample Log for LogLevel::ERROR", ERROR, CFE); - Log(logger, "Sample Log for LogLevel::FATAL", FATAL, CFE); - Log(logger, "Sample Log for LogLevel::WARN", WARN, CFE); - Log(logger, "Sample Log for LogLevel::INFO", INFO, CFE); + Log(logger, "Sample Log for LogLevel::ERROR", ERROR); + Log(logger, "Sample Log for LogLevel::FATAL", FATAL); + Log(logger, "Sample Log for LogLevel::WARN", WARN); + Log(logger, "Sample Log for LogLevel::INFO", INFO); const char* multiline_log = "First line of multiline log:\n" " Indented second line of multiline log\n" " Indented third line of multiline log\n" " Indented fourth line of multiline log"; - Log(logger, multiline_log, INFO, CFE); + Log(logger, multiline_log, INFO); - Log(logger, "Sample Log for LogLevel::DEBUG", DEBUG, CFE); + Log(logger, "Sample Log for LogLevel::DEBUG", DEBUG); } From cee198f94be88ca76ab9026d4ec3d9661be9f985 Mon Sep 17 00:00:00 2001 From: David Gillingham Date: Tue, 1 Oct 2024 20:22:15 +0000 Subject: [PATCH 06/50] Revert "Merge branch 'NGWPC-3889' into 'development'" This reverts merge request !3 --- .../Build_and_Run_Standalone_Test.yml | 9 - .github/workflows/add_to_project.yml | 20 - .github/workflows/ngen_integration.yaml | 4 +- .gitmodules | 4 +- CMakeLists.txt | 38 +- INSTALL.md | 8 +- bmi/bmi.h | 4 +- bmi/bmi.hxx | 84 + configs/README.md | 44 +- configs/cat_87_bmi_config_cfe.txt | 5 +- configs/cat_87_bmi_config_cfe_pass.txt | 6 +- configs/cat_87_config_cfe_pass_surfnash.txt | 27 - configs/cat_89_bmi_config_cfe_unit_test.txt | 3 +- .../laramie_bmi_config_cfe_pass_aet_rz.txt | 5 +- include/bmi.h | 4 +- include/bmi_cfe.h | 20 +- include/cfe.h | 64 +- include/giuh.h | 2 +- include/logger.h | 65 - include/nash_cascade.h | 27 - ...surfgiuh.json => realization_cfe_pet.json} | 0 .../realization_cfe_pet_surfnash_calib.json | 97 -- src/Makefile | 2 +- src/bmi_cfe.c | 1510 ++++++++--------- src/cfe.c | 479 +++--- src/giuh.c | 18 +- src/logger.c | 135 -- src/main.c | 3 +- src/main_cfe_aorc_pet_rz_aet.cxx | 1 + src/main_pass_forcings.c | 2 +- src/nash_cascade.c | 144 -- test/main_unit_test.c | 3 +- 32 files changed, 1153 insertions(+), 1684 deletions(-) delete mode 100644 .github/workflows/add_to_project.yml create mode 100644 bmi/bmi.hxx delete mode 100644 configs/cat_87_config_cfe_pass_surfnash.txt delete mode 100644 include/logger.h delete mode 100644 include/nash_cascade.h rename realizations/{realization_cfe_pet_surfgiuh.json => realization_cfe_pet.json} (100%) delete mode 100644 realizations/realization_cfe_pet_surfnash_calib.json delete mode 100644 src/logger.c delete mode 100644 src/nash_cascade.c diff --git a/.github/workflows/Build_and_Run_Standalone_Test.yml b/.github/workflows/Build_and_Run_Standalone_Test.yml index 1efecc60..146d4abf 100644 --- a/.github/workflows/Build_and_Run_Standalone_Test.yml +++ b/.github/workflows/Build_and_Run_Standalone_Test.yml @@ -57,15 +57,6 @@ jobs: make cd .. ./run_cfe.sh FORCINGPET - - - name : Build and Run AETROOTZONE option - run: | - git clone https://github.com/NOAA-OWP/SoilMoistureProfiles extern/SoilMoistureProfiles - cd build - cmake ../ -DAETROOTZONE=ON - make - cd .. - ./run_cfe.sh AETROOTZONE - name: Build and Run BMI Unit Test run: | diff --git a/.github/workflows/add_to_project.yml b/.github/workflows/add_to_project.yml deleted file mode 100644 index 5e7832d4..00000000 --- a/.github/workflows/add_to_project.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Add to Project - -on: - #pull_request_target: - #pull_request: - # types: - # - opened - issues: - types: - - opened - -jobs: - add-to-project: - name: Add issues to formulation project - runs-on: ubuntu-latest - steps: - - uses: actions/add-to-project@v0.6.1 - with: - project-url: https://github.com/orgs/NOAA-OWP/projects/30 - github-token: ${{ secrets.TEST-CFE-2 }} diff --git a/.github/workflows/ngen_integration.yaml b/.github/workflows/ngen_integration.yaml index a7b3bdcd..87afc8a0 100644 --- a/.github/workflows/ngen_integration.yaml +++ b/.github/workflows/ngen_integration.yaml @@ -36,7 +36,7 @@ jobs: - name: Build the CFE Library run: | - cmake -B cmake_build -S . -DNGEN=ON -DCMAKE_C_FLAGS='-g -Og -fsanitize=address -Werror' + cmake -B cmake_build -S . -DNGEN=ON make -C cmake_build - name: Save CFE to a Temp Directory @@ -84,5 +84,5 @@ jobs: - name: Run Ngen Test for CFE Couple with PET run: | mv ${{ steps.ngen_id1.outputs.build-dir }} ./ngen-build/ - inputfile='extern/cfe/cfe/realizations/realization_cfe_pet_surfgiuh.json' + inputfile='extern/cfe/cfe/realizations/realization_cfe_pet.json' ./ngen-build/ngen ./data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" $inputfile diff --git a/.gitmodules b/.gitmodules index 69681f14..0592b6d9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "extern/aorc_bmi"] path = extern/aorc_bmi - url = https://gitlab.sh.nextgenwaterprediction.com/NGWPC/nwm-ngen/aorc_bmi.git + url = https://github.com/NOAA-OWP/aorc_bmi.git [submodule "extern/evapotranspiration"] path = extern/evapotranspiration - url = https://gitlab.sh.nextgenwaterprediction.com/NGWPC/nwm-ngen/evapotranspiration.git + url = https://github.com/NOAA-OWP/evapotranspiration.git diff --git a/CMakeLists.txt b/CMakeLists.txt index f89143cf..2fcd4e6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,9 @@ set(Red "${Esc}[32m") set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_C_COMPILER $ENV{CC}) +set(CMAKE_CXX_COMPILER $ENV{CXX}) + # module setup options option(BASE "BASE" OFF) @@ -43,6 +46,7 @@ endif() # set the project name project(cfebmi VERSION 1.0.0 DESCRIPTION "OWP CFE BMI Module Shared Library") +set(CMAKE_BUILD_TYPE Debug) IF(CMAKE_BUILD_TYPE MATCHES Debug) message("Debug build.") ENDIF(CMAKE_BUILD_TYPE MATCHES Debug) @@ -51,33 +55,31 @@ message(CMAKE_CXX_COMPILER " ${CMAKE_CXX_COMPILER}") message(CMAKE_C_COMPILER " ${CMAKE_C_COMPILER}") message("CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") + # add the executable ## cfe + aorc + pet + smp if(AETROOTZONE) -add_executable(${exe_name} ./src/main_cfe_aorc_pet_rz_aet.cxx ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c - ./src/conceptual_reservoir.c ./src/nash_cascade.c ./extern/aorc_bmi/src/aorc.c - ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c +add_executable(${exe_name} ./src/main_cfe_aorc_pet_rz_aet.cxx ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c + ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c ./extern/evapotranspiration/src/bmi_pet.c) -add_library(cfelib ./extern/SoilMoistureProfiles/src/bmi_soil_moisture_profile.cxx - ./extern/SoilMoistureProfiles/src/soil_moisture_profile.cxx - ./extern/SoilMoistureProfiles/include/bmi_soil_moisture_profile.hxx - ./extern/SoilMoistureProfiles/include/soil_moisture_profile.hxx) +add_library(cfelib ./extern/SoilMoistureProfiles/src/bmi_soil_moisture_profile.cxx ./extern/SoilMoistureProfiles/src/soil_moisture_profile.cxx + ./extern/SoilMoistureProfiles/include/bmi_soil_moisture_profile.hxx ./extern/SoilMoistureProfiles/include/soil_moisture_profile.hxx) target_link_libraries(${exe_name} LINK_PUBLIC cfelib) elseif(FORCING) -add_executable(${exe_name} ./src/main_pass_forcings.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c - ./src/nash_cascade.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c) +add_executable(${exe_name} ./src/main_pass_forcings.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c + ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c) elseif(FORCINGPET) -add_executable(${exe_name} ./src/main_cfe_aorc_pet.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c - ./src/nash_cascade.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c - ./extern/evapotranspiration/src/pet.c ./extern/evapotranspiration/src/bmi_pet.c) +add_executable(${exe_name} ./src/main_cfe_aorc_pet.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c + ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c + ./extern/evapotranspiration/src/bmi_pet.c) elseif(BASE) -add_executable(${exe_name} ./src/main.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c - ./src/nash_cascade.c) +add_executable(${exe_name} ./src/main.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) elseif(UNITTEST) -add_executable(${exe_name} ./test/main_unit_test.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c - ./src/nash_cascade.c) +add_executable(${exe_name} ./test/main_unit_test.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) endif() @@ -96,9 +98,9 @@ set(CFE_LIB_DESC_CMAKE "OWP CFE BMI Module Shared Library") add_compile_definitions(BMI_ACTIVE) if(WIN32) - add_library(cfebmi ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c) + add_library(cfebmi ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) else() - add_library(cfebmi SHARED ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/logger.c ./src/conceptual_reservoir.c ./src/nash_cascade.c) + add_library(cfebmi SHARED ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) endif() target_include_directories(cfebmi PRIVATE include) diff --git a/INSTALL.md b/INSTALL.md index 7a118288..1c1b925a 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -80,9 +80,9 @@ See general instructions [here](https://github.com/NOAA-OWP/ngen/wiki/NGen-Tutor - cmake -B cmake_build -S . -DBMI_C_LIB_ACTIVE=ON -DBMI_FORTRAN_ACTIVE=ON -DNGEN_ACTIVATE_PYTHON=ON - make -j4 -C cmake_build - #### CFE - - git submodule update --remote extern/cfe/cfe - - cmake -B extern/cfe/cfe/cmake_build -S extern/cfe/cfe/ -DNGEN=ON - - make -C extern/cfe/cfe/cmake_build + - git submodule update --remote extern/cfe/cfe + - cmake -B extern/cfe/cmake_build -S extern/cfe/cfe/ -DNGEN=ON + - make -C extern/cfe/cmake_build - #### SLoTH SLoTH is also needed to run SFT in the ngen framework. SLoTH is a BMI that is used to set a bmi variable(s) that is not provided by other BMIs but required by the model. So build [SLoTH](https://github.com/NOAA-OWP/SLoTH) using the following instructions - cd extern/sloth/ && git checkout latest @@ -103,7 +103,7 @@ The following pre-process step needs to be completed before running the example. **Note:** Make sure the "library_file" and "init_config" in the BMI blocks in the realization file are pointing to the right files, these paths depend on how you build your models. ``` - Run: ../cmake_build/ngen data/catchment_data.geojson cat-27 data/nexus_data.geojson nex-26 realizations/realization_cfe_pet_surfgiuh.json + Run: ../cmake_build/ngen data/catchment_data.geojson cat-27 data/nexus_data.geojson nex-26 realizations/realization_cfe_pet.json ``` ## Alternative: Compiling and Running CFE diff --git a/bmi/bmi.h b/bmi/bmi.h index 9cc04d64..e6bb6d12 100755 --- a/bmi/bmi.h +++ b/bmi/bmi.h @@ -35,8 +35,8 @@ SOFTWARE. extern "C" { #endif -const static int BMI_SUCCESS = 0; -const static int BMI_FAILURE = 1; +#define BMI_SUCCESS (0) +#define BMI_FAILURE (1) #define BMI_MAX_UNITS_NAME (2048) #define BMI_MAX_TYPE_NAME (2048) diff --git a/bmi/bmi.hxx b/bmi/bmi.hxx new file mode 100644 index 00000000..87b290b7 --- /dev/null +++ b/bmi/bmi.hxx @@ -0,0 +1,84 @@ +// The Basic Model Interface (BMI) C++ specification. +// +// This language specification is derived from the Scientific +// Interface Definition Language (SIDL) file bmi.sidl located at +// https://github.com/csdms/bmi. + +#ifndef BMI_HXX +#define BMI_HXX +#include +#include + +namespace bmixx { + + //const int BMI_SUCCESS = 0; + // const int BMI_FAILURE = 1; + + const int MAX_COMPONENT_NAME = 2048; + const int MAX_VAR_NAME = 2048; + const int MAX_UNITS_NAME = 2048; + const int MAX_TYPE_NAME = 2048; + + class Bmi { + public: + // Model control functions. + virtual void Initialize(std::string config_file) = 0; + virtual void Update() = 0; + virtual void UpdateUntil(double time) = 0; + virtual void Finalize() = 0; + + // Model information functions. + virtual std::string GetComponentName() = 0; + virtual int GetInputItemCount() = 0; + virtual int GetOutputItemCount() = 0; + virtual std::vector GetInputVarNames() = 0; + virtual std::vector GetOutputVarNames() = 0; + + // Variable information functions + virtual int GetVarGrid(std::string name) = 0; + virtual std::string GetVarType(std::string name) = 0; + virtual std::string GetVarUnits(std::string name) = 0; + virtual int GetVarItemsize(std::string name) = 0; + virtual int GetVarNbytes(std::string name) = 0; + virtual std::string GetVarLocation(std::string name) = 0; + + virtual double GetCurrentTime() = 0; + virtual double GetStartTime() = 0; + virtual double GetEndTime() = 0; + virtual std::string GetTimeUnits() = 0; + virtual double GetTimeStep() = 0; + + // Variable getters + virtual void GetValue(std::string name, void *dest) = 0; + virtual void *GetValuePtr(std::string name) = 0; + virtual void GetValueAtIndices(std::string name, void *dest, int *inds, int count) = 0; + + // Variable setters + virtual void SetValue(std::string name, void *src) = 0; + virtual void SetValueAtIndices(std::string name, int *inds, int count, void *src) = 0; + + // Grid information functions + virtual int GetGridRank(const int grid) = 0; + virtual int GetGridSize(const int grid) = 0; + virtual std::string GetGridType(const int grid) = 0; + + virtual void GetGridShape(const int grid, int *shape) = 0; + virtual void GetGridSpacing(const int grid, double *spacing) = 0; + virtual void GetGridOrigin(const int grid, double *origin) = 0; + + virtual void GetGridX(const int grid, double *x) = 0; + virtual void GetGridY(const int grid, double *y) = 0; + virtual void GetGridZ(const int grid, double *z) = 0; + + virtual int GetGridNodeCount(const int grid) = 0; + virtual int GetGridEdgeCount(const int grid) = 0; + virtual int GetGridFaceCount(const int grid) = 0; + + virtual void GetGridEdgeNodes(const int grid, int *edge_nodes) = 0; + virtual void GetGridFaceEdges(const int grid, int *face_edges) = 0; + virtual void GetGridFaceNodes(const int grid, int *face_nodes) = 0; + virtual void GetGridNodesPerFace(const int grid, int *nodes_per_face) = 0; + }; +} + +#endif diff --git a/configs/README.md b/configs/README.md index b1d8bc3f..2fbf06dc 100644 --- a/configs/README.md +++ b/configs/README.md @@ -19,54 +19,40 @@ Example configuration files are provided in this directory. To build and run the | gw_storage | *double* | | meters/meters [m/m] | parameter_adjustable | | initial condition for groundwater reservoir - it is the ground water as a decimal fraction of the maximum groundwater storage (max_gw_storage) for the initial timestep | | alpha_fc | *double* | | | parameter_adjustable | | field capacity | | soil_storage| *double* | | meters/meters [m/m] | parameter_adjustable | | initial condition for soil reservoir - it is the water in the soil as a decimal fraction of maximum soil water storage (smcmax * depth) for the initial timestep | -| N_nash | *int* | | | parameter_adjustable | | number of Nash lf reservoirs (optional, defaults to 2, ignored if storage values present) | -| K_nash | *double* | | 1/meters [m^-1] | parameter_adjustable | subsurface runoff | Nash Config param for lateral subsurface runoff | +| K_nash | *int* | | | parameter_adjustable | | number of Nash lf reservoirs (optional, defaults to 2, ignored if storage values present) | | *K_lf | *double* | | | parameter_adjustable | | Nash Config param - primary reservoir | | nash_storage | *double* | | | parameter_adjustable | | Nash Config param - secondary reservoir | | giuh_ordinates | *double* | | | parameter_adjustable | | Giuh ordinates in dt time steps | | num_timesteps | *int* | | | time_info | | set to `1` if `forcing_file=BMI` | | verbosity | *int* | `0`-`3` | | option | | prints various debug and bmi info | -| surface_water_partitioning_scheme | *char* | `Xinanjiang` or `Schaake` | | parameter_adjustable | infiltraton exces | | -| surface_runoff_scheme | *char* | GIUH or NASH_CASCADE | | parameter_adjustable | surface runoff | also supports 1 for GIUH and 2 for NASH_CASCADE; default is GIUH | -| N_nash_surface | *int* | | | parameter_adjustable | surface runoff | number of Nash reservoirs for surface runoff | -| K_nash_surface | *double* | | 1/hour [h^-1] | parameter_adjustable | surface runoff | Nash Config param for surface runoff | -| nash_storage_surface | *double* | | meters [m] | parameter_adjustable | surface runoff | Nash Config param; reservoir surface storage; default is zero storage | -| nsubsteps_nash_surface | *int* | | | parameter_adjustable | surface runoff | optional (default = 10); number of subtimstep for Nash runoff | -| Kinf_nash_surface | *double* | | 1/hour [h^-1] | parameter_adjustable | surface runoff | optional (default = 0.05); storage fraction per hour that moves from reservoirs to soil | -| retention_depth_nash_surface | *double* | | m | parameter_adjustable | surface runoff | optional (default = 0.001); water retention depth threshold (only applied to the first reservoir) | -| *a_Xinanjiang_inflection_point_parameter | *double* | | | parameter_adjustable | infiltration excess runoff | when `surface_water_partitioning_scheme=Xinanjiang` | -| *b_Xinanjiang_shape_parameter=1 | *double* | | | parameter_adjustable | infiltration excess runoff | when `surface_water_partitioning_scheme=Xinanjiang` | -| *x_Xinanjiang_shape_parameter=1 | *double* | | | parameter_adjustable | infiltration excess runoff | when `surface_water_partitioning_scheme=Xinanjiang` | -| urban_decimal_fraction | *double* | 0.0 - 1.0 | | parameter_adjustable | infiltration excess runoff | when `surface_water_partitioning_scheme=Xinanjiang` | +| surface_partitioning_scheme | *char* | `Xinanjiang` or `Schaake` | | parameter_adjustable | direct runoff | | +| *a_Xinanjiang_inflection_point_parameter | *double* | | | parameter_adjustable | direct runoff | when `surface_partitioning_scheme=Xinanjiang` | +| *b_Xinanjiang_shape_parameter=1 | *double* | | | parameter_adjustable | direct runoff | when `surface_partitioning_scheme=Xinanjiang` | +| *x_Xinanjiang_shape_parameter=1 | *double* | | | parameter_adjustable | direct runoff | when `surface_partitioning_scheme=Xinanjiang` | +| urban_decimal_fraction | *double* | 0.0 - 1.0 | | parameter_adjustable | direct runoff | when `surface_partitioning_scheme=Xinanjiang` | | is_aet_rootzone | *boolean* | True, true or 1 | | coupling parameter | `rootzone-based AET` | when `CFE coupled to SoilMoistureProfile` | | max_rootzone_layer | *double* | | meters [m] | parameter_adjustable | AET | layer of the soil that is the maximum root zone depth. That is, the depth of the layer where the AET is drawn from | | soil_layer_depths | 1D array | | meters [m] | parameter_adjustable | AET | an array of depths from the surface. Example, soil_layer_depths=0.1,0.4,1.0,2.0 | is_sft_coupled | *boolean* | True, true or 1 | | coupling parameter | `ice_fraction-based runoff` | when `CFE coupled to SoilFreezeThaw`| -## Infiltration excess runoff options in CFE -The user has the option to pick a particular infiltration excess runoff (aka surface water partitioning) method: +## Direct runoff options in CFE -1. Schaake function (configuration: `surface_water_partitioning_scheme=Schaake`) -2. Xinanjiang function (configuration: `surface_water_partitioning_scheme=Xinanjiang`). When using this runoff method the user must also include four parameters. +The user has the option to pick a particular direct runoff (aka surface partitioning) method: + +1. Schaake function (configuration: `surface_partitioning_scheme=Schaake`) +2. Xinanjiang function (configuration: `surface_partitioning_scheme=Xinanjiang`). When using this runoff method the user must also include four parameters. If the **Xinanjiang** scheme is choosen, four parameters need to be included in the configuration file: 1. a_Xinanjiang_inflection_point_parameter 2. b_Xinanjiang_shape_parameter 3. x_Xinanjiang_shape_parameter -4. urban_decimal_fraction - -## Surface runoff options in CFE -The user has the option to pick a particular surface runoff (aka surface runoff scheme) method: - -1. GIUH-based surface runoff (configuration: `surface_runoff_scheme=GIUH`). This is the default option. -2. Nash_Cascade-based surface runoff (configuration: `surface_runoff_scheme=NASH_CASCADE`). In this method, GIUH is used to derive Nash cascade parameters K and N. - +4. urban_decimal_fraction ## Rootzone-based Actual Evapotranspiration (AET) The user has the option to turn ON and OFF rootzone-based AET, default option is OFF. To turn it ON, the following parameters need to be included in the configuration file. 1. `is_aet_rootzone=true` -2. `soil_layer_depths` +2. `soil_layer_depths` 3. `max_rootzone_layer` ## CFE coupled to Soil freeze-thaw model (SFT) @@ -77,6 +63,6 @@ The Soil Freeze-Thaw (SFT) model is a standalone model. For detailed informatio * If the runoff scheme is Xinanjiang, no additional parameters are needed in the CFE config files. * If the runoff scheme is Schaake, the CFE config file will need an additional parameter, namely: * `ice_content_threshold` : (type double, unit m). This represent the ice content above which soil is impermeable. + - -**Note:** By defualt `is_sft_coupled` and `is_aet_rootzone` are set to `OFF`, that means these changes do not affect the basic functionality of CFE. +**Note:** By defualt `is_sft_coupled` and `is_aet_rootzone` are set to `OFF`, that means these changes do not affect the basic functionality of CFE. \ No newline at end of file diff --git a/configs/cat_87_bmi_config_cfe.txt b/configs/cat_87_bmi_config_cfe.txt index 87dae955..ff6729b0 100644 --- a/configs/cat_87_bmi_config_cfe.txt +++ b/configs/cat_87_bmi_config_cfe.txt @@ -20,13 +20,12 @@ nash_storage=0.0,0.0 giuh_ordinates=0.06,0.51,0.28,0.12,0.03 num_timesteps=1 verbosity=2 -surface_runoff_scheme=GIUH -surface_water_partitioning_scheme=Xinanjiang +surface_partitioning_scheme=Xinanjiang a_Xinanjiang_inflection_point_parameter=1 b_Xinanjiang_shape_parameter=1 x_Xinanjiang_shape_parameter=1 urban_decimal_fraction=0.0 DEBUG=0 -#surface_water_partitioning_scheme=Schaake +#surface_partitioning_scheme=Schaake #ice_fraction=0 #ice_content_threshold=0.15 diff --git a/configs/cat_87_bmi_config_cfe_pass.txt b/configs/cat_87_bmi_config_cfe_pass.txt index 1c5f40f1..529a4efd 100644 --- a/configs/cat_87_bmi_config_cfe_pass.txt +++ b/configs/cat_87_bmi_config_cfe_pass.txt @@ -20,12 +20,12 @@ nash_storage=0.0,0.0 giuh_ordinates=0.06,0.51,0.28,0.12,0.03 num_timesteps=1 verbosity=1 -surface_runoff_scheme=GIUH -surface_water_partitioning_scheme=Xinanjiang +surface_partitioning_scheme=Xinanjiang a_Xinanjiang_inflection_point_parameter=1 b_Xinanjiang_shape_parameter=1 x_Xinanjiang_shape_parameter=1 urban_decimal_fraction=0.0 -#surface_water_partitioning_scheme=Schaake +#surface_partitioning_scheme=Schaake #ice_fraction=1 #ice_content_threshold=0.15 + diff --git a/configs/cat_87_config_cfe_pass_surfnash.txt b/configs/cat_87_config_cfe_pass_surfnash.txt deleted file mode 100644 index 3e38fa1a..00000000 --- a/configs/cat_87_config_cfe_pass_surfnash.txt +++ /dev/null @@ -1,27 +0,0 @@ -forcing_file=BMI -soil_params.depth=2.0[m] -soil_params.b=4.05[] -soil_params.satdk=0.00000338[m s-1] -soil_params.satpsi=0.355[m] -soil_params.slop=0.01[m/m] -soil_params.smcmax=0.439[m/m] -soil_params.wltsmc=0.066[m/m] -soil_params.expon=1.0[] -soil_params.expon_secondary=1.0[] -max_gw_storage=0.25[m] -Cgw=1.8e-05[m h-1] -expon=6.0[] -gw_storage=0.125[m/m] -alpha_fc=0.33[] -soil_storage=0.585626[m/m] -K_nash=0.03[] -K_lf=0.01[] -nash_storage=0.0,0.0 -surface_runoff_scheme=2 -N_nash_surface=2 -K_nash_surface=0.83089 -nsubsteps_nash_surface=10 -nash_storage_surface=0.0,0.0 -num_timesteps=1 -verbosity=1 -surface_partitioning_scheme=Schaake diff --git a/configs/cat_89_bmi_config_cfe_unit_test.txt b/configs/cat_89_bmi_config_cfe_unit_test.txt index b4f6608e..692175de 100644 --- a/configs/cat_89_bmi_config_cfe_unit_test.txt +++ b/configs/cat_89_bmi_config_cfe_unit_test.txt @@ -20,5 +20,4 @@ nash_storage=0.0,0.0 giuh_ordinates=0.06,0.51,0.28,0.12,0.03 num_timesteps=1 verbosity=2 -surface_runoff_scheme=GIUH -surface_water_partitioning_scheme=Schaake +surface_partitioning_scheme=Schaake diff --git a/configs/laramie_bmi_config_cfe_pass_aet_rz.txt b/configs/laramie_bmi_config_cfe_pass_aet_rz.txt index b0f899ad..457fce8d 100644 --- a/configs/laramie_bmi_config_cfe_pass_aet_rz.txt +++ b/configs/laramie_bmi_config_cfe_pass_aet_rz.txt @@ -20,9 +20,8 @@ nash_storage=0.0,0.0 giuh_ordinates=0.06,0.51,0.28,0.12,0.03 num_timesteps=1 verbosity=1 -surface_runoff_scheme=GIUH -surface_water_partitioning_scheme=Schaake -#surface_water_partitioning_scheme=Xinanjiang +surface_partitioning_scheme=Schaake +#surface_partitioning_scheme=Xinanjiang #a_Xinanjiang_inflection_point_parameter=1 #b_Xinanjiang_shape_parameter=1 #x_Xinanjiang_shape_parameter=1 diff --git a/include/bmi.h b/include/bmi.h index 9cc04d64..e6bb6d12 100755 --- a/include/bmi.h +++ b/include/bmi.h @@ -35,8 +35,8 @@ SOFTWARE. extern "C" { #endif -const static int BMI_SUCCESS = 0; -const static int BMI_FAILURE = 1; +#define BMI_SUCCESS (0) +#define BMI_FAILURE (1) #define BMI_MAX_UNITS_NAME (2048) #define BMI_MAX_TYPE_NAME (2048) diff --git a/include/bmi_cfe.h b/include/bmi_cfe.h index 1616eb9d..30efb34d 100644 --- a/include/bmi_cfe.h +++ b/include/bmi_cfe.h @@ -50,7 +50,6 @@ struct cfe_state_struct { double timestep_rainfall_input_m; double soil_reservoir_storage_deficit_m; double infiltration_depth_m; - double* infiltration_excess_m; double gw_reservoir_storage_deficit_m; double timestep_h; @@ -65,7 +64,7 @@ struct cfe_state_struct { struct massbal vol_struct; /* xinanjiang_dev */ - struct infiltration_excess_parameters_structure infiltration_excess_params_struct; + struct direct_runoff_parameters_structure direct_runoff_params_struct; // Epoch-based start time (BMI start time is considered 0.0) long epoch_start_time; @@ -83,16 +82,14 @@ struct cfe_state_struct { //LKC Changed this to N_nash for consistency //int num_lateral_flow_nash_reservoirs; - + //LKC: added N_nash the same way as the other Nash parameters - making this consistent double K_lf; - double K_nash; + double K_nash; int N_nash; int num_giuh_ordinates; - int surface_runoff_scheme; // options: giuh-based runoff and nash cascade-based runoff - // *********************************************************** // ******************* Dynamic allocations ******************* // *********************************************************** @@ -106,9 +103,12 @@ struct cfe_state_struct { double* nash_storage; double* runoff_queue_m_per_timestep; - struct nash_cascade_parameters nash_surface_params; + /* xinanjiang_dev + changing the name to the more general "direct runoff" + double* flux_Schaake_output_runoff_m;*/ + double* flux_output_direct_runoff_m ; - double* flux_direct_runoff_m; + double* flux_giuh_runoff_m; double* flux_nash_lateral_runoff_m; double* flux_from_deep_gw_to_chan_m; double* flux_perc_m; @@ -137,13 +137,13 @@ extern void get_word_cfe(char *theString,int *start,int *end,char *theWord,int * /*int read_init_config_cfe(const char* config_file, cfe_state_struct* model, double* alpha_fc, double* soil_storage, int* is_soil_storage_ratio);*/ -//LKC removed double alpha_fc since it has been added to the soil parameter structure +//LKC removed double alpha_fc since it has been added to the soil parameter structure int read_init_config_cfe(const char* config_file, cfe_state_struct* model); /*extern void init_soil_reservoir(cfe_state_struct* cfe_ptr, double alpha_fc, double max_storage, double storage, int is_storage_ratios);*/ -//LKC removed double alpha_fc since it has been added to the soil parameter structure +//LKC removed double alpha_fc since it has been added to the soil parameter structure extern void init_soil_reservoir(cfe_state_struct* cfe_ptr); //extern double init_reservoir_storage(int is_ratio, double amount, double max_amount); diff --git a/include/cfe.h b/include/cfe.h index 66e608ff..795ac52e 100644 --- a/include/cfe.h +++ b/include/cfe.h @@ -9,15 +9,16 @@ #include #include "conceptual_reservoir.h" #include "giuh.h" -#include "nash_cascade.h" #define TRUE 1 #define FALSE 0 +#define MAX_NUM_GIUH_ORDINATES 10 +#define MAX_NUM_NASH_CASCADE 3 #define MAX_NUM_RAIN_DATA 720 // t-shirt approximation of the hydrologic routing funtionality of the National Water Model v 1.2, 2.0, and 2.1 // This code was developed to test the hypothesis that the National Water Model runoff generation, vadose zone -// dynamics, and conceptual groundwater model can be greatly simplified by acknowledging that it is truly a +// dynamics, and conceptual groundwater model can be greatly simplified by acknowledging that it is truly a // conceptual model. The hypothesis is supported by a number of observations made during a 2017-2018 deep dive // into the NWM code. Thesed are: // @@ -26,7 +27,7 @@ // function by Moore, 1985. The Schaake function is a single valued function of soil moisture deficit, // predicts 100% runoff when the soil is saturated, like the curve-number method, and is fundamentally simple. // 2. Run-on infiltration is strictly not calculated. Overland flow routing applies the Schaake function repeatedly -// to predict this phenomenon, which violates the underlying assumption of the PDM method that only rainfall +// to predict this phenomenon, which violates the underlying assumption of the PDM method that only rainfall // inputs affect soil moisture. // 3. The water-content based Richards' equation, applied using a coarse-discretization, can be replaced with a simple // conceptual reservoir because it never allows saturation or infiltration-excess runoff unless deactivated by @@ -99,11 +100,10 @@ typedef struct evapotranspiration_structure evapotranspiration_structure; struct massbal { double volstart ; - double vol_runoff ; - double vol_infilt ; - double vol_runon_infilt ; - double vol_out_surface ; - double vol_end_surface ; + double vol_runoff ; + double vol_infilt ; + double vol_out_giuh ; + double vol_end_giuh ; double vol_to_gw ; double vol_in_gw_start ; double vol_in_gw_end ; @@ -117,8 +117,8 @@ struct massbal double vol_soil_to_gw ; // this should equal vol_to_gw double vol_soil_end ; double vol_et_from_soil ; - double vol_et_from_rain ; - double vol_et_to_atm ; + double vol_et_from_rain ; + double vol_et_to_atm ; double volin ; double volout ; double volend ; @@ -129,11 +129,9 @@ typedef struct massbal massbal; //-------------------------- typedef enum {Schaake=1, Xinanjiang=2} surface_water_partition_type; -typedef enum {GIUH=1, NASH_CASCADE=2} surface_runoff_scheme; - /* xinanjiang_dev*/ -struct infiltration_excess_parameters_structure { - surface_water_partition_type surface_water_partitioning_scheme; +struct direct_runoff_parameters_structure{ + surface_water_partition_type surface_partitioning_scheme; double Schaake_adjusted_magic_constant_by_soil_type; double a_Xinanjiang_inflection_point_parameter; double b_Xinanjiang_shape_parameter; @@ -141,25 +139,29 @@ struct infiltration_excess_parameters_structure { double urban_decimal_fraction; double ice_content_threshold; // ice content above which soil is impermeable }; -typedef struct infiltration_excess_parameters_structure infiltration_excess_parameters_structure; +typedef struct direct_runoff_parameters_structure direct_runoff_parameters_structure; // function prototypes // -------------------------------- extern void Schaake_partitioning_scheme(double dt, double field_capacity_m, double magic_number, double deficit, double qinsur, - double smcmax, double soil_depth, double *runsrf, double *pddum, double ice_fraction_schaake, - double ice_content_threshold); + double smcmax, double soil_depth, double *runsrf, double *pddum, double ice_fraction_schaake, double ice_content_threshold); -// xinanjiang_dev: XinJiang function written by Rachel adapted by Jmframe and FLO, +// xinanjiang_dev: XinJiang function written by Rachel adapted by Jmframe and FLO, extern void Xinanjiang_partitioning_scheme(double water_input_depth_m, double field_capacity_m, - double max_soil_moisture_storage_m, double column_total_soil_water_m, - struct infiltration_excess_parameters_structure *parms, double *infiltration_excess_m, - double *infiltration_depth_m, double ice_fraction_xinanjiang); + double max_soil_moisture_storage_m, double column_total_soil_water_m, + struct direct_runoff_parameters_structure *parms, double *surface_runoff_depth_m, + double *infiltration_depth_m, double ice_fraction_xinanjiang); + +extern double convolution_integral(double runoff_m, int num_giuh_ordinates, + double *giuh_ordinates, double *runoff_queue_m_per_timestep); + +extern double nash_cascade(double flux_lat_m,int num_lateral_flow_nash_reservoirs, + double K_nash,double *nash_storage_arr); extern void et_from_rainfall(double *timestep_rainfall_input_m, struct evapotranspiration_structure *et_struct); -extern void et_from_soil(struct conceptual_reservoir *soil_res, struct evapotranspiration_structure *et_struct, - struct NWM_soil_parameters *soil_parms); +extern void et_from_soil(struct conceptual_reservoir *soil_res, struct evapotranspiration_structure *et_struct, struct NWM_soil_parameters *soil_parms); extern int is_fabs_less_than_epsilon(double a,double epsilon); @@ -168,13 +170,19 @@ extern void cfe( struct NWM_soil_parameters NWM_soil_params_struct, struct conceptual_reservoir *soil_reservoir_struct, double timestep_h, - /* xinanjiang_dev: since we are doing the option for Schaake and XinJiang, + + /* xinanjiang_dev: since we are doing the option for Schaake and XinJiang, instead of passing in the constants pass in a structure with the constants for both subroutines. //double Schaake_adjusted_magic_constant_by_soil_type,*/ - struct infiltration_excess_parameters_structure infiltration_excess_params_struct, + struct direct_runoff_parameters_structure direct_runoff_param_struct, + double timestep_rainfall_input_m, - double *infiltration_excess_m_ptr, + + /* xinanjiang_dev: + double *Schaake_output_runoff_m_ptr,*/ + double *flux_output_direct_runoff_m, + double *infiltration_depth_m_ptr, double *flux_perc_m_ptr, double *flux_lat_m_ptr, @@ -189,12 +197,10 @@ extern void cfe( int num_lateral_flow_nash_reservoirs, double K_nash, double *nash_storage_arr, - struct nash_cascade_parameters *nash_surface_params, struct evapotranspiration_structure *evap_struct, double *Qout_m_ptr, struct massbal *massbal_struct, - double time_step_size, - int surface_runoff_scheme + double time_step_size ); #endif //CFE_CFE_H diff --git a/include/giuh.h b/include/giuh.h index 849f9b2f..82ce2f34 100644 --- a/include/giuh.h +++ b/include/giuh.h @@ -2,6 +2,6 @@ #define _GIUH_H extern double giuh_convolution_integral(double runoff_m, int num_giuh_ordinates, - double *giuh_ordinates, double *runoff_queue_m_per_timestep); + double *giuh_ordinates, double *runoff_queue_m_per_timestep); #endif diff --git a/include/logger.h b/include/logger.h deleted file mode 100644 index 80ac4baf..00000000 --- a/include/logger.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef LOGGER_H -#define LOGGER_H - -#include -#include -#include -#include - -typedef enum { - NONE = 0, - DEBUG, - INFO, - FATAL, - WARN, - ERROR, -} LogLevel; - -typedef enum { - NGEN, - NOAHOWP, - SNOW17, - UEB, - CFE, - SACSMA, - LASAM, - SMP, - SFT, - TROUTE, - SCHISM, - SFINCS, - GC2D, - TOPOFLOW, - MODULE_COUNT -} LoggingModule; - -static const char* module_name[MODULE_COUNT] = { - "NGEN ", - "NOAHOWP ", - "SNOW17 ", - "UEB ", - "CFE ", - "SACSMA ", - "LASAM ", - "SMP ", - "SFT ", - "TROUTE ", - "SCHISM ", - "SFINCS ", - "GC2D ", - "TOPOFLOW" -}; - -typedef struct { - LogLevel logLevel; - FILE* logFile; -} Logger; - -Logger* GetInstance(); -void SetLogPreferences(Logger* logger, LogLevel level); -void Log(Logger* logger, const char* message, LogLevel messageLevel); -LogLevel GetLogLevel(const char* logLevel); -char* createTimestamp(); -void setup_logger(void); - -#endif diff --git a/include/nash_cascade.h b/include/nash_cascade.h deleted file mode 100644 index 7b809f19..00000000 --- a/include/nash_cascade.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _NASH_H -#define _NASH_H - -#include - -#define MAX_NUM_NASH_CASCADE 3 - -// this data structure describes runoff using Nash Cascade model in the subsurface and on the surface -struct nash_cascade_parameters { - int N_nash; // Number of Nash cascade reservoirs; [-] - double K_nash; // Fraction of storage per hour that moves from one reservoir to the next (time constant); [1/hour] - int nsubsteps; // the number of substeps that each dt is divided into - double *nash_storage; // storage array nash cascade reservoirs; [m] - double retention_depth; /* parameter that represents retention depth process that is equivalent [m] - to that used in WRF-Hydro */ - double runon_infiltration; // infiltration losses from surface runoff water to soil (or riparian groundwater) [m/hr] - int is_riparian_gw; // flag to turn on/off riparian groundwater (currently used in LASAM only) - double K_infiltration; // Fraction of storage per hour that moves from reservoirs to soil (time constant); [1/hour] -}; - -extern double nash_cascade(double flux_lat_m,int num_lateral_flow_nash_reservoirs, - double K_nash,double *nash_storage_arr); - -double nash_cascade_surface_runoff(double runoff_m, double soil_storage_deficit_m, - struct nash_cascade_parameters *nash_params); - -#endif diff --git a/realizations/realization_cfe_pet_surfgiuh.json b/realizations/realization_cfe_pet.json similarity index 100% rename from realizations/realization_cfe_pet_surfgiuh.json rename to realizations/realization_cfe_pet.json diff --git a/realizations/realization_cfe_pet_surfnash_calib.json b/realizations/realization_cfe_pet_surfnash_calib.json deleted file mode 100644 index f9cb3e7c..00000000 --- a/realizations/realization_cfe_pet_surfnash_calib.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "global": { - "formulations": [ - { - "name": "bmi_multi", - "params": { - "model_type_name": "bmi_multi_noahowp_cfe", - "forcing_file": "", - "init_config": "", - "allow_exceed_end_time": true, - "main_output_variable": "Q_OUT", - "output_variables" : [ - "RAIN_RATE", - "INFILTRATION_EXCESS", - "NASH_LATERAL_RUNOFF", - "DEEP_GW_TO_CHANNEL_FLUX", - "SOIL_TO_GW_FLUX", - "Q_OUT", - "POTENTIAL_ET", - "ACTUAL_ET", - "SOIL_STORAGE" - ], - "modules": [ - { - "name": "bmi_c++", - "params": { - "model_type_name": "bmi_c++_sloth", - "library_file": "./extern/sloth/cmake_build/libslothmodel", - "init_config": "/dev/null", - "allow_exceed_end_time": true, - "main_output_variable": "z", - "uses_forcing_file": false, - "model_params": { - "sloth_ice_fraction_schaake(1,double,m,node)": 0.0, - "sloth_ice_fraction_xinanjiang(1,double,1,node)": 0.0, - "sloth_smp(1,double,1,node)": 0.0 - } - } - }, - { - "name": "bmi_c", - "params": { - "model_type_name": "bmi_c_pet", - "library_file": "./extern/evapotranspiration/evapotranspiration/cmake_build/libpetbmi", - "forcing_file": "", - "init_config": "./extern/cfe/cfe/configs/cat_87_bmi_config_pet_pass.txt", - "allow_exceed_end_time": true, - "main_output_variable": "water_potential_evaporation_flux", - "registration_function":"register_bmi_pet", - "uses_forcing_file": false - } - }, - { - "name": "bmi_c", - "params": { - "model_type_name": "bmi_c_cfe", - "library_file": "./extern/cfe/cfe/cmake_build/libcfebmi", - "forcing_file": "", - "init_config": "./extern/cfe/cfe/configs/cat_87_config_cfe_pass_surfnash.txt", - "allow_exceed_end_time": true, - "main_output_variable": "Q_OUT", - "registration_function": "register_bmi_cfe", - "variables_names_map": { - "water_potential_evaporation_flux" : "water_potential_evaporation_flux", - "atmosphere_water__liquid_equivalent_precipitation_rate" : "APCP_surface", - "atmosphere_air_water~vapor__relative_saturation" : "SPFH_2maboveground", - "land_surface_air__temperature" : "TMP_2maboveground", - "land_surface_wind__x_component_of_velocity" : "UGRD_10maboveground", - "land_surface_wind__y_component_of_velocity" : "VGRD_10maboveground", - "land_surface_radiation~incoming~longwave__energy_flux" : "DLWRF_surface", - "land_surface_radiation~incoming~shortwave__energy_flux" : "DSWRF_surface", - "land_surface_air__pressure" : "PRES_surface", - "ice_fraction_schaake" : "sloth_ice_fraction_schaake", - "ice_fraction_xinanjiang" : "sloth_ice_fraction_xinanjiang", - "soil_moisture_profile" : "sloth_smp" - }, - "model_params": { - "K_infiltration": 0.04 - }, - "uses_forcing_file": false - } - } - ], - "uses_forcing_file": false - } - } - ], - "forcing": { - "path" : "./extern/cfe/cfe/forcings/cat87_01Dec2015-.csv" - } - }, - "time": { - "start_time": "2015-12-01 00:00:00", - "end_time": "2015-12-01 00:00:00", - "output_interval": 3600 - } -} diff --git a/src/Makefile b/src/Makefile index 4c17e172..b78e3ebe 100755 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,7 @@ LFLAGS = LIBS = -lm # define the C source files -SRCS = main.c cfe.c bmi_cfe.c logger.c +SRCS = main.c cfe.c bmi_cfe.c # define the C object files OBJS = $(SRCS:.c=.o) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 235f01d7..e8c32d47 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -5,8 +5,6 @@ #include "bmi_cfe.h" #include #include -#include "logger.h" - #ifndef WATER_SPECIFIC_WEIGHT #define WATER_SPECIFIC_WEIGHT 9810 #define STANDARD_ATMOSPHERIC_PRESSURE_PASCALS 101325 @@ -15,19 +13,18 @@ #define CFE_DEBUG 1 #define INPUT_VAR_NAME_COUNT 5 -#define OUTPUT_VAR_NAME_COUNT 14 +#define OUTPUT_VAR_NAME_COUNT 13 #define STATE_VAR_NAME_COUNT 94 // must match var_info array size -#define PARAM_VAR_NAME_COUNT 18 +#define PARAM_VAR_NAME_COUNT 17 // NOTE: If you update the params, also update the unit test in ../test/main_unit_test_bmi.c static const char *param_var_names[PARAM_VAR_NAME_COUNT] = { - "maxsmc", "satdk", "slope", "b", "Klf", + "maxsmc", "satdk", "slope", "b", "Klf", "Kn", "Cgw", "expon", "max_gw_storage", "satpsi","wltsmc","alpha_fc","refkdt", "a_Xinanjiang_inflection_point_parameter","b_Xinanjiang_shape_parameter","x_Xinanjiang_shape_parameter", - "K_infiltration", "N_nash" }; @@ -35,7 +32,7 @@ static const char *param_var_types[PARAM_VAR_NAME_COUNT] = { "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", - "double","double","double", "double", + "double","double","double", "int" }; //---------------------------------------------- @@ -109,25 +106,25 @@ Variable var_info[] = { { 43, "volout", "double", 1 }, { 44, "volin", "double", 1 }, { 45, "vol_from_gw", "double", 1 }, - { 46, "vol_out_surface", "double", 1 }, + { 46, "vol_out_giuh", "double", 1 }, { 47, "vol_in_nash", "double", 1 }, { 48, "vol_out_nash", "double", 1 }, { 49, "vol_in_gw_start", "double", 1 }, { 50, "vol_soil_start", "double", 1 }, - //----------------------------------------- + //----------------------------------------- // More top-level, static allocation vars //----------------------------------------- - { 51, "epoch_start_time", "long", 1 }, - { 52, "num_timesteps", "int", 1 }, - { 53, "current_time_step", "int", 1 }, - { 54, "time_step_size", "int", 1 }, - { 55, "is_forcing_from_bmi", "int", 1 }, - { 56, "forcing_file", "string", 1 }, // strlen - { 57, "surface_water_partitioning_scheme", "int", 1 }, // from infiltration_excess_params_struct - { 58, "N_nash", "int", 1 }, - { 59, "K_lf", "double", 1 }, - { 60, "K_nash", "double", 1 }, - { 61, "num_giuh_ordinates", "int", 1 }, + { 51, "epoch_start_time", "long", 1 }, + { 52, "num_timesteps", "int", 1 }, + { 53, "current_time_step", "int", 1 }, + { 54, "time_step_size", "int", 1 }, + { 55, "is_forcing_from_bmi", "int", 1 }, + { 56, "forcing_file", "string", 1 }, // strlen + { 57, "surface_partitioning_scheme", "int", 1 }, // from direct_runoff_params_struct + { 58, "N_nash", "int", 1 }, + { 59, "K_lf", "double", 1 }, + { 60, "K_nash", "double", 1 }, + { 61, "num_giuh_ordinates", "int", 1 }, //--------------------------------------- // Vars in aorc_forcing_data_cfe struct //--------------------------------------- @@ -142,7 +139,7 @@ Variable var_info[] = { { 70, "latitude", "double", 1 }, { 71, "longitude", "double", 1 }, { 72, "time", "long", 1 }, - //------------------------------------------ + //------------------------------------------ // More top-level, dynamic allocation vars // (all pointers except verbosity) //------------------------------------------ @@ -152,7 +149,7 @@ Variable var_info[] = { { 76, "nash_storage", "double*", 1 }, // num_lat_flow { 77, "runoff_queue_m_per_timestep", "double*", 1 }, // num_giuh { 78, "flux_Schaake_output_runoff_m", "double*", 1 }, - { 79, "flux_direct_runoff_m", "double*", 1 }, + { 79, "flux_giuh_runoff_m", "double*", 1 }, { 80, "flux_nash_lateral_runoff_m", "double*", 1 }, { 81, "flux_from_deep_gw_to_chan_m", "double*", 1 }, { 82, "flux_from_soil_to_gw_m", "double*", 1 }, @@ -161,7 +158,7 @@ Variable var_info[] = { { 85, "flux_Qout_m", "double*", 1 }, { 86, "verbosity", "int", 1 }, //--------------------------------------- - // infiltration_excess_runoff_params_struct vars + // direct_runoff_params_struct vars // xinanjiang or schaake flag [56] //--------------------------------------- { 87, "Schaake_adjusted_magic_constant_by_soil_type", "double", 1}, @@ -183,9 +180,8 @@ int j = 0; // Don't forget to update Get_value/Get_value_at_indices (and setter) implementation if these are adjusted static const char *output_var_names[OUTPUT_VAR_NAME_COUNT] = { "RAIN_RATE", - "GIUH_RUNOFF", - "INFILTRATION_EXCESS", "DIRECT_RUNOFF", + "GIUH_RUNOFF", "NASH_LATERAL_RUNOFF", "DEEP_GW_TO_CHANNEL_FLUX", "SOIL_TO_GW_FLUX", @@ -194,8 +190,8 @@ static const char *output_var_names[OUTPUT_VAR_NAME_COUNT] = { "ACTUAL_ET", "GW_STORAGE", "SOIL_STORAGE", - "SOIL_STORAGE_CHANGE", - "SURF_RUNOFF_SCHEME" + "SOIL_STORAGE_CHANGE", + "SURF_RUNOFF_SCHEME" }; static const char *output_var_types[OUTPUT_VAR_NAME_COUNT] = { @@ -211,7 +207,6 @@ static const char *output_var_types[OUTPUT_VAR_NAME_COUNT] = { "double", "double", "double", - "double", "int" }; @@ -228,7 +223,6 @@ static const int output_var_item_count[OUTPUT_VAR_NAME_COUNT] = { 1, 1, 1, - 1, 1 }; @@ -245,7 +239,6 @@ static const char *output_var_units[OUTPUT_VAR_NAME_COUNT] = { "m", "m", "m", - "m", "none" }; @@ -262,7 +255,6 @@ static const int output_var_grids[OUTPUT_VAR_NAME_COUNT] = { 0, 0, 0, - 0, 0 }; @@ -279,8 +271,7 @@ static const char *output_var_locations[OUTPUT_VAR_NAME_COUNT] = { "node", "node", "node", - "node", - "none" + "node" }; // Don't forget to update Get_value/Get_value_at_indices (and setter) implementation if these are adjusted @@ -319,17 +310,17 @@ static const int input_var_item_count[INPUT_VAR_NAME_COUNT] = { static const char input_var_grids[INPUT_VAR_NAME_COUNT] = { 0, 0, - 0, - 0, - 0 + 0, + 0, + 0 }; static const char *input_var_locations[INPUT_VAR_NAME_COUNT] = { "node", "node", - "node", - "node", - "node" + "node", + "node", + "node" }; static int Get_start_time (Bmi *self, double * time) @@ -349,7 +340,7 @@ static int Get_end_time (Bmi *self, double * time) cfe_state_struct *cfe; cfe = (cfe_state_struct *) self->data; Get_start_time(self, time); - + // see if forcings read in or via BMI (framework to set) //jmframe: the docs say that "If the model doesn’t define an end time, a large number" // so even if it is BMI, we should still use num_timesteps @@ -483,12 +474,12 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) int is_verbosity_set = FALSE; /* xinanjiang_dev*/ - int is_infiltration_excess_method_set = FALSE; + int is_direct_runoff_method_set = FALSE; int is_a_Xinanjiang_inflection_point_parameter_set = FALSE; - int is_b_Xinanjiang_shape_parameter_set = FALSE; - int is_x_Xinanjiang_shape_parameter_set = FALSE; - int is_urban_decimal_fraction_set = FALSE; - + int is_b_Xinanjiang_shape_parameter_set = FALSE; + int is_x_Xinanjiang_shape_parameter_set = FALSE; + int is_urban_decimal_fraction_set = FALSE; + /* Ice fraction */ int is_sft_coupled_set = FALSE; int is_ice_content_threshold_set = FALSE; @@ -504,19 +495,6 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) int is_soil_layer_depths_string_val_set = FALSE; int is_max_rootzone_layer_set = FALSE; /*--------------------------------------------------------*/ - - /* ------------ Nash model struct -AJK ------------------ */ - int is_surface_runoff_scheme_set = FALSE; - int is_K_nash_surface_set = FALSE; - int is_N_nash_surface_set = FALSE; - int is_nsubsteps_nash_surface_set = FALSE; - int is_nash_storage_surface_set = FALSE; - int is_K_infiltration_nash_surface_set = FALSE; - int is_retention_depth_nash_surface_set = FALSE; - - char* nash_storage_surface_string_val; - /*--------------------------------------------------------*/ - // Default value model->NWM_soil_params.refkdt = 3.0; @@ -562,7 +540,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__depth_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -578,7 +556,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__satdk_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -589,7 +567,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__satpsi_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -600,7 +578,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__slop_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -611,7 +589,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__smcmax_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -622,7 +600,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__wltsmc_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -643,7 +621,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_gw_max_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -658,7 +636,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_Cgw_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -675,7 +653,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_gw_storage_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -699,7 +677,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_alpha_fc_set = TRUE; continue; } - + if (strcmp(param_key, "soil_storage") == 0) { /* char* trailing_chars; double parsed_value = strtod(param_value, &trailing_chars); @@ -709,7 +687,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_storage_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } @@ -758,13 +736,13 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) if ( strcmp(param_value, "true")==0 || strcmp(param_value, "True")==0 || strcmp(param_value,"1")==0) is_aet_rootzone_set = TRUE; - + continue; } if (is_aet_rootzone_set == TRUE) { if (strcmp(param_key, "soil_layer_depths") == 0) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf("Found configured soil depth values ('%s')\n", param_value); #endif soil_layer_depths_string_val = strdup(param_value); @@ -777,400 +755,243 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) } } - /*--------------------------------------------------------------------------*/ - - /* Nash cascade based surface runoff */ - if (strcmp(param_key, "surface_runoff_scheme") == 0) { - if (strcmp(param_value, "GIUH")==0 || strcmp(param_value, "giuh")==0 || strcmp(param_value,"1")==0 ) - model->surface_runoff_scheme = GIUH; - if (strcmp(param_value, "NASH_CASCADE")==0 || strcmp(param_value, "nash_cascade")==0 || strcmp(param_value,"2")==0) - model->surface_runoff_scheme = NASH_CASCADE; - is_surface_runoff_scheme_set = TRUE; - continue; - } - - if (strcmp(param_key, "N_nash_surface") == 0) { - model->nash_surface_params.N_nash = strtol(param_value, NULL,10); - is_N_nash_surface_set = TRUE; - continue; - } - if (strcmp(param_key, "K_nash_surface") == 0) { - model->nash_surface_params.K_nash = strtod(param_value, NULL); - is_K_nash_surface_set = TRUE; - continue; - } - if (strcmp(param_key, "nsubsteps_nash_surface") == 0) { - model->nash_surface_params.nsubsteps = strtol(param_value, NULL, 10); - is_nsubsteps_nash_surface_set = TRUE; - continue; - } - if (strcmp(param_key, "nash_storage_surface") == 0) { - nash_storage_surface_string_val = strdup(param_value); - is_nash_storage_surface_set = TRUE; - continue; - } - if (strcmp(param_key, "Kinf_nash_surface") == 0) { - model->nash_surface_params.K_infiltration = strtod(param_value, NULL); - is_K_infiltration_nash_surface_set = TRUE; - continue; + /*--------------------------------------------------------------------------*/ + + /* xinanjiang_dev: Need the option to run either runoff method in the config file, + *////////////////////////////////////////////////////////////////////////////// + if (strcmp(param_key, "surface_partitioning_scheme") == 0) { + if (strcmp(param_value, "Schaake")==0 || strcmp(param_value, "schaake")==0 || strcmp(param_value,"1")==0 ) + model->direct_runoff_params_struct.surface_partitioning_scheme = Schaake; + if (strcmp(param_value, "Xinanjiang")==0 || strcmp(param_value, "xinanjiang")==0 || strcmp(param_value,"2")==0) + model->direct_runoff_params_struct.surface_partitioning_scheme = Xinanjiang; + is_direct_runoff_method_set = TRUE; + continue; } - if (strcmp(param_key, "retention_depth_nash_surface") == 0) { - model->nash_surface_params.retention_depth = strtod(param_value, NULL); - is_retention_depth_nash_surface_set = TRUE; - continue; + if (model->direct_runoff_params_struct.surface_partitioning_scheme == Xinanjiang) { //Check that logical statement is correct + if (strcmp(param_key, "a_Xinanjiang_inflection_point_parameter") == 0){ + model->direct_runoff_params_struct.a_Xinanjiang_inflection_point_parameter = strtod(param_value, NULL); + is_a_Xinanjiang_inflection_point_parameter_set = TRUE; + } + if (strcmp(param_key, "b_Xinanjiang_shape_parameter") == 0) { + model->direct_runoff_params_struct.b_Xinanjiang_shape_parameter = strtod(param_value, NULL); + is_b_Xinanjiang_shape_parameter_set = TRUE; + } + if (strcmp(param_key, "x_Xinanjiang_shape_parameter") == 0) { + model->direct_runoff_params_struct.x_Xinanjiang_shape_parameter = strtod(param_value, NULL); + is_x_Xinanjiang_shape_parameter_set = TRUE; + } + if (strcmp(param_key, "urban_decimal_fraction") == 0) { + model->direct_runoff_params_struct.urban_decimal_fraction = strtod(param_value, NULL); + is_urban_decimal_fraction_set = TRUE; + } } - - /*--------------------------------------------------------------------------*/ - /* xinanjiang_dev: Need the option to run either runoff method in the config file, - *////////////////////////////////////////////////////////////////////////////// - if (strcmp(param_key, "surface_water_partitioning_scheme") == 0 || strcmp(param_key, "surface_partitioning_scheme") == 0) { - if (strcmp(param_value, "Schaake")==0 || strcmp(param_value, "schaake")==0 || strcmp(param_value,"1")==0 ) - model->infiltration_excess_params_struct.surface_water_partitioning_scheme = Schaake; - if (strcmp(param_value, "Xinanjiang")==0 || strcmp(param_value, "xinanjiang")==0 || strcmp(param_value,"2")==0) - model->infiltration_excess_params_struct.surface_water_partitioning_scheme = Xinanjiang; - is_infiltration_excess_method_set = TRUE; - - if (strcmp(param_key, "surface_partitioning_scheme") == 0) { - printf("WARNING: The name \"surface_partitioning_scheme\" in the config file will be deprecated; use the new name \"surface_water_partitioning_scheme\" instead. \n"); - } - continue; - } - if (model->infiltration_excess_params_struct.surface_water_partitioning_scheme == Xinanjiang) { //Check that logical statement is correct - if (strcmp(param_key, "a_Xinanjiang_inflection_point_parameter") == 0){ - model->infiltration_excess_params_struct.a_Xinanjiang_inflection_point_parameter = strtod(param_value, NULL); - is_a_Xinanjiang_inflection_point_parameter_set = TRUE; - } - if (strcmp(param_key, "b_Xinanjiang_shape_parameter") == 0) { - model->infiltration_excess_params_struct.b_Xinanjiang_shape_parameter = strtod(param_value, NULL); - is_b_Xinanjiang_shape_parameter_set = TRUE; - } - if (strcmp(param_key, "x_Xinanjiang_shape_parameter") == 0) { - model->infiltration_excess_params_struct.x_Xinanjiang_shape_parameter = strtod(param_value, NULL); - is_x_Xinanjiang_shape_parameter_set = TRUE; - } - if (strcmp(param_key, "urban_decimal_fraction") == 0) { - model->infiltration_excess_params_struct.urban_decimal_fraction = strtod(param_value, NULL); - is_urban_decimal_fraction_set = TRUE; - } - } - - /* Ice fraction: if set to true and runoff scheme is Schaake, additional parameters are needed in the config file, - *////////////////////////////////////////////////////////////////////////////// - if (strcmp(param_key, "is_sft_coupled") == 0) { + /* Ice fraction: if set to true and runoff scheme is Schaake, additional parameters are needed in the config file, + *////////////////////////////////////////////////////////////////////////////// + if (strcmp(param_key, "is_sft_coupled") == 0) { if ( strcmp(param_value, "true")==0 || strcmp(param_value, "True")==0 || strcmp(param_value,"1")==0) is_sft_coupled_set = TRUE; - + continue; - } - - if (is_sft_coupled_set == TRUE && model->infiltration_excess_params_struct.surface_water_partitioning_scheme == Schaake) { + } + + if (is_sft_coupled_set == TRUE && model->direct_runoff_params_struct.surface_partitioning_scheme == Schaake) { if (strcmp(param_key, "ice_content_threshold") == 0) { - model->infiltration_excess_params_struct.ice_content_threshold = strtod(param_value, NULL); + model->direct_runoff_params_struct.ice_content_threshold = strtod(param_value, NULL); is_ice_content_threshold_set = TRUE; - // Check if units are present and print warning if missing from config file + // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 +#if CFE_DEBUG >= 1 printf ("WARNING: [units] expected for '%s' in config file \n", param_key); #endif } } } } - + if (is_forcing_file_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'forcing_file' not found in config file\n"); + printf("Config param 'forcing_file' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_soil_params__depth_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.depth' not found in config file\n"); + printf("Config param 'soil_params.depth' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_soil_params__bb_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.bb' not found in config file\n"); + printf("Config param 'soil_params.bb' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } - + if (is_soil_params__satdk_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.satdk' not found in config file\n"); + printf("Config param 'soil_params.satdk' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_soil_params__satpsi_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.satpsi' not found in config file\n"); + printf("Config param 'soil_params.satpsi' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_soil_params__slop_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.slop' not found in config file\n"); + printf("Config param 'soil_params.slop' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_soil_params__smcmax_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.smcmax' not found in config file\n"); + printf("Config param 'soil_params.smcmax' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_soil_params__wltsmc_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.wltsmc' not found in config file\n"); + printf("Config param 'soil_params.wltsmc' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_soil_params__expon_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.expon' not found in config file, defaulting to 1 (linear)\n"); + printf("Config param 'soil_params.expon' not found in config file, defaulting to 1 (linear)\n"); #endif - model->soil_reservoir.exponent_primary = 1.0; - //is_soil_params__expon_set == TRUE; - // Don't return BMI_FAILURE, this is a optional config - //return BMI_FAILURE; + model->soil_reservoir.exponent_primary = 1.0; + //is_soil_params__expon_set == TRUE; + // Don't return BMI_FAILURE, this is a optional config + //return BMI_FAILURE; } if (is_soil_params__expon2_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_params.expon_secondary' not found in config file, defaulting to 1 (linear)\n"); + printf("Config param 'soil_params.expon_secondary' not found in config file, defaulting to 1 (linear)\n"); #endif - model->soil_reservoir.exponent_secondary = 1.0; - // Don't return BMI_FAILURE, this is a optional config - //return BMI_FAILURE; + model->soil_reservoir.exponent_secondary = 1.0; + // Don't return BMI_FAILURE, this is a optional config + //return BMI_FAILURE; } if (is_Cgw_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'Cgw' not found in config file\n"); + printf("Config param 'Cgw' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_expon_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'expon' not found in config file\n"); + printf("Config param 'expon' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_alpha_fc_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'alpha_fc' not found in config file\n"); + printf("Config param 'alpha_fc' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_soil_storage_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'soil_storage' not found in config file\n"); + printf("Config param 'soil_storage' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_K_nash_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'K_nash' not found in config file\n"); + printf("Config param 'K_nash' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_K_lf_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'K_lf' not found in config file\n"); + printf("Config param 'K_lf' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_gw_max_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'max_gw_storage' not found in config file\n"); + printf("Config param 'max_gw_storage' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_gw_storage_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'gw_storage' not found in config file\n"); + printf("Config param 'gw_storage' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_num_timesteps_set == FALSE && strcmp(model->forcing_file, "BMI")) { #if CFE_DEBUG >= 1 - printf("Config param 'num_timesteps' not found in config file\n"); + printf("Config param 'num_timesteps' not found in config file\n"); #endif - return BMI_FAILURE; + return BMI_FAILURE; } if (is_verbosity_set == FALSE) { - printf("Config param 'verbosity' not found in config file\n"); - printf("setting verbosity to a high value\n"); - model->verbosity = 10; - return BMI_FAILURE; - } - if (is_infiltration_excess_method_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param \"surface_water_partitioning_scheme\" not found in config file\n"); -#endif - return BMI_FAILURE; - } - /* xinanjiang_dev*/ - if(model->infiltration_excess_params_struct.surface_water_partitioning_scheme == Xinanjiang){ - if (is_a_Xinanjiang_inflection_point_parameter_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'a_Xinanjiang_inflection_point_parameter' not found in config file\n"); -#endif - return BMI_FAILURE; - } - if (is_b_Xinanjiang_shape_parameter_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'b_Xinanjiang_shape_parameter' not found in config file\n"); -#endif - return BMI_FAILURE; - } - if (is_x_Xinanjiang_shape_parameter_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'x_Xinanjiang_shape_parameter' not found in config file\n"); -#endif - return BMI_FAILURE; - } - if (is_urban_decimal_fraction_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'urban_decimal_fraction' not found in config file\n"); -#endif - return BMI_FAILURE; - } - } - - /*------------------- surface runoff scheme -AJK----------------------------- */ - if(is_surface_runoff_scheme_set == FALSE) { - model->surface_runoff_scheme = GIUH; - } - - // Used for parsing strings representing arrays of values below - char *copy, *value; - - if (model->surface_runoff_scheme == GIUH) { - - // Handle GIUH ordinates, bailing if they were not provided - if (is_giuh_originates_string_val_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("GIUH ordinate string not set!\n"); -#endif + printf("Config param 'verbosity' not found in config file\n"); + printf("setting verbosity to a high value\n"); + model->verbosity = 10; return BMI_FAILURE; - } -#if CFE_DEBUG >= 1 - printf("GIUH ordinates string value found in config ('%s')\n", giuh_originates_string_val); -#endif - - model->num_giuh_ordinates = count_delimited_values(giuh_originates_string_val, ","); - + } + if (is_direct_runoff_method_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Counted number of GIUH ordinates (%d)\n", model->num_giuh_ordinates); + printf("Config param 'direct_runoff_method' not found in config file\n"); #endif - - if (model->num_giuh_ordinates < 1) return BMI_FAILURE; - - model->giuh_ordinates = malloc(sizeof(double) * model->num_giuh_ordinates); - // Work with a copy of the original pointer so that the original remains unchanged and can be freed at end - copy = giuh_originates_string_val; - // Now iterate back through and get the values - int i = 0; - while ((value = strsep(©, ",")) != NULL) - model->giuh_ordinates[i++] = strtod(value, NULL); - // Finally, free the original string memory - free(giuh_originates_string_val); - } - else if(model->surface_runoff_scheme == NASH_CASCADE) { - if (is_N_nash_surface_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'N_nash_surface' not found in config file\n"); -#endif - return BMI_FAILURE; - } - if (is_K_nash_surface_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'K_nash_surface' not found in config file\n"); -#endif - return BMI_FAILURE; - } - if (is_nsubsteps_nash_surface_set == FALSE) { +/* xinanjiang_dev*/ + if(model->direct_runoff_params_struct.surface_partitioning_scheme == Xinanjiang){ + if (is_a_Xinanjiang_inflection_point_parameter_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'nsubsteps_nash_surface' not found in config file, default value is 10.\n"); + printf("Config param 'a_Xinanjiang_inflection_point_parameter' not found in config file\n"); #endif - model->nash_surface_params.nsubsteps = 10; // default value of the number of sub-timesteps - } - if (is_nash_storage_surface_set == FALSE) { + return BMI_FAILURE; + } + if (is_b_Xinanjiang_shape_parameter_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'nash_storage_surface' not found in config file\n"); + printf("Config param 'b_Xinanjiang_shape_parameter' not found in config file\n"); #endif - return BMI_FAILURE; - } - if (is_K_infiltration_nash_surface_set == FALSE) { + return BMI_FAILURE; + } + if (is_x_Xinanjiang_shape_parameter_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'Kinf_nash_surface' not found in config file, default value is 0.05 [1/hr] \n"); + printf("Config param 'x_Xinanjiang_shape_parameter' not found in config file\n"); #endif - model->nash_surface_params.K_infiltration = 0.05; // used in the runon infiltration - } - if (is_retention_depth_nash_surface_set == FALSE) { + return BMI_FAILURE; + } + if (is_urban_decimal_fraction_set == FALSE) { #if CFE_DEBUG >= 1 - printf("Config param 'retention_depth_nash_surface' not found in config file, default value is 1.0 [mm] \n"); + printf("Config param 'urban_decimal_fraction' not found in config file\n"); #endif - model->nash_surface_params.retention_depth = 0.001; // usually in the range of 1-5 mm - } - - - // Now handle the Nash storage array properly - // First, when there are values, read how many there are, and have that override any set count value - int value_count = count_delimited_values(nash_storage_surface_string_val, ","); - - assert (value_count == model->nash_surface_params.N_nash); - - if (value_count > 2) { - model->nash_surface_params.nash_storage = malloc(sizeof(double) * value_count); - // Work with a copy of the original pointer so that the original remains unchanged and can be freed at end - copy = nash_storage_surface_string_val; - // Now iterate back through and get the values - int k = 0; - while ((value = strsep(©, ",")) != NULL) - model->nash_surface_params.nash_storage[k++] = strtod(value, NULL); - - // Make sure at the end to free this too, since it was a copy - free(nash_storage_surface_string_val); - } - else { - // If Nash storage values weren't set, initialize them to 0.0 - model->nash_surface_params.nash_storage = malloc(sizeof(double) * model->nash_surface_params.N_nash); - for (j = 0; j < model->nash_surface_params.N_nash; j++) - model->nash_surface_params.nash_storage[j] = 0.0; - } - - - // initialize default parameters + return BMI_FAILURE; + } } - /*------------------- surface runoff scheme END ----------------------------- */ - - if(model->infiltration_excess_params_struct.surface_water_partitioning_scheme == Schaake) { - model->infiltration_excess_params_struct.Schaake_adjusted_magic_constant_by_soil_type = model->NWM_soil_params.refkdt * model->NWM_soil_params.satdk / 0.000002; - + if(model->direct_runoff_params_struct.surface_partitioning_scheme == Schaake){ + model->direct_runoff_params_struct.Schaake_adjusted_magic_constant_by_soil_type = model->NWM_soil_params.refkdt * model->NWM_soil_params.satdk / 0.000002; #if CFE_DEBUG >= 1 - printf("Schaake Magic Constant calculated\n"); + printf("Schaake Magic Constant calculated\n"); #endif } - if (is_sft_coupled_set == TRUE && model->infiltration_excess_params_struct.surface_water_partitioning_scheme == Schaake) { - + if (is_sft_coupled_set == TRUE && model->direct_runoff_params_struct.surface_partitioning_scheme == Schaake) { + if(!is_ice_content_threshold_set) { #if CFE_DEBUG >= 1 printf("is_sft_coupled and Schaake scheme are set to TRUE but param 'ice_fraction_threshold' not found in config file\n"); exit(-9); #endif } - + } - + // set sft_coupled flag to false if the parameter is not provided in the config file. model->soil_reservoir.is_sft_coupled = (is_sft_coupled_set == TRUE) ? TRUE : FALSE; - + +// Used for parsing strings representing arrays of values below + char *copy, *value; /*------------------- Root zone AET development -rlm----------------------------- */ if (is_aet_rootzone_set == TRUE ) { @@ -1186,41 +1007,41 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) #endif return BMI_FAILURE; } - + #if CFE_DEBUG >=1 printf("Soil layer depths string values found in config ('%d')\n", is_soil_layer_depths_string_val_set); #endif - + model->soil_reservoir.n_soil_layers = lround(count_delimited_values(soil_layer_depths_string_val, ",")); printf("n_soil_layers set in bmi_cfe.c: %d\n", model->soil_reservoir.n_soil_layers); - + #if CFE_DEBUG >= 1 printf("Counted number of soil depths (%d)\n", model->soil_reservoir.n_soil_layers); #endif - + if (model->soil_reservoir.n_soil_layers < 1) return BMI_FAILURE; - + model->soil_reservoir.soil_layer_depths_m = malloc(sizeof(double) * (model->soil_reservoir.n_soil_layers + 1)); copy = soil_layer_depths_string_val; - + i = 1; while((value = strsep(©, ",")) != NULL) { model->soil_reservoir.soil_layer_depths_m[i++] = strtod(value, NULL); } - + free(soil_layer_depths_string_val); - + //Check that the last depth read in from the cfe config file (soil_layer_depths) matches the total depth (soil_params.depth) //from the cfe config file. if(model->NWM_soil_params.D != model->soil_reservoir.soil_layer_depths_m[model->soil_reservoir.n_soil_layers]){ printf("WARNING: soil_params.depth is not equal to the last soil layer depth in the CFE config file!\n"); return BMI_FAILURE; } - + } - // Calculate the thickness (delta) of each soil layer +// Calculate the thickness (delta) of each soil layer if (is_aet_rootzone_set == TRUE ) { model->soil_reservoir.is_aet_rootzone = TRUE; model->soil_reservoir.delta_soil_layer_depth_m = malloc(sizeof(double) * (model->soil_reservoir.n_soil_layers + 1)); @@ -1234,9 +1055,9 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) previous_depth = current_depth; } // Solve for the soil water content at field capacity via Clapp-Hornberger. See "Parameter Estimation for a Conceptual - // Functional Equivalent (CFE) Formulation of the National Water Model" equations 1-3 for detailed description. + // Functional Equivalent (CFE) Formulation of the National Water Model" equations 1-3 for detailed description. double base = (model->NWM_soil_params.alpha_fc * STANDARD_ATMOSPHERIC_PRESSURE_PASCALS)/(WATER_SPECIFIC_WEIGHT * model->NWM_soil_params.satpsi); - double exponent = -1/model->NWM_soil_params.bb; + double exponent = -1/model->NWM_soil_params.bb; model->soil_reservoir.soil_water_content_field_capacity = model->NWM_soil_params.smcmax * pow(base, exponent); } else { @@ -1245,10 +1066,37 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) } /*--------------------END OF ROOT ZONE ADJUSTED AET DEVELOPMENT -rlm ------------------------------*/ - + #if CFE_DEBUG >= 1 printf("All CFE config params present\n"); +#endif + + // Handle GIUH ordinates, bailing if they were not provided + if (is_giuh_originates_string_val_set == FALSE) { +#if CFE_DEBUG >= 1 + printf("GIUH ordinate string not set!\n"); +#endif + return BMI_FAILURE; + } +#if CFE_DEBUG >= 1 + printf("GIUH ordinates string value found in config ('%s')\n", giuh_originates_string_val); #endif + model->num_giuh_ordinates = count_delimited_values(giuh_originates_string_val, ","); +#if CFE_DEBUG >= 1 + printf("Counted number of GIUH ordinates (%d)\n", model->num_giuh_ordinates); +#endif + if (model->num_giuh_ordinates < 1) + return BMI_FAILURE; + + model->giuh_ordinates = malloc(sizeof(double) * model->num_giuh_ordinates); + // Work with copy of the string pointer to make sure the original pointer remains unchanged, so mem can be freed at end + copy = giuh_originates_string_val; + // Now iterate back through and get the values (this modifies the string, which is why we needed the full string copy above) + int i = 0; + while ((value = strsep(©, ",")) != NULL) + model->giuh_ordinates[i++] = strtod(value, NULL); + // Finally, free the original string memory + free(giuh_originates_string_val); // Now handle the Nash storage array properly if (is_nash_storage_string_val_set == TRUE) { @@ -1257,7 +1105,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) // TODO: consider adding a warning if value_count and N_nash (assuming it was read from the config and not default) disagree // Ignore the values if there are not enough, and use whatever was set, or defaults if (value_count < 2) { - + model->nash_storage = malloc(sizeof(double) * model->N_nash); for (j = 0; j < model->N_nash; j++) model->nash_storage[j] = 0.0; @@ -1265,7 +1113,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) else { model->N_nash = value_count; model->nash_storage = malloc(sizeof(double) * value_count); - // Work with a copy of the original pointer so that the original remains unchanged and can be freed at end + // Work with copy the string pointer to make sure the original remains unchanged, so it can be freed at end copy = nash_storage_string_val; // Now iterate back through and get the values int k = 0; @@ -1292,11 +1140,6 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) static int Initialize (Bmi *self, const char *file) { - // setup the logger - setup_logger(); - Logger* logger = GetInstance(); - Log(logger, "In CFE Initialize()\n", INFO); - //FIXME, we can use the input file to help imply "framework" support or "standalone" //an empty init file string indicates things will come from set_value??? //what happens when both occur, that is we have a config file and framewrok @@ -1316,7 +1159,7 @@ static int Initialize (Bmi *self, const char *file) // LKC: removed alpha_fc and S_soil - both added to the soil structure in the model //double S_soil; - + //int is_S_soil_ratio; int config_read_result = read_init_config_cfe(file, cfe_bmi_data_ptr); @@ -1325,7 +1168,7 @@ static int Initialize (Bmi *self, const char *file) // time_step_size is set to 3600 in the "new_bmi_cfe" function. cfe_bmi_data_ptr->timestep_h = cfe_bmi_data_ptr->time_step_size / 3600.0; - + // JG NOTE: this is done in init_soil_reservoir //max_soil_storage = cfe_bmi_data_ptr->NWM_soil_params.D * cfe_bmi_data_ptr->NWM_soil_params.smcmax; @@ -1336,15 +1179,16 @@ static int Initialize (Bmi *self, const char *file) ************************************************************************/ /* xinanjiang_dev - changing the name to the more suitable "infiltration excess"*/ - cfe_bmi_data_ptr->infiltration_excess_m = malloc(sizeof(double)); - *cfe_bmi_data_ptr->infiltration_excess_m = 0.0; + changing the name to the more general "direct runoff" + cfe_bmi_data_ptr->flux_Schaake_output_runoff_m = malloc(sizeof(double));*/ + cfe_bmi_data_ptr->flux_output_direct_runoff_m = malloc(sizeof(double)); + *cfe_bmi_data_ptr->flux_output_direct_runoff_m = 0.0; cfe_bmi_data_ptr->flux_Qout_m = malloc(sizeof(double)); *cfe_bmi_data_ptr->flux_Qout_m = 0.0; cfe_bmi_data_ptr->flux_from_deep_gw_to_chan_m = malloc(sizeof(double)); *cfe_bmi_data_ptr->flux_from_deep_gw_to_chan_m = 0.0; - cfe_bmi_data_ptr->flux_direct_runoff_m = malloc(sizeof(double)); - *cfe_bmi_data_ptr->flux_direct_runoff_m = 0.0; + cfe_bmi_data_ptr->flux_giuh_runoff_m = malloc(sizeof(double)); + *cfe_bmi_data_ptr->flux_giuh_runoff_m = 0.0; cfe_bmi_data_ptr->flux_lat_m = malloc(sizeof(double)); *cfe_bmi_data_ptr->flux_lat_m = 0.0; cfe_bmi_data_ptr->flux_nash_lateral_runoff_m = malloc(sizeof(double)); @@ -1371,68 +1215,68 @@ static int Initialize (Bmi *self, const char *file) else { - cfe_bmi_data_ptr->is_forcing_from_bmi = FALSE; - - // Figure out the number of lines first (also char count) - int forcing_line_count, max_forcing_line_length; - int count_result = read_file_line_counts_cfe(cfe_bmi_data_ptr->forcing_file, &forcing_line_count, &max_forcing_line_length); - if (count_result == -1) { - printf("Configured forcing file '%s' could not be opened for reading\n", cfe_bmi_data_ptr->forcing_file); - return BMI_FAILURE; - } - if (forcing_line_count == 1) { - printf("Invalid header-only forcing file '%s'\n", cfe_bmi_data_ptr->forcing_file); - return BMI_FAILURE; - } - // Infer the number of time steps: assume a header, so equal to the number of lines minus 1 - cfe_bmi_data_ptr->num_timesteps = forcing_line_count - 1; - -#if CFE_DEBUG > 0 - printf("Counts - Lines: %d | Max Line: %d | Num Time Steps: %d\n", forcing_line_count, max_forcing_line_length, - cfe_bmi_data_ptr->num_timesteps); -#endif - - // Now initialize empty arrays that depend on number of time steps - cfe_bmi_data_ptr->forcing_data_precip_kg_per_m2 = malloc(sizeof(double) * (cfe_bmi_data_ptr->num_timesteps + 1)); - cfe_bmi_data_ptr->forcing_data_time = malloc(sizeof(long) * (cfe_bmi_data_ptr->num_timesteps + 1)); - - // Now open it again to read the forcings - FILE* ffp = fopen(cfe_bmi_data_ptr->forcing_file, "r"); - // Ensure still exists - if (ffp == NULL) { - printf("Forcing file '%s' disappeared!", cfe_bmi_data_ptr->forcing_file); - return BMI_FAILURE; - } - - // Read forcing file and parse forcings - char line_str[max_forcing_line_length + 1]; - long year, month, day, hour, minute; - double dsec; - // First read the header line - if (fgets(line_str, max_forcing_line_length + 1, ffp) == NULL) - return BMI_FAILURE; - - aorc_forcing_data_cfe forcings; - - for (i = 0; i < cfe_bmi_data_ptr->num_timesteps; i++) { - if (fgets(line_str, max_forcing_line_length + 1, ffp) == NULL) // read in a line of AORC data. - return BMI_FAILURE; - parse_aorc_line_cfe(line_str, &year, &month, &day, &hour, &minute, &dsec, &forcings); -#if CFE_DEBUG > 0 - printf("Forcing data: [%s]\n", line_str); - printf("Forcing details - s_time: %ld | precip: %f\n", forcings.time, forcings.precip_kg_per_m2); -#endif - cfe_bmi_data_ptr->forcing_data_precip_kg_per_m2[i] = forcings.precip_kg_per_m2; //* ((double)cfe_bmi_data_ptr->time_step_size); - cfe_bmi_data_ptr->forcing_data_time[i] = forcings.time; - - // TODO: make sure some kind of conversion isn't needed for the rain rate data - // assumed 1000 kg/m3 density of water. This result is mm/h; - //rain_rate[i] = (double) aorc_data.precip_kg_per_m2; - } - - cfe_bmi_data_ptr->epoch_start_time = cfe_bmi_data_ptr->forcing_data_time[0]; + cfe_bmi_data_ptr->is_forcing_from_bmi = FALSE; + + // Figure out the number of lines first (also char count) + int forcing_line_count, max_forcing_line_length; + int count_result = read_file_line_counts_cfe(cfe_bmi_data_ptr->forcing_file, &forcing_line_count, &max_forcing_line_length); + if (count_result == -1) { + printf("Configured forcing file '%s' could not be opened for reading\n", cfe_bmi_data_ptr->forcing_file); + return BMI_FAILURE; + } + if (forcing_line_count == 1) { + printf("Invalid header-only forcing file '%s'\n", cfe_bmi_data_ptr->forcing_file); + return BMI_FAILURE; + } + // Infer the number of time steps: assume a header, so equal to the number of lines minus 1 + cfe_bmi_data_ptr->num_timesteps = forcing_line_count - 1; + + #if CFE_DEBUG > 0 + printf("Counts - Lines: %d | Max Line: %d | Num Time Steps: %d\n", forcing_line_count, max_forcing_line_length, + cfe_bmi_data_ptr->num_timesteps); + #endif + + // Now initialize empty arrays that depend on number of time steps + cfe_bmi_data_ptr->forcing_data_precip_kg_per_m2 = malloc(sizeof(double) * (cfe_bmi_data_ptr->num_timesteps + 1)); + cfe_bmi_data_ptr->forcing_data_time = malloc(sizeof(long) * (cfe_bmi_data_ptr->num_timesteps + 1)); + + // Now open it again to read the forcings + FILE* ffp = fopen(cfe_bmi_data_ptr->forcing_file, "r"); + // Ensure still exists + if (ffp == NULL) { + printf("Forcing file '%s' disappeared!", cfe_bmi_data_ptr->forcing_file); + return BMI_FAILURE; + } + + // Read forcing file and parse forcings + char line_str[max_forcing_line_length + 1]; + long year, month, day, hour, minute; + double dsec; + // First read the header line + if (fgets(line_str, max_forcing_line_length + 1, ffp) == NULL) + return BMI_FAILURE; + + aorc_forcing_data_cfe forcings; + + for (i = 0; i < cfe_bmi_data_ptr->num_timesteps; i++) { + if (fgets(line_str, max_forcing_line_length + 1, ffp) == NULL) // read in a line of AORC data. + return BMI_FAILURE; + parse_aorc_line_cfe(line_str, &year, &month, &day, &hour, &minute, &dsec, &forcings); + #if CFE_DEBUG > 0 + printf("Forcing data: [%s]\n", line_str); + printf("Forcing details - s_time: %ld | precip: %f\n", forcings.time, forcings.precip_kg_per_m2); + #endif + cfe_bmi_data_ptr->forcing_data_precip_kg_per_m2[i] = forcings.precip_kg_per_m2; //* ((double)cfe_bmi_data_ptr->time_step_size); + cfe_bmi_data_ptr->forcing_data_time[i] = forcings.time; + + // TODO: make sure some kind of conversion isn't needed for the rain rate data + // assumed 1000 kg/m3 density of water. This result is mm/h; + //rain_rate[i] = (double) aorc_data.precip_kg_per_m2; + } + + cfe_bmi_data_ptr->epoch_start_time = cfe_bmi_data_ptr->forcing_data_time[0]; } // end if is_forcing_from_bmi - + // divide by 1000 to convert from mm/h to m w/ 1h timestep as per t-shirt_0.99f cfe_bmi_data_ptr->timestep_rainfall_input_m = cfe_bmi_data_ptr->forcing_data_precip_kg_per_m2[0] / 1000; @@ -1471,11 +1315,10 @@ static int Initialize (Bmi *self, const char *file) cfe_bmi_data_ptr->soil_reservoir.smc_profile = malloc(sizeof(double)*cfe_bmi_data_ptr->soil_reservoir.n_soil_layers + 1); else cfe_bmi_data_ptr->soil_reservoir.smc_profile = malloc(sizeof(double)*1); - + #if CFE_DEBUG > 0 printf("At declaration of smc_profile size, soil_reservoir.n_soil_layers = %i\n", cfe_bmi_data_ptr->soil_reservoir.n_soil_layers); #endif - Log(logger, "Success in CFE BMI Initialization\n", INFO); return BMI_SUCCESS; } @@ -1494,10 +1337,10 @@ static int Update (Bmi *self) // if (current_time >= end_time) { // return BMI_FAILURE; // } - + cfe_state_struct* cfe_ptr = ((cfe_state_struct *) self->data); - // Two modes to get forcing data... 0/FALSE) read from file, 1/TRUE) pass with bmi + // Two modes to get forcing data... 0/FALSE) read from file, 1/TRUE) pass with bmi if (cfe_ptr->is_forcing_from_bmi == TRUE) // BMI sets the precipitation to the aorc structure. // divide by 1000 to convert from mm/h to m w/ 1h timestep as per t-shirt_0.99f @@ -1511,10 +1354,10 @@ static int Update (Bmi *self) cfe_ptr->timestep_rainfall_input_m *= cfe_ptr->time_step_fraction; //Accumulate volume for mass balance cfe_ptr->vol_struct.volin += cfe_ptr->timestep_rainfall_input_m; - + run_cfe(cfe_ptr); - - // Advance the model time + + // Advance the model time cfe_ptr->current_time_step += 1; return BMI_SUCCESS; @@ -1527,7 +1370,7 @@ static int Update_until (Bmi *self, double t) // "the time argument can be a non-integral multiple of time steps" cfe_state_struct* cfe_ptr = ((cfe_state_struct *) self->data); - + double dt; double now; @@ -1535,10 +1378,10 @@ static int Update_until (Bmi *self, double t) return BMI_FAILURE; if(self->get_current_time(self, &now) == BMI_FAILURE) - return BMI_FAILURE; + return BMI_FAILURE; { - + int n; double frac; const double n_steps = (t - now) / dt; @@ -1548,7 +1391,7 @@ static int Update_until (Bmi *self, double t) frac = n_steps - (int)n_steps; if (frac > 0){ printf("WARNING: CFE trying to update a fraction of a timestep\n"); - + // change timestep to remaining fraction & call update() cfe_ptr->time_step_size = frac * dt; //Record the time step fraction so `Update` can adjust input if needed @@ -1558,13 +1401,30 @@ static int Update_until (Bmi *self, double t) cfe_ptr->time_step_fraction = 1.0; cfe_ptr->time_step_size = dt; } - + } return BMI_SUCCESS; } +// cfe_state_struct* cfe_ptr = ((cfe_state_struct *) self->data); +// +// int t_int = (int) t; +// if ((t - ((double)t_int)) != 0) +// return BMI_FAILURE; +// +// for (int j = 0; j < t_int; j++){ +// +// self->update(self); +// if (cfe_ptr->verbosity > 1) +// print_cfe_flux_at_timestep(cfe_ptr); +// +// } +// return BMI_SUCCESS; +//} + + static int Finalize (Bmi *self) { // Function assumes everything that is needed is retrieved from the model before Finalize is called. @@ -1575,7 +1435,7 @@ static int Finalize (Bmi *self) free(model->forcing_data_precip_kg_per_m2); if( model->forcing_data_time != NULL ) free(model->forcing_data_time); - + if( model->giuh_ordinates != NULL ) free(model->giuh_ordinates); if( model->nash_storage != NULL ) @@ -1584,24 +1444,24 @@ static int Finalize (Bmi *self) free(model->runoff_queue_m_per_timestep); if( model->flux_Qout_m != NULL ) free(model->flux_Qout_m); - + /* xinanjiang_dev: changing name to the more general "direct runoff" if( model->flux_Schaake_output_runoff_m != NULL ) free(model->flux_Schaake_output_runoff_m);*/ - if( model->infiltration_excess_m != NULL ) - free(model->infiltration_excess_m); + if( model->flux_output_direct_runoff_m != NULL ) + free(model->flux_output_direct_runoff_m); if( model->flux_from_deep_gw_to_chan_m != NULL ) free(model->flux_from_deep_gw_to_chan_m); - if( model->flux_direct_runoff_m != NULL ) - free(model->flux_direct_runoff_m); + if( model->flux_giuh_runoff_m != NULL ) + free(model->flux_giuh_runoff_m); if( model->flux_lat_m != NULL ) free(model->flux_lat_m); if( model->flux_nash_lateral_runoff_m != NULL ) free(model->flux_nash_lateral_runoff_m); if( model->flux_perc_m != NULL ) free(model->flux_perc_m); - + free(self->data); } @@ -1652,7 +1512,7 @@ static int Get_var_grid(Bmi *self, const char *name, int *grid) } // If we get here, it means the variable name wasn't recognized grid[0] = '\0'; - + return BMI_FAILURE; } @@ -1804,6 +1664,9 @@ static int Get_var_nbytes (Bmi *self, const char *name, int * nbytes) static int Get_value_ptr (Bmi *self, const char *name, void **dest) { + + + /*********** Calibration Params Hacked ************/ if (strcmp (name, "maxsmc") == 0) { cfe_state_struct *cfe_ptr; @@ -1829,16 +1692,23 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) *dest = (void*)&cfe_ptr->NWM_soil_params.bb; return BMI_SUCCESS; } - + // + //if (strcmp (name, "multiplier") == 0) { + // cfe_state_struct *cfe_ptr; + // cfe_ptr = (cfe_state_struct *) self->data; + // *dest = (void*)&cfe_ptr->NWM_soil_params.mult; + // return BMI_SUCCESS; + //} + if (strcmp (name, "Klf") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; - // LKC: this seems to be a bug, since in init_soil_reservoir cfe_ptr->soil_reservoir.coeff_secondary = cfe_ptr->K_lf; + // LKC: this seems to be a bug, since in init_soil_reservoir cfe_ptr->soil_reservoir.coeff_secondary = cfe_ptr->K_lf; // Therefore we need to change cfe_ptr->K_lf here *dest = (void*)&cfe_ptr->K_lf; return BMI_SUCCESS; } - + if (strcmp (name, "Kn") == 0) { cfe_state_struct *cfe_ptr; @@ -1852,7 +1722,7 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) *dest = (void*)&cfe_ptr->gw_reservoir.coeff_primary; return BMI_SUCCESS; } - + if (strcmp (name, "expon") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; @@ -1865,68 +1735,71 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) *dest = (void*)&cfe_ptr->gw_reservoir.storage_max_m; return BMI_SUCCESS; } +/**********Parameter Derived from config file - root zone adjusted AET development - rlm ***********/ + if (strcmp (name, "soil__num_cells") == 0) { + cfe_state_struct *cfe_ptr; + cfe_ptr = (cfe_state_struct *) self->data; + *dest = (void*)&cfe_ptr->soil_reservoir.n_soil_layers; + return BMI_SUCCESS; + } +/**************************************************************************************************/ + if (strcmp (name, "satpsi") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; *dest = (void*)&cfe_ptr->NWM_soil_params.satpsi; return BMI_SUCCESS; - } + } if (strcmp (name, "wltsmc") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; *dest = (void*)&cfe_ptr->NWM_soil_params.wltsmc; return BMI_SUCCESS; - } + } if (strcmp (name, "alpha_fc") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; *dest = (void*)&cfe_ptr->NWM_soil_params.alpha_fc; return BMI_SUCCESS; - } + } if (strcmp (name, "refkdt") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; *dest = (void*)&cfe_ptr->NWM_soil_params.refkdt; return BMI_SUCCESS; - } - if (strcmp (name, "a_Xinanjiang_inflection_point_parameter") == 0) { + } + if (strcmp (name, "a_Xinanjiang_inflection_point_parameter") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; - *dest = (void*)&cfe_ptr->infiltration_excess_params_struct.a_Xinanjiang_inflection_point_parameter; + *dest = (void*)&cfe_ptr->direct_runoff_params_struct.a_Xinanjiang_inflection_point_parameter; return BMI_SUCCESS; - } + } - if (strcmp (name, "b_Xinanjiang_shape_parameter") == 0) { + if (strcmp (name, "b_Xinanjiang_shape_parameter") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; - *dest = (void*)&cfe_ptr->infiltration_excess_params_struct.b_Xinanjiang_shape_parameter; + *dest = (void*)&cfe_ptr->direct_runoff_params_struct.b_Xinanjiang_shape_parameter; return BMI_SUCCESS; - } + } - if (strcmp (name, "x_Xinanjiang_shape_parameter") == 0) { + if (strcmp (name, "x_Xinanjiang_shape_parameter") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; - *dest = (void*)&cfe_ptr->infiltration_excess_params_struct.x_Xinanjiang_shape_parameter; + *dest = (void*)&cfe_ptr->direct_runoff_params_struct.x_Xinanjiang_shape_parameter; return BMI_SUCCESS; - } - - if (strcmp (name, "N_nash") == 0) { + } + + if (strcmp (name, "N_nash") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; *dest = (void*)&cfe_ptr->N_nash; return BMI_SUCCESS; } - - if (strcmp (name, "K_infiltration") == 0) { - cfe_state_struct *cfe_ptr; - cfe_ptr = (cfe_state_struct *) self->data; - *dest = (void*)&cfe_ptr->nash_surface_params.K_infiltration; - return BMI_SUCCESS; - } - + + /***********************************************************/ /*********** OUTPUT ***********************************/ /***********************************************************/ @@ -1937,13 +1810,13 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) return BMI_SUCCESS; } - if (strcmp (name, "INFILTRATION_EXCESS") == 0) { - *dest = (void*) ((cfe_state_struct *)(self->data))->infiltration_excess_m; + if (strcmp (name, "DIRECT_RUNOFF") == 0) { + *dest = (void*) ((cfe_state_struct *)(self->data))->flux_output_direct_runoff_m; return BMI_SUCCESS; } - if (strcmp (name, "GIUH_RUNOFF") == 0 || strcmp (name, "DIRECT_RUNOFF") == 0) { - *dest = (void *) ((cfe_state_struct *)(self->data))->flux_direct_runoff_m; + if (strcmp (name, "GIUH_RUNOFF") == 0) { + *dest = (void *) ((cfe_state_struct *)(self->data))->flux_giuh_runoff_m; return BMI_SUCCESS; } @@ -1961,12 +1834,12 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) *dest = (void *) ((cfe_state_struct *)(self->data))->flux_perc_m; return BMI_SUCCESS; } - + if (strcmp (name, "Q_OUT") == 0) { *dest = ((cfe_state_struct *)(self->data))->flux_Qout_m; return BMI_SUCCESS; } - + if (strcmp (name, "POTENTIAL_ET") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; @@ -1980,35 +1853,35 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) *dest = (void*)&cfe_ptr-> et_struct.actual_et_m_per_timestep; return BMI_SUCCESS; } - + if (strcmp (name, "GW_STORAGE") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; *dest = (void*)&cfe_ptr-> gw_reservoir.storage_m; return BMI_SUCCESS; } - + if (strcmp (name, "SOIL_STORAGE") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; *dest = (void*)&cfe_ptr-> soil_reservoir.storage_m; return BMI_SUCCESS; } - + if (strcmp (name, "SOIL_STORAGE_CHANGE") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; *dest = (void*)&cfe_ptr->soil_reservoir.storage_change_m; return BMI_SUCCESS; } - + if (strcmp (name, "SURF_RUNOFF_SCHEME") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; - *dest = (void*)&cfe_ptr->infiltration_excess_params_struct.surface_water_partitioning_scheme; + *dest = (void*)&cfe_ptr->direct_runoff_params_struct.surface_partitioning_scheme; return BMI_SUCCESS; } - + /***********************************************************/ /*********** INPUT ***********************************/ @@ -2019,7 +1892,6 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) *dest = (void*)&cfe_ptr-> et_struct.potential_et_m_per_s; return BMI_SUCCESS; } - if (strcmp (name, "atmosphere_water__liquid_equivalent_precipitation_rate") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; @@ -2033,7 +1905,6 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) *dest = (void*)&cfe_ptr->soil_reservoir.ice_fraction_schaake; return BMI_SUCCESS; } - if (strcmp (name, "ice_fraction_xinanjiang") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; @@ -2041,21 +1912,12 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) return BMI_SUCCESS; } - /**************************************************************************************************/ - /**********Parameter Derived from config file - root zone adjusted AET development - rlm ***********/ - if (strcmp (name, "soil__num_cells") == 0) { - cfe_state_struct *cfe_ptr; - cfe_ptr = (cfe_state_struct *) self->data; - *dest = (void*)&cfe_ptr->soil_reservoir.n_soil_layers; - return BMI_SUCCESS; - } - - //--------------Root zone adjusted AET development -rlm -ajk ------- +//--------------Root zone adjusted AET development -rlm -ajk ------- if (strcmp (name, "soil_moisture_profile") == 0) { *dest = (void *) ((cfe_state_struct *)(self->data))->soil_reservoir.smc_profile; return BMI_SUCCESS; } - //------------------------------------------------------------------- +//------------------------------------------------------------------- return BMI_FAILURE; } @@ -2080,9 +1942,9 @@ static int Get_value_at_indices (Bmi *self, const char *name, void *dest, int *i return BMI_FAILURE; // For now, all variables are non-array scalar values, with only 1 item of type double - + // Thus, there is only ever one value to return (len must be 1) and it must always be from index 0 - if (len > 1 || inds[0] != 0) + if (len > 1 || inds[0] != 0) return BMI_FAILURE; void* ptr; @@ -2110,7 +1972,7 @@ static int Set_value_at_indices (Bmi *self, const char *name, int * inds, int le { if (len < 1) return BMI_FAILURE; - + // Get "adjusted_index" for variable int adjusted_index = Get_adjusted_index_for_variable(name); if (adjusted_index < 0) @@ -2126,17 +1988,17 @@ static int Set_value_at_indices (Bmi *self, const char *name, int * inds, int le // Thus, there is only ever one value to return (len must be 1) and it must always be from index 0 // ajk: modifying it to work with soil moisture column for rootzone depth based AET if (strcmp(name, "soil_moisture_profile") == 0 || strcmp(name, "soil_layer_depths_m") == 0) { //Adding soil layer depths since they will be needed for root zone adjusted AET estimations -rlm - + len = ((cfe_state_struct *)(self->data))->soil_reservoir.n_soil_layers + 1; void *ptr = NULL; //(double*) malloc (sizeof (double)* len); status = Get_value_ptr(self, name, &ptr); - + if (status == BMI_FAILURE) return BMI_FAILURE; - + memcpy(ptr, src, var_item_size * len); - + return BMI_SUCCESS; } @@ -2148,17 +2010,16 @@ static int Set_value_at_indices (Bmi *self, const char *name, int * inds, int le if (status == BMI_FAILURE) return BMI_FAILURE; memcpy(ptr, src, var_item_size * len); - + if (strcmp (name, "maxsmc") == 0 || strcmp (name, "alpha_fc") == 0 || strcmp (name, "wltsmc") == 0 || strcmp (name, "maxsmc") == 0 || strcmp (name, "b") == 0 || strcmp (name, "slope") == 0 || strcmp (name, "satpsi") == 0 || strcmp (name, "Klf") == 0 || strcmp (name, "satdk") == 0){ - + cfe_state_struct* cfe_ptr = (cfe_state_struct *) self->data; - init_soil_reservoir(cfe_ptr); - } + init_soil_reservoir(cfe_ptr); + } if (strcmp (name, "refkdt") == 0 || strcmp (name, "satdk") == 0){ cfe_state_struct* cfe_ptr = (cfe_state_struct *) self->data; - cfe_ptr->infiltration_excess_params_struct.Schaake_adjusted_magic_constant_by_soil_type = cfe_ptr->NWM_soil_params.refkdt * cfe_ptr->NWM_soil_params.satdk / 0.000002; - - } + cfe_ptr->direct_runoff_params_struct.Schaake_adjusted_magic_constant_by_soil_type = cfe_ptr->NWM_soil_params.refkdt * cfe_ptr->NWM_soil_params.satdk / 0.000002; + } if (strcmp (name, "N_nash") == 0) { cfe_state_struct* cfe_ptr = (cfe_state_struct *) self->data; @@ -2166,14 +2027,14 @@ static int Set_value_at_indices (Bmi *self, const char *name, int * inds, int le cfe_ptr->nash_storage = malloc(sizeof(double) * cfe_ptr->N_nash); if( cfe_ptr->nash_storage == NULL ) return BMI_FAILURE; for (j = 0; j < cfe_ptr->N_nash; j++) - cfe_ptr->nash_storage[j] = 0.0; - } + cfe_ptr->nash_storage[j] = 0.0; + } if (strcmp (name, "storage_max_m") == 0) { cfe_state_struct* cfe_ptr = (cfe_state_struct *) self->data; cfe_ptr->gw_reservoir.storage_m = cfe_ptr->gw_reservoir.gw_storage * cfe_ptr->gw_reservoir.storage_max_m; - } - + } + return BMI_SUCCESS; } @@ -2188,8 +2049,8 @@ static int Set_value (Bmi *self, const char *name, void *array) // Then we can just ... return Set_value_at_indices(self, name, inds, 1, array); - - + + /* This is the sample code from read the docs void * dest = NULL; int nbytes = 0; @@ -2203,7 +2064,7 @@ static int Set_value (Bmi *self, const char *name, void *array) memcpy (dest, array, nbytes); return BMI_SUCCESS; -*/ +*/ } @@ -2251,7 +2112,7 @@ static int Get_output_var_names (Bmi *self, char ** names) // *********************************************************** static int Get_state_var_count (Bmi *self, int * count) { - if (!self) { + if (!self){ return BMI_FAILURE; } @@ -2272,24 +2133,24 @@ static int Get_state_var_names (Bmi *self, char ** names) // These names can simply be internal vs. standard // names because they are not used for coupling. //--------------------------------------------------- - if (!self) { - return BMI_FAILURE; + if (!self){ + return BMI_FAILURE; } int n_state_vars = STATE_VAR_NAME_COUNT; int MAX_NAME_LEN = 512; //int MAX_NAME_LEN = BMI_MAX_VAR_NAME; - + for (int i = 0; i < n_state_vars; i++) { strncpy(names[i], var_info[i].name, MAX_NAME_LEN); - //-------------------------------- + //-------------------------------- // Option to print all the names //-------------------------------- // if (i==0) printf(" State variable names:"); // printf(" var name[%d] = %s\n", i, names[i]); } - + return BMI_SUCCESS; } @@ -2298,8 +2159,8 @@ static int Get_state_var_types (Bmi *self, char ** types) { //--------------------------------------------------- // Note: This pulls information from the var_info - // structure defined at the top, which helps to - // prevent implementation errors. + // structure defined at the top, which helps to + // prevent implementation errors. //--------------------------------------------------- // This is used for model state serialization, and // returns a string array of all state variable @@ -2307,23 +2168,23 @@ static int Get_state_var_types (Bmi *self, char ** types) // Later, bmi.get_var_type() may be extended to // get more than input & output variable types. //--------------------------------------------------- - if (!self) { - return BMI_FAILURE; + if (!self){ + return BMI_FAILURE; } - int n_state_vars = STATE_VAR_NAME_COUNT; + int n_state_vars = STATE_VAR_NAME_COUNT; int MAX_NAME_LEN = 512; //int MAX_NAME_LEN = BMI_MAX_VAR_NAME; for (int i = 0; i < n_state_vars; i++) { - strncpy(types[i], var_info[i].type, MAX_NAME_LEN); - //-------------------------------- + strncpy(types[i], var_info[i].type, MAX_NAME_LEN); + //-------------------------------- // Option to print all the types //-------------------------------- // if (i==0) printf(" State var_types:"); // printf(" var type[%d] = %s\n", i, types[i]); } - + return BMI_SUCCESS; } @@ -2332,8 +2193,8 @@ static int Get_state_var_sizes (Bmi *self, unsigned int size_list[]) { //--------------------------------------------------- // Note: This pulls information from the var_info - // structure defined at the top, which helps to - // prevent implementation errors. + // structure defined at the top, which helps to + // prevent implementation errors. //--------------------------------------------------- // This is used for model state serialization, and // returns a string array of all state variable @@ -2341,8 +2202,8 @@ static int Get_state_var_sizes (Bmi *self, unsigned int size_list[]) // Size is number of array elements (not bytes). // Just number of elements, even for n-dim arrays. //--------------------------------------------------- - if (!self) { - return BMI_FAILURE; + if (!self){ + return BMI_FAILURE; } cfe_state_struct *state; @@ -2372,7 +2233,7 @@ static int Get_state_var_sizes (Bmi *self, unsigned int size_list[]) for (int i = 0; i < n_state_vars; i++) { size_list[i] = var_info[i].size; } - + return BMI_SUCCESS; } @@ -2383,8 +2244,8 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) // Return array of pointers to state variables // in same order as defined in state struct. //---------------------------------------------- - if (!self) { - return BMI_FAILURE; + if (!self){ + return BMI_FAILURE; } cfe_state_struct *state; @@ -2400,33 +2261,33 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) //-------------------------------------------------- ptr_list[0] = &(state->timestep_rainfall_input_m); - ptr_list[1] = &(state->soil_reservoir_storage_deficit_m); + ptr_list[1] = &(state->soil_reservoir_storage_deficit_m); ptr_list[2] = &(state->infiltration_depth_m); ptr_list[3] = &(state->gw_reservoir_storage_deficit_m); ptr_list[4] = &(state->timestep_h); //-------------------------------- // Vars in soil reservoir struct //-------------------------------- - ptr_list[5] = &(state->soil_reservoir.is_exponential ); - ptr_list[6] = &(state->soil_reservoir.storage_max_m ); - ptr_list[7] = &(state->soil_reservoir.storage_m ); - ptr_list[8] = &(state->soil_reservoir.coeff_primary ); - ptr_list[9] = &(state->soil_reservoir.exponent_primary ); - ptr_list[10] = &(state->soil_reservoir.storage_threshold_primary_m ); - ptr_list[11] = &(state->soil_reservoir.storage_threshold_secondary_m ); - ptr_list[12] = &(state->soil_reservoir.coeff_secondary ); + ptr_list[5] = &(state->soil_reservoir.is_exponential ); + ptr_list[6] = &(state->soil_reservoir.storage_max_m ); + ptr_list[7] = &(state->soil_reservoir.storage_m ); + ptr_list[8] = &(state->soil_reservoir.coeff_primary ); + ptr_list[9] = &(state->soil_reservoir.exponent_primary ); + ptr_list[10] = &(state->soil_reservoir.storage_threshold_primary_m ); + ptr_list[11] = &(state->soil_reservoir.storage_threshold_secondary_m ); + ptr_list[12] = &(state->soil_reservoir.coeff_secondary ); ptr_list[13] = &(state->soil_reservoir.exponent_secondary ); ptr_list[14] = &(state->soil_reservoir.ice_fraction_schaake); ptr_list[15] = &(state->soil_reservoir.ice_fraction_xinanjiang); //------------------------------ // Vars in gw reservoir struct - //------------------------------ + //------------------------------ ptr_list[16] = &(state->gw_reservoir.is_exponential ); ptr_list[17] = &(state->gw_reservoir.storage_max_m ); ptr_list[18] = &(state->gw_reservoir.storage_m ); ptr_list[19] = &(state->gw_reservoir.coeff_primary ); ptr_list[20] = &(state->gw_reservoir.exponent_primary ); - ptr_list[21] = &(state->gw_reservoir.storage_threshold_primary_m ); + ptr_list[21] = &(state->gw_reservoir.storage_threshold_primary_m ); ptr_list[22] = &(state->gw_reservoir.storage_threshold_secondary_m ); ptr_list[23] = &(state->gw_reservoir.coeff_secondary ); ptr_list[24] = &(state->gw_reservoir.exponent_secondary ); @@ -2436,14 +2297,14 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) ptr_list[25] = &(state->NWM_soil_params.smcmax ); ptr_list[26] = &(state->NWM_soil_params.wltsmc); ptr_list[27] = &(state->NWM_soil_params.satdk); - ptr_list[28] = &(state->NWM_soil_params.satpsi); + ptr_list[28] = &(state->NWM_soil_params.satpsi); ptr_list[29] = &(state->NWM_soil_params.bb); ptr_list[30] = &(state->NWM_soil_params.slop); ptr_list[31] = &(state->NWM_soil_params.D); ptr_list[32] = &(state->NWM_soil_params.wilting_point_m); //-------------------- // Vars in et_struct - //-------------------- + //-------------------- ptr_list[33] = &(state->et_struct.potential_et_m_per_s ); ptr_list[34] = &(state->et_struct.potential_et_m_per_timestep ); ptr_list[35] = &(state->et_struct.actual_et_m_per_timestep ); @@ -2459,23 +2320,23 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) ptr_list[42] = &(state->vol_struct.volstart ); ptr_list[43] = &(state->vol_struct.volout ); ptr_list[44] = &(state->vol_struct.volin ); - ptr_list[45] = &(state->vol_struct.vol_from_gw ); - ptr_list[46] = &(state->vol_struct.vol_out_surface ); + ptr_list[45] = &(state->vol_struct.vol_from_gw ); + ptr_list[46] = &(state->vol_struct.vol_out_giuh ); ptr_list[47] = &(state->vol_struct.vol_in_nash ); ptr_list[48] = &(state->vol_struct.vol_out_nash ); ptr_list[49] = &(state->vol_struct.vol_in_gw_start ); ptr_list[50] = &(state->vol_struct.vol_soil_start ); - //----------------------------------------- + //----------------------------------------- // More top-level, static allocation vars //----------------------------------------- - ptr_list[51] = &(state->epoch_start_time ); + ptr_list[51] = &(state->epoch_start_time ); ptr_list[52] = &(state->num_timesteps ); ptr_list[53] = &(state->current_time_step ); ptr_list[54] = &(state->time_step_size ); ptr_list[55] = &(state->is_forcing_from_bmi ); ptr_list[56] = state->forcing_file; - // ####### ptr_list[55] = &(state->forcing_file ); - ptr_list[57] = &(state->infiltration_excess_params_struct.surface_water_partitioning_scheme ); + // ####### ptr_list[55] = &(state->forcing_file ); + ptr_list[57] = &(state->direct_runoff_params_struct.surface_partitioning_scheme ); // ptr_list[56] = &(state->Schaake_adjusted_magic_constant_by_soil_type ); ptr_list[58] = &(state->N_nash); ptr_list[59] = &(state->K_lf); @@ -2495,7 +2356,7 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) ptr_list[70] = &(state->aorc.latitude ); ptr_list[71] = &(state->aorc.longitude ); ptr_list[72] = &(state->aorc.time ); - //------------------------------------------ + //------------------------------------------ // More top-level, dynamic allocation vars //---------------------------------------------------- // These vars ARE pointers to different-sized arrays @@ -2507,29 +2368,29 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) ptr_list[75] = state->giuh_ordinates; ptr_list[76] = state->nash_storage; ptr_list[77] = state->runoff_queue_m_per_timestep; - ptr_list[78] = state->infiltration_excess_m; - ptr_list[79] = state->flux_direct_runoff_m; + ptr_list[78] = state->flux_output_direct_runoff_m; + ptr_list[79] = state->flux_giuh_runoff_m; ptr_list[80] = state->flux_nash_lateral_runoff_m; ptr_list[81] = state->flux_from_deep_gw_to_chan_m; ptr_list[82] = state->flux_perc_m; ptr_list[83] = state->flux_lat_m; ptr_list[84] = state->flux_Qout_m; - ptr_list[85] = &(state->verbosity); + ptr_list[85] = &(state->verbosity); //--------------------------------------- - // infiltration_excess_params_struct vars + // direct_runoff_params_struct vars // xinanjiang or schaake flag [56] //--------------------------------------- - ptr_list[86] = &(state->infiltration_excess_params_struct.Schaake_adjusted_magic_constant_by_soil_type ); - ptr_list[87] = &(state->infiltration_excess_params_struct.a_Xinanjiang_inflection_point_parameter ); - ptr_list[88] = &(state->infiltration_excess_params_struct.b_Xinanjiang_shape_parameter ); - ptr_list[89] = &(state->infiltration_excess_params_struct.x_Xinanjiang_shape_parameter ); + ptr_list[86] = &(state->direct_runoff_params_struct.Schaake_adjusted_magic_constant_by_soil_type ); + ptr_list[87] = &(state->direct_runoff_params_struct.a_Xinanjiang_inflection_point_parameter ); + ptr_list[88] = &(state->direct_runoff_params_struct.b_Xinanjiang_shape_parameter ); + ptr_list[89] = &(state->direct_runoff_params_struct.x_Xinanjiang_shape_parameter ); //------------------------------------------------------------- // Root zone AET development -rlm // ------------------------------------------------------------ ptr_list[90] = &(state->soil_reservoir.soil_layer_depths_m); ptr_list[91] = &(state->soil_reservoir.max_rootzone_layer); //------------------------------------------------------------- - + return BMI_SUCCESS; } @@ -2570,7 +2431,7 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) } int n_state_vars, i; - self->get_state_var_count(self, &n_state_vars); + self->get_state_var_count(self, &n_state_vars); unsigned int sizes[ n_state_vars ]; self->get_state_var_sizes(self, sizes); unsigned int size = sizes[ index ]; @@ -2590,7 +2451,7 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) // else if (index == 6){ // for (i=0; ititle[i] = *( (char *)src + i); } } -// else if (index == 7){ +// else if (index == 7){ // for (i=0; isubcat[i] = *( (char *)src + i); } } @@ -2602,9 +2463,9 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) else if (index == 1){ state->soil_reservoir_storage_deficit_m = *(double *)src; } else if (index == 2){ - state->infiltration_depth_m = *(double *)src; } + state->infiltration_depth_m = *(double *)src; } else if (index == 3){ - state->gw_reservoir_storage_deficit_m = *(double *)src; } + state->gw_reservoir_storage_deficit_m = *(double *)src; } else if (index == 4){ state->timestep_h = *(double *)src; } //---------------------------------------------------------------- @@ -2617,151 +2478,151 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) else if (index == 7){ state->soil_reservoir.storage_m = *(double *)src; } else if (index == 8){ - state->soil_reservoir.coeff_primary = *(double *)src; } + state->soil_reservoir.coeff_primary = *(double *)src; } else if (index == 9){ state->soil_reservoir.exponent_primary = *(double *)src; } else if (index == 10){ - state->soil_reservoir.storage_threshold_primary_m = *(double *)src; } + state->soil_reservoir.storage_threshold_primary_m = *(double *)src; } else if (index == 11){ state->soil_reservoir.storage_threshold_secondary_m = *(double *)src; } else if (index == 12){ - state->soil_reservoir.coeff_secondary = *(double *)src; } + state->soil_reservoir.coeff_secondary = *(double *)src; } else if (index == 13){ state->soil_reservoir.exponent_secondary = *(double *)src; } //---------------------------------------------------------------- // gw_reservoir vars - //---------------------------------------------------------------- + //---------------------------------------------------------------- else if (index == 14){ state->gw_reservoir.is_exponential = *(int *)src; } else if (index == 15){ - state->gw_reservoir.storage_max_m = *(double *)src; } + state->gw_reservoir.storage_max_m = *(double *)src; } else if (index == 16){ - state->gw_reservoir.storage_m = *(double *)src; } + state->gw_reservoir.storage_m = *(double *)src; } else if (index == 17){ - state->gw_reservoir.coeff_primary = *(double *)src; } + state->gw_reservoir.coeff_primary = *(double *)src; } else if (index == 18){ - state->gw_reservoir.exponent_primary = *(double *)src; } + state->gw_reservoir.exponent_primary = *(double *)src; } else if (index == 19){ state->gw_reservoir.storage_threshold_primary_m = *(double *)src; } else if (index == 20){ - state->gw_reservoir.storage_threshold_secondary_m = *(double *)src; } + state->gw_reservoir.storage_threshold_secondary_m = *(double *)src; } else if (index == 21){ - state->gw_reservoir.coeff_secondary = *(double *)src; } + state->gw_reservoir.coeff_secondary = *(double *)src; } else if (index == 22){ state->gw_reservoir.exponent_secondary = *(double *)src; } - //---------------------------------------------------------------- + //---------------------------------------------------------------- // NWM_soil_params vars - //---------------------------------------------------------------- + //---------------------------------------------------------------- else if (index == 23){ - state->NWM_soil_params.smcmax = *(double *)src; } + state->NWM_soil_params.smcmax = *(double *)src; } else if (index == 24){ - state->NWM_soil_params.wltsmc = *(double *)src; } + state->NWM_soil_params.wltsmc = *(double *)src; } else if (index == 25){ - state->NWM_soil_params.satdk = *(double *)src; } + state->NWM_soil_params.satdk = *(double *)src; } else if (index == 26){ - state->NWM_soil_params.satpsi= *(double *)src; } + state->NWM_soil_params.satpsi= *(double *)src; } else if (index == 27){ - state->NWM_soil_params.bb = *(double *)src; } + state->NWM_soil_params.bb = *(double *)src; } else if (index == 28){ - state->NWM_soil_params.slop = *(double *)src; } + state->NWM_soil_params.slop = *(double *)src; } else if (index == 29){ - state->NWM_soil_params.D = *(double *)src; } + state->NWM_soil_params.D = *(double *)src; } else if (index == 30){ state->NWM_soil_params.wilting_point_m = *(double *)src; } //---------------------------------------------------------------- // et_struct vars //---------------------------------------------------------------- else if (index == 31){ - state->et_struct.potential_et_m_per_s = *(double *)src; } + state->et_struct.potential_et_m_per_s = *(double *)src; } else if (index == 32){ - state->et_struct.potential_et_m_per_timestep = *(double *)src; } + state->et_struct.potential_et_m_per_timestep = *(double *)src; } else if (index == 33){ state->et_struct.actual_et_m_per_timestep = *(double *)src; } //---------------------------------------------------------------- // vol_struct vars //---------------------------------------------------------------- else if (index == 34){ - state->vol_struct.vol_runoff = *(double *)src; } + state->vol_struct.vol_runoff = *(double *)src; } else if (index == 35){ - state->vol_struct.vol_infilt = *(double *)src; } + state->vol_struct.vol_infilt = *(double *)src; } else if (index == 36){ state->vol_struct.vol_to_soil = *(double *)src; } else if (index == 37){ - state->vol_struct.vol_to_gw = *(double *)src; } + state->vol_struct.vol_to_gw = *(double *)src; } else if (index == 38){ - state->vol_struct.vol_soil_to_gw = *(double *)src; } + state->vol_struct.vol_soil_to_gw = *(double *)src; } else if (index == 39){ state->vol_struct.vol_soil_to_lat_flow = *(double *)src; } else if (index == 40){ - state->vol_struct.volstart = *(double *)src; } + state->vol_struct.volstart = *(double *)src; } else if (index == 41){ - state->vol_struct.volout = *(double *)src; } + state->vol_struct.volout = *(double *)src; } else if (index == 42){ state->vol_struct.volin = *(double *)src; } else if (index == 43){ - state->vol_struct.vol_from_gw = *(double *)src; } + state->vol_struct.vol_from_gw = *(double *)src; } else if (index == 44){ - state->vol_struct.vol_out_giuh = *(double *)src; } + state->vol_struct.vol_out_giuh = *(double *)src; } else if (index == 45){ state->vol_struct.vol_in_nash = *(double *)src; } else if (index == 46){ - state->vol_struct.vol_out_nash = *(double *)src; } + state->vol_struct.vol_out_nash = *(double *)src; } else if (index == 47){ - state->vol_struct.vol_in_gw_start = *(double *)src; } + state->vol_struct.vol_in_gw_start = *(double *)src; } else if (index == 48){ state->vol_struct.vol_soil_start = *(double *)src; } //---------------------------------------------------------------- // More top-level vars //---------------------------------------------------------------- else if (index == 49){ - state->epoch_start_time = *(long *)src; } + state->epoch_start_time = *(long *)src; } else if (index == 50){ - state->num_timesteps = *(int *)src; } //######## LONG? + state->num_timesteps = *(int *)src; } //######## LONG? else if (index == 51){ state->current_time_step = *(int *)src; } else if (index == 52){ - state->time_step_size = *(int *)src; } + state->time_step_size = *(int *)src; } else if (index == 53){ - state->is_forcing_from_bmi= *(int *)src; } + state->is_forcing_from_bmi= *(int *)src; } else if (index == 54){ // forcing_file is a string memcpy(state->forcing_file, src, size); } // state->forcing_file = (char *)src; } // Doesn't work - else if (index == 55){ - state->infiltration_excess_params_struct.surface_water_partitioning_scheme = *(int *)src; } + else if (index == 55){ + state->direct_runoff_params_struct.surface_partitioning_scheme = *(int *)src; } else if (index == 56){ - state->N_nash = *(int *)src; } + state->N_nash = *(int *)src; } else if (index == 57){ state->K_lf = *(double *)src; } else if (index == 58){ - state->K_nash = *(double *)src; } + state->K_nash = *(double *)src; } else if (index == 59){ - state->num_giuh_ordinates = *(int *)src; } + state->num_giuh_ordinates = *(int *)src; } //---------------------------------------------------------------- // aorc forcing vars //---------------------------------------------------------------- else if (index == 60){ - state->aorc.precip_kg_per_m2 = *(double *)src; } + state->aorc.precip_kg_per_m2 = *(double *)src; } else if (index == 61){ - state->aorc.incoming_longwave_W_per_m2 = *(double *)src; } + state->aorc.incoming_longwave_W_per_m2 = *(double *)src; } else if (index == 62){ state->aorc.incoming_shortwave_W_per_m2 = *(double *)src; } else if (index == 63){ - state->aorc.surface_pressure_Pa = *(double *)src; } + state->aorc.surface_pressure_Pa = *(double *)src; } else if (index == 64){ - state->aorc.specific_humidity_2m_kg_per_kg= *(double *)src; } + state->aorc.specific_humidity_2m_kg_per_kg= *(double *)src; } else if (index == 65){ state->aorc.air_temperature_2m_K = *(double *)src; } else if (index == 66){ - state->aorc.u_wind_speed_10m_m_per_s = *(double *)src; } + state->aorc.u_wind_speed_10m_m_per_s = *(double *)src; } else if (index == 67){ - state->aorc.v_wind_speed_10m_m_per_s = *(double *)src; } + state->aorc.v_wind_speed_10m_m_per_s = *(double *)src; } else if (index == 68){ state->aorc.latitude = *(double *)src; } else if (index == 69){ - state->aorc.longitude= *(double *)src; } + state->aorc.longitude= *(double *)src; } else if (index == 70){ - state->aorc.time = *(long *)src; } + state->aorc.time = *(long *)src; } //---------------------------------------------------------------- // More top-level dynamically-allocated vars // (pointers to scalars or arrays) @@ -2771,12 +2632,12 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) // CORRECT: *( ((double *)src) + i) ?? // INCORRECT: *( (double *)(src + i)) // INCORRECT: *( (double *)src + i) ?? - // INCORRECT: *(double *)src + i + // INCORRECT: *(double *)src + i //--------------------------------------------- // Note: state->X is a pointer to an array // We don't need to change that pointer, // just the values in the array. - //--------------------------------------------- + //--------------------------------------------- else if (index == 71){ for (i=0; iforcing_data_precip_kg_per_m2[i] = *( ((double *)src) + i); } } @@ -2788,46 +2649,46 @@ static int Get_state_var_ptrs (Bmi *self, void *ptr_list[]) state->giuh_ordinates[i] = *( ((double *)src) + i); } } else if (index == 74){ for (i=0; inash_storage[i] = *( ((double *)src) + i); } } + state->nash_storage[i] = *( ((double *)src) + i); } } else if (index == 75){ for (i=0; irunoff_queue_m_per_timestep[i] = *( ((double *)src) + i); } } + state->runoff_queue_m_per_timestep[i] = *( ((double *)src) + i); } } else if (index == 76){ for (i=0; iinfiltration_excess_m[i] = *( ((double *)src) + i); } } + state->flux_output_direct_runoff_m[i] = *( ((double *)src) + i); } } else if (index == 77){ for (i=0; iflux_direct_runoff_m[i] = *( ((double *)src) + i); } } + state->flux_giuh_runoff_m[i] = *( ((double *)src) + i); } } else if (index == 78){ for (i=0; iflux_nash_lateral_runoff_m[i] = *( ((double *)src) + i); } } + state->flux_nash_lateral_runoff_m[i] = *( ((double *)src) + i); } } else if (index == 79){ for (i=0; iflux_from_deep_gw_to_chan_m[i] = *( ((double *)src) + i); } } + state->flux_from_deep_gw_to_chan_m[i] = *( ((double *)src) + i); } } else if (index == 80){ for (i=0; iflux_perc_m[i] = *( ((double *)src) + i); } } + state->flux_perc_m[i] = *( ((double *)src) + i); } } else if (index == 81){ for (i=0; iflux_lat_m[i] = *( ((double *)src) + i); } } + state->flux_lat_m[i] = *( ((double *)src) + i); } } else if (index == 82){ for (i=0; iflux_Qout_m[i] = *( ((double *)src) + i); } } + state->flux_Qout_m[i] = *( ((double *)src) + i); } } else if (index == 83){ // verbosity is not a pointer state->verbosity = *(int *)src; } //-------------------------------------------------------------------------- - // infiltration_excess_params_struct vars (includes xinanjiang AND schaake) + // direct_runoff_params_struc vars (includes xinanjiang AND schaake) //-------------------------------------------------------------------------- - else if (index == 84){ - state->infiltration_excess_params_struct.Schaake_adjusted_magic_constant_by_soil_type = *(double *)src; } - else if (index == 85){ - state->infiltration_excess_params_struct.a_Xinanjiang_inflection_point_parameter = *(double *)src; } - else if (index == 86){ - state->infiltration_excess_params_struct.b_Xinanjiang_shape_parameter = *(double *)src; } - else if (index == 87){ - state->infiltration_excess_params_struct.x_Xinanjiang_shape_parameter = *(double *)src; } - + else if (index == 84){ + state->direct_runoff_params_struct.Schaake_adjusted_magic_constant_by_soil_type = *(double *)src; } + else if (index == 85){ + state->direct_runoff_params_struct.a_Xinanjiang_inflection_point_parameter = *(double *)src; } + else if (index == 86){ + state->direct_runoff_params_struct.b_Xinanjiang_shape_parameter = *(double *)src; } + else if (index == 87){ + state->direct_runoff_params_struct.x_Xinanjiang_shape_parameter = *(double *)src; } + return BMI_SUCCESS; }*/ @@ -3019,9 +2880,10 @@ cfe_state_struct *new_bmi_cfe(void) /* xinanjiang_dev changing the name to the more general "direct runoff" data->flux_Schaake_output_runoff_m = NULL;*/ - data->infiltration_excess_m = NULL; + data->flux_output_direct_runoff_m = NULL; + data->flux_from_deep_gw_to_chan_m = NULL; - data->flux_direct_runoff_m = NULL; + data->flux_giuh_runoff_m = NULL; data->flux_lat_m = NULL; data->flux_nash_lateral_runoff_m = NULL; data->flux_perc_m = NULL; @@ -3064,9 +2926,9 @@ Bmi* register_bmi_cfe(Bmi *model) { model->set_value = Set_value; model->set_value_at_indices = Set_value_at_indices; - model->get_grid_size = Get_grid_size; - model->get_grid_rank = Get_grid_rank; - model->get_grid_type = Get_grid_type; + model->get_grid_size = Get_grid_size; + model->get_grid_rank = Get_grid_rank; + model->get_grid_type = Get_grid_type; model->get_grid_shape = Get_grid_shape; // N/a for grid type scalar model->get_grid_spacing = Get_grid_spacing; // N/a for grid type scalar @@ -3091,40 +2953,47 @@ Bmi* register_bmi_cfe(Bmi *model) { extern void run_cfe(cfe_state_struct* cfe_ptr){ cfe( - &cfe_ptr->soil_reservoir_storage_deficit_m, // Set in cfe function - cfe_ptr->NWM_soil_params, // Set by config file - &cfe_ptr->soil_reservoir, // Set in "init_soil_reservoir" function - cfe_ptr->timestep_h, // Set in initialize - cfe_ptr->infiltration_excess_params_struct, // Set by config file, includes parameters for Schaake and/or XinanJiang*/ - cfe_ptr->timestep_rainfall_input_m, // Set by bmi (set value) or read from file. - cfe_ptr->infiltration_excess_m, - &cfe_ptr->infiltration_depth_m, // Set by Schaake partitioning scheme - cfe_ptr->flux_perc_m, // Set to zero in definition. - cfe_ptr->flux_lat_m, // Set by CFE function after soil_resevroir calc - &cfe_ptr->gw_reservoir_storage_deficit_m, // Set by CFE function after soil_resevroir calc - &cfe_ptr->gw_reservoir, // Set in initialize and from config file - cfe_ptr->flux_from_deep_gw_to_chan_m, // Set by CFE function after gw_reservoir calc - cfe_ptr->flux_direct_runoff_m, // Set in CFE by convolution_integral or Nash Cascade model - cfe_ptr->num_giuh_ordinates, // Set by config file with func. count_delimited_values - cfe_ptr->giuh_ordinates, // Set by configuration file. - cfe_ptr->runoff_queue_m_per_timestep, // Set in initialize - cfe_ptr->flux_nash_lateral_runoff_m, // Set in CFE from nash_cascade function - cfe_ptr->N_nash, // Set from config file - cfe_ptr->K_nash, // Set from config file - cfe_ptr->nash_storage, // Set from config file - &cfe_ptr->nash_surface_params, // struct containing Nash cascade model's parameters set by config file - &cfe_ptr->et_struct, // Set to zero with initalize. Set by BMI (set_value) during run - cfe_ptr->flux_Qout_m, // Set by CFE function + &cfe_ptr->soil_reservoir_storage_deficit_m, // Set in cfe function + cfe_ptr->NWM_soil_params, // Set by config file + &cfe_ptr->soil_reservoir, // Set in "init_soil_reservoir" function + cfe_ptr->timestep_h, // Set in initialize + + /* xinanjiang_dev + changing the name to the more general "direct runoff" + cfe_ptr->Schaake_adjusted_magic_constant_by_soil_type, // Set by config file*/ + cfe_ptr->direct_runoff_params_struct, // Set by config file, includes parameters for Schaake and/or XinanJiang*/ + + cfe_ptr->timestep_rainfall_input_m, // Set by bmi (set value) or read from file. + + /* xinanjiang_dev + cfe_ptr->flux_Schaake_output_runoff_m, // Set by cfe function*/ + cfe_ptr->flux_output_direct_runoff_m, + + &cfe_ptr->infiltration_depth_m, // Set by Schaake partitioning scheme + cfe_ptr->flux_perc_m, // Set to zero in definition. + cfe_ptr->flux_lat_m, // Set by CFE function after soil_resevroir calc + &cfe_ptr->gw_reservoir_storage_deficit_m, // Set by CFE function after soil_resevroir calc + &cfe_ptr->gw_reservoir, // Set in initialize and from config file + cfe_ptr->flux_from_deep_gw_to_chan_m, // Set by CFE function after gw_reservoir calc + cfe_ptr->flux_giuh_runoff_m, // Set in CFE by convolution_integral + cfe_ptr->num_giuh_ordinates, // Set by config file with func. count_delimited_values + cfe_ptr->giuh_ordinates, // Set by configuration file. + cfe_ptr->runoff_queue_m_per_timestep, // Set in initialize + cfe_ptr->flux_nash_lateral_runoff_m, // Set in CFE from nash_cascade function + cfe_ptr->N_nash, // Set from config file + cfe_ptr->K_nash, // Set from config file + cfe_ptr->nash_storage, // Set from config file + &cfe_ptr->et_struct, // Set to zero with initalize. Set by BMI (set_value) during run + cfe_ptr->flux_Qout_m, // Set by CFE function &cfe_ptr->vol_struct, - cfe_ptr->time_step_size, - cfe_ptr->surface_runoff_scheme - ); + cfe_ptr->time_step_size + ); } // Functions for setting up CFE data, i.e., initializing... /*extern void init_soil_reservoir(cfe_state_struct* cfe_ptr, double alpha_fc, double max_storage, double storage, int is_storage_ratios) -*/ +{*/ extern void init_soil_reservoir(cfe_state_struct* cfe_ptr) { // calculate the activation storage for the secondary lateral flow outlet in the soil nonlinear reservoir. @@ -3135,13 +3004,13 @@ extern void init_soil_reservoir(cfe_state_struct* cfe_ptr) // this equation calculates the amount of water stored in the 2 m thick soil column when the water content // at the center of the bottom discretization (trigger_z_m, below 0.5) is at field capacity // Initial parentheses calc equation 3 from param equiv. doc - //#define STANDARD_ATMOSPHERIC_PRESSURE_PASCALS 101325 +//#define STANDARD_ATMOSPHERIC_PRESSURE_PASCALS 101325 // This may need to be changed as follows later, but for now, use the constant value //double Omega = (alpha_fc * cfe->forcing_data_surface_pressure_Pa[0] / WATER_SPECIFIC_WEIGHT) - 0.5; double Omega = ( cfe_ptr->NWM_soil_params.alpha_fc * STANDARD_ATMOSPHERIC_PRESSURE_PASCALS / WATER_SPECIFIC_WEIGHT) - 0.5; - double lower_lim = pow(Omega, (1.0 - 1.0 / cfe_ptr->NWM_soil_params.bb)) / + double lower_lim = pow(Omega, (1.0 - 1.0 / cfe_ptr->NWM_soil_params.bb)) / (1.0 - 1.0 / cfe_ptr->NWM_soil_params.bb); - double upper_lim = pow(Omega + cfe_ptr->NWM_soil_params.D, + double upper_lim = pow(Omega + cfe_ptr->NWM_soil_params.D, (1.0 - 1.0 / cfe_ptr->NWM_soil_params.bb)) / (1.0 - 1.0 / cfe_ptr->NWM_soil_params.bb); @@ -3197,13 +3066,13 @@ extern void init_soil_reservoir(cfe_state_struct* cfe_ptr) } }*/ -extern void initialize_volume_trackers(cfe_state_struct* cfe_ptr) { - cfe_ptr->vol_struct.volin = 0; - cfe_ptr->vol_struct.vol_runoff = 0; - cfe_ptr->vol_struct.vol_infilt = 0; - cfe_ptr->vol_struct.vol_to_soil = 0; - cfe_ptr->vol_struct.vol_to_gw = 0; - cfe_ptr->vol_struct.vol_soil_to_gw = 0; +extern void initialize_volume_trackers(cfe_state_struct* cfe_ptr){ + cfe_ptr->vol_struct.volin = 0; + cfe_ptr->vol_struct.vol_runoff = 0; + cfe_ptr->vol_struct.vol_infilt = 0; + cfe_ptr->vol_struct.vol_to_soil = 0; + cfe_ptr->vol_struct.vol_to_gw = 0; + cfe_ptr->vol_struct.vol_soil_to_gw = 0; cfe_ptr->vol_struct.vol_soil_to_lat_flow = 0; cfe_ptr->vol_struct.volout = 0; cfe_ptr->vol_struct.vol_from_gw = 0; @@ -3217,7 +3086,7 @@ extern void initialize_volume_trackers(cfe_state_struct* cfe_ptr) { cfe_ptr->vol_struct.vol_soil_start = cfe_ptr->soil_reservoir.storage_m; cfe_ptr->vol_struct.vol_et_from_soil = 0; cfe_ptr->vol_struct.vol_et_from_rain = 0; - cfe_ptr->vol_struct.vol_et_to_atm = 0; + cfe_ptr->vol_struct.vol_et_to_atm = 0; } /**************************************************************************/ @@ -3227,51 +3096,42 @@ extern void initialize_volume_trackers(cfe_state_struct* cfe_ptr) { /**************************************************************************/ /**************************************************************************/ /**************************************************************************/ -extern void print_cfe_flux_header() { - printf("# , hourly , infiltration excess, direct ,lateral, base, total, storage, ice fraction, ice fraction \n"); - printf("Time [h],rainfall [mm], excess [mm],runoff [mm],flow [mm],flow [mm],discharge [mm],storage [mm],schaake [mm],xinan [-]\n"); +extern void print_cfe_flux_header(){ + printf("# , hourly , direct, giuh ,lateral, base, total, storage, ice fraction, ice fraction \n"); + printf("Time [h],rainfall [mm],runoff [mm],runoff [mm],flow [mm],flow [mm],discharge [mm],storage [mm],schaake [mm],xinan [-]\n"); } -extern void print_cfe_flux_at_timestep(cfe_state_struct* cfe_ptr) { +extern void print_cfe_flux_at_timestep(cfe_state_struct* cfe_ptr){ printf("%d, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf\n", - cfe_ptr->current_time_step, - cfe_ptr->timestep_rainfall_input_m*1000.0, - *cfe_ptr->infiltration_excess_m*1000.0, - *cfe_ptr->flux_direct_runoff_m*1000.0, - *cfe_ptr->flux_nash_lateral_runoff_m*1000.0, - *cfe_ptr->flux_from_deep_gw_to_chan_m*1000.0, - *cfe_ptr->flux_Qout_m*1000.0, - cfe_ptr->soil_reservoir.storage_m*1000.0, - cfe_ptr->soil_reservoir.ice_fraction_schaake*1000.0, - cfe_ptr->soil_reservoir.ice_fraction_xinanjiang); + cfe_ptr->current_time_step, + cfe_ptr->timestep_rainfall_input_m*1000.0, + *cfe_ptr->flux_output_direct_runoff_m*1000.0, + *cfe_ptr->flux_giuh_runoff_m*1000.0, + *cfe_ptr->flux_nash_lateral_runoff_m*1000.0, + *cfe_ptr->flux_from_deep_gw_to_chan_m*1000.0, + *cfe_ptr->flux_Qout_m*1000.0, + cfe_ptr->soil_reservoir.storage_m*1000.0, + cfe_ptr->soil_reservoir.ice_fraction_schaake*1000.0, + cfe_ptr->soil_reservoir.ice_fraction_xinanjiang); } extern void mass_balance_check(cfe_state_struct* cfe_ptr){ //----------------------------------------------------------- // PERFORM MASS BALANCE CHECKS AND WRITE RESULTS TO stderr. //----------------------------------------------------------- - - double volend = cfe_ptr->soil_reservoir.storage_m+cfe_ptr->gw_reservoir.storage_m; + + double volend= cfe_ptr->soil_reservoir.storage_m+cfe_ptr->gw_reservoir.storage_m; double vol_in_gw_end = cfe_ptr->gw_reservoir.storage_m; - double vol_surface_end = 0.0; // volume in the giuh or nash array at the end (on the surface) + double vol_end_giuh = 0.0; double vol_in_nash_end = 0.0; double vol_soil_end; - + // the GIUH queue might have water in it at the end of the simulation, so sum it up. - if (cfe_ptr->surface_runoff_scheme == GIUH) { - for(i=0;inum_giuh_ordinates;i++) - vol_surface_end += cfe_ptr->runoff_queue_m_per_timestep[i]; - } - else if (cfe_ptr->surface_runoff_scheme == NASH_CASCADE) { - for(i=0;inash_surface_params.N_nash;i++) - vol_surface_end += cfe_ptr->nash_surface_params.nash_storage[i]; - } - - cfe_ptr->vol_struct.vol_end_surface = vol_surface_end; // update the vol in the mass balance struct - + for(i=0;inum_giuh_ordinates;i++) vol_end_giuh+=cfe_ptr->runoff_queue_m_per_timestep[i]; + for(i=0;iN_nash;i++) vol_in_nash_end+=cfe_ptr->nash_storage[i]; - + vol_soil_end=cfe_ptr->soil_reservoir.storage_m; - + double global_residual; /* xinanjiang_dev @@ -3282,81 +3142,89 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ double soil_residual; double nash_residual; double gw_residual; - - global_residual = cfe_ptr->vol_struct.volstart + cfe_ptr->vol_struct.volin - - cfe_ptr->vol_struct.volout - volend - cfe_ptr->vol_struct.vol_end_surface; - printf("========================= Simulation Summary ========================= \n"); - printf("********************* GLOBAL MASS BALANCE ********************* \n"); - printf(" Volume initial = %8.4lf m\n",cfe_ptr->vol_struct.volstart); - printf(" Volume input = %8.4lf m\n",cfe_ptr->vol_struct.volin); - printf(" Volume output = %8.4lf m\n",cfe_ptr->vol_struct.volout); - printf(" Final volume = %8.4lf m\n",volend); - printf(" Global residual = %6.4e m\n",global_residual); - if(cfe_ptr->vol_struct.volin>0.0) - printf(" Global percent error = %6.4e percent of inputs\n",global_residual/cfe_ptr->vol_struct.volin*100.0); - else - printf(" Global pct. err: %6.4e percent of initial\n",global_residual/cfe_ptr->vol_struct.volstart*100.0); - - if(!is_fabs_less_than_epsilon(global_residual,1.0e-12)) - printf("WARNING: GLOBAL MASS BALANCE CHECK FAILED\n"); - - direct_residual = cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_infilt - - cfe_ptr->vol_struct.vol_et_from_rain; - printf("********************* DIRECT RUNOFF MASS BALANCE ***************\n"); - printf(" Surface runoff = %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); - printf(" Infiltration = %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); - printf(" Vol_et_from_rain = %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_rain); - printf(" Direct residual = %6.4e m\n",direct_residual); // should equal 0.0 + global_residual = cfe_ptr->vol_struct.volstart + cfe_ptr->vol_struct.volin - + cfe_ptr->vol_struct.volout - volend - vol_end_giuh; + printf("GLOBAL MASS BALANCE\n"); + printf(" initial volume: %8.4lf m\n",cfe_ptr->vol_struct.volstart); + printf(" volume input: %8.4lf m\n",cfe_ptr->vol_struct.volin); + printf(" volume output: %8.4lf m\n",cfe_ptr->vol_struct.volout); + printf(" final volume: %8.4lf m\n",volend); + printf(" residual: %6.4e m\n",global_residual); + if(cfe_ptr->vol_struct.volin>0.0) printf("global pct. err: %6.4e percent of inputs\n",global_residual/cfe_ptr->vol_struct.volin*100.0); + else printf("global pct. err: %6.4e percent of initial\n",global_residual/cfe_ptr->vol_struct.volstart*100.0); + if(!is_fabs_less_than_epsilon(global_residual,1.0e-12)) + printf("WARNING: GLOBAL MASS BALANCE CHECK FAILED\n"); + + /* xinanjiang_dev + schaake_residual = cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.vol_sch_runoff - cfe_ptr->vol_struct.vol_sch_infilt; + printf(" SCHAAKE MASS BALANCE\n"); + printf(" surface runoff: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_runoff); + printf(" infiltration: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_infilt); + printf("schaake residual: %6.4e m\n",schaake_residual); // should equal 0.0 + if(!is_fabs_less_than_epsilon(schaake_residual,1.0e-12)) + printf("WARNING: SCHAAKE PARTITIONING MASS BALANCE CHECK FAILED\n");*/ + direct_residual = cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_infilt-cfe_ptr->vol_struct.vol_et_from_rain; + printf(" DIRECT RUNOFF MASS BALANCE\n"); + printf(" surface runoff: %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); + printf(" infiltration: %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); + printf(" vol_et_from_rain: %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_rain); + printf("direct residual: %6.4e m\n",direct_residual); // should equal 0.0 if(!is_fabs_less_than_epsilon(direct_residual,1.0e-12)) - printf("WARNING: DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILED\n"); - - giuh_residual = cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_out_surface - cfe_ptr->vol_struct.vol_end_surface - - cfe_ptr->vol_struct.vol_runon_infilt; - printf("********************* SURFACE MASS BALANCE *********************\n"); - printf(" Volume into surface = %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); - fprintf(stderr," Volume out surface = %8.4lf m\n",cfe_ptr->vol_struct.vol_out_surface); - fprintf(stderr," Volume end surface = %8.4lf m\n",cfe_ptr->vol_struct.vol_end_surface); - fprintf(stderr," Runon infiltration = %8.4lf m\n",cfe_ptr->vol_struct.vol_runon_infilt); - printf(" Surface residual = %6.4e m\n",giuh_residual); // should equal zero - if(!is_fabs_less_than_epsilon(giuh_residual,1.0e-12)) - printf("WARNING: GIUH MASS BALANCE CHECK FAILED\n"); + printf("WARNING: DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILED\n"); + + /* xinanjiang_dev + giuh_residual = cfe_ptr->vol_struct.vol_out_giuh - cfe_ptr->vol_struct.vol_sch_runoff - vol_end_giuh; */ + giuh_residual = cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_out_giuh - vol_end_giuh; + printf(" GIUH MASS BALANCE\n"); + /* xinanjiang_dev + printf(" vol. into giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_runoff); */ + printf(" vol. into giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); + fprintf(stderr," vol. out giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_out_giuh); + fprintf(stderr," vol. end giuh q: %8.4lf m\n",cfe_ptr->vol_struct.vol_end_giuh); + printf(" giuh residual: %6.4e m\n",giuh_residual); // should equal zero + if(!is_fabs_less_than_epsilon(giuh_residual,1.0e-12)) + printf("WARNING: GIUH MASS BALANCE CHECK FAILED\n"); - soil_residual = cfe_ptr->vol_struct.vol_soil_start + cfe_ptr->vol_struct.vol_infilt + cfe_ptr->vol_struct.vol_runon_infilt - - cfe_ptr->vol_struct.vol_soil_to_lat_flow - vol_soil_end - cfe_ptr->vol_struct.vol_to_gw - - cfe_ptr->vol_struct.vol_et_from_soil; + /* xinanjiang_dev + soil_residual=cfe_ptr->vol_struct.vol_soil_start + cfe_ptr->vol_struct.vol_sch_infilt - */ + soil_residual=cfe_ptr->vol_struct.vol_soil_start + cfe_ptr->vol_struct.vol_infilt - + cfe_ptr->vol_struct.vol_soil_to_lat_flow - vol_soil_end - cfe_ptr->vol_struct.vol_to_gw - cfe_ptr->vol_struct.vol_et_from_soil; + + printf(" SOIL WATER CONCEPTUAL RESERVOIR MASS BALANCE\n"); + printf(" init soil vol: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_start); - printf("*********** SOIL WATER CONCEPTUAL RESERVOIR MASS BALANCE *******\n"); - printf(" Initial soil vol = %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_start); - printf(" Volume into soil = %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); - printf(" Volume soil2latflow = %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_to_lat_flow); - printf(" Volume soil to GW = %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_to_gw); - printf(" Final volume soil = %8.4lf m\n",vol_soil_end); - printf(" Volume et from soil = %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_soil); - printf(" Volume soil residual = %6.4e m\n",soil_residual); + /* xinanjiang_dev + printf(" vol. into soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_infilt); */ + printf(" vol. into soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); + printf("vol.soil2latflow: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_to_lat_flow); + printf(" vol. soil to gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_to_gw); + printf(" final vol. soil: %8.4lf m\n",vol_soil_end); + printf(" vol. et from soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_soil); + printf("vol. soil resid.: %6.4e m\n",soil_residual); if(!is_fabs_less_than_epsilon(soil_residual,1.0e-12)) - printf("WARNING: SOIL CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); - - nash_residual = cfe_ptr->vol_struct.vol_in_nash - cfe_ptr->vol_struct.vol_out_nash - vol_in_nash_end; - printf("********* NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE *******\n"); - printf(" Volume to Nash = %8.4lf m\n",cfe_ptr->vol_struct.vol_in_nash); - printf(" Volume from Nash = %8.4lf m\n",cfe_ptr->vol_struct.vol_out_nash); - printf(" Final vol. Nash = %8.4lf m\n",vol_in_nash_end); - printf(" Nash casc resid. = %6.4e m\n",nash_residual); + printf("WARNING: SOIL CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + + nash_residual=cfe_ptr->vol_struct.vol_in_nash - cfe_ptr->vol_struct.vol_out_nash - vol_in_nash_end; + printf(" NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE\n"); + printf(" vol. to nash: %8.4lf m\n",cfe_ptr->vol_struct.vol_in_nash); + printf(" vol. from nash: %8.4lf m\n",cfe_ptr->vol_struct.vol_out_nash); + printf(" final vol. nash: %8.4lf m\n",vol_in_nash_end); + printf("nash casc resid.: %6.4e m\n",nash_residual); if(!is_fabs_less_than_epsilon(nash_residual,1.0e-12)) - printf("WARNING: NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); - - + printf("WARNING: NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + + gw_residual = cfe_ptr->vol_struct.vol_in_gw_start + cfe_ptr->vol_struct.vol_to_gw - cfe_ptr->vol_struct.vol_from_gw - vol_in_gw_end; - printf("********** GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE *******\n"); - printf(" Initial GW storage = %8.4lf m\n",cfe_ptr->vol_struct.vol_in_gw_start); - printf(" Volume to GW = %8.4lf m\n",cfe_ptr->vol_struct.vol_to_gw); - printf(" Volume from GW = %8.4lf m\n",cfe_ptr->vol_struct.vol_from_gw); - printf(" Final GW storage = %8.4lf m\n",vol_in_gw_end); - printf(" GW residual = %6.4e m\n",gw_residual); + printf(" GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE\n"); + printf("init gw. storage: %8.4lf m\n",cfe_ptr->vol_struct.vol_in_gw_start); + printf(" vol to gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_to_gw); + printf(" vol from gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_from_gw); + printf("final gw.storage: %8.4lf m\n",vol_in_gw_end); + printf(" gw. residual: %6.4e m\n",gw_residual); if(!is_fabs_less_than_epsilon(gw_residual,1.0e-12)) - fprintf(stderr,"WARNING: GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + fprintf(stderr,"WARNING: GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); } /**************************************************************************/ @@ -3377,59 +3245,59 @@ void parse_aorc_line_cfe(char *theString,long *year,long *month, long *day,long int start,end; int wordlen; char theWord[150]; - + //len=strlen(theString); - + start=0; /* begin at the beginning of theString */ get_word_cfe(theString,&start,&end,theWord,&wordlen); *year=atol(theWord); - + get_word_cfe(theString,&start,&end,theWord,&wordlen); *month=atol(theWord); - + get_word_cfe(theString,&start,&end,theWord,&wordlen); *day=atol(theWord); - + get_word_cfe(theString,&start,&end,theWord,&wordlen); *hour=atol(theWord); - + get_word_cfe(theString,&start,&end,theWord,&wordlen); *minute=atol(theWord); - + get_word_cfe(theString,&start,&end,theWord,&wordlen); *second=(double)atof(theWord); - + get_word_cfe(theString,&start,&end,theWord,&wordlen); aorc->precip_kg_per_m2=atof(theWord); //printf("%s, %s, %lf, %lf\n", theString, theWord, (double)atof(theWord), aorc->precip_kg_per_m2); - + get_word_cfe(theString,&start,&end,theWord,&wordlen); - aorc->incoming_longwave_W_per_m2=(double)atof(theWord); - + aorc->incoming_longwave_W_per_m2=(double)atof(theWord); + get_word_cfe(theString,&start,&end,theWord,&wordlen); - aorc->incoming_shortwave_W_per_m2=(double)atof(theWord); - + aorc->incoming_shortwave_W_per_m2=(double)atof(theWord); + get_word_cfe(theString,&start,&end,theWord,&wordlen); - aorc->surface_pressure_Pa=(double)atof(theWord); - + aorc->surface_pressure_Pa=(double)atof(theWord); + get_word_cfe(theString,&start,&end,theWord,&wordlen); aorc->specific_humidity_2m_kg_per_kg=(double)atof(theWord); - + get_word_cfe(theString,&start,&end,theWord,&wordlen); - aorc->air_temperature_2m_K=(double)atof(theWord); - + aorc->air_temperature_2m_K=(double)atof(theWord); + get_word_cfe(theString,&start,&end,theWord,&wordlen); - aorc->u_wind_speed_10m_m_per_s=(double)atof(theWord); - + aorc->u_wind_speed_10m_m_per_s=(double)atof(theWord); + get_word_cfe(theString,&start,&end,theWord,&wordlen); - aorc->v_wind_speed_10m_m_per_s=(double)atof(theWord); + aorc->v_wind_speed_10m_m_per_s=(double)atof(theWord); - //---------------------------------------------- + //---------------------------------------------- // Is this needed? It has not been set. (SDP) //---------------------------------------------- aorc->time = -9999.0; //###################################################### - + return; } @@ -3568,12 +3436,13 @@ void i_alloc_cfe(int **var, int size) { */ -double greg_2_jul_cfe(long year, - long mon, - long day, - long h, - long mi, - double se) { +double greg_2_jul_cfe( + long year, + long mon, + long day, + long h, + long mi, + double se) { long m = mon, d = day, y = year; long c, ya, j; double seconds = h * 3600.0 + mi * 60 + se; @@ -3606,7 +3475,7 @@ double greg_2_jul_cfe(long year, ** Copied from Algorithm 199 in Collected algorithms of the CACM ** Author: Robert G. Tantzen, Translator: Nat Howard ** -** Modified by FLO 4/99 to account for nagging round off error +** Modified by FLO 4/99 to account for nagging round off error ** */ void calc_date_cfe(double jd, long *y, long *m, long *d, long *h, long *mi, @@ -3658,3 +3527,4 @@ int dayofweek(double j) { j += 0.5; return (int) (j + 1) % 7; } + diff --git a/src/cfe.c b/src/cfe.c index 7f83551a..80403929 100644 --- a/src/cfe.c +++ b/src/cfe.c @@ -11,20 +11,26 @@ extern void cfe( struct NWM_soil_parameters NWM_soil_params_struct, struct conceptual_reservoir *soil_reservoir_struct, double timestep_h, - /* xinanjiang_dev: since we are doing the option for Schaake and XinJiang, + + /* xinanjiang_dev: since we are doing the option for Schaake and XinJiang, instead of passing in the constants pass in a structure with the constants for both subroutines. //double Schaake_adjusted_magic_constant_by_soil_type,*/ - struct infiltration_excess_parameters_structure infiltration_excess_params_struct, + struct direct_runoff_parameters_structure direct_runoff_params_struct, + double timestep_rainfall_input_m, - double *infiltration_excess_m_ptr, + + /* xinanjiang_dev: rename to the general "direct runoff" + double *Schaake_output_runoff_m_ptr,*/ + double *flux_output_direct_runoff_m, + double *infiltration_depth_m_ptr, double *flux_perc_m_ptr, double *flux_lat_m_ptr, double *gw_reservoir_storage_deficit_m_ptr, struct conceptual_reservoir *gw_reservoir_struct, double *flux_from_deep_gw_to_chan_m_ptr, - double *direct_runoff_m_ptr, + double *giuh_runoff_m_ptr, int num_giuh_ordinates, double *giuh_ordinates_arr, double *runoff_queue_m_per_timestep_arr, @@ -32,33 +38,33 @@ extern void cfe( int num_lateral_flow_nash_reservoirs, double K_nash, double *nash_storage_arr, - struct nash_cascade_parameters *nash_surface_params, struct evapotranspiration_structure *evap_struct, double *Qout_m_ptr, struct massbal *massbal_struct, - double time_step_size, - int surface_runoff_scheme + double time_step_size ){ // ####################################################################### // CFE STATE SPACE FUNCTION // ####################################################################### // #### COPY THE MODEL FUNCTION STATE SPACE TO LOCAL VARIABLES #### // #### Reason: so we don't have to re-write domain science code to de-reference a whole bunch of pointers -// #### Note: all of thes variables are storages in [m] or fluxes in [m/timestep] +// #### Note: all of thes variables are storages in [m] or fluxes in [m/timestep] double soil_reservoir_storage_deficit_m = *soil_reservoir_storage_deficit_m_ptr; // storage [m] - - double infiltration_excess_m = *infiltration_excess_m_ptr; // Schaake/Xinanjiang partitioned excess water this timestep [m]*/ + + /* xinanjiang_dev: rename to the general "direct runoff" + double Schaake_output_runoff_m = *Schaake_output_runoff_m_ptr; // Schaake partitioned runoff this timestep [m]*/ + double direct_output_runoff_m = *flux_output_direct_runoff_m; // Schaake partitioned runoff this timestep [m]*/ double infiltration_depth_m = *infiltration_depth_m_ptr; // Schaake partitioned infiltration this timestep [m] double flux_perc_m = *flux_perc_m_ptr; // water moved from soil reservoir to gw reservoir this timestep [m] double flux_lat_m = *flux_lat_m_ptr; // water moved from soil reservoir to lateral flow Nash cascad this timestep [m] double gw_reservoir_storage_deficit_m = *gw_reservoir_storage_deficit_m_ptr; // deficit in gw reservoir storage [m] double flux_from_deep_gw_to_chan_m = *flux_from_deep_gw_to_chan_m_ptr; // water moved from gw reservoir to catchment outlet nexus this timestep [m] - double direct_runoff_m = *direct_runoff_m_ptr; // water leaving GIUH or NASH cascade reservoir to outlet this timestep [m] + double giuh_runoff_m = *giuh_runoff_m_ptr; // water leaving GIUH to outlet this timestep [m] double nash_lateral_runoff_m = *nash_lateral_runoff_m_ptr; // water leaving lateral subsurface flow Nash cascade this timestep [m] - double Qout_m = *Qout_m_ptr; // the total runoff this timestep (Surface+Nash+GW) [m] + double Qout_m = *Qout_m_ptr; // the total runoff this timestep (GIUH+Nash+GW) [m] // LOCAL VARIABLES, the values of which are not important to describe the model state. They are like notes on scrap paper. - + double diff=0.0; double primary_flux=0.0; // pointers to these variables passed to conceptual nonlinear reservoir which has two outlets, primary & secondary double secondary_flux=0.0; // pointers to these variables passed to conceptual nonlinear reservoir which has two outlets, primary & secondary @@ -68,59 +74,59 @@ extern void cfe( // store current soil storage_m in a temp. variable double storage_temp_m = soil_reservoir_struct->storage_m; - + //################################################## // partition rainfall using Schaake function //################################################## evap_struct->potential_et_m_per_timestep = evap_struct->potential_et_m_per_s * time_step_size; evap_struct->reduced_potential_et_m_per_timestep = evap_struct->potential_et_m_per_s * time_step_size; - + evap_struct->actual_et_from_rain_m_per_timestep = 0; if(timestep_rainfall_input_m > 0) {et_from_rainfall(×tep_rainfall_input_m,evap_struct);} - + massbal_struct->vol_et_from_rain = massbal_struct->vol_et_from_rain + evap_struct->actual_et_from_rain_m_per_timestep; massbal_struct->vol_et_to_atm = massbal_struct->vol_et_to_atm + evap_struct->actual_et_from_rain_m_per_timestep; massbal_struct->volout=massbal_struct->volout+evap_struct->actual_et_from_rain_m_per_timestep; - // LKC: Change this. Now evaporation happens before runoff calculation. This was creating issues since it modifies storage_m and not storage_deficit + // LKC: Change this. Now evaporation happens before runoff calculation. This was creating issues since it modifies storage_m and not storage_deficit evap_struct->actual_et_from_soil_m_per_timestep = 0; - if(soil_reservoir_struct->storage_m > NWM_soil_params_struct.wilting_point_m) + if(soil_reservoir_struct->storage_m > NWM_soil_params_struct.wilting_point_m) {et_from_soil(soil_reservoir_struct, evap_struct, &NWM_soil_params_struct);} massbal_struct->vol_et_from_soil = massbal_struct->vol_et_from_soil + evap_struct->actual_et_from_soil_m_per_timestep; massbal_struct->vol_et_to_atm = massbal_struct->vol_et_to_atm + evap_struct->actual_et_from_soil_m_per_timestep; - massbal_struct->volout=massbal_struct->volout+evap_struct->actual_et_from_soil_m_per_timestep; - + massbal_struct->volout=massbal_struct->volout+evap_struct->actual_et_from_soil_m_per_timestep; + evap_struct->actual_et_m_per_timestep=evap_struct->actual_et_from_rain_m_per_timestep+evap_struct->actual_et_from_soil_m_per_timestep; // LKC: This needs to be calcualted here after et_from_soil since soil_reservoir_struct->storage_m changes soil_reservoir_storage_deficit_m=(NWM_soil_params_struct.smcmax*NWM_soil_params_struct.D-soil_reservoir_struct->storage_m); - + // NEW FLO - if(0.0 < timestep_rainfall_input_m) + if(0.0 < timestep_rainfall_input_m) { - if (infiltration_excess_params_struct.surface_water_partitioning_scheme == Schaake) + if (direct_runoff_params_struct.surface_partitioning_scheme == Schaake) { // to ensure that ice_fraction_schaake is set to 0.0 for uncoupled SFT if(!soil_reservoir_struct->is_sft_coupled) soil_reservoir_struct->ice_fraction_schaake = 0.0; - + Schaake_partitioning_scheme(timestep_h, soil_reservoir_struct->storage_threshold_primary_m, - infiltration_excess_params_struct.Schaake_adjusted_magic_constant_by_soil_type, + direct_runoff_params_struct.Schaake_adjusted_magic_constant_by_soil_type, soil_reservoir_storage_deficit_m, timestep_rainfall_input_m, NWM_soil_params_struct.smcmax, - NWM_soil_params_struct.D, &infiltration_excess_m, &infiltration_depth_m, - soil_reservoir_struct->ice_fraction_schaake, infiltration_excess_params_struct.ice_content_threshold); + NWM_soil_params_struct.D, &direct_output_runoff_m, &infiltration_depth_m, + soil_reservoir_struct->ice_fraction_schaake, direct_runoff_params_struct.ice_content_threshold); } - else if (infiltration_excess_params_struct.surface_water_partitioning_scheme == Xinanjiang) + else if (direct_runoff_params_struct.surface_partitioning_scheme == Xinanjiang) { // to ensure that ice_fraction_xinan is set to 0.0 for uncoupled SFT if(!soil_reservoir_struct->is_sft_coupled) soil_reservoir_struct->ice_fraction_xinanjiang = 0.0; - + Xinanjiang_partitioning_scheme(timestep_rainfall_input_m, soil_reservoir_struct->storage_threshold_primary_m, soil_reservoir_struct->storage_max_m, soil_reservoir_struct->storage_m, - &infiltration_excess_params_struct, &infiltration_excess_m, &infiltration_depth_m, + &direct_runoff_params_struct, &direct_output_runoff_m, &infiltration_depth_m, soil_reservoir_struct->ice_fraction_xinanjiang); } else @@ -132,7 +138,7 @@ extern void cfe( } else // NEW FLO no need to call Schaake or Xinanjiang if it's not raining. { - infiltration_excess_m = 0.0; + direct_output_runoff_m = 0.0; infiltration_depth_m = 0.0; } @@ -141,73 +147,73 @@ extern void cfe( //-------------------------------------------------------------------------------------------------- if(soil_reservoir_storage_deficit_mstorage_m=soil_reservoir_struct->storage_max_m; // LKC: should the deficit of soil be set to zero here - if the condition is reached, soil is full soil_reservoir_storage_deficit_m = 0; } - - - + + + #ifdef DEBUG /* xinanjiang_dev printf("After Schaake function: rain:%8.5lf mm runoff:%8.5lf mm infiltration:%8.5lf mm residual:%e m\n", timestep_rainfall_input_m,Schaake_output_runoff_m*1000.0,infiltration_depth_m*1000.0, timestep_rainfall_input_m-Schaake_output_runoff_m-infiltration_depth_m); */ printf("After direct runoff function: rain:%8.5lf mm runoff:%8.5lf mm infiltration:%8.5lf mm residual:%e m\n", - timestep_rainfall_input_m,infiltration_excess_m*1000.0,infiltration_depth_m*1000.0, - timestep_rainfall_input_m-infiltration_excess_m-infiltration_depth_m); -#endif + timestep_rainfall_input_m,direct_output_runoff_m*1000.0,infiltration_depth_m*1000.0, + timestep_rainfall_input_m-direct_output_runoff_m-infiltration_depth_m); +#endif - massbal_struct->vol_runoff += infiltration_excess_m; // edit FLO + massbal_struct->vol_runoff += direct_output_runoff_m; // edit FLO massbal_struct->vol_infilt += infiltration_depth_m; // edit FLO - + // put infiltration flux into soil conceptual reservoir. If not enough room // limit amount transferred to deficit //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - + if(flux_perc_m>soil_reservoir_storage_deficit_m) { diff=flux_perc_m-soil_reservoir_storage_deficit_m; // the amount that there is not capacity ffor - infiltration_depth_m=soil_reservoir_storage_deficit_m; + infiltration_depth_m=soil_reservoir_storage_deficit_m; massbal_struct->vol_runoff+=diff; // send excess water back to GIUH runoff edit FLO massbal_struct->vol_infilt-=diff; // correct overprediction of infilt. edit FLO - infiltration_excess_m+=diff; // bug found by Nels. This was missing and fixes it. - + direct_output_runoff_m+=diff; // bug found by Nels. This was missing and fixes it. // LKC: again here soil_reservoir_storage_deficit_m = 0; } - massbal_struct->vol_to_soil += infiltration_depth_m; + massbal_struct->vol_to_soil += infiltration_depth_m; soil_reservoir_struct->storage_m += infiltration_depth_m; // put the infiltrated water in the soil. - + // calculate fluxes from the soil storage into the deep groundwater (percolation) and to lateral subsurface flow //-------------------------------------------------------------------------------------------------------------- - + conceptual_reservoir_flux_calc(soil_reservoir_struct,&percolation_flux,&lateral_flux); flux_perc_m=percolation_flux; // m/h <<<<<<<<<<< flux of percolation from soil to g.w. reservoir >>>>>>>>> - + flux_lat_m=lateral_flux; // m/h <<<<<<<<<<< flux into the lateral flow Nash cascade >>>>>>>> - + // calculate flux of base flow from deep groundwater reservoir to channel //-------------------------------------------------------------------------- //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gw_reservoir_storage_deficit_m= gw_reservoir_struct->storage_max_m-gw_reservoir_struct->storage_m; // limit amount transferred to deficit iff there is insuffienct avail. storage - if(flux_perc_m>gw_reservoir_storage_deficit_m) { + if(flux_perc_m>gw_reservoir_storage_deficit_m) + { diff=flux_perc_m-gw_reservoir_storage_deficit_m; flux_perc_m=gw_reservoir_storage_deficit_m; massbal_struct->vol_runoff+=diff; // send excess water back to GIUH runoff massbal_struct->vol_infilt-=diff; // correct overprediction of infilt. - } - - + } + + massbal_struct->vol_to_gw +=flux_perc_m; massbal_struct->vol_soil_to_gw +=flux_perc_m; - + gw_reservoir_struct->storage_m += flux_perc_m; soil_reservoir_struct->storage_m -= flux_perc_m; soil_reservoir_struct->storage_m -= flux_lat_m; @@ -216,45 +222,40 @@ extern void cfe( // get change in storage (used in the soil moisture coupler) soil_reservoir_struct->storage_change_m = soil_reservoir_struct->storage_m - storage_temp_m ; - + conceptual_reservoir_flux_calc(gw_reservoir_struct,&primary_flux,&secondary_flux); - - + + flux_from_deep_gw_to_chan_m=primary_flux; // m/h <<<<<<<<<< BASE FLOW FLUX >>>>>>>>> if(flux_from_deep_gw_to_chan_m > gw_reservoir_struct->storage_m) { - flux_from_deep_gw_to_chan_m=gw_reservoir_struct->storage_m; - // TODO: set a flag when flux larger than storage - printf("WARNING: Groundwater flux larger than storage \n"); + flux_from_deep_gw_to_chan_m=gw_reservoir_struct->storage_m; + // TODO: set a flag when flux larger than storage + printf("WARNING: Groundwater flux larger than storage \n"); } - + massbal_struct->vol_from_gw+=flux_from_deep_gw_to_chan_m; - + // in the instance of calling the gw reservoir the secondary flux should be zero- verify if(is_fabs_less_than_epsilon(secondary_flux,1.0e-09)==FALSE) printf("problem with nonzero flux point 1\n"); - + // adjust state of deep groundwater conceptual nonlinear reservoir //----------------------------------------------------------------- - + gw_reservoir_struct->storage_m -= flux_from_deep_gw_to_chan_m; - // Solve the convolution integral ffor this time step - - if (surface_runoff_scheme == GIUH) - direct_runoff_m = giuh_convolution_integral(infiltration_excess_m,num_giuh_ordinates, - giuh_ordinates_arr,runoff_queue_m_per_timestep_arr); - else if (surface_runoff_scheme == NASH_CASCADE) - direct_runoff_m = nash_cascade_surface_runoff(infiltration_excess_m, soil_reservoir_storage_deficit_m, - nash_surface_params); - soil_reservoir_struct->storage_m += nash_surface_params->runon_infiltration; // put the runon infiltrated water in the soil. - soil_reservoir_storage_deficit_m -= nash_surface_params->runon_infiltration; // update soil deficit - - massbal_struct->vol_out_surface += direct_runoff_m; - massbal_struct->volout += direct_runoff_m; - massbal_struct->volout += flux_from_deep_gw_to_chan_m; - massbal_struct->vol_runon_infilt += nash_surface_params->runon_infiltration; + // Solve the convolution integral ffor this time step + + /* xinanjiang_dev + giuh_runoff_m = convolution_integral(Schaake_output_runoff_m,num_giuh_ordinates, */ + giuh_runoff_m = giuh_convolution_integral(direct_output_runoff_m,num_giuh_ordinates, + giuh_ordinates_arr,runoff_queue_m_per_timestep_arr); + massbal_struct->vol_out_giuh+=giuh_runoff_m; + massbal_struct->volout+=giuh_runoff_m; + massbal_struct->volout+=flux_from_deep_gw_to_chan_m; + // Route lateral flow through the Nash cascade. nash_lateral_runoff_m = nash_cascade(flux_lat_m,num_lateral_flow_nash_reservoirs, K_nash,nash_storage_arr); @@ -265,17 +266,21 @@ extern void cfe( fprintf(out_debug_fptr,"%d %lf %lf %lf\n",tstep,flux_lat_m,nash_lateral_runoff_m,flux_from_deep_gw_to_chan_m); #endif - Qout_m = direct_runoff_m + nash_lateral_runoff_m + flux_from_deep_gw_to_chan_m; - - // #### COPY BACK STATE VALUES BY POINTER REFERENCE SO VISIBLE TO FRAMEWORK #### + Qout_m = giuh_runoff_m + nash_lateral_runoff_m + flux_from_deep_gw_to_chan_m; + + // #### COPY BACK STATE VALUES BY POINTER REFERENCE SO VISIBLE TO FRAMEWORK #### *soil_reservoir_storage_deficit_m_ptr = soil_reservoir_storage_deficit_m; - *infiltration_excess_m_ptr = infiltration_excess_m; + + /* xinanjiang_dev + *Schaake_output_runoff_m_ptr = Schaake_output_runoff_m; */ + *flux_output_direct_runoff_m = direct_output_runoff_m; + *infiltration_depth_m_ptr = infiltration_depth_m; *flux_perc_m_ptr = flux_perc_m; *flux_lat_m_ptr = flux_lat_m; *gw_reservoir_storage_deficit_m_ptr = gw_reservoir_storage_deficit_m; *flux_from_deep_gw_to_chan_m_ptr = flux_from_deep_gw_to_chan_m; - *direct_runoff_m_ptr = direct_runoff_m; + *giuh_runoff_m_ptr = giuh_runoff_m; *nash_lateral_runoff_m_ptr = nash_lateral_runoff_m; *Qout_m_ptr = Qout_m; @@ -286,21 +291,57 @@ extern void cfe( //#################################################################################################### + +//############################################################## +//################### NASH CASCADE ######################### +//############################################################## +extern double nash_cascade(double flux_lat_m,int num_lateral_flow_nash_reservoirs, + double K_nash,double *nash_storage_arr) +{ +//############################################################## +// Solve for the flow through the Nash cascade to delay the +// arrival of the lateral flow into the channel +//############################################################## +// local vars +int i; +double outflow_m; +static double Q[MAX_NUM_NASH_CASCADE]; + +//Loop through reservoirs +for(i = 0; i < num_lateral_flow_nash_reservoirs; i++) + { + Q[i] = K_nash*nash_storage_arr[i]; + nash_storage_arr[i] -= Q[i]; + + if (i==0) nash_storage_arr[i] += flux_lat_m; + else nash_storage_arr[i] += Q[i-1]; + } + +/* Get Qout */ +outflow_m = Q[num_lateral_flow_nash_reservoirs-1]; + +//Return the flow output +return (outflow_m); + +} + + + //############################################################## //######### SCHAAKE RUNOFF PARTITIONING SCHEME ############# //############################################################## -void Schaake_partitioning_scheme(double timestep_h, double field_capacity_m, double Schaake_adjusted_magic_constant_by_soil_type, - double column_total_soil_moisture_deficit_m, double water_input_depth_m, double smcmax, - double soil_depth, double *infiltration_excess_m, double *infiltration_depth_m, - double ice_fraction_schaake, double ice_content_threshold) +void Schaake_partitioning_scheme(double timestep_h, double field_capacity_m, double Schaake_adjusted_magic_constant_by_soil_type, + double column_total_soil_moisture_deficit_m, + double water_input_depth_m, double smcmax, double soil_depth, double *surface_runoff_depth_m, + double *infiltration_depth_m, double ice_fraction_schaake, double ice_content_threshold) { /*! =============================================================================== - This subtroutine takes water_input_depth_m and partitions it into infiltration_excess_m and - infiltration_depth_m using the scheme from Schaake et al. 1996. + This subtroutine takes water_input_depth_m and partitions it into surface_runoff_depth_m and + infiltration_depth_m using the scheme from Schaake et al. 1996. ! -------------------------------------------------------------------------------- -! ! modified by FLO April 2020 to eliminate reference to ice processes, +! ! modified by FLO April 2020 to eliminate reference to ice processes, ! ! and to de-obfuscate and use descriptive and dimensionally consistent variable names. ! -------------------------------------------------------------------------------- IMPLICIT NONE @@ -312,90 +353,95 @@ void Schaake_partitioning_scheme(double timestep_h, double field_capacity_m, dou double water_input_depth_m amount of water input to soil surface this time step [m] ! outputs - double infiltration_excess_m amount of water partitioned to surface water this time step [m] + double surface_runoff_depth_m amount of water partitioned to surface water this time step [m] --------------------------------------------------------------------------------*/ - assert (ice_fraction_schaake >=0.0); +assert (ice_fraction_schaake >=0.0); + +double timestep_d,Schaake_parenthetical_term,Ic,Px; - double timestep_d,Schaake_parenthetical_term,Ic,Px; - - if(0.0 < water_input_depth_m) { - if (0.0 > column_total_soil_moisture_deficit_m) { - *infiltration_excess_m=water_input_depth_m; - *infiltration_depth_m=0.0; +if(0.0 < water_input_depth_m) + { + if (0.0 > column_total_soil_moisture_deficit_m) + { + *surface_runoff_depth_m=water_input_depth_m; + *infiltration_depth_m=0.0; } - else { - // partition time-step total applied water as per Schaake et al. 1996. - // change from dt in [s] to dt1 in [d] because kdt has units of [d^(-1)] - timestep_d = timestep_h /24.0; // timestep_d is the time step in days. - - // calculate the parenthetical part of Eqn. 34 from Schaake et al. Note the magic constant has units of [d^(-1)] - - Schaake_parenthetical_term = (1.0 - exp ( - Schaake_adjusted_magic_constant_by_soil_type * timestep_d)); - - // From Schaake et al. Eqn. 2., using the column total moisture deficit - // BUT the way it is used here, it is the cumulative soil moisture deficit in the entire soil profile. - // "Layer" info not used in this subroutine in noah-mp, except to sum up the total soil moisture storage. - // NOTE: when column_total_soil_moisture_deficit_m becomes zero, which occurs when the soil column is saturated, - // then Ic=0, where Ic in the Schaake paper is called the "spatially averaged infiltration capacity", - // and is defined in Eqn. 12. - - Ic = column_total_soil_moisture_deficit_m * Schaake_parenthetical_term; - - Px=water_input_depth_m; // Total water input to partitioning scheme this time step [m] - - // This is eqn 24 from Schaake et al. NOTE: this is 0 in the case of a saturated soil column, when Ic=0. - // Physically happens only if soil has no-flow lower b.c. - - *infiltration_depth_m = (Px * (Ic / (Px + Ic))); + else + { + // partition time-step total applied water as per Schaake et al. 1996. + // change from dt in [s] to dt1 in [d] because kdt has units of [d^(-1)] + timestep_d = timestep_h /24.0; // timestep_d is the time step in days. + + // calculate the parenthetical part of Eqn. 34 from Schaake et al. Note the magic constant has units of [d^(-1)] + + Schaake_parenthetical_term = (1.0 - exp ( - Schaake_adjusted_magic_constant_by_soil_type * timestep_d)); + + // From Schaake et al. Eqn. 2., using the column total moisture deficit + // BUT the way it is used here, it is the cumulative soil moisture deficit in the entire soil profile. + // "Layer" info not used in this subroutine in noah-mp, except to sum up the total soil moisture storage. + // NOTE: when column_total_soil_moisture_deficit_m becomes zero, which occurs when the soil column is saturated, + // then Ic=0, where Ic in the Schaake paper is called the "spatially averaged infiltration capacity", + // and is defined in Eqn. 12. + + Ic = column_total_soil_moisture_deficit_m * Schaake_parenthetical_term; + + Px=water_input_depth_m; // Total water input to partitioning scheme this time step [m] + + // This is eqn 24 from Schaake et al. NOTE: this is 0 in the case of a saturated soil column, when Ic=0. + // Physically happens only if soil has no-flow lower b.c. + + *infiltration_depth_m = (Px * (Ic / (Px + Ic))); - if( 0.0 < (water_input_depth_m-(*infiltration_depth_m)) ) { - *infiltration_excess_m = water_input_depth_m - (*infiltration_depth_m); + if( 0.0 < (water_input_depth_m-(*infiltration_depth_m)) ) + { + *surface_runoff_depth_m = water_input_depth_m - (*infiltration_depth_m); } - else *infiltration_excess_m=0.0; - *infiltration_depth_m = water_input_depth_m - (*infiltration_excess_m); + else *surface_runoff_depth_m=0.0; + *infiltration_depth_m = water_input_depth_m - (*surface_runoff_depth_m); } } - else { - *infiltration_excess_m = 0.0; - *infiltration_depth_m = 0.0; +else + { + *surface_runoff_depth_m = 0.0; + *infiltration_depth_m = 0.0; } - // impermeable fraction due to frozen soil - double factor = 1.0; - //field_capacity_m = SMCREF in NOAH_MP - // ice_content_threshold = frzk in NOAH_MP - //double frzk = 0.15; // Ice content above which soil is impermeable - - if (ice_fraction_schaake > 1.0E-2) { - int cv_frz = 3; // should this be moved to config file as well, probably not. - double field_capacity = field_capacity_m/soil_depth; // divide by the reservior depth to get SMCREF [m/m] (unitless) - double frz_fact = smcmax/field_capacity * (0.412 / 0.468); - double frzx = ice_content_threshold * frz_fact; - - double acrt = cv_frz * frzx / ice_fraction_schaake; - double sum1 =1; - - for (int i1=1; i1 < cv_frz; i1++) { - int k = 1; - for (int i2=i1+1; i2 1.0E-2) { + int cv_frz = 3; // should this be moved to config file as well, probably not. + double field_capacity = field_capacity_m/soil_depth; // divide by the reservior depth to get SMCREF [m/m] (unitless) + double frz_fact = smcmax/field_capacity * (0.412 / 0.468); + double frzx = ice_content_threshold * frz_fact; + + double acrt = cv_frz * frzx / ice_fraction_schaake; + double sum1 =1; + + for (int i1=1; i1 < cv_frz; i1++) { + int k = 1; + for (int i2=i1+1; i2urban_decimal_fraction * 0.95) + ((1 - parms->urban_decimal_fraction) * ice_fraction_xinanjiang); - + // Calculate the impervious runoff (see eq. 309 from Knoben et al). impervious_runoff_m = impervious_fraction * water_input_depth_m; - + // Calculate total estimated pervious runoff. if ((tension_water_m/max_tension_water_m) <= (0.5 - parms->a_Xinanjiang_inflection_point_parameter)) { - pervious_runoff_m = (1 - impervious_fraction) * water_input_depth_m * + pervious_runoff_m = (1 - impervious_fraction) * water_input_depth_m * (pow((0.5 - parms->a_Xinanjiang_inflection_point_parameter), - (1.0 - parms->b_Xinanjiang_shape_parameter)) * - pow((1.0 - (tension_water_m/max_tension_water_m)), - parms->b_Xinanjiang_shape_parameter)); + (1.0 - parms->b_Xinanjiang_shape_parameter)) * + pow((1.0 - (tension_water_m/max_tension_water_m)), + parms->b_Xinanjiang_shape_parameter)); } else { - pervious_runoff_m = (1 - impervious_fraction) * water_input_depth_m * (1.0 - + pervious_runoff_m = (1 - impervious_fraction) * water_input_depth_m * (1.0 - pow((0.5 + parms->a_Xinanjiang_inflection_point_parameter), - (1.0 - parms->b_Xinanjiang_shape_parameter)) * + (1.0 - parms->b_Xinanjiang_shape_parameter)) * pow((1.0 - (tension_water_m/max_tension_water_m)), (parms->b_Xinanjiang_shape_parameter))); } - // Separate the surface water from the pervious runoff + // Separate the surface water from the pervious runoff // NOTE: If impervious runoff is added to this subroutine, impervious runoff should be added to - // the infiltration_excess_m. - *infiltration_excess_m = pervious_runoff_m * (1.0 - pow((1.0 - (free_water_m/max_free_water_m)),parms->x_Xinanjiang_shape_parameter)); + // the surface_runoff_depth_m. + *surface_runoff_depth_m = pervious_runoff_m * (1.0 - pow((1.0 - (free_water_m/max_free_water_m)),parms->x_Xinanjiang_shape_parameter)); } - // Separate the surface water from the pervious runoff - *infiltration_excess_m = pervious_runoff_m * (1.0 - pow((1.0 - + // Separate the surface water from the pervious runoff + *surface_runoff_depth_m = pervious_runoff_m * (1.0 - pow((1.0 - (free_water_m/max_free_water_m)),parms->x_Xinanjiang_shape_parameter)) + impervious_runoff_m; // The surface runoff depth is bounded by a minimum of 0 and a maximum of the water input depth. // Check that the estimated surface runoff is not less than 0.0 and if so, change the value to 0.0. - if(*infiltration_excess_m < 0.0) *infiltration_excess_m = 0.0; + if(*surface_runoff_depth_m < 0.0) *surface_runoff_depth_m = 0.0; // Check that the estimated surface runoff does not exceed the amount of water input to the soil surface. If it does, // change the surface water runoff value to the water input depth. - if(*infiltration_excess_m > water_input_depth_m) *infiltration_excess_m = water_input_depth_m; + if(*surface_runoff_depth_m > water_input_depth_m) *surface_runoff_depth_m = water_input_depth_m; // Separate the infiltration from the total water input depth to the soil surface. - *infiltration_depth_m = water_input_depth_m - *infiltration_excess_m; + *infiltration_depth_m = water_input_depth_m - *surface_runoff_depth_m; - return; +return; } @@ -528,17 +574,17 @@ void et_from_rainfall(double *timestep_rainfall_input_m, struct evapotranspirati iff it is raining, take PET from rainfall first. Wet veg. is efficient evaporator. */ - if (*timestep_rainfall_input_m >0.0) { - - if (*timestep_rainfall_input_m > et_struct->potential_et_m_per_timestep) { + if (*timestep_rainfall_input_m >0.0){ + if (*timestep_rainfall_input_m > et_struct->potential_et_m_per_timestep){ + et_struct->actual_et_from_rain_m_per_timestep = et_struct->potential_et_m_per_timestep; *timestep_rainfall_input_m -= et_struct->actual_et_from_rain_m_per_timestep; } - else { + else{ // LKC: This was incorrectly set to potential instead of actual - et_struct->actual_et_from_rain_m_per_timestep = *timestep_rainfall_input_m; + et_struct->actual_et_from_rain_m_per_timestep = *timestep_rainfall_input_m; *timestep_rainfall_input_m = 0.0; } @@ -553,15 +599,15 @@ void et_from_rainfall(double *timestep_rainfall_input_m, struct evapotranspirati void et_from_soil(struct conceptual_reservoir *soil_res, struct evapotranspiration_structure *et_struct, struct NWM_soil_parameters *soil_parms) { /* - take AET from soil moisture storage, + take AET from soil moisture storage, using Budyko type function to limit PET if wiltingactual_et_from_soil_m_per_timestep = 0; // if rootzone-based AET turned ON @@ -575,7 +621,7 @@ void et_from_soil(struct conceptual_reservoir *soil_res, struct evapotranspirati // wilting point, actual et from this timestep is 0 and no water is removed. if ( soil_res->smc_profile[soil_res->max_rootzone_layer] <= soil_parms->wltsmc ) { et_struct->actual_et_from_soil_m_per_timestep = 0; - } + } // calculate the amount of moisture removed by evapotranspiration for the bottom layer of the root zone else if (soil_res->smc_profile[soil_res->max_rootzone_layer] >= soil_res->soil_water_content_field_capacity) { et_struct->actual_et_from_soil_m_per_timestep = min(et_struct->reduced_potential_et_m_per_timestep, layer_storage_m); @@ -584,32 +630,32 @@ void et_from_soil(struct conceptual_reservoir *soil_res, struct evapotranspirati Budyko_numerator = soil_res->smc_profile[soil_res->max_rootzone_layer] - soil_parms->wltsmc; Budyko_denominator = soil_res->soil_water_content_field_capacity - soil_parms->wltsmc; Budyko = Budyko_numerator / Budyko_denominator; - + et_struct->actual_et_from_soil_m_per_timestep = min(Budyko * et_struct->reduced_potential_et_m_per_timestep, layer_storage_m); } - + // Reduce remaining PET and remove moisture from soil profile equal to the calculated AET (actual_et_from_soil_m_per_timestep - et_struct->reduced_potential_et_m_per_timestep -= et_struct->actual_et_from_soil_m_per_timestep; - soil_res->smc_profile[soil_res->max_rootzone_layer] -= (et_struct->actual_et_from_soil_m_per_timestep / + et_struct->reduced_potential_et_m_per_timestep -= et_struct->actual_et_from_soil_m_per_timestep; + soil_res->smc_profile[soil_res->max_rootzone_layer] -= (et_struct->actual_et_from_soil_m_per_timestep / soil_res->delta_soil_layer_depth_m[soil_res->max_rootzone_layer]); soil_res->storage_m -= et_struct->actual_et_from_soil_m_per_timestep; } - else if (et_struct->reduced_potential_et_m_per_timestep > 0) { - - if (soil_res->storage_m >= soil_res->storage_threshold_primary_m) { + else if (et_struct->reduced_potential_et_m_per_timestep > 0){ + + if (soil_res->storage_m >= soil_res->storage_threshold_primary_m){ et_struct->actual_et_from_soil_m_per_timestep = min(et_struct->reduced_potential_et_m_per_timestep, soil_res->storage_m); - } - else if (soil_res->storage_m > soil_parms->wilting_point_m && soil_res->storage_m < soil_res->storage_threshold_primary_m) { - + } + else if (soil_res->storage_m > soil_parms->wilting_point_m && soil_res->storage_m < soil_res->storage_threshold_primary_m){ + Budyko_numerator = soil_res->storage_m - soil_parms->wilting_point_m; Budyko_denominator = soil_res->storage_threshold_primary_m - soil_parms->wilting_point_m; Budyko = Budyko_numerator / Budyko_denominator; - // LKC: Include check to guarantee EAT is not larger than soil storage + // LKC: Include check to guarantee EAT is not larger than soil storage et_struct->actual_et_from_soil_m_per_timestep = min(Budyko * et_struct->reduced_potential_et_m_per_timestep, soil_res->storage_m); - + } - + soil_res->storage_m -= et_struct->actual_et_from_soil_m_per_timestep; et_struct->reduced_potential_et_m_per_timestep = et_struct->reduced_potential_et_m_per_timestep - et_struct->actual_et_from_soil_m_per_timestep; } @@ -619,6 +665,7 @@ void et_from_soil(struct conceptual_reservoir *soil_res, struct evapotranspirati extern int is_fabs_less_than_epsilon(double a,double epsilon) // returns true if fabs(a) -#include -#include -#include -#include -#include -#include -#include "logger.h" - -#define MODULE_COUNT 14 - - -Logger* loggerInstance = NULL; - -long long timeInMilliseconds(void) { - struct timeval tv; - - gettimeofday(&tv,NULL); - return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); -} - -char* createTimestamp() { - static char buffer[256]; - char buffer1[256]; - time_t now = time(NULL); - - long millis = (long)(timeInMilliseconds() % 1000); - - - struct tm* timeinfo = gmtime(&now); - strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S:", timeinfo); - snprintf(buffer1, sizeof(buffer1), "%03d/", (int)millis); - strcat(buffer, buffer1); - - return buffer; -} - -void SetLogPreferences(Logger* logger, LogLevel level) { - logger->logLevel = level; - - // get the log file path - char * log_file_path; - log_file_path = getenv("NGEN_LOG_FILE_PATH"); - - logger->logFile = fopen(log_file_path, "a"); - if (logger->logFile == NULL) { - printf("Can't Open Log File\n"); - // create local log directory and file - const char* logFileDir = "./run_logs/ngen_"; - char fullPath[256]; - snprintf(fullPath, sizeof(fullPath), "%s%s/", logFileDir, createTimestamp()); - - printf("Log File Directory: %s\n", fullPath); - - char mkdir_cmd[512]; - snprintf(mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p %s", fullPath); - int status = system(mkdir_cmd); - if (status == -1) { - fprintf(stderr, "Error: %s\n", strerror(errno)); - } else { - printf("Directories are created\n"); - } - - char logFilePath[512]; - snprintf(logFilePath, sizeof(logFilePath), "%scfe_log.txt", fullPath); - logger->logFile = fopen(logFilePath, "w"); - if (logger->logFile == NULL) { - fprintf(stderr, "Can't Open local Log File\n"); - } - else { - printf("Log File Path: %s\n", logFilePath); - } - } - else { - printf("Log File Path: %s\n", log_file_path); - } -} - -Logger* GetInstance() { - if (loggerInstance == NULL) { - loggerInstance = (Logger*)malloc(sizeof(Logger)); - SetLogPreferences(loggerInstance, NONE); - } - return loggerInstance; -} - -void Log(Logger* logger, const char* message, LogLevel messageLevel) { - if (messageLevel >= logger->logLevel) { - const char* logType; - switch (messageLevel) { - case FATAL: logType = "FATAL "; break; - case DEBUG: logType = "DEBUG "; break; - case INFO: logType = "INFO "; break; - case WARN: logType = "WARN "; break; - case ERROR: logType = "ERROR "; break; - default: logType = "NONE "; break; - } - - char final_message[1024]; - snprintf(final_message, sizeof(final_message), "%s %s %s%s\n", - createTimestamp(), module_name[CFE], logType, message); - - if (logger->logFile != NULL) { - fprintf(logger->logFile, "%s", final_message); - fflush(logger->logFile); - } - } -} - -LogLevel GetLogLevel(const char* logLevel) { - if (strcmp(logLevel, "DEBUG") == 0) return DEBUG; - if (strcmp(logLevel, "INFO") == 0) return INFO; - if (strcmp(logLevel, "WARN") == 0) return ERROR; - if (strcmp(logLevel, "ERROR") == 0) return ERROR; - if (strcmp(logLevel, "FATAL") == 0) return ERROR; - return NONE; -} - -void setup_logger(void) { - Logger* logger = GetInstance(); - - Log(logger, "Sample Log for LogLevel::ERROR", ERROR); - Log(logger, "Sample Log for LogLevel::FATAL", FATAL); - Log(logger, "Sample Log for LogLevel::WARN", WARN); - Log(logger, "Sample Log for LogLevel::INFO", INFO); - - const char* multiline_log = - "First line of multiline log:\n" - " Indented second line of multiline log\n" - " Indented third line of multiline log\n" - " Indented fourth line of multiline log"; - Log(logger, multiline_log, INFO); - - Log(logger, "Sample Log for LogLevel::DEBUG", DEBUG); -} diff --git a/src/main.c b/src/main.c index e462ebb6..105bd340 100644 --- a/src/main.c +++ b/src/main.c @@ -11,7 +11,8 @@ This is not part of BMI, but acts as the driver that calls the model. */ int main(int argc, const char *argv[]) -{ +{ + //////////////////////////////////////////////////////////////// ////////////// USING UPDATE ////////////////////////////// //////////////////////////////////////////////////////////////// diff --git a/src/main_cfe_aorc_pet_rz_aet.cxx b/src/main_cfe_aorc_pet_rz_aet.cxx index 02d24949..d1034cb9 100644 --- a/src/main_cfe_aorc_pet_rz_aet.cxx +++ b/src/main_cfe_aorc_pet_rz_aet.cxx @@ -12,6 +12,7 @@ #include "../extern/evapotranspiration/include/pet.h" #include "../extern/evapotranspiration/include/bmi_pet.h" +#include "../bmi/bmi.hxx" #include "../extern/SoilMoistureProfiles/include/bmi_soil_moisture_profile.hxx" #include "../extern/SoilMoistureProfiles/include/soil_moisture_profile.hxx" diff --git a/src/main_pass_forcings.c b/src/main_pass_forcings.c index 80c01031..ce774bf5 100644 --- a/src/main_pass_forcings.c +++ b/src/main_pass_forcings.c @@ -128,7 +128,7 @@ int /************************************************************************ Finalize both the CFE and AORC bmi models ************************************************************************/ - printf("Finalizing CFE and AORC models\n"); + printf("Finalizing BFE and AORC models\n"); cfe_bmi_model->finalize(cfe_bmi_model); aorc_bmi_model->finalize(aorc_bmi_model); diff --git a/src/nash_cascade.c b/src/nash_cascade.c deleted file mode 100644 index d03a69de..00000000 --- a/src/nash_cascade.c +++ /dev/null @@ -1,144 +0,0 @@ -#ifndef _NASH_C -#define _NASH_C - -#include "nash_cascade.h" - -//############################################################## -//################### NASH CASCADE ######################### -//############################################################## -extern double nash_cascade(double flux_lat_m,int num_lateral_flow_nash_reservoirs, - double K_nash,double *nash_storage_arr) -{ - //############################################################## - // Solve for the flow through the Nash cascade to delay the - // arrival of the lateral flow into the channel - //############################################################## - // local vars - int i; - double outflow_m; - static double Q[MAX_NUM_NASH_CASCADE]; - - //Loop through reservoirs - for(i = 0; i < num_lateral_flow_nash_reservoirs; i++) - { - Q[i] = K_nash*nash_storage_arr[i]; - nash_storage_arr[i] -= Q[i]; - - if (i==0) nash_storage_arr[i] += flux_lat_m; - else nash_storage_arr[i] += Q[i-1]; - } - - /* Get Qout */ - outflow_m = Q[num_lateral_flow_nash_reservoirs-1]; - - //Return the flow output - return (outflow_m); - -} - - -//############################################################## -//############## NASH CASCADE SURFACE RUNOFF ################ -//############################################################## -double nash_cascade_surface_runoff(double runoff_m, double soil_storage_deficit_m, - struct nash_cascade_parameters *nash_params) -{ - //############################################################## - // Solve for the flow through the Nash cascade to delay the - // arrival of the lateral flow into the channel - //############################################################## - - int nsubsteps = nash_params->nsubsteps; - int N_nash = nash_params->N_nash; - double K_nash = nash_params->K_nash; - double depth_r = nash_params->retention_depth; - double K_infil = nash_params->K_infiltration; - - // local vars - double dt_h = 1.0; // model timestep [hour] - double subdt = dt_h/nsubsteps; - double S = 0.0; - double dS = 0.0; // change in reservoir storage - double dS_infil = 0.0; // change in reservoir storage due to infiltration - double Q_r; // discharge from reservoir - double Q_out; // discharge at the outlet (the last reservoir) per subtimestep - double Q_infil; // discharge from reservoirs to soil - double Q_to_channel_m = 0.0; // total outflow to channel per timestep - double Q_to_soil_m = 0.0; // runon infiltration (losses from surface runoff to soil) - double soil_deficit_m = soil_storage_deficit_m; // local variable to track the soil storage deficit - double infil_m; - - nash_params->nash_storage[0] += runoff_m; - - // Loop through number of sub-timesteps - for (int ts = 0; ts < nsubsteps; ts++) { - - //Loop through reservoirs - for(int i = 0; i < N_nash; i++) { - - // First: infiltration capacity should be satisfied before routing water through Nash reservoirs - - // compute runon infiltration - Q_infil = K_infil * nash_params->nash_storage[i]; - infil_m = Q_infil * subdt; - - // case 1: soil_deficit greater than infil_m, all water can infiltrate, and soil_deficit decreases - // case 2: soil_deficit less than infil_m, portion of water can infiltrate, soil_deficit gets zero - // case 3: soil_deficit is zero, no infiltration - - // determine the right amount of infiltrated water based on soil deficit - if (soil_deficit_m > 0.0 && soil_deficit_m > infil_m) { - soil_deficit_m -= infil_m; // update soil deficit for the next subtimstep iteration - } - else if (soil_deficit_m > 0.0) { - infil_m = soil_deficit_m; - soil_deficit_m = 0.0; - } - else { - infil_m = 0.0; // if got here, then soil is fully saturated, so no infiltration - } - - // update nash storage (subtract infiltrated water) - if (nash_params->nash_storage[i] >= infil_m) - nash_params->nash_storage[i] = nash_params->nash_storage[i] - infil_m; - else { - infil_m = nash_params->nash_storage[i]; - nash_params->nash_storage[i] = 0.0; - } - - - Q_to_soil_m += infil_m; // water volume that infiltrates per subtimestep from each reservoir - - /*=========================================================================================*/ - // Second: time to route water through Nash reservoirs - S = nash_params->nash_storage[i]; - - // determine the amount of surface water available for routing from the first reservoir - if (i == 0 && S > depth_r) - S -= depth_r; - else if (i == 0) - S = 0.0; - - Q_r = K_nash * S; // flow from reservoir i to i+1 - dS = fmin(Q_r * subdt, S); // storage change in reservoir i per subtimestep - nash_params->nash_storage[i] -= dS; // updated storage in reservoir i - - if(i < (N_nash-1)) - nash_params->nash_storage[i+1] += dS; - else - Q_out = Q_r; - - } - - Q_to_channel_m += Q_out * subdt; // Q_r at the end of N_nash loop is the discharge at the outlet - - } - - nash_params->runon_infiltration = Q_to_soil_m; - - // Return the flow output - return (Q_to_channel_m); - -} - -#endif diff --git a/test/main_unit_test.c b/test/main_unit_test.c index fbda063b..eb21c4e8 100644 --- a/test/main_unit_test.c +++ b/test/main_unit_test.c @@ -393,7 +393,7 @@ main(int argc, const char *argv[]){ printf("\nTEST BMI MODEL PARAMETERS\n*************************\n"); //Set number of params -- UPDATE if params changed -#define PARAM_COUNT 18 +#define PARAM_COUNT 17 // expected_param_names copied directly from param_var_names[PARAM_VAR_NAME_COUNT] in ../src/bmi_cfe.c static const char *expected_param_names[PARAM_COUNT] = { @@ -401,7 +401,6 @@ main(int argc, const char *argv[]){ "Kn", "Cgw", "expon", "max_gw_storage", "satpsi","wltsmc","alpha_fc","refkdt", "a_Xinanjiang_inflection_point_parameter","b_Xinanjiang_shape_parameter","x_Xinanjiang_shape_parameter", - "K_infiltration", "N_nash"}; double test_set_value = 4.2; double test_get_value = 0.0; From c99ab2600f612ee788bd3f0cc42dbda01d2fdf86 Mon Sep 17 00:00:00 2001 From: "areg.amirkhanian" Date: Thu, 3 Oct 2024 22:39:11 -0700 Subject: [PATCH 07/50] recommit of logger changes for CFE module --- CMakeLists.txt | 14 ++--- include/logger.h | 65 +++++++++++++++++++++++ src/Makefile | 2 +- src/bmi_cfe.c | 8 +++ src/logger.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 include/logger.h create mode 100644 src/logger.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fcd4e6a..0cfad7c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,7 @@ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") ## cfe + aorc + pet + smp if(AETROOTZONE) -add_executable(${exe_name} ./src/main_cfe_aorc_pet_rz_aet.cxx ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c +add_executable(${exe_name} ./src/main_cfe_aorc_pet_rz_aet.cxx ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c ./extern/evapotranspiration/src/bmi_pet.c) @@ -70,16 +70,16 @@ add_library(cfelib ./extern/SoilMoistureProfiles/src/bmi_soil_moisture_profile.c ./extern/SoilMoistureProfiles/include/bmi_soil_moisture_profile.hxx ./extern/SoilMoistureProfiles/include/soil_moisture_profile.hxx) target_link_libraries(${exe_name} LINK_PUBLIC cfelib) elseif(FORCING) -add_executable(${exe_name} ./src/main_pass_forcings.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c +add_executable(${exe_name} ./src/main_pass_forcings.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c) elseif(FORCINGPET) -add_executable(${exe_name} ./src/main_cfe_aorc_pet.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c +add_executable(${exe_name} ./src/main_cfe_aorc_pet.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c ./extern/evapotranspiration/src/bmi_pet.c) elseif(BASE) -add_executable(${exe_name} ./src/main.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) +add_executable(${exe_name} ./src/main.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c) elseif(UNITTEST) -add_executable(${exe_name} ./test/main_unit_test.c ./src/cfe.c ./src/bmi_cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) +add_executable(${exe_name} ./test/main_unit_test.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c) endif() @@ -98,9 +98,9 @@ set(CFE_LIB_DESC_CMAKE "OWP CFE BMI Module Shared Library") add_compile_definitions(BMI_ACTIVE) if(WIN32) - add_library(cfebmi ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) + add_library(cfebmi ./src/bmi_cfe.c ./src/logger.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) else() - add_library(cfebmi SHARED ./src/bmi_cfe.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) + add_library(cfebmi SHARED ./src/bmi_cfe.c ./src/logger.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) endif() target_include_directories(cfebmi PRIVATE include) diff --git a/include/logger.h b/include/logger.h new file mode 100644 index 00000000..80ac4baf --- /dev/null +++ b/include/logger.h @@ -0,0 +1,65 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include +#include + +typedef enum { + NONE = 0, + DEBUG, + INFO, + FATAL, + WARN, + ERROR, +} LogLevel; + +typedef enum { + NGEN, + NOAHOWP, + SNOW17, + UEB, + CFE, + SACSMA, + LASAM, + SMP, + SFT, + TROUTE, + SCHISM, + SFINCS, + GC2D, + TOPOFLOW, + MODULE_COUNT +} LoggingModule; + +static const char* module_name[MODULE_COUNT] = { + "NGEN ", + "NOAHOWP ", + "SNOW17 ", + "UEB ", + "CFE ", + "SACSMA ", + "LASAM ", + "SMP ", + "SFT ", + "TROUTE ", + "SCHISM ", + "SFINCS ", + "GC2D ", + "TOPOFLOW" +}; + +typedef struct { + LogLevel logLevel; + FILE* logFile; +} Logger; + +Logger* GetInstance(); +void SetLogPreferences(Logger* logger, LogLevel level); +void Log(Logger* logger, const char* message, LogLevel messageLevel); +LogLevel GetLogLevel(const char* logLevel); +char* createTimestamp(); +void setup_logger(void); + +#endif diff --git a/src/Makefile b/src/Makefile index b78e3ebe..4c17e172 100755 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,7 @@ LFLAGS = LIBS = -lm # define the C source files -SRCS = main.c cfe.c bmi_cfe.c +SRCS = main.c cfe.c bmi_cfe.c logger.c # define the C object files OBJS = $(SRCS:.c=.o) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index e8c32d47..a8d37270 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -5,6 +5,8 @@ #include "bmi_cfe.h" #include #include +#include "logger.h" + #ifndef WATER_SPECIFIC_WEIGHT #define WATER_SPECIFIC_WEIGHT 9810 #define STANDARD_ATMOSPHERIC_PRESSURE_PASCALS 101325 @@ -1140,6 +1142,11 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) static int Initialize (Bmi *self, const char *file) { + // setup the logger + setup_logger(); + Logger* logger = GetInstance(); + Log(logger, "In CFE Initialize()\n", INFO); + //FIXME, we can use the input file to help imply "framework" support or "standalone" //an empty init file string indicates things will come from set_value??? //what happens when both occur, that is we have a config file and framewrok @@ -1319,6 +1326,7 @@ static int Initialize (Bmi *self, const char *file) #if CFE_DEBUG > 0 printf("At declaration of smc_profile size, soil_reservoir.n_soil_layers = %i\n", cfe_bmi_data_ptr->soil_reservoir.n_soil_layers); #endif + Log(logger, "Success in CFE BMI Initialization\n", INFO); return BMI_SUCCESS; } diff --git a/src/logger.c b/src/logger.c new file mode 100644 index 00000000..b6cda190 --- /dev/null +++ b/src/logger.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include +#include +#include "logger.h" + +#define MODULE_COUNT 14 + + +Logger* loggerInstance = NULL; + +long long timeInMilliseconds(void) { + struct timeval tv; + + gettimeofday(&tv,NULL); + return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); +} + +char* createTimestamp() { + static char buffer[256]; + char buffer1[256]; + time_t now = time(NULL); + + long millis = (long)(timeInMilliseconds() % 1000); + + + struct tm* timeinfo = gmtime(&now); + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S:", timeinfo); + snprintf(buffer1, sizeof(buffer1), "%03d/", (int)millis); + strcat(buffer, buffer1); + + return buffer; +} + +void SetLogPreferences(Logger* logger, LogLevel level) { + logger->logLevel = level; + + // get the log file path + char * log_file_path; + log_file_path = getenv("NGEN_LOG_FILE_PATH"); + + logger->logFile = fopen(log_file_path, "a"); + if (logger->logFile == NULL) { + printf("Can't Open Log File\n"); + // create local log directory and file + const char* logFileDir = "./run_logs/ngen_"; + char fullPath[256]; + snprintf(fullPath, sizeof(fullPath), "%s%s/", logFileDir, createTimestamp()); + + printf("Log File Directory: %s\n", fullPath); + + char mkdir_cmd[512]; + snprintf(mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p %s", fullPath); + int status = system(mkdir_cmd); + if (status == -1) { + fprintf(stderr, "Error: %s\n", strerror(errno)); + } else { + printf("Directories are created\n"); + } + + char logFilePath[512]; + snprintf(logFilePath, sizeof(logFilePath), "%scfe_log.txt", fullPath); + logger->logFile = fopen(logFilePath, "w"); + if (logger->logFile == NULL) { + fprintf(stderr, "Can't Open local Log File\n"); + } + else { + printf("Log File Path: %s\n", logFilePath); + } + } + else { + printf("Log File Path: %s\n", log_file_path); + } +} + +Logger* GetInstance() { + if (loggerInstance == NULL) { + loggerInstance = (Logger*)malloc(sizeof(Logger)); + SetLogPreferences(loggerInstance, NONE); + } + return loggerInstance; +} + +void Log(Logger* logger, const char* message, LogLevel messageLevel) { + if (messageLevel >= logger->logLevel) { + const char* logType; + switch (messageLevel) { + case FATAL: logType = "FATAL "; break; + case DEBUG: logType = "DEBUG "; break; + case INFO: logType = "INFO "; break; + case WARN: logType = "WARN "; break; + case ERROR: logType = "ERROR "; break; + default: logType = "NONE "; break; + } + + char final_message[1024]; + snprintf(final_message, sizeof(final_message), "%s %s %s%s\n", + createTimestamp(), module_name[CFE], logType, message); + + if (logger->logFile != NULL) { + fprintf(logger->logFile, "%s", final_message); + fflush(logger->logFile); + } + } +} + +LogLevel GetLogLevel(const char* logLevel) { + if (strcmp(logLevel, "DEBUG") == 0) return DEBUG; + if (strcmp(logLevel, "INFO") == 0) return INFO; + if (strcmp(logLevel, "WARN") == 0) return ERROR; + if (strcmp(logLevel, "ERROR") == 0) return ERROR; + if (strcmp(logLevel, "FATAL") == 0) return ERROR; + return NONE; +} + +void setup_logger(void) { + Logger* logger = GetInstance(); + + Log(logger, "Sample Log for LogLevel::ERROR", ERROR); + Log(logger, "Sample Log for LogLevel::FATAL", FATAL); + Log(logger, "Sample Log for LogLevel::WARN", WARN); + Log(logger, "Sample Log for LogLevel::INFO", INFO); + + const char* multiline_log = + "First line of multiline log:\n" + " Indented second line of multiline log\n" + " Indented third line of multiline log\n" + " Indented fourth line of multiline log"; + Log(logger, multiline_log, INFO); + + Log(logger, "Sample Log for LogLevel::DEBUG", DEBUG); +} From d6d640dbefb77a985254152ed9efd2e70f17349f Mon Sep 17 00:00:00 2001 From: "areg.amirkhanian" Date: Mon, 28 Oct 2024 16:21:10 -0700 Subject: [PATCH 08/50] NGWPC-4428 - CFE logging changes --- include/logger.h | 5 +- src/bmi_cfe.c | 378 +++++++++++++++++------------------------------ src/cfe.c | 13 +- src/logger.c | 44 ++++-- 4 files changed, 173 insertions(+), 267 deletions(-) diff --git a/include/logger.h b/include/logger.h index 80ac4baf..75324820 100644 --- a/include/logger.h +++ b/include/logger.h @@ -5,6 +5,7 @@ #include #include #include +#include // for variable args: va_list typedef enum { NONE = 0, @@ -56,8 +57,8 @@ typedef struct { } Logger; Logger* GetInstance(); -void SetLogPreferences(Logger* logger, LogLevel level); -void Log(Logger* logger, const char* message, LogLevel messageLevel); +void SetLogPreferences(Logger* logger); +void Log(LogLevel messageLevel, const char* message, ...); LogLevel GetLogLevel(const char* logLevel); char* createTimestamp(); void setup_logger(void); diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index a8d37270..ee3dc583 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -12,8 +12,6 @@ #define STANDARD_ATMOSPHERIC_PRESSURE_PASCALS 101325 #endif -#define CFE_DEBUG 1 - #define INPUT_VAR_NAME_COUNT 5 #define OUTPUT_VAR_NAME_COUNT 13 @@ -380,9 +378,8 @@ static int Get_time_units (Bmi *self, char * units) static int Get_current_time (Bmi *self, double * time) { Get_start_time(self, time); -#if CFE_DEBUG > 1 - printf("Current model time step: '%d'\n", ((cfe_state_struct *) self->data)->current_time_step); -#endif + Log(DEBUG, "Current model time step: '%d'\n", ((cfe_state_struct *) self->data)->current_time_step); + *time += (((cfe_state_struct *) self->data)->current_time_step * ((cfe_state_struct *) self->data)->time_step_size); return BMI_SUCCESS; } @@ -411,12 +408,11 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) // Note that this determines max line length including the ending return character, if present int count_result = read_file_line_counts_cfe(config_file, &config_line_count, &max_config_line_length); if (count_result == -1) { - printf("Invalid config file '%s'", config_file); + Log(ERROR, "Invalid config file '%s'", config_file); return BMI_FAILURE; } -#if CFE_DEBUG >= 1 - printf("Config file details - Line Count: %d | Max Line Length %d\n", config_line_count, max_config_line_length); -#endif + + Log(DEBUG, "Config file details - Line Count: %d | Max Line Length %d\n", config_line_count, max_config_line_length); FILE* fp = fopen(config_file, "r"); if (fp == NULL) @@ -514,9 +510,9 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) char *param_key, *param_value, *param_units; if (fgets(config_line, max_config_line_length + 1, fp) == NULL) return BMI_FAILURE; -#if CFE_DEBUG >= 3 - printf("Line value: ['%s']\n", config_line); -#endif + + Log(DEBUG, "Line value: ['%s']\n", config_line); + char* config_line_ptr = config_line; config_line_ptr = strsep(&config_line_ptr, "\n"); param_key = strsep(&config_line_ptr, "="); @@ -524,9 +520,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) param_value = strsep(&config_line_ptr, "["); param_units = strsep(&config_line_ptr, "]"); -#if CFE_DEBUG >= 1 - printf("Config Value - Param: '%s' | Value: '%s' | Units: '%s'\n", param_key, param_value, param_units); -#endif + Log(DEBUG, "Config Value - Param: '%s' | Value: '%s' | Units: '%s'\n", param_key, param_value, param_units); if (strcmp(param_key, "forcing_file") == 0) { model->forcing_file = strdup(param_value); @@ -542,9 +536,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__depth_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log(WARN, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -558,9 +550,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__satdk_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log(WARN, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -569,9 +559,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__satpsi_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log(WARN, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -580,9 +568,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__slop_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log( WARN, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -591,9 +577,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__smcmax_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log( WARN, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -602,9 +586,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__wltsmc_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log( WARN, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -623,9 +605,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_gw_max_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log( WARN, "[units] expected for '%s' in config file \n", param_key); } // Also set the true storage if storage was already read and was a ratio, and so we were waiting for this /* if (is_gw_storage_set == TRUE && is_gw_storage_ratio == TRUE) { @@ -638,9 +618,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_Cgw_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log( WARN, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -655,9 +633,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_gw_storage_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log( WARN, "[units] expected for '%s' in config file \n", param_key); } /* char* trailing_chars; gw_storage_literal = strtod(param_value, &trailing_chars); @@ -689,9 +665,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_storage_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log( WARN, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -715,9 +689,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) continue; } if (strcmp(param_key, "giuh_ordinates") == 0) { -#if CFE_DEBUG >= 1 - printf("Found configured GIUH ordinate values ('%s')\n", param_value); -#endif + Log(DEBUG, "Found configured GIUH ordinate values ('%s')\n", param_value); giuh_originates_string_val = strdup(param_value); is_giuh_originates_string_val_set = TRUE; continue; @@ -744,9 +716,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) if (is_aet_rootzone_set == TRUE) { if (strcmp(param_key, "soil_layer_depths") == 0) { -#if CFE_DEBUG >= 1 - printf("Found configured soil depth values ('%s')\n", param_value); -#endif + Log(DEBUG, "Found configured soil depth values ('%s')\n", param_value); soil_layer_depths_string_val = strdup(param_value); is_soil_layer_depths_string_val_set = TRUE; continue; @@ -803,188 +773,134 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_ice_content_threshold_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { -#if CFE_DEBUG >= 1 - printf ("WARNING: [units] expected for '%s' in config file \n", param_key); -#endif + Log( WARN, "[units] expected for '%s' in config file \n", param_key); } } } } if (is_forcing_file_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'forcing_file' not found in config file\n"); -#endif + Log(ERROR, "Config param 'forcing_file' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__depth_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.depth' not found in config file\n"); -#endif + Log(ERROR, "Config param 'soil_params.depth' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__bb_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.bb' not found in config file\n"); -#endif + Log(ERROR, "Config param 'soil_params.bb' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__satdk_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.satdk' not found in config file\n"); -#endif + Log(ERROR, "Config param 'soil_params.satdk' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__satpsi_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.satpsi' not found in config file\n"); -#endif + Log(ERROR, "Config param 'soil_params.satpsi' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__slop_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.slop' not found in config file\n"); -#endif + Log(ERROR, "Config param 'soil_params.slop' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__smcmax_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.smcmax' not found in config file\n"); -#endif + Log(ERROR, "Config param 'soil_params.smcmax' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__wltsmc_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.wltsmc' not found in config file\n"); -#endif + Log(ERROR, "Config param 'soil_params.wltsmc' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__expon_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.expon' not found in config file, defaulting to 1 (linear)\n"); -#endif + Log(ERROR, "Config param 'soil_params.expon' not found in config file, defaulting to 1 (linear)\n"); model->soil_reservoir.exponent_primary = 1.0; //is_soil_params__expon_set == TRUE; // Don't return BMI_FAILURE, this is a optional config //return BMI_FAILURE; } if (is_soil_params__expon2_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_params.expon_secondary' not found in config file, defaulting to 1 (linear)\n"); -#endif + Log(ERROR, "Config param 'soil_params.expon_secondary' not found in config file, defaulting to 1 (linear)\n"); model->soil_reservoir.exponent_secondary = 1.0; // Don't return BMI_FAILURE, this is a optional config //return BMI_FAILURE; } if (is_Cgw_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'Cgw' not found in config file\n"); -#endif + Log(ERROR, "Config param 'Cgw' not found in config file\n"); return BMI_FAILURE; } if (is_expon_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'expon' not found in config file\n"); -#endif + Log(ERROR, "Config param 'expon' not found in config file\n"); return BMI_FAILURE; } if (is_alpha_fc_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'alpha_fc' not found in config file\n"); -#endif + Log(ERROR, "Config param 'alpha_fc' not found in config file\n"); return BMI_FAILURE; } if (is_soil_storage_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'soil_storage' not found in config file\n"); -#endif + Log(ERROR, "Config param 'soil_storage' not found in config file\n"); return BMI_FAILURE; } if (is_K_nash_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'K_nash' not found in config file\n"); -#endif + Log(ERROR, "Config param 'K_nash' not found in config file\n"); return BMI_FAILURE; } if (is_K_lf_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'K_lf' not found in config file\n"); -#endif + Log(ERROR, "Config param 'K_lf' not found in config file\n"); return BMI_FAILURE; } if (is_gw_max_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'max_gw_storage' not found in config file\n"); -#endif + Log(ERROR, "Config param 'max_gw_storage' not found in config file\n"); return BMI_FAILURE; } if (is_gw_storage_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'gw_storage' not found in config file\n"); -#endif + Log(ERROR, "Config param 'gw_storage' not found in config file\n"); return BMI_FAILURE; } if (is_num_timesteps_set == FALSE && strcmp(model->forcing_file, "BMI")) { -#if CFE_DEBUG >= 1 - printf("Config param 'num_timesteps' not found in config file\n"); -#endif + Log(ERROR, "Config param 'num_timesteps' not found in config file\n"); return BMI_FAILURE; } if (is_verbosity_set == FALSE) { - printf("Config param 'verbosity' not found in config file\n"); - printf("setting verbosity to a high value\n"); + Log(ERROR, "Config param 'verbosity' not found in config file\n"); + Log(ERROR, "setting verbosity to a high value\n"); model->verbosity = 10; return BMI_FAILURE; } if (is_direct_runoff_method_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'direct_runoff_method' not found in config file\n"); -#endif + Log(ERROR, "Config param 'direct_runoff_method' not found in config file\n"); return BMI_FAILURE; } /* xinanjiang_dev*/ if(model->direct_runoff_params_struct.surface_partitioning_scheme == Xinanjiang){ if (is_a_Xinanjiang_inflection_point_parameter_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'a_Xinanjiang_inflection_point_parameter' not found in config file\n"); -#endif + Log(ERROR, "Config param 'a_Xinanjiang_inflection_point_parameter' not found in config file\n"); return BMI_FAILURE; } if (is_b_Xinanjiang_shape_parameter_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'b_Xinanjiang_shape_parameter' not found in config file\n"); -#endif + Log(ERROR, "Config param 'b_Xinanjiang_shape_parameter' not found in config file\n"); return BMI_FAILURE; } if (is_x_Xinanjiang_shape_parameter_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'x_Xinanjiang_shape_parameter' not found in config file\n"); -#endif + Log(ERROR, "Config param 'x_Xinanjiang_shape_parameter' not found in config file\n"); return BMI_FAILURE; } - if (is_urban_decimal_fraction_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'urban_decimal_fraction' not found in config file\n"); -#endif - return BMI_FAILURE; - } + if (is_urban_decimal_fraction_set == FALSE) { + Log(ERROR, "Config param 'urban_decimal_fraction' not found in config file\n"); + return BMI_FAILURE; + } } if(model->direct_runoff_params_struct.surface_partitioning_scheme == Schaake){ model->direct_runoff_params_struct.Schaake_adjusted_magic_constant_by_soil_type = model->NWM_soil_params.refkdt * model->NWM_soil_params.satdk / 0.000002; -#if CFE_DEBUG >= 1 - printf("Schaake Magic Constant calculated\n"); -#endif + Log(INFO, "Schaake Magic Constant calculated\n"); } if (is_sft_coupled_set == TRUE && model->direct_runoff_params_struct.surface_partitioning_scheme == Schaake) { if(!is_ice_content_threshold_set) { -#if CFE_DEBUG >= 1 - printf("is_sft_coupled and Schaake scheme are set to TRUE but param 'ice_fraction_threshold' not found in config file\n"); - exit(-9); -#endif + Log(FATAL, "is_sft_coupled and Schaake scheme are set to TRUE but param 'ice_fraction_threshold' not found in config file\n"); + exit(-9); } } @@ -998,31 +914,23 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) /*------------------- Root zone AET development -rlm----------------------------- */ if (is_aet_rootzone_set == TRUE ) { if (is_max_rootzone_layer_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'max_rootzone_layer' not found in config file\n"); -#endif + Log(ERROR, "Config param 'max_rootzone_layer' not found in config file\n"); return BMI_FAILURE; } if (is_soil_layer_depths_string_val_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Soil layer depths string/values not set!\n"); -#endif + Log(ERROR, "Soil layer depths string/values not set!\n"); return BMI_FAILURE; } -#if CFE_DEBUG >=1 - printf("Soil layer depths string values found in config ('%d')\n", is_soil_layer_depths_string_val_set); -#endif + Log(INFO, "Soil layer depths string values found in config ('%d')\n", is_soil_layer_depths_string_val_set); model->soil_reservoir.n_soil_layers = lround(count_delimited_values(soil_layer_depths_string_val, ",")); - printf("n_soil_layers set in bmi_cfe.c: %d\n", model->soil_reservoir.n_soil_layers); + Log(INFO, "n_soil_layers set in bmi_cfe.c: %d\n", model->soil_reservoir.n_soil_layers); -#if CFE_DEBUG >= 1 - printf("Counted number of soil depths (%d)\n", model->soil_reservoir.n_soil_layers); -#endif + Log(INFO, "Counted number of soil depths (%d)\n", model->soil_reservoir.n_soil_layers); if (model->soil_reservoir.n_soil_layers < 1) - return BMI_FAILURE; + return BMI_FAILURE; model->soil_reservoir.soil_layer_depths_m = malloc(sizeof(double) * (model->soil_reservoir.n_soil_layers + 1)); copy = soil_layer_depths_string_val; @@ -1037,7 +945,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) //Check that the last depth read in from the cfe config file (soil_layer_depths) matches the total depth (soil_params.depth) //from the cfe config file. if(model->NWM_soil_params.D != model->soil_reservoir.soil_layer_depths_m[model->soil_reservoir.n_soil_layers]){ - printf("WARNING: soil_params.depth is not equal to the last soil layer depth in the CFE config file!\n"); + Log(WARN, "soil_params.depth is not equal to the last soil layer depth in the CFE config file!\n"); return BMI_FAILURE; } @@ -1052,7 +960,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) for (int i=1; i <= model->soil_reservoir.n_soil_layers; i++) { current_depth = model->soil_reservoir.soil_layer_depths_m[i]; if (current_depth <= previous_depth) - printf("WARNING: soil depths may be out of order. One or more soil layer depths is less than or equal to the previous layer. Check CFE config file.\n"); + Log(WARN, "Soil depths may be out of order. One or more soil layer depths is less than or equal to the previous layer. Check CFE config file.\n"); model->soil_reservoir.delta_soil_layer_depth_m[i] = current_depth - previous_depth; previous_depth = current_depth; } @@ -1069,24 +977,16 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) /*--------------------END OF ROOT ZONE ADJUSTED AET DEVELOPMENT -rlm ------------------------------*/ -#if CFE_DEBUG >= 1 - printf("All CFE config params present\n"); -#endif + Log(INFO, "All CFE config params present\n"); // Handle GIUH ordinates, bailing if they were not provided if (is_giuh_originates_string_val_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("GIUH ordinate string not set!\n"); -#endif + Log(ERROR, "GIUH ordinate string not set!\n"); return BMI_FAILURE; } -#if CFE_DEBUG >= 1 - printf("GIUH ordinates string value found in config ('%s')\n", giuh_originates_string_val); -#endif + Log(INFO, "GIUH ordinates string value found in config ('%s')\n", giuh_originates_string_val); model->num_giuh_ordinates = count_delimited_values(giuh_originates_string_val, ","); -#if CFE_DEBUG >= 1 - printf("Counted number of GIUH ordinates (%d)\n", model->num_giuh_ordinates); -#endif + Log(INFO, "Counted number of GIUH ordinates (%d)\n", model->num_giuh_ordinates); if (model->num_giuh_ordinates < 1) return BMI_FAILURE; @@ -1132,9 +1032,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) model->nash_storage[j] = 0.0; } fclose(fp); -#if CFE_DEBUG >= 1 - printf("Finished function parsing CFE config\n"); -#endif + Log(DEBUG, "Finished function parsing CFE config\n"); return BMI_SUCCESS; } @@ -1143,9 +1041,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) static int Initialize (Bmi *self, const char *file) { // setup the logger - setup_logger(); - Logger* logger = GetInstance(); - Log(logger, "In CFE Initialize()\n", INFO); + Log(INFO, "In CFE Initialize()\n"); //FIXME, we can use the input file to help imply "framework" support or "standalone" //an empty init file string indicates things will come from set_value??? @@ -1228,20 +1124,18 @@ static int Initialize (Bmi *self, const char *file) int forcing_line_count, max_forcing_line_length; int count_result = read_file_line_counts_cfe(cfe_bmi_data_ptr->forcing_file, &forcing_line_count, &max_forcing_line_length); if (count_result == -1) { - printf("Configured forcing file '%s' could not be opened for reading\n", cfe_bmi_data_ptr->forcing_file); + Log(ERROR, "Configured forcing file '%s' could not be opened for reading\n", cfe_bmi_data_ptr->forcing_file); return BMI_FAILURE; } if (forcing_line_count == 1) { - printf("Invalid header-only forcing file '%s'\n", cfe_bmi_data_ptr->forcing_file); + Log(ERROR, "Invalid header-only forcing file '%s'\n", cfe_bmi_data_ptr->forcing_file); return BMI_FAILURE; } // Infer the number of time steps: assume a header, so equal to the number of lines minus 1 cfe_bmi_data_ptr->num_timesteps = forcing_line_count - 1; - #if CFE_DEBUG > 0 - printf("Counts - Lines: %d | Max Line: %d | Num Time Steps: %d\n", forcing_line_count, max_forcing_line_length, + Log(INFO, "Counts - Lines: %d | Max Line: %d | Num Time Steps: %d\n", forcing_line_count, max_forcing_line_length, cfe_bmi_data_ptr->num_timesteps); - #endif // Now initialize empty arrays that depend on number of time steps cfe_bmi_data_ptr->forcing_data_precip_kg_per_m2 = malloc(sizeof(double) * (cfe_bmi_data_ptr->num_timesteps + 1)); @@ -1251,7 +1145,7 @@ static int Initialize (Bmi *self, const char *file) FILE* ffp = fopen(cfe_bmi_data_ptr->forcing_file, "r"); // Ensure still exists if (ffp == NULL) { - printf("Forcing file '%s' disappeared!", cfe_bmi_data_ptr->forcing_file); + Log(ERROR, "Forcing file '%s' disappeared!", cfe_bmi_data_ptr->forcing_file); return BMI_FAILURE; } @@ -1269,10 +1163,8 @@ static int Initialize (Bmi *self, const char *file) if (fgets(line_str, max_forcing_line_length + 1, ffp) == NULL) // read in a line of AORC data. return BMI_FAILURE; parse_aorc_line_cfe(line_str, &year, &month, &day, &hour, &minute, &dsec, &forcings); - #if CFE_DEBUG > 0 - printf("Forcing data: [%s]\n", line_str); - printf("Forcing details - s_time: %ld | precip: %f\n", forcings.time, forcings.precip_kg_per_m2); - #endif + Log(INFO, "Forcing data: [%s]\n", line_str); + Log(INFO, "Forcing details - s_time: %ld | precip: %f\n", forcings.time, forcings.precip_kg_per_m2); cfe_bmi_data_ptr->forcing_data_precip_kg_per_m2[i] = forcings.precip_kg_per_m2; //* ((double)cfe_bmi_data_ptr->time_step_size); cfe_bmi_data_ptr->forcing_data_time[i] = forcings.time; @@ -1323,10 +1215,8 @@ static int Initialize (Bmi *self, const char *file) else cfe_bmi_data_ptr->soil_reservoir.smc_profile = malloc(sizeof(double)*1); -#if CFE_DEBUG > 0 - printf("At declaration of smc_profile size, soil_reservoir.n_soil_layers = %i\n", cfe_bmi_data_ptr->soil_reservoir.n_soil_layers); -#endif - Log(logger, "Success in CFE BMI Initialization\n", INFO); + Log(INFO, "At declaration of smc_profile size, soil_reservoir.n_soil_layers = %i\n", cfe_bmi_data_ptr->soil_reservoir.n_soil_layers); + Log(INFO, "Success in CFE BMI Initialization\n"); return BMI_SUCCESS; } @@ -1341,7 +1231,7 @@ static int Update (Bmi *self) // double current_time, end_time; // self->get_current_time(self, ¤t_time); // self->get_end_time(self, &end_time); -// printf("end time: %lf\n", end_time); +// Log( DEBUG, "end time: %lf\n", end_time); // if (current_time >= end_time) { // return BMI_FAILURE; // } @@ -1398,7 +1288,7 @@ static int Update_until (Bmi *self, double t) } frac = n_steps - (int)n_steps; if (frac > 0){ - printf("WARNING: CFE trying to update a fraction of a timestep\n"); + Log( WARN, "WARNING: CFE trying to update a fraction of a timestep\n"); // change timestep to remaining fraction & call update() cfe_ptr->time_step_size = frac * dt; @@ -2155,8 +2045,8 @@ static int Get_state_var_names (Bmi *self, char ** names) //-------------------------------- // Option to print all the names //-------------------------------- - // if (i==0) printf(" State variable names:"); - // printf(" var name[%d] = %s\n", i, names[i]); + // if (i==0) Log( WARN, " State variable names:"); + // Log( WARN, " var name[%d] = %s\n", i, names[i]); } return BMI_SUCCESS; @@ -2189,8 +2079,8 @@ static int Get_state_var_types (Bmi *self, char ** types) //-------------------------------- // Option to print all the types //-------------------------------- - // if (i==0) printf(" State var_types:"); - // printf(" var type[%d] = %s\n", i, types[i]); + // if (i==0) Log( WARN, " State var_types:"); + // Log( WARN, " var type[%d] = %s\n", i, types[i]); } return BMI_SUCCESS; @@ -3105,11 +2995,11 @@ extern void initialize_volume_trackers(cfe_state_struct* cfe_ptr){ /**************************************************************************/ /**************************************************************************/ extern void print_cfe_flux_header(){ - printf("# , hourly , direct, giuh ,lateral, base, total, storage, ice fraction, ice fraction \n"); - printf("Time [h],rainfall [mm],runoff [mm],runoff [mm],flow [mm],flow [mm],discharge [mm],storage [mm],schaake [mm],xinan [-]\n"); + Log(INFO, "# , hourly , direct, giuh ,lateral, base, total, storage, ice fraction, ice fraction \n"); + Log(INFO, "Time [h],rainfall [mm],runoff [mm],runoff [mm],flow [mm],flow [mm],discharge [mm],storage [mm],schaake [mm],xinan [-]\n"); } extern void print_cfe_flux_at_timestep(cfe_state_struct* cfe_ptr){ - printf("%d, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf\n", + Log(INFO, "%d, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf, %lf\n", cfe_ptr->current_time_step, cfe_ptr->timestep_rainfall_input_m*1000.0, *cfe_ptr->flux_output_direct_runoff_m*1000.0, @@ -3153,16 +3043,16 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ global_residual = cfe_ptr->vol_struct.volstart + cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.volout - volend - vol_end_giuh; - printf("GLOBAL MASS BALANCE\n"); - printf(" initial volume: %8.4lf m\n",cfe_ptr->vol_struct.volstart); - printf(" volume input: %8.4lf m\n",cfe_ptr->vol_struct.volin); - printf(" volume output: %8.4lf m\n",cfe_ptr->vol_struct.volout); - printf(" final volume: %8.4lf m\n",volend); - printf(" residual: %6.4e m\n",global_residual); - if(cfe_ptr->vol_struct.volin>0.0) printf("global pct. err: %6.4e percent of inputs\n",global_residual/cfe_ptr->vol_struct.volin*100.0); - else printf("global pct. err: %6.4e percent of initial\n",global_residual/cfe_ptr->vol_struct.volstart*100.0); + Log(INFO, "GLOBAL MASS BALANCE\n"); + Log(INFO, " initial volume: %8.4lf m\n",cfe_ptr->vol_struct.volstart); + Log(INFO, " volume input: %8.4lf m\n",cfe_ptr->vol_struct.volin); + Log(INFO, " volume output: %8.4lf m\n",cfe_ptr->vol_struct.volout); + Log(INFO, " final volume: %8.4lf m\n",volend); + Log(INFO, " residual: %6.4e m\n",global_residual); + if(cfe_ptr->vol_struct.volin>0.0) Log(INFO, "global pct. err: %6.4e percent of inputs\n",global_residual/cfe_ptr->vol_struct.volin*100.0); + else Log(INFO, "global pct. err: %6.4e percent of initial\n",global_residual/cfe_ptr->vol_struct.volstart*100.0); if(!is_fabs_less_than_epsilon(global_residual,1.0e-12)) - printf("WARNING: GLOBAL MASS BALANCE CHECK FAILED\n"); + Log(WARN, "WARNING: GLOBAL MASS BALANCE CHECK FAILED\n"); /* xinanjiang_dev schaake_residual = cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.vol_sch_runoff - cfe_ptr->vol_struct.vol_sch_infilt; @@ -3173,66 +3063,66 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ if(!is_fabs_less_than_epsilon(schaake_residual,1.0e-12)) printf("WARNING: SCHAAKE PARTITIONING MASS BALANCE CHECK FAILED\n");*/ direct_residual = cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_infilt-cfe_ptr->vol_struct.vol_et_from_rain; - printf(" DIRECT RUNOFF MASS BALANCE\n"); - printf(" surface runoff: %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); - printf(" infiltration: %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); - printf(" vol_et_from_rain: %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_rain); - printf("direct residual: %6.4e m\n",direct_residual); // should equal 0.0 + Log(INFO, " DIRECT RUNOFF MASS BALANCE\n"); + Log(INFO, " surface runoff: %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); + Log(INFO, " infiltration: %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); + Log(INFO, " vol_et_from_rain: %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_rain); + Log(INFO, "direct residual: %6.4e m\n",direct_residual); // should equal 0.0 if(!is_fabs_less_than_epsilon(direct_residual,1.0e-12)) - printf("WARNING: DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILED\n"); + Log(WARN, "WARNING: DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILED\n"); /* xinanjiang_dev giuh_residual = cfe_ptr->vol_struct.vol_out_giuh - cfe_ptr->vol_struct.vol_sch_runoff - vol_end_giuh; */ giuh_residual = cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_out_giuh - vol_end_giuh; - printf(" GIUH MASS BALANCE\n"); + Log(INFO, " GIUH MASS BALANCE\n"); /* xinanjiang_dev - printf(" vol. into giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_runoff); */ - printf(" vol. into giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); - fprintf(stderr," vol. out giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_out_giuh); - fprintf(stderr," vol. end giuh q: %8.4lf m\n",cfe_ptr->vol_struct.vol_end_giuh); - printf(" giuh residual: %6.4e m\n",giuh_residual); // should equal zero + Log(INFO, " vol. into giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_runoff); */ + Log(INFO, " vol. into giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); + Log(ERROR, " vol. out giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_out_giuh); + Log(ERROR, " vol. end giuh q: %8.4lf m\n",cfe_ptr->vol_struct.vol_end_giuh); + Log(INFO, " giuh residual: %6.4e m\n",giuh_residual); // should equal zero if(!is_fabs_less_than_epsilon(giuh_residual,1.0e-12)) - printf("WARNING: GIUH MASS BALANCE CHECK FAILED\n"); + Log(WARN, "WARNING: GIUH MASS BALANCE CHECK FAILED\n"); /* xinanjiang_dev soil_residual=cfe_ptr->vol_struct.vol_soil_start + cfe_ptr->vol_struct.vol_sch_infilt - */ soil_residual=cfe_ptr->vol_struct.vol_soil_start + cfe_ptr->vol_struct.vol_infilt - cfe_ptr->vol_struct.vol_soil_to_lat_flow - vol_soil_end - cfe_ptr->vol_struct.vol_to_gw - cfe_ptr->vol_struct.vol_et_from_soil; - printf(" SOIL WATER CONCEPTUAL RESERVOIR MASS BALANCE\n"); - printf(" init soil vol: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_start); + Log(INFO, " SOIL WATER CONCEPTUAL RESERVOIR MASS BALANCE\n"); + Log(INFO, " init soil vol: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_start); /* xinanjiang_dev - printf(" vol. into soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_infilt); */ - printf(" vol. into soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); - printf("vol.soil2latflow: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_to_lat_flow); - printf(" vol. soil to gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_to_gw); - printf(" final vol. soil: %8.4lf m\n",vol_soil_end); - printf(" vol. et from soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_soil); - printf("vol. soil resid.: %6.4e m\n",soil_residual); + Log(INFO, " vol. into soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_infilt); */ + Log(INFO, " vol. into soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); + Log(INFO, "vol.soil2latflow: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_to_lat_flow); + Log(INFO, " vol. soil to gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_soil_to_gw); + Log(INFO, " final vol. soil: %8.4lf m\n",vol_soil_end); + Log(INFO, " vol. et from soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_soil); + Log(INFO, "vol. soil resid.: %6.4e m\n",soil_residual); if(!is_fabs_less_than_epsilon(soil_residual,1.0e-12)) - printf("WARNING: SOIL CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + Log(WARN, "WARNING: SOIL CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); nash_residual=cfe_ptr->vol_struct.vol_in_nash - cfe_ptr->vol_struct.vol_out_nash - vol_in_nash_end; - printf(" NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE\n"); - printf(" vol. to nash: %8.4lf m\n",cfe_ptr->vol_struct.vol_in_nash); - printf(" vol. from nash: %8.4lf m\n",cfe_ptr->vol_struct.vol_out_nash); - printf(" final vol. nash: %8.4lf m\n",vol_in_nash_end); - printf("nash casc resid.: %6.4e m\n",nash_residual); + Log(INFO, " NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE\n"); + Log(INFO, " vol. to nash: %8.4lf m\n",cfe_ptr->vol_struct.vol_in_nash); + Log(INFO, " vol. from nash: %8.4lf m\n",cfe_ptr->vol_struct.vol_out_nash); + Log(INFO, " final vol. nash: %8.4lf m\n",vol_in_nash_end); + Log(INFO, "nash casc resid.: %6.4e m\n",nash_residual); if(!is_fabs_less_than_epsilon(nash_residual,1.0e-12)) - printf("WARNING: NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + Log(WARN, "WARNING: NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); gw_residual = cfe_ptr->vol_struct.vol_in_gw_start + cfe_ptr->vol_struct.vol_to_gw - cfe_ptr->vol_struct.vol_from_gw - vol_in_gw_end; - printf(" GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE\n"); - printf("init gw. storage: %8.4lf m\n",cfe_ptr->vol_struct.vol_in_gw_start); - printf(" vol to gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_to_gw); - printf(" vol from gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_from_gw); - printf("final gw.storage: %8.4lf m\n",vol_in_gw_end); - printf(" gw. residual: %6.4e m\n",gw_residual); + Log(INFO, " GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE\n"); + Log(INFO, "init gw. storage: %8.4lf m\n",cfe_ptr->vol_struct.vol_in_gw_start); + Log(INFO, " vol to gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_to_gw); + Log(INFO, " vol from gw: %8.4lf m\n",cfe_ptr->vol_struct.vol_from_gw); + Log(INFO, "final gw.storage: %8.4lf m\n",vol_in_gw_end); + Log(INFO, " gw. residual: %6.4e m\n",gw_residual); if(!is_fabs_less_than_epsilon(gw_residual,1.0e-12)) - fprintf(stderr,"WARNING: GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + Log(WARN, "WARNING: GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); } /**************************************************************************/ @@ -3354,7 +3244,7 @@ void itwo_alloc_cfe(int ***array, int rows, int cols) { int i, frows, fcols; if ((rows == 0) || (cols == 0)) { - printf("Error: Attempting to allocate array of size 0\n"); + Log(ERROR, "Error: Attempting to allocate array of size 0\n"); } frows = rows + 1; /* added one for FORTRAN numbering */ @@ -3379,7 +3269,7 @@ void dtwo_alloc_cfe(double ***array, int rows, int cols) { int i, frows, fcols; if ((rows == 0) || (cols == 0)) { - printf("Error: Attempting to allocate array of size 0\n"); + Log(ERROR, "Error: Attempting to allocate array of size 0\n"); } frows = rows + 1; /* added one for FORTRAN numbering */ @@ -3405,7 +3295,7 @@ void d_alloc_cfe(double **var, int size) { *var = (double *) malloc(size * sizeof(double)); if (*var == NULL) { - printf("Problem allocating memory for array in d_alloc.\n"); + Log(ERROR, "Problem allocating memory for array in d_alloc.\n"); return; } else memset(*var, 0, size * sizeof(double)); @@ -3417,7 +3307,7 @@ void i_alloc_cfe(int **var, int size) { *var = (int *) malloc(size * sizeof(int)); if (*var == NULL) { - printf("Problem allocating memory in i_alloc\n"); + Log(ERROR, "Problem allocating memory in i_alloc\n"); return; } else memset(*var, 0, size * sizeof(int)); diff --git a/src/cfe.c b/src/cfe.c index 80403929..b8be0606 100644 --- a/src/cfe.c +++ b/src/cfe.c @@ -1,4 +1,5 @@ #include "cfe.h" +#include "logger.h" #define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; }) #define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; }) @@ -131,8 +132,8 @@ extern void cfe( } else { - fprintf(stderr,"Problem, must specify one of Schaake of Xinanjiang partitioning scheme.\n"); - fprintf(stderr,"Program terminating.\n"); + Log(FATAL, "Problem, must specify one of Schaake of Xinanjiang partitioning scheme.\n"); + Log(FATAL, "Program terminating.\n"); exit(-1); // note -1 is arbitrary #############BOMB################ NEW FLO } } @@ -158,10 +159,10 @@ extern void cfe( #ifdef DEBUG /* xinanjiang_dev - printf("After Schaake function: rain:%8.5lf mm runoff:%8.5lf mm infiltration:%8.5lf mm residual:%e m\n", + Log(DEBUG, "After Schaake function: rain:%8.5lf mm runoff:%8.5lf mm infiltration:%8.5lf mm residual:%e m\n", timestep_rainfall_input_m,Schaake_output_runoff_m*1000.0,infiltration_depth_m*1000.0, timestep_rainfall_input_m-Schaake_output_runoff_m-infiltration_depth_m); */ - printf("After direct runoff function: rain:%8.5lf mm runoff:%8.5lf mm infiltration:%8.5lf mm residual:%e m\n", + Log(DEBUG, "After direct runoff function: rain:%8.5lf mm runoff:%8.5lf mm infiltration:%8.5lf mm residual:%e m\n", timestep_rainfall_input_m,direct_output_runoff_m*1000.0,infiltration_depth_m*1000.0, timestep_rainfall_input_m-direct_output_runoff_m-infiltration_depth_m); #endif @@ -230,13 +231,13 @@ extern void cfe( if(flux_from_deep_gw_to_chan_m > gw_reservoir_struct->storage_m) { flux_from_deep_gw_to_chan_m=gw_reservoir_struct->storage_m; // TODO: set a flag when flux larger than storage - printf("WARNING: Groundwater flux larger than storage \n"); + Log(WARN, "WARNING: Groundwater flux larger than storage \n"); } massbal_struct->vol_from_gw+=flux_from_deep_gw_to_chan_m; // in the instance of calling the gw reservoir the secondary flux should be zero- verify - if(is_fabs_less_than_epsilon(secondary_flux,1.0e-09)==FALSE) printf("problem with nonzero flux point 1\n"); + if(is_fabs_less_than_epsilon(secondary_flux,1.0e-09)==FALSE) Log(ERROR, "problem with nonzero flux point 1\n"); // adjust state of deep groundwater conceptual nonlinear reservoir diff --git a/src/logger.c b/src/logger.c index b6cda190..439ccb47 100644 --- a/src/logger.c +++ b/src/logger.c @@ -29,14 +29,18 @@ char* createTimestamp() { struct tm* timeinfo = gmtime(&now); strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S:", timeinfo); - snprintf(buffer1, sizeof(buffer1), "%03d/", (int)millis); + snprintf(buffer1, sizeof(buffer1), "%03d", (int)millis); strcat(buffer, buffer1); return buffer; } -void SetLogPreferences(Logger* logger, LogLevel level) { - logger->logLevel = level; +void SetLogPreferences(Logger* logger) { +#ifdef LOG_LEVEL + logger->logLevel = LOG_LEVEL; +#else + logger->logLevel = INFO; +#endif // get the log file path char * log_file_path; @@ -79,12 +83,21 @@ void SetLogPreferences(Logger* logger, LogLevel level) { Logger* GetInstance() { if (loggerInstance == NULL) { loggerInstance = (Logger*)malloc(sizeof(Logger)); - SetLogPreferences(loggerInstance, NONE); + SetLogPreferences(loggerInstance); } return loggerInstance; } -void Log(Logger* logger, const char* message, LogLevel messageLevel) { +void Log(LogLevel messageLevel, const char* message, ...) { + Logger* logger = GetInstance(); + va_list arglist; + va_start(arglist, message); + + int length = vsnprintf(NULL, 0, message, arglist); + char *buffer = malloc(length * sizeof *buffer); + vsnprintf(buffer, length, message, arglist); + va_end(arglist); + if (messageLevel >= logger->logLevel) { const char* logType; switch (messageLevel) { @@ -98,13 +111,15 @@ void Log(Logger* logger, const char* message, LogLevel messageLevel) { char final_message[1024]; snprintf(final_message, sizeof(final_message), "%s %s %s%s\n", - createTimestamp(), module_name[CFE], logType, message); + createTimestamp(), module_name[CFE], logType, buffer); if (logger->logFile != NULL) { fprintf(logger->logFile, "%s", final_message); fflush(logger->logFile); } } + + free(buffer); } LogLevel GetLogLevel(const char* logLevel) { @@ -116,20 +131,19 @@ LogLevel GetLogLevel(const char* logLevel) { return NONE; } -void setup_logger(void) { - Logger* logger = GetInstance(); - - Log(logger, "Sample Log for LogLevel::ERROR", ERROR); - Log(logger, "Sample Log for LogLevel::FATAL", FATAL); - Log(logger, "Sample Log for LogLevel::WARN", WARN); - Log(logger, "Sample Log for LogLevel::INFO", INFO); +void setup_logger() { + LogLevel level = ERROR; + Log(ERROR, "Sample Log for LogLevel::%d", level); + Log(FATAL, "Sample Log for LogLevel::FATAL"); + Log(WARN, "Sample Log for LogLevel::WARN"); + Log(INFO, "Sample Log for LogLevel::INFO"); const char* multiline_log = "First line of multiline log:\n" " Indented second line of multiline log\n" " Indented third line of multiline log\n" " Indented fourth line of multiline log"; - Log(logger, multiline_log, INFO); + Log(INFO, multiline_log); - Log(logger, "Sample Log for LogLevel::DEBUG", DEBUG); + Log(DEBUG, "Sample Log for LogLevel::DEBUG"); } From 59024887e1b9703c3594dbf289bee05ad80df096 Mon Sep 17 00:00:00 2001 From: "areg.amirkhanian" Date: Mon, 2 Dec 2024 16:02:33 -0800 Subject: [PATCH 09/50] Logger optimization --- src/logger.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/logger.c b/src/logger.c index 439ccb47..cc3bcafe 100644 --- a/src/logger.c +++ b/src/logger.c @@ -48,13 +48,13 @@ void SetLogPreferences(Logger* logger) { logger->logFile = fopen(log_file_path, "a"); if (logger->logFile == NULL) { - printf("Can't Open Log File\n"); + printf("Can't Open Log File for CFE\n"); // create local log directory and file const char* logFileDir = "./run_logs/ngen_"; char fullPath[256]; snprintf(fullPath, sizeof(fullPath), "%s%s/", logFileDir, createTimestamp()); - printf("Log File Directory: %s\n", fullPath); + printf("CFE Log File Directory: %s\n", fullPath); char mkdir_cmd[512]; snprintf(mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p %s", fullPath); @@ -62,21 +62,21 @@ void SetLogPreferences(Logger* logger) { if (status == -1) { fprintf(stderr, "Error: %s\n", strerror(errno)); } else { - printf("Directories are created\n"); + printf("Directories are created for CFE\n"); } char logFilePath[512]; snprintf(logFilePath, sizeof(logFilePath), "%scfe_log.txt", fullPath); logger->logFile = fopen(logFilePath, "w"); if (logger->logFile == NULL) { - fprintf(stderr, "Can't Open local Log File\n"); + fprintf(stderr, "Can't Open local Log File for CFE\n"); } else { - printf("Log File Path: %s\n", logFilePath); + printf("CFE Log File Path: %s\n", logFilePath); } } else { - printf("Log File Path: %s\n", log_file_path); + printf("CFE Log File Path: %s\n", log_file_path); } } From 726ffb336ea4c75c9f0d44ff5b1b262972ad1101 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 3 Dec 2024 08:07:06 -0800 Subject: [PATCH 10/50] Added release number, date and commit hash of official release --- version.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 version.txt diff --git a/version.txt b/version.txt new file mode 100644 index 00000000..60ef0128 --- /dev/null +++ b/version.txt @@ -0,0 +1,3 @@ +version=1.0.0 +date=2024-11-01 +commit=c4f731ac683cd30135f4401b80329da8f9f6a0c0 From 2945134a6088f5c5fffb5b54800ca83c862465c7 Mon Sep 17 00:00:00 2001 From: "areg.amirkhanian" Date: Mon, 16 Dec 2024 15:13:41 -0800 Subject: [PATCH 11/50] Adding feature for dynamically setting log level systemwide --- include/logger.h | 1 - src/logger.c | 57 ++++++++++++++++++++---------------------------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/include/logger.h b/include/logger.h index 75324820..2255880c 100644 --- a/include/logger.h +++ b/include/logger.h @@ -61,6 +61,5 @@ void SetLogPreferences(Logger* logger); void Log(LogLevel messageLevel, const char* message, ...); LogLevel GetLogLevel(const char* logLevel); char* createTimestamp(); -void setup_logger(void); #endif diff --git a/src/logger.c b/src/logger.c index cc3bcafe..21077a2f 100644 --- a/src/logger.c +++ b/src/logger.c @@ -36,11 +36,15 @@ char* createTimestamp() { } void SetLogPreferences(Logger* logger) { -#ifdef LOG_LEVEL - logger->logLevel = LOG_LEVEL; -#else - logger->logLevel = INFO; -#endif + // get the log level for CFE module + char * logLevel = getenv("cfe_ll"); + if (strlen(logLevel) != 0) { + logger->logLevel = GetLogLevel(logLevel); + } + else { + logger->logLevel = INFO; + } + printf("CFE module's log level is set at: %s\n", logLevel); // get the log file path char * log_file_path; @@ -88,6 +92,20 @@ Logger* GetInstance() { return loggerInstance; } +const char* getLogLevelString(LogLevel level) { + const char* logType; + switch (level) { + case FATAL: logType = "FATAL "; break; + case DEBUG: logType = "DEBUG "; break; + case INFO: logType = "INFO "; break; + case WARN: logType = "WARN "; break; + case ERROR: logType = "ERROR "; break; + default: logType = "NONE "; break; + } + + return logType; +} + void Log(LogLevel messageLevel, const char* message, ...) { Logger* logger = GetInstance(); va_list arglist; @@ -99,19 +117,9 @@ void Log(LogLevel messageLevel, const char* message, ...) { va_end(arglist); if (messageLevel >= logger->logLevel) { - const char* logType; - switch (messageLevel) { - case FATAL: logType = "FATAL "; break; - case DEBUG: logType = "DEBUG "; break; - case INFO: logType = "INFO "; break; - case WARN: logType = "WARN "; break; - case ERROR: logType = "ERROR "; break; - default: logType = "NONE "; break; - } - char final_message[1024]; snprintf(final_message, sizeof(final_message), "%s %s %s%s\n", - createTimestamp(), module_name[CFE], logType, buffer); + createTimestamp(), module_name[CFE], getLogLevelString(messageLevel), buffer); if (logger->logFile != NULL) { fprintf(logger->logFile, "%s", final_message); @@ -130,20 +138,3 @@ LogLevel GetLogLevel(const char* logLevel) { if (strcmp(logLevel, "FATAL") == 0) return ERROR; return NONE; } - -void setup_logger() { - LogLevel level = ERROR; - Log(ERROR, "Sample Log for LogLevel::%d", level); - Log(FATAL, "Sample Log for LogLevel::FATAL"); - Log(WARN, "Sample Log for LogLevel::WARN"); - Log(INFO, "Sample Log for LogLevel::INFO"); - - const char* multiline_log = - "First line of multiline log:\n" - " Indented second line of multiline log\n" - " Indented third line of multiline log\n" - " Indented fourth line of multiline log"; - Log(INFO, multiline_log); - - Log(DEBUG, "Sample Log for LogLevel::DEBUG"); -} From a7e84514408660c4c58642dadc79687c0f80e2a0 Mon Sep 17 00:00:00 2001 From: "areg.amirkhanian" Date: Fri, 10 Jan 2025 12:02:46 -0800 Subject: [PATCH 12/50] changed the way logLevel char pointer is checked for NULL --- src/logger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logger.c b/src/logger.c index 21077a2f..4b4ab329 100644 --- a/src/logger.c +++ b/src/logger.c @@ -38,7 +38,7 @@ char* createTimestamp() { void SetLogPreferences(Logger* logger) { // get the log level for CFE module char * logLevel = getenv("cfe_ll"); - if (strlen(logLevel) != 0) { + if (logLevel != NULL) { logger->logLevel = GetLogLevel(logLevel); } else { From 38974052ac465d95eb7f17a1a79790db15fe42fa Mon Sep 17 00:00:00 2001 From: "peter.a.kronenberg" Date: Tue, 14 Jan 2025 18:34:49 -0500 Subject: [PATCH 13/50] Revert "changed the way logLevel char pointer is checked for NULL" This reverts commit a7e84514408660c4c58642dadc79687c0f80e2a0. --- src/logger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logger.c b/src/logger.c index 4b4ab329..21077a2f 100644 --- a/src/logger.c +++ b/src/logger.c @@ -38,7 +38,7 @@ char* createTimestamp() { void SetLogPreferences(Logger* logger) { // get the log level for CFE module char * logLevel = getenv("cfe_ll"); - if (logLevel != NULL) { + if (strlen(logLevel) != 0) { logger->logLevel = GetLogLevel(logLevel); } else { From 8ce0fd1e69690c662fda757f0c37c49400bf9458 Mon Sep 17 00:00:00 2001 From: "peter.a.kronenberg" Date: Tue, 14 Jan 2025 18:34:49 -0500 Subject: [PATCH 14/50] Revert "Adding feature for dynamically setting log level systemwide" This reverts commit 2945134a6088f5c5fffb5b54800ca83c862465c7. --- include/logger.h | 1 + src/logger.c | 57 ++++++++++++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/include/logger.h b/include/logger.h index 2255880c..75324820 100644 --- a/include/logger.h +++ b/include/logger.h @@ -61,5 +61,6 @@ void SetLogPreferences(Logger* logger); void Log(LogLevel messageLevel, const char* message, ...); LogLevel GetLogLevel(const char* logLevel); char* createTimestamp(); +void setup_logger(void); #endif diff --git a/src/logger.c b/src/logger.c index 21077a2f..cc3bcafe 100644 --- a/src/logger.c +++ b/src/logger.c @@ -36,15 +36,11 @@ char* createTimestamp() { } void SetLogPreferences(Logger* logger) { - // get the log level for CFE module - char * logLevel = getenv("cfe_ll"); - if (strlen(logLevel) != 0) { - logger->logLevel = GetLogLevel(logLevel); - } - else { - logger->logLevel = INFO; - } - printf("CFE module's log level is set at: %s\n", logLevel); +#ifdef LOG_LEVEL + logger->logLevel = LOG_LEVEL; +#else + logger->logLevel = INFO; +#endif // get the log file path char * log_file_path; @@ -92,20 +88,6 @@ Logger* GetInstance() { return loggerInstance; } -const char* getLogLevelString(LogLevel level) { - const char* logType; - switch (level) { - case FATAL: logType = "FATAL "; break; - case DEBUG: logType = "DEBUG "; break; - case INFO: logType = "INFO "; break; - case WARN: logType = "WARN "; break; - case ERROR: logType = "ERROR "; break; - default: logType = "NONE "; break; - } - - return logType; -} - void Log(LogLevel messageLevel, const char* message, ...) { Logger* logger = GetInstance(); va_list arglist; @@ -117,9 +99,19 @@ void Log(LogLevel messageLevel, const char* message, ...) { va_end(arglist); if (messageLevel >= logger->logLevel) { + const char* logType; + switch (messageLevel) { + case FATAL: logType = "FATAL "; break; + case DEBUG: logType = "DEBUG "; break; + case INFO: logType = "INFO "; break; + case WARN: logType = "WARN "; break; + case ERROR: logType = "ERROR "; break; + default: logType = "NONE "; break; + } + char final_message[1024]; snprintf(final_message, sizeof(final_message), "%s %s %s%s\n", - createTimestamp(), module_name[CFE], getLogLevelString(messageLevel), buffer); + createTimestamp(), module_name[CFE], logType, buffer); if (logger->logFile != NULL) { fprintf(logger->logFile, "%s", final_message); @@ -138,3 +130,20 @@ LogLevel GetLogLevel(const char* logLevel) { if (strcmp(logLevel, "FATAL") == 0) return ERROR; return NONE; } + +void setup_logger() { + LogLevel level = ERROR; + Log(ERROR, "Sample Log for LogLevel::%d", level); + Log(FATAL, "Sample Log for LogLevel::FATAL"); + Log(WARN, "Sample Log for LogLevel::WARN"); + Log(INFO, "Sample Log for LogLevel::INFO"); + + const char* multiline_log = + "First line of multiline log:\n" + " Indented second line of multiline log\n" + " Indented third line of multiline log\n" + " Indented fourth line of multiline log"; + Log(INFO, multiline_log); + + Log(DEBUG, "Sample Log for LogLevel::DEBUG"); +} From c6bf363255a8d2c79a698d6dbb0301642d9cec9e Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Sun, 26 Jan 2025 08:58:52 -0800 Subject: [PATCH 15/50] Updated version file for release rc-1.1.0 --- version.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.txt b/version.txt index 60ef0128..d9c5a614 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -version=1.0.0 -date=2024-11-01 -commit=c4f731ac683cd30135f4401b80329da8f9f6a0c0 +version=rc-1.1.0 +date=2025-01-26 +commit=a2bbe71e271d7c5d48e72147aa61f835986a69ab From 162eded1cc63449ef5a742be183b17b85e8038c7 Mon Sep 17 00:00:00 2001 From: "yuqiong.liu" Date: Tue, 28 Jan 2025 09:53:46 -0500 Subject: [PATCH 16/50] enhancement to allow flexible order in cfe config --- src/bmi_cfe.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index ee3dc583..7e05ab82 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -629,7 +629,9 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) } if (strcmp(param_key, "gw_storage") == 0) { model->gw_reservoir.gw_storage = strtod(param_value, NULL); - model->gw_reservoir.storage_m = model->gw_reservoir.gw_storage * model->gw_reservoir.storage_max_m; //edited by RLM to fix units from [m/m] to [m] + // YLiu: move the calculation of GW storage out of the FOR loop so that the position of gw_storage + // relative to that of max_gw_storage in the config file would not matter + //model->gw_reservoir.storage_m = model->gw_reservoir.gw_storage * model->gw_reservoir.storage_max_m; //edited by RLM to fix units from [m/m] to [m] is_gw_storage_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { @@ -705,6 +707,11 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) continue; } + // compute gw storage in meters + if ((is_gw_storage_set == TRUE) && (is_gw_max_set == TRUE)) { + model->gw_reservoir.storage_m = model->gw_reservoir.gw_storage * model->gw_reservoir.storage_max_m; + } + /*-------------------- Root zone AET development -rlm -----------------------*/ if (strcmp(param_key, "aet_rootzone") == 0) { From 446a7d9b0ef6489b7ea44307de9bce17862f025f Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 28 Jan 2025 07:18:13 -0800 Subject: [PATCH 17/50] Update version file for release rc-1.1.0A --- version.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.txt b/version.txt index d9c5a614..4f92f887 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -version=rc-1.1.0 -date=2025-01-26 -commit=a2bbe71e271d7c5d48e72147aa61f835986a69ab +version=rc-1.1.0A +date=2025-01-28 +commit=0ce4e7ff53ce76ae9353eb35f28a1b9fe22cfa57 From a8cafe50a960d7e61cfc83e0815562b6609d19ed Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Fri, 31 Jan 2025 16:11:28 -0800 Subject: [PATCH 18/50] Update version file for Release 1.1.0 --- version.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.txt b/version.txt index 4f92f887..f99384a4 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -version=rc-1.1.0A -date=2025-01-28 -commit=0ce4e7ff53ce76ae9353eb35f28a1b9fe22cfa57 +version=1.1.0 +date=2025-01-31 +commit=0efb62356fea60b1d289c77d337d0828be5a2cd6 From 6e59657bddb8654e1ff7ca70a041275850a996af Mon Sep 17 00:00:00 2001 From: "yuqiong.liu" Date: Mon, 3 Feb 2025 17:07:03 -0500 Subject: [PATCH 19/50] fix cfe error --- src/bmi_cfe.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 7e05ab82..5b88e913 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -706,11 +706,6 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_verbosity_set = TRUE; continue; } - - // compute gw storage in meters - if ((is_gw_storage_set == TRUE) && (is_gw_max_set == TRUE)) { - model->gw_reservoir.storage_m = model->gw_reservoir.gw_storage * model->gw_reservoir.storage_max_m; - } /*-------------------- Root zone AET development -rlm -----------------------*/ if (strcmp(param_key, "aet_rootzone") == 0) { @@ -864,6 +859,12 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) Log(ERROR, "Config param 'gw_storage' not found in config file\n"); return BMI_FAILURE; } + + // compute gw storage in meters + if ((is_gw_storage_set == TRUE) && (is_gw_max_set == TRUE)) { + model->gw_reservoir.storage_m = model->gw_reservoir.gw_storage * model->gw_reservoir.storage_max_m; + } + if (is_num_timesteps_set == FALSE && strcmp(model->forcing_file, "BMI")) { Log(ERROR, "Config param 'num_timesteps' not found in config file\n"); return BMI_FAILURE; From 810978f04ac533134d6ff990c10939af4d063d79 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Mon, 3 Feb 2025 14:18:06 -0800 Subject: [PATCH 20/50] Update version file for master hotfix 1.1.1 --- version.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.txt b/version.txt index f99384a4..58928c6e 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -version=1.1.0 -date=2025-01-31 -commit=0efb62356fea60b1d289c77d337d0828be5a2cd6 +version=1.1.1 +date=2025-02-03 +commit=4b563921424378aae64059d20d0289963a250867 From dc6d5e7cd4b554c79023798eb97d1a16b93e56e5 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Thu, 6 Feb 2025 11:44:10 -0800 Subject: [PATCH 21/50] Added RTX licensing verbiage --- LICENSE | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/LICENSE b/LICENSE index a0230332..e87083b9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,34 @@ +Copyright 2025 Raytheon Company + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Licensed under: https://opensource.org/license/bsd-2-clause + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +All rights reserved. Based on Government sponsored work under contract GS-35F-204GA. + +----------------- + +Copyright 2025 Raytheon Company + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Licensed under: https://opensource.org/license/bsd-2-clause + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +All rights reserved. Based on Government sponsored work under contract GS-35F-204GA. + +----------------- + + “Software code created by U.S. Government employees is not subject to copyright in the United States (17 U.S.C. §105). The United States/Department of Commerce reserve all rights to seek and obtain copyright protection in countries other From 0843fa03af8a1b78d6db8397ff6e1ace7033e358 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Thu, 6 Feb 2025 12:40:36 -0800 Subject: [PATCH 22/50] Remove duplicated RTX licensing verbiage --- LICENSE | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/LICENSE b/LICENSE index e87083b9..76542965 100644 --- a/LICENSE +++ b/LICENSE @@ -13,21 +13,6 @@ All rights reserved. Based on Government sponsored work under contract GS-35F-20 ----------------- -Copyright 2025 Raytheon Company - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -Licensed under: https://opensource.org/license/bsd-2-clause - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -All rights reserved. Based on Government sponsored work under contract GS-35F-204GA. - ------------------ - “Software code created by U.S. Government employees is not subject to copyright in the United States (17 U.S.C. §105). The United States/Department of Commerce From 564e25fb708889c229b4e9ba4ef4512948a98ec5 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Wed, 12 Feb 2025 20:05:50 -0800 Subject: [PATCH 23/50] Update version file for release rc-1.2.0 on 2025-02-12 --- version.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.txt b/version.txt index 58928c6e..89689b1b 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -version=1.1.1 -date=2025-02-03 -commit=4b563921424378aae64059d20d0289963a250867 +version=rc-1.2.0 +date=2025-02-12 +commit=0626082731cf350d654ce46ef52c6c7226d2bf8e From bbac306d0962fc0bdedf3a71948f7857bbe73b61 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 25 Feb 2025 16:28:44 -0800 Subject: [PATCH 24/50] Update version file for release 1.2.0 on 2025-02-25 --- version.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/version.txt b/version.txt index 89689b1b..071945d4 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ -version=rc-1.2.0 -date=2025-02-12 -commit=0626082731cf350d654ce46ef52c6c7226d2bf8e +version=1.2.0 +date=2025-02-25 +commit=90c72a7b40972f872d28d750767d504076e0b202 From 6e6c6e6241e80ab53f8dc464730bd56f90785f95 Mon Sep 17 00:00:00 2001 From: "peter.a.kronenberg" Date: Wed, 19 Mar 2025 15:55:56 -0400 Subject: [PATCH 25/50] Delete version.txt --- version.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 version.txt diff --git a/version.txt b/version.txt deleted file mode 100644 index 071945d4..00000000 --- a/version.txt +++ /dev/null @@ -1,3 +0,0 @@ -version=1.2.0 -date=2025-02-25 -commit=90c72a7b40972f872d28d750767d504076e0b202 From 01314ded14ad2742d987f7c84dc9f7ad64a6a543 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Thu, 24 Apr 2025 21:57:59 -0700 Subject: [PATCH 26/50] Refactor and add env var checking --- include/logger.h | 64 +------ src/logger.c | 456 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 357 insertions(+), 163 deletions(-) diff --git a/include/logger.h b/include/logger.h index 75324820..381c0fd5 100644 --- a/include/logger.h +++ b/include/logger.h @@ -1,66 +1,20 @@ #ifndef LOGGER_H #define LOGGER_H -#include -#include -#include -#include #include // for variable args: va_list typedef enum { NONE = 0, - DEBUG, - INFO, - FATAL, - WARN, - ERROR, + DEBUG = 1, + INFO = 2, + WARN = 3, // CM: Delete when Kyle is ready +// WARNING = 3, // CM: Change when Kyle is ready + ERROR = 4, // CM: Delete when Kyle is ready +// SEVERE = 4, // CM: Change when Kyle is ready + FATAL = 5, } LogLevel; -typedef enum { - NGEN, - NOAHOWP, - SNOW17, - UEB, - CFE, - SACSMA, - LASAM, - SMP, - SFT, - TROUTE, - SCHISM, - SFINCS, - GC2D, - TOPOFLOW, - MODULE_COUNT -} LoggingModule; - -static const char* module_name[MODULE_COUNT] = { - "NGEN ", - "NOAHOWP ", - "SNOW17 ", - "UEB ", - "CFE ", - "SACSMA ", - "LASAM ", - "SMP ", - "SFT ", - "TROUTE ", - "SCHISM ", - "SFINCS ", - "GC2D ", - "TOPOFLOW" -}; - -typedef struct { - LogLevel logLevel; - FILE* logFile; -} Logger; - -Logger* GetInstance(); -void SetLogPreferences(Logger* logger); +// Public Methods void Log(LogLevel messageLevel, const char* message, ...); -LogLevel GetLogLevel(const char* logLevel); -char* createTimestamp(); -void setup_logger(void); -#endif +#endif // LOGGER_H diff --git a/src/logger.c b/src/logger.c index cc3bcafe..c13db43d 100644 --- a/src/logger.c +++ b/src/logger.c @@ -1,149 +1,389 @@ +#include "logger.h" #include #include #include -#include +#include +#include #include #include -#include -#include "logger.h" +#include +#include +#include +#include + +// Define Environment Variable Names +#define MODULE_NAME "CFE" +#define EV_MODULE_LOGLEVEL "CFE_LOGLEVEL" // This modules log level +#define EV_MODULE_LOGFILEPATH "CFE_LOGFILEPATH" // This modules log full log filename +#define EV_NGEN_LOGFILEPATH "NGEN_LOG_FILE_PATH" // ngen log file + +#define EV_EWTS_LOGGING "NGEN_EWTS_LOGGING" // Enable/disable of Error Warning and Trapping System + +#define DS "/" // Directory separator +#define LOG_DIR_NGENCERF "/ngencerf/data" // ngenCERF log directory +#define LOG_DIR_DEFAULT "run-logs" // Default subdir if ngen log file env not found +#define LOG_FILE_EXT "log" +#define LOG_MODULE_NAME_LEN 8 // Width of module name for log entries + +bool ewtsEnabled = true; +bool openedAppendMode = true; +bool loggerInitialized = false; +FILE *logFile = NULL; +char logFilePath[1024] = ""; +LogLevel logLevel = INFO; +char moduleName[LOG_MODULE_NAME_LEN+1] = ""; +bool envLogLevelLogged = false; + +void TrimString(const char *input, char *output, size_t outputSize) { + if (!input || !output || outputSize == 0) { + if (output && outputSize > 0) output[0] = '\0'; + return; + } + + // Skip leading whitespace + while (isspace((unsigned char)*input)) { + input++; + } + + size_t len = strlen(input); + if (len == 0) { + output[0] = '\0'; + return; + } + + // Find the end of the string and move backward past trailing whitespace + const char *end = input + len - 1; + while (end > input && isspace((unsigned char)*end)) { + end--; + } + + size_t trimmedLen = end - input + 1; + if (trimmedLen >= outputSize) { + trimmedLen = outputSize - 1; + } + + strncpy(output, input, trimmedLen); + output[trimmedLen] = '\0'; +} + +void SetLogModuleName(void) { -#define MODULE_COUNT 14 + // Copy MODULE_NAME to a string variable + char src[20]; + strncpy(src, MODULE_NAME, sizeof(src)); + src[sizeof(src) - 1] = '\0'; // ensure null termination + // Convert to uppercase and copy, up to width or null terminator + size_t i = 0; + for (; i < LOG_MODULE_NAME_LEN && src[i] != '\0'; i++) { + moduleName[i] = toupper((unsigned char)src[i]); + } -Logger* loggerInstance = NULL; + // Pad with spaces if needed + for (; i < LOG_MODULE_NAME_LEN; i++) { + moduleName[i] = ' '; + } + moduleName[LOG_MODULE_NAME_LEN] = '\0'; // null-terminate +} -long long timeInMilliseconds(void) { +void CreateTimestamp(char *buffer, size_t size, bool appendMS, bool iso) { struct timeval tv; + gettimeofday(&tv, NULL); + + struct tm utc_tm; + gmtime_r(&tv.tv_sec, &utc_tm); - gettimeofday(&tv,NULL); - return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); + char base[32]; + if (iso) { + strftime(base, sizeof(base), "%Y-%m-%dT%H:%M:%S", &utc_tm); + } else { + strftime(base, sizeof(base), "%Y%m%dT%H%M%S", &utc_tm); + } + + if (appendMS) { + snprintf(buffer, size, "%s.%03ld", base, tv.tv_usec / 1000); + } else { + snprintf(buffer, size, "%s", base); + } } -char* createTimestamp() { - static char buffer[256]; - char buffer1[256]; - time_t now = time(NULL); - - long millis = (long)(timeInMilliseconds() % 1000); - +bool DirectoryExists(const char *path) { + struct stat info; + return (stat(path, &info) == 0 && (info.st_mode & S_IFDIR)); +} - struct tm* timeinfo = gmtime(&now); - strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S:", timeinfo); - snprintf(buffer1, sizeof(buffer1), "%03d", (int)millis); - strcat(buffer, buffer1); +bool CreateDirectory(const char *path) { + if (!DirectoryExists(path)) { + char cmd[512]; + snprintf(cmd, sizeof(cmd), "mkdir -p \"%s\"", path); + int status = system(cmd); + if (status == -1 || (WIFEXITED(status) && WEXITSTATUS(status) != 0)) { + fprintf(stderr, "Failed to create directory: %s\n", path); + return false; + } + } + return true; +} - return buffer; +bool LogFileReady(bool appendMode) { + if (logFile && !ferror(logFile)) { + fseek(logFile, 0, SEEK_END); + return true; + } else if (strlen(logFilePath) > 0) { + logFile = fopen(logFilePath, appendMode ? "a" : "w"); + if (logFile != NULL) { + openedAppendMode = appendMode; + if (loggerInitialized) Log(INFO, "Opened log file %s in %s mode\n", logFilePath, (appendMode ? "Append" : "Truncate")); + return true; + } + return false; + } + return false; } -void SetLogPreferences(Logger* logger) { -#ifdef LOG_LEVEL - logger->logLevel = LOG_LEVEL; -#else - logger->logLevel = INFO; -#endif - - // get the log file path - char * log_file_path; - log_file_path = getenv("NGEN_LOG_FILE_PATH"); - - logger->logFile = fopen(log_file_path, "a"); - if (logger->logFile == NULL) { - printf("Can't Open Log File for CFE\n"); - // create local log directory and file - const char* logFileDir = "./run_logs/ngen_"; - char fullPath[256]; - snprintf(fullPath, sizeof(fullPath), "%s%s/", logFileDir, createTimestamp()); - - printf("CFE Log File Directory: %s\n", fullPath); - - char mkdir_cmd[512]; - snprintf(mkdir_cmd, sizeof(mkdir_cmd), "mkdir -p %s", fullPath); - int status = system(mkdir_cmd); - if (status == -1) { - fprintf(stderr, "Error: %s\n", strerror(errno)); +/** + * Set the log file path name using the following pattern + * - Use the module log file if available (unset when first run by ngen), otherwise + * - Use ngen log file if available, otherwise + * - Use /ngencerf/data/run-logs//_ if available, othrewise + * - Use ~/run-logs//_ + * - Onced opened, save the full log path to the modules log environment variable so + * it is only opened once for each ngen run (vs for each catchment) + */ +void SetupLogFile(void) { + logFilePath[0] = '\0'; + bool appendEntries = true; + bool moduleLogEnvExists = false; + + const char *envVar = getenv(EV_MODULE_LOGFILEPATH); + if (envVar && envVar[0] != '\0') { + strncpy(logFilePath, envVar, sizeof(logFilePath) - 1); + moduleLogEnvExists = true; + } else { + envVar = getenv(EV_NGEN_LOGFILEPATH); + if (envVar && envVar[0] != '\0') { + strncpy(logFilePath, envVar, sizeof(logFilePath) - 1); } else { - printf("Directories are created for CFE\n"); - } + appendEntries = false; + char logFileDir[512]; + + if (DirectoryExists(LOG_DIR_NGENCERF)) { + snprintf(logFileDir, sizeof(logFileDir), "%s%s%s", LOG_DIR_NGENCERF, DS, LOG_DIR_DEFAULT); + } else { + const char *home = getenv("HOME"); // Get users home directly pathname + if (home) { + snprintf(logFileDir, sizeof(logFileDir), "%s%s%s", home, DS, LOG_DIR_DEFAULT); + } + else { + snprintf(logFileDir, sizeof(logFileDir), "~%s%s", DS, LOG_DIR_DEFAULT); + } + } + + if (CreateDirectory(logFileDir)) { + const char *envUsername = getenv("USER"); + if (envUsername) { + strncat(logFileDir, DS, sizeof(logFileDir) - strlen(logFileDir) - 1); + strncat(logFileDir, envUsername, sizeof(logFileDir) - strlen(logFileDir) - 1); + } else { + char dateStr[32]; + CreateTimestamp(dateStr, sizeof(dateStr), 0, 0); + strncat(logFileDir, DS, sizeof(logFileDir) - strlen(logFileDir) - 1); + strncat(logFileDir, dateStr, sizeof(logFileDir) - strlen(logFileDir) - 1); + } - char logFilePath[512]; - snprintf(logFilePath, sizeof(logFilePath), "%scfe_log.txt", fullPath); - logger->logFile = fopen(logFilePath, "w"); - if (logger->logFile == NULL) { - fprintf(stderr, "Can't Open local Log File for CFE\n"); + if (CreateDirectory(logFileDir)) { + char timestamp[32]; + CreateTimestamp(timestamp, sizeof(timestamp), 0, 0); + snprintf(logFilePath, sizeof(logFilePath), "%s%s%s_%s.%s", + logFileDir, DS, MODULE_NAME, timestamp, LOG_FILE_EXT); + } + } } - else { - printf("CFE Log File Path: %s\n", logFilePath); + } + + if (LogFileReady(appendEntries)) { + if (!moduleLogEnvExists) setenv(EV_MODULE_LOGFILEPATH, logFilePath, 1); + } else { + printf("Unable to open log file "); + if (strlen(logFilePath) > 0) { + printf("%s (Perhaps check permissions)\n", logFilePath); } + printf("Log entries will be written to stdout\n"); } - else { - printf("CFE Log File Path: %s\n", log_file_path); +} + +const char* ConvertLogLevelToString(LogLevel level) { + switch (level) { + case DEBUG: return "DEBUG "; + case INFO: return "INFO "; + case WARN: return "WARN "; // CM: Delete when Kyle is ready +// case WARNING: return "WARNING"; // CM: Add when Kyle is ready + case ERROR: return "ERROR "; // CM: Delete when Kyle is ready +// case SEVERE: return "SEVERE "; // CM: Add when Kyle is ready + case FATAL: return "FATAL "; + default: return "NONE "; } } -Logger* GetInstance() { - if (loggerInstance == NULL) { - loggerInstance = (Logger*)malloc(sizeof(Logger)); - SetLogPreferences(loggerInstance); +LogLevel ConvertStringToLogLevel(const char* str) { + char trimmedStr[20] = {0}; + TrimString(str, trimmedStr, sizeof(trimmedStr)); + if (strcasecmp(trimmedStr, "DEBUG") == 0) return DEBUG; + if (strcasecmp(trimmedStr, "INFO") == 0) return INFO; + if (strcasecmp(trimmedStr, "WARN") == 0) return WARN; // CM: Delete when Kyle is ready +// if (strcasecmp(trimmedStr, "WARNING") == 0) return WARNING; // CM: Change when Kyle is ready + if (strcasecmp(trimmedStr, "ERROR") == 0) return ERROR; // CM: Delete when Kyle is ready +// if (strcasecmp(trimmedStr, "SEVERE") == 0) return SEVERE; // CM: Change when Kyle is ready + if (strcasecmp(trimmedStr, "FATAL") == 0) return FATAL; + return NONE; +} + +bool IsEwtsEnabled(void) { + const char* ewtsStr = getenv(EV_EWTS_LOGGING); + if (ewtsStr && ewtsStr[0] != '\0') { + char trimmedStr[20] = {0}; + TrimString(ewtsStr, trimmedStr, sizeof(trimmedStr)); + bool ewtsEv = ((strcasecmp(trimmedStr, "ENABLE") == 0))? true:false; + if (ewtsEv != ewtsEnabled) { + ewtsEnabled = ewtsEv; + if (ewtsEnabled && loggerInitialized) { + char lMsg[100]; + snprintf(lMsg, sizeof(lMsg), "INFO ONLY: %s changed. Set to %s\n", EV_EWTS_LOGGING, trimmedStr); + Log(logLevel, lMsg); + } + } } - return loggerInstance; + return ewtsEnabled; } -void Log(LogLevel messageLevel, const char* message, ...) { - Logger* logger = GetInstance(); - va_list arglist; - va_start(arglist, message); - - int length = vsnprintf(NULL, 0, message, arglist); - char *buffer = malloc(length * sizeof *buffer); - vsnprintf(buffer, length, message, arglist); - va_end(arglist); - - if (messageLevel >= logger->logLevel) { - const char* logType; - switch (messageLevel) { - case FATAL: logType = "FATAL "; break; - case DEBUG: logType = "DEBUG "; break; - case INFO: logType = "INFO "; break; - case WARN: logType = "WARN "; break; - case ERROR: logType = "ERROR "; break; - default: logType = "NONE "; break; +void CheckLogLevel(void) { + const char* envLogLevel = getenv(EV_MODULE_LOGLEVEL); + if (envLogLevel && envLogLevel[0] != '\0') { + LogLevel envll = ConvertStringToLogLevel(envLogLevel); + if (envll != logLevel) { + logLevel = envll; + + char llStr[10]; + char llMsg[100]; + TrimString(ConvertLogLevelToString(logLevel), llStr, sizeof(llStr)); + snprintf(llMsg, sizeof(llMsg), "INFO ONLY: Log level changed in %s. Set to %s\n", EV_MODULE_LOGLEVEL, llStr); + if (loggerInitialized) Log(logLevel, llMsg); } + } +} + +void SetLogPreferences(void) { + + if (IsEwtsEnabled()) { + + // Set the module name used in log entries + SetLogModuleName(); + + // Set the log file path name and open it + SetupLogFile(); + loggerInitialized = true; + printf("Module %s Log File: %s\n", MODULE_NAME, logFilePath); + // This is an INFO message that always should be in the log but the + // logLevel could be different than INFO. Therefore use logLevel to + // ensure the message is recorded in the log + Log(logLevel, "INFO ONLY: Opened log file %s in %s mode\n", logFilePath, (openedAppendMode ? "Append" : "Truncate")); + Log(logLevel, "INFO ONLY: EWTS default ENABLED\n"); - char final_message[1024]; - snprintf(final_message, sizeof(final_message), "%s %s %s%s\n", - createTimestamp(), module_name[CFE], logType, buffer); + // Check for the log level environment variable for a log level + char llStr[10]; + char llMsg[80]; + TrimString(ConvertLogLevelToString(logLevel), llStr, sizeof(llStr)); + snprintf(llMsg,sizeof(llMsg), "INFO ONLY: Default log level is %s\n", llStr); + Log(logLevel, llMsg); + CheckLogLevel(); // Check for log level set in the environment variable - if (logger->logFile != NULL) { - fprintf(logger->logFile, "%s", final_message); - fflush(logger->logFile); - } + // FOR TESTING ONLY! Uncomment next line to generate test log entries + // WriteLogTestMsgs(); } - - free(buffer); } -LogLevel GetLogLevel(const char* logLevel) { - if (strcmp(logLevel, "DEBUG") == 0) return DEBUG; - if (strcmp(logLevel, "INFO") == 0) return INFO; - if (strcmp(logLevel, "WARN") == 0) return ERROR; - if (strcmp(logLevel, "ERROR") == 0) return ERROR; - if (strcmp(logLevel, "FATAL") == 0) return ERROR; - return NONE; +void TrimToOneNewline(char *str, size_t max_len) { + size_t len = strlen(str); + + // Strip trailing whitespace including \n, \r, spaces, tabs + while (len > 0 && isspace((unsigned char)str[len - 1])) { + str[--len] = '\0'; + } + + // Ensure room to add a newline + if (len + 1 < max_len) { + str[len] = '\n'; + str[len + 1] = '\0'; + } +} + +void Log(LogLevel messageLevel, const char* message, ...) { + if (IsEwtsEnabled()) { + if (!loggerInitialized) SetLogPreferences(); // Initialize logger on first call + + // Check for change in log level environment variable + CheckLogLevel(); + + if (messageLevel < logLevel) return; + + va_list arglist; + va_start(arglist, message); + va_list arglist_copy; + va_copy(arglist_copy, arglist); + + int length = vsnprintf(NULL, 0, message, arglist_copy); + va_end(arglist_copy); + + char *buffer = malloc(length + 1); + if (!buffer) return; // Always good to check + + vsnprintf(buffer, length + 1, message, arglist); + va_end(arglist); + + char timestamp[32]; + CreateTimestamp(timestamp, sizeof(timestamp), 1, 1); + + char logPrefix[128]; + snprintf(logPrefix, sizeof(logPrefix), "%s %s %s", timestamp, moduleName, ConvertLogLevelToString(messageLevel)); + + TrimToOneNewline(buffer, sizeof(buffer)); + char *line = strtok(buffer, "\n"); + if (LogFileReady(true)) { + while (line != NULL) { + fprintf(logFile, "%s %s\n", logPrefix, line); + line = strtok(NULL, "\n"); + } + fflush(logFile); + } else { + while (line != NULL) { + printf("%s %s\n", logPrefix, line); + line = strtok(NULL, "\n"); + } + fflush(stdout); + } + + free(buffer); + } } -void setup_logger() { - LogLevel level = ERROR; - Log(ERROR, "Sample Log for LogLevel::%d", level); - Log(FATAL, "Sample Log for LogLevel::FATAL"); - Log(WARN, "Sample Log for LogLevel::WARN"); - Log(INFO, "Sample Log for LogLevel::INFO"); +void WriteLogTestMsgs() { + + LogLevel savedLogLevel = logLevel; + + setenv(EV_MODULE_LOGLEVEL, "FATAL", 1); Log(FATAL, "Sample Log for LogLevel::FATAL"); + setenv(EV_MODULE_LOGLEVEL, "ERROR", 1); Log(ERROR, "Sample Log for LogLevel::ERROR"); + setenv(EV_MODULE_LOGLEVEL, "WARN", 1); Log(WARN, "Sample Log for LogLevel::WARN"); +// setenv(EV_MODULE_LOGLEVEL, "SEVERE", 1); Log(SEVERE, "Sample Log for LogLevel::SEVERE"); +// setenv(EV_MODULE_LOGLEVEL, "WARNING", 1); Log(WARNING, "Sample Log for LogLevel::WARNING"); + setenv(EV_MODULE_LOGLEVEL, "INFO", 1); Log(INFO, "Sample Log for LogLevel::INFO"); + setenv(EV_MODULE_LOGLEVEL, "DEBUG", 1); Log(DEBUG, "Sample Log for LogLevel::DEBUG"); const char* multiline_log = "First line of multiline log:\n" - " Indented second line of multiline log\n" - " Indented third line of multiline log\n" - " Indented fourth line of multiline log"; - Log(INFO, multiline_log); + "Second line of multiline log\n" + "Third line of multiline log\n" + "Fourth line of multiline log"; + Log(DEBUG, multiline_log); - Log(DEBUG, "Sample Log for LogLevel::DEBUG"); + setenv(EV_MODULE_LOGLEVEL, ConvertLogLevelToString(savedLogLevel), 1); } From 008815f0d28c64623df70e1c11184bb12ebe1c69 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 29 Apr 2025 07:53:25 -0700 Subject: [PATCH 27/50] Change ENABLE to ENABLED for EWTS flag --- src/logger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logger.c b/src/logger.c index c13db43d..f4ff59bf 100644 --- a/src/logger.c +++ b/src/logger.c @@ -243,7 +243,7 @@ bool IsEwtsEnabled(void) { if (ewtsStr && ewtsStr[0] != '\0') { char trimmedStr[20] = {0}; TrimString(ewtsStr, trimmedStr, sizeof(trimmedStr)); - bool ewtsEv = ((strcasecmp(trimmedStr, "ENABLE") == 0))? true:false; + bool ewtsEv = ((strcasecmp(trimmedStr, "ENABLED") == 0))? true:false; if (ewtsEv != ewtsEnabled) { ewtsEnabled = ewtsEv; if (ewtsEnabled && loggerInitialized) { From 413b0f0c68122799c31ff29f07ffdbd4135654d2 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 6 May 2025 11:05:46 -0700 Subject: [PATCH 28/50] Update logger to be consistent with other module loggers --- include/logger.h | 6 +-- src/logger.c | 123 ++++++++++++++++++++--------------------------- 2 files changed, 53 insertions(+), 76 deletions(-) diff --git a/include/logger.h b/include/logger.h index 381c0fd5..95b28c2f 100644 --- a/include/logger.h +++ b/include/logger.h @@ -7,10 +7,8 @@ typedef enum { NONE = 0, DEBUG = 1, INFO = 2, - WARN = 3, // CM: Delete when Kyle is ready -// WARNING = 3, // CM: Change when Kyle is ready - ERROR = 4, // CM: Delete when Kyle is ready -// SEVERE = 4, // CM: Change when Kyle is ready + WARNING = 3, // CM: Change when Kyle is ready + SEVERE = 4, // CM: Change when Kyle is ready FATAL = 5, } LogLevel; diff --git a/src/logger.c b/src/logger.c index f4ff59bf..e6846175 100644 --- a/src/logger.c +++ b/src/logger.c @@ -25,14 +25,13 @@ #define LOG_FILE_EXT "log" #define LOG_MODULE_NAME_LEN 8 // Width of module name for log entries -bool ewtsEnabled = true; -bool openedAppendMode = true; -bool loggerInitialized = false; -FILE *logFile = NULL; -char logFilePath[1024] = ""; +bool loggingEnabled = true; +bool openedAppendMode = true; +bool loggerInitialized = false; +FILE* logFile = NULL; +char logFilePath[1024] = ""; LogLevel logLevel = INFO; -char moduleName[LOG_MODULE_NAME_LEN+1] = ""; -bool envLogLevelLogged = false; +char moduleName[LOG_MODULE_NAME_LEN+1] = ""; void TrimString(const char *input, char *output, size_t outputSize) { if (!input || !output || outputSize == 0) { @@ -133,7 +132,6 @@ bool LogFileReady(bool appendMode) { logFile = fopen(logFilePath, appendMode ? "a" : "w"); if (logFile != NULL) { openedAppendMode = appendMode; - if (loggerInitialized) Log(INFO, "Opened log file %s in %s mode\n", logFilePath, (appendMode ? "Append" : "Truncate")); return true; } return false; @@ -203,6 +201,11 @@ void SetupLogFile(void) { if (LogFileReady(appendEntries)) { if (!moduleLogEnvExists) setenv(EV_MODULE_LOGFILEPATH, logFilePath, 1); + printf("Module %s Log File: %s\n", MODULE_NAME, logFilePath); + LogLevel saveLevel = logLevel; + logLevel = INFO; // Ensure this INFO message is always logged + Log(logLevel, "Opened log file %s in %s mode\n", logFilePath, (openedAppendMode ? "Append" : "Truncate")); + logLevel = saveLevel; } else { printf("Unable to open log file "); if (strlen(logFilePath) > 0) { @@ -216,10 +219,8 @@ const char* ConvertLogLevelToString(LogLevel level) { switch (level) { case DEBUG: return "DEBUG "; case INFO: return "INFO "; - case WARN: return "WARN "; // CM: Delete when Kyle is ready -// case WARNING: return "WARNING"; // CM: Add when Kyle is ready - case ERROR: return "ERROR "; // CM: Delete when Kyle is ready -// case SEVERE: return "SEVERE "; // CM: Add when Kyle is ready + case WARNING: return "WARNING"; + case SEVERE: return "SEVERE "; case FATAL: return "FATAL "; default: return "NONE "; } @@ -230,75 +231,51 @@ LogLevel ConvertStringToLogLevel(const char* str) { TrimString(str, trimmedStr, sizeof(trimmedStr)); if (strcasecmp(trimmedStr, "DEBUG") == 0) return DEBUG; if (strcasecmp(trimmedStr, "INFO") == 0) return INFO; - if (strcasecmp(trimmedStr, "WARN") == 0) return WARN; // CM: Delete when Kyle is ready -// if (strcasecmp(trimmedStr, "WARNING") == 0) return WARNING; // CM: Change when Kyle is ready - if (strcasecmp(trimmedStr, "ERROR") == 0) return ERROR; // CM: Delete when Kyle is ready -// if (strcasecmp(trimmedStr, "SEVERE") == 0) return SEVERE; // CM: Change when Kyle is ready + if (strcasecmp(trimmedStr, "WARNING") == 0) return WARNING; + if (strcasecmp(trimmedStr, "SEVERE") == 0) return SEVERE; if (strcasecmp(trimmedStr, "FATAL") == 0) return FATAL; return NONE; } -bool IsEwtsEnabled(void) { +void SetLoggingFlag(void) { const char* ewtsStr = getenv(EV_EWTS_LOGGING); if (ewtsStr && ewtsStr[0] != '\0') { char trimmedStr[20] = {0}; TrimString(ewtsStr, trimmedStr, sizeof(trimmedStr)); - bool ewtsEv = ((strcasecmp(trimmedStr, "ENABLED") == 0))? true:false; - if (ewtsEv != ewtsEnabled) { - ewtsEnabled = ewtsEv; - if (ewtsEnabled && loggerInitialized) { - char lMsg[100]; - snprintf(lMsg, sizeof(lMsg), "INFO ONLY: %s changed. Set to %s\n", EV_EWTS_LOGGING, trimmedStr); - Log(logLevel, lMsg); - } - } + bool loggingEnabled = ((strcasecmp(trimmedStr, "ENABLED") == 0))? true:false; } - return ewtsEnabled; + printf("%s Logging %s\n", MODULE_NAME, ((loggingEnabled)?"ENABLED":"DISABLED")); } -void CheckLogLevel(void) { +void SetLogLevel(void) { const char* envLogLevel = getenv(EV_MODULE_LOGLEVEL); if (envLogLevel && envLogLevel[0] != '\0') { - LogLevel envll = ConvertStringToLogLevel(envLogLevel); - if (envll != logLevel) { - logLevel = envll; - - char llStr[10]; - char llMsg[100]; - TrimString(ConvertLogLevelToString(logLevel), llStr, sizeof(llStr)); - snprintf(llMsg, sizeof(llMsg), "INFO ONLY: Log level changed in %s. Set to %s\n", EV_MODULE_LOGLEVEL, llStr); - if (loggerInitialized) Log(logLevel, llMsg); - } + logLevel = ConvertStringToLogLevel(envLogLevel); } + char llStr[10]; + char msg[100]; + TrimString(ConvertLogLevelToString(logLevel), llStr, sizeof(llStr)); + snprintf(msg, sizeof(msg), "Log level set to %s\n", llStr); + printf("%s %s", MODULE_NAME, msg); + LogLevel saveLevel = logLevel; + logLevel = INFO; // Ensure this INFO message is always logged + Log(logLevel, msg); + logLevel = saveLevel; } void SetLogPreferences(void) { - if (IsEwtsEnabled()) { - - // Set the module name used in log entries - SetLogModuleName(); - - // Set the log file path name and open it - SetupLogFile(); - loggerInitialized = true; - printf("Module %s Log File: %s\n", MODULE_NAME, logFilePath); - // This is an INFO message that always should be in the log but the - // logLevel could be different than INFO. Therefore use logLevel to - // ensure the message is recorded in the log - Log(logLevel, "INFO ONLY: Opened log file %s in %s mode\n", logFilePath, (openedAppendMode ? "Append" : "Truncate")); - Log(logLevel, "INFO ONLY: EWTS default ENABLED\n"); - - // Check for the log level environment variable for a log level - char llStr[10]; - char llMsg[80]; - TrimString(ConvertLogLevelToString(logLevel), llStr, sizeof(llStr)); - snprintf(llMsg,sizeof(llMsg), "INFO ONLY: Default log level is %s\n", llStr); - Log(logLevel, llMsg); - CheckLogLevel(); // Check for log level set in the environment variable + if (!loggerInitialized) { + loggerInitialized = true; // Only call this once - // FOR TESTING ONLY! Uncomment next line to generate test log entries - // WriteLogTestMsgs(); + SetLoggingFlag(); // Based on the environment variable + if (loggingEnabled) { + SetLogModuleName(); // Set the module name used in log entries + SetupLogFile(); + SetLogLevel(); + // FOR TESTING ONLY! Uncomment next line to generate test log entries + // WriteLogTestMsgs(); + } } } @@ -317,14 +294,18 @@ void TrimToOneNewline(char *str, size_t max_len) { } } -void Log(LogLevel messageLevel, const char* message, ...) { - if (IsEwtsEnabled()) { - if (!loggerInitialized) SetLogPreferences(); // Initialize logger on first call +LogLevel GetLogLevel(void) { + return logLevel; +} - // Check for change in log level environment variable - CheckLogLevel(); +bool IsLoggingEnabled(void) { + return loggingEnabled; +} + +void Log(LogLevel messageLevel, const char* message, ...) { + if (!loggerInitialized) SetLogPreferences(); // Initialize logger on first call - if (messageLevel < logLevel) return; + if (loggingEnabled && (messageLevel >= logLevel)) { va_list arglist; va_start(arglist, message); @@ -370,11 +351,9 @@ void WriteLogTestMsgs() { LogLevel savedLogLevel = logLevel; + setenv(EV_MODULE_LOGLEVEL, "SEVERE", 1); Log(SEVERE, "Sample Log for LogLevel::SEVERE"); setenv(EV_MODULE_LOGLEVEL, "FATAL", 1); Log(FATAL, "Sample Log for LogLevel::FATAL"); - setenv(EV_MODULE_LOGLEVEL, "ERROR", 1); Log(ERROR, "Sample Log for LogLevel::ERROR"); - setenv(EV_MODULE_LOGLEVEL, "WARN", 1); Log(WARN, "Sample Log for LogLevel::WARN"); -// setenv(EV_MODULE_LOGLEVEL, "SEVERE", 1); Log(SEVERE, "Sample Log for LogLevel::SEVERE"); -// setenv(EV_MODULE_LOGLEVEL, "WARNING", 1); Log(WARNING, "Sample Log for LogLevel::WARNING"); + setenv(EV_MODULE_LOGLEVEL, "WARNING", 1); Log(WARNING, "Sample Log for LogLevel::WARNING"); setenv(EV_MODULE_LOGLEVEL, "INFO", 1); Log(INFO, "Sample Log for LogLevel::INFO"); setenv(EV_MODULE_LOGLEVEL, "DEBUG", 1); Log(DEBUG, "Sample Log for LogLevel::DEBUG"); From c09f0249f8866befd597009d815ec8624219382c Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 6 May 2025 11:45:55 -0700 Subject: [PATCH 29/50] Change log levels ERROR to WARNING and WARNING to SEVERE --- src/bmi_cfe.c | 130 +++++++++++++++++++++++++------------------------- src/cfe.c | 4 +- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 5b88e913..0a669394 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -408,7 +408,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) // Note that this determines max line length including the ending return character, if present int count_result = read_file_line_counts_cfe(config_file, &config_line_count, &max_config_line_length); if (count_result == -1) { - Log(ERROR, "Invalid config file '%s'", config_file); + Log(WARNING, "Invalid config file '%s'", config_file); return BMI_FAILURE; } @@ -536,7 +536,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__depth_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log(WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -550,7 +550,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__satdk_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log(WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -559,7 +559,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__satpsi_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log(WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -568,7 +568,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__slop_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log( WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -577,7 +577,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__smcmax_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log( WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -586,7 +586,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_params__wltsmc_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log( WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -605,7 +605,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_gw_max_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log( WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } // Also set the true storage if storage was already read and was a ratio, and so we were waiting for this /* if (is_gw_storage_set == TRUE && is_gw_storage_ratio == TRUE) { @@ -618,7 +618,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_Cgw_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log( WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -635,7 +635,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_gw_storage_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log( WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } /* char* trailing_chars; gw_storage_literal = strtod(param_value, &trailing_chars); @@ -667,7 +667,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_soil_storage_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log( WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } continue; } @@ -775,88 +775,88 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) is_ice_content_threshold_set = TRUE; // Check if units are present and print warning if missing from config file if ((param_units == NULL) || (strlen(param_units) < 1)) { - Log( WARN, "[units] expected for '%s' in config file \n", param_key); + Log(SEVERE, "[units] expected for '%s' in config file \n", param_key); } } } } if (is_forcing_file_set == FALSE) { - Log(ERROR, "Config param 'forcing_file' not found in config file\n"); + Log(WARNING, "Config param 'forcing_file' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__depth_set == FALSE) { - Log(ERROR, "Config param 'soil_params.depth' not found in config file\n"); + Log(WARNING, "Config param 'soil_params.depth' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__bb_set == FALSE) { - Log(ERROR, "Config param 'soil_params.bb' not found in config file\n"); + Log(WARNING, "Config param 'soil_params.bb' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__satdk_set == FALSE) { - Log(ERROR, "Config param 'soil_params.satdk' not found in config file\n"); + Log(WARNING, "Config param 'soil_params.satdk' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__satpsi_set == FALSE) { - Log(ERROR, "Config param 'soil_params.satpsi' not found in config file\n"); + Log(WARNING, "Config param 'soil_params.satpsi' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__slop_set == FALSE) { - Log(ERROR, "Config param 'soil_params.slop' not found in config file\n"); + Log(WARNING, "Config param 'soil_params.slop' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__smcmax_set == FALSE) { - Log(ERROR, "Config param 'soil_params.smcmax' not found in config file\n"); + Log(WARNING, "Config param 'soil_params.smcmax' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__wltsmc_set == FALSE) { - Log(ERROR, "Config param 'soil_params.wltsmc' not found in config file\n"); + Log(WARNING, "Config param 'soil_params.wltsmc' not found in config file\n"); return BMI_FAILURE; } if (is_soil_params__expon_set == FALSE) { - Log(ERROR, "Config param 'soil_params.expon' not found in config file, defaulting to 1 (linear)\n"); + Log(WARNING, "Config param 'soil_params.expon' not found in config file, defaulting to 1 (linear)\n"); model->soil_reservoir.exponent_primary = 1.0; //is_soil_params__expon_set == TRUE; // Don't return BMI_FAILURE, this is a optional config //return BMI_FAILURE; } if (is_soil_params__expon2_set == FALSE) { - Log(ERROR, "Config param 'soil_params.expon_secondary' not found in config file, defaulting to 1 (linear)\n"); + Log(WARNING, "Config param 'soil_params.expon_secondary' not found in config file, defaulting to 1 (linear)\n"); model->soil_reservoir.exponent_secondary = 1.0; // Don't return BMI_FAILURE, this is a optional config //return BMI_FAILURE; } if (is_Cgw_set == FALSE) { - Log(ERROR, "Config param 'Cgw' not found in config file\n"); + Log(WARNING, "Config param 'Cgw' not found in config file\n"); return BMI_FAILURE; } if (is_expon_set == FALSE) { - Log(ERROR, "Config param 'expon' not found in config file\n"); + Log(WARNING, "Config param 'expon' not found in config file\n"); return BMI_FAILURE; } if (is_alpha_fc_set == FALSE) { - Log(ERROR, "Config param 'alpha_fc' not found in config file\n"); + Log(WARNING, "Config param 'alpha_fc' not found in config file\n"); return BMI_FAILURE; } if (is_soil_storage_set == FALSE) { - Log(ERROR, "Config param 'soil_storage' not found in config file\n"); + Log(WARNING, "Config param 'soil_storage' not found in config file\n"); return BMI_FAILURE; } if (is_K_nash_set == FALSE) { - Log(ERROR, "Config param 'K_nash' not found in config file\n"); + Log(WARNING, "Config param 'K_nash' not found in config file\n"); return BMI_FAILURE; } if (is_K_lf_set == FALSE) { - Log(ERROR, "Config param 'K_lf' not found in config file\n"); + Log(WARNING, "Config param 'K_lf' not found in config file\n"); return BMI_FAILURE; } if (is_gw_max_set == FALSE) { - Log(ERROR, "Config param 'max_gw_storage' not found in config file\n"); + Log(WARNING, "Config param 'max_gw_storage' not found in config file\n"); return BMI_FAILURE; } if (is_gw_storage_set == FALSE) { - Log(ERROR, "Config param 'gw_storage' not found in config file\n"); + Log(WARNING, "Config param 'gw_storage' not found in config file\n"); return BMI_FAILURE; } @@ -866,35 +866,35 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) } if (is_num_timesteps_set == FALSE && strcmp(model->forcing_file, "BMI")) { - Log(ERROR, "Config param 'num_timesteps' not found in config file\n"); + Log(WARNING, "Config param 'num_timesteps' not found in config file\n"); return BMI_FAILURE; } if (is_verbosity_set == FALSE) { - Log(ERROR, "Config param 'verbosity' not found in config file\n"); - Log(ERROR, "setting verbosity to a high value\n"); + Log(WARNING, "Config param 'verbosity' not found in config file\n"); + Log(WARNING, "setting verbosity to a high value\n"); model->verbosity = 10; return BMI_FAILURE; } if (is_direct_runoff_method_set == FALSE) { - Log(ERROR, "Config param 'direct_runoff_method' not found in config file\n"); + Log(WARNING, "Config param 'direct_runoff_method' not found in config file\n"); return BMI_FAILURE; } /* xinanjiang_dev*/ if(model->direct_runoff_params_struct.surface_partitioning_scheme == Xinanjiang){ if (is_a_Xinanjiang_inflection_point_parameter_set == FALSE) { - Log(ERROR, "Config param 'a_Xinanjiang_inflection_point_parameter' not found in config file\n"); + Log(WARNING, "Config param 'a_Xinanjiang_inflection_point_parameter' not found in config file\n"); return BMI_FAILURE; } if (is_b_Xinanjiang_shape_parameter_set == FALSE) { - Log(ERROR, "Config param 'b_Xinanjiang_shape_parameter' not found in config file\n"); + Log(WARNING, "Config param 'b_Xinanjiang_shape_parameter' not found in config file\n"); return BMI_FAILURE; } if (is_x_Xinanjiang_shape_parameter_set == FALSE) { - Log(ERROR, "Config param 'x_Xinanjiang_shape_parameter' not found in config file\n"); + Log(WARNING, "Config param 'x_Xinanjiang_shape_parameter' not found in config file\n"); return BMI_FAILURE; } if (is_urban_decimal_fraction_set == FALSE) { - Log(ERROR, "Config param 'urban_decimal_fraction' not found in config file\n"); + Log(WARNING, "Config param 'urban_decimal_fraction' not found in config file\n"); return BMI_FAILURE; } } @@ -922,11 +922,11 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) /*------------------- Root zone AET development -rlm----------------------------- */ if (is_aet_rootzone_set == TRUE ) { if (is_max_rootzone_layer_set == FALSE) { - Log(ERROR, "Config param 'max_rootzone_layer' not found in config file\n"); + Log(WARNING, "Config param 'max_rootzone_layer' not found in config file\n"); return BMI_FAILURE; } if (is_soil_layer_depths_string_val_set == FALSE) { - Log(ERROR, "Soil layer depths string/values not set!\n"); + Log(WARNING, "Soil layer depths string/values not set!\n"); return BMI_FAILURE; } @@ -953,7 +953,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) //Check that the last depth read in from the cfe config file (soil_layer_depths) matches the total depth (soil_params.depth) //from the cfe config file. if(model->NWM_soil_params.D != model->soil_reservoir.soil_layer_depths_m[model->soil_reservoir.n_soil_layers]){ - Log(WARN, "soil_params.depth is not equal to the last soil layer depth in the CFE config file!\n"); + Log(SEVERE, "soil_params.depth is not equal to the last soil layer depth in the CFE config file!\n"); return BMI_FAILURE; } @@ -968,7 +968,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) for (int i=1; i <= model->soil_reservoir.n_soil_layers; i++) { current_depth = model->soil_reservoir.soil_layer_depths_m[i]; if (current_depth <= previous_depth) - Log(WARN, "Soil depths may be out of order. One or more soil layer depths is less than or equal to the previous layer. Check CFE config file.\n"); + Log(SEVERE, "Soil depths may be out of order. One or more soil layer depths is less than or equal to the previous layer. Check CFE config file.\n"); model->soil_reservoir.delta_soil_layer_depth_m[i] = current_depth - previous_depth; previous_depth = current_depth; } @@ -989,7 +989,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) // Handle GIUH ordinates, bailing if they were not provided if (is_giuh_originates_string_val_set == FALSE) { - Log(ERROR, "GIUH ordinate string not set!\n"); + Log(WARNING, "GIUH ordinate string not set!\n"); return BMI_FAILURE; } Log(INFO, "GIUH ordinates string value found in config ('%s')\n", giuh_originates_string_val); @@ -1132,11 +1132,11 @@ static int Initialize (Bmi *self, const char *file) int forcing_line_count, max_forcing_line_length; int count_result = read_file_line_counts_cfe(cfe_bmi_data_ptr->forcing_file, &forcing_line_count, &max_forcing_line_length); if (count_result == -1) { - Log(ERROR, "Configured forcing file '%s' could not be opened for reading\n", cfe_bmi_data_ptr->forcing_file); + Log(WARNING, "Configured forcing file '%s' could not be opened for reading\n", cfe_bmi_data_ptr->forcing_file); return BMI_FAILURE; } if (forcing_line_count == 1) { - Log(ERROR, "Invalid header-only forcing file '%s'\n", cfe_bmi_data_ptr->forcing_file); + Log(WARNING, "Invalid header-only forcing file '%s'\n", cfe_bmi_data_ptr->forcing_file); return BMI_FAILURE; } // Infer the number of time steps: assume a header, so equal to the number of lines minus 1 @@ -1153,7 +1153,7 @@ static int Initialize (Bmi *self, const char *file) FILE* ffp = fopen(cfe_bmi_data_ptr->forcing_file, "r"); // Ensure still exists if (ffp == NULL) { - Log(ERROR, "Forcing file '%s' disappeared!", cfe_bmi_data_ptr->forcing_file); + Log(WARNING, "Forcing file '%s' disappeared!", cfe_bmi_data_ptr->forcing_file); return BMI_FAILURE; } @@ -1239,7 +1239,7 @@ static int Update (Bmi *self) // double current_time, end_time; // self->get_current_time(self, ¤t_time); // self->get_end_time(self, &end_time); -// Log( DEBUG, "end time: %lf\n", end_time); +// Log(DEBUG, "end time: %lf\n", end_time); // if (current_time >= end_time) { // return BMI_FAILURE; // } @@ -1296,7 +1296,7 @@ static int Update_until (Bmi *self, double t) } frac = n_steps - (int)n_steps; if (frac > 0){ - Log( WARN, "WARNING: CFE trying to update a fraction of a timestep\n"); + Log(WARNING, "CFE trying to update a fraction of a timestep\n"); // change timestep to remaining fraction & call update() cfe_ptr->time_step_size = frac * dt; @@ -2053,8 +2053,8 @@ static int Get_state_var_names (Bmi *self, char ** names) //-------------------------------- // Option to print all the names //-------------------------------- - // if (i==0) Log( WARN, " State variable names:"); - // Log( WARN, " var name[%d] = %s\n", i, names[i]); + // if (i==0) Log(SEVERE, " State variable names:"); + // Log(SEVERE, " var name[%d] = %s\n", i, names[i]); } return BMI_SUCCESS; @@ -2087,8 +2087,8 @@ static int Get_state_var_types (Bmi *self, char ** types) //-------------------------------- // Option to print all the types //-------------------------------- - // if (i==0) Log( WARN, " State var_types:"); - // Log( WARN, " var type[%d] = %s\n", i, types[i]); + // if (i==0) Log(SEVERE, " State var_types:"); + // Log(SEVERE, " var type[%d] = %s\n", i, types[i]); } return BMI_SUCCESS; @@ -3060,7 +3060,7 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ if(cfe_ptr->vol_struct.volin>0.0) Log(INFO, "global pct. err: %6.4e percent of inputs\n",global_residual/cfe_ptr->vol_struct.volin*100.0); else Log(INFO, "global pct. err: %6.4e percent of initial\n",global_residual/cfe_ptr->vol_struct.volstart*100.0); if(!is_fabs_less_than_epsilon(global_residual,1.0e-12)) - Log(WARN, "WARNING: GLOBAL MASS BALANCE CHECK FAILED\n"); + Log(SEVERE, "GLOBAL MASS BALANCE CHECK FAILED\n"); /* xinanjiang_dev schaake_residual = cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.vol_sch_runoff - cfe_ptr->vol_struct.vol_sch_infilt; @@ -3069,7 +3069,7 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ printf(" infiltration: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_infilt); printf("schaake residual: %6.4e m\n",schaake_residual); // should equal 0.0 if(!is_fabs_less_than_epsilon(schaake_residual,1.0e-12)) - printf("WARNING: SCHAAKE PARTITIONING MASS BALANCE CHECK FAILED\n");*/ + printf("SCHAAKE PARTITIONING MASS BALANCE CHECK FAILED\n");*/ direct_residual = cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_infilt-cfe_ptr->vol_struct.vol_et_from_rain; Log(INFO, " DIRECT RUNOFF MASS BALANCE\n"); Log(INFO, " surface runoff: %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); @@ -3077,7 +3077,7 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ Log(INFO, " vol_et_from_rain: %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_rain); Log(INFO, "direct residual: %6.4e m\n",direct_residual); // should equal 0.0 if(!is_fabs_less_than_epsilon(direct_residual,1.0e-12)) - Log(WARN, "WARNING: DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILED\n"); + Log(SEVERE, "DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILED\n"); /* xinanjiang_dev giuh_residual = cfe_ptr->vol_struct.vol_out_giuh - cfe_ptr->vol_struct.vol_sch_runoff - vol_end_giuh; */ @@ -3087,11 +3087,11 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ /* xinanjiang_dev Log(INFO, " vol. into giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_sch_runoff); */ Log(INFO, " vol. into giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); - Log(ERROR, " vol. out giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_out_giuh); - Log(ERROR, " vol. end giuh q: %8.4lf m\n",cfe_ptr->vol_struct.vol_end_giuh); + Log(WARNING, " vol. out giuh: %8.4lf m\n",cfe_ptr->vol_struct.vol_out_giuh); + Log(WARNING, " vol. end giuh q: %8.4lf m\n",cfe_ptr->vol_struct.vol_end_giuh); Log(INFO, " giuh residual: %6.4e m\n",giuh_residual); // should equal zero if(!is_fabs_less_than_epsilon(giuh_residual,1.0e-12)) - Log(WARN, "WARNING: GIUH MASS BALANCE CHECK FAILED\n"); + Log(SEVERE, "GIUH MASS BALANCE CHECK FAILED\n"); /* xinanjiang_dev soil_residual=cfe_ptr->vol_struct.vol_soil_start + cfe_ptr->vol_struct.vol_sch_infilt - */ @@ -3110,7 +3110,7 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ Log(INFO, " vol. et from soil: %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_soil); Log(INFO, "vol. soil resid.: %6.4e m\n",soil_residual); if(!is_fabs_less_than_epsilon(soil_residual,1.0e-12)) - Log(WARN, "WARNING: SOIL CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + Log(SEVERE, "SOIL CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); nash_residual=cfe_ptr->vol_struct.vol_in_nash - cfe_ptr->vol_struct.vol_out_nash - vol_in_nash_end; Log(INFO, " NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE\n"); @@ -3119,7 +3119,7 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ Log(INFO, " final vol. nash: %8.4lf m\n",vol_in_nash_end); Log(INFO, "nash casc resid.: %6.4e m\n",nash_residual); if(!is_fabs_less_than_epsilon(nash_residual,1.0e-12)) - Log(WARN, "WARNING: NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + Log(SEVERE, "NASH CASCADE CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); gw_residual = cfe_ptr->vol_struct.vol_in_gw_start + cfe_ptr->vol_struct.vol_to_gw - cfe_ptr->vol_struct.vol_from_gw - vol_in_gw_end; @@ -3130,7 +3130,7 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ Log(INFO, "final gw.storage: %8.4lf m\n",vol_in_gw_end); Log(INFO, " gw. residual: %6.4e m\n",gw_residual); if(!is_fabs_less_than_epsilon(gw_residual,1.0e-12)) - Log(WARN, "WARNING: GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); + Log(SEVERE, "GROUNDWATER CONCEPTUAL RESERVOIR MASS BALANCE CHECK FAILED\n"); } /**************************************************************************/ @@ -3252,7 +3252,7 @@ void itwo_alloc_cfe(int ***array, int rows, int cols) { int i, frows, fcols; if ((rows == 0) || (cols == 0)) { - Log(ERROR, "Error: Attempting to allocate array of size 0\n"); + Log(WARNING, "Error: Attempting to allocate array of size 0\n"); } frows = rows + 1; /* added one for FORTRAN numbering */ @@ -3277,7 +3277,7 @@ void dtwo_alloc_cfe(double ***array, int rows, int cols) { int i, frows, fcols; if ((rows == 0) || (cols == 0)) { - Log(ERROR, "Error: Attempting to allocate array of size 0\n"); + Log(WARNING, "Error: Attempting to allocate array of size 0\n"); } frows = rows + 1; /* added one for FORTRAN numbering */ @@ -3303,7 +3303,7 @@ void d_alloc_cfe(double **var, int size) { *var = (double *) malloc(size * sizeof(double)); if (*var == NULL) { - Log(ERROR, "Problem allocating memory for array in d_alloc.\n"); + Log(WARNING, "Problem allocating memory for array in d_alloc.\n"); return; } else memset(*var, 0, size * sizeof(double)); @@ -3315,7 +3315,7 @@ void i_alloc_cfe(int **var, int size) { *var = (int *) malloc(size * sizeof(int)); if (*var == NULL) { - Log(ERROR, "Problem allocating memory in i_alloc\n"); + Log(WARNING, "Problem allocating memory in i_alloc\n"); return; } else memset(*var, 0, size * sizeof(int)); diff --git a/src/cfe.c b/src/cfe.c index b8be0606..b511f0d6 100644 --- a/src/cfe.c +++ b/src/cfe.c @@ -231,13 +231,13 @@ extern void cfe( if(flux_from_deep_gw_to_chan_m > gw_reservoir_struct->storage_m) { flux_from_deep_gw_to_chan_m=gw_reservoir_struct->storage_m; // TODO: set a flag when flux larger than storage - Log(WARN, "WARNING: Groundwater flux larger than storage \n"); + Log(SEVERE, "WARNING: Groundwater flux larger than storage \n"); } massbal_struct->vol_from_gw+=flux_from_deep_gw_to_chan_m; // in the instance of calling the gw reservoir the secondary flux should be zero- verify - if(is_fabs_less_than_epsilon(secondary_flux,1.0e-09)==FALSE) Log(ERROR, "problem with nonzero flux point 1\n"); + if(is_fabs_less_than_epsilon(secondary_flux,1.0e-09)==FALSE) Log(WARNING, "problem with nonzero flux point 1\n"); // adjust state of deep groundwater conceptual nonlinear reservoir From b0d149e53c26a7db5daac6e46103c74d35572682 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Tue, 6 May 2025 13:03:52 -0700 Subject: [PATCH 30/50] Flush stdout after print --- src/logger.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/logger.c b/src/logger.c index e6846175..58e2dca6 100644 --- a/src/logger.c +++ b/src/logger.c @@ -202,6 +202,7 @@ void SetupLogFile(void) { if (LogFileReady(appendEntries)) { if (!moduleLogEnvExists) setenv(EV_MODULE_LOGFILEPATH, logFilePath, 1); printf("Module %s Log File: %s\n", MODULE_NAME, logFilePath); + fflush(stdout); // Force flushing of stdout LogLevel saveLevel = logLevel; logLevel = INFO; // Ensure this INFO message is always logged Log(logLevel, "Opened log file %s in %s mode\n", logFilePath, (openedAppendMode ? "Append" : "Truncate")); @@ -212,6 +213,7 @@ void SetupLogFile(void) { printf("%s (Perhaps check permissions)\n", logFilePath); } printf("Log entries will be written to stdout\n"); + fflush(stdout); // Force flushing of stdout } } @@ -245,6 +247,7 @@ void SetLoggingFlag(void) { bool loggingEnabled = ((strcasecmp(trimmedStr, "ENABLED") == 0))? true:false; } printf("%s Logging %s\n", MODULE_NAME, ((loggingEnabled)?"ENABLED":"DISABLED")); + fflush(stdout); // Force flushing of stdout } void SetLogLevel(void) { @@ -257,6 +260,7 @@ void SetLogLevel(void) { TrimString(ConvertLogLevelToString(logLevel), llStr, sizeof(llStr)); snprintf(msg, sizeof(msg), "Log level set to %s\n", llStr); printf("%s %s", MODULE_NAME, msg); + fflush(stdout); // Force flushing of stdout LogLevel saveLevel = logLevel; logLevel = INFO; // Ensure this INFO message is always logged Log(logLevel, msg); @@ -340,7 +344,7 @@ void Log(LogLevel messageLevel, const char* message, ...) { printf("%s %s\n", logPrefix, line); line = strtok(NULL, "\n"); } - fflush(stdout); + fflush(stdout); // Force flushing of stdout } free(buffer); From fc7fe352fbf3ac177f9bec835f71bc9533c5c1de Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Thu, 15 May 2025 06:55:46 -0700 Subject: [PATCH 31/50] Remove placholder comments --- include/logger.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/logger.h b/include/logger.h index 95b28c2f..877d9a8d 100644 --- a/include/logger.h +++ b/include/logger.h @@ -7,8 +7,8 @@ typedef enum { NONE = 0, DEBUG = 1, INFO = 2, - WARNING = 3, // CM: Change when Kyle is ready - SEVERE = 4, // CM: Change when Kyle is ready + WARNING = 3, + SEVERE = 4, FATAL = 5, } LogLevel; From 3cbc26e0b9fb333fd2e2c362cd3b8c3414513889 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Mon, 21 Jul 2025 12:08:22 -0700 Subject: [PATCH 32/50] Fix merge errors --- include/bmi_cfe.h | 1 + include/cfe.h | 1 + src/bmi_cfe.c | 68 +++++++++++++++-------------------------------- 3 files changed, 24 insertions(+), 46 deletions(-) diff --git a/include/bmi_cfe.h b/include/bmi_cfe.h index 5e9c5d26..1d215541 100644 --- a/include/bmi_cfe.h +++ b/include/bmi_cfe.h @@ -7,6 +7,7 @@ extern "C" { #include "./cfe.h" #include "bmi.h" +#include "nash_cascade.h" //-------------------------------------------------- // Experiment to simplify BMI implementation (SDP) diff --git a/include/cfe.h b/include/cfe.h index fd84e1da..3b93d6d3 100644 --- a/include/cfe.h +++ b/include/cfe.h @@ -9,6 +9,7 @@ #include #include "conceptual_reservoir.h" #include "giuh.h" +#include "nash_cascade.h" #define TRUE 1 #define FALSE 0 diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 9f982081..1a2f1f71 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -942,7 +942,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) Log(SEVERE, "Config param 'soil_storage' not found in config file. Aborting...\n"); return BMI_FAILURE; } - if (is_K_nash_set == FALSE) { + if (is_K_nash_subsurface_set == FALSE) { Log(SEVERE, "Config param 'K_nash' not found in config file. Aborting...\n"); return BMI_FAILURE; } @@ -955,7 +955,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) return BMI_FAILURE; } if (is_gw_storage_set == FALSE) { - Log(SEVERE, "Config param 'gw_storage' not found in config file\. Aborting...n"); + Log(SEVERE, "Config param 'gw_storage' not found in config file. Aborting...n"); return BMI_FAILURE; } @@ -992,13 +992,13 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) return BMI_FAILURE; } if (is_urban_decimal_fraction_set == FALSE) { - Log(SEVERE, "Config param 'urban_decimal_fraction' not found in config file. Aborting...\n"); - return BMI_FAILURE; + Log(SEVERE, "Config param 'urban_decimal_fraction' not found in config file. Aborting...\n"); + return BMI_FAILURE; } } - if(model->direct_runoff_params_struct.surface_partitioning_scheme == Schaake){ - model->direct_runoff_params_struct.Schaake_adjusted_magic_constant_by_soil_type = model->NWM_soil_params.refkdt * model->NWM_soil_params.satdk / 0.000002; + if(model->infiltration_excess_params_struct.surface_water_partitioning_scheme == Schaake){ + model->infiltration_excess_params_struct.Schaake_adjusted_magic_constant_by_soil_type = model->NWM_soil_params.refkdt * model->NWM_soil_params.satdk / 0.000002; Log(INFO, "Schaake Magic Constant calculated\n"); } @@ -1021,7 +1021,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) Log(INFO, "Counted number of GIUH ordinates (%d)\n", model->num_giuh_ordinates); if (model->num_giuh_ordinates < 1) { - Log(SEVERE, ("Number of GIUH ordinates less than 1 (%d). Aborting... \n", model->num_giuh_ordinates); + Log(SEVERE, "Number of GIUH ordinates less than 1 (%d). Aborting... \n", model->num_giuh_ordinates); return BMI_FAILURE; } @@ -1040,32 +1040,28 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) } else if(model->surface_runoff_scheme == NASH_CASCADE) { if (is_N_nash_surface_set == FALSE) { - Log(SEVERE, "Config param 'N_nash_surface' not found in config file. Aborting...\n"); - return BMI_FAILURE; + Log(SEVERE, "Config param 'N_nash_surface' not found in config file. Aborting...\n"); + return BMI_FAILURE; } if (is_K_nash_surface_set == FALSE) { - Log(SEVERE, "Config param 'K_nash_surface' not found in config file. Aborting...\n"); - return BMI_FAILURE; + Log(SEVERE, "Config param 'K_nash_surface' not found in config file. Aborting...\n"); + return BMI_FAILURE; } if (is_nsubsteps_nash_surface_set == FALSE) { Log(INFO, "Config param 'nsubsteps_nash_surface' not found in config file, default value is 10.\n"); model->nash_surface_params.nsubsteps = 10; // default value of the number of sub-timesteps } if (is_nash_storage_surface_set == FALSE) { - Log(SEVERE, "Config param 'nash_storage_surface' not found in config file. Aborting...\n"); - return BMI_FAILURE; + Log(SEVERE, "Config param 'nash_storage_surface' not found in config file. Aborting...\n"); + return BMI_FAILURE; } if (is_K_infiltration_nash_surface_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'Kinf_nash_surface' not found in config file, default value is 0.001 [1/hr] \n"); -#endif - model->nash_surface_params.K_infiltration = 0.001; // used in the runon infiltration + Log(WARNING, "Config param 'Kinf_nash_surface' not found in config file, default value is 0.001 [1/hr] \n"); + model->nash_surface_params.K_infiltration = 0.001; // used in the runon infiltration } if (is_retention_depth_nash_surface_set == FALSE) { -#if CFE_DEBUG >= 1 - printf("Config param 'retention_depth_nash_surface' not found in config file, default value is 1.0 [mm] \n"); -#endif - model->nash_surface_params.retention_depth = 0.001; // usually in the range of 1-5 mm + Log(WARNING, "Config param 'retention_depth_nash_surface' not found in config file, default value is 1.0 [mm] \n"); + model->nash_surface_params.retention_depth = 0.001; // usually in the range of 1-5 mm } @@ -1102,10 +1098,7 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) if(model->infiltration_excess_params_struct.surface_water_partitioning_scheme == Schaake) { model->infiltration_excess_params_struct.Schaake_adjusted_magic_constant_by_soil_type = model->NWM_soil_params.refkdt * model->NWM_soil_params.satdk / 0.000002; - -#if CFE_DEBUG >= 1 - printf("Schaake Magic Constant calculated\n"); -#endif + Log(INFO, "Schaake Magic Constant calculated\n"); } if (is_sft_coupled_set == TRUE && model->infiltration_excess_params_struct.surface_water_partitioning_scheme == Schaake) { @@ -1120,9 +1113,6 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) // set sft_coupled flag to false if the parameter is not provided in the config file. model->soil_reservoir.is_sft_coupled = (is_sft_coupled_set == TRUE) ? TRUE : FALSE; -// Used for parsing strings representing arrays of values below - char *copy, *value; - /*------------------- Root zone AET development -rlm----------------------------- */ if (is_aet_rootzone_set == TRUE ) { if (is_max_rootzone_layer_set == FALSE) { @@ -1479,7 +1469,7 @@ static int Update_until (Bmi *self, double t) } if(self->get_current_time(self, &now) == BMI_FAILURE) { - Log(SEVERE, "get_current_time failure. Aborting...\ + Log(SEVERE, "get_current_time failure. Aborting...\n"); return BMI_FAILURE; } @@ -1965,7 +1955,7 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) if (strcmp (name, "SURF_RUNOFF_SCHEME") == 0) { cfe_state_struct *cfe_ptr; cfe_ptr = (cfe_state_struct *) self->data; - *dest = (void*)&cfe_ptr->direct_runoff_params_struct.surface_partitioning_scheme; + *dest = (void*)&cfe_ptr->infiltration_excess_params_struct.surface_water_partitioning_scheme; return BMI_SUCCESS; } @@ -3213,29 +3203,15 @@ extern void mass_balance_check(cfe_state_struct* cfe_ptr){ direct_residual = cfe_ptr->vol_struct.volin - cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_infilt - cfe_ptr->vol_struct.vol_et_from_rain; - Log(INFO, ("******************* PRECIPITATION MASS BALANCE *****************\n"); + Log(INFO, "******************* PRECIPITATION MASS BALANCE *****************\n"); Log(INFO, " Volume input = %8.4lf m\n",cfe_ptr->vol_struct.volin); Log(INFO, " Infiltration Excess = %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); Log(INFO, " Infiltration = %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); Log(INFO, " Vol_et_from_rain = %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_rain); Log(INFO, " Precip residual = %6.4e m\n",direct_residual); // should equal 0.0 if (!is_fabs_less_than_epsilon(direct_residual,1.0e-12)) - Log(SEVERE, "DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILED\n"); + Log(SEVERE, "DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILEDS\n"); - /* xinanjiang_dev - giuh_residual = cfe_ptr->vol_struct.vol_out_giuh - cfe_ptr->vol_struct.vol_sch_runoff - vol_end_giuh; */ - giuh_residual = cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_out_giuh - vol_end_giuh; - Log(INFO, " GIUH MASS BALANCE\n"); - - Log(INFO, "******************* PRECIPITATION MASS BALANCE *****************\n"); - Log(INFO, " Volume input = %8.4lf m\n",cfe_ptr->vol_struct.volin); - Log(INFO, " Infiltration Excess = %8.4lf m\n",cfe_ptr->vol_struct.vol_runoff); - Log(INFO, " Infiltration = %8.4lf m\n",cfe_ptr->vol_struct.vol_infilt); - Log(INFO, " Vol_et_from_rain = %8.4lf m\n",cfe_ptr->vol_struct.vol_et_from_rain); - Log(INFO, " Precip residual = %6.4e m\n",direct_residual); // should equal 0.0 - if(!is_fabs_less_than_epsilon(direct_residual,1.0e-12)) - Log(WARNING "DIRECT RUNOFF PARTITIONING MASS BALANCE CHECK FAILED\n"); - giuh_residual = cfe_ptr->vol_struct.vol_runoff - cfe_ptr->vol_struct.vol_out_surface - cfe_ptr->vol_struct.vol_end_surface - cfe_ptr->vol_struct.vol_runon_infilt; Log(INFO, "********************* SURFACE MASS BALANCE *********************\n"); From c2d4de516cef57846ac52c46c95a9378068b48b6 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Mon, 21 Jul 2025 14:40:06 -0700 Subject: [PATCH 33/50] Fix header file reference --- include/bmi_cfe.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/bmi_cfe.h b/include/bmi_cfe.h index 1d215541..5e9c5d26 100644 --- a/include/bmi_cfe.h +++ b/include/bmi_cfe.h @@ -7,7 +7,6 @@ extern "C" { #include "./cfe.h" #include "bmi.h" -#include "nash_cascade.h" //-------------------------------------------------- // Experiment to simplify BMI implementation (SDP) From 6182848dddda63da39c64c7c4479ec2e1d66a842 Mon Sep 17 00:00:00 2001 From: Ian Todd Date: Fri, 15 Aug 2025 13:37:13 +0000 Subject: [PATCH 34/50] Created serialization methods for CFE state data --- CMakeLists.txt | 30 +++++-- include/bmi_cfe.h | 4 + include/bmi_serialization.h | 18 ++++ include/vecbuf.hpp | 115 +++++++++++++++++++++++++ src/bmi_cfe.c | 91 +++++++++++++++++++- src/bmi_serialization.cxx | 162 ++++++++++++++++++++++++++++++++++++ 6 files changed, 412 insertions(+), 8 deletions(-) create mode 100644 include/bmi_serialization.h create mode 100644 include/vecbuf.hpp create mode 100644 src/bmi_serialization.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 28d68a30..4ed24c8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,28 +64,40 @@ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") if(AETROOTZONE) add_executable(${exe_name} ./src/main_cfe_aorc_pet_rz_aet.cxx ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c - ./extern/evapotranspiration/src/bmi_pet.c) + ./extern/evapotranspiration/src/bmi_pet.c ./src/bmi_serialization.cxx ./src/nash_cascade.c) add_library(cfelib ./extern/SoilMoistureProfiles/src/bmi_soil_moisture_profile.cxx ./extern/SoilMoistureProfiles/src/soil_moisture_profile.cxx ./extern/SoilMoistureProfiles/include/bmi_soil_moisture_profile.hxx ./extern/SoilMoistureProfiles/include/soil_moisture_profile.hxx) target_link_libraries(${exe_name} LINK_PUBLIC cfelib) elseif(FORCING) add_executable(${exe_name} ./src/main_pass_forcings.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c - ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c) + ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./src/bmi_serialization.cxx ./src/nash_cascade.c) elseif(FORCINGPET) add_executable(${exe_name} ./src/main_cfe_aorc_pet.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c ./extern/aorc_bmi/src/aorc.c ./extern/aorc_bmi/src/bmi_aorc.c ./extern/evapotranspiration/src/pet.c - ./extern/evapotranspiration/src/bmi_pet.c) + ./extern/evapotranspiration/src/bmi_pet.c ./src/bmi_serialization.cxx ./src/nash_cascade.c) elseif(BASE) -add_executable(${exe_name} ./src/main.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c) +add_executable(${exe_name} ./src/main.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c + ./src/bmi_serialization.cxx ./src/nash_cascade.c) elseif(UNITTEST) -add_executable(${exe_name} ./test/main_unit_test.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c) +add_executable(${exe_name} ./test/main_unit_test.c ./src/cfe.c ./src/bmi_cfe.c ./src/logger.c ./src/giuh.c ./src/conceptual_reservoir.c + ./src/bmi_serialization.cxx ./src/nash_cascade.c) endif() +# ----------------------------------------------------------------------------- +# Find the Boost library and configure usage +set(Boost_USE_STATIC_LIBS OFF) +set(Boost_USE_MULTITHREADED ON) +set(Boost_USE_STATIC_RUNTIME OFF) +find_package(Boost 1.79.0 REQUIRED COMPONENTS serialization) + + + if(NOT NGEN) target_link_libraries(${exe_name} PRIVATE m) target_include_directories(${exe_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(${exe_name} PRIVATE Boost::serialization) endif() @@ -98,9 +110,11 @@ set(CFE_LIB_DESC_CMAKE "OWP CFE BMI Module Shared Library") add_compile_definitions(BMI_ACTIVE) if(WIN32) - add_library(cfebmi ./src/bmi_cfe.c ./src/logger.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) + add_library(cfebmi ./src/bmi_cfe.c ./src/logger.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c + ./src/bmi_serialization.cxx) else() - add_library(cfebmi SHARED ./src/bmi_cfe.c ./src/logger.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c) + add_library(cfebmi SHARED ./src/bmi_cfe.c ./src/logger.c ./src/cfe.c ./src/giuh.c ./src/conceptual_reservoir.c + ./src/bmi_serialization.cxx) endif() target_include_directories(cfebmi PRIVATE include) @@ -109,6 +123,8 @@ set_target_properties(cfebmi PROPERTIES VERSION ${PROJECT_VERSION}) set_target_properties(cfebmi PROPERTIES PUBLIC_HEADER ./include/bmi_cfe.h) +target_link_libraries(cfebmi PRIVATE Boost::serialization) + include(GNUInstallDirs) install(TARGETS cfebmi diff --git a/include/bmi_cfe.h b/include/bmi_cfe.h index 5e9c5d26..05c79ad6 100644 --- a/include/bmi_cfe.h +++ b/include/bmi_cfe.h @@ -7,6 +7,7 @@ extern "C" { #include "./cfe.h" #include "bmi.h" +#include //-------------------------------------------------- // Experiment to simplify BMI implementation (SDP) @@ -111,6 +112,9 @@ struct cfe_state_struct { int verbosity; + char* serialized; + uint64_t serialized_length; // needs a permanent anchor for get_value_ptr + }; typedef struct cfe_state_struct cfe_state_struct; diff --git a/include/bmi_serialization.h b/include/bmi_serialization.h new file mode 100644 index 00000000..f0f6c600 --- /dev/null +++ b/include/bmi_serialization.h @@ -0,0 +1,18 @@ +#ifndef CFE_BMI_SERIALIZATION +#define CFE_BMI_SERIALIZATION + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bmi.h" + +int free_serialized_cfe(Bmi* bmi); +int load_serialized_cfe(Bmi* bmi, const char* data); +int new_serialized_cfe(Bmi* bmi); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/vecbuf.hpp b/include/vecbuf.hpp new file mode 100644 index 00000000..6db982cc --- /dev/null +++ b/include/vecbuf.hpp @@ -0,0 +1,115 @@ +#ifndef HPP_STRING_VECBUF +#define HPP_STRING_VECBUF +// https://gist.github.com/stephanlachnit/4a06f8475afd144e73235e2a2584b000 +// SPDX-FileCopyrightText: 2023 Stephan Lachnit +// SPDX-License-Identifier: MIT + +#include +#include +#include + +template> +class vecbuf : public std::basic_streambuf { +public: + using streambuf = std::basic_streambuf; + using char_type = typename streambuf::char_type; + using int_type = typename streambuf::int_type; + using traits_type = typename streambuf::traits_type; + using vector = std::vector; + using value_type = typename vector::value_type; + using size_type = typename vector::size_type; + + // Constructor for vecbuf with optional initial capacity + vecbuf(size_type capacity = 0) : vector_() { reserve(capacity); } + + // Forwarder for std::vector::shrink_to_fit() + constexpr void shrink_to_fit() { vector_.shrink_to_fit(); } + + // Forwarder for std::vector::clear() + constexpr void clear() { vector_.clear(); } + + // Forwarder for std::vector::reserve + constexpr void reserve(size_type capacity) { vector_.reserve(capacity); setp_from_vector(); } + + // Increase the capacity of the buffer by reserving the current_size + additional_capacity + constexpr void reserve_additional(size_type additional_capacity) { reserve(size() + additional_capacity); } + + // Forwarder for std::vector::data + constexpr const value_type* data() const { return vector_.data(); } + + // Forwarder for std::vector::size + constexpr size_type size() const { return vector_.size(); } + + // Forwarder for std::vector::capacity + constexpr size_type capacity() const { return vector_.capacity(); } + + // Implements std::basic_streambuf::xsputn + std::streamsize xsputn(const char_type* s, std::streamsize count) override { + try { + reserve_additional(count); + } + catch (const std::bad_alloc& error) { + // reserve did not work, use slow algorithm + return xsputn_slow(s, count); + } + // reserve worked, use fast algorithm + return xsputn_fast(s, count); + } + +protected: + // Calculates value to std::basic_streambuf::pbase from vector + constexpr value_type* pbase_from_vector() const { return const_cast(vector_.data()); } + + // Calculates value to std::basic_streambuf::pptr from vector + constexpr value_type* pptr_from_vector() const { return const_cast(vector_.data() + vector_.size()); } + + // Calculates value to std::basic_streambuf::epptr from vector + constexpr value_type* epptr_from_vector() const { return const_cast(vector_.data()) + vector_.capacity(); } + + // Sets the values for std::basic_streambuf::pbase, std::basic_streambuf::pptr and std::basic_streambuf::epptr from vector + constexpr void setp_from_vector() { streambuf::setp(pbase_from_vector(), epptr_from_vector()); streambuf::pbump(size()); } + +private: + // std::vector containing the data + vector vector_; + + // Fast implementation of std::basic_streambuf::xsputn if reserve_additional(count) succeeded + std::streamsize xsputn_fast(const char_type* s, std::streamsize count) { + // store current pptr (end of vector location) + auto* old_pptr = pptr_from_vector(); + // resize the vector, does not move since space already reserved + vector_.resize(vector_.size() + count); + // directly memcpy new content to old pptr (end of vector before it was resized) + traits_type::copy(old_pptr, s, count); + // reserve() already calls setp_from_vector(), only adjust pptr to new epptr + streambuf::pbump(count); + + return count; + } + + // Slow implementation of std::basic_streambuf::xsputn if reserve_additional(count) did not succeed, might calls std::basic_streambuf::overflow() + std::streamsize xsputn_slow(const char_type* s, std::streamsize count) { + // reserving entire vector failed, emplace char for char + std::streamsize written = 0; + while (written < count) { + try { + // copy one char, should throw eventually std::bad_alloc + vector_.emplace_back(s[written]); + } + catch (const std::bad_alloc& error) { + // try overflow(), if eof return, else continue + int_type c = this->overflow(traits_type::to_int_type(s[written])); + if (traits_type::eq_int_type(c, traits_type::eof())) { + return written; + } + } + // update pbase, pptr and epptr + setp_from_vector(); + written++; + } + return written; + } + +}; + +#endif diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 1a2f1f71..c64dd1a3 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -6,6 +6,7 @@ #include #include #include "logger.h" +#include "bmi_serialization.h" #ifndef WATER_SPECIFIC_WEIGHT #define WATER_SPECIFIC_WEIGHT 9810 @@ -1537,6 +1538,10 @@ static int Finalize (Bmi *self) free(model->flux_nash_lateral_runoff_m); if( model->flux_perc_m != NULL ) free(model->flux_perc_m); + + // clear serialized data + if (model->serialized != NULL) + free(model->serialized); free(self->data); } @@ -1619,7 +1624,19 @@ static int Get_var_type (Bmi *self, const char *name, char * type) } } - // If we get here, it means the variable type wasn't recognized + // serialization + if (strcmp(name, "serialization_create") == 0) { + strncpy(type, "uint64_t", BMI_MAX_TYPE_NAME); + return BMI_SUCCESS; + } else if (strcmp(name, "serialization_state") == 0) { + strncpy(type, "char", BMI_MAX_TYPE_NAME); + return BMI_SUCCESS; + } else if (strcmp(name, "serialization_free") == 0) { + strncpy(type, "int", BMI_MAX_TYPE_NAME); + return BMI_SUCCESS; + } + + // If we get here, it means the variable name wasn't recognized type[0] = '\0'; Log(SEVERE, "variable type wasn't recognized. Aborting...\n"); return BMI_FAILURE; @@ -1656,6 +1673,14 @@ static int Get_var_itemsize (Bmi *self, const char *name, int * size) *size = sizeof(long); return BMI_SUCCESS; } + else if (strcmp(type, "uint64_t") == 0) { + *size = sizeof(uint64_t); + return BMI_SUCCESS; + } + else if (strcmp(type, "char") == 0) { + *size = sizeof(char); + return BMI_SUCCESS; + } else { *size = 0; Log(SEVERE, "variable itemsize wasn't recognized. Aborting...\n"); @@ -1711,6 +1736,20 @@ static int Get_var_units (Bmi *self, const char *name, char * units) static int Get_var_nbytes (Bmi *self, const char *name, int * nbytes) { + // serialization + if (strcmp(name, "serialization_create") == 0) { + *nbytes = sizeof(uint64_t); + return BMI_SUCCESS; + } else if (strcmp(name, "serialization_state") == 0) { + cfe_state_struct* model = (cfe_state_struct*)self->data; + if (model->serialized == NULL) + return BMI_FAILURE; + *nbytes = model->serialized_length; + return BMI_SUCCESS; + } else if (strcmp(name, "serialization_free") == 0) { + *nbytes = sizeof(int); + return BMI_SUCCESS; + } int item_size; int item_size_result = Get_var_itemsize(self, name, &item_size); if (item_size_result != BMI_SUCCESS) { @@ -2003,6 +2042,27 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) } //------------------------------------------------------------------- + + /***********************************************************/ + /******** SERIALIZATION ******************************/ + /***********************************************************/ + if (strcmp(name, "serialization_create") == 0) { + int result = new_serialized_cfe(self); + if (result == BMI_SUCCESS) { + cfe_state_struct* model = (cfe_state_struct*)self->data; + *dest = &model->serialized_length; + return BMI_SUCCESS; + } + return BMI_FAILURE; + } + if (strcmp(name, "serialization_state") == 0) { + cfe_state_struct* model = (cfe_state_struct*)self->data; + if (model->serialized == NULL) + return BMI_FAILURE; + *dest = model->serialized; + return BMI_SUCCESS; + } + return BMI_FAILURE; } @@ -2042,6 +2102,24 @@ static int Get_value_at_indices (Bmi *self, const char *name, void *dest, int *i static int Get_value (Bmi *self, const char *name, void *dest) { + // special cases for serialization + if (strcmp(name, "serialization_create") == 0) { + void* ptr; + int result = Get_value_ptr(self, name, &ptr); // run this to create a new save state + if (result != BMI_SUCCESS) + return BMI_FAILURE; + memcpy(dest, ptr, sizeof(uint64_t)); + return BMI_SUCCESS; + } + if (strcmp(name, "serialization_state") == 0) { + cfe_state_struct* model = (cfe_state_struct*)self->data; + if (model->serialized != NULL) { + memcpy(dest, model->serialized, model->serialized_length); + return BMI_SUCCESS; + } + return BMI_FAILURE; + } + // Use nested call to "by index" version // Here, for now at least, we know all the variables are scalar, so @@ -2080,6 +2158,17 @@ static int Set_value_at_indices (Bmi *self, const char *name, int * inds, int le static int Set_value (Bmi *self, const char *name, void *src) { + // serialization + if (strcmp(name, "serialization_state") == 0) { + return load_serialized_cfe(self, (char*)src); + } else if (strcmp(name, "serialization_free") == 0) { + return free_serialized_cfe(self); + } + // Avoid using set value, call instead set_value_at_index + // Use nested call to "by index" version + + // Here, for now at least, we know all the variables are scalar, so + int inds[] = {0}; void * dest = NULL; int nbytes = 0; diff --git a/src/bmi_serialization.cxx b/src/bmi_serialization.cxx new file mode 100644 index 00000000..a2dcabeb --- /dev/null +++ b/src/bmi_serialization.cxx @@ -0,0 +1,162 @@ +extern "C" { +#include "bmi_serialization.h" +#include "bmi_cfe.h" +#include "logger.h" +} +#include "vecbuf.hpp" + +#include + +#include +#include +#include + + +class CfeSerializer { + public: + CfeSerializer(Bmi* bmi); + cfe_state_struct* data; + private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version); +}; + + +CfeSerializer::CfeSerializer(Bmi* bmi) { + void* data = bmi->data; + this->data = (cfe_state_struct*)data; +} + + +template +void CfeSerializer::serialize(Archive& ar, const unsigned int version) { + using boost::serialization::make_array; + cfe_state_struct* state = this->data; + + // base + ar & state->current_time_step; // update + ar & state->timestep_rainfall_input_m; // update + ar & state->nwm_ponded_depth_m; // update + ar & state->soil_reservoir_storage_deficit_m; // cfe + ar & *state->infiltration_excess_m; // cfe; stored as pointer on struct + ar & state->infiltration_depth_m; // cfe + ar & *state->flux_perc_m; // cfe; stored as pointer on struct + ar & *state->flux_lat_m; // cfe; stored as pointer on struct + ar & state->gw_reservoir_storage_deficit_m; // cfe + ar & *state->flux_from_deep_gw_to_chan_m; // cfe; stored as pointer on struct + ar & *state->flux_direct_runoff_m; // cfe; stored as pointer on struct + ar & *state->flux_nash_lateral_runoff_m; // cfe, nash_cascade_subsurface; stored as pointer on struct + ar & *state->flux_Qout_m; // cfe; stored as pointer on struct + ar & make_array(state->nash_storage_subsurface, // nash_cascade_subsurface + state->N_nash_subsurface); // config + + // conceptual_reservoir + ar & state->soil_reservoir.storage_m; // cfe, et_from_soil + ar & state->soil_reservoir.storage_change_m; // cfe + ar & make_array(state->soil_reservoir.smc_profile, // et_from_soil + state->soil_reservoir.n_soil_layers); // config + ar & state->soil_reservoir.ice_fraction_schaake; // cfe + ar & state->soil_reservoir.ice_fraction_xinanjiang; // cfe + ar & state->gw_reservoir.storage_m; // cfe + + // nash_cascade_parameters + if (state->nash_surface_params.nash_storage != NULL) { // will be null or malloc'd based on config + ar & make_array(state->nash_surface_params.nash_storage, // et_from_retention_depth, nash_cascade_surface + state->nash_surface_params.N_nash); // config + } + + // evapotransporation_struct + ar & state->et_struct.potential_et_m_per_timestep; // cfe + ar & state->et_struct.reduced_potential_et_m_per_timestep; // cfe, et_from_soil, et_from_rainfall + ar & state->et_struct.actual_et_from_rain_m_per_timestep; // cfe + ar & state->et_struct.actual_et_from_soil_m_per_timestep; // cfe, et_from_soil + ar & state->et_struct.actual_et_m_per_timestep; // cfe + ar & state->et_struct.potential_et_m_per_s; // cfe + + // massball, all set in cfe + ar & state->vol_struct.volin; // set in update + ar & state->vol_struct.vol_et_from_rain; + ar & state->vol_struct.vol_et_to_atm; + ar & state->vol_struct.volout; + ar & state->vol_struct.vol_et_from_retention_depth; + ar & state->vol_struct.vol_out_surface; + ar & state->vol_struct.vol_et_from_soil; + ar & state->vol_struct.vol_runoff; + ar & state->vol_struct.vol_infilt; + ar & state->vol_struct.vol_to_soil; + ar & state->vol_struct.vol_to_gw; + ar & state->vol_struct.vol_soil_to_gw; + ar & state->vol_struct.vol_soil_to_lat_flow; + ar & state->vol_struct.vol_from_gw; + ar & state->vol_struct.vol_out_surface; + ar & state->vol_struct.vol_in_nash; + ar & state->vol_struct.vol_out_nash; + + if (state->surface_runoff_scheme == GIUH) { // config + ar & make_array(state->runoff_queue_m_per_timestep, // giuh_convolution_integral + state->num_giuh_ordinates + 1); // config + } + /// state->nash_surface_params.runon_infiltration not used for input or GetValue + // else if (state->surface_runoff_scheme == NASH_CASCADE) { + // ar & state->nash_surface_params.runon_infiltration; + // } +} + + +extern "C" { + +int free_serialized_cfe(Bmi* bmi) { + cfe_state_struct* model = (cfe_state_struct*)bmi->data; + if (model->serialized != NULL) { + free(model->serialized); + model->serialized = NULL; + } + model->serialized_length = 0; + return BMI_SUCCESS; +} + +int load_serialized_cfe(Bmi* bmi, const char* data) { + CfeSerializer serializer(bmi); + std::istringstream stream(data); + boost::archive::binary_iarchive archive(stream); + try { + archive >> serializer; + return BMI_SUCCESS; + } catch (const std::exception &e) { + Log(LogLevel::SEVERE, "Deserializing CFE encountered an error: %s", e.what()); + return BMI_FAILURE; + } +} + +int new_serialized_cfe(Bmi* bmi) { + CfeSerializer serializer(bmi); + vecbuf stream; + boost::archive::binary_oarchive archive(stream); + try { + archive << serializer; + } catch (const std::exception &e) { + Log(LogLevel::SEVERE, "Serializing CFE encountered an error: %s", e.what()); + return BMI_FAILURE; + } + // copy serialized data into cfe data + cfe_state_struct* model = (cfe_state_struct*)bmi->data; + // clear previous data if it exists + if (model->serialized != NULL) { + free(model->serialized); + } + // set size and allocate memory + model->serialized_length = stream.size(); + model->serialized = (char*)malloc(model->serialized_length); + // make sure memory could be allocated + if (model->serialized == NULL) { + Log(LogLevel::SEVERE, "CFE failed to allocate memory for serializing."); + model->serialized_length = 0; + return BMI_FAILURE; + } + // copy stream data to new allocation + memcpy(model->serialized, stream.data(), model->serialized_length); + return BMI_SUCCESS; +} + +} From 819fea97aa9ded74c60e5d92cfb56097153337b5 Mon Sep 17 00:00:00 2001 From: Ian Todd Date: Wed, 20 Aug 2025 07:32:37 -0400 Subject: [PATCH 35/50] Change malloc to calloc to ensure null pointers --- src/bmi_cfe.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index c64dd1a3..831134f7 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -2998,21 +2998,9 @@ int read_file_line_counts_cfe(const char* file_name, int* line_count, int* max_l cfe_state_struct *new_bmi_cfe(void) { cfe_state_struct *data; - data = (cfe_state_struct *) malloc(sizeof(cfe_state_struct)); + data = (cfe_state_struct *) calloc(1, sizeof(cfe_state_struct)); data->time_step_size = 3600; data->time_step_fraction = 1.0; - data->forcing_data_precip_kg_per_m2 = NULL; - data->forcing_data_time = NULL; - data->giuh_ordinates = NULL; - data->nash_storage_subsurface = NULL; - data->runoff_queue_m_per_timestep = NULL; - data->flux_Qout_m = NULL; - data->infiltration_excess_m = NULL; - data->flux_from_deep_gw_to_chan_m = NULL; - data->flux_direct_runoff_m = NULL; - data->flux_lat_m = NULL; - data->flux_nash_lateral_runoff_m = NULL; - data->flux_perc_m = NULL; return data; } From 5bc8187cfefe8c93a5729545af7f96e766bc6598 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Wed, 3 Sep 2025 11:30:22 -0700 Subject: [PATCH 36/50] Fix log level from SEVERE to WARNING for a warning. --- src/cfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cfe.c b/src/cfe.c index ac2a4396..a9dcff80 100644 --- a/src/cfe.c +++ b/src/cfe.c @@ -244,7 +244,7 @@ extern void cfe( if(flux_from_deep_gw_to_chan_m > gw_reservoir_struct->storage_m) { flux_from_deep_gw_to_chan_m=gw_reservoir_struct->storage_m; // TODO: set a flag when flux larger than storage - Log(SEVERE, "WARNING: Groundwater flux larger than storage \n"); + Log(WARNING, "Groundwater flux larger than storage \n"); } massbal_struct->vol_from_gw+=flux_from_deep_gw_to_chan_m; From 4d3d6a5db0ca13c1aaa958eccfe73cb1705a8efa Mon Sep 17 00:00:00 2001 From: Yuqiong Liu Date: Tue, 9 Sep 2025 10:02:32 -0400 Subject: [PATCH 37/50] fix flux larger than storage error --- src/conceptual_reservoir.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/conceptual_reservoir.c b/src/conceptual_reservoir.c index 496429ce..d3424711 100644 --- a/src/conceptual_reservoir.c +++ b/src/conceptual_reservoir.c @@ -46,6 +46,11 @@ extern void conceptual_reservoir_flux_calc(struct conceptual_reservoir *da_reser *primary_flux_m = da_reservoir->coeff_primary * (exp_term - 1.0); *secondary_flux_m = 0.0; + + // cap so flux never exceeds storage + if (*primary_flux_m > da_reservoir->storage_m) { + *primary_flux_m = da_reservoir->storage_m; + } return; } From bd53f465ad354bd5b4a77399c34de5b1cce1129d Mon Sep 17 00:00:00 2001 From: Yuqiong Liu Date: Thu, 11 Sep 2025 16:37:05 -0400 Subject: [PATCH 38/50] changed warning msg per OWP request --- src/cfe.c | 7 ++++++- src/conceptual_reservoir.c | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/cfe.c b/src/cfe.c index a9dcff80..a4ce89ef 100644 --- a/src/cfe.c +++ b/src/cfe.c @@ -244,7 +244,12 @@ extern void cfe( if(flux_from_deep_gw_to_chan_m > gw_reservoir_struct->storage_m) { flux_from_deep_gw_to_chan_m=gw_reservoir_struct->storage_m; // TODO: set a flag when flux larger than storage - Log(WARNING, "Groundwater flux larger than storage \n"); + // Adding specific warning message requested by OWP (9/11/2025) + Log(WARNING, + "Groundwater flux larger than storage. " + "While this will not cause a run failure, " + "parameter combinations may not be realistic " + "and a mass balance error will occur.\n"); } massbal_struct->vol_from_gw+=flux_from_deep_gw_to_chan_m; diff --git a/src/conceptual_reservoir.c b/src/conceptual_reservoir.c index d3424711..83879e41 100644 --- a/src/conceptual_reservoir.c +++ b/src/conceptual_reservoir.c @@ -47,10 +47,10 @@ extern void conceptual_reservoir_flux_calc(struct conceptual_reservoir *da_reser *primary_flux_m = da_reservoir->coeff_primary * (exp_term - 1.0); *secondary_flux_m = 0.0; - // cap so flux never exceeds storage - if (*primary_flux_m > da_reservoir->storage_m) { - *primary_flux_m = da_reservoir->storage_m; - } + // cap so flux never exceeds storage, but commented out here per OWP request (9/11/2025) + //if (*primary_flux_m > da_reservoir->storage_m) { + // *primary_flux_m = da_reservoir->storage_m; + //} return; } From 156c8f9eadf0f8c329f7924a14683f8730cec4b1 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Fri, 12 Sep 2025 13:10:51 -0700 Subject: [PATCH 39/50] Express dimensionless units of ice_fraction_xinanjiang and soil_moisture_profile as "1" so UDUNITS2 can parse them --- src/bmi_cfe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 831134f7..b3a800b3 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -320,8 +320,8 @@ static const char *input_var_units[INPUT_VAR_NAME_COUNT] = { "mm h-1", //"atmosphere_water__liquid_equivalent_precipitation_rate" "m s-1", //"water_potential_evaporation_flux" "m", // ice fraction in meters - "none", // ice fraction [-] - "none" // soil moisture profile is in decimal fraction -rlm + "1", // ice fraction [-] + "1" // soil moisture profile is in decimal fraction -rlm }; static const int input_var_item_count[INPUT_VAR_NAME_COUNT] = { From 54e5de1bddcb3749d4c70d8b445805335c69535a Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Thu, 25 Sep 2025 07:54:58 -0700 Subject: [PATCH 40/50] Throttle groundwater flux warning message to a single log entry. --- src/cfe.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/cfe.c b/src/cfe.c index a4ce89ef..62eb014b 100644 --- a/src/cfe.c +++ b/src/cfe.c @@ -1,9 +1,12 @@ #include "cfe.h" #include "logger.h" +#include #define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; }) #define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; }) +static bool groundwaterFluxIssueLogged = false; + // CFE STATE SPACE FUNCTION // ####################################################################### // Adapted version of Conceptual Functional Equivalent model re-written in state-space form July, 2021 //#################################################################################################### @@ -242,14 +245,17 @@ extern void cfe( flux_from_deep_gw_to_chan_m=primary_flux; // m/h <<<<<<<<<< BASE FLOW FLUX >>>>>>>>> if(flux_from_deep_gw_to_chan_m > gw_reservoir_struct->storage_m) { - flux_from_deep_gw_to_chan_m=gw_reservoir_struct->storage_m; - // TODO: set a flag when flux larger than storage - // Adding specific warning message requested by OWP (9/11/2025) - Log(WARNING, - "Groundwater flux larger than storage. " - "While this will not cause a run failure, " - "parameter combinations may not be realistic " - "and a mass balance error will occur.\n"); + flux_from_deep_gw_to_chan_m=gw_reservoir_struct->storage_m; + // TODO: set a flag when flux larger than storage + // Adding specific warning message requested by OWP (9/11/2025) + if (!groundwaterFluxIssueLogged) { + Log(WARNING, + "Groundwater flux larger than storage. " + "While this will not cause a run failure, " + "parameter combinations may not be realistic " + "and a mass balance error will occur.\n"); + groundwaterFluxIssueLogged = true; + } } massbal_struct->vol_from_gw+=flux_from_deep_gw_to_chan_m; From afc52d6ff39033221f5760deb205162e44fff51e Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Thu, 25 Sep 2025 17:54:00 -0700 Subject: [PATCH 41/50] Adapt serialization to revised protocol --- src/bmi_cfe.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index b3a800b3..44dd72df 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -1628,6 +1628,9 @@ static int Get_var_type (Bmi *self, const char *name, char * type) if (strcmp(name, "serialization_create") == 0) { strncpy(type, "uint64_t", BMI_MAX_TYPE_NAME); return BMI_SUCCESS; + } else if (strcmp(name, "serialization_size") == 0) { + strncpy(type, "uint64_t", BMI_MAX_TYPE_NAME); + return BMI_SUCCESS; } else if (strcmp(name, "serialization_state") == 0) { strncpy(type, "char", BMI_MAX_TYPE_NAME); return BMI_SUCCESS; @@ -1737,7 +1740,7 @@ static int Get_var_units (Bmi *self, const char *name, char * units) static int Get_var_nbytes (Bmi *self, const char *name, int * nbytes) { // serialization - if (strcmp(name, "serialization_create") == 0) { + if (strcmp(name, "serialization_create") == 0 || strcmp(name, "serialization_size") == 0) { *nbytes = sizeof(uint64_t); return BMI_SUCCESS; } else if (strcmp(name, "serialization_state") == 0) { @@ -2046,14 +2049,12 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) /***********************************************************/ /******** SERIALIZATION ******************************/ /***********************************************************/ - if (strcmp(name, "serialization_create") == 0) { - int result = new_serialized_cfe(self); - if (result == BMI_SUCCESS) { - cfe_state_struct* model = (cfe_state_struct*)self->data; - *dest = &model->serialized_length; - return BMI_SUCCESS; - } - return BMI_FAILURE; + if (strcmp(name, "serialization_size") == 0) { + cfe_state_struct* model = (cfe_state_struct*)self->data; + if (model->serialized == NULL) + return BMI_FAILURE; + *dest = (void*)&model->serialized_length; + return BMI_SUCCESS; } if (strcmp(name, "serialization_state") == 0) { cfe_state_struct* model = (cfe_state_struct*)self->data; @@ -2103,14 +2104,6 @@ static int Get_value_at_indices (Bmi *self, const char *name, void *dest, int *i static int Get_value (Bmi *self, const char *name, void *dest) { // special cases for serialization - if (strcmp(name, "serialization_create") == 0) { - void* ptr; - int result = Get_value_ptr(self, name, &ptr); // run this to create a new save state - if (result != BMI_SUCCESS) - return BMI_FAILURE; - memcpy(dest, ptr, sizeof(uint64_t)); - return BMI_SUCCESS; - } if (strcmp(name, "serialization_state") == 0) { cfe_state_struct* model = (cfe_state_struct*)self->data; if (model->serialized != NULL) { @@ -2161,6 +2154,8 @@ static int Set_value (Bmi *self, const char *name, void *src) // serialization if (strcmp(name, "serialization_state") == 0) { return load_serialized_cfe(self, (char*)src); + } else if (strcmp(name, "serialization_create") == 0) { + return new_serialized_cfe(self); } else if (strcmp(name, "serialization_free") == 0) { return free_serialized_cfe(self); } From e11666d6230df85b7ccb6a8d90e0aee894ff9c64 Mon Sep 17 00:00:00 2001 From: Mohammed Karim Date: Wed, 17 Sep 2025 16:09:18 -0700 Subject: [PATCH 42/50] ice_fraction_schaake (dimensionless) --- src/bmi_cfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index b3a800b3..6f07f582 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -319,7 +319,7 @@ static const char *input_var_types[INPUT_VAR_NAME_COUNT] = { static const char *input_var_units[INPUT_VAR_NAME_COUNT] = { "mm h-1", //"atmosphere_water__liquid_equivalent_precipitation_rate" "m s-1", //"water_potential_evaporation_flux" - "m", // ice fraction in meters + "1", // ice fraction should be dimensionless "1", // ice fraction [-] "1" // soil moisture profile is in decimal fraction -rlm }; From 7aab752589624c64ff8d1a725d02273a28444133 Mon Sep 17 00:00:00 2001 From: Mohammed Karim Date: Wed, 17 Sep 2025 16:13:42 -0700 Subject: [PATCH 43/50] SURF_RUNOFF_SCHEME unit from None to 1 (dimensionless) --- src/bmi_cfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 6f07f582..c7b80e55 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -259,7 +259,7 @@ static const char *output_var_units[OUTPUT_VAR_NAME_COUNT] = { "m", "m", "m", - "none", + "1", "m" }; From 33571ec2eff06f6c5bc0f8ee7b6d9d02f33be147 Mon Sep 17 00:00:00 2001 From: "Carolyn.Maynard" Date: Wed, 17 Dec 2025 12:20:10 -0800 Subject: [PATCH 44/50] Make GetLogLevel and IsLoggingEnabled public --- include/logger.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/logger.h b/include/logger.h index 877d9a8d..2fa6aafd 100644 --- a/include/logger.h +++ b/include/logger.h @@ -2,6 +2,7 @@ #define LOGGER_H #include // for variable args: va_list +#include typedef enum { NONE = 0, @@ -14,5 +15,7 @@ typedef enum { // Public Methods void Log(LogLevel messageLevel, const char* message, ...); +bool IsLoggingEnabled(void); +LogLevel GetLogLevel(void); #endif // LOGGER_H From beb56353575098dba423cde4169efe0653db9db9 Mon Sep 17 00:00:00 2001 From: Ian Todd Date: Fri, 16 Jan 2026 10:37:31 -0500 Subject: [PATCH 45/50] Add a serialization_size option in Get_value --- src/bmi_cfe.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index bd0f4edc..bd0183fc 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -2111,6 +2111,13 @@ static int Get_value (Bmi *self, const char *name, void *dest) return BMI_SUCCESS; } return BMI_FAILURE; + } else if (strcmp(name, "serialization_size") == 0) { + cfe_state_struct* model = (cfe_state_struct*)self->data; + if (model->serialized != NULL) { + memcpy(dest, &model->serialized_length, sizeof(uint64_t)); + return BMI_SUCCESS; + } + return BMI_FAILURE; } // Use nested call to "by index" version From c1d33b75782a56ef86b7f71f29f15ff3ff1db34a Mon Sep 17 00:00:00 2001 From: Ian Todd Date: Fri, 16 Jan 2026 13:15:03 -0500 Subject: [PATCH 46/50] Compatibility for serialization size using Get_value --- src/bmi_cfe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index bd0183fc..8783f72a 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -2115,6 +2115,7 @@ static int Get_value (Bmi *self, const char *name, void *dest) cfe_state_struct* model = (cfe_state_struct*)self->data; if (model->serialized != NULL) { memcpy(dest, &model->serialized_length, sizeof(uint64_t)); + uint64_t *set_value = (uint64_t *)dest; return BMI_SUCCESS; } return BMI_FAILURE; From db4f19075e205e3490145dc60b60315225fe3cf1 Mon Sep 17 00:00:00 2001 From: Ian Todd Date: Fri, 16 Jan 2026 14:46:37 -0500 Subject: [PATCH 47/50] Remove leftover test code --- src/bmi_cfe.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 8783f72a..bd0183fc 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -2115,7 +2115,6 @@ static int Get_value (Bmi *self, const char *name, void *dest) cfe_state_struct* model = (cfe_state_struct*)self->data; if (model->serialized != NULL) { memcpy(dest, &model->serialized_length, sizeof(uint64_t)); - uint64_t *set_value = (uint64_t *)dest; return BMI_SUCCESS; } return BMI_FAILURE; From c61bdc8b5e22eb971843bfdbec86dea8837d6aaf Mon Sep 17 00:00:00 2001 From: Ian Todd Date: Thu, 22 Jan 2026 11:34:35 -0500 Subject: [PATCH 48/50] Reset time implemented --- src/bmi_cfe.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index bd0183fc..737809ae 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -1637,6 +1637,9 @@ static int Get_var_type (Bmi *self, const char *name, char * type) } else if (strcmp(name, "serialization_free") == 0) { strncpy(type, "int", BMI_MAX_TYPE_NAME); return BMI_SUCCESS; + } else if (strcmp(name, "reset_time")) { + strncpy(type, "double", BMI_MAX_TYPE_NAME); + return BMI_SUCCESS; } // If we get here, it means the variable name wasn't recognized @@ -1752,6 +1755,9 @@ static int Get_var_nbytes (Bmi *self, const char *name, int * nbytes) } else if (strcmp(name, "serialization_free") == 0) { *nbytes = sizeof(int); return BMI_SUCCESS; + } else if (strcmp(name, "reset_time") == 0) { + *nbytes = sizeof(double); + return BMI_SUCCESS; } int item_size; int item_size_result = Get_var_itemsize(self, name, &item_size); @@ -2165,6 +2171,11 @@ static int Set_value (Bmi *self, const char *name, void *src) return new_serialized_cfe(self); } else if (strcmp(name, "serialization_free") == 0) { return free_serialized_cfe(self); + } else if (strcmp(name, "reset_time") == 0) { + cfe_state_struct *model = (cfe_state_struct *)self->data; + // time step only used for indexing into forcing data during update + model->current_time_step = 0; + return BMI_SUCCESS; } // Avoid using set value, call instead set_value_at_index // Use nested call to "by index" version From 364a22357f43cc796e1bfd7da6b9ef7bcd8c4068 Mon Sep 17 00:00:00 2001 From: Ian Todd Date: Thu, 12 Feb 2026 13:56:34 -0500 Subject: [PATCH 49/50] Merge with OWP master branch --- src/bmi_cfe.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index 737809ae..809423ef 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -1176,6 +1176,8 @@ int read_init_config_cfe(const char* config_file, cfe_state_struct* model) else { model->soil_reservoir.is_aet_rootzone = FALSE; model->soil_reservoir.n_soil_layers = 1; + model->soil_reservoir.soil_layer_depths_m = NULL; + model->soil_reservoir.delta_soil_layer_depth_m = NULL; } /*--------------------END OF ROOT ZONE ADJUSTED AET DEVELOPMENT -rlm ------------------------------*/ @@ -1367,6 +1369,11 @@ static int Initialize (Bmi *self, const char *file) cfe_bmi_data_ptr->gw_reservoir.coeff_secondary = 0.0; // 0.0 means that secondary outlet is not applied cfe_bmi_data_ptr->gw_reservoir.exponent_secondary = 1.0; // linear + // Not used in gw reservoir + cfe_bmi_data_ptr->gw_reservoir.smc_profile = NULL; + cfe_bmi_data_ptr->gw_reservoir.soil_layer_depths_m = NULL; + cfe_bmi_data_ptr->gw_reservoir.delta_soil_layer_depth_m = NULL; + // Initialize soil conceptual reservoirs //LKC Remove alpha_fc init_soil_reservoir(cfe_bmi_data_ptr); @@ -1512,7 +1519,10 @@ static int Finalize (Bmi *self) free(model->forcing_data_precip_kg_per_m2); if( model->forcing_data_time != NULL ) free(model->forcing_data_time); - + if( model->forcing_file != NULL ){ + free(model->forcing_file); + } + if( model->giuh_ordinates != NULL ) free(model->giuh_ordinates); if( model->nash_storage_subsurface != NULL ) @@ -1521,7 +1531,10 @@ static int Finalize (Bmi *self) free(model->runoff_queue_m_per_timestep); if( model->flux_Qout_m != NULL ) free(model->flux_Qout_m); - + + if( model->soil_reservoir.smc_profile != NULL ) + free(model->soil_reservoir.smc_profile); + /* xinanjiang_dev: changing name to the more general "direct runoff" if( model->flux_Schaake_output_runoff_m != NULL ) free(model->flux_Schaake_output_runoff_m);*/ @@ -3187,6 +3200,7 @@ extern void init_soil_reservoir(cfe_state_struct* cfe_ptr) }*/ extern void initialize_volume_trackers(cfe_state_struct* cfe_ptr) { + cfe_ptr->vol_struct.volstart = 0.0; cfe_ptr->vol_struct.volin = 0; cfe_ptr->vol_struct.vol_runoff = 0; cfe_ptr->vol_struct.vol_infilt = 0; From 9a7f35c33d6fa680cae3ffb44988754862aa9235 Mon Sep 17 00:00:00 2001 From: Ian Todd Date: Fri, 20 Feb 2026 13:31:23 -0500 Subject: [PATCH 50/50] Merge resolution for changes from OWP master --- src/bmi_cfe.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/bmi_cfe.c b/src/bmi_cfe.c index cc8c75b3..809423ef 100644 --- a/src/bmi_cfe.c +++ b/src/bmi_cfe.c @@ -3027,27 +3027,6 @@ cfe_state_struct *new_bmi_cfe(void) data = (cfe_state_struct *) calloc(1, sizeof(cfe_state_struct)); data->time_step_size = 3600; data->time_step_fraction = 1.0; - // NJF Ensure that all "optional" pointers are initialized to NULL - // Should probabably ensure that *all* pointers are initialized to NULL - // but that would require some more significant refactoring... - data->soil_reservoir.smc_profile = NULL; - data->soil_reservoir.soil_layer_depths_m = NULL; - data->soil_reservoir.delta_soil_layer_depth_m = NULL; - data->nash_surface_params.nash_storage = NULL; - data->forcing_file = NULL; - - data->forcing_data_precip_kg_per_m2 = NULL; - data->forcing_data_time = NULL; - data->giuh_ordinates = NULL; - data->nash_storage_subsurface = NULL; - data->runoff_queue_m_per_timestep = NULL; - data->flux_Qout_m = NULL; - data->infiltration_excess_m = NULL; - data->flux_from_deep_gw_to_chan_m = NULL; - data->flux_direct_runoff_m = NULL; - data->flux_lat_m = NULL; - data->flux_nash_lateral_runoff_m = NULL; - data->flux_perc_m = NULL; return data; }