From 9cad38b83eca200192df892350e4d1bb86dd7a87 Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 15 Feb 2024 15:58:38 -0500 Subject: [PATCH] WIP addressing #162, #150 --- .github/workflows/build-and-test.yml | 4 +- CMakeLists.txt | 2 +- src/outfile/swmm_output.c | 6 +- src/solver/consts.h | 2 + src/solver/error.c | 3 +- src/solver/error.h | 22 +- src/solver/error.txt | 22 +- src/solver/funcs.h | 7 +- src/solver/hotstart.c | 88 +++++-- src/solver/include/swmm5.h | 72 +++++- src/solver/swmm5.c | 366 ++++++++++++++++++++------- tests/CMakeLists.txt | 13 +- tests/outfile/test_output.cpp | 2 +- 13 files changed, 453 insertions(+), 156 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 56e87d3c0..44f3f4132 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -2,9 +2,9 @@ name: Build and Test on: push: - branches: [ master, develop, release, lew-develop ] + branches: [ master, develop, release, bug_fixes ] pull_request: - branches: [ master, develop, release, lew-develop ] + branches: [ master, develop, release ] env: OMP_NUM_THREADS: 1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 99af0d1cd..4ea592379 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ add_subdirectory(src/solver) add_subdirectory(src/run) if(BUILD_TESTS) - enable_testing() + enable_testing() add_subdirectory(tests) endif() diff --git a/src/outfile/swmm_output.c b/src/outfile/swmm_output.c index 82bbbcf6c..f5752bb89 100644 --- a/src/outfile/swmm_output.c +++ b/src/outfile/swmm_output.c @@ -175,7 +175,7 @@ int EXPORT_OUT_API SMO_open(SMO_Handle p_handle, const char *path) if (p_data == NULL) return -1; else { - strncpy(p_data->name, path, MAXFILENAME); + strncpy_s(p_data->name, path, MAXFILENAME); // Attempt to open binary output file for reading only if ((_fopen(&(p_data->file), path, "rb")) != 0) @@ -526,7 +526,7 @@ int EXPORT_OUT_API SMO_getElementName(SMO_Handle p_handle, SMO_elementType type, *length = p_data->elementNames[idx].length; *name = newCharArray(*length + 1); // Writes IDname and an additional null character to name - strncpy(*name, p_data->elementNames[idx].IDname, + strncpy_s(*name, p_data->elementNames[idx].IDname, (*length + 1) * sizeof(char)); } } @@ -1036,7 +1036,7 @@ void errorLookup(int errcode, char *dest_msg, int dest_len) msg = ERR440; } - strncpy(dest_msg, msg, MAXMSG); + strncpy_s(dest_msg, msg, MAXMSG); } // Local functions: diff --git a/src/solver/consts.h b/src/solver/consts.h index 8208c1466..001c82293 100644 --- a/src/solver/consts.h +++ b/src/solver/consts.h @@ -13,6 +13,7 @@ // Biuld 5.3.0 // - Added MAXHOTSTARTFILES constant to support saving multiple hotstart files // at different times. +// - Added const for MAX_ERR_MSG_LENGTH to define the maximum length of an error message. // //----------------------------------------------------------------------------- @@ -28,6 +29,7 @@ #define EOFMARK 0x1A // Use 0x04 for UNIX systems #define MAXTITLE 3 // Max. # title lines #define MAXMSG 1024 // Max. # characters in message text +#define MAX_ERR_MSG_LENGTH 256 // Max. # characters in error message text #define MAXLINE 1024 // Max. # characters per input line #define MAXFNAME 259 // Max. # characters in file name #define MAXTOKS 40 // Max. items per line of input diff --git a/src/solver/error.c b/src/solver/error.c index 626950b6f..9267851ac 100644 --- a/src/solver/error.c +++ b/src/solver/error.c @@ -23,9 +23,10 @@ #define _CRT_SECURE_NO_DEPRECATE #include +#include "consts.h" #include "error.h" -char ErrString[256]; +char ErrString[MAX_ERR_MSG_LENGTH]; char* error_getMsg(int errCode, char* msg) { diff --git a/src/solver/error.h b/src/solver/error.h index fedf0847e..595380daf 100644 --- a/src/solver/error.h +++ b/src/solver/error.h @@ -7,13 +7,18 @@ // Author: L. Rossman // // Error codes -// +// +// Update History: +// ============== +// Build 5.3.0: +// - Moved API error codes to swmm.h so that they can be accessed for +// interpretation //----------------------------------------------------------------------------- #ifndef ERROR_H #define ERROR_H -enum ErrorType { +enum ErrorType { // ... Runtime Errors ERR_NONE = 0, @@ -161,19 +166,8 @@ enum ErrorType { // ... Runtime Errors ERR_SYSTEM = 500, -// ... API Errors - ERR_API_NOT_OPEN = 501, - ERR_API_NOT_STARTED = 502, - ERR_API_NOT_ENDED = 503, - ERR_API_OBJECT_TYPE = 504, - ERR_API_OBJECT_INDEX = 505, - ERR_API_OBJECT_NAME = 506, - ERR_API_PROPERTY_TYPE = 507, - ERR_API_PROPERTY_VALUE = 508, - ERR_API_TIME_PERIOD = 509, - // ... Additional Errors - MAXERRMSG = 1000 + MAXERRMSG = 1000, }; char* error_getMsg(int i, char* msg); diff --git a/src/solver/error.txt b/src/solver/error.txt index 30d45a783..fdd743e07 100644 --- a/src/solver/error.txt +++ b/src/solver/error.txt @@ -122,14 +122,16 @@ ERR(357,"\n ERROR 357: inflows and outflows interface files have same name.") ERR(361,"\n ERROR 361: could not open external file used for Time Series %s.") ERR(363,"\n ERROR 363: invalid data in external file used for Time Series %s.") +ERR(500, "\n ERROR 500: System exception thrown.") + // API Error Keys -ERR(500,"\n ERROR 500: System exception thrown.") -ERR(501,"\n API Error 501: project not opened.") -ERR(502,"\n API Error 502: simulation not started.") -ERR(503,"\n API Error 503: simulation not ended.") -ERR(504,"\n API Error 504: invalid object type.") -ERR(505,"\n API Error 505: invalid object index.") -ERR(506,"\n API Error 506: invalid object name.") -ERR(507,"\n API Error 507: invalid property type.") -ERR(508,"\n API Error 508: invalid property value.") -ERR(509,"\n API Error 509: invalid time period.") +ERR(-999901,"\n API Error -999901: project not opened.") +ERR(-999902,"\n API Error -999902: simulation not started.") +ERR(-999903,"\n API Error -999903: simulation not ended.") +ERR(-999904,"\n API Error -999904: invalid object type.") +ERR(-999905,"\n API Error -999905: invalid object index.") +ERR(-999906,"\n API Error -999906: invalid object name.") +ERR(-999907,"\n API Error -999907: invalid property type.") +ERR(-999908,"\n API Error -999908: invalid property value.") +ERR(-999909,"\n API Error -999909: invalid time period.") +ERR(-999910,"\n API Error -999910: cannot open hot start interface file %s.") diff --git a/src/solver/funcs.h b/src/solver/funcs.h index fb204a8be..027de6e78 100644 --- a/src/solver/funcs.h +++ b/src/solver/funcs.h @@ -38,6 +38,8 @@ #ifndef FUNCS_H #define FUNCS_H + + //----------------------------------------------------------------------------- // Project Methods //----------------------------------------------------------------------------- @@ -165,8 +167,8 @@ void output_saveResults(double reportTime); void output_updateAvgResults(void); void output_readDateTime(long period, DateTime *aDate); void output_readSubcatchResults(long period, int index); -void output_readNodeResults(int long, int index); -void output_readLinkResults(int long, int index); +void output_readNodeResults(long period, int index); +void output_readLinkResults(long period, int index); //----------------------------------------------------------------------------- // Groundwater Methods @@ -401,6 +403,7 @@ void iface_saveOutletResults(DateTime reportDate, FILE* file); //----------------------------------------------------------------------------- int hotstart_open(void); void hotstart_save(void); +int hotstart_save_to_file(const char* hotstartFile); void hotstart_close(void); //----------------------------------------------------------------------------- diff --git a/src/solver/hotstart.c b/src/solver/hotstart.c index 3ad152c79..1a0c2f207 100644 --- a/src/solver/hotstart.c +++ b/src/solver/hotstart.c @@ -63,8 +63,8 @@ static int fileVersion; //----------------------------------------------------------------------------- // Function declarations //----------------------------------------------------------------------------- -static int initializeFromHostartFile(void); -static int initializeSaveHostartFile(TFile *hotstartFile); +static int initializeFromHotstartFile(void); +static int initializeSaveHotstartFile(TFile *hotstartFile); static void readRunoff(void); static void saveRunoff(TFile *hotstartFile); static void readRouting(void); @@ -77,15 +77,22 @@ static int readDouble(double* x, FILE* f); int hotstart_open() { int i; + // --- open hot start files - if ( !initializeFromHostartFile() ) return FALSE; //input hot start file + if ( !initializeFromHotstartFile() ) return FALSE; //input hot start file - for (int i = 0; i < MAXHOTSTARTFILES; i++) + for (i = 0; i < MAXHOTSTARTFILES; i++) { - if (!initializeSaveHostartFile(&FhotstartOutputs[i])) return FALSE; //output hot start file + if (initializeSaveHotstartFile(&FhotstartOutputs[i])) + { + report_writeErrorMsg(ERR_HOTSTART_FILE_OPEN, FhotstartOutputs[i].name); + return FALSE; + } } + return TRUE; } + //============================================================================= void hotstart_save() @@ -109,6 +116,42 @@ void hotstart_save() } } } + +//============================================================================= + +int hotstart_save_to_file(const char* hotstartFile) +// +// Input: hotStartFile = filepath to hotstart file to use +// Output: returns error code +// Purpose: Saves and closes hotstart files that are to be written at end of simulation. +{ + int error_code = 0; + char fname[MAXFNAME + 1]; + TFile *hotstart = (TFile *) calloc(1, sizeof(TFile)); + + hotstart->file = NULL; + hotstart->mode = SAVE_FILE; + hotstart->saveDateTime = 0; + + sstrncpy(fname, hotstartFile, MAXFNAME); + sstrncpy(hotstart->name, addAbsolutePath(fname), MAXFNAME); + + if(error_code = initializeSaveHotstartFile(hotstart)) + { + FREE(hotstart); + return error_code; + } + else + { + saveRunoff(hotstart); + saveRouting(hotstart); + fclose(hotstart->file); + hotstart->file = NULL; + FREE(hotstart); + return error_code; + } +} + //============================================================================= void hotstart_close() @@ -133,7 +176,7 @@ void hotstart_close() //============================================================================= -int initializeFromHostartFile() +int initializeFromHotstartFile() // // Input: none // Output: none @@ -157,7 +200,9 @@ int initializeFromHostartFile() if ( FhotstartInput.mode != USE_FILE ) return TRUE; if ( (FhotstartInput.file = fopen(FhotstartInput.name, "r+b")) == NULL) { - report_writeErrorMsg(ERR_HOTSTART_FILE_OPEN, FhotstartInput.name); + report_writeErrorMsg( + ERR_HOTSTART_FILE_OPEN, + FhotstartInput.name); return FALSE; } @@ -219,13 +264,14 @@ int initializeFromHostartFile() //============================================================================= -int initializeSaveHostartFile(TFile *hotstartFile) +int initializeSaveHotstartFile(TFile *hotstartFile) // -// Input: none -// Output: none +// Input: hotStartFile = filepath to hotstart file to use +// Output: returns error code // Purpose: opens a new routing hotstart file to save results to. // { + int error_code = 0; int nSubcatch; int nLandUses; int nNodes; @@ -238,8 +284,8 @@ int initializeSaveHostartFile(TFile *hotstartFile) if (hotstartFile->mode != SAVE_FILE ) return TRUE; if ( (hotstartFile->file = fopen(hotstartFile->name, "w+b")) == NULL) { - report_writeErrorMsg(ERR_HOTSTART_FILE_OPEN, hotstartFile->name); - return FALSE; + error_code = ERR_HOTSTART_FILE_OPEN; + return error_code; } // --- write file stamp & number of objects to file @@ -256,20 +302,20 @@ int initializeSaveHostartFile(TFile *hotstartFile) fwrite(&nLinks, sizeof(int), 1, hotstartFile->file); fwrite(&nPollut, sizeof(int), 1, hotstartFile->file); fwrite(&flowUnits, sizeof(int), 1, hotstartFile->file); - return TRUE; + return error_code; } //============================================================================= void saveRouting(TFile *hotstartFile) // -// Input: none +// Input: hotStartFile = hotstart file to use // Output: none // Purpose: saves current state of all nodes and links to hotstart file. // { int i, j; - float x[3]; + float x[3] = { 0 }; for (i = 0; i < Nobjects[NODE]; i++) { @@ -316,7 +362,7 @@ void readRouting() { int i, j; float x; - double xgw[4]; + double xgw[4] = { 0 }; FILE* f = FhotstartInput.file; // --- for file format 2, assign GW moisture content and lower depth @@ -395,15 +441,15 @@ void readRouting() //============================================================================= -void saveRunoff(TFile *hotstartFile) +void saveRunoff(TFile *hotstartFile) // -// Input: none +// Input: hotStartFile = hotstart file to use // Output: none // Purpose: saves current state of all subcatchments to hotstart file. // { int i, j, k; - double x[6]; + double x[6] = { 0 }; FILE* f = hotstartFile->file; for (i = 0; i < Nobjects[SUBCATCH]; i++) @@ -469,7 +515,7 @@ void saveRunoff(TFile *hotstartFile) //============================================================================= -void readRunoff() +void readRunoff() // // Input: none // Output: none @@ -477,7 +523,7 @@ void readRunoff() // { int i, j, k; - double x[6]; + double x[6] = { 0 }; FILE* f = FhotstartInput.file; for (i = 0; i < Nobjects[SUBCATCH]; i++) diff --git a/src/solver/include/swmm5.h b/src/solver/include/swmm5.h index e150a74f2..a2f6a6296 100644 --- a/src/solver/include/swmm5.h +++ b/src/solver/include/swmm5.h @@ -106,17 +106,51 @@ typedef enum { } swmm_LinkProperty; typedef enum { - swmm_STARTDATE = 0, - swmm_CURRENTDATE = 1, - swmm_ELAPSEDTIME = 2, - swmm_ROUTESTEP = 3, + swmm_STARTDATE = 0, + swmm_CURRENTDATE = 1, + swmm_ELAPSEDTIME = 2, + swmm_ROUTESTEP = 3, swmm_MAXROUTESTEP = 4, - swmm_REPORTSTEP = 5, - swmm_TOTALSTEPS = 6, - swmm_NOREPORT = 7, - swmm_FLOWUNITS = 8 + swmm_REPORTSTEP = 5, + swmm_TOTALSTEPS = 6, + swmm_NOREPORT = 7, + swmm_FLOWUNITS = 8, + swmm_ENDDATE = 9, + swmm_REPORTSTART = 10, + swmm_UNITSYSTEM = 11, + swmm_SURCHARGEMETHOD = 12, + swmm_ALLOWPONDING = 13, + swmm_INERTIADAMPING = 14, + swmm_NORMALFLOWLTD = 15, + swmm_SKIPSTEADYSTATE = 16, + swmm_IGNORERAINFALL = 17, + swmm_IGNORERDII = 18, + swmm_IGNORESNOWMELT = 19, + swmm_IGNOREGWATER = 20, + swmm_IGNOREROUTING = 21, + swmm_IGNOREQUALITY = 22, + swmm_RULESTEP= 23, + swmm_SWEEPSTART = 24, + swmm_SWEEPEND = 25, + swmm_MAXTRIALS = 26, + swmm_NUMTHREADS = 27, + swmm_MINROUTESTEP = 28, + swmm_LENGTHENINGSTEP = 29, + swmm_STARTDRYDAYS = 30, + swmm_COURANTFACTOR = 31, + swmm_MINSURFAREA = 32, + swmm_MINSLOPE = 33, + swmm_RUNOFFERROR = 34, + swmm_FLOWERROR = 35, + swmm_QUALERROR = 36, + swmm_HEADTOL = 37, + swmm_SYSFLOWTOL = 38, + swmm_LATFLOWTOL = 39, + } swmm_SystemProperty; + + typedef enum { swmm_CFS = 0, // cubic feet per second swmm_GPM = 1, // gallons per minute @@ -126,11 +160,30 @@ typedef enum { swmm_MLD = 5 // million liters per day } swmm_FlowUnitsProperty; + +typedef enum { + // ... API Errors + ERR_API_NOT_OPEN = -999901, + ERR_API_NOT_STARTED = -999902, + ERR_API_NOT_ENDED = -999903, + ERR_API_OBJECT_TYPE = -999904, + ERR_API_OBJECT_INDEX = -999905, + ERR_API_OBJECT_NAME = -999906, + ERR_API_PROPERTY_TYPE = -999907, + ERR_API_PROPERTY_VALUE = -999908, + ERR_API_TIME_PERIOD = -999909, + ERR_API_HOTSTART_FILE_OPEN = -999910, + ERR_API_NO_DATA = -999999 +} swmm_API_Errors; + + int DLLEXPORT swmm_run(const char *f1, const char *f2, const char *f3); int DLLEXPORT swmm_open(const char *f1, const char *f2, const char *f3); int DLLEXPORT swmm_start(int saveFlag); int DLLEXPORT swmm_step(double *elapsedTime); int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime); +int DLLEXPORT swmm_useHotStart(const char* hotStartFile); +int DLLEXPORT swmm_saveHotStart(const char* hotStartFile); int DLLEXPORT swmm_end(void); int DLLEXPORT swmm_report(void); int DLLEXPORT swmm_close(void); @@ -138,13 +191,14 @@ int DLLEXPORT swmm_close(void); int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, float *qualErr); int DLLEXPORT swmm_getVersion(void); int DLLEXPORT swmm_getError(char *errMsg, int msgLen); +int DLLEXPORT swmm_getErrorFromCode(int error_code, char *outErrMsg[256]); int DLLEXPORT swmm_getWarnings(void); int DLLEXPORT swmm_getCount(int objType); void DLLEXPORT swmm_getName(int objType, int index, char *name, int size); int DLLEXPORT swmm_getIndex(int objType, const char *name); double DLLEXPORT swmm_getValue(int property, int index); -void DLLEXPORT swmm_setValue(int property, int index, double value); +int DLLEXPORT swmm_setValue(int property, int index, double value); double DLLEXPORT swmm_getSavedValue(int property, int index, int period); void DLLEXPORT swmm_writeLine(const char *line); void DLLEXPORT swmm_decodeDate(double date, int *year, int *month, int *day, diff --git a/src/solver/swmm5.c b/src/solver/swmm5.c index 904587cd2..6c505c62b 100644 --- a/src/solver/swmm5.c +++ b/src/solver/swmm5.c @@ -44,6 +44,8 @@ // - Support added for relative file names. // Build 5.3.0: // - Added support for saving hot start files at specific times. +// - Expanded SWMM api to save and use prescribed hotstart files. +// - Expanded SWMM api to allow setting some system options. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -172,10 +174,11 @@ static double getSavedNodeValue(int index, int property, int period); static double getSavedLinkValue(int index, int property, int period); static double getSystemValue(int property); static double getMaxRouteStep(); -static void setNodeLatFlow(int index, double value); -static void setOutfallStage(int index, double value); -static void setLinkSetting(int index, double value); -static void setRoutingStep(double value); +static int setNodeLatFlow(int index, double value); +static int setOutfallStage(int index, double value); +static int setLinkSetting(int index, double value); +static int setRoutingStep(double value); +static int setSystemValue(int property, double value); static void getAbsolutePath(const char* fname, char* absPath, size_t size); // Exception filtering function @@ -185,7 +188,7 @@ static int xfilter(int xc, char* module, double elapsedTime, long step); //============================================================================= -int DLLEXPORT swmm_run(const char *f1, const char *f2, const char *f3) +int DLLEXPORT swmm_run(const char *f1, const char *f2, const char *f3) // // Input: f1 = name of input file // f2 = name of report file @@ -407,6 +410,7 @@ int DLLEXPORT swmm_start(int saveResults) #endif return ErrorCode; } + //============================================================================= int DLLEXPORT swmm_step(double *elapsedTime) @@ -468,7 +472,7 @@ int DLLEXPORT swmm_step(double *elapsedTime) //============================================================================= -int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime) +int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime) // // Input: strideStep = number of seconds to advance the simulation // elapsedTime = current elapsed time in decimal days @@ -516,6 +520,69 @@ int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime) //============================================================================= +int DLLEXPORT swmm_useHotStart(const char* hotStartFile) +// +// Input: hotStartFile = filepath to hotstart file to use +// Output: returns error code +// Purpose: Sets the hotstart file to use for simulation. Errors does not terminate simulation unless +// there is a prior terminating error. +{ + int error_code = 0; + FILE *f; + char fname[MAXFNAME + 1]; + + if (ErrorCode) + return ErrorCode; + else if (!IsOpenFlag) + return (ErrorCode = ERR_API_NOT_OPEN); + else if (IsStartedFlag) + return (ErrorCode = ERR_API_NOT_ENDED); + + sstrncpy(fname, hotStartFile, MAXFNAME); + + // Try to open the hotstart file first to see if it exists + if ((f = fopen(addAbsolutePath(fname), "r")) == NULL) + { + return (error_code = ERR_API_HOTSTART_FILE_OPEN); + } + else + { + fclose(f); + } + + FhotstartInput.mode = USE_FILE; + sstrncpy(FhotstartInput.name, addAbsolutePath(fname), MAXFNAME); + + + return error_code; +} + +//============================================================================= + +int DLLEXPORT swmm_saveHotStart(const char* hotStartFile) +// +// Input: hotStartFile = filepath to hotstart file to save +// Output: returns error code +// Purpose: Saves hotstart file at the current simulation time +{ + int error_code = 0; + + if (ErrorCode) + return ErrorCode; + else if (!IsOpenFlag) + return (ErrorCode = ERR_API_NOT_OPEN); + else if (!IsStartedFlag) + return (ErrorCode = ERR_API_NOT_STARTED); + + + error_code = hotstart_save_to_file(hotStartFile); + + return error_code; + +} + +//============================================================================= + void execRouting() // // Input: none @@ -617,7 +684,6 @@ void saveResults() } - //============================================================================= int DLLEXPORT swmm_end(void) @@ -671,7 +737,7 @@ int DLLEXPORT swmm_report() //============================================================================= -void DLLEXPORT swmm_writeLine(const char *line) +void DLLEXPORT swmm_writeLine(const char *line) // // Input: line = a character string // Output: returns an error code @@ -710,7 +776,7 @@ int DLLEXPORT swmm_close() //============================================================================= -int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, +int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, float *qualErr) // // Input: none @@ -736,7 +802,7 @@ int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, //============================================================================= -int DLLEXPORT swmm_getVersion() +int DLLEXPORT swmm_getVersion() // // Input: none // Output: returns SWMM engine version number @@ -762,7 +828,7 @@ int DLLEXPORT swmm_getWarnings() //============================================================================= -int DLLEXPORT swmm_getError(char *errMsg, int msgLen) +int DLLEXPORT swmm_getError(char *errMsg, int msgLen) // // Input: errMsg = character array to hold error message text // msgLen = maximum size of errMsg @@ -782,7 +848,7 @@ int DLLEXPORT swmm_getError(char *errMsg, int msgLen) //============================================================================= -int DLLEXPORT swmm_getCount(int objType) +int DLLEXPORT swmm_getCount(int objType) // // Input: objType = a type of SWMM object // Output: returns the number of objects; @@ -797,7 +863,7 @@ int DLLEXPORT swmm_getCount(int objType) //============================================================================= -void DLLEXPORT swmm_getName(int objType, int index, char *name, int size) +void DLLEXPORT swmm_getName(int objType, int index, char *name, int size) // // Input: objType = a type of SWMM object // index = the object's index in the array of objects @@ -828,7 +894,7 @@ void DLLEXPORT swmm_getName(int objType, int index, char *name, int size) //============================================================================= -int DLLEXPORT swmm_getIndex(int objType, const char *name) +int DLLEXPORT swmm_getIndex(int objType, const char *name) // // Input: objType = a type of SWMM object // name = the object's ID name @@ -844,7 +910,7 @@ int DLLEXPORT swmm_getIndex(int objType, const char *name) //============================================================================= -double DLLEXPORT swmm_getValue(int property, int index) +double DLLEXPORT swmm_getValue(int property, int index) // // Input: property = an object's property code // index = the object's index in the array of like objects @@ -853,7 +919,7 @@ double DLLEXPORT swmm_getValue(int property, int index) // Purpose: retrieves the value of an object's property. { if (!IsOpenFlag) - return 0; + return ERR_API_NOT_OPEN; if (property < 100) return getSystemValue(property); if (property < 200) @@ -864,67 +930,83 @@ double DLLEXPORT swmm_getValue(int property, int index) return getNodeValue(property, index); if (property < 500) return getLinkValue(property, index); - return 0; + return ERR_API_NO_DATA; } //============================================================================= -void DLLEXPORT swmm_setValue(int property, int index, double value) +int DLLEXPORT swmm_setValue(int property, int index, double value) // // Input: property = an object's property code // index = the object's index in the array of like objects // value = the property's new value -// Output: none +// Output: returns error code // Purpose: sets the value of an object's property. { if (!IsOpenFlag) - return; + return ERR_API_NOT_OPEN; + + if (property < 100) + return setSystemValue(property, value); + + switch (property) { case swmm_GAGE_RAINFALL: if (index < 0 || index >= Nobjects[GAGE]) - return; - if (value >= 0.0) + return ERR_API_OBJECT_INDEX; + else if (value >= 0.0) + { Gage[index].apiRainfall = value; - return; + return 0; + } + else + return ERR_API_PROPERTY_VALUE; + case swmm_SUBCATCH_RPTFLAG: - if (!IsStartedFlag && index >= 0 && index < Nobjects[SUBCATCH]) + if(IsStartedFlag) + return ERR_API_NOT_ENDED; + else if (index >= 0 && index < Nobjects[SUBCATCH]) + { Subcatch[index].rptFlag = (value > 0.0); - return; + return 0; + } + else + return ERR_API_OBJECT_INDEX; case swmm_NODE_LATFLOW: - setNodeLatFlow(index, value); - return; + return setNodeLatFlow(index, value); case swmm_NODE_HEAD: - setOutfallStage(index, value); - return; + return setOutfallStage(index, value); case swmm_NODE_RPTFLAG: - if (!IsStartedFlag && index >= 0 && index < Nobjects[NODE]) + if (IsStartedFlag) + return ERR_API_NOT_ENDED; + else if (index >= 0 && index < Nobjects[NODE]) + { Node[index].rptFlag = (value > 0.0); - return; + return 0; + } + else + return ERR_API_OBJECT_INDEX; case swmm_LINK_SETTING: - setLinkSetting(index, value); - return; + return setLinkSetting(index, value); case swmm_LINK_RPTFLAG: - if (!IsStartedFlag && index >= 0 && index < Nobjects[LINK]) + if (IsStartedFlag) + return ERR_API_NOT_ENDED; + if (index >= 0 && index < Nobjects[LINK]) + { Link[index].rptFlag = (value > 0.0); - return; - case swmm_ROUTESTEP: - setRoutingStep(value); - return; - case swmm_REPORTSTEP: - if (!IsStartedFlag && value > 0) - ReportStep = (int)value; - return; - case swmm_NOREPORT: - if (!IsStartedFlag) - RptFlags.disabled = (value > 0.0); - return; + return 0; + } + else + return ERR_API_OBJECT_INDEX; + default: + return ERR_API_PROPERTY_TYPE; } } //============================================================================= -double DLLEXPORT swmm_getSavedValue(int property, int index, int period) +double DLLEXPORT swmm_getSavedValue(int property, int index, int period) // // Input: property = an object's property code // index = the object's index in the array of like objects @@ -951,7 +1033,7 @@ double DLLEXPORT swmm_getSavedValue(int property, int index, int period) //============================================================================= -void DLLEXPORT swmm_decodeDate(double date, int *year, int *month, int *day, +void DLLEXPORT swmm_decodeDate(double date, int *year, int *month, int *day, int *hour, int *minute, int *second, int *dayOfWeek) // // Input: date = an encoded date in decimal days @@ -1131,90 +1213,122 @@ double getLinkValue(int property, int index) double getSystemValue(int property) // // Input: property = a system property code -// Output: returns current property value +// Output: returns current property value or an error code // Purpose: retrieves current value of a system property. { - switch (property) - { - case swmm_STARTDATE: - return StartDateTime; - case swmm_CURRENTDATE: - return StartDateTime + ElapsedTime; - case swmm_ELAPSEDTIME: - return ElapsedTime; - case swmm_ROUTESTEP: - return RouteStep; - case swmm_MAXROUTESTEP: - return getMaxRouteStep(); - case swmm_REPORTSTEP: - return ReportStep; - case swmm_TOTALSTEPS: - return Nperiods; - case swmm_NOREPORT: - return RptFlags.disabled; - case swmm_FLOWUNITS: - return FlowUnits; - default: - return 0; - } + switch (property) + { + case swmm_STARTDATE: + return StartDateTime; + case swmm_ENDDATE: + return EndDateTime; + case swmm_REPORTSTART: + return ReportStart; + case swmm_CURRENTDATE: + return StartDateTime + ElapsedTime; + case swmm_ELAPSEDTIME: + return ElapsedTime; + case swmm_ROUTESTEP: + return RouteStep; + case swmm_MAXROUTESTEP: + return getMaxRouteStep(); + case swmm_REPORTSTEP: + return ReportStep; + case swmm_TOTALSTEPS: + return Nperiods; + case swmm_NOREPORT: + return RptFlags.disabled; + case swmm_FLOWUNITS: + return FlowUnits; + case swmm_UNITSYSTEM: + return UnitSystem; + case swmm_SURCHARGEMETHOD: + return SurchargeMethod; + case swmm_ALLOWPONDING: + return AllowPonding; + case swmm_INERTIADAMPING: + return InertDamping; + case swmm_NORMALFLOWLTD: + return NormalFlowLtd; + case swmm_SKIPSTEADYSTATE: + return SkipSteadyState; + case swmm_IGNORERAINFALL: + return IgnoreRainfall; + default: + return ERR_API_PROPERTY_TYPE; + } } //============================================================================= -void setNodeLatFlow(int index, double value) +int setNodeLatFlow(int index, double value) // // Input: index = the index of a node // value = the node's external inflow value -// Output: none +// Output: returns an error code // Purpose: sets the value of a node's external inflow. { if (index < 0 || index >= Nobjects[NODE]) - return; + return ERR_API_OBJECT_INDEX; Node[index].apiExtInflow = value / UCF(FLOW); + return 0; } //============================================================================= -void setOutfallStage(int index, double value) +int setOutfallStage(int index, double value) // // Input: index = the index of an outfall node // value = the outfall's fixed stage elevation -// Output: none +// Output: returns an error code // Purpose: sets the value of an outfall node's fixed stage. { TNode* node; if (index < 0 || index >= Nobjects[NODE]) - return; + return ERR_API_OBJECT_INDEX; + node = &Node[index]; + if (node->type != OUTFALL) - return; + return ERR_API_OBJECT_TYPE; + Outfall[node->subIndex].fixedStage = value / UCF(LENGTH); Outfall[node->subIndex].type = FIXED_OUTFALL; + + return 0; } //============================================================================= -void setLinkSetting(int index, double value) +int setLinkSetting(int index, double value) // // Input: index = the index of a link // value = the link's new setting -// Output: node +// Output: returns an error code // Purpose: sets the value of a link's setting. { TLink* link; if (index < 0 || index >= Nobjects[LINK]) - return; + return ERR_API_OBJECT_INDEX; + link = &Link[index]; if (value < 0.0 || link->type == CONDUIT) - return; + return ERR_API_OBJECT_INDEX; + if (link->type != PUMP && value > 1.0) value = 1.0; + if (link->targetSetting == value) - return; + return 0; + link->targetSetting = value; + if (link->targetSetting * link->setting == 0.0) link->timeLastSet = StartDateTime + ElapsedTime; + link_setSetting(index, 0.0); + + return 0; } //============================================================================= @@ -1232,7 +1346,7 @@ double getSavedDate(int period) //============================================================================= -double getSavedSubcatchValue(int property, int index, int period) +double getSavedSubcatchValue(int property, int index, int period) // // Input: property = index of a computed property // index = index of a subcatchment @@ -1267,7 +1381,7 @@ double getSavedSubcatchValue(int property, int index, int period) //============================================================================= -double getSavedNodeValue(int property, int index, int period) +double getSavedNodeValue(int property, int index, int period) // // Input: property = index of a computed property // index = index of a node @@ -1306,7 +1420,7 @@ double getSavedNodeValue(int property, int index, int period) //============================================================================= -double getSavedLinkValue(int property, int index, int period) +double getSavedLinkValue(int property, int index, int period) // // Input: property = index of a computed property // index = index of a link @@ -1362,18 +1476,88 @@ double getMaxRouteStep() //============================================================================= -void setRoutingStep(double value) +int setRoutingStep(double value) // // Input: value = a routing time step (in decimal seconds) -// Output: none +// Output: returns an error code // Purpose: sets the value of the current flow routing time step. { if (value <= 0.0) - return; + return ERR_API_PROPERTY_VALUE; + if (value <= MinRouteStep) value = MinRouteStep; + CourantFactor = 0.0; RouteStep = value; + + return 0; +} + +//============================================================================= + +int setSystemValue(int property, double value) +// +// Input: property = a system property code +// value = the property's new value +// Output: none +// Purpose: sets the value of a system property. +{ + int y, m, d, h, min, s; + + if (IsStartedFlag) + return ERR_API_NOT_ENDED; + + switch (property) + { + case swmm_ROUTESTEP: + return setRoutingStep(value); + case swmm_REPORTSTEP: + if (value > 0) + { + ReportStep = (int)value; + return 0; + } + else + { + return ERR_API_PROPERTY_VALUE; + } + + case swmm_NOREPORT: + RptFlags.disabled = (value > 0.0); + return 0; + case swmm_STARTDATE: + StartDateTime = value; + datetime_decodeDate(value, &y, &m, &d); + datetime_decodeTime(value, &h, &min, &s); + StartDate = datetime_encodeDate(y, m, d); + StartTime = datetime_encodeTime(h, min, s); + TotalDuration = floor((EndDate - StartDate) * SECperDAY + (EndTime - StartTime) * SECperDAY); + // convert total duration to milliseconds + TotalDuration *= 1000.0; + return 0; + case swmm_ENDDATE: + EndDateTime = value; + datetime_decodeDate(value, &y, &m, &d); + datetime_decodeTime(value, &h, &min, &s); + EndDate = datetime_encodeDate(y, m, d); + EndTime = datetime_encodeTime(h, min, s); + TotalDuration = floor((EndDate - StartDate) * SECperDAY + (EndTime - StartTime) * SECperDAY); + // convert total duration to milliseconds + TotalDuration *= 1000.0; + return 0; + case swmm_REPORTSTART: + ReportStart = value; + datetime_decodeDate(value, &y, &m, &d); + datetime_decodeTime(value, &h, &min, &s); + ReportStartDate = datetime_encodeDate(y, m, d); + ReportStartTime = datetime_encodeTime(h, min, s); + return 0; + + default: + return ERR_API_PROPERTY_TYPE; + } + } //============================================================================= @@ -1558,7 +1742,7 @@ DateTime getDateTime(double elapsedMsec) //============================================================================= -int isRelativePath(const char* fname) +static int isRelativePath(const char* fname) // // Input: fname = a file name // Output: returns 1 if fname's path is relative or 0 if absolute diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6849149e9..eb8c552cf 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,7 +2,7 @@ # CMakeLists.txt - CMake configuration file for swmm-solver/tests # # Created: Mar 4, 2020 -# Updated: May 21, 2020 +# Updated: Feb 01, 2024 # # Author: Michael E. Tryby # US EPA ORD/CESER @@ -13,6 +13,11 @@ include(../extern/boost.cmake) +# Add solver tests +add_subdirectory(solver) + + +# Add output file tests add_subdirectory(outfile) @@ -21,6 +26,12 @@ set(TEST_BIN_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$") # ctest doesn't like tests added in subdirectories so adding them here +add_test( + NAME test_solver + COMMAND "${TEST_BIN_DIRECTORY}/test_solver" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/solver/data +) + add_test(NAME test_output COMMAND "${TEST_BIN_DIRECTORY}/test_output" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/outfile/data diff --git a/tests/outfile/test_output.cpp b/tests/outfile/test_output.cpp index f4686245f..92e172ba4 100644 --- a/tests/outfile/test_output.cpp +++ b/tests/outfile/test_output.cpp @@ -12,6 +12,7 @@ //#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE "output" +#define BOOST_TEST_DYN_LINK #include #include @@ -288,7 +289,6 @@ BOOST_FIXTURE_TEST_CASE(test_getSystemSeries, Fixture) { BOOST_CHECK(check_cdd_float(test_vec, ref_vec, 3)); } - BOOST_FIXTURE_TEST_CASE(test_getSubcatchResult, Fixture) { error = SMO_getSubcatchResult(p_handle, 1, 1, &array, &array_dim); BOOST_REQUIRE(error == 0);