diff --git a/.travis.yml b/.travis.yml index 3349562..308cb2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ env: - TEST_HOME=nrtestsuite before_install: - - sudo apt-get -qq update +# - sudo apt-get -qq update - eval "${MATRIX_EVAL}" #install: diff --git a/README.md b/README.md index 56dc1fa..5e5949e 100755 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ EPANET {#epanet-readme} ## For EPANET-related questions and discussion -For community discussion, FAQ, and roadmapping of the project, go to the [Community Forum](http://community.wateranalytics.org/category/epanet). +For community discussion, FAQ, and roadmapping of the project, go to the [Community Forum](http://community.wateranalytics.org/category/epanet). ## What is on this Repository? The EPANET Library is a pressurized pipe network hydraulic and water quality analysis toolkit written in C. If you are interested in using/extending EPANET for academic, personal, or commercial use, then you've come to the right place. diff --git a/appveyor.yml b/appveyor.yml index 1d3f5ae..dd74d64 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,16 +21,15 @@ environment: GENERATOR: "Visual Studio 15 2017" GROUP: "SUPPORTED" BOOST_ROOT: "C:/Libraries/boost_1_67_0" - PLATFORM: "win32" - REF_BUILD_ID: "220dev5" + REF_BUILD_ID: "538_1" # New build on Visual Studio 15 2017 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 GENERATOR: "Visual Studio 15 2017 Win64" GROUP: "EXPERIMENTAL" BOOST_ROOT: "C:/Libraries/boost_1_67_0" PLATFORM: "win64" - REF_BUILD_ID: "381_2" + REF_BUILD_ID: "538_2" # called before repo cloning init: diff --git a/include/epanet2.bas b/include/epanet2.bas index f0ba641..c09fd4a 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -140,19 +140,29 @@ Public Const EN_CMD = 9 Public Const EN_DDA = 0 ' Demand driven analysis Public Const EN_PDA = 1 ' Pressure driven analysis -Public Const EN_TRIALS = 0 ' Hydraulic options +Public Const EN_TRIALS = 0 ' Simulation options Public Const EN_ACCURACY = 1 Public Const EN_TOLERANCE = 2 Public Const EN_EMITEXPON = 3 Public Const EN_DEMANDMULT = 4 Public Const EN_HEADERROR = 5 Public Const EN_FLOWCHANGE = 6 -Public Const EN_DEMANDDEFPAT = 7 -Public Const EN_HEADLOSSFORM = 8 -Public Const EN_GLOBALEFFIC = 9 -Public Const EN_GLOBALPRICE = 10 -Public Const EN_GLOBALPATTERN = 11 -Public Const EN_DEMANDCHARGE = 12 +Public Const EN_HEADLOSSFORM = 7 +Public Const EN_GLOBALEFFIC = 8 +Public Const EN_GLOBALPRICE = 9 +Public Const EN_GLOBALPATTERN = 10 +Public Const EN_DEMANDCHARGE = 11 +Public Const EN_SP_GRAVITY = 12 +Public Const EN_SP_VISCOS = 13 +Public Const EN_UNBALANCED = 14 +Public Const EN_CHECKFREQ = 15 +Public Const EN_MAXCHECK = 16 +Public Const EN_DAMPLIMIT = 17 +Public Const EN_SP_DIFFUS = 18 +Public Const EN_BULKORDER = 19 +Public Const EN_WALLORDER = 20 +Public Const EN_TANKORDER = 21 +Public Const EN_CONCENLIMIT = 22 Public Const EN_LOWLEVEL = 0 ' Control types Public Const EN_HILEVEL = 1 diff --git a/include/epanet2.h b/include/epanet2.h index 38448da..2bbc94f 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -343,7 +343,7 @@ extern "C" { EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); int DLLEXPORT ENgetcurve(int index, char* id, int *nPoints, - EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); + EN_API_FLOAT_TYPE *xValues, EN_API_FLOAT_TYPE *yValues); int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *xValues, EN_API_FLOAT_TYPE *yValues, int nPoints); diff --git a/include/epanet2.vb b/include/epanet2.vb index b4345eb..58cad9f 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -145,19 +145,29 @@ Public Const EN_CMD = 9 Public Const EN_DDA = 0 ' Demand driven analysis Public Const EN_PDA = 1 ' Pressure driven analysis -Public Const EN_TRIALS = 0 ' Hydraulic options +Public Const EN_TRIALS = 0 ' Simulation options Public Const EN_ACCURACY = 1 Public Const EN_TOLERANCE = 2 Public Const EN_EMITEXPON = 3 Public Const EN_DEMANDMULT = 4 Public Const EN_HEADERROR = 5 Public Const EN_FLOWCHANGE = 6 -Public Const EN_DEMANDDEFPAT = 7 -Public Const EN_HEADLOSSFORM = 8 -Public Const EN_GLOBALEFFIC = 9 -Public Const EN_GLOBALPRICE = 10 -Public Const EN_GLOBALPATTERN = 11 -Public Const EN_DEMANDCHARGE = 12 +Public Const EN_HEADLOSSFORM = 7 +Public Const EN_GLOBALEFFIC = 8 +Public Const EN_GLOBALPRICE = 9 +Public Const EN_GLOBALPATTERN = 10 +Public Const EN_DEMANDCHARGE = 11 +Public Const EN_SP_GRAVITY = 12 +Public Const EN_SP_VISCOS = 13 +Public Const EN_UNBALANCED = 14 +Public Const EN_CHECKFREQ = 15 +Public Const EN_MAXCHECK = 16 +Public Const EN_DAMPLIMIT = 17 +Public Const EN_SP_DIFFUS = 18 +Public Const EN_BULKORDER = 19 +Public Const EN_WALLORDER = 20 +Public Const EN_TANKORDER = 21 +Public Const EN_CONCENLIMIT = 22 Public Const EN_LOWLEVEL = 0 ' Control types Public Const EN_HILEVEL = 1 diff --git a/include/epanet2_2.h b/include/epanet2_2.h index c7774d3..f6ad002 100644 --- a/include/epanet2_2.h +++ b/include/epanet2_2.h @@ -1422,7 +1422,7 @@ typedef struct Project *EN_Project; @ref EN_MAXID characters. */ int DLLEXPORT EN_getcurve(EN_Project ph, int index, char* id, int *nPoints, - double **xValues, double **yValues); + double *xValues, double *yValues); /** @brief assigns a set of data points to a curve. diff --git a/include/epanet2_enums.h b/include/epanet2_enums.h index 558d670..52a4eb4 100644 --- a/include/epanet2_enums.h +++ b/include/epanet2_enums.h @@ -9,7 +9,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 03/17/2019 + Last Updated: 04/03/2019 ****************************************************************************** */ @@ -282,24 +282,34 @@ typedef enum { /// Simulation options /** -These options specify hydraulic convergence criteria, choice of head loss formula, and -several other parameters applied on a network-wide basis. They are accessed using the +These constants identify the hydraulic and water quality simulation options +that are applied on a network-wide basis. They are accessed using the @ref EN_getoption and @ref EN_setoption functions. */ typedef enum { - EN_TRIALS = 0, //!< Maximum hydraulic trials allowed - EN_ACCURACY = 1, //!< Maximum total relative flow change for hydraulic convergence + EN_TRIALS = 0, //!< Maximum hydraulic trials allowed for hydraulic convergence + EN_ACCURACY = 1, //!< Total normalized flow change for hydraulic convergence EN_TOLERANCE = 2, //!< Water quality tolerance EN_EMITEXPON = 3, //!< Exponent in emitter discharge formula EN_DEMANDMULT = 4, //!< Global demand multiplier EN_HEADERROR = 5, //!< Maximum head loss error for hydraulic convergence EN_FLOWCHANGE = 6, //!< Maximum flow change for hydraulic convergence - EN_DEFDEMANDPAT = 7, //!< Index of the default demand time pattern - EN_HEADLOSSFORM = 8, //!< Head loss formula (see @ref EN_HeadLossType) - EN_GLOBALEFFIC = 9, //!< Global pump efficiency (percent) - EN_GLOBALPRICE = 10, //!< Global energy price per KWH - EN_GLOBALPATTERN = 11, //!< Index of a global energy price pattern - EN_DEMANDCHARGE = 12 //!< Energy charge per max. KW usage + EN_HEADLOSSFORM = 7, //!< Head loss formula (see @ref EN_HeadLossType) + EN_GLOBALEFFIC = 8, //!< Global pump efficiency (percent) + EN_GLOBALPRICE = 9, //!< Global energy price per KWH + EN_GLOBALPATTERN = 10, //!< Index of a global energy price pattern + EN_DEMANDCHARGE = 11, //!< Energy charge per max. KW usage + EN_SP_GRAVITY = 12, //!< Specific gravity + EN_SP_VISCOS = 13, //!< Specific viscosity (relative to water at 20 deg C) + EN_UNBALANCED = 14, //!< Extra trials allowed if hydraulics don't converge + EN_CHECKFREQ = 15, //!< Frequency of hydraulic status checks + EN_MAXCHECK = 16, //!< Maximum trials for status checking + EN_DAMPLIMIT = 17, //!< Accuracy level where solution damping begins + EN_SP_DIFFUS = 18, //!< Specific diffusivity (relative to chlorine at 20 deg C) + EN_BULKORDER = 19, //!< Bulk water reaction order for pipes + EN_WALLORDER = 20, //!< Wall reaction order for pipes (either 0 or 1) + EN_TANKORDER = 21, //!< Bulk water reaction order for tanks + EN_CONCENLIMIT = 22 //!< Limiting concentration for growth reactions } EN_Option; /// Types of simple controls diff --git a/include/epanet_py.h b/include/epanet_py.h index e708756..a559d6e 100644 --- a/include/epanet_py.h +++ b/include/epanet_py.h @@ -139,7 +139,7 @@ int EXPORT_PY_API curv_getlength(Handle ph, int index, int *len); int EXPORT_PY_API curv_gettype(Handle ph, int curveIndex, int *outType); int EXPORT_PY_API curv_getvalue(Handle ph, int curveIndex, int pointIndex, double *x, double *y); int EXPORT_PY_API curv_setvalue(Handle ph, int curveIndex, int pointIndex, double x, double y); -int EXPORT_PY_API curv_get(Handle ph, int curveIndex, char* id, int *nValues, double **xValues, double **yValues); +int EXPORT_PY_API curv_get(Handle ph, int curveIndex, char* id, int *nValues, double *xValues, double *yValues); int EXPORT_PY_API curv_set(Handle ph, int index, double *x, double *y, int len); int EXPORT_PY_API scntl_add(Handle ph, int type, int linkIndex, double setting, int nodeIndex, double level, int *index); diff --git a/src/epanet.c b/src/epanet.c index c715b47..ac6476b 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -7,16 +7,20 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 03/17/2019 + Last Updated: 04/03/2019 ****************************************************************************** */ -#include -#include -#include -#ifndef __APPLE__ -#include +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include #endif +#include +#include + #include #include @@ -168,10 +172,7 @@ int DLLEXPORT EN_init(EN_Project p, const char *rptFile, const char *outFile, initunits(p); inittanks(p); convertunits(p); - - // Initialize the default demand pattern p->parser.MaxPats = 0; - getpatterns(p); p->Openflag = TRUE; return errcode; } @@ -226,10 +227,6 @@ int DLLEXPORT EN_open(EN_Project p, const char *inpFile, const char *rptFile, p->parser.InFile = NULL; } - // Free temporary linked lists used for Patterns & Curves - freeTmplist(p->parser.Patlist); - freeTmplist(p->parser.Curvelist); - // If using previously saved hydraulics file then open it if (p->outfile.Hydflag == USE) ERRCODE(openhydfile(p)); @@ -279,7 +276,7 @@ int DLLEXPORT EN_getcomment(EN_Project p, int object, int index, char *comment) /*---------------------------------------------------------------- ** Input: object = a type of object (see EN_ObjectType) ** index = the object's index -** Output: comment = the object's descriptive comment +** Output: comment = the object's descriptive comment ** Returns: error code ** Purpose: Retrieves an object's descriptive comment **---------------------------------------------------------------- @@ -840,7 +837,7 @@ int DLLEXPORT EN_closeQ(EN_Project p) if (!p->Openflag) return 102; closequal(p); p->quality.OpenQflag = FALSE; - closeoutfile(p); + closeoutfile(p); return 0; } @@ -1104,8 +1101,7 @@ int DLLEXPORT EN_getoption(EN_Project p, int option, double *value) v = qual->Ctol * Ucf[QUALITY]; break; case EN_EMITEXPON: - if (hyd->Qexp > 0.0) - v = 1.0 / hyd->Qexp; + if (hyd->Qexp > 0.0) v = 1.0 / hyd->Qexp; break; case EN_DEMANDMULT: v = hyd->Dmult; @@ -1116,9 +1112,6 @@ int DLLEXPORT EN_getoption(EN_Project p, int option, double *value) case EN_FLOWCHANGE: v = hyd->FlowChangeLimit * Ucf[FLOW]; break; - case EN_DEFDEMANDPAT: - v = hyd->DefPat; - break; case EN_HEADLOSSFORM: v = hyd->Formflag; break; @@ -1134,6 +1127,39 @@ int DLLEXPORT EN_getoption(EN_Project p, int option, double *value) case EN_DEMANDCHARGE: v = hyd->Dcost; break; + case EN_SP_GRAVITY: + v = hyd->SpGrav; + break; + case EN_SP_VISCOS: + v = hyd->Viscos / VISCOS; + break; + case EN_UNBALANCED: + v = hyd->ExtraIter; + break; + case EN_CHECKFREQ: + v = hyd->CheckFreq; + break; + case EN_MAXCHECK: + v = hyd->MaxCheck; + break; + case EN_DAMPLIMIT: + v = hyd->DampLimit; + break; + case EN_SP_DIFFUS: + v = qual->Diffus / DIFFUS; + break; + case EN_BULKORDER: + v = qual->BulkOrder; + break; + case EN_WALLORDER: + v = qual->WallOrder; + break; + case EN_TANKORDER: + v = qual->TankOrder; + break; + case EN_CONCENLIMIT: + v = qual->Climit * p->Ucf[QUALITY]; + break; default: return 251; @@ -1156,16 +1182,28 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) Hydraul *hyd = &p->hydraul; Quality *qual = &p->quality; - Snode *node; - Pdemand demand; - const int Njuncs = net->Njuncs; + int Njuncs = net->Njuncs; double *Ucf = p->Ucf; - int i, j; - int tmpPat, pat, error; - char tmpId[MAXID + 1]; + int i, j, pat; double Ke, n, ucf; if (!p->Openflag) return 102; + + // The EN_UNBALANCED option can be < 0 indicating that the simulation + // should be halted if no convergence is reached in EN_TRIALS. Other + // values set the number of additional trials to use with no more + // link status changes to achieve convergence. + if (option == EN_UNBALANCED) + { + hyd->ExtraIter = (int)value; + if (hyd->ExtraIter < 0) hyd->ExtraIter = -1; + return 0; + } + + // All other option values must be non-negative + if (value < 0.0) return 213; + + // Process the speficied option switch (option) { case EN_TRIALS: @@ -1174,12 +1212,11 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) break; case EN_ACCURACY: - if (value < 1.e-5 || value > 1.e-1) return 213; + if (value < 1.e-8 || value > 1.e-1) return 213; hyd->Hacc = value; break; case EN_TOLERANCE: - if (value < 0.0) return 213; qual->Ctol = value / Ucf[QUALITY]; break; @@ -1196,59 +1233,33 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) break; case EN_DEMANDMULT: - if (value <= 0.0) return 213; hyd->Dmult = value; break; case EN_HEADERROR: - if (value < 0.0) return 213; hyd->HeadErrorLimit = value / Ucf[HEAD]; break; case EN_FLOWCHANGE: - if (value < 0.0) return 213; hyd->FlowChangeLimit = value / Ucf[FLOW]; break; - case EN_DEFDEMANDPAT: - //check that the pattern exists or is set to zero to delete the default pattern - pat = ROUND(value); - if (pat < 0 || pat > net->Npats) return 205; - tmpPat = hyd->DefPat; - //get the new pattern ID - if (pat == 0) - { - strncpy(tmpId, p->parser.DefPatID, MAXID); - } - else - { - error = EN_getpatternid(p, pat, tmpId); - if (error != 0) return error; - } - // replace node patterns with default pattern - for (i = 1; i <= net->Nnodes; i++) - { - node = &net->Node[i]; - for (demand = node->D; demand != NULL; demand = demand->next) - { - if (demand->Pat == tmpPat) - { - demand->Pat = pat; - demand->Name = xstrcpy(&demand->Name, "", MAXID); - } - } - } - strncpy(p->parser.DefPatID, tmpId, MAXID); - hyd->DefPat = pat; + case EN_HEADLOSSFORM: + // Can't change if hydraulic solver is open + if (p->hydraul.OpenHflag) return 262; + i = ROUND(value); + if (i < HW || i > CM) return 213; + hyd->Formflag = i; + if (hyd->Formflag == HW) hyd->Hexp = 1.852; + else hyd->Hexp = 2.0; break; case EN_GLOBALEFFIC: - if (value <= 0.0 || value > 100.0) return 213; + if (value <= 1.0 || value > 100.0) return 213; hyd->Epump = value; break; case EN_GLOBALPRICE: - if (value < 0.0) return 213; hyd->Ecost = value; break; @@ -1259,10 +1270,53 @@ int DLLEXPORT EN_setoption(EN_Project p, int option, double value) break; case EN_DEMANDCHARGE: - if (value < 0.0) return 213; hyd->Dcost = value; break; + case EN_SP_GRAVITY: + if (value <= 0.0) return 213; + Ucf[PRESSURE] *= (value / hyd->SpGrav); + hyd->SpGrav = value; + break; + + case EN_SP_VISCOS: + if (value <= 0.0) return 213; + hyd->Viscos = value * VISCOS; + break; + + case EN_CHECKFREQ: + hyd->CheckFreq = (int)value; + break; + + case EN_MAXCHECK: + hyd->MaxCheck = (int)value; + break; + + case EN_DAMPLIMIT: + hyd->DampLimit = value; + break; + + case EN_SP_DIFFUS: + qual->Diffus = value * DIFFUS; + break; + + case EN_BULKORDER: + qual->BulkOrder = value; + break; + + case EN_WALLORDER: + if (value == 0.0 || value == 1.0) qual->WallOrder = value; + else return 213; + break; + + case EN_TANKORDER: + qual->TankOrder = value; + break; + + case EN_CONCENLIMIT: + qual->Climit = value / p->Ucf[QUALITY]; + break; + default: return 251; } @@ -1589,13 +1643,20 @@ int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, char *chemName, Quality *qual = &p->quality; double *Ucf = p->Ucf; - int i; + int i, oldQualFlag, traceNodeIndex; double ccf = 1.0; if (!p->Openflag) return 102; if (qual->OpenQflag) return 262; - if (qualType < EN_NONE || qualType > EN_TRACE) return 251; - qual->Qualflag = (char)qualType; + if (qualType < NONE || qualType > TRACE) return 251; + if (qualType == TRACE) + { + traceNodeIndex = findnode(net, traceNode); + if (traceNodeIndex == 0) return 212; + } + + oldQualFlag = qual->Qualflag; + qual->Qualflag = qualType; qual->Ctol *= Ucf[QUALITY]; if (qual->Qualflag == CHEM) // Chemical analysis { @@ -1623,7 +1684,7 @@ int DLLEXPORT EN_setqualtype(EN_Project p, int qualType, char *chemName, // When changing from CHEM to AGE or TRACE, nodes initial quality // values must be returned to their original ones - if ((qual->Qualflag == AGE || qual->Qualflag == TRACE) & (Ucf[QUALITY] != 1)) + if ((qual->Qualflag == AGE || qual->Qualflag == TRACE) && oldQualFlag == CHEM) { for (i = 1; i <= p->network.Nnodes; i++) { @@ -1693,7 +1754,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType) demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); demand->Base = 0.0; - demand->Pat = hyd->DefPat; // Use default pattern + demand->Pat = 0; demand->Name = NULL; demand->next = NULL; node->D = demand; @@ -2778,7 +2839,7 @@ int DLLEXPORT EN_setdemandname(EN_Project p, int nodeIndex, int demandIndex, // Check that demandName is not too long if (strlen(demandName) > MAXID) return 250; - + // Locate demand category record and assign demandName to it for (d = p->network.Node[nodeIndex].D; n < demandIndex && d->next != NULL; d = d->next) n++; @@ -3880,7 +3941,6 @@ int DLLEXPORT EN_addpattern(EN_Project p, char *id) { Network *net = &p->network; Parser *parser = &p->parser; - Hydraul *hyd = &p->hydraul; int i, n, err = 0; Spattern *pat; @@ -3915,9 +3975,6 @@ int DLLEXPORT EN_addpattern(EN_Project p, char *id) // Update the number of patterns net->Npats = n; parser->MaxPats = n; - - // Make new pattern be default demand pattern if name matches - if (strcmp(id, parser->DefPatID) == 0) hyd->DefPat = n; return 0; } @@ -3946,14 +4003,6 @@ int DLLEXPORT EN_deletepattern(EN_Project p, int index) // Adjust references by other objects to patterns adjustpatterns(net, index); - // Modify default demand pattern - if (hyd->DefPat == index) - { - hyd->DefPat = 0; - strcpy(parser->DefPatID, ""); - } - else if (hyd->DefPat > index) hyd->DefPat--; - // Modify global energy price pattern if (hyd->Epat == index) hyd->Epat = 0; else if (hyd->Epat > index) hyd->Epat--; @@ -4179,6 +4228,7 @@ int DLLEXPORT EN_addcurve(EN_Project p, char *id) curve = &net->Curve[n]; strcpy(curve->ID, id); curve->Comment = NULL; + curve->Capacity = 1; curve->Npts = 1; curve->Type = GENERIC_CURVE; curve->X = (double *)calloc(1, sizeof(double)); @@ -4250,20 +4300,11 @@ int DLLEXPORT EN_getcurveindex(EN_Project p, char *id, int *index) **---------------------------------------------------------------- */ { - int i; - *index = 0; if (!p->Openflag) return 102; - for (i = 1; i <= p->network.Ncurves; i++) - { - if (strcmp(id, p->network.Curve[i].ID) == 0) - { - *index = i; - return 0; - } - } - *index = 0; - return 206; + *index = findcurve(&p->network, id); + if (*index == 0) return 206; + return 0; } int DLLEXPORT EN_getcurveid(EN_Project p, int index, char *id) @@ -4352,8 +4393,8 @@ int DLLEXPORT EN_getcurvevalue(EN_Project p, int curveIndex, int pointIndex, if (!p->Openflag) return 102; if (curveIndex < 1 || curveIndex > p->network.Ncurves) return 206; if (pointIndex < 1 || pointIndex > p->network.Curve[curveIndex].Npts) return 251; - *x = (double)p->network.Curve[curveIndex].X[pointIndex - 1]; - *y = (double)p->network.Curve[curveIndex].Y[pointIndex - 1]; + *x = p->network.Curve[curveIndex].X[pointIndex - 1]; + *y = p->network.Curve[curveIndex].Y[pointIndex - 1]; return 0; } @@ -4367,23 +4408,48 @@ int DLLEXPORT EN_setcurvevalue(EN_Project p, int curveIndex, int pointIndex, ** Output: none ** Returns: error code ** Purpose: sets the value of a specific point on a data curve +** Note: if pointIndex exceeds the curve's length a new point is added. **---------------------------------------------------------------- */ { Network *net = &p->network; Scurve *curve; + double x1 = -1.e37, x2 = 1.e37; + int n = pointIndex - 1; + // Check for valid input if (!p->Openflag) return 102; if (curveIndex <= 0 || curveIndex > net->Ncurves) return 206; curve = &net->Curve[curveIndex]; - if (pointIndex <= 0 || pointIndex > curve->Npts) return 251; - curve->X[pointIndex - 1] = x; - curve->Y[pointIndex - 1] = y; + if (pointIndex <= 0) return 251; + + // Check that new point maintains increasing x values + if (n - 1 >= 0) x1 = curve->X[n-1]; + if (n + 1 < curve->Npts) x2 = curve->X[n+1]; + if (x <= x1 || x >= x2) return 230; + + // Expand curve if need be + if (pointIndex > curve->Npts) pointIndex = curve->Npts + 1; + if (pointIndex >= curve->Capacity) + { + if (resizecurve(curve, curve->Capacity + 10) > 0) return 101; + } + + // Increase curve's number of points if need be + if (pointIndex > curve->Npts) + { + curve->Npts++; + n = curve->Npts - 1; + } + + // Insert new point into curve + curve->X[n] = x; + curve->Y[n] = y; return 0; } int DLLEXPORT EN_getcurve(EN_Project p, int index, char *id, int *nPoints, - double **xValues, double **yValues) + double *xValues, double *yValues) /*---------------------------------------------------------------- ** Input: index = data curve index ** Output: id = ID name of data curve @@ -4408,8 +4474,8 @@ int DLLEXPORT EN_getcurve(EN_Project p, int index, char *id, int *nPoints, *nPoints = curve->Npts; for (i = 0; i < curve->Npts; i++) { - *xValues[i] = curve->X[i]; - *yValues[i] = curve->Y[i]; + xValues[i] = curve->X[i]; + yValues[i] = curve->Y[i]; } return 0; } @@ -4438,15 +4504,12 @@ int DLLEXPORT EN_setcurve(EN_Project p, int index, double *xValues, // Check that x values are increasing for (j = 1; j < nPoints; j++) if (xValues[j-1] >= xValues[j]) return 230; - // Re-set number of points & reallocate memory for values + // Expand size of curve's data arrays if need be curve = &net->Curve[index]; - curve->Npts = nPoints; - curve->X = (double *)realloc(curve->X, nPoints * sizeof(double)); - curve->Y = (double *)realloc(curve->Y, nPoints * sizeof(double)); - if (curve->X == NULL) return 101; - if (curve->Y == NULL) return 101; + if (resizecurve(curve, nPoints) > 0) return 101; // Load values into curve + curve->Npts = nPoints; for (j = 0; j < nPoints; j++) { curve->X[j] = xValues[j]; diff --git a/src/epanet2.c b/src/epanet2.c index 935d6c4..2490c64 100644 --- a/src/epanet2.c +++ b/src/epanet2.c @@ -10,10 +10,12 @@ Last Updated: 03/17/2019 ****************************************************************************** */ -#ifndef __APPLE__ -#include +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include #else -#include + #include #endif #include @@ -645,7 +647,7 @@ int DLLEXPORT ENsetcurvevalue(int curveIndex, int pointIndex, EN_API_FLOAT_TYPE } int DLLEXPORT ENgetcurve(int index, char *id, int *nPoints, - EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues) + EN_API_FLOAT_TYPE *xValues, EN_API_FLOAT_TYPE *yValues) { int i; Network *net = &_defaultProject->network; @@ -657,8 +659,8 @@ int DLLEXPORT ENgetcurve(int index, char *id, int *nPoints, *nPoints = curve->Npts; for (i = 0; i < curve->Npts; i++) { - *xValues[i] = (EN_API_FLOAT_TYPE)curve->X[i]; - *yValues[i] = (EN_API_FLOAT_TYPE)curve->Y[i]; + xValues[i] = (EN_API_FLOAT_TYPE)curve->X[i]; + yValues[i] = (EN_API_FLOAT_TYPE)curve->Y[i]; } return 0; } diff --git a/src/epanet_py.c b/src/epanet_py.c index 3ee3898..6cc3302 100644 --- a/src/epanet_py.c +++ b/src/epanet_py.c @@ -11,8 +11,13 @@ ****************************************************************************** */ - -#include +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include "epanet_py.h" @@ -39,7 +44,7 @@ int EXPORT_PY_API proj_create(Handle *ph) if (handle != NULL) { EN_createproject(&handle->project); - handle->error = error_new_manager(&error_lookup); + handle->error = create_error_manager(&error_lookup); *ph = handle; return 0; } @@ -55,7 +60,7 @@ int EXPORT_PY_API proj_delete(Handle *ph) else { EN_deleteproject(&handle->project); - error_dst_manager(handle->error); + delete_error_manager(handle->error); } free(handle); *ph = NULL; @@ -67,51 +72,51 @@ int EXPORT_PY_API proj_run(Handle ph, const char *input_path, const char *report_path, const char *output_path) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_runproject(pr->project, input_path, report_path, output_path, NULL)); + return set_error(pr->error, EN_runproject(pr->project, input_path, report_path, output_path, NULL)); } int EXPORT_PY_API proj_init(Handle ph, const char *rptFile, const char *outFile, EN_FlowUnits unitsType, EN_HeadLossType headLossType) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_init(pr->project, rptFile, outFile, unitsType, headLossType)); + return set_error(pr->error, EN_init(pr->project, rptFile, outFile, unitsType, headLossType)); } int EXPORT_PY_API proj_open(Handle ph, const char *inpFile, const char *rptFile, const char *binOutFile) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_open(pr->project, inpFile, rptFile, binOutFile)); + return set_error(pr->error, EN_open(pr->project, inpFile, rptFile, binOutFile)); } int EXPORT_PY_API proj_gettitle(Handle ph, char *line1, char *line2, char *line3) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_gettitle(pr->project, line1, line2, line3)); + return set_error(pr->error, EN_gettitle(pr->project, line1, line2, line3)); } int EXPORT_PY_API proj_settitle(Handle ph, const char *line1, const char *line2, const char *line3) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_settitle(pr->project, (char *)line1, (char *)line2, (char *)line3)); + return set_error(pr->error, EN_settitle(pr->project, (char *)line1, (char *)line2, (char *)line3)); } int EXPORT_PY_API proj_getcount(Handle ph, EN_CountType code, int *count) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcount(pr->project, code, count)); + return set_error(pr->error, EN_getcount(pr->project, code, count)); } int EXPORT_PY_API proj_savefile(Handle ph, const char *filename) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_saveinpfile(pr->project, filename)); + return set_error(pr->error, EN_saveinpfile(pr->project, filename)); } int EXPORT_PY_API proj_close(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_close(pr->project)); + return set_error(pr->error, EN_close(pr->project)); } @@ -120,55 +125,55 @@ int EXPORT_PY_API proj_close(Handle ph) int EXPORT_PY_API hydr_solve(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_solveH(pr->project)); + return set_error(pr->error, EN_solveH(pr->project)); } int EXPORT_PY_API hydr_save(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_saveH(pr->project)); + return set_error(pr->error, EN_saveH(pr->project)); } int EXPORT_PY_API hydr_open(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_openH(pr->project)); + return set_error(pr->error, EN_openH(pr->project)); } int EXPORT_PY_API hydr_init(Handle ph, EN_InitHydOption saveFlag) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_initH(pr->project, saveFlag)); + return set_error(pr->error, EN_initH(pr->project, saveFlag)); } int EXPORT_PY_API hydr_run(Handle ph, long *currentTime) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_runH(pr->project, currentTime)); + return set_error(pr->error, EN_runH(pr->project, currentTime)); } int EXPORT_PY_API hydr_next(Handle ph, long *tStep) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_nextH(pr->project, tStep)); + return set_error(pr->error, EN_nextH(pr->project, tStep)); } int EXPORT_PY_API hydr_close(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_closeH(pr->project)); + return set_error(pr->error, EN_closeH(pr->project)); } int EXPORT_PY_API hydr_savefile(Handle ph, char *filename) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_savehydfile(pr->project, filename)); + return set_error(pr->error, EN_savehydfile(pr->project, filename)); } int EXPORT_PY_API hydr_usefile(Handle ph, char *filename) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_usehydfile(pr->project, filename)); + return set_error(pr->error, EN_usehydfile(pr->project, filename)); } @@ -177,43 +182,43 @@ int EXPORT_PY_API hydr_usefile(Handle ph, char *filename) int EXPORT_PY_API qual_solve(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_solveQ(pr->project)); + return set_error(pr->error, EN_solveQ(pr->project)); } int EXPORT_PY_API qual_open(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_openQ(pr->project)); + return set_error(pr->error, EN_openQ(pr->project)); } int EXPORT_PY_API qual_init(Handle ph, EN_InitHydOption saveFlag) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_initQ(pr->project, saveFlag)); + return set_error(pr->error, EN_initQ(pr->project, saveFlag)); } int EXPORT_PY_API qual_run(Handle ph, long *currentTime) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_runQ(pr->project, currentTime)); + return set_error(pr->error, EN_runQ(pr->project, currentTime)); } int EXPORT_PY_API qual_next(Handle ph, long *tStep) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_nextQ(pr->project, tStep)); + return set_error(pr->error, EN_nextQ(pr->project, tStep)); } int EXPORT_PY_API qual_step(Handle ph, long *timeLeft) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_stepQ(pr->project, timeLeft)); + return set_error(pr->error, EN_stepQ(pr->project, timeLeft)); } int EXPORT_PY_API qual_close(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_closeQ(pr->project)); + return set_error(pr->error, EN_closeQ(pr->project)); } @@ -222,43 +227,43 @@ int EXPORT_PY_API qual_close(Handle ph) int EXPORT_PY_API rprt_writeline(Handle ph, char *line) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_writeline(pr->project, line)); + return set_error(pr->error, EN_writeline(pr->project, line)); } int EXPORT_PY_API rprt_writeresults(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_report(pr->project)); + return set_error(pr->error, EN_report(pr->project)); } int EXPORT_PY_API rprt_clear(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_clearreport(pr->project)); + return set_error(pr->error, EN_clearreport(pr->project)); } int EXPORT_PY_API rprt_reset(Handle ph) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_resetreport(pr->project)); + return set_error(pr->error, EN_resetreport(pr->project)); } int EXPORT_PY_API rprt_set(Handle ph, char *reportCommand) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setreport(pr->project, reportCommand)); + return set_error(pr->error, EN_setreport(pr->project, reportCommand)); } int EXPORT_PY_API rprt_setlevel(Handle ph, EN_StatusReport code) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setstatusreport(pr->project, code)); + return set_error(pr->error, EN_setstatusreport(pr->project, code)); } int EXPORT_PY_API rprt_anlysstats(Handle ph, EN_AnalysisStatistic code, double* value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getstatistic(pr->project, code, value)); + return set_error(pr->error, EN_getstatistic(pr->project, code, value)); } @@ -267,55 +272,55 @@ int EXPORT_PY_API rprt_anlysstats(Handle ph, EN_AnalysisStatistic code, double* int EXPORT_PY_API anlys_getoption(Handle ph, EN_Option code, double *value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getoption(pr->project, (int)code, value)); + return set_error(pr->error, EN_getoption(pr->project, (int)code, value)); } int EXPORT_PY_API anlys_setoption(Handle ph, EN_Option code, double value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setoption(pr->project, (int)code, value)); + return set_error(pr->error, EN_setoption(pr->project, (int)code, value)); } int EXPORT_PY_API anlys_getflowunits(Handle ph, int *code) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getflowunits(pr->project, code)); + return set_error(pr->error, EN_getflowunits(pr->project, code)); } int EXPORT_PY_API anlys_setflowunits(Handle ph, EN_FlowUnits code) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setflowunits(pr->project, code)); + return set_error(pr->error, EN_setflowunits(pr->project, code)); } int EXPORT_PY_API anlys_gettimeparam(Handle ph, EN_TimeParameter code, long *value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_gettimeparam(pr->project, code, value)); + return set_error(pr->error, EN_gettimeparam(pr->project, code, value)); } int EXPORT_PY_API anlys_settimeparam(Handle ph, EN_TimeParameter code, long value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_settimeparam(pr->project, code, value)); + return set_error(pr->error, EN_settimeparam(pr->project, code, value)); } int EXPORT_PY_API anlys_getqualinfo(Handle ph, int *qualcode, char *chemname, char *chemunits, int *tracenode) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getqualinfo(pr->project, qualcode, chemname, chemunits, tracenode)); + return set_error(pr->error, EN_getqualinfo(pr->project, qualcode, chemname, chemunits, tracenode)); } int EXPORT_PY_API anlys_getqualtype(Handle ph, int *qualcode, int *tracenode) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getqualtype(pr->project, qualcode, tracenode)); + return set_error(pr->error, EN_getqualtype(pr->project, qualcode, tracenode)); } int EXPORT_PY_API anlys_setqualtype(Handle ph, EN_QualityType qualcode, char *chemname, char *chemunits, char *tracenode) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setqualtype(pr->project, qualcode, chemname, chemunits, tracenode)); + return set_error(pr->error, EN_setqualtype(pr->project, qualcode, chemname, chemunits, tracenode)); } @@ -324,61 +329,61 @@ int EXPORT_PY_API anlys_setqualtype(Handle ph, EN_QualityType qualcode, char *ch int EXPORT_PY_API node_add(Handle ph, char *id, EN_NodeType nodeType) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_addnode(pr->project, id, nodeType)); + return set_error(pr->error, EN_addnode(pr->project, id, nodeType)); } int EXPORT_PY_API node_delete(Handle ph, int index, int actionCode) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_deletenode(pr->project, index, actionCode)); + return set_error(pr->error, EN_deletenode(pr->project, index, actionCode)); } int EXPORT_PY_API node_getindex(Handle ph, char *id, int *index) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getnodeindex(pr->project, id, index)); + return set_error(pr->error, EN_getnodeindex(pr->project, id, index)); } int EXPORT_PY_API node_getid(Handle ph, int index, char *id) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getnodeid(pr->project, index, id)); + return set_error(pr->error, EN_getnodeid(pr->project, index, id)); } int EXPORT_PY_API node_setid(Handle ph, int index, char *newid) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getnodeid(pr->project, index, newid)); + return set_error(pr->error, EN_getnodeid(pr->project, index, newid)); } int EXPORT_PY_API node_gettype(Handle ph, int index, int *code) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getnodetype(pr->project, index, code)); + return set_error(pr->error, EN_getnodetype(pr->project, index, code)); } int EXPORT_PY_API node_getvalue(Handle ph, int index, EN_NodeProperty code, double *value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getnodevalue(pr->project, index, (int)code, value)); + return set_error(pr->error, EN_getnodevalue(pr->project, index, (int)code, value)); } int EXPORT_PY_API node_setvalue(Handle ph, int index, EN_NodeProperty code, double value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setnodevalue(pr->project, index, (int)code, value)); + return set_error(pr->error, EN_setnodevalue(pr->project, index, (int)code, value)); } int EXPORT_PY_API node_getcoord(Handle ph, int index, double *x, double *y) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcoord(pr->project, index, x, y)); + return set_error(pr->error, EN_getcoord(pr->project, index, x, y)); } int EXPORT_PY_API node_setcoord(Handle ph, int index, double x, double y) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setcoord(pr->project, index, x, y)); + return set_error(pr->error, EN_setcoord(pr->project, index, x, y)); } @@ -387,55 +392,55 @@ int EXPORT_PY_API node_setcoord(Handle ph, int index, double x, double y) int EXPORT_PY_API dmnd_getmodel(Handle ph, int *type, double *pmin, double *preq, double *pexp) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getdemandmodel(pr->project, type, pmin, preq, pexp)); + return set_error(pr->error, EN_getdemandmodel(pr->project, type, pmin, preq, pexp)); } int EXPORT_PY_API dmnd_setmodel(Handle ph, int type, double pmin, double preq, double pexp) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setdemandmodel(pr->project, type, pmin, preq, pexp)); + return set_error(pr->error, EN_setdemandmodel(pr->project, type, pmin, preq, pexp)); } int EXPORT_PY_API dmnd_getcount(Handle ph, int nodeIndex, int *numDemands) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getnumdemands(pr->project, nodeIndex, numDemands)); + return set_error(pr->error, EN_getnumdemands(pr->project, nodeIndex, numDemands)); } int EXPORT_PY_API dmnd_getbase(Handle ph, int nodeIndex, int demandIndex, double *baseDemand) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getbasedemand(pr->project, nodeIndex, demandIndex, baseDemand)); + return set_error(pr->error, EN_getbasedemand(pr->project, nodeIndex, demandIndex, baseDemand)); } int EXPORT_PY_API dmnd_setbase(Handle ph, int nodeIndex, int demandIndex, double baseDemand) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setbasedemand(pr->project, nodeIndex, demandIndex, baseDemand)); + return set_error(pr->error, EN_setbasedemand(pr->project, nodeIndex, demandIndex, baseDemand)); } int EXPORT_PY_API dmnd_getpattern(Handle ph, int nodeIndex, int demandIndex, int *patIndex) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getdemandpattern(pr->project, nodeIndex, demandIndex, patIndex)); + return set_error(pr->error, EN_getdemandpattern(pr->project, nodeIndex, demandIndex, patIndex)); } int EXPORT_PY_API dmnd_setpattern(Handle ph, int nodeIndex, int demandIndex, int patIndex) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setdemandpattern(pr->project, nodeIndex, demandIndex, patIndex)); + return set_error(pr->error, EN_setdemandpattern(pr->project, nodeIndex, demandIndex, patIndex)); } int EXPORT_PY_API dmnd_getname(Handle ph, int nodeIndex, int demandIdx, char *demandName) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getdemandname(pr->project, nodeIndex, demandIdx, demandName)); + return set_error(pr->error, EN_getdemandname(pr->project, nodeIndex, demandIdx, demandName)); } int EXPORT_PY_API dmnd_setname(Handle ph, int nodeIndex, int demandIdx, char *demandName) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setdemandname(pr->project, nodeIndex, demandIdx, demandName)); + return set_error(pr->error, EN_setdemandname(pr->project, nodeIndex, demandIdx, demandName)); } @@ -444,67 +449,67 @@ int EXPORT_PY_API dmnd_setname(Handle ph, int nodeIndex, int demandIdx, char *de int EXPORT_PY_API link_add(Handle ph, char *id, EN_LinkType linkType, char *fromNode, char *toNode) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_addlink(pr->project, id, linkType, fromNode, toNode)); + return set_error(pr->error, EN_addlink(pr->project, id, linkType, fromNode, toNode)); } int EXPORT_PY_API link_delete(Handle ph, int index, int actionCode) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_deletelink(pr->project, index, actionCode)); + return set_error(pr->error, EN_deletelink(pr->project, index, actionCode)); } int EXPORT_PY_API link_getindex(Handle ph, char *id, int *index) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getlinkindex(pr->project, id, index)); + return set_error(pr->error, EN_getlinkindex(pr->project, id, index)); } int EXPORT_PY_API link_getid(Handle ph, int index, char *id) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getlinkid(pr->project, index, id)); + return set_error(pr->error, EN_getlinkid(pr->project, index, id)); } int EXPORT_PY_API link_setid(Handle ph, int index, char *newid) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setlinkid(pr->project, index, newid)); + return set_error(pr->error, EN_setlinkid(pr->project, index, newid)); } int EXPORT_PY_API link_gettype(Handle ph, int index, int *code) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getlinktype(pr->project, index, code)); + return set_error(pr->error, EN_getlinktype(pr->project, index, code)); } int EXPORT_PY_API link_settype(Handle ph, int *index, EN_LinkType type, int actionCode) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setlinktype(pr->project, index, type, actionCode)); + return set_error(pr->error, EN_setlinktype(pr->project, index, type, actionCode)); } int EXPORT_PY_API link_getnodes(Handle ph, int index, int *node1, int *node2) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getlinknodes(pr->project, index, node1, node2)); + return set_error(pr->error, EN_getlinknodes(pr->project, index, node1, node2)); } int EXPORT_PY_API link_setnodes(Handle ph, int index, int node1, int node2) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setlinknodes(pr->project, index, node1, node2)); + return set_error(pr->error, EN_setlinknodes(pr->project, index, node1, node2)); } int EXPORT_PY_API link_getvalue(Handle ph, int index, EN_LinkProperty code, double *value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getlinkvalue(pr->project, index, code, value)); + return set_error(pr->error, EN_getlinkvalue(pr->project, index, code, value)); } int EXPORT_PY_API link_setvalue(Handle ph, int index, int code, double value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setlinkvalue(pr->project, index, code, value)); + return set_error(pr->error, EN_setlinkvalue(pr->project, index, code, value)); } @@ -513,19 +518,19 @@ int EXPORT_PY_API link_setvalue(Handle ph, int index, int code, double value) int EXPORT_PY_API pump_gettype(Handle ph, int linkIndex, int *outType) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getpumptype(pr->project, linkIndex, outType)); + return set_error(pr->error, EN_getpumptype(pr->project, linkIndex, outType)); } int EXPORT_PY_API pump_getheadcurveindex(Handle ph, int pumpIndex, int *curveIndex) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getheadcurveindex(pr->project, pumpIndex, curveIndex)); + return set_error(pr->error, EN_getheadcurveindex(pr->project, pumpIndex, curveIndex)); } int EXPORT_PY_API pump_setheadcurveindex(Handle ph, int pumpIndex, int curveIndex) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setheadcurveindex(pr->project, pumpIndex, curveIndex)); + return set_error(pr->error, EN_setheadcurveindex(pr->project, pumpIndex, curveIndex)); } @@ -534,49 +539,49 @@ int EXPORT_PY_API pump_setheadcurveindex(Handle ph, int pumpIndex, int curveInde int EXPORT_PY_API ptrn_add(Handle ph, char *id) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_addpattern(pr->project, id)); + return set_error(pr->error, EN_addpattern(pr->project, id)); } int EXPORT_PY_API ptrn_getindex(Handle ph, char *id, int *index) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getpatternindex(pr->project, id, index)); + return set_error(pr->error, EN_getpatternindex(pr->project, id, index)); } int EXPORT_PY_API ptrn_getid(Handle ph, int index, char *id) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getpatternid(pr->project, index, id)); + return set_error(pr->error, EN_getpatternid(pr->project, index, id)); } int EXPORT_PY_API ptrn_getlength(Handle ph, int index, int *len) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getpatternlen(pr->project, index, len)); + return set_error(pr->error, EN_getpatternlen(pr->project, index, len)); } int EXPORT_PY_API ptrn_getvalue(Handle ph, int index, int period, double *value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getpatternvalue(pr->project, index, period, value)); + return set_error(pr->error, EN_getpatternvalue(pr->project, index, period, value)); } int EXPORT_PY_API ptrn_setvalue(Handle ph, int index, int period, double value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setpatternvalue(pr->project, index, period, value)); + return set_error(pr->error, EN_setpatternvalue(pr->project, index, period, value)); } int EXPORT_PY_API ptrn_getavgvalue(Handle ph, int index, double *value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getaveragepatternvalue(pr->project, index, value)); + return set_error(pr->error, EN_getaveragepatternvalue(pr->project, index, value)); } int EXPORT_PY_API ptrn_set(Handle ph, int index, double *values, int len) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setpattern(pr->project, index, values, len)); + return set_error(pr->error, EN_setpattern(pr->project, index, values, len)); } @@ -585,55 +590,55 @@ int EXPORT_PY_API ptrn_set(Handle ph, int index, double *values, int len) int EXPORT_PY_API curv_add(Handle ph, char *id) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_addcurve(pr->project, id)); + return set_error(pr->error, EN_addcurve(pr->project, id)); } int EXPORT_PY_API curv_getindex(Handle ph, char *id, int *index) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcurveindex(pr->project, id, index)); + return set_error(pr->error, EN_getcurveindex(pr->project, id, index)); } int EXPORT_PY_API curv_getid(Handle ph, int index, char *id) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcurveid(pr->project, index, id)); + return set_error(pr->error, EN_getcurveid(pr->project, index, id)); } int EXPORT_PY_API curv_getlength(Handle ph, int index, int *len) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcurvelen(pr->project, index, len)); + return set_error(pr->error, EN_getcurvelen(pr->project, index, len)); } int EXPORT_PY_API curv_gettype(Handle ph, int index, int *type) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcurvetype(pr->project, index, type)); + return set_error(pr->error, EN_getcurvetype(pr->project, index, type)); } int EXPORT_PY_API curv_getvalue(Handle ph, int curveIndex, int pointIndex, double *x, double *y) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcurvevalue(pr->project, curveIndex, pointIndex, x, y)); + return set_error(pr->error, EN_getcurvevalue(pr->project, curveIndex, pointIndex, x, y)); } int EXPORT_PY_API curv_setvalue(Handle ph, int curveIndex, int pointIndex, double x, double y) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setcurvevalue(pr->project, curveIndex, pointIndex, x, y)); + return set_error(pr->error, EN_setcurvevalue(pr->project, curveIndex, pointIndex, x, y)); } -int EXPORT_PY_API curv_get(Handle ph, int curveIndex, char* id, int *nValues, double **xValues, double **yValues) +int EXPORT_PY_API curv_get(Handle ph, int curveIndex, char* id, int *nValues, double *xValues, double *yValues) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcurve(pr->project, curveIndex, id, nValues, xValues, yValues)); + return set_error(pr->error, EN_getcurve(pr->project, curveIndex, id, nValues, xValues, yValues)); } int EXPORT_PY_API curv_set(Handle ph, int index, double *x, double *y, int len) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setcurve(pr->project, index, x, y, len)); + return set_error(pr->error, EN_setcurve(pr->project, index, x, y, len)); } @@ -642,25 +647,25 @@ int EXPORT_PY_API curv_set(Handle ph, int index, double *x, double *y, int len) int EXPORT_PY_API scntl_add(Handle ph, int type, int linkIndex, double setting, int nodeIndex, double level, int *index) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_addcontrol(pr->project, type, linkIndex, setting, nodeIndex, level, index)); + return set_error(pr->error, EN_addcontrol(pr->project, type, linkIndex, setting, nodeIndex, level, index)); } int EXPORT_PY_API scntl_delete(Handle ph, int index) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_deletecontrol(pr->project, index)); + return set_error(pr->error, EN_deletecontrol(pr->project, index)); } int EXPORT_PY_API scntl_get(Handle ph, int controlIndex, int *controlType, int *linkIndex, double *setting, int *nodeIndex, double *level) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getcontrol(pr->project, controlIndex, controlType, linkIndex, setting, nodeIndex, level)); + return set_error(pr->error, EN_getcontrol(pr->project, controlIndex, controlType, linkIndex, setting, nodeIndex, level)); } int EXPORT_PY_API scntl_set(Handle ph, int cindex, int ctype, int lindex, double setting, int nindex, double level) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setcontrol(pr->project, cindex, ctype, lindex, setting, nindex, level)); + return set_error(pr->error, EN_setcontrol(pr->project, cindex, ctype, lindex, setting, nindex, level)); } @@ -669,85 +674,85 @@ int EXPORT_PY_API scntl_set(Handle ph, int cindex, int ctype, int lindex, double int EXPORT_PY_API rcntl_add(Handle ph, char *rule) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_addrule(pr->project, rule)); + return set_error(pr->error, EN_addrule(pr->project, rule)); } int EXPORT_PY_API rcntl_delete(Handle ph, int index) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_deleterule(pr->project, index)); + return set_error(pr->error, EN_deleterule(pr->project, index)); } int EXPORT_PY_API rcntl_get(Handle ph, int index, int *nPremises, int *nThenActions, int *nElseActions, double *priority) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getrule(pr->project, index, nPremises, nThenActions, nElseActions, priority)); + return set_error(pr->error, EN_getrule(pr->project, index, nPremises, nThenActions, nElseActions, priority)); } int EXPORT_PY_API rcntl_getid(Handle ph, int index, char *id) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getruleID(pr->project, index, id)); + return set_error(pr->error, EN_getruleID(pr->project, index, id)); } int EXPORT_PY_API rcntl_getpremise(Handle ph, int ruleIndex, int premiseIndex, int *logop, int *object, int *objIndex, int *variable, int *relop, int *status, double *value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getpremise(pr->project, ruleIndex, premiseIndex, logop, object, objIndex, variable, relop, status, value)); + return set_error(pr->error, EN_getpremise(pr->project, ruleIndex, premiseIndex, logop, object, objIndex, variable, relop, status, value)); } int EXPORT_PY_API rcntl_setpremise(Handle ph, int ruleIndex, int premiseIndex, int logop, int object, int objIndex, int variable, int relop, int status, double value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setpremise(pr->project, ruleIndex, premiseIndex, logop, object, objIndex, variable, relop, status, value)); + return set_error(pr->error, EN_setpremise(pr->project, ruleIndex, premiseIndex, logop, object, objIndex, variable, relop, status, value)); } int EXPORT_PY_API rcntl_setpremiseindex(Handle ph, int ruleIndex, int premiseIndex, int objIndex) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setpremiseindex(pr->project, ruleIndex, premiseIndex, objIndex)); + return set_error(pr->error, EN_setpremiseindex(pr->project, ruleIndex, premiseIndex, objIndex)); } int EXPORT_PY_API rcntl_setpremisestatus(Handle ph, int ruleIndex, int premiseIndex, int status) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setpremisestatus(pr->project, ruleIndex, premiseIndex, status)); + return set_error(pr->error, EN_setpremisestatus(pr->project, ruleIndex, premiseIndex, status)); } int EXPORT_PY_API rcntl_setpremisevalue(Handle ph, int ruleIndex, int premiseIndex, double value) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setpremisevalue(pr->project, ruleIndex, premiseIndex, value)); + return set_error(pr->error, EN_setpremisevalue(pr->project, ruleIndex, premiseIndex, value)); } int EXPORT_PY_API rcntl_getthenaction(Handle ph, int ruleIndex, int actionIndex, int *linkIndex, int *status, double *setting) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getthenaction(pr->project, ruleIndex, actionIndex, linkIndex, status, setting)); + return set_error(pr->error, EN_getthenaction(pr->project, ruleIndex, actionIndex, linkIndex, status, setting)); } int EXPORT_PY_API rcntl_setthenaction(Handle ph, int ruleIndex, int actionIndex, int linkIndex, int status, double setting) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setthenaction(pr->project, ruleIndex, actionIndex, linkIndex, status, setting)); + return set_error(pr->error, EN_setthenaction(pr->project, ruleIndex, actionIndex, linkIndex, status, setting)); } int EXPORT_PY_API rcntl_getelseaction(Handle ph, int ruleIndex, int actionIndex, int *linkIndex, int *status, double *setting) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_getelseaction(pr->project, ruleIndex, actionIndex, linkIndex, status, setting)); + return set_error(pr->error, EN_getelseaction(pr->project, ruleIndex, actionIndex, linkIndex, status, setting)); } int EXPORT_PY_API rcntl_setelseaction(Handle ph, int ruleIndex, int actionIndex, int linkIndex, int status, double setting) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setelseaction(pr->project, ruleIndex, actionIndex, linkIndex, status, setting)); + return set_error(pr->error, EN_setelseaction(pr->project, ruleIndex, actionIndex, linkIndex, status, setting)); } int EXPORT_PY_API rcntl_setrulepriority(Handle ph, int index, double priority) { handle_t *pr = (handle_t *)ph; - return error_set(pr->error, EN_setrulepriority(pr->project, index, priority)); + return set_error(pr->error, EN_setrulepriority(pr->project, index, priority)); } @@ -756,7 +761,7 @@ int EXPORT_PY_API rcntl_setrulepriority(Handle ph, int index, double priority) void EXPORT_PY_API err_clear(Handle ph) { handle_t *pr = (handle_t *)ph; - error_clear(pr->error); + clear_error(pr->error); } int EXPORT_PY_API err_check(Handle ph, char** msg_buffer) @@ -767,7 +772,7 @@ int EXPORT_PY_API err_check(Handle ph, char** msg_buffer) // { handle_t *pr = (handle_t *)ph; - return error_check(pr->error, msg_buffer); + return check_error(pr->error, msg_buffer); } int EXPORT_PY_API toolkit_getversion(int *version) diff --git a/src/funcs.h b/src/funcs.h index 6fffeb3..ac17b55 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 03/17/2019 + Last Updated: 04/03/2019 ****************************************************************************** */ #ifndef FUNCS_H @@ -17,8 +17,6 @@ void initpointers(Project *); int allocdata(Project *); -void freeTmplist(STmplist *); -void freeFloatlist(SFloatlist *); void freedata(Project *); int openfiles(Project *, const char *, const char *,const char *); @@ -36,8 +34,12 @@ int findlink(Network *, char *); int findtank(Network *, int); int findvalve(Network *, int); int findpump(Network *, int); +int findpattern(Network *, char *); +int findcurve(Network *, char *); + void adjustpatterns(Network *, int); void adjustcurves(Network *, int); +int resizecurve(Scurve *, int); int getcomment(Network *, int, int, char *); int setcomment(Network *, int, int, const char *); @@ -65,8 +67,6 @@ void convertunits(Project *); int netsize(Project *); int readdata(Project *); int updatepumpparams(Project *, int); -int getpatterns(Project *); -int getcurves(Project *); int findmatch(char *, char *[]); int match(const char *, const char *); int gettokens(char *, char **, int, char *); diff --git a/src/hash.c b/src/hash.c index 95ac8dd..5e8e7cd 100755 --- a/src/hash.c +++ b/src/hash.c @@ -11,11 +11,13 @@ ****************************************************************************** */ -#ifndef __APPLE__ -#include -#else -#include -#endif + #ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include + #else + #include + #endif #include #include "hash.h" @@ -84,7 +86,7 @@ int hashtable_update(HashTable *ht, char *key, int new_data) { unsigned int i = gethash(key); DataEntry *entry; - + if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; entry = ht[i]; while (entry != NULL) @@ -104,7 +106,7 @@ int hashtable_delete(HashTable *ht, char *key) { unsigned int i = gethash(key); DataEntry *entry, *preventry; - + if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; preventry = NULL; @@ -164,7 +166,7 @@ void hashtable_free(HashTable *ht) { DataEntry *entry, *nextentry; int i; - + for (i = 0; i < HASHTABLEMAXSIZE; i++) { entry = ht[i]; diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c index b6c002c..69785d8 100644 --- a/src/hydcoeffs.c +++ b/src/hydcoeffs.c @@ -11,13 +11,16 @@ ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif + #include #include "types.h" @@ -85,12 +88,12 @@ void resistcoeff(Project *pr, int k) double e, d, L; Slink *link = &net->Link[k]; - + link->Qa = 0.0; switch (link->Type) { // ... Link is a pipe. Compute resistance based on headloss formula. - // Friction factor for D-W formula gets included during head loss + // Friction factor for D-W formula gets included during head loss // calculation. case CVPIPE: case PIPE: @@ -244,14 +247,14 @@ void linkcoeffs(Project *pr) // Update linear system coeffs. associated with start node n1 // ... node n1 is junction - if (n1 <= net->Njuncs) + if (n1 <= net->Njuncs) { sm->Aii[sm->Row[n1]] += hyd->P[k]; // Diagonal coeff. sm->F[sm->Row[n1]] += hyd->Y[k]; // RHS coeff. } // ... node n1 is a tank/reservoir - else sm->F[sm->Row[n2]] += (hyd->P[k] * hyd->NodeHead[n1]); + else sm->F[sm->Row[n2]] += (hyd->P[k] * hyd->NodeHead[n1]); // Update linear system coeffs. associated with end node n2 // ... node n2 is junction @@ -262,7 +265,7 @@ void linkcoeffs(Project *pr) } // ... node n2 is a tank/reservoir - else sm->F[sm->Row[n1]] += (hyd->P[k] * hyd->NodeHead[n2]); + else sm->F[sm->Row[n1]] += (hyd->P[k] * hyd->NodeHead[n2]); } } @@ -321,13 +324,13 @@ void valvecoeffs(Project *pr) // Coeffs. for fixed status valves have already been computed if (hyd->LinkSetting[k] == MISSING) continue; - // Start & end nodes of valve's link + // Start & end nodes of valve's link link = &net->Link[k]; n1 = link->N1; n2 = link->N2; // Call valve-specific function - switch (link->Type) + switch (link->Type) { case PRV: prvcoeff(pr, k, n1, n2); @@ -482,7 +485,7 @@ void demandcoeffs(Project *pr) double dp, // pressure range over which demand can vary (ft) n, // exponent in head loss v. demand function hloss, // head loss in supplying demand (ft) - hgrad; // gradient of demand head loss (ft/cfs) + hgrad; // gradient of demand head loss (ft/cfs) // Get demand function parameters if (hyd->DemandModel == DDA) return; @@ -659,12 +662,12 @@ void DWpipecoeff(Project *pr, int k) Slink *link = &pr->network.Link[k]; double q = ABS(hyd->LinkFlow[k]); - double r = link->R; // Resistance coeff. - double ml = link->Km; // Minor loss coeff. + double r = link->R; // Resistance coeff. + double ml = link->Km; // Minor loss coeff. double e = link->Kc / link->Diam; // Relative roughness double s = hyd->Viscos * link->Diam; // Viscosity / diameter double hloss, hgrad, f, dfdq, r1; - + // Compute head loss and its derivative // ... use Hagen-Poiseuille formula for laminar flow (Re <= 2000) if (q <= A2 * s) @@ -673,7 +676,7 @@ void DWpipecoeff(Project *pr, int k) hloss = hyd->LinkFlow[k] * (r + ml * q); hgrad = r + 2.0 * ml * q; } - + // ... otherwise use Darcy-Weisbach formula with friction factor else { @@ -683,7 +686,7 @@ void DWpipecoeff(Project *pr, int k) hloss = r1 * q * hyd->LinkFlow[k]; hgrad = (2.0 * r1 * q) + (dfdq * r * q * q); } - + // Compute P and Y coefficients hyd->P[k] = 1.0 / hgrad; hyd->Y[k] = hloss / hgrad; @@ -753,7 +756,7 @@ void pumpcoeff(Project *pr, int k) int p; // Pump index double h0, // Shutoff head - q, // Abs. value of flow + q, // Abs. value of flow r, // Flow resistance coeff. n, // Flow exponent coeff. setting, // Pump speed setting @@ -899,7 +902,7 @@ void gpvcoeff(Project *pr, int k) Hydraul *hyd = &pr->hydraul; // Treat as a pipe if valve closed - if (hyd->LinkStatus[k] == CLOSED) valvecoeff(pr, k); + if (hyd->LinkStatus[k] == CLOSED) valvecoeff(pr, k); // Otherwise utilize segment of head loss curve // bracketing current flow (curve index is stored @@ -939,7 +942,7 @@ void pbvcoeff(Project *pr, int k) // If valve fixed OPEN or CLOSED then treat as a pipe if (hyd->LinkSetting[k] == MISSING || hyd->LinkSetting[k] == 0.0) { - valvecoeff(pr, k); + valvecoeff(pr, k); } // If valve is active @@ -948,7 +951,7 @@ void pbvcoeff(Project *pr, int k) // Treat as a pipe if minor loss > valve setting if (link->Km * SQR(hyd->LinkFlow[k]) > hyd->LinkSetting[k]) { - valvecoeff(pr, k); + valvecoeff(pr, k); } // Otherwise force headloss across valve to be equal to setting else @@ -983,7 +986,7 @@ void tcvcoeff(Project *pr, int k) } // Then apply usual valve formula - valvecoeff(pr, k); + valvecoeff(pr, k); // Restore original loss coeff. link->Km = km; @@ -1017,7 +1020,7 @@ void prvcoeff(Project *pr, int k, int n1, int n2) { // Set coeffs. to force head at downstream - // node equal to valve setting & force flow + // node equal to valve setting & force flow // to equal to flow excess at downstream node. hyd->P[k] = 0.0; @@ -1069,7 +1072,7 @@ void psvcoeff(Project *pr, int k, int n1, int n2) if (hyd->LinkStatus[k] == ACTIVE) { // Set coeffs. to force head at upstream - // node equal to valve setting & force flow + // node equal to valve setting & force flow // equal to flow excess at upstream node. hyd->P[k] = 0.0; @@ -1138,7 +1141,7 @@ void fcvcoeff(Project *pr, int k, int n1, int n2) else { - valvecoeff(pr, k); + valvecoeff(pr, k); sm->Aij[sm->Ndx[k]] -= hyd->P[k]; sm->Aii[i] += hyd->P[k]; sm->Aii[j] += hyd->P[k]; @@ -1162,7 +1165,7 @@ void valvecoeff(Project *pr, int k) Slink *link = &pr->network.Link[k]; double flow, q, y, qa, hgrad; - + flow = hyd->LinkFlow[k]; // Valve is closed. Use a very small matrix coeff. diff --git a/src/hydraul.c b/src/hydraul.c index 703f7b7..1e3532e 100755 --- a/src/hydraul.c +++ b/src/hydraul.c @@ -3,7 +3,7 @@ Project: OWA EPANET Version: 2.2 Module: hydraul.c - Description: implements EPANET's hydraulic engine + Description: implements EPANET's hydraulic engine Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE @@ -11,13 +11,16 @@ ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif + #include #include "types.h" @@ -48,9 +51,9 @@ void tanklevels(Project *, long); int openhyd(Project *pr) /* *-------------------------------------------------------------- - * Input: none - * Output: returns error code - * Purpose: opens hydraulics solver system + * Input: none + * Output: returns error code + * Purpose: opens hydraulics solver system *-------------------------------------------------------------- */ { @@ -92,8 +95,8 @@ void inithyd(Project *pr, int initflag) **-------------------------------------------------------------- ** Input: initflag > 0 if link flows should be re-initialized ** = 0 if not -** Output: none -** Purpose: initializes hydraulics solver system +** Output: none +** Purpose: initializes hydraulics solver system **-------------------------------------------------------------- */ { @@ -106,7 +109,7 @@ void inithyd(Project *pr, int initflag) Stank *tank; Slink *link; Spump *pump; - + // Initialize tanks for (i = 1; i <= net->Ntanks; i++) { @@ -128,7 +131,7 @@ void inithyd(Project *pr, int initflag) for (i = 1; i <= net->Nlinks; i++) { link = &net->Link[i]; - + // Initialize status and setting hyd->LinkStatus[i] = link->Status; hyd->LinkSetting[i] = link->Kc; @@ -140,7 +143,7 @@ void inithyd(Project *pr, int initflag) if ( (link->Type == PRV || link->Type == PSV || link->Type == FCV) && (link->Kc != MISSING) - ) hyd->LinkStatus[i] = ACTIVE; + ) hyd->LinkStatus[i] = ACTIVE; // Initialize flows if necessary if (hyd->LinkStatus[i] <= CLOSED) @@ -170,7 +173,7 @@ void inithyd(Project *pr, int initflag) // Re-position hydraulics file if (pr->outfile.Saveflag) - { + { fseek(out->HydFile,out->HydOffset,SEEK_SET); } @@ -185,10 +188,10 @@ void inithyd(Project *pr, int initflag) int runhyd(Project *pr, long *t) /* **-------------------------------------------------------------- -** Input: none +** Input: none ** Output: t = pointer to current time (in seconds) -** Returns: error code -** Purpose: solves network hydraulics in a single time period +** Returns: error code +** Purpose: solves network hydraulics in a single time period **-------------------------------------------------------------- */ { @@ -199,7 +202,7 @@ int runhyd(Project *pr, long *t) int iter; // Iteration count int errcode; // Error code double relerr; // Solution accuracy - + // Find new demands & control actions *t = time->Htime; demands(pr); @@ -212,7 +215,7 @@ int runhyd(Project *pr, long *t) { // Report new status & save results if (rpt->Statflag) writehydstat(pr,iter,relerr); - + // If system unbalanced and no extra trials // allowed, then activate the Haltflag if (relerr > hyd->Hacc && hyd->ExtraIter == -1) @@ -229,11 +232,11 @@ int runhyd(Project *pr, long *t) int nexthyd(Project *pr, long *tstep) /* **-------------------------------------------------------------- -** Input: none +** Input: none ** Output: tstep = pointer to time step (in seconds) -** Returns: error code +** Returns: error code ** Purpose: finds length of next time step & updates tank -** levels and rule-based contol actions +** levels and rule-based contol actions **-------------------------------------------------------------- */ { @@ -242,7 +245,7 @@ int nexthyd(Project *pr, long *tstep) long hydstep; // Actual time step int errcode = 0; // Error code - + // Save current results to hydraulics file and // force end of simulation if Haltflag is active if (pr->outfile.Saveflag) errcode = savehyd(pr, &time->Htime); @@ -277,14 +280,14 @@ int nexthyd(Project *pr, long *tstep) *tstep = hydstep; return errcode; } - + void closehyd(Project *pr) /* **-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: closes hydraulics solver system +** Input: none +** Output: returns error code +** Purpose: closes hydraulics solver system **-------------------------------------------------------------- */ { @@ -296,9 +299,9 @@ void closehyd(Project *pr) int allocmatrix(Project *pr) /* **-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: allocates memory used for solution matrix coeffs. +** Input: none +** Output: returns error code +** Purpose: allocates memory used for solution matrix coeffs. **-------------------------------------------------------------- */ { @@ -306,7 +309,7 @@ int allocmatrix(Project *pr) Hydraul *hyd = &pr->hydraul; int errcode = 0; - + hyd->P = (double *) calloc(net->Nlinks+1,sizeof(double)); hyd->Y = (double *) calloc(net->Nlinks+1,sizeof(double)); hyd->DemandFlow = (double *) calloc(net->Nnodes + 1, sizeof(double)); @@ -328,14 +331,14 @@ int allocmatrix(Project *pr) void freematrix(Project *pr) /* **-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: frees memory used for solution matrix coeffs. +** Input: none +** Output: none +** Purpose: frees memory used for solution matrix coeffs. **-------------------------------------------------------------- */ { Hydraul *hyd = &pr->hydraul; - + free(hyd->P); free(hyd->Y); free(hyd->DemandFlow); @@ -351,7 +354,7 @@ void initlinkflow(Project *pr, int i, char s, double k) ** Input: i = link index ** s = link status ** k = link setting (i.e., pump speed) -** Output: none +** Output: none ** Purpose: sets initial flow in link to QZERO if link is closed, ** to design flow for a pump, or to flow at velocity of ** 1 fps for other links. @@ -362,7 +365,7 @@ void initlinkflow(Project *pr, int i, char s, double k) Network *n = &pr->network; Slink *link = &n->Link[i]; - + if (s == CLOSED) { hyd->LinkFlow[i] = QZERO; @@ -383,8 +386,8 @@ void setlinkflow(Project *pr, int k, double dh) **-------------------------------------------------------------- ** Input: k = link index ** dh = head loss across link -** Output: none -** Purpose: sets flow in link based on current headloss +** Output: none +** Purpose: sets flow in link based on current headloss **-------------------------------------------------------------- */ { @@ -396,7 +399,7 @@ void setlinkflow(Project *pr, int k, double dh) double x ,y; Slink *link = &net->Link[k]; Scurve *curve; - + switch (link->Type) { case CVPIPE: @@ -408,7 +411,7 @@ void setlinkflow(Project *pr, int k, double dh) y = sqrt(ABS(dh) / link->R / 1.32547); hyd->LinkFlow[k] = x * y; } - + // For Hazen-Williams or Manning formulas use inverse of formula else { @@ -416,16 +419,16 @@ void setlinkflow(Project *pr, int k, double dh) y = 1.0 / hyd->Hexp; hyd->LinkFlow[k] = pow(x, y); } - + // Change sign of flow to match sign of head loss if (dh < 0.0) hyd->LinkFlow[k] = -hyd->LinkFlow[k]; break; - + case PUMP: // Convert headloss to pump head gain dh = -dh; p = findpump(net, k); - + // For custom pump curve, interpolate from curve if (net->Pump[p].Ptype == CUSTOM) { @@ -435,7 +438,7 @@ void setlinkflow(Project *pr, int k, double dh) hyd->LinkFlow[k] = interp(curve->Npts, curve->Y, curve->X, dh) * hyd->LinkSetting[k] / pr->Ucf[FLOW]; } - + // Otherwise use inverse of power curve else { @@ -459,7 +462,7 @@ void setlinkstatus(Project *pr, int index, char value, StatusType *s, double *k ** s = pointer to link status ** k = pointer to link setting ** Output: none -** Purpose: sets link status to OPEN or CLOSED +** Purpose: sets link status to OPEN or CLOSED **---------------------------------------------------------------- */ { @@ -467,11 +470,11 @@ void setlinkstatus(Project *pr, int index, char value, StatusType *s, double *k Slink *link = &net->Link[index]; LinkType t = link->Type; - - // Status set to open + + // Status set to open if (value == 1) { - // Adjust link setting for pumps & valves + // Adjust link setting for pumps & valves if (t == PUMP) *k = 1.0; if (t > PUMP && t != GPV) *k = MISSING; @@ -479,7 +482,7 @@ void setlinkstatus(Project *pr, int index, char value, StatusType *s, double *k *s = OPEN; } - // Status set to closed + // Status set to closed else if (value == 0) { // Adjust link setting for pumps & valves @@ -509,7 +512,7 @@ void setlinksetting(Project *pr, int index, double value, StatusType *s, Slink *link = &net->Link[index]; LinkType t = link->Type; - + // For a pump, status is OPEN if speed > 0, CLOSED otherwise if (t == PUMP) { @@ -531,15 +534,15 @@ void setlinksetting(Project *pr, int index, double value, StatusType *s, if (*k == MISSING && *s <= CLOSED) *s = OPEN; *k = value; } -} +} void demands(Project *pr) /* **-------------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: computes demands at nodes during current time period +** Input: none +** Output: none +** Purpose: computes demands at nodes during current time period **-------------------------------------------------------------------- */ { @@ -610,9 +613,9 @@ void demands(Project *pr) int controls(Project *pr) /* **--------------------------------------------------------------------- -** Input: none -** Output: number of links whose setting changes -** Purpose: implements simple controls based on time or tank levels +** Input: none +** Output: number of links whose setting changes +** Purpose: implements simple controls based on time or tank levels **--------------------------------------------------------------------- */ { @@ -626,7 +629,7 @@ int controls(Project *pr) double k1, k2; char s1, s2; Slink *link; - Scontrol *control; + Scontrol *control; // Examine each control statement setsum = 0; @@ -680,7 +683,7 @@ int controls(Project *pr) if (pr->report.Statflag) writecontrolaction(pr,k,i); setsum++; } - } + } } return setsum; } @@ -689,9 +692,9 @@ int controls(Project *pr) long timestep(Project *pr) /* **---------------------------------------------------------------- -** Input: none -** Output: returns time step until next change in hydraulics -** Purpose: computes time step to advance hydraulic simulation +** Input: none +** Output: returns time step until next change in hydraulics +** Purpose: computes time step to advance hydraulic simulation **---------------------------------------------------------------- */ { @@ -699,26 +702,26 @@ long timestep(Project *pr) Times *time = &pr->times; long n, t, tstep; - + // Normal time step is hydraulic time step tstep = time->Hstep; - + // Revise time step based on time until next demand period // (n = next pattern period, t = time till next period) n = ((time->Htime + time->Pstart) / time->Pstep) + 1; t = n * time->Pstep - time->Htime; if (t > 0 && t < tstep) tstep = t; - + // Revise time step based on time until next reporting period t = time->Rtime - time->Htime; if (t > 0 && t < tstep) tstep = t; - + // Revise time step based on smallest time to fill or drain a tank tanktimestep(pr, &tstep); - + // Revise time step based on smallest time to activate a control controltimestep(pr, &tstep); - + // Evaluate rule-based controls (which will also update tank levels) if (net->Nrules > 0) ruletimestep(pr, &tstep); else tanklevels(pr, tstep); @@ -729,10 +732,10 @@ long timestep(Project *pr) int tanktimestep(Project *pr, long *tstep) /* **----------------------------------------------------------------- -** Input: *tstep = current time step -** Output: *tstep = modified current time step +** Input: *tstep = current time step +** Output: *tstep = modified current time step ** Purpose: revises time step based on shortest time to fill or -** drain a tank +** drain a tank **----------------------------------------------------------------- */ { @@ -777,10 +780,10 @@ int tanktimestep(Project *pr, long *tstep) void controltimestep(Project *pr, long *tstep) /* **------------------------------------------------------------------ -** Input: *tstep = current time step -** Output: *tstep = modified current time step +** Input: *tstep = current time step +** Output: *tstep = modified current time step ** Purpose: revises time step based on shortest time to activate -** a simple control +** a simple control **------------------------------------------------------------------ */ { @@ -792,24 +795,24 @@ void controltimestep(Project *pr, long *tstep) long t, t1, t2; Slink *link; Scontrol *control; - + // Examine each control for (i = 1; i <= net->Ncontrols; i++) { t = 0; control = &net->Control[i]; - // Control depends on a tank level + // Control depends on a tank level if ( (n = control->Node) > 0) { // Skip node if not a tank or reservoir if ((j = n - net->Njuncs) <= 0) continue; - + // Find current head and flow into tank h = hyd->NodeHead[n]; q = hyd->NodeDemand[n]; if (ABS(q) <= QZERO) continue; - + // Find time to reach upper or lower control level if ( (h < control->Grade && control->Type == HILEVEL && q > 0.0) || (h > control->Grade && control->Type == LOWLEVEL && q < 0.0) ) @@ -821,7 +824,7 @@ void controltimestep(Project *pr, long *tstep) // Control is based on elapsed time if (control->Type == TIMER) - { + { if (control->Time > pr->times.Htime) { t = control->Time - pr->times.Htime; @@ -853,16 +856,16 @@ void controltimestep(Project *pr, long *tstep) void ruletimestep(Project *pr, long *tstep) /* **-------------------------------------------------------------- -** Input: *tstep = current time step (sec) -** Output: *tstep = modified time step +** Input: *tstep = current time step (sec) +** Output: *tstep = modified time step ** Purpose: updates next time step by checking if any rules -** will fire before then; also updates tank levels. +** will fire before then; also updates tank levels. **-------------------------------------------------------------- */ { Network *net = &pr->network; Times *time = &pr->times; - + long tnow, // Start of time interval for rule evaluation tmax, // End of time interval for rule evaluation dt, // Normal time increment for rule evaluation @@ -880,7 +883,7 @@ void ruletimestep(Project *pr, long *tstep) } // Otherwise, time increment equals rule evaluation time step and - // first actual increment equals time until next even multiple of + // first actual increment equals time until next even multiple of // Rulestep occurs. else { @@ -888,7 +891,7 @@ void ruletimestep(Project *pr, long *tstep) dt1 = time->Rulestep - (tnow % time->Rulestep); } - // Make sure time increment is no larger than current time step + // Make sure time increment is no larger than current time step dt = MIN(dt, *tstep); dt1 = MIN(dt1, *tstep); if (dt1 == 0) dt1 = dt; @@ -918,14 +921,14 @@ void ruletimestep(Project *pr, long *tstep) *tstep = time->Htime - tnow; time->Htime = tnow; } - + void addenergy(Project *pr, long hstep) /* **------------------------------------------------------------- -** Input: hstep = time step (sec) -** Output: none -** Purpose: accumulates pump energy usage +** Input: hstep = time step (sec) +** Output: none +** Purpose: accumulates pump energy usage **------------------------------------------------------------- */ { @@ -1003,10 +1006,10 @@ void addenergy(Project *pr, long hstep) void getenergy(Project *pr, int k, double *kw, double *eff) /* **---------------------------------------------------------------- -** Input: k = link index +** Input: k = link index ** Output: *kw = kwatt energy used ** *eff = efficiency (pumps only) -** Purpose: computes flow energy associated with link k +** Purpose: computes flow energy associated with link k **---------------------------------------------------------------- */ { @@ -1022,7 +1025,7 @@ void getenergy(Project *pr, int k, double *kw, double *eff) double speed; // current speed setting Scurve *curve; Slink *link = &net->Link[k]; - + // No energy if link is closed if (hyd->LinkStatus[k] <= CLOSED) { @@ -1065,10 +1068,10 @@ void getenergy(Project *pr, int k, double *kw, double *eff) void tanklevels(Project *pr, long tstep) /* **---------------------------------------------------------------- -** Input: tstep = current time step -** Output: none -** Purpose: computes new water levels in tanks after current -** time step +** Input: tstep = current time step +** Output: none +** Purpose: computes new water levels in tanks after current +** time step **---------------------------------------------------------------- */ { @@ -1077,17 +1080,17 @@ void tanklevels(Project *pr, long tstep) int i, n; double dv; - + for (i = 1; i <= net->Ntanks; i++) { Stank *tank = &net->Tank[i]; if (tank->A == 0.0) continue; // Skip reservoirs - - // Update the tank's volume & water elevation + + // Update the tank's volume & water elevation n = tank->Node; dv = hyd->NodeDemand[n] * tstep; tank->V += dv; - + // Check if tank full/empty within next second if (tank->V + hyd->NodeDemand[n] >= tank->Vmax) { @@ -1105,10 +1108,10 @@ void tanklevels(Project *pr, long tstep) double tankvolume(Project *pr, int i, double h) /* **-------------------------------------------------------------------- -** Input: i = tank index -** h = water elevation in tank -** Output: returns water volume in tank -** Purpose: finds water volume in tank i corresponding to elev. h. +** Input: i = tank index +** h = water elevation in tank +** Output: returns water volume in tank +** Purpose: finds water volume in tank i corresponding to elev. h. **-------------------------------------------------------------------- */ { @@ -1122,7 +1125,7 @@ double tankvolume(Project *pr, int i, double h) // Use level*area if no volume curve j = tank->Vcurve; if (j == 0) return(tank->Vmin + (h - tank->Hmin) * tank->A); - + // If curve exists, interpolate on h to find volume v // remembering that volume curve is in original units. else @@ -1138,10 +1141,10 @@ double tankvolume(Project *pr, int i, double h) double tankgrade(Project *pr, int i, double v) /* **------------------------------------------------------------------- -** Input: i = tank index -** v = volume in tank -** Output: returns water level in tank -** Purpose: finds water level in tank i corresponding to volume v. +** Input: i = tank index +** v = volume in tank +** Output: returns water level in tank +** Purpose: finds water level in tank i corresponding to volume v. **------------------------------------------------------------------- */ { @@ -1154,7 +1157,7 @@ double tankgrade(Project *pr, int i, double v) // Use area if no volume curve j = tank->Vcurve; if (j == 0) return(tank->Hmin + (v - tank->Vmin) / tank->A); - + // If curve exists, interpolate on volume (originally the Y-variable // but used here as the X-variable) to find new level above bottom. // Remember that volume curve is stored in original units. diff --git a/src/hydsolver.c b/src/hydsolver.c index 3c0694a..0b0da25 100644 --- a/src/hydsolver.c +++ b/src/hydsolver.c @@ -12,13 +12,16 @@ ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif + #include #include "types.h" @@ -453,7 +456,7 @@ void newlinkflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum) hbal->maxflownode = -1; } - // Update net flows to fixed grade nodes + // Update net flows to fixed grade nodes if (hyd->LinkStatus[k] > CLOSED) { if (n1 > net->Njuncs) hyd->NodeDemand[n1] -= hyd->LinkFlow[k]; @@ -487,7 +490,7 @@ void newemitterflows(Project *pr, Hydbalance *hbal, double *qsum, // Skip junction if it does not have an emitter if (net->Node[i].Ke == 0.0) continue; - // Find emitter head loss and gradient + // Find emitter head loss and gradient emitheadloss(pr, i, &hloss, &hgrad); // Find emitter flow change diff --git a/src/hydstatus.c b/src/hydstatus.c index f7edb6b..2a4d20e 100644 --- a/src/hydstatus.c +++ b/src/hydstatus.c @@ -10,8 +10,15 @@ License: see LICENSE Last Updated: 11/27/2018 ****************************************************************************** */ - +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include + #include "types.h" #include "funcs.h" @@ -48,7 +55,7 @@ int valvestatus(Project *pr) n1, n2; // Start & end nodes double hset; // Valve head setting StatusType status; // Valve status settings - Slink *link; + Slink *link; // Examine each valve for (i = 1; i <= net->Nvalves; i++) @@ -59,7 +66,7 @@ int valvestatus(Project *pr) // Ignore valve if its status is fixed to OPEN/CLOSED if (hyd->LinkSetting[k] == MISSING) continue; - + // Get start/end node indexes & save current status n1 = link->N1; n2 = link->N2; @@ -449,7 +456,7 @@ void tankstatus(Project *pr, int k, int n1, int n2) // Case 2: Downstream head > tank head // (e.g., an open outflow check valve would close) - else if (cvstatus(pr, OPEN, h, q) == CLOSED) + else if (cvstatus(pr, OPEN, h, q) == CLOSED) { hyd->LinkStatus[k] = TEMPCLOSED; } diff --git a/src/inpfile.c b/src/inpfile.c index 45b5aab..946f364 100644 --- a/src/inpfile.c +++ b/src/inpfile.c @@ -7,17 +7,20 @@ Description: saves network data to an EPANET formatted text file Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE -Last Updated: 03/17/2019 +Last Updated: 04/03/2019 ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif + #include #include "types.h" @@ -38,7 +41,7 @@ extern char *TstatTxt[]; extern char *RptFlagTxt[]; extern char *SectTxt[]; -void saveauxdata(Project *pr, FILE *f) +void saveauxdata(Project *pr, FILE *f) /* ------------------------------------------------------------ Writes auxilary data from original input file to new file. @@ -51,7 +54,7 @@ void saveauxdata(Project *pr, FILE *f) char line[MAXLINE + 1]; char s[MAXLINE + 1]; FILE *InFile = pr->parser.InFile; - + // Re-open the input file if (InFile == NULL) { @@ -88,7 +91,7 @@ void saveauxdata(Project *pr, FILE *f) } } } - + // Write line of auxilary data to file else { @@ -147,7 +150,7 @@ int saveinpfile(Project *pr, const char *fname) // Open the new text file if ((f = fopen(fname, "wt")) == NULL) return 302; - // Write [TITLE] section + // Write [TITLE] section fprintf(f, s_TITLE); for (i = 0; i < 3; i++) { @@ -205,7 +208,7 @@ int saveinpfile(Project *pr, const char *fname) } } - // Write [PIPES] section + // Write [PIPES] section fprintf(f, "\n\n"); fprintf(f, s_PIPES); for (i = 1; i <= net->Nlinks; i++) @@ -244,7 +247,7 @@ int saveinpfile(Project *pr, const char *fname) // Pump has constant power if (pump->Ptype == CONST_HP) sprintf(s1, " POWER %.4f", link->Km); - // Pump has a head curve + // Pump has a head curve else if ((j = pump->Hcurve) > 0) { sprintf(s1, " HEAD %s", net->Curve[j].ID); @@ -539,16 +542,16 @@ int saveinpfile(Project *pr, const char *fname) fprintf(f, "\n ORDER TANK %-.2f", qual->TankOrder); fprintf(f, "\n GLOBAL BULK %-.6f", qual->Kbulk * SECperDAY); fprintf(f, "\n GLOBAL WALL %-.6f", qual->Kwall * SECperDAY); - + if (qual->Climit > 0.0) { - fprintf(f, "\n LIMITING POTENTIAL %-.6f", qual->Climit); + fprintf(f, "\n LIMITING POTENTIAL %-.6f", qual->Climit * pr->Ucf[QUALITY]); } if (qual->Rfactor != MISSING && qual->Rfactor != 0.0) { fprintf(f, "\n ROUGHNESS CORRELATION %-.6f", qual->Rfactor); } - + // Pipe-specific parameters for (i = 1; i <= net->Nlinks; i++) { @@ -633,10 +636,6 @@ int saveinpfile(Project *pr, const char *fname) fprintf(f, "\n UNITS %s", FlowUnitsTxt[parser->Flowflag]); fprintf(f, "\n PRESSURE %s", PressUnitsTxt[parser->Pressflag]); fprintf(f, "\n HEADLOSS %s", FormTxt[hyd->Formflag]); - if (hyd->DefPat >= 1 && hyd->DefPat <= net->Npats) - { - fprintf(f, "\n PATTERN %s", net->Pattern[hyd->DefPat].ID); - } switch (out->Hydflag) { case USE: @@ -797,8 +796,7 @@ int saveinpfile(Project *pr, const char *fname) saveauxdata(pr, f); // Close the new input file - fprintf(f, "\n"); - fprintf(f, s_END); + fprintf(f, "\n%s\n", s_END); fclose(f); return 0; } diff --git a/src/input1.c b/src/input1.c index e1cbcdf..219caaf 100644 --- a/src/input1.c +++ b/src/input1.c @@ -3,26 +3,31 @@ Project: OWA EPANET Version: 2.2 Module: input1.c -Description: retrieves network data from an EPANET input file +Description: retrieves network data from an EPANET input file Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE -Last Updated: 03/17/2019 +Last Updated: 04/03/2019 ****************************************************************************** */ -#include -#include -#include -#ifndef __APPLE__ -#include +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include #endif +#include +#include + +#include #include "types.h" #include "funcs.h" #include "hash.h" #include "text.h" -#include + // Default values #define MAXITER 200 // Default max. # hydraulic iterations @@ -56,7 +61,7 @@ int getdata(Project *pr) int errcode = 0; // Assign default data values & reporting options - setdefaults(pr); + setdefaults(pr); initreport(&pr->report); // Read in network data @@ -100,9 +105,10 @@ void setdefaults(Project *pr) parser->Unitsflag = US; // US unit system parser->Flowflag = GPM; // Flow units are gpm parser->Pressflag = PSI; // Pressure units are psi + parser->DefPat = 0; // Default demand pattern index out->Hydflag = SCRATCH; // No external hydraulics file rpt->Tstatflag = SERIES; // Generate time series output - + hyd->Formflag = HW; // Use Hazen-Williams formula hyd->Htol = HTOL; // Default head tolerance hyd->Qtol = QTOL; // Default flow tolerance @@ -117,7 +123,6 @@ void setdefaults(Project *pr) hyd->ExtraIter = -1; // Stop if network unbalanced hyd->Viscos = MISSING; // Temporary viscosity hyd->SpGrav = SPGRAV; // Default specific gravity - hyd->DefPat = 0; // Default demand pattern index hyd->Epat = 0; // No energy price pattern hyd->Ecost = 0.0; // Zero unit energy cost hyd->Dcost = 0.0; // Zero energy demand charge @@ -326,16 +331,13 @@ void adjustdata(Project *pr) } // Use default pattern if none assigned to a demand - for (i = 1; i <= net->Nnodes; i++) + parser->DefPat = findpattern(net, parser->DefPatID); + if (parser->DefPat > 0) for (i = 1; i <= net->Nnodes; i++) { node = &net->Node[i]; for (demand = node->D; demand != NULL; demand = demand->next) { - if (demand->Pat == 0) - { - demand->Pat = hyd->DefPat; - xstrcpy(&demand->Name, "", MAXID); - } + if (demand->Pat == 0) demand->Pat = parser->DefPat; } } @@ -353,7 +355,7 @@ int inittanks(Project *pr) */ { Network *net = &pr->network; - + int i, j, n = 0; double a; int errcode = 0, levelerr; @@ -546,7 +548,7 @@ void convertunits(Project *pr) Slink *link; Spump *pump; Scontrol *control; - + // Convert nodal elevations & initial WQ // (WQ source units are converted in QUALITY.C for (i = 1; i <= net->Nnodes; i++) @@ -644,7 +646,7 @@ void convertunits(Project *pr) pump->H0 /= pr->Ucf[HEAD]; pump->R *= (pow(pr->Ucf[FLOW], pump->N) / pr->Ucf[HEAD]); } - + // Convert flow range & max. head units pump->Q0 /= pr->Ucf[FLOW]; pump->Qmax /= pr->Ucf[FLOW]; @@ -653,7 +655,7 @@ void convertunits(Project *pr) } else { - // For flow control valves, convert flow setting + // For flow control valves, convert flow setting // while for other valves convert pressure setting link->Diam /= pr->Ucf[DIAM]; link->Km = 0.02517 * link->Km / SQR(link->Diam) / SQR(link->Diam); diff --git a/src/input2.c b/src/input2.c index 2820e35..5adb3e4 100644 --- a/src/input2.c +++ b/src/input2.c @@ -7,16 +7,20 @@ Description: reads and interprets network data from an EPANET input file Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE -Last Updated: 03/17/2019 +Last Updated: 04/03/2019 ****************************************************************************** */ -#include -#include -#include -#ifndef __APPLE__ -#include +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include #endif +#include +#include + #include #include "types.h" @@ -31,7 +35,6 @@ extern char *SectTxt[]; // Input section keywords (see ENUMSTXT.H) // Exported functions int addnodeID(Network *n, int, char *); int addlinkID(Network *n, int, char *); -STmplist *getlistitem(char *, STmplist *); // Imported functions extern int powercurve(double, double, double, double, double, double *, @@ -39,8 +42,8 @@ extern int powercurve(double, double, double, double, double, double *, // Local functions static int newline(Project *, int, char *); -static int addpattern(Parser *, char *); -static int addcurve(Parser *, char *); +static int addpattern(Network *, char *); +static int addcurve(Network *, char *); static int unlinked(Project *); static int getpumpparams(Project *); static void inperrmsg(Project *, int, int, char *); @@ -56,11 +59,12 @@ int netsize(Project *pr) */ { Parser *parser = &pr->parser; - + char line[MAXLINE + 1]; // Line from input data file char *tok; // First token of line int sect, newsect; // Input data sections int errcode = 0; // Error code + Spattern *pattern; // Initialize object counts parser->MaxJuncs = 0; @@ -73,13 +77,20 @@ int netsize(Project *pr) parser->MaxCurves = 0; sect = -1; - // Add a default demand pattern - parser->MaxPats = -1; - addpattern(parser,""); - if (parser->InFile == NULL) return 0; + // Add a "dummy" time pattern with index of 0 and a single multiplier + // of 1.0 to be used by all demands not assigned a pattern + pr->network.Npats = -1; + errcode = addpattern(&pr->network, ""); + if (errcode) return errcode; + pattern = &pr->network.Pattern[0]; + pattern->Length = 1; + pattern[0].F = (double *)calloc(1, sizeof(double)); + pattern[0].F[0] = 1.0; + parser->MaxPats = pr->network.Npats; // Make a pass through input file counting number of each object + if (parser->InFile == NULL) return 0; while (fgets(line, MAXLINE, parser->InFile) != NULL) { // Skip blank lines & those beginning with a comment @@ -111,8 +122,14 @@ int netsize(Project *pr) case _VALVES: parser->MaxValves++; break; case _CONTROLS: parser->MaxControls++; break; case _RULES: addrule(parser,tok); break; - case _PATTERNS: errcode = addpattern(parser, tok); break; - case _CURVES: errcode = addcurve(parser, tok); break; + case _PATTERNS: + errcode = addpattern(&pr->network, tok); + parser->MaxPats = pr->network.Npats; + break; + case _CURVES: + errcode = addcurve(&pr->network, tok); + parser->MaxCurves = pr->network.Ncurves; + break; } if (errcode) break; } @@ -162,12 +179,15 @@ int readdata(Project *pr) net->Nvalves = 0; net->Ncontrols = 0; net->Nrules = 0; - net->Ncurves = parser->MaxCurves; - net->Npats = parser->MaxPats; + + // Patterns & Curves were created previously in netsize() + parser->MaxPats = net->Npats; + parser->MaxCurves = net->Ncurves; parser->PrevPat = NULL; parser->PrevCurve = NULL; + + // Initialize full line comment, input data section and error count parser->LineComment[0] = '\0'; - sect = -1; errsum = 0; @@ -246,16 +266,14 @@ int readdata(Project *pr) // Stop if reach end of file or max. error count if (errsum == MAXERRS) break; } - + // Check for errors if (errsum > 0) errcode = 200; - + // Check for unlinked nodes if (!errcode) errcode = unlinked(pr); - // Get pattern & curve data from temporary lists - if (!errcode) errcode = getpatterns(pr); - if (!errcode) errcode = getcurves(pr); + // Determine pump curve parameters if (!errcode) errcode = getpumpparams(pr); // Free input buffer @@ -397,7 +415,7 @@ int updatepumpparams(Project *pr, int pumpindex) curve->Type = PUMP_CURVE; npts = curve->Npts; - // Generic power function curve + // Generic power function curve if (npts == 1) { pump->Ptype = POWER_FUNC; @@ -407,7 +425,7 @@ int updatepumpparams(Project *pr, int pumpindex) q2 = 2.0 * q1; h2 = 0.0; } - + // 3 point curve with shutoff head else if (npts == 3 && curve->X[0] == 0.0) { @@ -418,7 +436,7 @@ int updatepumpparams(Project *pr, int pumpindex) q2 = curve->X[2]; h2 = curve->Y[2]; } - + // Custom pump curve else { @@ -431,7 +449,7 @@ int updatepumpparams(Project *pr, int pumpindex) pump->Q0 = (curve->X[0] + pump->Qmax) / 2.0; pump->Hmax = curve->Y[0]; } - + // Compute shape factors & limits of power function curves if (pump->Ptype == POWER_FUNC) { @@ -461,7 +479,7 @@ int addnodeID(Network *net, int n, char *id) **-------------------------------------------------------------- */ { - if (findnode(net,id)) return 0; + if (findnode(net,id)) return 0; strncpy(net->Node[n].ID, id, MAXID); hashtable_insert(net->NodeHashTable, net->Node[n].ID, n); return 1; @@ -483,7 +501,7 @@ int addlinkID(Network *net, int n, char *id) return 1; } -int addpattern(Parser *parser, char *id) +int addpattern(Network *network, char *id) /* **------------------------------------------------------------- ** Input: id = pattern ID label @@ -492,34 +510,33 @@ int addpattern(Parser *parser, char *id) **-------------------------------------------------------------- */ { - STmplist *patlist; + int n = network->Npats; + Spattern *pattern; - // Check if ID is same as last one processed - if (parser->Patlist != NULL && strcmp(id, parser->Patlist->ID) == 0) return 0; - - // Check that pattern was not already created - if (getlistitem(id, parser->Patlist) == NULL) + // Check if pattern was already created + if (n > 0) { - // Update pattern count & create new list element - (parser->MaxPats)++; - patlist = (STmplist *)malloc(sizeof(STmplist)); - if (patlist == NULL) return 101; - - // Initialize list element properties - else - { - patlist->i = parser->MaxPats; - strncpy(patlist->ID, id, MAXID); - patlist->x = NULL; - patlist->y = NULL; - patlist->next = parser->Patlist; - parser->Patlist = patlist; - } + if (strcmp(id, network->Pattern[n].ID) == 0) return 0; + if (findpattern(network, id) > 0) return 0; } + if (strlen(id) > MAXID) return 250; + + // Update pattern count & add a new pattern to the database + n = n + 2; + network->Pattern = (Spattern *)realloc(network->Pattern, n * sizeof(Spattern)); + if (network->Pattern == NULL) return 101; + (network->Npats)++; + + // Initialize the pattern + pattern = &network->Pattern[network->Npats]; + strncpy(pattern->ID, id, MAXID); + pattern->Comment = NULL; + pattern->Length = 0; + pattern->F = NULL; return 0; } -int addcurve(Parser *parser, char *id) +int addcurve(Network *network, char *id) /* **------------------------------------------------------------- ** Input: id = curve ID label @@ -528,51 +545,34 @@ int addcurve(Parser *parser, char *id) **-------------------------------------------------------------- */ { - STmplist *curvelist; + int n = network->Ncurves; + Scurve *curve; - // Check if ID is same as last one processed - if (parser->Curvelist != NULL && strcmp(id, parser->Curvelist->ID) == 0) return 0; - - // Check that curve was not already created - if (getlistitem(id, parser->Curvelist) == NULL) + // Check if was already created + if (n > 0) { - // Update curve count & create new list element - (parser->MaxCurves)++; - curvelist = (STmplist *)malloc(sizeof(STmplist)); - if (curvelist == NULL) return 101; - - // Initialize list element properties - else - { - curvelist->i = parser->MaxCurves; - strncpy(curvelist->ID, id, MAXID); - curvelist->x = NULL; - curvelist->y = NULL; - curvelist->next = parser->Curvelist; - parser->Curvelist = curvelist; - } + if (strcmp(id, network->Curve[n].ID) == 0) return 0; + if (findcurve(network, id) > 0) return 0; } + if (strlen(id) > MAXID) return 250; + + n = n + 2; + network->Curve = (Scurve *)realloc(network->Curve, n * sizeof(Scurve)); + if (network->Curve == NULL) return 101; + (network->Ncurves)++; + + // Initialize the curve + curve = &network->Curve[network->Ncurves]; + strncpy(curve->ID, id, MAXID); + curve->Type = GENERIC_CURVE; + curve->Comment = NULL; + curve->Capacity = 0; + curve->Npts = 0; + curve->X = NULL; + curve->Y = NULL; return 0; } -STmplist *getlistitem(char *id, STmplist *list) -/* -**------------------------------------------------------------- -** Input: id = ID label -** list = pointer to head of a temporary list -** Output: returns list item with requested ID label -** Purpose: searches for item in temporary list -**------------------------------------------------------------- -*/ -{ - STmplist *item; - for (item = list; item != NULL; item = item->next) - { - if (strcmp(item->ID, id) == 0) return item; - } - return NULL; -} - int unlinked(Project *pr) /* **-------------------------------------------------------------- @@ -587,7 +587,7 @@ int unlinked(Project *pr) Network *net = &pr->network; int *marked; int i, err, errcode; - + errcode = 0; err = 0; @@ -596,19 +596,19 @@ int unlinked(Project *pr) ERRCODE(MEMCHECK(marked)); if (errcode) return errcode; memset(marked, 0, (net->Nnodes + 1) * sizeof(int)); - + // Mark end nodes of each link for (i = 1; i <= net->Nlinks; i++) { marked[net->Link[i].N1]++; marked[net->Link[i].N2]++; } - + // Check each junction for (i = 1; i <= net->Njuncs; i++) { // If not marked then error - if (marked[i] == 0) + if (marked[i] == 0) { err++; sprintf(pr->Msg, "Error 233: %s %s", geterrmsg(233, pr->Msg), net->Node[i].ID); @@ -621,144 +621,6 @@ int unlinked(Project *pr) return errcode; } -int getpatterns(Project *pr) -/* -**----------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: retrieves pattern data from temporary linked list -**------------------------------------------------------------- -*/ -{ - Network *net = &pr->network; - Hydraul *hyd = &pr->hydraul; - Parser *parser = &pr->parser; - - int i, j; - SFloatlist *f; - STmplist *tmppattern; - Spattern *pattern; - - // Start at head of the list of patterns - tmppattern = parser->Patlist; - - // Traverse list of temporary patterns - while (tmppattern != NULL) - { - // Get index of temporary pattern in network's Pattern array - i = tmppattern->i; - - // Check if this is the default pattern - if (strcmp(tmppattern->ID, parser->DefPatID) == 0) hyd->DefPat = i; - - // Copy temporary patttern to network's pattern - if (i >= 0 && i <= parser->MaxPats) - { - pattern = &net->Pattern[i]; - strcpy(pattern->ID, tmppattern->ID); - - /* Give pattern a length of at least 1 */ - if (pattern->Length == 0) pattern->Length = 1; - - // Allocate array of pattern factors - pattern->F = (double *)calloc(pattern->Length, sizeof(double)); - if (pattern->F == NULL) return 101; - - // Start at head of temporary pattern multiplier list - // (which holds multipliers in reverse order) - f = tmppattern->x; - j = pattern->Length - 1; - - // Use at least one multiplier equal to 1.0 - if (f == NULL) pattern->F[0] = 1.0; - - // Traverse temporary multiplier list, copying Pattern array */ - else while (f != NULL && j >= 0) - { - pattern->F[j] = f->value; - f = f->next; - j--; - } - } - tmppattern = tmppattern->next; - } - return 0; -} - -int getcurves(Project *pr) -/* -**----------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: retrieves curve data from temporary linked list -**----------------------------------------------------------- -*/ -{ - Network *net = &pr->network; - Parser *parser = &pr->parser; - - int i, j; - double x; - char errmsg[MAXMSG+1]; - SFloatlist *fx, *fy; - STmplist *tmpcurve; - Scurve *curve; - - // Traverse list of temporary curves - tmpcurve = parser->Curvelist; - while (tmpcurve != NULL) - { - // Get index of temporary curve in network's Curve array - i = tmpcurve->i; - - // Copy temporary curve to network's curve - if (i >= 1 && i <= parser->MaxCurves) - { - curve = &net->Curve[i]; - strcpy(curve->ID, tmpcurve->ID); - - // Check that network curve has data points - if (curve->Npts <= 0) - { - sprintf(pr->Msg, "Error 230: %s %s", geterrmsg(230, errmsg), curve->ID); - writeline(pr, pr->Msg); - return 200; - } - - // Allocate memory for network's curve data - curve->X = (double *)calloc(curve->Npts, sizeof(double)); - curve->Y = (double *)calloc(curve->Npts, sizeof(double)); - if (curve->X == NULL || curve->Y == NULL) return 101; - - // Traverse list of x,y data - x = BIG; - fx = tmpcurve->x; - fy = tmpcurve->y; - j = curve->Npts - 1; - while (fx != NULL && fy != NULL && j >= 0) - { - // Check that x data is in ascending order - if (fx->value >= x) - { - sprintf(pr->Msg, "Error 230: %s %s", geterrmsg(230, errmsg), curve->ID); - writeline(pr, pr->Msg); - return 200; - } - x = fx->value; - - // Copy x,y data to network's curve - curve->X[j] = fx->value; - fx = fx->next; - curve->Y[j] = fy->value; - fy = fy->next; - j--; - } - } - tmpcurve = tmpcurve->next; - } - return 0; -} - int findmatch(char *line, char *keyword[]) /* **-------------------------------------------------------------- @@ -824,17 +686,17 @@ int gettokens(char *s, char** Tok, int maxToks, char *comment) **-------------------------------------------------------------- */ { - int m, n; - size_t len; + int n; + size_t len, m; char *c, *c2; - + // clear comment comment[0] = '\0'; - + // Begin with no tokens for (n=0; n 0 && n < MAXTOKS) { m = (int)strcspn(s,SEPSTR); // Find token length + if (m == len) // s is last token + { + Tok[n] = s; + n++; + break; + } len -= m+1; // Update length of s if (m == 0) s++; // No token found else @@ -868,7 +736,7 @@ int gettokens(char *s, char** Tok, int maxToks, char *comment) { s++; // Start token after quote m = (int)strcspn(s,"\"\n\r"); // Find end quote (or EOL) - } + } s[m] = '\0'; // Null-terminate the token Tok[n] = s; // Save pointer to token n++; // Update token count @@ -876,7 +744,7 @@ int gettokens(char *s, char** Tok, int maxToks, char *comment) } } return n; -} +} double hour(char *time, char *units) /* @@ -914,7 +782,7 @@ double hour(char *time, char *units) if (match(units, w_DAYS)) return (y[0] * 24.0); } - // Convert hh:mm:ss format to decimal hours + // Convert hh:mm:ss format to decimal hours if (n > 1) y[0] = y[0] + y[1] / 60.0 + y[2] / 3600.0; // If am/pm attached then adjust hour accordingly @@ -933,7 +801,7 @@ double hour(char *time, char *units) else return (y[0] + 12.0); } return -1.0; -} +} int getfloat(char *s, double *y) /* @@ -979,14 +847,14 @@ void inperrmsg(Project *pr, int err, int sect, char *line) */ { Parser *parser = &pr->parser; - + char errStr[MAXMSG + 1] = ""; char tok[MAXMSG + 1]; // Get token associated with input error if (parser->ErrTok >= 0) strcpy(tok, parser->Tok[parser->ErrTok]); else strcpy(tok, ""); - + // write error message to report file sprintf(pr->Msg, "Error %d: %s %s in %s section:", err, geterrmsg(err, errStr), tok, SectTxt[sect]); diff --git a/src/input3.c b/src/input3.c index 2608f75..61324a3 100644 --- a/src/input3.c +++ b/src/input3.c @@ -7,16 +7,20 @@ Description: parses network data from a line of an EPANET input file Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE -Last Updated: 03/17/2019 +Last Updated: 04/03/2019 ****************************************************************************** */ -#include -#include -#include -#ifndef __APPLE__ -#include +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include #endif +#include +#include + #include #include "types.h" @@ -36,7 +40,6 @@ int powercurve(double, double, double, double, double, double *, double *, // Imported Functions extern int addnodeID(Network *, int, char *); extern int addlinkID(Network *, int, char *); -extern STmplist *getlistitem(char *, STmplist *); // Local functions static int optionchoice(Project *, int); @@ -83,7 +86,6 @@ int juncdata(Project *pr) double el, // elevation y = 0.0; // base demand Pdemand demand; // demand record - STmplist *patlist; // list of demands Snode *node; // Add new junction to data base @@ -100,9 +102,8 @@ int juncdata(Project *pr) if (n >= 3 && !getfloat(parser->Tok[2], &y)) return setError(parser, 2, 202); if (n >= 4) { - patlist = getlistitem(parser->Tok[3], parser->Patlist); - if (patlist == NULL) return setError(parser, 3, 205); - p = patlist->i; + p = findpattern(net, parser->Tok[3]); + if (p == 0) return setError(parser, 3, 205); } // Save junction data @@ -158,7 +159,6 @@ int tankdata(Project *pr) minvol = 0.0, // Minimum volume diam = 0.0, // Diameter area; // X-sect. area - STmplist *tmplist; Snode *node; Stank *tank; @@ -182,9 +182,8 @@ int tankdata(Project *pr) // Head pattern supplied if (n == 3) { - tmplist = getlistitem(parser->Tok[2], parser->Patlist); - if (tmplist == NULL) return setError(parser, 2, 205); - pattern = tmplist->i; + pattern = findpattern(net, parser->Tok[2]); + if (pattern == 0) return setError(parser, 2, 205); } } else if (n < 6) return 201; @@ -201,9 +200,8 @@ int tankdata(Project *pr) // If volume curve supplied check it exists if (n == 8) { - tmplist = getlistitem(parser->Tok[7], parser->Curvelist); - if (tmplist == NULL) return setError(parser, 7, 206); - curve = tmplist->i; + curve = findcurve(net, parser->Tok[7]); + if (curve == 0) return setError(parser, 7, 206); net->Curve[curve].Type = VOLUME_CURVE; } if (initlevel < 0.0) return setError(parser, 2, 209); @@ -353,12 +351,12 @@ int pumpdata(Project *pr) Network *net = &pr->network; Parser *parser = &pr->parser; - int j, + int j, m, // Token array indexes j1, // Start-node index j2, // End-node index - m, n; // # data items + n, // # data items + c, p; // Curve & Pattern indexes double y; - STmplist *tmplist; // Temporary list Slink *link; Spump *pump; @@ -428,15 +426,15 @@ int pumpdata(Project *pr) } else if (match(parser->Tok[m - 1], w_HEAD)) // Custom pump curve { - tmplist = getlistitem(parser->Tok[m], parser->Curvelist); - if (tmplist == NULL) return setError(parser, m, 206); - pump->Hcurve = tmplist->i; + c = findcurve(net, parser->Tok[m]); + if (c == 0) return setError(parser, m, 206); + pump->Hcurve = c; } else if (match(parser->Tok[m - 1], w_PATTERN)) // Speed/status pattern { - tmplist = getlistitem(parser->Tok[m], parser->Patlist); - if (tmplist == NULL) return setError(parser, m, 205); - pump->Upat = tmplist->i; + p = findpattern(net, parser->Tok[m]); + if (p == 0) return setError(parser, m, 205); + pump->Upat = p; } else if (match(parser->Tok[m - 1], w_SPEED)) // Speed setting { @@ -465,7 +463,8 @@ int valvedata(Project *pr) Network *net = &pr->network; Parser *parser = &pr->parser; - int j1, // Start-node index + int c, // Curve index + j1, // Start-node index j2, // End-node index n; // # data items char status = ACTIVE, // Valve status @@ -473,7 +472,6 @@ int valvedata(Project *pr) double diam = 0.0, // Valve diameter setting, // Valve setting lcoeff = 0.0; // Minor loss coeff. - STmplist *tmplist; // Temporary list Slink *link; // Add new valve to data base @@ -504,10 +502,10 @@ int valvedata(Project *pr) // Find headloss curve for GPV if (type == GPV) { - tmplist = getlistitem(parser->Tok[5], parser->Curvelist); - if (tmplist == NULL) return setError(parser, 5, 206); - setting = tmplist->i; - net->Curve[tmplist->i].Type = HLOSS_CURVE; + c = findcurve(net, parser->Tok[5]); + if (c == 0) return setError(parser, 5, 206); + setting = c; + net->Curve[c].Type = HLOSS_CURVE; status = OPEN; } else if (!getfloat(parser->Tok[5], &setting)) return setError(parser, 5, 202); @@ -554,43 +552,47 @@ int patterndata(Project *pr) Network *net = &pr->network; Parser *parser = &pr->parser; - int i, n; + int i, j, n, n1; double x; - SFloatlist *f; - STmplist *p; Spattern *pattern; + // "n" is the number of pattern factors contained in the line n = parser->Ntokens - 1; if (n < 1) return 201; - // Check for a new pattern - if (parser->PrevPat != NULL && - strcmp(parser->Tok[0], parser->PrevPat->ID) == 0) p = parser->PrevPat; + // Check if previous input line was for the same pattern + if (parser->PrevPat && strcmp(parser->Tok[0], parser->PrevPat->ID) == 0) + { + pattern = parser->PrevPat; + } + + // Otherwise retrieve pattern from the network's Pattern array else { - p = getlistitem(parser->Tok[0], parser->Patlist); - if (p == NULL) return setError(parser, 0, 205); - pattern = &(net->Pattern[p->i]); - pattern->Comment = xstrcpy(&pattern->Comment, parser->Comment, MAXMSG); + i = findpattern(net, parser->Tok[0]); + if (i == 0) return setError(parser, 0, 205); + pattern = &(net->Pattern[i]); + if (pattern->Comment == NULL && parser->Comment[0]) + { + pattern->Comment = xstrcpy(&pattern->Comment, parser->Comment, MAXMSG); + } } + // Expand size of the pattern's factors array + n1 = pattern->Length; + pattern->Length += n; + pattern->F = realloc(pattern->F, pattern->Length * sizeof(double)); + // Add parsed multipliers to the pattern - for (i = 1; i <= n; i++) + for (j = 1; j <= n; j++) { - if (!getfloat(parser->Tok[i], &x)) return setError(parser, i, 202); - f = (SFloatlist *)malloc(sizeof(SFloatlist)); - if (f == NULL) return 101; - f->value = x; - f->next = p->x; - p->x = f; + if (!getfloat(parser->Tok[j], &x)) return setError(parser, j, 202); + pattern->F[n1 + j - 1] = x; } - // Save # multipliers for pattern - net->Pattern[p->i].Length += n; - - // Set previous pattern pointer - parser->PrevPat = p; - return (0); + // Save a reference to this pattern for processing additional pattern data + parser->PrevPat = pattern; + return 0; } int curvedata(Project *pr) @@ -608,46 +610,46 @@ int curvedata(Project *pr) Network *net = &pr->network; Parser *parser = &pr->parser; + int i; double x, y; - SFloatlist *fx, *fy; - STmplist *c; Scurve *curve; - // Check for valid curve ID - if (parser->Ntokens < 3) return 201; - if (parser->PrevCurve != NULL && - strcmp(parser->Tok[0], parser->PrevCurve->ID) == 0) c = parser->PrevCurve; - else - { - c = getlistitem(parser->Tok[0], parser->Curvelist); - if (c == NULL) return setError(parser, 0, 206); - curve = &(net->Curve[c->i]); - curve->Comment = xstrcpy(&curve->Comment, parser->Comment, MAXMSG); - } - // Check for valid data + if (parser->Ntokens < 3) return 201; if (!getfloat(parser->Tok[1], &x)) return setError(parser, 1, 202); if (!getfloat(parser->Tok[2], &y)) return setError(parser, 2, 202); - // Add new data point to curve - fx = (SFloatlist *)malloc(sizeof(SFloatlist)); - if (fx == NULL) return 101; - fy = (SFloatlist *)malloc(sizeof(SFloatlist)); - if (fy == NULL) + // Check if previous input line was for the same curve + if (parser->PrevCurve && strcmp(parser->Tok[0], parser->PrevCurve->ID) == 0) { - free(fx); - return 101; + curve = parser->PrevCurve; } - fx->value = x; - fx->next = c->x; - c->x = fx; - fy->value = y; - fy->next = c->y; - c->y = fy; - net->Curve[c->i].Npts++; - // Save the pointer to this curve - parser->PrevCurve = c; + // Otherwise retrieve curve from the network's Curve array + else + { + i = findcurve(net, parser->Tok[0]); + if (i == 0) return setError(parser, 0, 206); + curve = &(net->Curve[i]); + if (curve->Comment == NULL && parser->Comment[0]) + { + curve->Comment = xstrcpy(&curve->Comment, parser->Comment, MAXMSG); + } + } + + // Expand size of data arrays if need be + if (curve->Capacity == curve->Npts) + { + if (resizecurve(curve, curve->Capacity + 10) > 0) return 101; + } + + // Add new data point to curve + curve->X[curve->Npts] = x; + curve->Y[curve->Npts] = y; + curve->Npts++; + + // Save a reference to this curve for processing additional curve data + parser->PrevCurve = curve; return 0; } @@ -709,7 +711,6 @@ int demanddata(Project *pr) double y; Pdemand demand; Pdemand cur_demand; - STmplist *patlist; // Extract data from tokens n = parser->Ntokens; @@ -729,9 +730,8 @@ int demanddata(Project *pr) if (j > net->Njuncs) return 0; if (n >= 3) { - patlist = getlistitem(parser->Tok[2], parser->Patlist); - if (patlist == NULL) return setError(parser, 2, 205); - p = patlist->i; + p = findpattern(net, parser->Tok[2]); + if (p == 0) return setError(parser, 2, 205); } // Replace any demand entered in [JUNCTIONS] section @@ -742,7 +742,10 @@ int demanddata(Project *pr) // with what is specified in this section demand->Base = y; demand->Pat = p; - demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXID); + if (parser->Comment[0]) + { + demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXID); + } hyd->NodeDemand[j] = MISSING; // marker - next iteration will append a new category. } @@ -755,7 +758,11 @@ int demanddata(Project *pr) if (demand == NULL) return 101; demand->Base = y; demand->Pat = p; - demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXID); + demand->Name = NULL; + if (parser->Comment[0]) + { + demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXID); + } demand->next = NULL; cur_demand->next = demand; } @@ -896,7 +903,6 @@ int sourcedata(Project *pr) p = 0; // Time pattern index char type = CONCEN; // Source type double c0 = 0; // Initial quality - STmplist *patlist; Psource source; // Check for enough tokens & that source node exists @@ -925,9 +931,8 @@ int sourcedata(Project *pr) if (n > i + 1 && strlen(parser->Tok[i + 1]) > 0 && strcmp(parser->Tok[i + 1], "*") != 0) { - patlist = getlistitem(parser->Tok[i + 1], parser->Patlist); - if (patlist == NULL) return setError(parser, i+1, 205); - p = patlist->i; + p = findpattern(net, parser->Tok[i + 1]); + if (p == 0) return setError(parser, i + 1, 205); } // Destroy any existing source assigned to node @@ -1331,10 +1336,9 @@ int energydata(Project *pr) Hydraul *hyd = &pr->hydraul; Parser *parser = &pr->parser; - int j, k, n; + int j, k, n, p, c; double y; - STmplist *listitem; Slink *Link = net->Link; Spump *Pump = net->Pump; @@ -1381,10 +1385,10 @@ int energydata(Project *pr) // Price PATTERN being set else if (match(parser->Tok[n - 2], w_PATTERN)) { - listitem = getlistitem(parser->Tok[n - 1], parser->Patlist); - if (listitem == NULL) return setError(parser, n - 1, 205); - if (j == 0) hyd->Epat = listitem->i; - else Pump[j].Epat = listitem->i; + p = findpattern(net, parser->Tok[n - 1]); + if (p == 0) return setError(parser, n - 1, 205); + if (j == 0) hyd->Epat = p; + else Pump[j].Epat = p; return 0; } @@ -1399,10 +1403,10 @@ int energydata(Project *pr) } else { - listitem = getlistitem(parser->Tok[n - 1], parser->Curvelist); - if (listitem == NULL) return setError(parser, n - 1, 206); - Pump[j].Ecurve = listitem->i; - net->Curve[listitem->i].Type = EFFIC_CURVE; + c = findcurve(net, parser->Tok[n - 1]); + if (c == 0) return setError(parser, n - 1, 206); + Pump[j].Ecurve = c; + net->Curve[c].Type = EFFIC_CURVE; } return 0; } diff --git a/src/mempool.c b/src/mempool.c index 55552ed..eb75e43 100755 --- a/src/mempool.c +++ b/src/mempool.c @@ -14,10 +14,14 @@ ****************************************************************************** */ -#include -#ifndef __APPLE__ -#include +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include #endif + #include "mempool.h" /* diff --git a/src/outfile/CMakeLists.txt b/src/outfile/CMakeLists.txt index cdd2f0f..c129221 100644 --- a/src/outfile/CMakeLists.txt +++ b/src/outfile/CMakeLists.txt @@ -21,7 +21,9 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) # configure file groups set(EPANET_OUT_SOURCES src/epanet_output.c - ../util/errormanager.c) + ../util/errormanager.c + ../util/filemanager.c + ../util/cstr_helper.c) # the binary output file API diff --git a/src/outfile/src/epanet_output.c b/src/outfile/src/epanet_output.c index dcbcd75..7d46e03 100644 --- a/src/outfile/src/epanet_output.c +++ b/src/outfile/src/epanet_output.c @@ -2,8 +2,9 @@ // // epanet_output.c -- API for reading results from EPANET binary output file // -// Version: 0.30 -// Date 09/06/2017 +// Version: 0.40 +// Date 04/02/2019 +// 09/06/2017 // 06/17/2016 // 08/05/2014 // 05/21/2014 @@ -40,18 +41,13 @@ #include #include "util/errormanager.h" +#include "util/filemanager.h" #include "epanet_output.h" #include "messages.h" // NOTE: These depend on machine data model and may change when porting -// F_OFF Must be a 8 byte / 64 bit integer for large file support -#ifdef _WIN32 // Windows (32-bit and 64-bit) -#define F_OFF __int64 -#else // Other platforms -#define F_OFF off_t -#endif #define INT4 int // Must be a 4 byte / 32 bit integer type #define REAL4 float // Must be a 4 byte / 32 bit real type #define WORDSIZE 4 // Memory alignment 4 byte word size for both int and real @@ -71,33 +67,28 @@ // Typedefs for opaque pointer typedef struct data_s { - char name[MAXFNAME+1]; // file path/name - FILE* file; // FILE structure pointer INT4 nodeCount, tankCount, linkCount, pumpCount, valveCount, nPeriods; F_OFF outputStartPos; // starting file position of output data F_OFF bytesPerPeriod; // bytes saved per simulation time period - error_handle_t* error_handle; + error_handle_t *error_handle; + file_handle_t *file_handle; } data_t; //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- -void errorLookup(int errcode, char* errmsg, int length); -int validateFile(ENR_Handle); -float getNodeValue(ENR_Handle, int, int, int); -float getLinkValue(ENR_Handle, int, int, int); +void errorLookup(int errcode, char* errmsg, int length); +int validateFile(ENR_Handle); +float getNodeValue(ENR_Handle, int, int, int); +float getLinkValue(ENR_Handle, int, int, int); -int _fopen(FILE **f, const char *name, const char *mode); -int _fseek(FILE* stream, F_OFF offset, int whence); -F_OFF _ftell(FILE* stream); - -float* newFloatArray(int n); -int* newIntArray(int n); -char* newCharArray(int n); +float *newFloatArray(int n); +int *newIntArray(int n); +char *newCharArray(int n); -int EXPORT_OUT_API ENR_init(ENR_Handle* dp_handle) +int EXPORT_OUT_API ENR_init(ENR_Handle *dp_handle) // Purpose: Initialized pointer for the opaque ENR_Handle. // // Returns: Error code 0 on success, -1 on failure @@ -114,6 +105,7 @@ int EXPORT_OUT_API ENR_init(ENR_Handle* dp_handle) if (p_data != NULL){ p_data->error_handle = create_error_manager(&errorLookup); + p_data->file_handle = create_file_manager(); *dp_handle = p_data; } else @@ -123,7 +115,7 @@ int EXPORT_OUT_API ENR_init(ENR_Handle* dp_handle) return errorcode; } -int EXPORT_OUT_API ENR_close(ENR_Handle* p_handle) +int EXPORT_OUT_API ENR_close(ENR_Handle *p_handle) /*------------------------------------------------------------------------ ** Input: *p_handle = pointer to ENR_Handle struct ** @@ -143,13 +135,16 @@ int EXPORT_OUT_API ENR_close(ENR_Handle* p_handle) p_data = (data_t*)(*p_handle); - if (p_data == NULL || p_data->file == NULL) + if (p_data == NULL || p_data->file_handle == NULL) errorcode = -1; else { + close_file(p_data->file_handle); + delete_error_manager(p_data->error_handle); - fclose(p_data->file); + delete_file_manager(p_data->file_handle); + free(p_data); *p_handle = NULL; @@ -178,23 +173,22 @@ int EXPORT_OUT_API ENR_open(ENR_Handle p_handle, const char* path) if (p_data == NULL) return -1; else { - strncpy(p_data->name, path, MAXFNAME); // Attempt to open binary output file for reading only - if ((_fopen(&(p_data->file), path, "rb")) != 0) errorcode = 434; + if ((open_file(p_data->file_handle, path, "rb")) != 0) + errorcode = 434; // Perform checks to insure the file is valid else if ((err = validateFile(p_data)) != 0) errorcode = err; // If a warning is encountered read file header if (errorcode < 400 ) { - // read network size - fseek(p_data->file, 2*WORDSIZE, SEEK_SET); - fread(&(p_data->nodeCount), WORDSIZE, 1, p_data->file); - fread(&(p_data->tankCount), WORDSIZE, 1, p_data->file); - fread(&(p_data->linkCount), WORDSIZE, 1, p_data->file); - fread(&(p_data->pumpCount), WORDSIZE, 1, p_data->file); - fread(&(p_data->valveCount), WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, 2*WORDSIZE, SEEK_SET); + read_file(&(p_data->nodeCount), WORDSIZE, 1, p_data->file_handle); + read_file(&(p_data->tankCount), WORDSIZE, 1, p_data->file_handle); + read_file(&(p_data->linkCount), WORDSIZE, 1, p_data->file_handle); + read_file(&(p_data->pumpCount), WORDSIZE, 1, p_data->file_handle); + read_file(&(p_data->valveCount), WORDSIZE, 1, p_data->file_handle); // Compute positions and offsets for retrieving data // fixed portion of header + title section + filenames + chem names @@ -240,8 +234,8 @@ int EXPORT_OUT_API ENR_getVersion(ENR_Handle p_handle, int* version) if (p_data == NULL) return -1; else { - fseek(p_data->file, 1*WORDSIZE, SEEK_SET); - if (fread(version, WORDSIZE, 1, p_data->file) != 1) + seek_file(p_data->file_handle, 1*WORDSIZE, SEEK_SET); + if (read_file(version, WORDSIZE, 1, p_data->file_handle) != 1) errorcode = 436; } @@ -319,26 +313,26 @@ int EXPORT_OUT_API ENR_getUnits(ENR_Handle p_handle, ENR_Units code, int* unitFl switch (code) { case ENR_flowUnits: - _fseek(p_data->file, 9*WORDSIZE, SEEK_SET); - fread(unitFlag, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, 9*WORDSIZE, SEEK_SET); + read_file(unitFlag, WORDSIZE, 1, p_data->file_handle); break; case ENR_pressUnits: - _fseek(p_data->file, 10*WORDSIZE, SEEK_SET); - fread(unitFlag, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, 10*WORDSIZE, SEEK_SET); + read_file(unitFlag, WORDSIZE, 1, p_data->file_handle); break; case ENR_qualUnits: offset = 7*WORDSIZE; - _fseek(p_data->file, offset, SEEK_SET); - fread(unitFlag, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_SET); + read_file(unitFlag, WORDSIZE, 1, p_data->file_handle); if (*unitFlag == 0) *unitFlag = ENR_NONE; else if (*unitFlag == 1) { offset = 15*WORDSIZE + 3*MAXMSG_P1 + 2*(MAXFNAME+1) + MAXID_P1; - _fseek(p_data->file, offset, SEEK_SET); - fread(temp, MAXID_P1, 1, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_SET); + read_file(temp, MAXID_P1, 1, p_data->file_handle); if (!strcmp(temp, "mg/L")) *unitFlag = ENR_MGL; else *unitFlag = ENR_UGL; @@ -379,18 +373,18 @@ int EXPORT_OUT_API ENR_getTimes(ENR_Handle p_handle, ENR_Time code, int* time) switch (code) { case ENR_reportStart: - fseek(p_data->file, 12*WORDSIZE, SEEK_SET); - fread(time, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, 12*WORDSIZE, SEEK_SET); + read_file(time, WORDSIZE, 1, p_data->file_handle); break; case ENR_reportStep: - fseek(p_data->file, 13*WORDSIZE, SEEK_SET); - fread(time, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, 13*WORDSIZE, SEEK_SET); + read_file(time, WORDSIZE, 1, p_data->file_handle); break; case ENR_simDuration: - fseek(p_data->file, 14*WORDSIZE, SEEK_SET); - fread(time, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, 14*WORDSIZE, SEEK_SET); + read_file(time, WORDSIZE, 1, p_data->file_handle); break; case ENR_numPeriods: @@ -405,7 +399,6 @@ int EXPORT_OUT_API ENR_getTimes(ENR_Handle p_handle, ENR_Time code, int* time) } int EXPORT_OUT_API ENR_getChemData(ENR_Handle p_handle, char** name, int* length) - { return 0; } @@ -459,8 +452,8 @@ int EXPORT_OUT_API ENR_getElementName(ENR_Handle p_handle, ENR_ElementType type, if (!errorcode) { - _fseek(p_data->file, offset, SEEK_SET); - fread(temp, 1, MAXID_P1, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_SET); + read_file(temp, 1, MAXID_P1, p_data->file_handle); *name = temp; *length = MAXID_P1; @@ -505,9 +498,9 @@ int EXPORT_OUT_API ENR_getEnergyUsage(ENR_Handle p_handle, int pumpIndex, offset += (pumpIndex - 1)*(WORDSIZE + 6*WORDSIZE); // Power summary is 1 int and 6 floats for each pump - _fseek(p_data->file, offset, SEEK_SET); - fread(linkIndex, WORDSIZE, 1, p_data->file); - fread(temp, WORDSIZE, 6, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_SET); + read_file(linkIndex, WORDSIZE, 1, p_data->file_handle); + read_file(temp, WORDSIZE, 6, p_data->file_handle); *outValues = temp; *length = NENERGYRESULTS; @@ -541,8 +534,8 @@ int EXPORT_OUT_API ENR_getNetReacts(ENR_Handle p_handle, float** outValues, int* // Reaction summary is 4 floats located right before epilogue. // This offset is relative to the end of the file. offset = - 3*WORDSIZE - 4*WORDSIZE; - _fseek(p_data->file, offset, SEEK_END); - fread(temp, WORDSIZE, 4, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_END); + read_file(temp, WORDSIZE, 4, p_data->file_handle); *outValues = temp; *length = NREACTRESULTS; @@ -670,8 +663,8 @@ int EXPORT_OUT_API ENR_getNodeAttribute(ENR_Handle p_handle, int periodIndex, // add offset for node and attribute offset += ((attr - 1)*p_data->nodeCount)*WORDSIZE; - _fseek(p_data->file, offset, SEEK_SET); - fread(temp, WORDSIZE, p_data->nodeCount, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_SET); + read_file(temp, WORDSIZE, p_data->nodeCount, p_data->file_handle); *outValueArray = temp; *length = p_data->nodeCount; @@ -720,8 +713,8 @@ int EXPORT_OUT_API ENR_getLinkAttribute(ENR_Handle p_handle, int periodIndex, // add offset for link and attribute offset += ((attr - 1)*p_data->linkCount)*WORDSIZE; - _fseek(p_data->file, offset, SEEK_SET); - fread(temp, WORDSIZE, p_data->linkCount, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_SET); + read_file(temp, WORDSIZE, p_data->linkCount, p_data->file_handle); *outValueArray = temp; *length = p_data->linkCount; @@ -852,16 +845,16 @@ int validateFile(ENR_Handle p_handle) p_data = (data_t*)p_handle; // Read magic number from beginning of file - fseek(p_data->file, 0L, SEEK_SET); - fread(&magic1, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, 0L, SEEK_SET); + read_file(&magic1, WORDSIZE, 1, p_data->file_handle); // Fast forward to end and read file epilogue - fseek(p_data->file, -3*WORDSIZE, SEEK_END); - fread(&(p_data->nPeriods), WORDSIZE, 1, p_data->file); - fread(&hydcode, WORDSIZE, 1, p_data->file); - fread(&magic2, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, -3*WORDSIZE, SEEK_END); + read_file(&(p_data->nPeriods), WORDSIZE, 1, p_data->file_handle); + read_file(&hydcode, WORDSIZE, 1, p_data->file_handle); + read_file(&magic2, WORDSIZE, 1, p_data->file_handle); - filepos = _ftell(p_data->file); + filepos = tell_file(p_data->file_handle); // Is the file an EPANET binary file? if (magic1 != magic2) errorcode = 435; @@ -891,8 +884,8 @@ float getNodeValue(ENR_Handle p_handle, int periodIndex, int nodeIndex, // add byte position for attribute and node offset += ((attr - 1)*p_data->nodeCount + (nodeIndex - 1))*WORDSIZE; - _fseek(p_data->file, offset, SEEK_SET); - fread(&y, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_SET); + read_file(&y, WORDSIZE, 1, p_data->file_handle); return y; } @@ -915,56 +908,12 @@ float getLinkValue(ENR_Handle p_handle, int periodIndex, int linkIndex, // add byte position for attribute and link offset += ((attr - 1)*p_data->linkCount + (linkIndex - 1))*WORDSIZE; - _fseek(p_data->file, offset, SEEK_SET); - fread(&y, WORDSIZE, 1, p_data->file); + seek_file(p_data->file_handle, offset, SEEK_SET); + read_file(&y, WORDSIZE, 1, p_data->file_handle); return y; } -int _fopen(FILE **f, const char *name, const char *mode) { - // - // Purpose: Substitute for fopen_s on platforms where it doesn't exist - // Note: fopen_s is part of C++11 standard - // - int ret = 0; -#ifdef _WIN32 - ret = (int)fopen_s(f, name, mode); -#else - *f = fopen(name, mode); - if (!*f) - ret = -1; -#endif - return ret; -} - -int _fseek(FILE* stream, F_OFF offset, int whence) -// -// Purpose: Selects platform fseek() for large file support -// -{ -#ifdef _WIN32 // Windows (32-bit and 64-bit) -#define FSEEK64 _fseeki64 -#else // Other platforms -#define FSEEK64 fseeko -#endif - - return FSEEK64(stream, offset, whence); -} - -F_OFF _ftell(FILE* stream) -// -// Purpose: Selects platform ftell() for large file support -// -{ -#ifdef _WIN32 // Windows (32-bit and 64-bit) -#define FTELL64 _ftelli64 -#else // Other platforms -#define FTELL64 ftello -#endif - - return FTELL64(stream); -} - float* newFloatArray(int n) // // Warning: Caller must free memory allocated by this function. diff --git a/src/output.c b/src/output.c index fd92077..5082cbb 100644 --- a/src/output.c +++ b/src/output.c @@ -11,13 +11,16 @@ Last Updated: 11/27/2018 ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif + #include #include "types.h" @@ -32,7 +35,7 @@ static int savetimestat(Project *, REAL4 *, HdrType); static int savenetreacts(Project *, double, double, double, double); static int saveepilog(Project *); -// Functions to write/read x[1] to x[n] to/from binary file +// Functions to write/read x[1] to x[n] to/from binary file size_t f_save(REAL4 *x, int n, FILE *file) { return fwrite(x + 1, sizeof(REAL4), n, file); @@ -79,7 +82,7 @@ int savenetdata(Project *pr) { // Write integer variables to outFile ibuf[0] = MAGICNUMBER; - ibuf[1] = 20012; // keep version at 2.00.12 so that GUI will run + ibuf[1] = 20012; // keep version at 2.00.12 so that GUI will run ibuf[2] = net->Nnodes; ibuf[3] = net->Ntanks; ibuf[4] = net->Nlinks; @@ -141,7 +144,7 @@ int savenetdata(Project *pr) x[i] = (REAL4)(net->Node[i].El * pr->Ucf[ELEV]); } f_save(x, net->Nnodes, outFile); - + // Save link lengths & diameters to outFile for (i = 1; i <= net->Nlinks; i++) { @@ -185,7 +188,7 @@ int savehyd(Project *pr, long *htime) int errcode = 0; REAL4 *x; FILE *HydFile = out->HydFile; - + x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); if (x == NULL) return 101; @@ -212,13 +215,13 @@ int savehyd(Project *pr, long *htime) fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile); //f_save(x, net->Nlinks, HydFile); - // Save link status + // Save link status for (i = 1; i <= net->Nlinks; i++) x[i] = (REAL4)hyd->LinkStatus[i]; fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile); //f_save(x, net->Nlinks, HydFile); - // Save link settings & check for successful write-to-disk - // (We assume that if any of the previous fwrites failed, + // Save link settings & check for successful write-to-disk + // (We assume that if any of the previous fwrites failed, // then this one will also fail.) for (i = 1; i <= net->Nlinks; i++) x[i] = (REAL4)hyd->LinkSetting[i]; if (fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile) < @@ -354,7 +357,7 @@ int readhyd(Project *pr, long *hydtime) int result = 1; REAL4 *x; FILE *HydFile = out->HydFile; - + x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); if (x == NULL) return 0; @@ -473,7 +476,7 @@ int nodeoutput(Project *pr, int j, REAL4 *x, double ucf) } } - // Write x[1] to x[net->Nnodes] to output file + // Write x[1] to x[net->Nnodes] to output file if (f_save(x, net->Nnodes, outFile) < (unsigned)net->Nnodes) return 308; return 0; } @@ -497,7 +500,7 @@ int linkoutput(Project *pr, int j, REAL4 *x, double ucf) int i; double a, h, q, f, setting; FILE *outFile = out->TmpOutFile; - + // Load computed results (in proper units) into buffer x switch (j) { @@ -631,7 +634,7 @@ int savefinaloutput(Project *pr) int errcode = 0; REAL4 *x; FILE *outFile = out->OutFile; - + // Save time series statistic if computed if (rpt->Tstatflag != SERIES && out->TmpOutFile != NULL) { @@ -680,7 +683,7 @@ int savetimestat(Project *pr, REAL4 *x, HdrType objtype) long startbyte, skipbytes; float *stat1, *stat2, xx; FILE *outFile = out->OutFile; - + // Compute number of bytes in temp output file to skip over (skipbytes) // when moving from one time period to the next for a particular variable if (objtype == NODEHDR) @@ -722,11 +725,11 @@ int savetimestat(Project *pr, REAL4 *x, HdrType objtype) stat2[i] = MISSING; } - // Position temp output file at start of output + // Position temp output file at start of output fseek(out->TmpOutFile, startbyte + (j - n1) * n * sizeof(REAL4), SEEK_SET); - // Process each time period + // Process each time period for (p = 1; p <= rpt->Nperiods; p++) { // Get output results for time period & update stats @@ -751,7 +754,7 @@ int savetimestat(Project *pr, REAL4 *x, HdrType objtype) } } - // Advance file to next period + // Advance file to next period if (p < rpt->Nperiods) fseek(out->TmpOutFile, skipbytes, SEEK_CUR); } @@ -821,7 +824,7 @@ int savenetreacts(Project *pr, double wbulk, double wwall, double wtank, double double t; REAL4 w[4]; FILE *outFile = out->OutFile; - + if (time->Dur > 0) t = (double)time->Dur / 3600.; else t = 1.; w[0] = (REAL4)(wbulk / t); @@ -846,7 +849,7 @@ int saveepilog(Project *pr) int errcode = 0; INT4 i; FILE *outFile = out->OutFile; - + i = rpt->Nperiods; if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) errcode = 308; i = pr->Warnflag; diff --git a/src/project.c b/src/project.c index fd4cb8d..1ee3295 100644 --- a/src/project.c +++ b/src/project.c @@ -7,17 +7,19 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 03/17/2019 + Last Updated: 04/03/2019 ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif //*** For the Windows SDK _tempnam function ***// #ifdef _WIN32 @@ -174,7 +176,7 @@ int openoutfile(Project *pr) // Close output file if already opened closeoutfile(pr); - + // If output file name was supplied, then attempt to // open it. Otherwise open a temporary output file. pr->outfile.OutFile = fopen(pr->outfile.OutFname, "w+b"); @@ -272,9 +274,6 @@ void initpointers(Project *pr) pr->network.NodeHashTable = NULL; pr->network.LinkHashTable = NULL; - pr->parser.Patlist = NULL; - pr->parser.Curvelist = NULL; - pr->hydraul.smatrix.Aii = NULL; pr->hydraul.smatrix.Aij = NULL; pr->hydraul.smatrix.F = NULL; @@ -284,7 +283,7 @@ void initpointers(Project *pr) pr->hydraul.smatrix.XLNZ = NULL; pr->hydraul.smatrix.NZSUB = NULL; pr->hydraul.smatrix.LNZ = NULL; - + initrules(pr); } @@ -315,10 +314,10 @@ int allocdata(Project *pr) if (!errcode) { n = pr->parser.MaxNodes + 1; - pr->network.Node = (Snode *)calloc(n, sizeof(Snode)); + pr->network.Node = (Snode *)calloc(n, sizeof(Snode)); pr->hydraul.NodeDemand = (double *)calloc(n, sizeof(double)); pr->hydraul.NodeHead = (double *)calloc(n, sizeof(double)); - pr->quality.NodeQual = (double *)calloc(n, sizeof(double)); + pr->quality.NodeQual = (double *)calloc(n, sizeof(double)); ERRCODE(MEMCHECK(pr->network.Node)); ERRCODE(MEMCHECK(pr->hydraul.NodeDemand)); ERRCODE(MEMCHECK(pr->hydraul.NodeHead)); @@ -329,8 +328,8 @@ int allocdata(Project *pr) if (!errcode) { n = pr->parser.MaxLinks + 1; - pr->network.Link = (Slink *)calloc(n, sizeof(Slink)); - pr->hydraul.LinkFlow = (double *)calloc(n, sizeof(double)); + pr->network.Link = (Slink *)calloc(n, sizeof(Slink)); + pr->hydraul.LinkFlow = (double *)calloc(n, sizeof(double)); pr->hydraul.LinkSetting = (double *)calloc(n, sizeof(double)); pr->hydraul.LinkStatus = (StatusType *)calloc(n, sizeof(StatusType)); ERRCODE(MEMCHECK(pr->network.Link)); @@ -339,8 +338,8 @@ int allocdata(Project *pr) ERRCODE(MEMCHECK(pr->hydraul.LinkStatus)); } - // Allocate memory for tanks, sources, pumps, valves, - // controls, demands, time patterns, & operating curves + // Allocate memory for tanks, sources, pumps, valves, and controls + // (memory for Patterns and Curves arrays expanded as each is added) if (!errcode) { pr->network.Tank = @@ -351,35 +350,15 @@ int allocdata(Project *pr) (Svalve *)calloc(pr->parser.MaxValves + 1, sizeof(Svalve)); pr->network.Control = (Scontrol *)calloc(pr->parser.MaxControls + 1, sizeof(Scontrol)); - pr->network.Pattern = - (Spattern *)calloc(pr->parser.MaxPats + 1, sizeof(Spattern)); - pr->network.Curve = - (Scurve *)calloc(pr->parser.MaxCurves + 1, sizeof(Scurve)); ERRCODE(MEMCHECK(pr->network.Tank)); ERRCODE(MEMCHECK(pr->network.Pump)); ERRCODE(MEMCHECK(pr->network.Valve)); ERRCODE(MEMCHECK(pr->network.Control)); - ERRCODE(MEMCHECK(pr->network.Pattern)); - ERRCODE(MEMCHECK(pr->network.Curve)); } - // Initialize pointers used in patterns, curves, and demand category lists + // Initialize pointers used in nodes and links if (!errcode) { - for (n = 0; n <= pr->parser.MaxPats; n++) - { - pr->network.Pattern[n].Length = 0; - pr->network.Pattern[n].F = NULL; - pr->network.Pattern[n].Comment = NULL; - } - for (n = 0; n <= pr->parser.MaxCurves; n++) - { - pr->network.Curve[n].Npts = 0; - pr->network.Curve[n].Type = GENERIC_CURVE; - pr->network.Curve[n].X = NULL; - pr->network.Curve[n].Y = NULL; - pr->network.Curve[n].Comment = NULL; - } for (n = 0; n <= pr->parser.MaxNodes; n++) { pr->network.Node[n].D = NULL; // node demand @@ -397,43 +376,6 @@ int allocdata(Project *pr) return errcode; } -void freeTmplist(STmplist *t) -/*---------------------------------------------------------------- -** Input: t = pointer to start of a temporary list -** Output: none -** Purpose: frees memory used for temporary storage -** of pattern & curve data -**---------------------------------------------------------------- -*/ -{ - STmplist *tnext; - while (t != NULL) - { - tnext = t->next; - freeFloatlist(t->x); - freeFloatlist(t->y); - free(t); - t = tnext; - } -} - -void freeFloatlist(SFloatlist *f) -/*---------------------------------------------------------------- -** Input: f = pointer to start of list of floats -** Output: none -** Purpose: frees memory used for storing list of floats -**---------------------------------------------------------------- -*/ -{ - SFloatlist *fnext; - while (f != NULL) - { - fnext = f->next; - free(f); - f = fnext; - } -} - void freedata(Project *pr) /*---------------------------------------------------------------- ** Input: none @@ -444,7 +386,6 @@ void freedata(Project *pr) { int j; Pdemand demand, nextdemand; - Psource source; // Free memory for computed results free(pr->hydraul.NodeDemand); @@ -472,8 +413,7 @@ void freedata(Project *pr) demand = nextdemand; } // Free memory used for WQ source data - source = pr->network.Node[j].S; - free(source); + free(pr->network.Node[j].S); free(pr->network.Node[j].Comment); } free(pr->network.Node); @@ -509,7 +449,8 @@ void freedata(Project *pr) // Free memory for curves if (pr->network.Curve != NULL) { - for (j = 0; j <= pr->parser.MaxCurves; j++) + // There is no Curve[0] + for (j = 1; j <= pr->parser.MaxCurves; j++) { free(pr->network.Curve[j].X); free(pr->network.Curve[j].Y); @@ -807,6 +748,40 @@ int findvalve(Network *network, int index) return NOTFOUND; } +int findpattern(Network *network, char *id) +/*---------------------------------------------------------------- +** Input: id = time pattern ID +** Output: none +** Returns: time pattern index, or 0 if pattern not found +** Purpose: finds index of time pattern given its ID +**---------------------------------------------------------------- +*/ +{ + int i; + for (i = 1; i <= network->Npats; i++) + { + if (strcmp(id, network->Pattern[i].ID) == 0) return i; + } + return 0; +} + +int findcurve(Network *network, char *id) +/*---------------------------------------------------------------- +** Input: id = data curve ID +** Output: none +** Returns: data curve index, or 0 if curve not found +** Purpose: finds index of data curve given its ID +**---------------------------------------------------------------- +*/ +{ + int i; + for (i = 1; i <= network->Ncurves; i++) + { + if (strcmp(id, network->Curve[i].ID) == 0) return i; + } + return 0; +} + void adjustpattern(int *pat, int index) /*---------------------------------------------------------------- ** Local function that modifies a reference to a deleted time pattern @@ -902,6 +877,35 @@ void adjustcurves(Network *network, int index) } } +int resizecurve(Scurve *curve, int size) +/*---------------------------------------------------------------- +** Input: curve = a data curve object +** size = desired number of curve data points +** Output: error code +** Purpose: resizes a data curve to a desired size +**---------------------------------------------------------------- +*/ +{ + double *x; + double *y; + + if (curve->Capacity < size) + { + x = (double *)realloc(curve->X, size * sizeof(double)); + if (x == NULL) return 101; + y = (double *)realloc(curve->Y, size * sizeof(double)); + if (y == NULL) + { + free(x); + return 101; + } + curve->X = x; + curve->Y = y; + curve->Capacity = size; + } + return 0; +} + int getcomment(Network *network, int object, int index, char *comment) //---------------------------------------------------------------- // Input: object = a type of network object @@ -1027,7 +1031,7 @@ char *xstrcpy(char **s1, const char *s2, const size_t n) { size_t n1 = 0, n2; - // Source string is empty -- free destination string + // Source string is empty -- free destination string if (s2 == NULL || strlen(s2) == 0) { free(*s1); diff --git a/src/quality.c b/src/quality.c index de27f63..d46bd68 100644 --- a/src/quality.c +++ b/src/quality.c @@ -11,13 +11,16 @@ Last Updated: 11/27/2018 ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif + #include #include "mempool.h" @@ -60,7 +63,7 @@ int openqual(Project *pr) int errcode = 0; int n; - + // Return if no quality analysis requested if (qual->Qualflag == NONE) return errcode; diff --git a/src/qualreact.c b/src/qualreact.c index 0805a21..3b4b741 100644 --- a/src/qualreact.c +++ b/src/qualreact.c @@ -11,6 +11,13 @@ Last Updated: 11/27/2018 ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include #include "types.h" @@ -25,6 +32,7 @@ double mixtank(Project *, int, double, double ,double); // Imported functions extern void addseg(Project *, int, double, double); +extern void reversesegs(Project *, int); // Local functions static double piperate(Project *, int); @@ -163,7 +171,7 @@ void reactpipes(Project *pr, long dt) } -void reacttanks(Project *pr, long dt) +void reacttanks(Project *pr, long dt) /* **-------------------------------------------------------------- ** Input: dt = time step @@ -532,7 +540,7 @@ void tankmix2(Project *pr, int i, double vin, double win, double vnet) vt = MAX(0.0, (mixzone->v + vnet - vmz)); if (vin > 0.0) { - mixzone->c = ((stagzone->c) * (stagzone->v) + win) / + mixzone->c = ((mixzone->c) * (mixzone->v) + win) / (mixzone->v + vin); } if (vt > 0.0) @@ -676,18 +684,14 @@ void tankmix4(Project *pr, int i, double vin, double win, double vnet) seg = qual->LastSeg[k]; if (vnet > 0.0) { - // ... quality is the same, so just add flow volume to last seg + // ... inflow quality is same as last segment's quality, + // so just add inflow volume to last segment if (fabs(seg->c - cin) < qual->Ctol) seg->v += vnet; - // ... otherwise add a new last seg to tank which points to old last seg - else - { - qual->LastSeg[k] = NULL; - addseg(pr, k, vnet, cin); - qual->LastSeg[k]->prev = seg; - } + // ... otherwise add a new last segment with inflow quality + else addseg(pr, k, vnet, cin); - // ... update reported tank quality + // Update reported tank quality tank->C = qual->LastSeg[k]->c; } @@ -697,28 +701,48 @@ void tankmix4(Project *pr, int i, double vin, double win, double vnet) vsum = 0.0; wsum = 0.0; vnet = -vnet; + + // Reverse segment chain so segments are processed from last to first + reversesegs(pr, k); + + // While there is still volume to remove while (vnet > 0.0) { - seg = qual->LastSeg[k]; + // ... start with reversed first segment + seg = qual->FirstSeg[k]; if (seg == NULL) break; + + // ... find volume to remove from it vseg = seg->v; vseg = MIN(vseg, vnet); - if (seg == qual->FirstSeg[k]) vseg = vnet; + if (seg == qual->LastSeg[k]) vseg = vnet; + + // ... update total volume & mass removed vsum += vseg; wsum += (seg->c) * vseg; + + // ... update remiaing volume to remove vnet -= vseg; - if (vnet >= 0.0 && vseg >= seg->v) // Seg used up + + // ... if no more volume left in current segment + if (vnet >= 0.0 && vseg >= seg->v) { + // ... replace current segment with previous one if (seg->prev) { - qual->LastSeg[k] = seg->prev; + qual->FirstSeg[k] = seg->prev; seg->prev = qual->FreeSeg; qual->FreeSeg = seg; } } - else seg->v -= vseg; // Remaining volume in segment + + // ... otherwise reduce volume of current segment + else seg->v -= vseg; } + // Restore original orientation of segment chain + reversesegs(pr, k); + // Reported tank quality is mixture of flow released and any inflow tank->C = (wsum + win) / (vsum + vin); } diff --git a/src/qualroute.c b/src/qualroute.c index 72bb66f..aa8e26a 100644 --- a/src/qualroute.c +++ b/src/qualroute.c @@ -11,13 +11,17 @@ Last Updated: 11/27/2018 ****************************************************************************** */ -#include -#ifndef __APPLE__ -#include +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include #else -#include + #include #endif +#include + #include + #include "mempool.h" #include "types.h" diff --git a/src/report.c b/src/report.c index 5605ffb..c35d73f 100644 --- a/src/report.c +++ b/src/report.c @@ -11,13 +11,15 @@ ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif #ifdef _WIN32 #define snprintf _snprintf @@ -83,7 +85,7 @@ int copyreport(Project* pr, char *filename) FILE *tfile; int c; Report *rpt = &pr->report; - + // Check that project's report file exists if (rpt->RptFile == NULL) return 0; @@ -101,7 +103,7 @@ int copyreport(Project* pr, char *filename) while ((c = fgetc(rpt->RptFile)) != EOF) fputc(c, tfile); fclose(rpt->RptFile); } - + // Close destination file fclose(tfile); diff --git a/src/rules.c b/src/rules.c index 7e9945f..d0addb8 100644 --- a/src/rules.c +++ b/src/rules.c @@ -11,13 +11,15 @@ ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif #include "types.h" #include "funcs.h" diff --git a/src/smatrix.c b/src/smatrix.c index 510bb3e..1b14794 100755 --- a/src/smatrix.c +++ b/src/smatrix.c @@ -18,13 +18,16 @@ linsolve() -- called from netsolve() in HYDRAUL.C */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif #include #include -#ifndef __APPLE__ -#include -#else -#include -#endif + #include #include diff --git a/src/types.h b/src/types.h index 4329414..476afac 100755 --- a/src/types.h +++ b/src/types.h @@ -7,7 +7,7 @@ Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE - Last Updated: 03/17/2019 + Last Updated: 04/03/2019 ****************************************************************************** */ @@ -317,23 +317,6 @@ struct IDstring // Holds component ID label char ID[MAXID+1]; }; -struct Floatlist // Element of List of Numbers -{ - double value; // element's numerical value - struct Floatlist *next; // next element on the list -}; -typedef struct Floatlist SFloatlist; - -struct Tmplist // Item of Temporary List of Objects -{ - int i; // object's index - char ID[MAXID+1]; // object's ID name - SFloatlist *x; // list of data values - SFloatlist *y; // list of data values - struct Tmplist *next; // next object on list -}; -typedef struct Tmplist STmplist; // Pointer to temporary list of objects - typedef struct // Time Pattern Object { char ID[MAXID+1]; // pattern ID @@ -348,6 +331,7 @@ typedef struct // Curve Object char *Comment; // curve comment CurveType Type; // curve type int Npts; // number of points + int Capacity; // size of X & Y arrays double *X; // x-values double *Y; // y-values } Scurve; @@ -573,14 +557,11 @@ typedef struct { ErrTok, // Index of error-producing token Unitsflag, // Unit system flag Flowflag, // Flow units flag - Pressflag; // Pressure units flag + Pressflag, // Pressure units flag + DefPat; // Default demand pattern - STmplist - *Patlist, // Temporary time pattern list - *PrevPat, // Previous pattern list element - *Curvelist, // Temporary list of curves - *PrevCurve; // Previous curve list element - + Spattern *PrevPat; // Previous pattern processed + Scurve *PrevCurve; // Previous curve processed double *X; // Temporary array for curve data } Parser; @@ -738,7 +719,6 @@ typedef struct { *Xflow; // Inflow - outflow at each node int - DefPat, // Default demand pattern Epat, // Energy cost time pattern DemandModel, // Fixed or pressure dependent Formflag, // Head loss formula flag diff --git a/src/util/cstr_helper.c b/src/util/cstr_helper.c new file mode 100644 index 0000000..7d005a0 --- /dev/null +++ b/src/util/cstr_helper.c @@ -0,0 +1,48 @@ +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: util/cstr_helper.c + Description: Provides C string helper functions + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 04/02/2019 + ****************************************************************************** +*/ + +#include +#include + +#include "cstr_helper.h" + + +int copy_cstr(const char *source, char **dest) +// Determines length, allocates memory, and returns a null terminated copy +// Be Aware: caller is responsible for freeing memory +{ + size_t size; + + size = 1 + strlen(source); + *dest = (char *) calloc(size, sizeof(char)); + + if (*dest == NULL) + return -1; + else { +#ifdef _MSC_VER + strncpy_s(*dest, size, source, size); +#else + strncpy(*dest, source, size); +#endif + } + return 0; +} + + +bool isnullterm_cstr(const char *source) +{ + if (strchr(source, '\0')) + return true; + else + return false; +} diff --git a/src/util/cstr_helper.h b/src/util/cstr_helper.h new file mode 100644 index 0000000..c344042 --- /dev/null +++ b/src/util/cstr_helper.h @@ -0,0 +1,25 @@ +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: util/cstr_helper.h + Description: Provides C string helper functions + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 04/02/2019 + ****************************************************************************** +*/ + +#ifndef CSTR_HELPER_H_ +#define CSTR_HELPER_H_ + + +#include + + +int copy_cstr(const char *source, char **destination); +bool isnullterm_cstr(const char *source); + + +#endif /* CSTR_HELPER_H_ */ diff --git a/src/util/errormanager.c b/src/util/errormanager.c index 5a9876c..57c19de 100644 --- a/src/util/errormanager.c +++ b/src/util/errormanager.c @@ -1,14 +1,15 @@ -//----------------------------------------------------------------------------- -// -// errormanager.c -// -// Purpose: Provides a simple interface for managing runtime error messages. -// -// Date: 08/25/2017 -// -// Author: Michael E. Tryby -// US EPA - ORD/NRMRL -//----------------------------------------------------------------------------- +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: util/errormanager.c + Description: Provides a simple interface for managing errors + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 04/02/2019 + ****************************************************************************** +*/ //#ifdef _WIN32 //#define _CRTDBG_MAP_ALLOC @@ -22,6 +23,12 @@ #include "errormanager.h" +typedef struct error_s { + int error_status; + void (*p_msg_lookup)(int, char*, int); +} error_handle_t; + + error_handle_t *create_error_manager(void (*p_error_message)(int, char*, int)) // // Purpose: Constructs a new error handle. diff --git a/src/util/errormanager.h b/src/util/errormanager.h index 6939f35..49823a9 100644 --- a/src/util/errormanager.h +++ b/src/util/errormanager.h @@ -1,11 +1,15 @@ /* - * errormanager.h - * - * Created on: Aug 25, 2017 - * - * Author: Michael E. Tryby - * US EPA - ORD/NRMRL - */ + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: util/errormanager.h + Description: Provides a simple interface for managing errors + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 04/02/2019 + ****************************************************************************** +*/ #ifndef ERRORMANAGER_H_ #define ERRORMANAGER_H_ @@ -17,11 +21,8 @@ extern "C" { #endif - -typedef struct error_s { - int error_status; - void (*p_msg_lookup)(int, char*, int); -} error_handle_t; +// Forward declaration +typedef struct error_s error_handle_t; error_handle_t* create_error_manager(void (*p_error_message)(int, char*, int)); void delete_error_manager(error_handle_t* error_handle); diff --git a/src/util/filemanager.c b/src/util/filemanager.c new file mode 100644 index 0000000..eb9c4cd --- /dev/null +++ b/src/util/filemanager.c @@ -0,0 +1,211 @@ +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: util/filemanager.c + Description: Provides a simple interface for managing files + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 04/01/2019 + ****************************************************************************** +*/ + + +// MSVC ONLY +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif + +#include + +#include "filemanager.h" + + +typedef struct file_s { + char *filename; // Assumes this is a null terminated string + FILE *file; +} file_handle_t; + + +// local (private) functions +int _fopen(FILE **f, const char *name, const char *mode); +int _get_temp_filename(char **tempname); + + +file_handle_t *create_file_manager() { + + file_handle_t *file_handle; + file_handle = (file_handle_t *)calloc(1, sizeof(file_handle_t)); + + file_handle->filename = NULL; + file_handle->file = NULL; + + return file_handle; +} + +void delete_file_manager(file_handle_t *file_handle) { + + if (file_handle->file != NULL) + close_file(file_handle); + + free(file_handle->filename); + free(file_handle); +} + + +int get_filename(file_handle_t *file_handle, char **filename) +// +// BE AWARE: The memory allocated here must be freed by the caller +// +{ + return copy_cstr(file_handle->filename, filename); +} + + +int open_file(file_handle_t *file_handle, const char *filename, const char *file_mode) { + int error = 0; + + if (filename == NULL) + _get_temp_filename(&(file_handle->filename)); + else + copy_cstr(filename, &(file_handle->filename)); + + if (file_mode == NULL) + error = -1; + else { + error = _fopen(&(file_handle->file), file_handle->filename, file_mode); + } + + return error; +} + +int seek_file(file_handle_t *file_handle, F_OFF offset, int whence) +{ +#ifdef _MSC_VER // Windows (32-bit and 64-bit) +#define FSEEK64 _fseeki64 +#else // Other platforms +#define FSEEK64 fseeko +#endif + + return FSEEK64(file_handle->file, offset, whence); +} + +F_OFF tell_file(file_handle_t *file_handle) +{ +#ifdef _MSC_VER // Windows (32-bit and 64-bit) +#define FTELL64 _ftelli64 +#else // Other platforms +#define FTELL64 ftello +#endif + + return FTELL64(file_handle->file); +} + +// Read and write to a binary file +size_t read_file(void *ptr, size_t size, size_t nmemb, file_handle_t *file_handle) +{ + return fread(ptr, size, nmemb, file_handle->file); +} + +size_t write_file(const void * ptr, size_t size, size_t count, file_handle_t *file_handle) +{ + return fwrite(ptr, size, count, file_handle->file); +} + + +// print and get from a text file +int printf_file(file_handle_t *file_handle, const char *format, ... ) +{ + int error = 0; + va_list args; + + va_start(args, format); + error = vfprintf(file_handle->file, format, args); + va_end(args); + + return error; +} + +int gets_file(char *str, int num, file_handle_t *file_handle) +{ + fgets(str, num, file_handle->file); + return 0; +} + + +int close_file(file_handle_t *file_handle) { + int error = 0; + + if (file_handle->file != NULL) { + error = fclose(file_handle->file); + file_handle->file = NULL; + } + return error; +} + +int remove_file(file_handle_t *file_handle) { + return remove(file_handle->filename); +} + + +bool is_valid(file_handle_t *file_handle) +{ + if ((file_handle->filename == NULL && file_handle->file == NULL) || + (isnullterm_cstr(file_handle->filename) && file_handle != NULL)) + return true; + else + return false; +} + + +int _fopen(FILE **f, const char *name, const char *mode) +// +// Purpose: Substitute for fopen_s on platforms where it doesn't exist +// Note: fopen_s is part of C++11 standard +// +{ + int ret = 0; + +#ifdef _MSC_VER + ret = (int)fopen_s(f, name, mode); +#else + *f = fopen(name, mode); + if (!*f) + ret = -1; +#endif + return ret; +} + +int _get_temp_filename(char **tempname) +{ + int error = 0; + +#ifdef _MSC_VER + char *name = NULL; + + // --- use Windows _tempnam function to get a pointer to an + // unused file name that begins with "en" + if ((name = _tempnam(name, "en")) == NULL) { + error = -1; + return error; + } + else + copy_cstr(name, tempname); + + // --- free the pointer returned by _tempnam + if (name) + free(name); + + // --- for non-Windows systems: +#else + // --- use system function mkstemp() to create a temporary file name + copy_cstr("enXXXXXX", tempname); + error = mkstemp(*tempname); +#endif + return error; +} diff --git a/src/util/filemanager.h b/src/util/filemanager.h new file mode 100644 index 0000000..a3866c3 --- /dev/null +++ b/src/util/filemanager.h @@ -0,0 +1,81 @@ +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: util/filemanager.h + Description: Provides a simple interface for managing files + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 04/01/2019 + ****************************************************************************** +*/ + +#ifndef FILEMANAGER_H_ +#define FILEMANAGER_H_ + + +#include +#include + +#include "cstr_helper.h" + + +// F_OFF Must be a 8 byte / 64 bit integer for large file support +#ifdef _MSC_VER // Windows (32-bit and 64-bit) +#define F_OFF __int64 +#else // Other platforms +#define F_OFF off_t +#endif + +#define FILE_MAXNAME 259 + + +#if defined(__cplusplus) +extern "C" { +#endif + +// Forward declariation of file_handle_t +typedef struct file_s file_handle_t; + + +file_handle_t *create_file_manager(); + +void delete_file_manager(file_handle_t *file_handle); + + +int get_filename(file_handle_t *file_handle, char **filename); + + +int open_file(file_handle_t *file_handle, const char *filename, const char *file_mode); + +int seek_file(file_handle_t *file_handle, F_OFF offset, int whence); + +F_OFF tell_file(file_handle_t *file_handle); + + +// Functions for working with binary files +size_t read_file(void *ptr, size_t size, size_t nmemb, file_handle_t *file_handle); + +size_t write_file(const void *ptr, size_t size, size_t count, file_handle_t *file_handle); + + +// Functions for working with text files +int printf_file(file_handle_t *file_handle, const char *format, ... ); + +int gets_file(char *str, int num, file_handle_t *file_handle); + + +int close_file(file_handle_t *file_handle); + +int remove_file(file_handle_t *file_handle); + + +bool is_valid(file_handle_t *file_handle); + + +#if defined(__cplusplus) +} +#endif + +#endif /* FILEMANAGER_H_ */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d16c376..3d63d97 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,7 +30,8 @@ set(toolkit_test_srcs test_pattern.cpp test_curve.cpp test_control.cpp - test_net_builder.cpp) + test_net_builder.cpp +) add_executable(test_toolkit ${toolkit_test_srcs}) @@ -59,6 +60,10 @@ add_test(NAME test_reent add_test(NAME test_errormanager COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_errormanager) +add_test(NAME test_filemanager + COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_filemanager + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/outfile/data) + add_test(NAME test_output COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_output WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/outfile/data) diff --git a/tests/test_analysis.cpp b/tests/test_analysis.cpp index f7120f1..b3c9a93 100644 --- a/tests/test_analysis.cpp +++ b/tests/test_analysis.cpp @@ -22,10 +22,12 @@ BOOST_AUTO_TEST_SUITE (test_analysis) BOOST_FIXTURE_TEST_CASE(test_anlys_getoption, FixtureOpenClose) { int i; - double array[13]; - std::vector test; - std::vector ref = {40.0, 0.001, 0.01, 0.5, 1.0, 0.0, 0.0, 1.0, 0.0, 75.0, 0.0, 0.0, 0.0}; + std::vector test(23); + double *array = test.data(); + + std::vector ref = {40.0, 0.001, 0.01, 0.5, 1.0, 0.0, 0.0, 0.0, 75.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 10.0, 2.0, 10.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0}; error = EN_solveH(ph); BOOST_REQUIRE(error == 0); @@ -34,24 +36,25 @@ BOOST_FIXTURE_TEST_CASE(test_anlys_getoption, FixtureOpenClose) BOOST_REQUIRE(error == 0); - for (i=EN_TRIALS; i<=EN_DEMANDCHARGE; i++) { - error = EN_getoption(ph, i, &array[i]); + for (i=EN_TRIALS; i<=EN_CONCENLIMIT; i++) { + error = EN_getoption(ph, i, array++); BOOST_REQUIRE(error == 0); } - test.assign(array, array + 13); BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), test.begin(), test.end()); - error = EN_getoption(ph, 18, &array[0]); + double temp; + error = EN_getoption(ph, 25, &temp); BOOST_CHECK(error == 251); } BOOST_FIXTURE_TEST_CASE(test_anlys_gettimeparam, FixtureOpenClose) { int i; - long array[16]; - std::vector test; + std::vector test(16); + long *array = test.data(); + std::vector ref = {86400, 3600, 300, 7200, 0, 3600, 0, 360, 0, 25, 0, 86400, 86400, 0, 3600, 0}; error = EN_solveH(ph); @@ -62,14 +65,15 @@ BOOST_FIXTURE_TEST_CASE(test_anlys_gettimeparam, FixtureOpenClose) for (i=EN_DURATION; i<=EN_NEXTEVENTTANK; i++) { - error = EN_gettimeparam(ph, i, &array[i]); + error = EN_gettimeparam(ph, i, array++); BOOST_REQUIRE(error == 0); } - test.assign(array, array + 16); BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), test.begin(), test.end()); - error = EN_gettimeparam(ph, 18, &array[0]); + long temp; + error = EN_gettimeparam(ph, 18, &temp); BOOST_CHECK(error == 251); } + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_curve.cpp b/tests/test_curve.cpp index 941b473..6eb5a63 100644 --- a/tests/test_curve.cpp +++ b/tests/test_curve.cpp @@ -35,6 +35,35 @@ BOOST_FIXTURE_TEST_CASE(test_curve_comments, FixtureOpenClose) error = EN_getcomment(ph, EN_CURVE, index, comment); BOOST_REQUIRE(error == 0); BOOST_CHECK(check_string(comment, (char *)"Curve 1")); + + // Test of EN_setcurve and EN_getcurve + int i; + char id1[] = "NewCurve"; + int n1 = 5; + double X1[] = {16.88889, 19.5, 22.13889, 25.94445, 33.33334}; + double Y1[] = {156.7, 146.5, 136.2, 117.9, 50.0}; + int n2; + double X2[5], Y2[5]; + char id2[EN_MAXID+1]; + + // Add data to a new curve + error = EN_addcurve(ph, id1); + BOOST_REQUIRE(error == 0); + error = EN_getcurveindex(ph, id1, &i); + BOOST_REQUIRE(error == 0); + error = EN_setcurve(ph, i, X1, Y1, n1); + BOOST_REQUIRE(error == 0); + + // Retrieve data from curve + error = EN_getcurve(ph, i, id2, &n2, X2, Y2); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(check_string(id2, id1)); + BOOST_REQUIRE(n2 == n1); + for (i = 0; i < n1; i++) + { + BOOST_CHECK(X1[i] == X2[i]); + BOOST_CHECK(Y1[i] == Y2[i]); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_link.cpp b/tests/test_link.cpp index 15f451e..aa20d23 100644 --- a/tests/test_link.cpp +++ b/tests/test_link.cpp @@ -11,6 +11,8 @@ ****************************************************************************** */ +#include + #include #include "test_toolkit.hpp" diff --git a/tests/test_net_builder.cpp b/tests/test_net_builder.cpp index 5c6f274..4d79c48 100644 --- a/tests/test_net_builder.cpp +++ b/tests/test_net_builder.cpp @@ -11,6 +11,14 @@ ****************************************************************************** */ +#ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #include +#else + #include +#endif + #include #include "test_toolkit.hpp" @@ -35,6 +43,9 @@ BOOST_AUTO_TEST_CASE(test_init_close) struct FixtureInitClose { FixtureInitClose() { + error = 0; + ph = NULL; + EN_createproject(&ph); EN_init(ph, DATA_PATH_RPT, DATA_PATH_OUT, EN_GPM, EN_HW); } @@ -50,55 +61,55 @@ struct FixtureInitClose { -//BOOST_AUTO_TEST_CASE(net_builder_I) -//{ -// int error = 0; -// int flag = 00; -// long t, tstep; -// int i, ind, Lindex, Nindex, Cindex; -// double h_orig, h_build, h_build_loaded; +// BOOST_AUTO_TEST_CASE(net_builder_I) +// { +// int error = 0; +// int flag = 00; +// long t, tstep; +// int i, ind, Lindex, Nindex, Cindex; +// double h_orig, h_build, h_build_loaded; // -// // first we load Net1.inp, run it and record the head in Tank 2 at the end of the simulation (h_orig) -// EN_Project ph = NULL; -// EN_createproject(&ph); +// // first we load Net1.inp, run it and record the head in Tank 2 at the end of the simulation (h_orig) +// EN_Project ph = NULL; +// EN_createproject(&ph); // -// std::string path_inp = std::string(DATA_PATH_NET1); -// std::string path_rpt = std::string(DATA_PATH_RPT); -// std::string path_out = std::string(DATA_PATH_OUT); +// std::string path_inp = std::string(DATA_PATH_NET1); +// std::string path_rpt = std::string(DATA_PATH_RPT); +// std::string path_out = std::string(DATA_PATH_OUT); // -// error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); -// BOOST_REQUIRE(error == 0); +// error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); +// BOOST_REQUIRE(error == 0); // -// error = EN_getnodeindex(ph, (char *)"2", &Nindex); -// BOOST_REQUIRE(error == 0); +// error = EN_getnodeindex(ph, (char *)"2", &Nindex); +// BOOST_REQUIRE(error == 0); // -// error = EN_openH(ph); -// BOOST_REQUIRE(error == 0); +// error = EN_openH(ph); +// BOOST_REQUIRE(error == 0); // -// error = EN_initH(ph, flag); -// BOOST_REQUIRE(error == 0); +// error = EN_initH(ph, flag); +// BOOST_REQUIRE(error == 0); // -// do { -// error = EN_runH(ph, &t); -// BOOST_REQUIRE(error == 0); +// do { +// error = EN_runH(ph, &t); +// BOOST_REQUIRE(error == 0); // -// // this is the head at the end of the simulation after loading the original Net1.inp -// error = EN_getnodevalue(ph, Nindex, EN_HEAD, &h_orig); -// BOOST_REQUIRE(error == 0); +// // this is the head at the end of the simulation after loading the original Net1.inp +// error = EN_getnodevalue(ph, Nindex, EN_HEAD, &h_orig); +// BOOST_REQUIRE(error == 0); // -// error = EN_nextH(ph, &tstep); -// BOOST_REQUIRE(error == 0); +// error = EN_nextH(ph, &tstep); +// BOOST_REQUIRE(error == 0); // -// } while (tstep > 0); +// } while (tstep > 0); // -// error = EN_closeH(ph); -// BOOST_REQUIRE(error == 0); +// error = EN_closeH(ph); +// BOOST_REQUIRE(error == 0); // -// error = EN_close(ph); -// BOOST_REQUIRE(error == 0); +// error = EN_close(ph); +// BOOST_REQUIRE(error == 0); // -// EN_deleteproject(&ph); -//} +// EN_deleteproject(&ph); +// } BOOST_FIXTURE_TEST_CASE(test_build_net1, FixtureInitClose) { @@ -121,8 +132,8 @@ BOOST_FIXTURE_TEST_CASE(test_build_net1, FixtureInitClose) BOOST_REQUIRE(error == 0); error = EN_setpattern(ph, 1, P, 12); BOOST_REQUIRE(error == 0); - error = EN_setoption(ph, EN_DEFDEMANDPAT, 1); - BOOST_REQUIRE(error == 0); + //error = EN_setoption(ph, EN_DEFDEMANDPAT, 1); + //BOOST_REQUIRE(error == 0); for (i = 0; i < 9; i++) { error = EN_addnode(ph, juncs[i], EN_JUNCTION); @@ -131,6 +142,8 @@ BOOST_FIXTURE_TEST_CASE(test_build_net1, FixtureInitClose) BOOST_REQUIRE(error == 0); error = EN_setnodevalue(ph, i + 1, EN_BASEDEMAND, d[i]); BOOST_REQUIRE(error == 0); + error = EN_setnodevalue(ph, i+1, EN_PATTERN, 1); + BOOST_REQUIRE(error == 0); error = EN_setcoord(ph, i + 1, X[i], Y[i]); BOOST_REQUIRE(error == 0); //error = EN_setdemandpattern(ph, i + 1, 1, 1); diff --git a/tests/test_pattern.cpp b/tests/test_pattern.cpp index 674ea76..bedad48 100644 --- a/tests/test_pattern.cpp +++ b/tests/test_pattern.cpp @@ -33,12 +33,9 @@ BOOST_AUTO_TEST_CASE(add_set_pattern) error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), ""); BOOST_REQUIRE(error == 0); - // Get the default pattern index - double dblPatIdx; - int defPatIdx; + // Assign the default pattern index + int defPatIdx = 1; int patIdx; - EN_getoption(ph, EN_DEFDEMANDPAT, &dblPatIdx); - defPatIdx = (int)dblPatIdx; // Rename the default pattern EN_setpatternid(ph, defPatIdx, (char *)"Pat1"); diff --git a/tests/test_project.cpp b/tests/test_project.cpp index 6f0b68c..6642870 100644 --- a/tests/test_project.cpp +++ b/tests/test_project.cpp @@ -11,6 +11,8 @@ ****************************************************************************** */ +#include + #include #include @@ -109,37 +111,39 @@ BOOST_AUTO_TEST_SUITE(test_proj_fixture) BOOST_FIXTURE_TEST_CASE(test_title, FixtureOpenClose) { // How is the API user supposed to know array size? - char c_test[3][80], c_ref[3][80]; + char c_test[3][80]; - strncpy(c_ref[0], " EPANET Example Network 1", 26); - strncpy(c_ref[1], "A simple example of modeling chlorine decay. Both bulk and", 59); - strncpy(c_ref[2], "wall reactions are included. ", 30); + // ref is an automatic variable and therefore doesn't need to be deleted + std::string ref[3] = { + " EPANET Example Network 1", + "A simple example of modeling chlorine decay. Both bulk and", + "wall reactions are included. "}; error = EN_gettitle(ph, c_test[0], c_test[1], c_test[2]); BOOST_REQUIRE(error == 0); - for (int i = 0; i < 3; i++) { - std::string test (c_test[i]); - std::string ref (c_ref[i]); - BOOST_CHECK(check_string(test, ref)); - } + for (int i = 0; i < 3; i++) { + std::string test (c_test[i]); + BOOST_CHECK(check_string(test, ref[i])); + } // Need a test for EN_settitle } BOOST_FIXTURE_TEST_CASE(test_getcount, FixtureOpenClose) { - int i, array[7]; + int i; - std::vector test; - std::vector ref = { 11, 2, 13, 1, 1, 2, 0 }; + std::vector test(7); + int *array = test.data(); + + std::vector ref = { 11, 2, 13, 1, 1, 2, 0 }; for (i=EN_NODECOUNT; i<=EN_RULECOUNT; i++) { - error = EN_getcount(ph, i, &array[i]); + error = EN_getcount(ph, i, array++); BOOST_REQUIRE(error == 0); } - test.assign(array, array + 7); BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), test.begin(), test.end()); error = EN_getcount(ph, 7, &i); diff --git a/tests/test_report.cpp b/tests/test_report.cpp index f1268a3..5b5850c 100644 --- a/tests/test_report.cpp +++ b/tests/test_report.cpp @@ -21,9 +21,10 @@ BOOST_AUTO_TEST_SUITE (test_report) BOOST_FIXTURE_TEST_CASE(test_rprt_anlysstats, FixtureOpenClose) { int i; - double array[5]; - std::vector test; + std::vector test(5); + double *array = test.data(); + std::vector ref = {3.0, 7.0799498320679432e-06, 1.6680242187483429e-08, 0.0089173150106518495, 0.99999998187144024}; @@ -35,15 +36,13 @@ BOOST_FIXTURE_TEST_CASE(test_rprt_anlysstats, FixtureOpenClose) for (i=EN_ITERATIONS; i<=EN_MASSBALANCE; i++) { - error = EN_getstatistic(ph, i, &array[i]); + error = EN_getstatistic(ph, i, array++); BOOST_REQUIRE(error == 0); } - - test.assign(array, array + 5); -// BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), test.begin(), test.end()); BOOST_CHECK(check_cdd_double(test, ref, 3)); - error = EN_getstatistic(ph, 8, &array[0]); + double temp; + error = EN_getstatistic(ph, 8, &temp); BOOST_CHECK(error == 251); } diff --git a/tests/test_toolkit.hpp b/tests/test_toolkit.hpp index 03ac664..1373ce7 100644 --- a/tests/test_toolkit.hpp +++ b/tests/test_toolkit.hpp @@ -14,7 +14,6 @@ #ifndef TEST_TOOLKIT_HPP #define TEST_TOOLKIT_HPP - #include "epanet2_2.h" @@ -25,6 +24,9 @@ struct FixtureOpenClose{ FixtureOpenClose() { + error = 0; + ph = NULL; + EN_createproject(&ph); error = EN_open(ph, DATA_PATH_NET1, DATA_PATH_RPT, DATA_PATH_OUT); } @@ -41,6 +43,9 @@ struct FixtureOpenClose{ struct FixtureAfterStep{ FixtureAfterStep() { + error = 0; + ph = NULL; + flag = 0; tstop = 10800; diff --git a/tests/util/CMakeLists.txt b/tests/util/CMakeLists.txt index 6173a07..0bd4a43 100644 --- a/tests/util/CMakeLists.txt +++ b/tests/util/CMakeLists.txt @@ -7,11 +7,14 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set (test_source -./test_errormanager.cpp -../../src/util/errormanager.c -) - -add_executable(test_errormanager ${test_source}) +add_executable(test_errormanager ./test_errormanager.cpp + ../../src/util/errormanager.c) target_include_directories(test_errormanager PUBLIC ../../src/) target_link_libraries(test_errormanager ${Boost_LIBRARIES}) + + +add_executable(test_filemanager ./test_filemanager.cpp + ../../src/util/filemanager.c + ../../src/util/cstr_helper.c) +target_include_directories(test_filemanager PUBLIC ../../src/) +target_link_libraries(test_filemanager ${Boost_LIBRARIES}) diff --git a/tests/util/test_errormanager.cpp b/tests/util/test_errormanager.cpp index 0003ee2..43f59e2 100644 --- a/tests/util/test_errormanager.cpp +++ b/tests/util/test_errormanager.cpp @@ -1,7 +1,7 @@ #define BOOST_TEST_MODULE errormanager -//#define BOOST_TEST_DYN_LINK + #include #include "util/errormanager.h" @@ -15,10 +15,10 @@ void mock_lookup(int errcode, char *errmsg, int len) char *msg = NULL; if (errcode == 100) { - msg = MESSAGE_STRING; + msg = (char *)MESSAGE_STRING; } else { - msg = ""; + msg = (char *)""; } strncpy(errmsg, msg, len); } diff --git a/tests/util/test_filemanager.cpp b/tests/util/test_filemanager.cpp new file mode 100644 index 0000000..7e17577 --- /dev/null +++ b/tests/util/test_filemanager.cpp @@ -0,0 +1,97 @@ +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: util/test_filemanager.cpp + Description: Tests for util/filemanager.c + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 04/01/2019 + ****************************************************************************** +*/ + +#define BOOST_TEST_MODULE filemanager + +#include +#include + +#include "util/filemanager.h" + + +#define DATA_PATH_OUTPUT "./example1.out" + + +boost::test_tools::predicate_result check_string(std::string test, std::string ref) +{ + if (ref.compare(test) == 0) + return true; + else + return false; +} + + +BOOST_AUTO_TEST_SUITE(test_filemanager) + + +BOOST_AUTO_TEST_CASE (test_create_destroy) +{ + file_handle_t *file_handle = NULL; + + file_handle = create_file_manager(); + BOOST_CHECK(file_handle != NULL); + BOOST_CHECK(is_valid(file_handle) == true); + + delete_file_manager(file_handle); +} + +BOOST_AUTO_TEST_CASE(test_open_close) +{ + int error = 0; + file_handle_t *file_handle = NULL; + + file_handle = create_file_manager(); + BOOST_CHECK(file_handle != NULL); + + error = open_file(file_handle, DATA_PATH_OUTPUT, "rb"); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(is_valid(file_handle) == true); + + error = close_file(file_handle); + BOOST_REQUIRE(error == 0); + + delete_file_manager(file_handle); +} + +struct Fixture{ + Fixture() { + error = 0; + file_handle = NULL; + + file_handle = create_file_manager(); + open_file(file_handle, NULL, "wt"); + } + ~Fixture() { + close_file(file_handle); + delete_file_manager(file_handle); + } + int error; + file_handle_t *file_handle; +}; + +BOOST_FIXTURE_TEST_CASE(test_temp_file, Fixture) +{ + char *filename; + + printf_file(file_handle, "%s", "This is a test."); + + error = get_filename(file_handle, &filename); + BOOST_REQUIRE(error == 0); + BOOST_CHECK(is_valid(file_handle) == true); + + BOOST_CHECK(boost::filesystem::exists(filename) == true); + + free(filename); +} + +BOOST_AUTO_TEST_SUITE_END()