diff --git a/ReleaseNotes2_3.md b/ReleaseNotes2_3.md index 5a3b2d3..2a51cd6 100644 --- a/ReleaseNotes2_3.md +++ b/ReleaseNotes2_3.md @@ -25,6 +25,7 @@ This document describes the changes and updates that have been made in version 2 - A possible loss of network connectivity when evaluating a Pressure Sustaining Valve was prevented. - Having the implied loss coefficient for an active Flow Control Valve be less than its fully opened value was prevented. - A new type of valve, a Positional Control Valve (PCV), was added that uses a valve characteristic curve to relate its loss coefficient to its fraction open setting. - + - A new set of functions have been added to get information about upcoming time step events. Users will now see what type of event is going to cause the end of a time step to occur. See ENtimetonextevent and EN_timetonextevent. + - A new set of functions have been added to allow users to set a reporting callback function. The user-supplied function will recieve all output normally directed to the report file. diff --git a/include/epanet2.bas b/include/epanet2.bas index bc2ce1e..2d41bb2 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -253,6 +253,12 @@ Public Const EN_R_IS_OPEN = 1 ' Rule status types Public Const EN_R_IS_CLOSED = 2 Public Const EN_R_IS_ACTIVE = 3 +Public Const EN_STEP_REPORT = 0 ' Types of events that can cause a timestep to end +Public Const EN_STEP_HYD = 1 +Public Const EN_STEP_WQ = 2 +Public Const EN_STEP_TANKEVENT = 3 +Public Const EN_STEP_CONTROLEVENT = 4 + Public Const EN_MISSING As Double = -1.0E10 'These are the external functions that comprise the DLL @@ -302,6 +308,7 @@ Public Const EN_MISSING As Double = -1.0E10 Declare Function ENgeterror Lib "epanet2.dll" (ByVal errcode As Long, ByVal errmsg As String, ByVal maxLen As Long) As Long Declare Function ENgetstatistic Lib "epanet2.dll" (ByVal type_ As Long, ByRef value As Single) As Long Declare Function ENgetresultindex Lib "epanet2.dll" (ByVal type_ As Long, ByVal index As Long, ByRef value As Long) As Long + Declare Function ENtimetonextevent Lib "epanet2.dll" (eventType As Long, duration As Long, elementIndex As Long) As Long 'Analysis Options Functions Declare Function ENgetoption Lib "epanet2.dll" (ByVal option_ As Long, value As Single) As Long diff --git a/include/epanet2.def b/include/epanet2.def index 66b87ed..d72da8e 100644 --- a/include/epanet2.def +++ b/include/epanet2.def @@ -131,3 +131,4 @@ EXPORTS ENstepQ = _ENstepQ@4 ENusehydfile = _ENusehydfile@4 ENwriteline = _ENwriteline@4 + ENtimetonextevent = _ENtimetonextevent@12 \ No newline at end of file diff --git a/include/epanet2.h b/include/epanet2.h index 99c0ad0..ec32e67 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -159,6 +159,8 @@ extern "C" { int DLLEXPORT ENgetresultindex(int type, int index, int *value); + int DLLEXPORT ENtimetonextevent(int *eventType, long *duration, int *elementIndex); + int DLLEXPORT ENsetreportcallback(void (*callback)(void *userData, void *EN_projectHandle, char*)); int DLLEXPORT ENsetreportcallbackuserdata(void *userData); diff --git a/include/epanet2_2.h b/include/epanet2_2.h index 852eab2..76dea18 100644 --- a/include/epanet2_2.h +++ b/include/epanet2_2.h @@ -650,6 +650,16 @@ typedef struct Project *EN_Project; */ int DLLEXPORT EN_getstatistic(EN_Project ph, int type, double* out_value); + + /** + @brief Get information about upcoming time step events, and what causes them. + @param ph an EPANET project handle. + @param[out] eventType the type of event that will occur (see @ref EN_TimestepEvent). + @param[out] duration the amount of time in the future this event will occur + @param[out] elementIndex the index of the element causing the event. + **/ + int DLLEXPORT EN_timetonextevent(EN_Project ph, int *eventType, long *duration, int *elementIndex); + /** @brief Retrieves the order in which a node or link appears in an @ref OutFile "output file". @param ph an EPANET project handle. diff --git a/include/epanet2_enums.h b/include/epanet2_enums.h index b3347bc..8c4be41 100644 --- a/include/epanet2_enums.h +++ b/include/epanet2_enums.h @@ -126,6 +126,18 @@ typedef enum { EN_NEXTEVENTTANK = 15 //!< Index of tank with shortest time to become empty or full (read only) } EN_TimeParameter; + +/** +These are the types of events that can cause a timestep to end. +**/ +typedef enum { + EN_STEP_REPORT = 0, + EN_STEP_HYD = 1, + EN_STEP_WQ = 2, + EN_STEP_TANKEVENT = 3, + EN_STEP_CONTROLEVENT = 4 +} EN_TimestepEvent; + /// Analysis convergence statistics /** These statistics report the convergence criteria for the most current hydraulic analysis diff --git a/src/epanet.c b/src/epanet.c index feb8c4e..6516f7a 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1623,6 +1623,42 @@ int DLLEXPORT EN_settimeparam(EN_Project p, int param, long value) return 0; } + +/// get the time to next event, and give a reason for the time step truncation +int DLLEXPORT EN_timetonextevent(EN_Project p, int *eventType, long *duration, int *elementIndex) +{ + Times *time = &p->times; + long hydStep, tankStep, controlStep; + int iTank, iControl; + + hydStep = time->Hstep; + tankStep = hydStep; + controlStep = hydStep; + + iTank = tanktimestep(p, &tankStep); + iControl = controltimestep(p, &controlStep); + + // return the lesser of the three step lengths + if (controlStep < tankStep) { + *eventType = (int)EN_STEP_CONTROLEVENT; + *duration = controlStep; + *elementIndex = iControl; + } + else if (tankStep < hydStep) { + *eventType = (int)EN_STEP_TANKEVENT; + *duration = tankStep; + *elementIndex = iTank; + } + else { + *eventType = (int)EN_STEP_HYD; + *duration = hydStep; + *elementIndex = 0; + } + + return 0; +} + + int DLLEXPORT EN_getqualinfo(EN_Project p, int *qualType, char *chemName, char *chemUnits, int *traceNode) /*---------------------------------------------------------------- diff --git a/src/epanet2.c b/src/epanet2.c index 5618550..be16f6a 100644 --- a/src/epanet2.c +++ b/src/epanet2.c @@ -242,6 +242,10 @@ int DLLEXPORT ENgetresultindex(int type, int index, int *value) return EN_getresultindex(_defaultProject, type, index, value); } +int DLLEXPORT ENtimetonextevent(int *eventType, long *duration, int *elementIndex) +{ + return EN_timetonextevent(_defaultProject, eventType, duration, elementIndex); +} /******************************************************************** diff --git a/src/funcs.h b/src/funcs.h index f3e5ea4..69d3f81 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -155,6 +155,7 @@ void closehyd(Project *); void setlinkstatus(Project *, int, char, StatusType *, double *); void setlinksetting(Project *, int, double, StatusType *, double *); int tanktimestep(Project *, long *); +int controltimestep(Project *, long *); void getenergy(Project *, int, double *, double *); double tankvolume(Project *, int, double); double tankgrade(Project *, int, double); @@ -164,7 +165,7 @@ double tankgrade(Project *, int, double); void resistcoeff(Project *, int); void headlosscoeffs(Project *); void matrixcoeffs(Project *); -void emitterheadloss(Project *, int, double *, double *); +void emitterheadloss(Project *, int, double *, double *); void demandheadloss(Project *, int, double, double, double *, double *); double pcvlosscoeff(Project *, int, double); diff --git a/src/hydraul.c b/src/hydraul.c index fe9999a..d6e4424 100755 --- a/src/hydraul.c +++ b/src/hydraul.c @@ -34,7 +34,6 @@ void initlinkflow(Project *, int, char, double); void demands(Project *); int controls(Project *); long timestep(Project *); -void controltimestep(Project *, long *); void ruletimestep(Project *, long *); void addenergy(Project *, long); void tanklevels(Project *, long); @@ -192,7 +191,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); @@ -390,7 +389,7 @@ void setlinkstatus(Project *pr, int index, char value, StatusType *s, double *k if (t == PUMP) { *k = 1.0; - // Check if a re-opened pump needs its flow reset + // Check if a re-opened pump needs its flow reset if (*s == CLOSED) resetpumpflow(pr, index); } if (t > PUMP && t != GPV) *k = MISSING; @@ -597,11 +596,11 @@ int controls(Project *pr) k1 = hyd->LinkSetting[k]; k2 = k1; if (link->Type > PIPE) k2 = control->Setting; - + // Check if a re-opened pump needs its flow reset if (link->Type == PUMP && s1 == CLOSED && s2 == OPEN) resetpumpflow(pr, k); - + if (s1 != s2 || k1 != k2) { hyd->LinkStatus[k] = s2; @@ -706,7 +705,7 @@ int tanktimestep(Project *pr, long *tstep) } -void controltimestep(Project *pr, long *tstep) +int controltimestep(Project *pr, long *tstep) /* **------------------------------------------------------------------ ** Input: *tstep = current time step @@ -719,7 +718,7 @@ void controltimestep(Project *pr, long *tstep) Network *net = &pr->network; Hydraul *hyd = &pr->hydraul; - int i, j, k, n; + int i, j, k, n, controlIndex; double h, q, v; long t, t1, t2; Slink *link; @@ -776,9 +775,14 @@ void controltimestep(Project *pr, long *tstep) k = control->Link; link = &net->Link[k]; if ( (link->Type > PIPE && hyd->LinkSetting[k] != control->Setting) - || (hyd->LinkStatus[k] != control->Status) ) *tstep = t; + || (hyd->LinkStatus[k] != control->Status) ) + { + *tstep = t; + controlIndex = i; + } } } + return controlIndex; } @@ -1011,7 +1015,7 @@ void getallpumpsenergy(Project *pr) getenergy(pr, pump->Link, &(pump->Energy.CurrentPower), &(pump->Energy.CurrentEffic)); } -} +} void tanklevels(Project *pr, long tstep) @@ -1131,6 +1135,5 @@ void resetpumpflow(Project *pr, int i) Network *net = &pr->network; Spump *pump = &net->Pump[findpump(net, i)]; if (pump->Ptype == CONST_HP) - pr->hydraul.LinkFlow[i] = pump->Q0; + pr->hydraul.LinkFlow[i] = pump->Q0; } - diff --git a/src/report.c b/src/report.c index 2922332..ae675da 100644 --- a/src/report.c +++ b/src/report.c @@ -885,14 +885,14 @@ void writeline(Project *pr, char *s) **-------------------------------------------------------------- */ { + Report *rpt = &pr->report; + if (pr->report.reportCallback != NULL) { pr->report.reportCallback(pr->report.reportCallbackUserData, pr, s); return; } - Report *rpt = &pr->report; - if (rpt->RptFile == NULL) return; if (rpt->Rptflag) {