diff --git a/include/epanet2.h b/include/epanet2.h index 0275f9c..80b0e57 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -1,26 +1,20 @@ /** @file epanet2.h @see http://github.com/openwateranalytics/epanet - */ /* - ******************************************************************* - - EPANET2.H - Prototypes for EPANET Functions Exported to DLL Toolkit - - VERSION: 2.00 - DATE: 5/8/00 - 10/25/00 - 3/1/01 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) - AUTHORS: L. Rossman - US EPA - NRMRL - OpenWaterAnalytics members: see git stats for contributors - - ******************************************************************* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: epanet2.h + Description: symbolic constants and function declarations for the EPANET API + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ - #ifndef EPANET2_H #define EPANET2_H @@ -67,144 +61,152 @@ // --- Define the EPANET toolkit constants -#define EN_MAXID 31 /**< Max. # characters in ID name */ -#define EN_MAXMSG 255 /**< Max. # characters in message text */ +#define EN_MAXID 31 //!< Max. # characters in ID name +#define EN_MAXMSG 255 //!< Max. # characters in message text /// Node property codes typedef enum { - EN_ELEVATION = 0, /**< Node Elevation */ - EN_BASEDEMAND = 1, /**< Node Base Demand, from last demand category */ - EN_PATTERN = 2, /**< Node Demand Pattern */ - EN_EMITTER = 3, /**< Node Emitter Coefficient */ - EN_INITQUAL = 4, /**< Node initial quality */ - EN_SOURCEQUAL = 5, /**< Node source quality */ - EN_SOURCEPAT = 6, /**< Node source pattern index */ - EN_SOURCETYPE = 7, /**< Node source type */ - EN_TANKLEVEL = 8, /**< Tank Level */ - EN_DEMAND = 9, /**< Node current simulated demand */ - EN_HEAD = 10, /**< Node Head value */ - EN_PRESSURE = 11, /**< Node pressure value */ - EN_QUALITY = 12, /**< Node quality value */ - EN_SOURCEMASS = 13, /**< Node source mass value */ - EN_INITVOLUME = 14, /**< Tank or Reservoir initial volume */ - EN_MIXMODEL = 15, /**< Tank mixing model */ - EN_MIXZONEVOL = 16, /**< Tank mixing zone volume */ - EN_TANKDIAM = 17, /**< Tank diameter */ - EN_MINVOLUME = 18, /**< Tank minimum volume */ - EN_VOLCURVE = 19, /**< Tank volume curve index */ - EN_MINLEVEL = 20, /**< Tank minimum level */ - EN_MAXLEVEL = 21, /**< Tank maximum level */ - EN_MIXFRACTION = 22, /**< Tank mixing fraction */ - EN_TANK_KBULK = 23, /**< Tank bulk decay coefficient */ - EN_TANKVOLUME = 24, /**< Tank current volume */ - EN_MAXVOLUME = 25 /**< Tank maximum volume */ + EN_ELEVATION = 0, //!< Elevation + EN_BASEDEMAND = 1, //!< Junction baseline demand, from last demand category + EN_PATTERN = 2, //!< Junction baseline demand pattern + EN_EMITTER = 3, //!< Junction emitter coefficient + EN_INITQUAL = 4, //!< Initial quality + EN_SOURCEQUAL = 5, //!< Quality source strength + EN_SOURCEPAT = 6, //!< Quality source pattern + EN_SOURCETYPE = 7, //!< Qualiy source type + EN_TANKLEVEL = 8, //!< Tank water level + EN_DEMAND = 9, //!< Current simulated demand + EN_HEAD = 10, //!< Current hydraulic head + EN_PRESSURE = 11, //!< Current pressure + EN_QUALITY = 12, //!< Current quality + EN_SOURCEMASS = 13, //!< Current source mass inflow + EN_INITVOLUME = 14, //!< Tank initial volume + EN_MIXMODEL = 15, //!< Tank mixing model + EN_MIXZONEVOL = 16, //!< Tank mixing zone volume + EN_TANKDIAM = 17, //!< Tank diameter + EN_MINVOLUME = 18, //!< Tank minimum volume + EN_VOLCURVE = 19, //!< Tank volume curve + EN_MINLEVEL = 20, //!< Tank minimum level + EN_MAXLEVEL = 21, //!< Tank maximum level + EN_MIXFRACTION = 22, //!< Tank mixing fraction + EN_TANK_KBULK = 23, //!< Tank bulk decay coefficient + EN_TANKVOLUME = 24, //!< Tank current volume + EN_MAXVOLUME = 25 //!< Tank maximum volume } EN_NodeProperty; /// Link property codes typedef enum { - EN_DIAMETER = 0, - EN_LENGTH = 1, - EN_ROUGHNESS = 2, - EN_MINORLOSS = 3, - EN_INITSTATUS = 4, - EN_INITSETTING = 5, - EN_KBULK = 6, - EN_KWALL = 7, - EN_FLOW = 8, - EN_VELOCITY = 9, - EN_HEADLOSS = 10, - EN_STATUS = 11, - EN_SETTING = 12, - EN_ENERGY = 13, - EN_LINKQUAL = 14, - EN_LINKPATTERN = 15, - EN_EFFICIENCY = 16, - EN_HEADCURVE = 17, - EN_EFFICIENCYCURVE = 18, - EN_PRICEPATTERN = 19, - EN_STATE = 20, - EN_CONST_POWER = 21, - EN_SPEED = 22 + EN_DIAMETER = 0, //!< Pipe/valve diameter + EN_LENGTH = 1, //!> Pipe length + EN_ROUGHNESS = 2, //!> Pipe roughness coefficient + EN_MINORLOSS = 3, //!> Pipe/valve minor loss coefficient + EN_INITSTATUS = 4, //!> Initial status (e.g., OPEN/CLOSED) + EN_INITSETTING = 5, //!> Initial pump speed or valve setting + EN_KBULK = 6, //!> Bulk chemical reaction coefficient + EN_KWALL = 7, //!> Pipe wall chemical reaction coefficient + EN_FLOW = 8, //!> Current link flow rate + EN_VELOCITY = 9, //!> Current link flow velocity + EN_HEADLOSS = 10, //!> Current head loss across link + EN_STATUS = 11, //!> Current link status + EN_SETTING = 12, //!> Current link setting + EN_ENERGY = 13, //!> Current pump energy usage + EN_LINKQUAL = 14, //!> Current link quality + EN_LINKPATTERN = 15, //!> Pump speed time pattern + EN_EFFICIENCY = 16, //!> Current pump efficiency + EN_HEADCURVE = 17, //!> Pump head v. flow curve + EN_EFFICIENCYCURVE = 18, //!> Pump efficiency v. flow curve + EN_PRICEPATTERN = 19, //!> Pump energy price time pattern + EN_STATE = 20, //!> Current pump status + EN_CONST_POWER = 21, //!> Horsepower of constant horsepower pump + EN_SPEED = 22 //!> Current pump speed setting } EN_LinkProperty; /// Time parameter codes typedef enum { - EN_DURATION = 0, - EN_HYDSTEP = 1, - EN_QUALSTEP = 2, - EN_PATTERNSTEP = 3, - EN_PATTERNSTART = 4, - EN_REPORTSTEP = 5, - EN_REPORTSTART = 6, - EN_RULESTEP = 7, - EN_STATISTIC = 8, - EN_PERIODS = 9, - EN_STARTTIME = 10, - EN_HTIME = 11, - EN_QTIME = 12, - EN_HALTFLAG = 13, - EN_NEXTEVENT = 14, - EN_NEXTEVENTIDX = 15 + EN_DURATION = 0, //!> Total simulation duration + EN_HYDSTEP = 1, //!> Hydraulic time step + EN_QUALSTEP = 2, //!> Water quality time step + EN_PATTERNSTEP = 3, //!> Time pattern period + EN_PATTERNSTART = 4, //!> Time when time patterns begin + EN_REPORTSTEP = 5, //!> Reporting time step + EN_REPORTSTART = 6, //!> Time when reporting starts + EN_RULESTEP = 7, //!> Rule evaluation time step + EN_STATISTIC = 8, //!> Reporting statistic code + EN_PERIODS = 9, //!> Number of reporting time periods + EN_STARTTIME = 10, //!> Simulation starting time of day + EN_HTIME = 11, //!> Elapsed time of current hydraulic solution + EN_QTIME = 12, //!> Elapsed time of current quality solution + EN_HALTFLAG = 13, //!> Flag indicating if simulation halted + EN_NEXTEVENT = 14, //!> Next time until a tank becomes empty or full + EN_NEXTEVENTIDX = 15 //!> Index of next tank that becomes empty or full } EN_TimeProperty; +/// Analysis statistic codes typedef enum { - EN_ITERATIONS = 0, - EN_RELATIVEERROR = 1, - EN_MAXHEADERROR = 2, - EN_MAXFLOWCHANGE = 3, - EN_MASSBALANCE = 4 + EN_ITERATIONS = 0, //!< Number of hydraulic iterations + EN_RELATIVEERROR = 1, //!< Sum of all flow changes / total flow + EN_MAXHEADERROR = 2, //!< Largest head loss error for links + EN_MAXFLOWCHANGE = 3, //!< Largest flow change in links + EN_MASSBALANCE = 4 //!< Water quality mass balance ratio } EN_AnalysisStatistic; +/// Object count codes typedef enum { - EN_NODECOUNT = 0, /**< Number of Nodes (Juntions + Tanks + Reservoirs) */ - EN_TANKCOUNT = 1, /**< Number of Tanks and Reservoirs */ - EN_LINKCOUNT = 2, /**< Number of Links (Pipes + Pumps + Valves) */ - EN_PATCOUNT = 3, /**< Number of Time Patterns */ - EN_CURVECOUNT = 4, /**< Number of Curves */ - EN_CONTROLCOUNT = 5, /**< Number of Control Statements */ - EN_RULECOUNT = 6 /**< Number of Rule-based Control Statements */ + EN_NODECOUNT = 0, //!< Number of nodes (Juntions + Tanks + Reservoirs) + EN_TANKCOUNT = 1, //!< Number of tanks and Reservoirs + EN_LINKCOUNT = 2, //!< Number of links (Pipes + Pumps + Valves) + EN_PATCOUNT = 3, //!< Number of time patterns + EN_CURVECOUNT = 4, //!< Number of curves + EN_CONTROLCOUNT = 5, //!< Number of simple controls + EN_RULECOUNT = 6 //!< Number of rule-based controls } EN_CountType; +/// Node type codes typedef enum { - EN_JUNCTION = 0, - EN_RESERVOIR = 1, - EN_TANK = 2 + EN_JUNCTION = 0, //!< Junction node + EN_RESERVOIR = 1, //!< Reservoir node + EN_TANK = 2 //!< Storage tank node } EN_NodeType; +/// Link type codes typedef enum { - EN_CVPIPE = 0, - EN_PIPE = 1, - EN_PUMP = 2, - EN_PRV = 3, - EN_PSV = 4, - EN_PBV = 5, - EN_FCV = 6, - EN_TCV = 7, - EN_GPV = 8 + EN_CVPIPE = 0, //!< Pipe with check valve + EN_PIPE = 1, //!< Pipe + EN_PUMP = 2, //!< Pump + EN_PRV = 3, //!< Pressure reducing valve + EN_PSV = 4, //!< Pressure sustaining valve + EN_PBV = 5, //!< Pressure breaker valve + EN_FCV = 6, //!< Flow control valve + EN_TCV = 7, //!< Throttle control valve + EN_GPV = 8 //!< General purpose valve } EN_LinkType; +/// Water quality analysis types typedef enum { - EN_NONE = 0, /* Quality analysis types. */ - EN_CHEM = 1, - EN_AGE = 2, - EN_TRACE = 3 + EN_NONE = 0, //!< No quality analysis + EN_CHEM = 1, //!< Chemical fate and transport + EN_AGE = 2, //!< Water age analysis + EN_TRACE = 3 //!< Source tracing analysis } EN_QualityType; +/// Water quality source types typedef enum { - EN_CONCEN = 0, /* Source quality types. */ - EN_MASS = 1, - EN_SETPOINT = 2, - EN_FLOWPACED = 3 + EN_CONCEN = 0, //!< Concentration inflow source + EN_MASS = 1, //!< Mass inflow source + EN_SETPOINT = 2, //!< Concentration setpoint source + EN_FLOWPACED = 3 //!< Concentration flow paced source } EN_SourceType; -typedef enum { /* Head loss formula: */ - EN_HW = 0, /* Hazen-Williams */ - EN_DW = 1, /* Darcy-Weisbach */ - EN_CM = 2 /* Chezy-Manning */ +/// Head loss formulas +typedef enum { + EN_HW = 0, //!< Hazen-Williams + EN_DW = 1, //!< Darcy-Weisbach + EN_CM = 2 //!< Chezy-Manning } EN_HeadLossType; +/// Flow units types typedef enum { - EN_CFS = 0, /* Flow units types. */ + EN_CFS = 0, EN_GPM = 1, EN_MGD = 2, EN_IMGD = 3, @@ -216,76 +218,130 @@ typedef enum { EN_CMD = 9 } EN_FlowUnits; -typedef enum { /* Demand model types. */ - EN_DDA = 0, /**< Demand driven analysis */ - EN_PDA = 1 /**< Pressure driven analysis */ +/// Demand model types +typedef enum { + EN_DDA = 0, //!< Demand driven analysis + EN_PDA = 1 //!< Pressure driven analysis } EN_DemandModel; /// Simulation Option codes typedef enum { - EN_TRIALS = 0, - EN_ACCURACY = 1, - EN_TOLERANCE = 2, - EN_EMITEXPON = 3, - EN_DEMANDMULT = 4, - EN_HEADERROR = 5, - EN_FLOWCHANGE = 6, - EN_DEMANDDEFPAT = 7, - EN_HEADLOSSFORM = 8 + EN_TRIALS = 0, //!> Maximum hydraulic trials allowed + EN_ACCURACY = 1, //!> Hydraulic convergence accuracy + EN_TOLERANCE = 2, //!> Water quality tolerance + EN_EMITEXPON = 3, //!> Exponent for emitter head loss formula + EN_DEMANDMULT = 4, //!> Global demand multiplier + EN_HEADERROR = 5, //!> Maximum allowable head loss error + EN_FLOWCHANGE = 6, //!> Maximum allowable flow change + EN_DEMANDDEFPAT = 7, //!> Default demand time pattern + EN_HEADLOSSFORM = 8 //!> Head loss formula code } EN_Option; +/// Simple control types typedef enum { - EN_LOWLEVEL = 0, /* Control types. */ - EN_HILEVEL = 1, /* See ControlType */ - EN_TIMER = 2, /* in TYPES.H. */ + EN_LOWLEVEL = 0, + EN_HILEVEL = 1, + EN_TIMER = 2, EN_TIMEOFDAY = 3 } EN_ControlType; +/// Reporting statistic types typedef enum { - EN_AVERAGE = 1, /* Time statistic types. */ - EN_MINIMUM = 2, /* See TstatType in TYPES.H */ - EN_MAXIMUM = 3, - EN_RANGE = 4 + EN_AVERAGE = 1, //!> Report average value over simulation period + EN_MINIMUM = 2, //!> Report minimum value over simulation period + EN_MAXIMUM = 3, //!> Report maximum value over simulation period + EN_RANGE = 4 //!> Report maximum - minimum over simulation period } EN_StatisticType; +/// Tank mixing models typedef enum { - EN_MIX1 = 0, /* Tank mixing models */ - EN_MIX2 = 1, - EN_FIFO = 2, - EN_LIFO = 3 + EN_MIX1 = 0, //!< Complete mix model + EN_MIX2 = 1, //!< 2-compartment model + EN_FIFO = 2, //!< First in, first out model + EN_LIFO = 3 //!< Last in, first out model } EN_MixingModel; +/// Hydraulic initialization options typedef enum { - EN_NOSAVE = 0, - EN_SAVE = 1, - EN_INITFLOW = 10, - EN_SAVE_AND_INIT = 11 + EN_NOSAVE = 0, //!> Don't save hydraulics; don't re-initialize flows + EN_SAVE = 1, //!> Save hydraulics to file, don't re-initialize flows + EN_INITFLOW = 10, //!> Don't save hydraulics; re-initialize flows + EN_SAVE_AND_INIT = 11 //!> Save hydraulics; re-initialize flows } EN_SaveOption; +/// Pump curve types typedef enum { - EN_CONST_HP = 0, /* constant horsepower */ - EN_POWER_FUNC = 1, /* power function */ - EN_CUSTOM = 2, /* user-defined custom curve */ - EN_NOCURVE = 3 /* no curve */ + EN_CONST_HP = 0, //!< Constant horsepower + EN_POWER_FUNC = 1, //!< Power function + EN_CUSTOM = 2, //!< User-defined custom curve + EN_NOCURVE = 3 //!< No curve } EN_PumpType; +/// Data curve types typedef enum { - EN_V_CURVE = 0, /* volume curve */ - EN_P_CURVE = 1, /* pump curve */ - EN_E_CURVE = 2, /* efficiency curve */ - EN_H_CURVE = 3, /* head loss curve */ - EN_G_CURVE = 4 /* General\default curve */ + EN_V_CURVE = 0, //!< Tank volume curve + EN_P_CURVE = 1, //!< Pump characteristic curve + EN_E_CURVE = 2, //!< Pump efficiency curve + EN_H_CURVE = 3, //!< Valve head loss curve + EN_G_CURVE = 4 //!< General\default curve } EN_CurveType; +/// Deletion action types typedef enum { - EN_UNCONDITIONAL = 0, - EN_CONDITIONAL = 1 + EN_UNCONDITIONAL = 0, //!> Delete all controls that contain object + EN_CONDITIONAL = 1 //!> Cancel object deletion if contained in controls } EN_ActionCodeType; +/// Rule object codes typedef enum { - EN_NO_REPORT = 0, - EN_NORMAL_REPORT = 1, - EN_FULL_REPORT = 2 + EN_R_NODE = 6, + EN_R_LINK = 7, + EN_R_SYSTEM = 8 +} EN_RuleObject; + +/// Rule variable codes +typedef enum { + EN_R_DEMAND = 0, + EN_R_HEAD = 1, + EN_R_GRADE = 2, + EN_R_LEVEL = 3, + EN_R_PRESSURE = 4, + EN_R_FLOW = 5, + EN_R_STATUS = 6, + EN_R_SETTING = 7, + EN_R_POWER = 8, + EN_R_TIME = 9, + EN_R_CLOCKTIME = 10, + EN_R_FILLTIME = 11, + EN_R_DRAINTIME = 12 +} EN_RuleVariable; + +/// Rule operator types +typedef enum { + EN_R_EQ = 0, + EN_R_NE = 1, + EN_R_LE = 2, + EN_R_GE = 3, + EN_R_LT = 4, + EN_R_GT = 5, + EN_R_IS = 6, + EN_R_NOT = 7, + EN_R_BELOW = 8, + EN_R_ABOVE = 9 +} EN_RuleOperator; + +/// Rule status types +typedef enum { + EN_R_IS_OPEN = 1, + EN_R_IS_CLOSED = 2, + EN_R_IS_ACTIVE = 3 +} EN_RuleStatus; + +/// Status report types +typedef enum { + EN_NO_REPORT = 0, + EN_NORMAL_REPORT = 1, + EN_FULL_REPORT = 2 } EN_StatusReport; // --- Declare the EPANET toolkit functions @@ -293,24 +349,30 @@ typedef enum { extern "C" { #endif +/** + @brief The EPANET Project wrapper object +*/ +typedef struct Project *EN_Project; + +/******************************************************************** + + System Functions + +********************************************************************/ + /** - @brief The EPANET Project wrapper object - */ - typedef void *EN_ProjectHandle; - - /** - @brief runs a complete EPANET simulation - @param inpFile pointer to name of input file (must exist) - @param rptFile pointer to name of report file (to be created) - @param binOutFile pointer to name of binary output file (to be created) - @param callback a callback function that takes a character string (char *) as its only parameter. - @return error code + brief runs a complete EPANET simulation + param inpFile pointer to name of input file (must exist) + param rptFile pointer to name of report file (to be created) + param binOutFile pointer to name of binary output file (to be created) + param callback a callback function that takes a character string (char *) as its only parameter. + return error code - The callback function should reside in and be used by the calling - code to display the progress messages that EPANET generates - as it carries out its computations. If this feature is not - needed then the argument should be NULL. - */ + The callback function should reside in and be used by the calling + code to display the progress messages that EPANET generates + as it carries out its computations. If this feature is not + needed then the argument should be NULL. + */ int DLLEXPORT ENepanet(const char *inpFile, const char *rptFile, const char *binOutFile, void (*callback) (char *)); @@ -347,7 +409,13 @@ extern "C" { @return Error code */ int DLLEXPORT ENclose(); - + +/******************************************************************** + + Hydraulic Analysis Functions + +********************************************************************/ + /** @brief Solves the network hydraulics for all time periods @return Error code @@ -422,6 +490,12 @@ extern "C" { @return Error code */ int DLLEXPORT ENusehydfile(char *filename); + +/******************************************************************** + + Water Quality Analysis Functions + +********************************************************************/ /** @brief Solves for network water quality in all time periods @@ -480,6 +554,12 @@ extern "C" { */ int DLLEXPORT ENcloseQ(); +/******************************************************************** + + Reporting Functions + +********************************************************************/ + /** @brief Writes line of text to the report file. @param line Text string to write @@ -504,68 +584,257 @@ extern "C" { @return Error code */ int DLLEXPORT ENsetreport(char *reportFormat); - + /** - @brief Retrieves parameters that define a simple control - @param controlIndex Position of control in list of controls added to the project - @param[out] controlType Control type code (see EN_ControlType enumeration) - @param[out] linkIndex Index of controlled link - @param[out] setting Control setting on link - @param[out] nodeIndex Index of controlling node (0 for TIMER or TIMEOFDAY control) - @param[out] level Control level (tank level, junction pressure, or time (seconds)) - @return Error code - */ - int DLLEXPORT ENgetcontrol(int controlIndex, int *controlType, int *linkIndex, - EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); - + @brief Set the level of hydraulic status reporting. + @param code Status reporting code (see EN_StatusReport). + @return Error code. + */ + int DLLEXPORT ENsetstatusreport(int code); + /** - @brief Retrieves the number of components of a given type in the network. - @param code Component code (see EPANET2.H) - @param[out] count Number of components in network - @return Error code - */ + @brief Get the API version number. + @param[out] version The version of EPANET + @return Error code. + + The version number is to be interpreted with implied decimals, i.e., + "20100" == "2(.)01(.)00" + */ + int DLLEXPORT ENgetversion(int *version); + + /** + @brief Retrieves the number of components of a given type in the network. + @param code Component code (see EPANET2.H) + @param[out] count Number of components in network + @return Error code + */ int DLLEXPORT ENgetcount(int code, int *count); - + /** - @brief Gets value for an analysis option - @param code Option code (see EPANET2.H) - @param[out] value Option value - @return Error code - */ + @brief Get the text of an error code. + @param errcode The error code + @param[out] errmsg The error string represented by the code + @param maxLen The maximum number of characters to copy into the char pointer errmsg + @return Error code + */ + int DLLEXPORT ENgeterror(int errcode, char *errmsg, int maxLen); + + /** + @brief Get hydraulic simulation statistic + @param code The type of statistic to get + @param[out] value The value of the statistic + @return Error code + */ + int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE* value); + +/******************************************************************** + + Analysis Options Functions + +********************************************************************/ + + /** + @brief Gets value for an analysis option + @param code Option code (see EPANET2.H) + @param[out] value Option value + @return Error code + */ int DLLEXPORT ENgetoption(int code, EN_API_FLOAT_TYPE *value); - + /** - @brief Retrieves value of specific time parameter. - @param code Time parameter code - @param[out] value Value of time parameter. - @return Error code - */ - int DLLEXPORT ENgettimeparam(int code, long *value); - + @brief Set a value for an anlysis option. + @param code The code for the desired option. + @param v The desired value for the option specified. + @return Error code. + @see EN_Option + */ + int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v); + /** - @brief Retrieves the flow units code - @param[out] code Code of flow units in use - @return Error code - */ + @brief Retrieves the flow units code + @param[out] code Code of flow units in use + @return Error code + */ int DLLEXPORT ENgetflowunits(int *code); - + /** - @brief Sets the flow units - @param code Code of flow units to use - @return Error code - */ + @brief Sets the flow units + @param code Code of flow units to use + @return Error code + */ int DLLEXPORT ENsetflowunits(int code); /** - @brief Retrieves the type of demand model in use and its parameters - @param[out] type Type of demand model (EN_DDA or EN_PDA) - @param[out] pmin Pressure below which there is no demand - @param[out] preq Pressure required to deliver full demand - @param[out] pexp Pressure exponent in demand function - @return Error code + @brief Retrieves value of specific time parameter. + @param code Time parameter code + @param[out] value Value of time parameter. + @return Error code + */ + int DLLEXPORT ENgettimeparam(int code, long *value); + + /** + @brief Set the value for a time parameter. + @param code The code for the parameter to set. + @param value The desired value of the parameter. + @return Error code. + @see EN_TimeProperty + */ + int DLLEXPORT ENsettimeparam(int code, long value); + + /** + @brief Get information about the type of water quality analysis requested. + @param[out] qualcode Type of analysis to be made (see EN_QualityType). + @param[out] chemname Name of the quality constituent. + @param[out] chemunits Concentration units of the constituent. + @param[out] tracenode ID of node being traced (if applicable). + @return Error code. + */ + int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, + int *tracenode); + + /** + @brief Retrieve the type of quality analytis to be run. + @param[out] qualcode The quality analysis code number. + @param[out] tracenode The index of node being traced, if qualcode == trace + @return Error code + @see ENsetqualtype + */ + int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode); + + /** + @brief Set the type of quality analysis called for. + @param qualcode Type of analysis to be made (see EN_QualityType). + @param chemname Name of the quality constituent. + @param chemunits Concentration units of the constituent. + @param tracenode ID of node being traced (if applicable). + @return Error code. + + Note: "chemname" and "chemunits" only apply when "qualcode" is EN_CHEM. + "tracenode" only applies when 'qualcode" is EN_TRACE. + */ + int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, + char *tracenode); + +/******************************************************************** + + Node Functions + +********************************************************************/ + + /** + @brief Add a new node to the project. + @param id The name of the node to be added. + @param nodeType The type of node being added (see EN_NodeType) + @return Error code. + */ + int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType); + + /** + @brief Delete a node from the project. + @param index The index of the node to be deleted. + @param actionCode The action taken if any control contains the node and its links. + @return Error code. + + If 'actionCode' is EN_UNCONDITIONAL then the node, its incident links and all + simple and rule-based controls that contain them are deleted. If set to + EN_CONDITIONAL then the node is not deleted if it or its incident links appear + in any controls and an error code is returned. + + */ + int DLLEXPORT ENdeletenode(int index, int actionCode); + + /** + @brief Get index of node with specified ID + @param id The string ID of the node + @param[out] index The node's index (first node is index 1) + @return Error code + @see ENgetnodeid + */ + int DLLEXPORT ENgetnodeindex(char *id, int *index); + + /** + @brief Get the string ID of the specified node. + @param index The index of the node (first node is index 1) + @param[out] id The string ID of the specified node. + @return Error code + @see ENgetnodeindex + + The ID string must be sized to hold at least MAXID characters. + */ + int DLLEXPORT ENgetnodeid(int index, char *id); + + /** + @brief Change the ID name for a node. + @param index The index of a node. First node is index 1. + @param newid A string containing the node's new ID name. + @return Error code. + */ + int DLLEXPORT ENsetnodeid(int index, char *newid); + + /** + @brief Get the type of node with specified index. + @param index The index of a node (first node is index 1) + @param[out] code The type code for the node (see the EN_NodeType enumeration) + @return Error code + */ + int DLLEXPORT ENgetnodetype(int index, int *code); + + /** + @brief Get a property value for specified node + @param index The index of a node (first node is index 1). + @param code The property type code + @param[out] value The value of the node's property. + @return Error code + @see EN_NodeProperty + */ + int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value); + + /** + @brief Set a property value for a node. + @param index The index of a node. First node is index 1. + @param code The code for the proprty to set. + @param v The value to set for this node and property. + @return Error code. + @see EN_NodeProperty + */ + int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v); + + /** + @brief Get coordinates (x,y) for a node. + @param index The index of a node (first node is index 1). + @param[out] x X-value of node's coordinate + @param[out] y Y-value of node's coordinate + @return Error code + @see ENsetcoord + */ + int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + + /** + @brief Set coordinates (x,y) for a node. + @param index The index of a node (first node is index 1) + @param x X-value of node's coordinate + @param y Y-value of node's coordinate + @return Error code + @see ENgetcoord + */ + int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + +/******************************************************************** + + Nodal Demand Functions + +********************************************************************/ + + /** + @brief Retrieves the type of demand model in use and its parameters + @param[out] type Type of demand model (EN_DDA or EN_PDA) + @param[out] pmin Pressure below which there is no demand + @param[out] preq Pressure required to deliver full demand + @param[out] pexp Pressure exponent in demand function + @return Error code */ int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin, - EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); /** @brief Sets the type of demand model to use and its parameters @@ -576,284 +845,410 @@ extern "C" { @return Error code */ int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin, - EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); /** - @brief Retrieves the index of the time pattern with specified ID - @param id String ID of the time pattern - @param[out] index Index of the specified time pattern - @return Error code - @see ENgetpatternid - */ - int DLLEXPORT ENgetpatternindex(char *id, int *index); - - /** - @brief Retrieves ID of a time pattern with specific index. - @param index The index of a time pattern. - @param[out] id The string ID of the time pattern. - @return Error code - @see ENgetpatternindex - */ - int DLLEXPORT ENgetpatternid(int index, char *id); - - /** - @brief Retrieves the number of multipliers in a time pattern. - @param index The index of a time pattern. - @param[out] len The length of the time pattern. - @return Error code - */ - int DLLEXPORT ENgetpatternlen(int index, int *len); - - /** - @brief Retrive a multiplier from a pattern for a specific time period. - @param index The index of a time pattern - @param period The pattern time period. First time period is 1. - @param[out] value Pattern multiplier - @return Error code - */ - int DLLEXPORT ENgetpatternvalue(int index, int period, EN_API_FLOAT_TYPE *value); - - /** - @brief Retrieve the average multiplier value in a time pattern - @param index The index of a time pattern - @param[out] value The average of all of this time pattern's values - @return Error code - */ - int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value); - - /** - @brief Retrieve the type of quality analytis to be run. - @param[out] qualcode The quality analysis code number. - @param[out] tracenode The index of node being traced, if qualcode == trace - @return Error code - @see ENsetqualtype - */ - int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode); - - /** - @brief Get the text of an error code. - @param errcode The error code - @param[out] errmsg The error string represented by the code - @param maxLen The maximum number of characters to copy into the char pointer errmsg - @return Error code - */ - int DLLEXPORT ENgeterror(int errcode, char *errmsg, int maxLen); - - /** - @brief Get hydraulic simulation statistic - @param code The type of statistic to get - @param[out] value The value of the statistic - @return Error code - */ - int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE* value); - - /** - @brief Get index of node with specified ID - @param id The string ID of the node - @param[out] index The node's index (first node is index 1) - @return Error code - @see ENgetnodeid - */ - int DLLEXPORT ENgetnodeindex(char *id, int *index); - - /** - @brief Get the string ID of the specified node. - @param index The index of the node (first node is index 1) - @param[out] id The string ID of the specified node. - @return Error code - @see ENgetnodeindex - - The ID string must be sized to hold at least MAXID characters. - */ - int DLLEXPORT ENgetnodeid(int index, char *id); - - /** - @brief Get the type of node with specified index. - @param index The index of a node (first node is index 1) - @param[out] code The type code for the node (see the EN_NodeType enumeration) - @return Error code - */ - int DLLEXPORT ENgetnodetype(int index, int *code); - - /** - @brief Get a property value for specified node - @param index The index of a node (first node is index 1). - @param code The property type code - @param[out] value The value of the node's property. - @return Error code - @see EN_NodeProperty - */ - int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value); - - /** - @brief Get coordinates (x,y) for a node. - @param index The index of a node (first node is index 1). - @param[out] x X-value of node's coordinate - @param[out] y Y-value of node's coordinate - @return Error code - @see ENsetcoord - */ - int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - - /** - @brief Set coordinates (x,y) for a node. - @param index The index of a node (first node is index 1) - @param x X-value of node's coordinate - @param y Y-value of node's coordinate - @return Error code - @see ENgetcoord - */ - int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - - /** - @brief Get the number of demand categories for a node. - @param nodeIndex The index of a node (first node is index 1) - @param[out] numDemands The number of demand categories - @return Error code - */ + @brief Get the number of demand categories for a node. + @param nodeIndex The index of a node (first node is index 1) + @param[out] numDemands The number of demand categories + @return Error code + */ int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands); - - /** - @brief Get a node's base demand for a specified category. - @param nodeIndex The index of a node (first node is index 1) - @param demandIndex The index of the demand category (starting at 1) - @return Error code - */ - int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIndex, - EN_API_FLOAT_TYPE *baseDemand); - - /** - @brief Get the index of the demand pattern assigned to a node for a category index. - @param nodeIndex The index of a node (first node is index 1). - @param demandIndex The index of a category (first category is index 1). - @param[out] pattIndex The index of the pattern for this node and category. - @return Error code - */ - int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIndex, int *pattIndex); - - /** - @brief Get the index of a Link with specified ID. - @param id The string ID of a link. - @param[out] index The index of the named link (first link is index 1) - @return Error code - @see ENgetlinkid - */ - int DLLEXPORT ENgetlinkindex(char *id, int *index); - - /** - @brief Get the string ID of a link with specified index - @param index The index of a link (first link is index 1) - @param[out] id The ID of the link. - @return Error code - @see ENgetlinkindex - The ID string must be sized to hold at least MAXID characters. - */ - int DLLEXPORT ENgetlinkid(int index, char *id); - /** - @brief Get the link type code for a specified link - @param index The index of a link (first link is index 1) - @param[out] code The type code of the link (see the EN_LinkType enumeration) - @return Error code - @see EN_LinkType - */ + @brief Get a node's base demand for a specified category. + @param nodeIndex The index of a node (first node is index 1) + @param demandIndex The index of the demand category (starting at 1) + @return Error code + */ + int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIndex, + EN_API_FLOAT_TYPE *baseDemand); + + /** + @brief Set a node's base demand for a demand category. + @param nodeIndex The node's index. + @param demandIndex The index of one of the node's demand categories. + @param baseDemand The base demand for the selected category. + @return Error code. + @see ENgetbasedemand + */ + int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIndex, + EN_API_FLOAT_TYPE baseDemand); + + /** + @brief Get the index of the demand pattern assigned to a node for a category index. + @param nodeIndex The index of a node (first node is index 1). + @param demandIndex The index of a category (first category is index 1). + @param[out] pattIndex The index of the pattern for this node and category. + @return Error code + */ + int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIndex, int *pattIndex); + + /** + @brief Set the time pattern assigned to a node's demand category. + @param nodeIndex The node's index. + @param demandIndex The index of one of the node's demand categories. + @param pattIndex The index of a time pattern applied to this demand category. + @return Error code + */ + int DLLEXPORT ENsetdemandpattern(int nodeIndex, int demandIndex, int patIndex); + + /** + @brief Retrieve the name of a node's demand category. + @param nodeIndex The node's index. + @param demandIdx Index of the node's demand. + @param demandName[out] Name of the category the demand belongs to. + @return Error code. + */ + int DLLEXPORT ENgetdemandname(int nodeIndex, int demandIdx, char *demandName); + + /** + @brief Set the name of a node's demand category. + @param nodeIndex The node's index. + @param demandIdx Index of the node's demand. + @param demandName Name for the category the demand belongs to. + @return Error code. + */ + int DLLEXPORT ENsetdemandname(int nodeIndex, int demandIdx, char *demandName); + +/******************************************************************** + + Link Functions + +********************************************************************/ + + /** + @brief Add a new link to the project. + @param id The name of the link to be added. + @param linkType The type of link being added (see EN_LinkType) + @param fromNode The id of the link's starting node + @param toNode The id of the link's ending node + @return Error code. + */ + int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode, char *toNode); + + /** + @brief Delete a link from the project. + @param index The index of the link to be deleted. + @param ctrlsCode The action taken if any control contains the link. + @return Error code. + + If 'actionCode' is EN_UNCONDITIONAL then the link an all simple and rule-based + controls that contain it are deleted. If set to EN_CONDITIONAL then the link + is not deleted if it appears in any control and an error code is returned. + + */ + int DLLEXPORT ENdeletelink(int index, int actionCode); + + /** + @brief Get the index of a Link with specified ID. + @param id The string ID of a link. + @param[out] index The index of the named link (first link is index 1) + @return Error code + @see ENgetlinkid + */ + int DLLEXPORT ENgetlinkindex(char *id, int *index); + + /** + @brief Get the string ID of a link with specified index + @param index The index of a link (first link is index 1) + @param[out] id The ID of the link. + @return Error code + @see ENgetlinkindex + + The ID string must be sized to hold at least MAXID characters. + */ + int DLLEXPORT ENgetlinkid(int index, char *id); + + /** + @brief Change the ID name for a link. + @param index The index of a link. First link is index 1. + @param newid A string containing the link's new ID name. + @return Error code. + */ + int DLLEXPORT ENsetlinkid(int index, char *newid); + + /** + @brief Get the link type code for a specified link + @param index The index of a link (first link is index 1) + @param[out] code The type code of the link (see the EN_LinkType enumeration) + @return Error code + @see EN_LinkType + */ int DLLEXPORT ENgetlinktype(int index, EN_LinkType *code); /** - @brief Set the link type code for a specified link - @param[in,out] index The index of a link before [in] and after [out] the type change - @param code The new type code of the link (see EN_LinkType). - @param actionCode Action taken if any controls contain the link. - @return Error code - @see the EN_LinkType enumeration + @brief Set the link type code for a specified link + @param[in,out] index The index of a link before [in] and after [out] the type change + @param code The new type code of the link (see EN_LinkType). + @param actionCode Action taken if any controls contain the link. + @return Error code + @see the EN_LinkType enumeration - If 'actionCode' is EN_UNCONDITIONAL then all simple and rule-based controls that - contain the link are deleted when the link's type is changed. If set to - EN_CONDITIONAL then the type change is cancelled if the link appears in any - control and an error code is returned. - */ + If 'actionCode' is EN_UNCONDITIONAL then all simple and rule-based controls that + contain the link are deleted when the link's type is changed. If set to + EN_CONDITIONAL then the type change is cancelled if the link appears in any + control and an error code is returned. + */ int DLLEXPORT ENsetlinktype(int *index, EN_LinkType Code, int actionCode); - + /** - @brief Get the indexes of a link's start- and end-nodes. - @param index The index of a link (first link is index 1) - @param[out] node1 The index of the link's start node (first node is index 1). - @param[out] node2 The index of the link's end node (first node is index 1). - @return Error code - @see ENgetnodeid, ENgetlinkid - */ + @brief Get the indexes of a link's start- and end-nodes. + @param index The index of a link (first link is index 1) + @param[out] node1 The index of the link's start node (first node is index 1). + @param[out] node2 The index of the link's end node (first node is index 1). + @return Error code + @see ENgetnodeid, ENgetlinkid + */ int DLLEXPORT ENgetlinknodes(int index, int *node1, int *node2); - + /** - @brief Get a property value for specified link. - @param index The index of a node (first node is index 1). - @param code The parameter desired. - @param[out] value The value of the link's specified property. - @return Error code - @see ENgetnodevalue, EN_LinkProperty - */ + @brief Set the indexes of a link's start- and end-nodes. + @param index The index of a link (first link is index 1) + @param node1 The index of the link's start node (first node is index 1). + @param node2 The index of the link's end node (first node is index 1). + @return Error code + @see ENsetnodeid, ENsetlinkid + */ + int DLLEXPORT ENsetlinknodes(int index, int node1, int node2); + + /** + @brief Get a property value for specified link. + @param index The index of a node (first node is index 1). + @param code The parameter desired. + @param[out] value The value of the link's specified property. + @return Error code + @see ENgetnodevalue, EN_LinkProperty + */ int DLLEXPORT ENgetlinkvalue(int index, int code, EN_API_FLOAT_TYPE *value); - + /** - @brief Get a curve's properties. - @param curveIndex The index of a curve (first curve is index 1). - @param[out] id The curve's string ID. Client code must preallocate at least MAXID characters. - @param[out] nValues The number of values in the curve's (x,y) list. - @param[out] xValues The curve's x-values. Pointer must be freed by client. - @param[out] yValues The curve's y-values. Pointer must be freed by client. - @return Error code. - */ - int DLLEXPORT ENgetcurve(int curveIndex, char* id, int *nValues, - EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); - + @brief Set a property value for a link. + @param index The index of a link. First link is index 1. + @param code The code for the property to set. + @param v The value to set for this link and property. + @return Error code. + @see EN_LinkProperty + */ + int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v); + +/******************************************************************** + + Pump Functions + +********************************************************************/ + /** - @brief Retrieves the curve index for a specified pump index. - @param pumpIndex The index of a pump - @param[out] curveIndex The index of the curve used by the pump. - @return Error code. - */ - int DLLEXPORT ENgetheadcurveindex(int pumpIndex, int *curveIndex); - - /** - @brief Sets the curve id for a specified pump index. - @param pumpIndex The index of the pump - @param curveIndex The index of the curve used by the pump - @return Error code. - */ - int DLLEXPORT ENsetheadcurveindex(int pumpIndex, int curveIndex); - - /** - @brief Get the type of pump - @param linkIndex The index of the pump element - @param[out] outType The integer-typed pump curve type signifier (output parameter) - @return Error code - @see EN_PumpType - */ + @brief Get the type of pump + @param linkIndex The index of the pump element + @param[out] outType The integer-typed pump curve type signifier (output parameter) + @return Error code + @see EN_PumpType + */ int DLLEXPORT ENgetpumptype(int linkIndex, int *outType); /** - @brief Get the type of a curve - @param curveIndex The index of the curve element - @param[out] outType The integer-typed curve curve type signifier (output parameter) - @return Error code - @see EN_CurveType - */ - int DLLEXPORT ENgetcurvetype(int curveIndex, int *outType); - - /** - @brief Get the version number. - @param[out] version The version of EPANET - @return Error code. + @brief Retrieves the curve index for a specified pump index. + @param pumpIndex The index of a pump + @param[out] curveIndex The index of the curve used by the pump. + @return Error code. + */ + int DLLEXPORT ENgetheadcurveindex(int pumpIndex, int *curveIndex); - The version number is to be interpreted with implied decimals, i.e., - "20100" == "2(.)01(.)00" - */ - int DLLEXPORT ENgetversion(int *version); + /** + @brief Sets the curve id for a specified pump index. + @param pumpIndex The index of the pump + @param curveIndex The index of the curve used by the pump + @return Error code. + */ + int DLLEXPORT ENsetheadcurveindex(int pumpIndex, int curveIndex); + +/******************************************************************** + + Time Pattern Functions + +********************************************************************/ + + /** + @brief Add a new time pattern. + @param id The string ID of the pattern to add. + @return Error code. + @see ENgetpatternindex + */ + int DLLEXPORT ENaddpattern(char *id); + + /** + @brief Retrieves the index of the time pattern with specified ID + @param id String ID of the time pattern + @param[out] index Index of the specified time pattern + @return Error code + @see ENgetpatternid + */ + int DLLEXPORT ENgetpatternindex(char *id, int *index); + + /** + @brief Retrieves ID of a time pattern with specific index. + @param index The index of a time pattern. + @param[out] id The string ID of the time pattern. + @return Error code + @see ENgetpatternindex + */ + int DLLEXPORT ENgetpatternid(int index, char *id); + + /** + @brief Retrieves the number of multipliers in a time pattern. + @param index The index of a time pattern. + @param[out] len The length of the time pattern. + @return Error code + */ + int DLLEXPORT ENgetpatternlen(int index, int *len); + + /** + @brief Retrive a multiplier from a pattern for a specific time period. + @param index The index of a time pattern + @param period The pattern time period. First time period is 1. + @param[out] value Pattern multiplier + @return Error code + */ + int DLLEXPORT ENgetpatternvalue(int index, int period, EN_API_FLOAT_TYPE *value); + + /** + @brief Set the multiplier for a specific pattern at a specific period. + @param index The index of a pattern. First pattern is index 1. + @param period The period of the pattern to set. + @param value The value of the multiplier to set. + @return Error code. + */ + int DLLEXPORT ENsetpatternvalue(int index, int period, EN_API_FLOAT_TYPE value); + + /** + @brief Retrieve the average multiplier value in a time pattern + @param index The index of a time pattern + @param[out] value The average of all of this time pattern's values + @return Error code + */ + int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value); + + /** + @brief Set multipliers for a specific pattern + @param index The index of a pattern. First pattern is index 1. + @param f An array of multipliers + @param len The length of array f. + @return Error code. + @see ENgetpatternindex + */ + int DLLEXPORT ENsetpattern(int index, EN_API_FLOAT_TYPE *f, int len); + +/******************************************************************** + + Data Curve Functions + +********************************************************************/ + + /** + @brief Add a new curve to the project. + @param id The name of the curve to be added. + @return Error code. + @see ENgetcurveindex ENsetcurve + */ + int DLLEXPORT ENaddcurve(char *id); + + /** + @brief Retrieve the index of a curve given its name. + @param id The ID name of a curve. + @param[out] index The index of the named curve. + @return Error code. + @see ENgetcurveid + */ + int DLLEXPORT ENgetcurveindex(char *id, int *index); + + /** + @brief Retrieve the ID name of a curve given its index. + @param index The index of a curve. + @param[out] id The ID of the specified curve. + @return Error code. + @see ENsetcurveindex + + NOTE: 'id' must be sized to hold MAXID characters. + */ + int DLLEXPORT ENgetcurveid(int index, char *id); + + /** + @brief Retrieve the number of points in a curve. + @param index The index of a curve. + @param[out] len The number of data points assigned to the curve. + @return Error code. + @see ENgetcurvevalue + */ + int DLLEXPORT ENgetcurvelen(int index, int *len); + + /** + @brief Get the type of a curve + @param curveIndex The index of the curve element + @param[out] outType The integer-typed curve curve type signifier (output parameter) + @return Error code + @see EN_CurveType + */ + int DLLEXPORT ENgetcurvetype(int curveIndex, int *outType); + + /** + @brief Retrieve an x,y data point for a curve. + @param curveIndex The index of a curve. + @param pointIndex The index of a point in the curve. + @param[out] x The x-value of the specified point. + @param[out] y The y-value of the specified point. + @return Error code. + @see ENgetcurvelen ENsetcurvevalue + */ + int DLLEXPORT ENgetcurvevalue(int curveIndex, int pointIndex, + EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + + /** + @brief Set the x and y values for a curve's data point. + @param curveIndex The index of a curve. + @param pointIndex The index of a point in the curve. + @param x The x-value of the point. + @param y The y-value of the point. + @return Error code. + */ + int DLLEXPORT ENsetcurvevalue(int curveIndex, int pointIndex, + EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + + /** + @brief Get a curve's properties. + @param curveIndex The index of a curve (first curve is index 1). + @param[out] id The curve's string ID. Client code must preallocate at least MAXID characters. + @param[out] nValues The number of values in the curve's (x,y) list. + @param[out] xValues The curve's x-values. Pointer must be freed by client. + @param[out] yValues The curve's y-values. Pointer must be freed by client. + @return Error code. + + The calling program is responsible for making xValues and yValues large enough + to hold nValues number of data points. + */ + int DLLEXPORT ENgetcurve(int curveIndex, char* id, int *nValues, + EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); + + /** + @brief Set the x,y values for all points on a curve. + @param index The index of a curve. + @param x An array of x-values for the curve. + @param y An array of y-values for the curve. + @param len The length of the arrays for x and y. + @return Error code. + */ + int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y, int len); + +/******************************************************************** + + Simple Controls Functions + +********************************************************************/ /** @brief Add a new simple control to the project. - @param[out] cindex The index of the new control. First control is index 1. + @param[out] index The index of the new control. First control is index 1. @param ctype The type of control to add (see EN_ControlType). @param lindex The index of a link to control. @param setting The control setting applied to the link. @@ -861,18 +1256,31 @@ extern "C" { @param level control point (tank level, junction pressure, or time in seconds). @return Error code. */ - int DLLEXPORT ENaddcontrol(int *cindex, int ctype, int lindex, - EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); - - /** - @brief Delete an existing simple control - @param cindex The index of the control. First control is index 1. - @return Error code. - */ - int DLLEXPORT ENdeletecontrol(int cindex); + int DLLEXPORT ENaddcontrol(int *index, int ctype, int lindex, EN_API_FLOAT_TYPE setting, + int nindex, EN_API_FLOAT_TYPE level); /** - @brief Set the parameters of an existing simple control. + @brief Delete an existing simple control + @param index The index of the control. First control is index 1. + @return Error code. + */ + int DLLEXPORT ENdeletecontrol(int index); + + /** + @brief Retrieves properties that define a simple control + @param index Position of control in list of controls added to the project + @param[out] ctype Control type code (see EN_ControlType enumeration) + @param[out] lindex Index of controlled link + @param[out] setting Control setting on link + @param[out] nindex Index of controlling node (0 for TIMER or TIMEOFDAY control) + @param[out] level Control level (tank level, junction pressure, or time (seconds)) + @return Error code + */ + int DLLEXPORT ENgetcontrol(int index, int *ctype, int *lindex, EN_API_FLOAT_TYPE *setting, + int *nindex, EN_API_FLOAT_TYPE *level); + + /** + @brief Set the properties of an existing simple control. @param cindex The index of the control. First control is index 1. @param ctype The type of control to use (see EN_ControlType). @param lindex The index of a link to control. @@ -884,246 +1292,33 @@ extern "C" { int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + +/******************************************************************** + + Rule-Based Controls Functions + +********************************************************************/ + /** - @brief Change the ID name for a node. - @param index The index of a node. First node is index 1. - @param newid A string containing the node's new ID name. + @brief Add a new control rule to the project. + @param rule Text of the rule following the format used in an EPANET input file. @return Error code. */ - int DLLEXPORT ENsetnodeid(int index, char *newid); - - /** - @brief Set a property value for a node. - @param index The index of a node. First node is index 1. - @param code The code for the proprty to set. - @param v The value to set for this node and property. - @return Error code. - @see EN_NodeProperty - */ - int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT ENaddrule(char *rule); /** - @brief Change the ID name for a link. - @param index The index of a link. First link is index 1. - @param newid A string containing the link's new ID name. + @brief Delete a rule-based control. + @param index The rule's index. @return Error code. */ - int DLLEXPORT ENsetlinkid(int index, char *newid); - - /** - @brief Set the indexes of a link's start- and end-nodes. - @param index The index of a link (first link is index 1) - @param node1 The index of the link's start node (first node is index 1). - @param node2 The index of the link's end node (first node is index 1). - @return Error code - @see ENsetnodeid, ENsetlinkid - */ - int DLLEXPORT ENsetlinknodes(int index, int node1, int node2); - - /** - @brief Set a property value for a link. - @param index The index of a link. First link is index 1. - @param code The code for the property to set. - @param v The value to set for this link and property. - @return Error code. - @see EN_LinkProperty - */ - int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v); - - /** - @brief Add a new time pattern. - @param id The string ID of the pattern to add. - @return Error code. - @see ENgetpatternindex - */ - int DLLEXPORT ENaddpattern(char *id); - - /** - @brief Set multipliers for a specific pattern - @param index The index of a pattern. First pattern is index 1. - @param f An array of multipliers - @param len The length of array f. - @return Error code. - @see ENgetpatternindex - */ - int DLLEXPORT ENsetpattern(int index, EN_API_FLOAT_TYPE *f, int len); - - /** - @brief Set the multiplier for a specific pattern at a specific period. - @param index The index of a pattern. First pattern is index 1. - @param period The period of the pattern to set. - @param value The value of the multiplier to set. - @return Error code. - */ - int DLLEXPORT ENsetpatternvalue(int index, int period, EN_API_FLOAT_TYPE value); - - /** - @brief Set the value for a time parameter. - @param code The code for the parameter to set. - @param value The desired value of the parameter. - @return Error code. - @see EN_TimeProperty - */ - int DLLEXPORT ENsettimeparam(int code, long value); - - /** - @brief Set a value for an anlysis option. - @param code The code for the desired option. - @param v The desired value for the option specified. - @return Error code. - @see EN_Option - */ - int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v); - - /** - @brief Set the level of hydraulic status reporting. - @param code Status reporting code (see EN_StatusReport). - @return Error code. - */ - int DLLEXPORT ENsetstatusreport(int code); - - /** - @brief Set the type of quality analysis called for. - @param qualcode Type of analysis to be made (see EN_QualityType). - @param chemname Name of the quality constituent. - @param chemunits Concentration units of the constituent. - @param tracenode ID of node being traced (if applicable). - @return Error code. - - Note: "chemname" and "chemunits" only apply when "qualcode" is EN_CHEM. - "tracenode" only applies when 'qualcode" is EN_TRACE. - */ - int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, - char *tracenode); - - /** - @brief Get information about the type of water quality analysis requested. - @param[out] qualcode Type of analysis to be made (see EN_QualityType). - @param[out] chemname Name of the quality constituent. - @param[out] chemunits Concentration units of the constituent. - @param[out] tracenode ID of node being traced (if applicable). - @return Error code. - */ - int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, - int *tracenode); - - /** - @brief Set the name of a node's demand category. - @param nodeIndex The node's index. - @param demandIdx Index of the node's demand. - @param demandName Name for the category the demand belongs to. - @return Error code. - @see ENgetdemandname - */ - int DLLEXPORT ENsetdemandname(int nodeIndex, int demandIdx, char *demandName); - - /** - @brief Retrieve the name of a node's demand category. - @param nodeIndex The node's index. - @param demandIdx Index of the node's demand. - @param demandName[out] Name of the category the demand belongs to. - @return Error code. - @see ENsetdemandname - */ - int DLLEXPORT ENgetdemandname(int nodeIndex, int demandIdx, char *demandName); - - /** - @brief Set a node's base demand for a demand category. - @param nodeIndex The node's index. - @param demandIndex The index of one of the node's demand categories. - @param baseDemand The base demand for the selected category. - @return Error code. - @see ENgetbasedemand - */ - int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIndex, - EN_API_FLOAT_TYPE baseDemand); - - /** - @brief Set the time pattern assigned to a node's demand category. - @param nodeIndex The node's index. - @param demandIndex The index of one of the node's demand categories. - @param pattIndex The index of a time pattern applied to this demand category. - @return Error code - */ - int DLLEXPORT ENsetdemandpattern(int nodeIndex, int demandIndex, int patIndex); - - /** - @brief Retrieve the index of a curve given its name. - @param id The ID name of a curve. - @param[out] index The index of the named curve. - @return Error code. - @see ENgetcurveid - */ - int DLLEXPORT ENgetcurveindex(char *id, int *index); - - /** - @brief Retrieve the ID name of a curve given its index. - @param index The index of a curve. - @param[out] id The ID of the specified curve. - @return Error code. - @see ENsetcurveindex - - NOTE: 'id' must be sized to hold MAXID characters. - */ - int DLLEXPORT ENgetcurveid(int index, char *id); - - /** - @brief Retrieve the number of points in a curve. - @param index The index of a curve. - @param[out] len The number of data points assigned to the curve. - @return Error code. - @see ENgetcurvevalue - */ - int DLLEXPORT ENgetcurvelen(int index, int *len); - - /** - @brief Retrieve an x,y data point for a curve. - @param curveIndex The index of a curve. - @param pointIndex The index of a point in the curve. - @param[out] x The x-value of the specified point. - @param[out] y The y-value of the specified point. - @return Error code. - @see ENgetcurvelen ENsetcurvevalue - */ - int DLLEXPORT ENgetcurvevalue(int curveIndex, int pointIndex, - EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - - /** - @brief Set the x and y values for a curve's data point. - @param curveIndex The index of a curve. - @param pointIndex The index of a point in the curve. - @param x The x-value of the point. - @param y The y-value of the point. - @return Error code. - */ - int DLLEXPORT ENsetcurvevalue(int curveIndex, int pointIndex, - EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - - /** - @brief Set the x,y values for all points on a curve. - @param index The index of a curve. - @param x An array of x-values for the curve. - @param y An array of y-values for the curve. - @param len The length of the arrays for x and y. - @return Error code. - */ - int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, - int len); - - /** - @brief Add a new curve to the project. - @param id The name of the curve to be added. - @return Error code. - @see ENgetcurveindex ENsetcurve - */ - int DLLEXPORT ENaddcurve(char *id); + int DLLEXPORT ENdeleterule(int index); /** @brief Get summary information for a rule-based control. - @param index The control's index. - @param[out] nPremises Number of premises in the IF section of the control. - @param[out] nThenActions Number of THEN actions in the control. - @param nElseActions[out] Number of ELSE actions in the control. + @param index The rule's index. + @param[out] nPremises Number of premises in the rule's IF section. + @param[out] nThenActions Number of actions in the rule's THEN section. + @param nElseActions[out] Number of actions in the rule's ELSE section. @param priority[out] Rule's priority. @return Error code. */ @@ -1131,23 +1326,23 @@ extern "C" { int *nFalseActions, EN_API_FLOAT_TYPE *priority); /** - @brief Set the priority of a rule-based control. - @param index The control's index. - @param priority The priority assigned to the control. - @return Error code. - */ - int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority); + @brief Get the ID name of a rule-based control. + @param index The rule's index. + @param id[out] The rule's ID name. + @return Error code. + */ + int DLLEXPORT ENgetruleID(int index, char* id); /** - @brief Get the components of a premise in a rule-based control. - @param ruleIndex The control's index. + @brief Get the properties of a premise in a rule-based control. + @param ruleIndex The rule's index. @param premiseIndex The premise's index. - @param logop[out] Logical operator (IF/AND/OR) of the premise - @param object[out] Type of object (e.g. TANK) the premise is looking at. + @param logop[out] Logical operator (IF = 1, AND = 2, OR = 3) of the premise. + @param object[out] Type of object the premise is looking at (see EN_RuleObject). @param objIndex[out] Index of the object (e.g. the index of the tank). - @param variable[out] Index of the variable to be checked (e.g. LEVEL). - @param relop[out] Relationship operator (e.g. ABOVE) in the premise. - @param status[out] Status of the object being checked (e.g. CLOSED) + @param variable[out] Index of the variable to be checked (see EN_RuleVariable). + @param relop[out] Relationship operator in the premise (see EN_RuleOperator). + @param status[out] Status of the object being checked (see EN_RuleStatus). @param value[out] Setting of the variable being checked (e.g. 5.5) @return Error code. */ @@ -1156,15 +1351,15 @@ extern "C" { int *relop, int *status, EN_API_FLOAT_TYPE *value); /** - @brief Set the components of a premise in a rule-based control. - @param ruleIndex The control's index. + @brief Set the properties of a premise in a rule-based control. + @param ruleIndex The rule's index. @param premiseIndex The premise's index. - @param logop Logical operator (IF/AND/OR) of the premise - @param object Type of object (e.g. TANK) the premise is looking at. + @param logop Logical operator (IF = 1, AND = 2, OR = 3) of the premise. + @param object Type of object the premise is looking at (see EN_RuleObject). @param objIndex Index of the object (e.g. the index of the tank). - @param variable Index of the variable to be checked (e.g. LEVEL). - @param relop Relationship operator (e.g. ABOVE) in the premise. - @param status Status of the object being checked (e.g. CLOSED) + @param variable Index of the variable to be checked (see EN_RuleVariable). + @param relop Relationship operator in the premise (see EN_RuleOperator). + @param status Status of the object being checked (see EN_RuleStatus). @param value Setting of the variable being checked (e.g. 5.5) @return Error code. */ @@ -1174,7 +1369,7 @@ extern "C" { /** @brief Set the index of an object in a premise of a rule-based control. - @param ruleIndex The control's index. + @param ruleIndex The rule's index. @param premiseIndex The premise's index. @param objIndex The index of the premise's object (e.g. the index of the tank). @return Error code. @@ -1183,16 +1378,16 @@ extern "C" { /** @brief Set the status in a premise of a rule-based control. - @param ruleIndex The control's index. + @param ruleIndex The rule's index. @param premiseIndex The premise's index. - @param status The status of the object being checked (e.g. CLOSED) + @param status The status of the object being checked (see EN_RuleStatus) @return Error code. */ int DLLEXPORT ENsetpremisestatus(int ruleIndex, int premiseIndex, int status); /** @brief Set the value in a premise of a rule-based control. - @param ruleIndex The control's index. + @param ruleIndex The rule's index. @param premiseIndex The premise's index. @param value The value of the premise's variable being checked (e.g. 5.5) @return Error code. @@ -1201,11 +1396,11 @@ extern "C" { EN_API_FLOAT_TYPE value); /** - @brief Get the components of a THEN action in a rule-based control. - @param ruleIndex The control's index. + @brief Get the properties of a THEN action in a rule-based control. + @param ruleIndex The rule's index. @param actionIndex Index of the THEN action to retrieve. @param linkIndex[out] Index of the link in the action (e.g. index of Pump 1) - @param status[out] Status of the link (e.g. CLOSED) + @param status[out] Status of the link (see EN_RuleStatus) @param setting[out] Value of the link's setting (e.g. pump speed 0.9) @return Error code. */ @@ -1213,8 +1408,8 @@ extern "C" { int *status, EN_API_FLOAT_TYPE *setting); /** - @brief Set the components of a THEN action in a rule-based control. - @param ruleIndex The control's index. + @brief Set the properties of a THEN action in a rule-based control. + @param ruleIndex The rule's index. @param actionIndex Index of the THEN action to modify. @param linkIndex Index of the link in the action (e.g. index of Pump 1) @param status Status assigned to the link (e.g. CLOSED) @@ -1225,11 +1420,11 @@ extern "C" { int status, EN_API_FLOAT_TYPE setting); /** - @brief Get the components of an ELSE action in a rule-based control. - @param ruleIndex The control's index. + @brief Get the properties of an ELSE action in a rule-based control. + @param ruleIndex The rule's index. @param actionIndex Index of the ELSE action to retrieve. @param linkIndex[out] Index of the link in the action (e.g. index of Pump 1). - @param status[out] Status of the link (e.g. CLOSED). + @param status[out] Status of the link (see EN_RuleStatus). @param setting[out] Value of the link's setting (e.g. pump speed 0.9) @return Error code. */ @@ -1237,11 +1432,11 @@ extern "C" { int *status, EN_API_FLOAT_TYPE *setting); /** - @brief Set the components of an ELSE action in a rule-based control. - @param ruleIndex The control's index. + @brief Set the properties of an ELSE action in a rule-based control. + @param ruleIndex The rule's index. @param actionIndex Index of the ELSE action being modified. @param linkIndex Index of the link in the action (e.g. index of Pump 1) - @param status Status assigned to the link (e.g. CLOSED) + @param status Status assigned to the link (see EN_RuleStatus) @param setting Setting value assigned to the link (e.g. pump speed 0.9) @return Error code. */ @@ -1249,265 +1444,178 @@ extern "C" { int status, EN_API_FLOAT_TYPE setting); /** - @brief Get the ID name of a rule-based control. - @param index The rule's index. - @param id[out] The rule's ID name. - @return Error code. - */ - int DLLEXPORT ENgetruleID(int index, char* id); - - /** - @brief Delete a rule-based control. - @param index The rule's index. - @return Error code. - */ - int DLLEXPORT ENdeleterule(int index); - - /** - @brief Add a new node to the project. - @param id The name of the node to be added. - @param nodeType The type of node being added (see EN_NodeType) - @return Error code. - */ - int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType); + @brief Set the priority of a rule-based control. + @param index The rule's index. + @param priority The priority assigned to the rule. + @return Error code. + */ + int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority); - /** - @brief Add a new link to the project. - @param id The name of the link to be added. - @param linkType The type of link being added (see EN_LinkType) - @param fromNode The id of the link's starting node - @param toNode The id of the link's ending node - @return Error code. - */ - int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode, char *toNode); - - /** - @brief Add a new control rule to the project. - @param rule Text of the rule following the format used in an INP file. - @return Error code. - */ - int DLLEXPORT ENaddrule(char *rule); - - /** - @brief Delete a node from the project. - @param index The index of the node to be deleted. - @param actionCode The action taken if any control contains the node and its links. - @return Error code. - - If 'actionCode' is EN_UNCONDITIONAL then the node, its incident links and all - simple and rule-based controls that contain them are deleted. If set to - EN_CONDITIONAL then the node is not deleted if it or its incident links appear - in any controls and an error code is returned. - - */ - int DLLEXPORT ENdeletenode(int index, int actionCode); - - /** - @brief Delete a link from the project. - @param index The index of the link to be deleted. - @param ctrlsCode The action taken if any control contains the link. - @return Error code. - - If 'actionCode' is EN_UNCONDITIONAL then the link an all simple and rule-based - controls that contain it are deleted. If set to EN_CONDITIONAL then the link - is not deleted if it appears in any control and an error code is returned. - - */ - int DLLEXPORT ENdeletelink(int index, int actionCode); - - - /*************************************************** +/******************************************************************** - Threadsafe versions of all epanet functions + Threadsafe versions of all EPANET functions - ***************************************************/ - int DLLEXPORT EN_createproject(EN_ProjectHandle *ph); - int DLLEXPORT EN_deleteproject(EN_ProjectHandle *ph); +********************************************************************/ - int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *f1, - const char *f2, const char *f3, void (*pviewprog)(char *)); - - void DLLEXPORT EN_clearError(EN_ProjectHandle ph); - int DLLEXPORT EN_checkError(EN_ProjectHandle ph, char** msg_buffer); - - int DLLEXPORT EN_init(EN_ProjectHandle ph, const char *rptFile, - const char *binOutFile, EN_FlowUnits unitsType, - EN_HeadLossType headLossType); - - int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *inpFile, + int DLLEXPORT EN_createproject(EN_Project *ph); + int DLLEXPORT EN_deleteproject(EN_Project *ph); + int DLLEXPORT EN_runproject(EN_Project ph, const char *f1, const char *f2, const char *f3, + void (*pviewprog)(char *)); + int DLLEXPORT EN_init(EN_Project ph, const char *rptFile, const char *outFile, + EN_FlowUnits unitsType, EN_HeadLossType headLossType); + int DLLEXPORT EN_open(EN_Project ph, const char *inpFile, const char *rptFile, const char *binOutFile); + int DLLEXPORT EN_saveinpfile(EN_Project ph, const char *filename); + int DLLEXPORT EN_close(EN_Project ph); - int DLLEXPORT EN_saveinpfile(EN_ProjectHandle ph, const char *filename); + int DLLEXPORT EN_solveH(EN_Project ph); + int DLLEXPORT EN_saveH(EN_Project ph); + int DLLEXPORT EN_openH(EN_Project ph); + int DLLEXPORT EN_initH(EN_Project ph, int saveFlag); + int DLLEXPORT EN_runH(EN_Project ph, long *currentTime); + int DLLEXPORT EN_nextH(EN_Project ph, long *tStep); + int DLLEXPORT EN_closeH(EN_Project ph); + int DLLEXPORT EN_savehydfile(EN_Project ph, char *filename); + int DLLEXPORT EN_usehydfile(EN_Project ph, char *filename); - int DLLEXPORT EN_close(EN_ProjectHandle ph); - int DLLEXPORT EN_solveH(EN_ProjectHandle ph); - - int DLLEXPORT EN_saveH(EN_ProjectHandle ph); - int DLLEXPORT EN_openH(EN_ProjectHandle ph); - int DLLEXPORT EN_initH(EN_ProjectHandle ph, int saveFlag); - int DLLEXPORT EN_runH(EN_ProjectHandle ph, long *currentTime); - int DLLEXPORT EN_nextH(EN_ProjectHandle ph, long *tStep); - int DLLEXPORT EN_closeH(EN_ProjectHandle ph); - int DLLEXPORT EN_savehydfile(EN_ProjectHandle ph, char *filename); - int DLLEXPORT EN_usehydfile(EN_ProjectHandle ph, char *filename); - - int DLLEXPORT EN_solveQ(EN_ProjectHandle ph); - int DLLEXPORT EN_openQ(EN_ProjectHandle ph); - int DLLEXPORT EN_initQ(EN_ProjectHandle ph, int saveFlag); - int DLLEXPORT EN_runQ(EN_ProjectHandle ph, long *currentTime); - int DLLEXPORT EN_nextQ(EN_ProjectHandle ph, long *tStep); - int DLLEXPORT EN_stepQ(EN_ProjectHandle ph, long *timeLeft); - int DLLEXPORT EN_closeQ(EN_ProjectHandle ph); - int DLLEXPORT EN_writeline(EN_ProjectHandle ph, char *line); - - int DLLEXPORT EN_report(EN_ProjectHandle ph); - int DLLEXPORT EN_resetreport(EN_ProjectHandle ph); - int DLLEXPORT EN_setreport(EN_ProjectHandle ph, char *reportCommand); - - int DLLEXPORT EN_getcontrol(EN_ProjectHandle ph, int controlIndex, - int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, - int *nodeIndex, EN_API_FLOAT_TYPE *level); - int DLLEXPORT EN_getcount(EN_ProjectHandle ph, EN_CountType code, int *count); - int DLLEXPORT EN_getoption(EN_ProjectHandle ph, EN_Option opt, - EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_gettimeparam(EN_ProjectHandle ph, int code, long *value); - int DLLEXPORT EN_getflowunits(EN_ProjectHandle ph, int *code); - int DLLEXPORT EN_setflowunits(EN_ProjectHandle ph, int code); - int DLLEXPORT EN_getpatternindex(EN_ProjectHandle ph, char *id, int *index); - int DLLEXPORT EN_getpatternid(EN_ProjectHandle ph, int index, char *id); - int DLLEXPORT EN_getpatternlen(EN_ProjectHandle ph, int index, int *len); - int DLLEXPORT EN_getpatternvalue(EN_ProjectHandle ph, int index, int period, - EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, - EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getqualtype(EN_ProjectHandle ph, int *qualcode, int *tracenode); - int DLLEXPORT EN_geterror(int errcode, char *errmsg, int maxLen); - - int DLLEXPORT EN_getstatistic(EN_ProjectHandle ph, int code, - EN_API_FLOAT_TYPE* value); - int DLLEXPORT EN_getnodeindex(EN_ProjectHandle ph, char *id, int *index); - int DLLEXPORT EN_getnodeid(EN_ProjectHandle ph, int index, char *id); - int DLLEXPORT EN_getnodetype(EN_ProjectHandle ph, int index, int *code); - int DLLEXPORT EN_getnodevalue(EN_ProjectHandle ph, int index, int code, - EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, - EN_API_FLOAT_TYPE *y); - int DLLEXPORT EN_setcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE x, - EN_API_FLOAT_TYPE y); - int DLLEXPORT EN_getnumdemands(EN_ProjectHandle ph, int nodeIndex, - int *numDemands); - int DLLEXPORT EN_getbasedemand(EN_ProjectHandle ph, int nodeIndex, - int demandIndex, EN_API_FLOAT_TYPE *baseDemand); - int DLLEXPORT EN_getdemandpattern(EN_ProjectHandle ph, int nodeIndex, - int demandIndex, int *pattIndex); - int DLLEXPORT EN_getlinkindex(EN_ProjectHandle ph, char *id, int *index); - int DLLEXPORT EN_getlinkid(EN_ProjectHandle ph, int index, char *id); - int DLLEXPORT EN_getlinktype(EN_ProjectHandle ph, int index, EN_LinkType *code); - int DLLEXPORT EN_getlinknodes(EN_ProjectHandle ph, int index, int *node1, - int *node2); - int DLLEXPORT EN_getlinkvalue(EN_ProjectHandle ph, int index, - EN_LinkProperty code, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_getcurve(EN_ProjectHandle ph, int curveIndex, char* id, - int *nValues, EN_API_FLOAT_TYPE **xValues, - EN_API_FLOAT_TYPE **yValues); - int DLLEXPORT EN_getheadcurveindex(EN_ProjectHandle ph, int pumpIndex, - int *curveIndex); - int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int pumpIndex, - int curveIndex); - int DLLEXPORT EN_getpumptype(EN_ProjectHandle ph, int linkIndex, int *outType); - int DLLEXPORT EN_getcurvetype(EN_ProjectHandle ph, int curveIndex, int *outType); + int DLLEXPORT EN_solveQ(EN_Project ph); + int DLLEXPORT EN_openQ(EN_Project ph); + int DLLEXPORT EN_initQ(EN_Project ph, int saveFlag); + int DLLEXPORT EN_runQ(EN_Project ph, long *currentTime); + int DLLEXPORT EN_nextQ(EN_Project ph, long *tStep); + int DLLEXPORT EN_stepQ(EN_Project ph, long *timeLeft); + int DLLEXPORT EN_closeQ(EN_Project ph); + int DLLEXPORT EN_writeline(EN_Project ph, char *line); + int DLLEXPORT EN_report(EN_Project ph); + int DLLEXPORT EN_resetreport(EN_Project ph); + int DLLEXPORT EN_setreport(EN_Project ph, char *reportCommand); + int DLLEXPORT EN_setstatusreport(EN_Project ph, int code); int DLLEXPORT EN_getversion(int *version); + int DLLEXPORT EN_getcount(EN_Project ph, EN_CountType code, int *count); + int DLLEXPORT EN_geterror(int errcode, char *errmsg, int maxLen); + int DLLEXPORT EN_getstatistic(EN_Project ph, int code, EN_API_FLOAT_TYPE* value); - int DLLEXPORT EN_addcontrol(EN_ProjectHandle ph, int *cindex, int ctype, - int lindex, EN_API_FLOAT_TYPE setting, int nindex, - EN_API_FLOAT_TYPE level); - int DLLEXPORT EN_setcontrol(EN_ProjectHandle ph, int cindex, int ctype, - int lindex, EN_API_FLOAT_TYPE setting, int nindex, - EN_API_FLOAT_TYPE level); - int DLLEXPORT EN_setnodeid(EN_ProjectHandle ph, int index, char *newid); - int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, - EN_API_FLOAT_TYPE v); - int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid); - int DLLEXPORT EN_setlinknodes(EN_ProjectHandle ph, int index, int node1, - int node2); - int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type, - int actionCode); - int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, - EN_API_FLOAT_TYPE v); - int DLLEXPORT EN_addpattern(EN_ProjectHandle ph, char *id); - int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, - EN_API_FLOAT_TYPE *f, int len); - int DLLEXPORT EN_setpatternvalue(EN_ProjectHandle ph, int index, int period, - EN_API_FLOAT_TYPE value); - int DLLEXPORT EN_settimeparam(EN_ProjectHandle ph, int code, long value); - int DLLEXPORT EN_setoption(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE v); - int DLLEXPORT EN_setstatusreport(EN_ProjectHandle ph, int code); - int DLLEXPORT EN_setqualtype(EN_ProjectHandle ph, int qualcode, char *chemname, + int DLLEXPORT EN_getoption(EN_Project ph, EN_Option opt, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_setoption(EN_Project ph, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_getflowunits(EN_Project ph, int *code); + int DLLEXPORT EN_setflowunits(EN_Project ph, int code); + int DLLEXPORT EN_gettimeparam(EN_Project ph, int code, long *value); + int DLLEXPORT EN_settimeparam(EN_Project ph, int code, long value); + int DLLEXPORT EN_getqualinfo(EN_Project ph, int *qualcode, char *chemname, + char *chemunits, int *tracenode); + int DLLEXPORT EN_getqualtype(EN_Project ph, int *qualcode, int *tracenode); + int DLLEXPORT EN_setqualtype(EN_Project ph, int qualcode, char *chemname, char *chemunits, char *tracenode); - int DLLEXPORT EN_getdemandmodel(EN_ProjectHandle ph, int *type, EN_API_FLOAT_TYPE *pmin, - EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); - int DLLEXPORT EN_setdemandmodel(EN_ProjectHandle ph, int type, EN_API_FLOAT_TYPE pmin, - EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); + int DLLEXPORT EN_addnode(EN_Project ph, char *id, EN_NodeType nodeType); + int DLLEXPORT EN_deletenode(EN_Project ph, int index, int actionCode); + int DLLEXPORT EN_getnodeindex(EN_Project ph, char *id, int *index); + int DLLEXPORT EN_getnodeid(EN_Project ph, int index, char *id); + int DLLEXPORT EN_setnodeid(EN_Project ph, int index, char *newid); + int DLLEXPORT EN_getnodetype(EN_Project ph, int index, int *code); + int DLLEXPORT EN_getnodevalue(EN_Project ph, int index, int code, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_setnodevalue(EN_Project ph, int index, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_getcoord(EN_Project ph, int index, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y); + int DLLEXPORT EN_setcoord(EN_Project ph, int index, EN_API_FLOAT_TYPE x, + EN_API_FLOAT_TYPE y); - int DLLEXPORT EN_setdemandname(EN_ProjectHandle ph, int nodeIndex, - int demandIdx, char *demandName); - int DLLEXPORT EN_getdemandname(EN_ProjectHandle ph, int nodeIndex, - int demandIdx, char *demandName); - int DLLEXPORT EN_getqualinfo(EN_ProjectHandle ph, int *qualcode, - char *chemname, char *chemunits, int *tracenode); - int DLLEXPORT EN_setbasedemand(EN_ProjectHandle ph, int nodeIndex, + int DLLEXPORT EN_getdemandmodel(EN_Project ph, int *type, EN_API_FLOAT_TYPE *pmin, + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp); + int DLLEXPORT EN_setdemandmodel(EN_Project ph, int type, EN_API_FLOAT_TYPE pmin, + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp); + int DLLEXPORT EN_getnumdemands(EN_Project ph, int nodeIndex, int *numDemands); + int DLLEXPORT EN_getbasedemand(EN_Project ph, int nodeIndex, + int demandIndex, EN_API_FLOAT_TYPE *baseDemand); + int DLLEXPORT EN_setbasedemand(EN_Project ph, int nodeIndex, int demandIndex, EN_API_FLOAT_TYPE baseDemand); - int DLLEXPORT EN_setdemandpattern(EN_ProjectHandle ph, int nodeIndex, - int demandIndex, int patIndex); - int DLLEXPORT EN_getcurveindex(EN_ProjectHandle ph, char *id, int *index); - int DLLEXPORT EN_getcurveid(EN_ProjectHandle ph, int index, char *id); - int DLLEXPORT EN_getcurvelen(EN_ProjectHandle ph, int index, int *len); - int DLLEXPORT EN_getcurvevalue(EN_ProjectHandle ph, int curveIndex, - int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - int DLLEXPORT EN_setcurvevalue(EN_ProjectHandle ph, int curveIndex, - int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - int DLLEXPORT EN_setcurve(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, + int DLLEXPORT EN_getdemandpattern(EN_Project ph, int nodeIndex, int demandIndex, + int *pattIndex); + int DLLEXPORT EN_setdemandpattern(EN_Project ph, int nodeIndex, int demandIndex, + int patIndex); + int DLLEXPORT EN_getdemandname(EN_Project ph, int nodeIndex, int demandIdx, + char *demandName); + int DLLEXPORT EN_setdemandname(EN_Project ph, int nodeIndex, int demandIdx, + char *demandName); + + int DLLEXPORT EN_addlink(EN_Project ph, char *id, EN_LinkType linkType, + char *fromNode, char *toNode); + int DLLEXPORT EN_deletelink(EN_Project ph, int index, int actionCode); + int DLLEXPORT EN_getlinkindex(EN_Project ph, char *id, int *index); + int DLLEXPORT EN_getlinkid(EN_Project ph, int index, char *id); + int DLLEXPORT EN_setlinkid(EN_Project ph, int index, char *newid); + int DLLEXPORT EN_getlinktype(EN_Project ph, int index, EN_LinkType *code); + int DLLEXPORT EN_setlinktype(EN_Project ph, int *index, EN_LinkType type, int actionCode); + int DLLEXPORT EN_getlinknodes(EN_Project ph, int index, int *node1, int *node2); + int DLLEXPORT EN_setlinknodes(EN_Project ph, int index, int node1, int node2); + int DLLEXPORT EN_getlinkvalue(EN_Project ph, int index, EN_LinkProperty code, + EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_setlinkvalue(EN_Project ph, int index, int code, EN_API_FLOAT_TYPE v); + + int DLLEXPORT EN_getpumptype(EN_Project ph, int linkIndex, int *outType); + int DLLEXPORT EN_getheadcurveindex(EN_Project ph, int pumpIndex, int *curveIndex); + int DLLEXPORT EN_setheadcurveindex(EN_Project ph, int pumpIndex, int curveIndex); + + int DLLEXPORT EN_addpattern(EN_Project ph, char *id); + int DLLEXPORT EN_getpatternindex(EN_Project ph, char *id, int *index); + int DLLEXPORT EN_getpatternid(EN_Project ph, int index, char *id); + int DLLEXPORT EN_getpatternlen(EN_Project ph, int index, int *len); + int DLLEXPORT EN_getpatternvalue(EN_Project ph, int index, int period, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_setpatternvalue(EN_Project ph, int index, int period, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_getaveragepatternvalue(EN_Project ph, int index, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_setpattern(EN_Project ph, int index, EN_API_FLOAT_TYPE *f, int len); + + int DLLEXPORT EN_addcurve(EN_Project ph, char *id); + int DLLEXPORT EN_getcurveindex(EN_Project ph, char *id, int *index); + int DLLEXPORT EN_getcurveid(EN_Project ph, int index, char *id); + int DLLEXPORT EN_getcurvelen(EN_Project ph, int index, int *len); + int DLLEXPORT EN_getcurvetype(EN_Project ph, int curveIndex, int *outType); + int DLLEXPORT EN_getcurvevalue(EN_Project ph, int curveIndex, int pointIndex, + EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + int DLLEXPORT EN_setcurvevalue(EN_Project ph, int curveIndex, int pointIndex, + EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + int DLLEXPORT EN_getcurve(EN_Project ph, int curveIndex, char* id, + int *nValues, EN_API_FLOAT_TYPE **xValues, + EN_API_FLOAT_TYPE **yValues); + int DLLEXPORT EN_setcurve(EN_Project ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int len); - int DLLEXPORT EN_addcurve(EN_ProjectHandle ph, char *id); - int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, + + int DLLEXPORT EN_addcontrol(EN_Project ph, int *cindex, int ctype, int lindex, + EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + int DLLEXPORT EN_deletecontrol(EN_Project ph, int index); + int DLLEXPORT EN_getcontrol(EN_Project ph, int controlIndex, + int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, + int *nodeIndex, EN_API_FLOAT_TYPE *level); + int DLLEXPORT EN_setcontrol(EN_Project ph, int cindex, int ctype, int lindex, + EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + + int DLLEXPORT EN_addrule(EN_Project ph, char *rule); + int DLLEXPORT EN_deleterule(EN_Project ph, int index); + int DLLEXPORT EN_getrule(EN_Project ph, int index, int *nPremises, int *nThenActions, int *nElseActions, EN_API_FLOAT_TYPE *priority); - int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, - EN_API_FLOAT_TYPE priority); - int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int ruleIndex, int premiseIndex, + int DLLEXPORT EN_getruleID(EN_Project ph, int index, char* id); + int DLLEXPORT EN_getpremise(EN_Project ph, int ruleIndex, int premiseIndex, int *logop, int *object, int *objIndex, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value); - int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int ruleIndex, int premiseIndex, + int DLLEXPORT EN_setpremise(EN_Project ph, int ruleIndex, int premiseIndex, int logop, int object, int objIndex, int variable, int relop, int status, EN_API_FLOAT_TYPE value); - int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int ruleIndex, + int DLLEXPORT EN_setpremiseindex(EN_Project ph, int ruleIndex, int premiseIndex, int objIndex); - int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int ruleIndex, + int DLLEXPORT EN_setpremisestatus(EN_Project ph, int ruleIndex, int premiseIndex, int status); - int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int ruleIndex, + int DLLEXPORT EN_setpremisevalue(EN_Project ph, int ruleIndex, int premiseIndex, EN_API_FLOAT_TYPE value); - int DLLEXPORT EN_getthenaction(EN_ProjectHandle ph, int ruleIndex, - int actionIndex, int *linkIndex, int *status, - EN_API_FLOAT_TYPE *setting); - int DLLEXPORT EN_setthenaction(EN_ProjectHandle ph, int ruleIndex, - int actionIndex, int linkIndex, int status, - EN_API_FLOAT_TYPE setting); - int DLLEXPORT EN_getelseaction(EN_ProjectHandle ph, int ruleIndex, - int actionIndex, int *linkIndex, int *status, - EN_API_FLOAT_TYPE *setting); - int DLLEXPORT EN_setelseaction(EN_ProjectHandle ph, int ruleIndex, - int actionIndex, int linkIndex, int status, - EN_API_FLOAT_TYPE setting); - int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int index, char* id); - int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType); - int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, - char *fromNode, char *toNode); - int DLLEXPORT EN_addrule(EN_ProjectHandle ph, char *rule); - int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode); - int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index, int actionCode); - int DLLEXPORT EN_deletecontrol(EN_ProjectHandle ph, int index); - int DLLEXPORT EN_deleterule(EN_ProjectHandle ph, int index); + int DLLEXPORT EN_getthenaction(EN_Project ph, int ruleIndex, int actionIndex, + int *linkIndex, int *status, EN_API_FLOAT_TYPE *setting); + int DLLEXPORT EN_setthenaction(EN_Project ph, int ruleIndex, int actionIndex, + int linkIndex, int status, EN_API_FLOAT_TYPE setting); + int DLLEXPORT EN_getelseaction(EN_Project ph, int ruleIndex, int actionIndex, + int *linkIndex, int *status, EN_API_FLOAT_TYPE *setting); + int DLLEXPORT EN_setelseaction(EN_Project ph, int ruleIndex, int actionIndex, + int linkIndex, int status, EN_API_FLOAT_TYPE setting); + int DLLEXPORT EN_setrulepriority(EN_Project ph, int index, EN_API_FLOAT_TYPE priority); #if defined(__cplusplus) } diff --git a/src/enumstxt.h b/src/enumstxt.h index 6688a1a..339dc40 100755 --- a/src/enumstxt.h +++ b/src/enumstxt.h @@ -1,14 +1,14 @@ /* -*********************************************************************** - -ENUMSTXT.H -- Text strings for enumerated data types in EPANET - -VERSION: 2.00 -DATE: 5/8/00 -AUTHOR: L. Rossman - US EPA - NRMRL - -********************************************************************** + ***************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: enumstxt.h + Description: text strings for enumerated data types + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ #ifndef ENUMSTXT_H diff --git a/src/epanet.c b/src/epanet.c index dbdae8b..8bd45e9 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1,127 +1,23 @@ /* -******************************************************************************* - -EPANET.C -- Hydraulic & Water Quality Simulator for Water Distribution Networks - -VERSION: 2.1 -AUTHORS: L. Rossman - US EPA - NRMRL - OpenWaterAnalytics members: see git stats for contributors - -EPANET performs extended period hydraulic and water quality analysis of -looped, pressurized piping networks. The program consists of the -following code modules: - - EPANET.C -- main module providing supervisory control - INPUT1.C -- controls processing of input data - INPUT2.C -- reads data from input file - INPUT3.C -- parses individual lines of input data - INPFILE.C -- saves modified input data to a text file - RULES.C -- implements rule-based control of piping system - HYDRAUL.C -- computes extended period hydraulic behavior - QUALITY.C -- tracks transport & fate of water quality - OUTPUT.C -- handles transfer of data to and from binary files - REPORT.C -- handles reporting of results to text file - SMATRIX.C -- sparse matrix linear equation solver routines - MEMPOOL.C -- memory allocation routines - HASH.C -- hash table routines - -The program can be compiled as either a stand-alone console application -or as a dynamic link library (DLL) of function calls depending on whether -the macro identifier 'DLL' is defined or not. - -See EPANET2.H for function prototypes of exported DLL functions -See FUNCS.H for prototypes of all other functions -See TYPES.H for declaration of global constants and data structures -See VARS.H for declaration of global variables -See TEXT.H for declaration of all string constants -See ENUMSTXT.H for assignment of string constants to enumerated types - -The following naming conventions are used in all modules of this program: -1. Names of exportable functions in the DLL begin with the "EN" prefix. -2. All other function names are lowercase. -3. Global variable names begin with an uppercase letter. -4. Local variable names are all lowercase. -5. Declared constants and enumerated values defined in TYPES.H are - all uppercase. -6. String constants defined in TEXT.H begin with a lower case character - followed by an underscore and then all uppercase characters (e.g. - t_HEADLOSS) - --------------------------------------------------------------------------- - -This is the main module of the EPANET program. It uses a series of -functions, all beginning with the letters EN, to control program behavior. -See the main() and ENepanet() functions below for the simplest example of -these. - -This module calls the following functions that reside in other modules: - RULES.C - initrules() - allocrules() - closerules() - INPUT1.C - getdata() - initreport() - INPUT2.C - netsize() - setreport() - HYDRAUL.C - openhyd() - inithyd() - runhyd() - nexthyd() - closehyd() - tankvolume() - getenergy() - setlinkstatus() - setlinksetting() - HYDCOEFFS - resistcoeff() - QUALITY.C - openqual() - initqual() - runqual() - nextqual() - stepqual() - closequal() - REPORT.C - writeline(pr, ) - writelogo() - writereport() - HASH.C - hashtable_create() - hashtable_find() - hashtable_free() - -The macro ERRCODE(x) is defined in TYPES.H. It says if the current -value of the error code variable (errcode) is not fatal (< 100) then -execute function x and set the error code equal to its return value. - -******************************************************************************* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: epanet.c + Description: implementation of the EPANET API functions + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ -/*** Need to define WINDOWS to use the getTmpName function ***/ -// --- define WINDOWS -#undef WINDOWS -#ifdef _WIN32 -#define WINDOWS -#endif -#ifdef __WIN32__ -#define WINDOWS -#endif -#ifdef WINDOWS -#include -#endif - -/************************************************************/ - #include #include #include #ifndef __APPLE__ #include #endif -#include +#include #include #include "epanet2.h" @@ -132,28 +28,41 @@ execute function x and set the error code equal to its return value. // This single global variable is used only when the library is called // in "legacy mode" with the 2.1-style API. -void *_defaultModel; +Project __defaultProject; +Project *_defaultProject = &__defaultProject; + +// Functions for creating and removing default temporary files +void createtmpfiles() +{ + getTmpName(_defaultProject->TmpHydFname); + getTmpName(_defaultProject->TmpOutFname); + getTmpName(_defaultProject->TmpStatFname); +} +void removetmpfiles() +{ + remove(_defaultProject->TmpHydFname); + remove(_defaultProject->TmpOutFname); + remove(_defaultProject->TmpStatFname); +} -// Local functions -void errorLookup(int errcode, char *errmsg, int len); -int isInControls(EN_Project *pr, int objType, int index); +/******************************************************************** + System Functions -/**************************************************************** - - LEGACY (v <= 2.1) API: uses global project variable - -*****************************************************************/ +********************************************************************/ +int DLLEXPORT ENepanet(const char *f1, const char *f2, const char *f3, + void (*pviewprog)(char *)) +{ /*------------------------------------------------------------------------ - ** Input: f1 = pointer to name of input file - ** f2 = pointer to name of report file - ** f3 = pointer to name of binary output file + ** Input: f1 = name of EPANET formatted input file + ** f2 = name of report file + ** f3 = name of binary output file ** pviewprog = see note below ** Output: none - ** Returns: error code - ** Purpose: runs a complete EPANET simulation + ** Returns: error code + ** Purpose: runs a complete EPANET simulation ** ** The pviewprog() argument is a pointer to a callback function ** that takes a character string (char *) as its only parameter. @@ -163,23 +72,14 @@ int isInControls(EN_Project *pr, int objType, int index); ** needed then the argument should be NULL. **------------------------------------------------------------------------- */ -int DLLEXPORT ENepanet(const char *f1, const char *f2, const char *f3, - void (*pviewprog)(char *)) -{ int errcode = 0; int warncode = 0; - EN_Project *p = NULL; - - // Create a default project - exit on failure - errcode = EN_createproject(&_defaultModel); - if (errcode < 0) return 101; // Run the project and record any warning - errcode = EN_runproject(_defaultModel, f1, f2, f3, pviewprog); + createtmpfiles(); + errcode = EN_runproject(_defaultProject, f1, f2, f3, pviewprog); if (errcode < 100) warncode = errcode; - - // Must delete the project even if run had errors - EN_deleteproject(&_defaultModel); + removetmpfiles(); // Return the warning code if the run had no errors if (warncode) errcode = MAX(errcode, warncode); @@ -189,662 +89,791 @@ int DLLEXPORT ENepanet(const char *f1, const char *f2, const char *f3, int DLLEXPORT ENinit(const char *f2, const char *f3, int UnitsType, int HeadlossFormula) { - // Create a default project - exit on failure int errcode = 0; - errcode = EN_createproject(&_defaultModel); - if (errcode < 0) return 101; - - // Initialize the project - errcode = EN_init(_defaultModel, f2, f3, UnitsType, HeadlossFormula); + createtmpfiles(); + errcode = EN_init(_defaultProject, f2, f3, UnitsType, HeadlossFormula); return errcode; } int DLLEXPORT ENopen(const char *f1, const char *f2, const char *f3) { - // Create a default project - exit on failure int errcode = 0; - errcode = EN_createproject(&_defaultModel); - if (errcode < 0) return 101; - - // Read in network data from an input file - errcode = EN_open(_defaultModel, f1, f2, f3); + createtmpfiles(); + errcode = EN_open(_defaultProject, f1, f2, f3); return errcode; } -int DLLEXPORT ENsaveinpfile(const char *filename) { - return EN_saveinpfile(_defaultModel, filename); +int DLLEXPORT ENsaveinpfile(const char *filename) +{ + return EN_saveinpfile(_defaultProject, filename); } int DLLEXPORT ENclose() { - EN_close(_defaultModel); - EN_deleteproject(&_defaultModel); + EN_close(_defaultProject); + removetmpfiles(); return 0; } -int DLLEXPORT ENsolveH() { return EN_solveH(_defaultModel); } +/******************************************************************** -int DLLEXPORT ENsaveH() { return EN_saveH(_defaultModel); } + Hydraulic Analysis Functions -int DLLEXPORT ENopenH() { return EN_openH(_defaultModel); } +********************************************************************/ -int DLLEXPORT ENinitH(int flag) { return EN_initH(_defaultModel, flag); } +int DLLEXPORT ENsolveH() { return EN_solveH(_defaultProject); } -int DLLEXPORT ENrunH(long *t) { return EN_runH(_defaultModel, t); } +int DLLEXPORT ENsaveH() { return EN_saveH(_defaultProject); } -int DLLEXPORT ENnextH(long *tstep) { return EN_nextH(_defaultModel, tstep); } +int DLLEXPORT ENopenH() { return EN_openH(_defaultProject); } -int DLLEXPORT ENcloseH() { return EN_closeH(_defaultModel); } +int DLLEXPORT ENinitH(int flag) { return EN_initH(_defaultProject, flag); } -int DLLEXPORT ENsavehydfile(char *filename) { - return EN_savehydfile(_defaultModel, filename); +int DLLEXPORT ENrunH(long *t) { return EN_runH(_defaultProject, t); } + +int DLLEXPORT ENnextH(long *tstep) { return EN_nextH(_defaultProject, tstep); } + +int DLLEXPORT ENcloseH() { return EN_closeH(_defaultProject); } + +int DLLEXPORT ENsavehydfile(char *filename) +{ + return EN_savehydfile(_defaultProject, filename); } -int DLLEXPORT ENusehydfile(char *filename) { - return EN_usehydfile(_defaultModel, filename); +int DLLEXPORT ENusehydfile(char *filename) +{ + return EN_usehydfile(_defaultProject, filename); } -int DLLEXPORT ENsolveQ() { return EN_solveQ(_defaultModel); } +/******************************************************************** -int DLLEXPORT ENopenQ() { return EN_openQ(_defaultModel); } + Water Quality Analysis Functions -int DLLEXPORT ENinitQ(int saveflag) { - return EN_initQ(_defaultModel, saveflag); +********************************************************************/ + +int DLLEXPORT ENsolveQ() { return EN_solveQ(_defaultProject); } + +int DLLEXPORT ENopenQ() { return EN_openQ(_defaultProject); } + +int DLLEXPORT ENinitQ(int saveflag) { return EN_initQ(_defaultProject, saveflag); } + +int DLLEXPORT ENrunQ(long *t) { return EN_runQ(_defaultProject, t); } + +int DLLEXPORT ENnextQ(long *tstep) { return EN_nextQ(_defaultProject, tstep); } + +int DLLEXPORT ENstepQ(long *tleft) { return EN_stepQ(_defaultProject, tleft); } + +int DLLEXPORT ENcloseQ() { return EN_closeQ(_defaultProject); } + +/******************************************************************** + + Reporting Functions + +********************************************************************/ + +int DLLEXPORT ENwriteline(char *line) { return EN_writeline(_defaultProject, line); } + +int DLLEXPORT ENreport() { return EN_report(_defaultProject); } + +int DLLEXPORT ENresetreport() { return EN_resetreport(_defaultProject); } + +int DLLEXPORT ENsetreport(char *s) { return EN_setreport(_defaultProject, s); } + +int DLLEXPORT ENsetstatusreport(int code) +{ + return EN_setstatusreport(_defaultProject, code); } -int DLLEXPORT ENrunQ(long *t) { return EN_runQ(_defaultModel, t); } - -int DLLEXPORT ENnextQ(long *tstep) { return EN_nextQ(_defaultModel, tstep); } - -int DLLEXPORT ENstepQ(long *tleft) { return EN_stepQ(_defaultModel, tleft); } - -int DLLEXPORT ENcloseQ() { return EN_closeQ(_defaultModel); } - -int DLLEXPORT ENwriteline(char *line) { - return EN_writeline(_defaultModel, line); -} - -int DLLEXPORT ENreport() { return EN_report(_defaultModel); } - -int DLLEXPORT ENresetreport() { return EN_resetreport(_defaultModel); } - -int DLLEXPORT ENsetreport(char *s) { return EN_setreport(_defaultModel, s); } - int DLLEXPORT ENgetversion(int *v) { return EN_getversion(v); } -int DLLEXPORT ENgetcontrol(int cindex, int *ctype, int *lindex, - EN_API_FLOAT_TYPE *setting, int *nindex, - EN_API_FLOAT_TYPE *level) { - return EN_getcontrol(_defaultModel, cindex, ctype, lindex, setting, nindex, - level); +int DLLEXPORT ENgetcount(int code, int *count) +{ + return EN_getcount(_defaultProject, (EN_CountType)code, count); } -int DLLEXPORT ENgetcount(int code, int *count) { - return EN_getcount(_defaultModel, (EN_CountType)code, count); +int DLLEXPORT ENgeterror(int errcode, char *errmsg, int n) +{ + return EN_geterror(errcode, errmsg, n); } -int DLLEXPORT ENgetoption(int code, EN_API_FLOAT_TYPE *value) { - return EN_getoption(_defaultModel, (EN_Option)code, value); +int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE *value) +{ + return EN_getstatistic(_defaultProject, code, value); } -int DLLEXPORT ENgettimeparam(int code, long *value) { - return EN_gettimeparam(_defaultModel, code, value); +/******************************************************************** + + Analysis Options Functions + +********************************************************************/ + +int DLLEXPORT ENgetoption(int code, EN_API_FLOAT_TYPE *value) +{ + return EN_getoption(_defaultProject, (EN_Option)code, value); } -int DLLEXPORT ENgetflowunits(int *code) { - return EN_getflowunits(_defaultModel, code); +int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v) +{ + return EN_setoption(_defaultProject, code, v); } -int DLLEXPORT ENsetflowunits(int code) { - return EN_setflowunits(_defaultModel, code); +int DLLEXPORT ENgetflowunits(int *code) { return EN_getflowunits(_defaultProject, code); } + +int DLLEXPORT ENsetflowunits(int code) { return EN_setflowunits(_defaultProject, code); } + +int DLLEXPORT ENgettimeparam(int code, long *value) +{ + return EN_gettimeparam(_defaultProject, code, value); } -int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin, - EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp) { - return EN_getdemandmodel(_defaultModel, type, pmin, preq, pexp); -} - -int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin, - EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp) { - return EN_setdemandmodel(_defaultModel, type, pmin, preq, pexp); -} - -int DLLEXPORT ENgetpatternindex(char *id, int *index) { - return EN_getpatternindex(_defaultModel, id, index); -} - -int DLLEXPORT ENgetpatternid(int index, char *id) { - return EN_getpatternid(_defaultModel, index, id); -} - -int DLLEXPORT ENgetpatternlen(int index, int *len) { - return EN_getpatternlen(_defaultModel, index, len); -} - -int DLLEXPORT ENgetpatternvalue(int index, int period, - EN_API_FLOAT_TYPE *value) { - return EN_getpatternvalue(_defaultModel, index, period, value); -} - -int DLLEXPORT ENgetcurveindex(char *id, int *index) { - return EN_getcurveindex(_defaultModel, id, index); -} - -int DLLEXPORT ENgetcurveid(int index, char *id) { - return EN_getcurveid(_defaultModel, index, id); -} - -int DLLEXPORT ENgetcurvelen(int index, int *len) { - return EN_getcurvelen(_defaultModel, index, len); -} - -int DLLEXPORT ENgetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE *x, - EN_API_FLOAT_TYPE *y) { - return EN_getcurvevalue(_defaultModel, index, pnt, x, y); -} - -int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode) { - return EN_getqualtype(_defaultModel, qualcode, tracenode); +int DLLEXPORT ENsettimeparam(int code, long value) +{ + return EN_settimeparam(_defaultProject, code, value); } int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, - int *tracenode) { - return EN_getqualinfo(_defaultModel, qualcode, chemname, chemunits, - tracenode); + int *tracenode) +{ + return EN_getqualinfo(_defaultProject, qualcode, chemname, chemunits, tracenode); } -int DLLEXPORT ENgeterror(int errcode, char *errmsg, int n) { - return EN_geterror(errcode, errmsg, n); -} - -int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE *value) { - return EN_getstatistic(_defaultModel, code, value); -} - -int DLLEXPORT ENgetnodeindex(char *id, int *index) { - return EN_getnodeindex(_defaultModel, id, index); -} - -int DLLEXPORT ENgetnodeid(int index, char *id) { - return EN_getnodeid(_defaultModel, index, id); -} - -int DLLEXPORT ENgetnodetype(int index, int *code) { - return EN_getnodetype(_defaultModel, index, code); -} - -int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x, - EN_API_FLOAT_TYPE *y) { - return EN_getcoord(_defaultModel, index, x, y); -} - -int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) { - return EN_setcoord(_defaultModel, index, x, y); -} - -int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value) { - return EN_getnodevalue(_defaultModel, index, code, value); -} - -int DLLEXPORT ENgetlinkindex(char *id, int *index) { - return EN_getlinkindex(_defaultModel, id, index); -} - -int DLLEXPORT ENgetlinkid(int index, char *id) { - return EN_getlinkid(_defaultModel, index, id); -} - -int DLLEXPORT ENgetlinktype(int index, EN_LinkType *code) { - return EN_getlinktype(_defaultModel, index, code); -} - -int DLLEXPORT ENgetlinknodes(int index, int *node1, int *node2) { - return EN_getlinknodes(_defaultModel, index, node1, node2); -} - -int DLLEXPORT ENgetlinkvalue(int index, int code, EN_API_FLOAT_TYPE *value) { - return EN_getlinkvalue(_defaultModel, index, (EN_LinkProperty)code, value); -} - -int DLLEXPORT ENgetcurve(int curveIndex, char *id, int *nValues, - EN_API_FLOAT_TYPE **xValues, - EN_API_FLOAT_TYPE **yValues) { - return EN_getcurve(_defaultModel, curveIndex, id, nValues, xValues, yValues); -} - -int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, - EN_API_FLOAT_TYPE setting, int nindex, - EN_API_FLOAT_TYPE level) { - return EN_setcontrol(_defaultModel, cindex, ctype, lindex, setting, nindex, - level); -} - -int DLLEXPORT ENaddcontrol(int *cindex, int ctype, int lindex, - EN_API_FLOAT_TYPE setting, int nindex, - EN_API_FLOAT_TYPE level) { - return EN_addcontrol(_defaultModel, cindex, ctype, lindex, setting, nindex, - level); -} - -int DLLEXPORT ENdeletecontrol(int cindex) { - return EN_deletecontrol(_defaultModel, cindex); -} - - -int DLLEXPORT ENsetnodeid(int index, char *newid) { - return EN_setnodeid(_defaultModel, index, newid); -} - -int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v) { - return EN_setnodevalue(_defaultModel, index, code, v); -} - -int DLLEXPORT ENsetlinkid(int index, char *newid) { - return EN_setlinkid(_defaultModel, index, newid); -} - -int DLLEXPORT ENsetlinknodes(int index, int node1, int node2) { - return EN_setlinknodes(_defaultModel, index, node1, node2); -} - -int DLLEXPORT ENsetlinktype(int *index, EN_LinkType type, int actionCode) { - return EN_setlinktype(_defaultModel, index, type, actionCode); -} - -int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) { - return EN_setlinkvalue(_defaultModel, index, code, v); -} - -int DLLEXPORT ENaddpattern(char *id) { - return EN_addpattern(_defaultModel, id); -} - -int DLLEXPORT ENsetpattern(int index, EN_API_FLOAT_TYPE *f, int n) { - return EN_setpattern(_defaultModel, index, f, n); -} - -int DLLEXPORT ENsetpatternvalue(int index, int period, - EN_API_FLOAT_TYPE value) { - return EN_setpatternvalue(_defaultModel, index, period, value); -} - -int DLLEXPORT ENaddcurve(char *id) { return EN_addcurve(_defaultModel, id); } - -int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, - int n) { - return EN_setcurve(_defaultModel, index, x, y, n); -} - -int DLLEXPORT ENsetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE x, - EN_API_FLOAT_TYPE y) { - return EN_setcurvevalue(_defaultModel, index, pnt, x, y); -} - -int DLLEXPORT ENsettimeparam(int code, long value) { - return EN_settimeparam(_defaultModel, code, value); -} - -int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v) { - return EN_setoption(_defaultModel, code, v); -} - -int DLLEXPORT ENsetstatusreport(int code) { - return EN_setstatusreport(_defaultModel, code); +int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode) +{ + return EN_getqualtype(_defaultProject, qualcode, tracenode); } int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, - char *tracenode) { - return EN_setqualtype(_defaultModel, qualcode, chemname, chemunits, - tracenode); + char *tracenode) +{ + return EN_setqualtype(_defaultProject, qualcode, chemname, chemunits, tracenode); } -int DLLEXPORT ENgetheadcurveindex(int index, int *curveindex) { - return EN_getheadcurveindex(_defaultModel, index, curveindex); +/******************************************************************** + + Node Functions + +********************************************************************/ + +int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType) +{ + return EN_addnode(_defaultProject, id, nodeType); } -int DLLEXPORT ENsetheadcurveindex(int index, int curveindex) { - return EN_setheadcurveindex(_defaultModel, index, curveindex); +int DLLEXPORT ENdeletenode(int index, int actionCode) +{ + return EN_deletenode(_defaultProject, index, actionCode); } -int DLLEXPORT ENgetpumptype(int index, int *type) { - return EN_getpumptype(_defaultModel, index, type); +int DLLEXPORT ENgetnodeindex(char *id, int *index) +{ + return EN_getnodeindex(_defaultProject, id, index); } -int DLLEXPORT ENgetcurvetype(int curveindex, int *type) { - return EN_getcurvetype(_defaultModel, curveindex, type); +int DLLEXPORT ENgetnodeid(int index, char *id) +{ + return EN_getnodeid(_defaultProject, index, id); } -int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands) { - return EN_getnumdemands(_defaultModel, nodeIndex, numDemands); +int DLLEXPORT ENsetnodeid(int index, char *newid) +{ + return EN_setnodeid(_defaultProject, index, newid); } -int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIdx, - EN_API_FLOAT_TYPE *baseDemand) { - return EN_getbasedemand(_defaultModel, nodeIndex, demandIdx, baseDemand); +int DLLEXPORT ENgetnodetype(int index, int *code) +{ + return EN_getnodetype(_defaultProject, index, code); } -int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIdx, - EN_API_FLOAT_TYPE baseDemand) { - return EN_setbasedemand(_defaultModel, nodeIndex, demandIdx, baseDemand); +int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value) +{ + return EN_getnodevalue(_defaultProject, index, code, value); } -int DLLEXPORT ENsetdemandpattern(int nodeIndex, int demandIdx, int patIndex) { - return EN_setdemandpattern(_defaultModel, nodeIndex, demandIdx, patIndex); +int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v) +{ + return EN_setnodevalue(_defaultProject, index, code, v); } -int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIdx, int *pattIdx) { - return EN_getdemandpattern(_defaultModel, nodeIndex, demandIdx, pattIdx); +int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) +{ + return EN_getcoord(_defaultProject, index, x, y); } -int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value) { - return EN_getaveragepatternvalue(_defaultModel, index, value); +int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) +{ + return EN_setcoord(_defaultProject, index, x, y); } -int DLLEXPORT ENgetdemandname(int nodeIndex, int demandIdx, - char *demandName) { - return EN_getdemandname(_defaultModel, nodeIndex, demandIdx, demandName); +/******************************************************************** + + Nodal Demand Functions + +********************************************************************/ + +int DLLEXPORT ENgetdemandmodel(int *type, EN_API_FLOAT_TYPE *pmin, + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp) +{ + return EN_getdemandmodel(_defaultProject, type, pmin, preq, pexp); } -int DLLEXPORT ENsetdemandname(int nodeIndex, int demandIdx, - char *demandName) { - return EN_setdemandname(_defaultModel, nodeIndex, demandIdx, demandName); +int DLLEXPORT ENsetdemandmodel(int type, EN_API_FLOAT_TYPE pmin, + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp) +{ + return EN_setdemandmodel(_defaultProject, type, pmin, preq, pexp); } -int DLLEXPORT ENgetrule(int index, int *nPremises, int *nThenActions, - int *nElseActions, EN_API_FLOAT_TYPE *priority) { - return EN_getrule(_defaultModel, index, nPremises, nThenActions, nElseActions, - priority); +int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands) +{ + return EN_getnumdemands(_defaultProject, nodeIndex, numDemands); } -int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority){ - return EN_setrulepriority(_defaultModel, index, priority); +int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE *baseDemand) +{ + return EN_getbasedemand(_defaultProject, nodeIndex, demandIdx, baseDemand); } -int DLLEXPORT ENgetpremise(int ruleIndex, int premiseIndex, int *logop, int *object, - int *objIndex, int *variable, int *relop, int *status, - EN_API_FLOAT_TYPE *value){ - return EN_getpremise(_defaultModel, ruleIndex, premiseIndex, logop, object, - objIndex, variable, relop, status, value); +int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand) +{ + return EN_setbasedemand(_defaultProject, nodeIndex, demandIdx, baseDemand); } -int DLLEXPORT ENsetpremise(int ruleIndex, int premiseIndex, int logop, int object, - int objIndex, int variable, int relop, int status, - EN_API_FLOAT_TYPE value){ - return EN_setpremise(_defaultModel, ruleIndex, premiseIndex, logop, object, - objIndex, variable, relop, status, value); +int DLLEXPORT ENsetdemandpattern(int nodeIndex, int demandIdx, int patIndex) +{ + return EN_setdemandpattern(_defaultProject, nodeIndex, demandIdx, patIndex); } -int DLLEXPORT ENsetpremiseindex(int ruleIndex, int premiseIndex, int objIndex){ - return EN_setpremiseindex(_defaultModel, ruleIndex, premiseIndex, objIndex); +int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIdx, int *pattIdx) +{ + return EN_getdemandpattern(_defaultProject, nodeIndex, demandIdx, pattIdx); } -int DLLEXPORT ENsetpremisestatus(int ruleIndex, int premiseIndex, int status){ - return EN_setpremisestatus(_defaultModel, ruleIndex, premiseIndex, status); +int DLLEXPORT ENgetdemandname(int nodeIndex, int demandIdx, char *demandName) +{ + return EN_getdemandname(_defaultProject, nodeIndex, demandIdx, demandName); } -int DLLEXPORT ENsetpremisevalue(int ruleIndex, int premiseIndex, EN_API_FLOAT_TYPE value){ - return EN_setpremisevalue(_defaultModel, ruleIndex, premiseIndex, value); +int DLLEXPORT ENsetdemandname(int nodeIndex, int demandIdx, char *demandName) +{ + return EN_setdemandname(_defaultProject, nodeIndex, demandIdx, demandName); +} + +/******************************************************************** + + Link Functions + +********************************************************************/ + +int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode, char *toNode) +{ + return EN_addlink(_defaultProject, id, linkType, fromNode, toNode); +} + +int DLLEXPORT ENdeletelink(int index, int actionCode) +{ + return EN_deletelink(_defaultProject, index, actionCode); +} + +int DLLEXPORT ENgetlinkindex(char *id, int *index) +{ + return EN_getlinkindex(_defaultProject, id, index); +} + +int DLLEXPORT ENgetlinkid(int index, char *id) +{ + return EN_getlinkid(_defaultProject, index, id); +} + +int DLLEXPORT ENsetlinkid(int index, char *newid) +{ + return EN_setlinkid(_defaultProject, index, newid); +} + +int DLLEXPORT ENgetlinktype(int index, EN_LinkType *code) +{ + return EN_getlinktype(_defaultProject, index, code); +} + +int DLLEXPORT ENsetlinktype(int *index, EN_LinkType type, int actionCode) +{ + return EN_setlinktype(_defaultProject, index, type, actionCode); +} + +int DLLEXPORT ENgetlinknodes(int index, int *node1, int *node2) +{ + return EN_getlinknodes(_defaultProject, index, node1, node2); +} + +int DLLEXPORT ENsetlinknodes(int index, int node1, int node2) +{ + return EN_setlinknodes(_defaultProject, index, node1, node2); +} + +int DLLEXPORT ENgetlinkvalue(int index, int code, EN_API_FLOAT_TYPE *value) +{ + return EN_getlinkvalue(_defaultProject, index, (EN_LinkProperty)code, value); +} + +int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) +{ + return EN_setlinkvalue(_defaultProject, index, code, v); +} + +/******************************************************************** + + Pump Functions + +********************************************************************/ + +int DLLEXPORT ENgetpumptype(int index, int *type) +{ + return EN_getpumptype(_defaultProject, index, type); +} + +int DLLEXPORT ENgetheadcurveindex(int index, int *curveindex) +{ + return EN_getheadcurveindex(_defaultProject, index, curveindex); +} + +int DLLEXPORT ENsetheadcurveindex(int index, int curveindex) +{ + return EN_setheadcurveindex(_defaultProject, index, curveindex); +} + +/******************************************************************** + + Time Pattern Functions + +********************************************************************/ + +int DLLEXPORT ENaddpattern(char *id) +{ + return EN_addpattern(_defaultProject, id); +} + +int DLLEXPORT ENgetpatternindex(char *id, int *index) +{ + return EN_getpatternindex(_defaultProject, id, index); +} + +int DLLEXPORT ENgetpatternid(int index, char *id) +{ + return EN_getpatternid(_defaultProject, index, id); +} + +int DLLEXPORT ENgetpatternlen(int index, int *len) +{ + return EN_getpatternlen(_defaultProject, index, len); +} + +int DLLEXPORT ENgetpatternvalue(int index, int period, EN_API_FLOAT_TYPE *value) +{ + return EN_getpatternvalue(_defaultProject, index, period, value); +} + +int DLLEXPORT ENsetpatternvalue(int index, int period, EN_API_FLOAT_TYPE value) +{ + return EN_setpatternvalue(_defaultProject, index, period, value); +} + +int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value) +{ + return EN_getaveragepatternvalue(_defaultProject, index, value); +} + +int DLLEXPORT ENsetpattern(int index, EN_API_FLOAT_TYPE *f, int n) +{ + return EN_setpattern(_defaultProject, index, f, n); +} + +/******************************************************************** + + Data Curve Functions + +********************************************************************/ + +int DLLEXPORT ENaddcurve(char *id) +{ + return EN_addcurve(_defaultProject, id); +} + +int DLLEXPORT ENgetcurveindex(char *id, int *index) +{ + return EN_getcurveindex(_defaultProject, id, index); +} + +int DLLEXPORT ENgetcurveid(int index, char *id) +{ + return EN_getcurveid(_defaultProject, index, id); +} + +int DLLEXPORT ENgetcurvelen(int index, int *len) +{ + return EN_getcurvelen(_defaultProject, index, len); +} + +int DLLEXPORT ENgetcurvetype(int curveindex, int *type) +{ + return EN_getcurvetype(_defaultProject, curveindex, type); +} + +int DLLEXPORT ENgetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y) +{ + return EN_getcurvevalue(_defaultProject, index, pnt, x, y); +} + +int DLLEXPORT ENsetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE x, + EN_API_FLOAT_TYPE y) +{ + return EN_setcurvevalue(_defaultProject, index, pnt, x, y); +} + +int DLLEXPORT ENgetcurve(int curveIndex, char *id, int *nValues, + EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues) +{ + return EN_getcurve(_defaultProject, curveIndex, id, nValues, xValues, yValues); +} + +int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int n) +{ + return EN_setcurve(_defaultProject, index, x, y, n); +} + +/******************************************************************** + + Simple Controls Functions + +********************************************************************/ + +int DLLEXPORT ENaddcontrol(int *cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, + int nindex, EN_API_FLOAT_TYPE level) +{ + return EN_addcontrol(_defaultProject, cindex, ctype, lindex, setting, nindex, level); +} + +int DLLEXPORT ENdeletecontrol(int cindex) +{ + return EN_deletecontrol(_defaultProject, cindex); +} + +int DLLEXPORT ENgetcontrol(int cindex, int *ctype, int *lindex, EN_API_FLOAT_TYPE *setting, + int *nindex, EN_API_FLOAT_TYPE *level) +{ + return EN_getcontrol(_defaultProject, cindex, ctype, lindex, setting, nindex, level); +} + +int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, + int nindex, EN_API_FLOAT_TYPE level) +{ + return EN_setcontrol(_defaultProject, cindex, ctype, lindex, setting, nindex, level); +} + +/******************************************************************** + + Rule-Based Controls Functions + +********************************************************************/ + +int DLLEXPORT ENaddrule(char *rule) +{ + return EN_addrule(_defaultProject, rule); +} + +int DLLEXPORT ENdeleterule(int index) +{ + return EN_deleterule(_defaultProject, index); +} + +int DLLEXPORT ENgetrule(int index, int *nPremises, int *nThenActions, int *nElseActions, + EN_API_FLOAT_TYPE *priority) +{ + return EN_getrule(_defaultProject, index, nPremises, nThenActions, nElseActions, priority); +} + +int DLLEXPORT ENgetruleID(int index, char* id) +{ + return EN_getruleID(_defaultProject, index, id); +} + +int DLLEXPORT ENgetpremise(int ruleIndex, int premiseIndex, int *logop, + int *object, int *objIndex, int *variable, + int *relop, int *status, EN_API_FLOAT_TYPE *value) +{ + return EN_getpremise(_defaultProject, ruleIndex, premiseIndex, logop, object, + objIndex, variable, relop, status, value); +} + +int DLLEXPORT ENsetpremise(int ruleIndex, int premiseIndex, int logop, + int object, int objIndex, int variable, int relop, + int status, EN_API_FLOAT_TYPE value) +{ + return EN_setpremise(_defaultProject, ruleIndex, premiseIndex, logop, object, + objIndex, variable, relop, status, value); +} + +int DLLEXPORT ENsetpremiseindex(int ruleIndex, int premiseIndex, int objIndex) +{ + return EN_setpremiseindex(_defaultProject, ruleIndex, premiseIndex, objIndex); +} + +int DLLEXPORT ENsetpremisestatus(int ruleIndex, int premiseIndex, int status) +{ + return EN_setpremisestatus(_defaultProject, ruleIndex, premiseIndex, status); +} + +int DLLEXPORT ENsetpremisevalue(int ruleIndex, int premiseIndex, EN_API_FLOAT_TYPE value) +{ + return EN_setpremisevalue(_defaultProject, ruleIndex, premiseIndex, value); } int DLLEXPORT ENgetthenaction(int ruleIndex, int actionIndex, int *linkIndex, - int *status, EN_API_FLOAT_TYPE *setting){ - return EN_getthenaction(_defaultModel, ruleIndex, actionIndex, linkIndex, status, setting); + int *status, EN_API_FLOAT_TYPE *setting) +{ + return EN_getthenaction(_defaultProject, ruleIndex, actionIndex, linkIndex, + status, setting); } int DLLEXPORT ENsetthenaction(int ruleIndex, int actionIndex, int linkIndex, - int status, EN_API_FLOAT_TYPE setting){ - return EN_setthenaction(_defaultModel, ruleIndex, actionIndex, linkIndex, status, setting); + int status, EN_API_FLOAT_TYPE setting) +{ + return EN_setthenaction(_defaultProject, ruleIndex, actionIndex, linkIndex, + status, setting); } int DLLEXPORT ENgetelseaction(int ruleIndex, int actionIndex, int *linkIndex, - int *status, EN_API_FLOAT_TYPE *setting){ - return EN_getelseaction(_defaultModel, ruleIndex, actionIndex, linkIndex, status, setting); + int *status, EN_API_FLOAT_TYPE *setting) +{ + return EN_getelseaction(_defaultProject, ruleIndex, actionIndex, linkIndex, + status, setting); } int DLLEXPORT ENsetelseaction(int ruleIndex, int actionIndex, int linkIndex, - int status, EN_API_FLOAT_TYPE setting){ - return EN_setelseaction(_defaultModel, ruleIndex, actionIndex, linkIndex, status, setting); + int status, EN_API_FLOAT_TYPE setting) +{ + return EN_setelseaction(_defaultProject, ruleIndex, actionIndex, linkIndex, + status, setting); } -int DLLEXPORT ENaddrule(char *rule) { - return EN_addrule(_defaultModel, rule); +int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority) +{ + return EN_setrulepriority(_defaultProject, index, priority); } -int DLLEXPORT ENgetruleID(int index, char* id){ - return EN_getruleID(_defaultModel, index, id); -} +/******************************************************************** -int DLLEXPORT ENdeleterule(int index) { - return EN_deleterule(_defaultModel, index); -} + System Functions -int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType) { - return EN_addnode(_defaultModel, id, nodeType); -} +********************************************************************/ -int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode, - char *toNode) { - return EN_addlink(_defaultModel, id, linkType, fromNode, toNode); -} - -int DLLEXPORT ENdeletelink(int index, int actionCode) { - return EN_deletelink(_defaultModel, index, actionCode); -} - -int DLLEXPORT ENdeletenode(int index, int actionCode) { - return EN_deletenode(_defaultModel, index, actionCode); -} - -/* ----------------------------------------------------------------- - Functions for opening & closing the EPANET system ----------------------------------------------------------------- +int DLLEXPORT EN_createproject(EN_Project *p) +/*---------------------------------------------------------------- +** Input: none +** Output: p = pointer to a new EPANET project +** Returns: error code +** Purpose: creates a new EPANET project +**---------------------------------------------------------------- */ - -/// allocate a project pointer -int DLLEXPORT EN_createproject(EN_ProjectHandle *ph) -// Note: No error handling available until project allocation { - int errorcode = 0; - EN_Project *project = calloc(1, sizeof(EN_Project)); - - if (project != NULL){ - *ph = project; - getTmpName(project->TmpHydFname); - getTmpName(project->TmpOutFname); - getTmpName(project->TmpStatFname); - } - else errorcode = -1; - return errorcode; -} - -int DLLEXPORT EN_deleteproject(EN_ProjectHandle *ph) -// Note: No error handling available after project deallocation -{ - int errorcode = 0; - EN_Project *p = (EN_Project*)(*ph); - - if (p == NULL) errorcode = -1; - else - { - if (p->Openflag) EN_close(*ph); - remove(p->TmpHydFname); - remove(p->TmpOutFname); - remove(p->TmpStatFname); - free(p); - *ph = NULL; - } + struct Project *project = (struct Project *)calloc(1, sizeof(struct Project)); + if (project == NULL) return -1; + getTmpName(project->TmpHydFname); + getTmpName(project->TmpOutFname); + getTmpName(project->TmpStatFname); + *p = project; return 0; } -int DLLEXPORT EN_runproject(EN_ProjectHandle ph, const char *f1, const char *f2, - const char *f3, void (*pviewprog)(char *)) +int DLLEXPORT EN_deleteproject(EN_Project *p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: deletes an EPANET project +**---------------------------------------------------------------- +*/ +{ + if (*p == NULL) return -1; + if ((*p)->Openflag) EN_close(*p); + remove((*p)->TmpHydFname); + remove((*p)->TmpOutFname); + remove((*p)->TmpStatFname); + free(*p); + *p = NULL; + return 0; +} + +int DLLEXPORT EN_runproject(EN_Project p, const char *f1, const char *f2, + const char *f3, void (*pviewprog)(char *)) +/*------------------------------------------------------------------------ +** Input: f1 = name of EPANET formatted input file +** f2 = name of report file +** f3 = name of binary output file +** pviewprog = see note below +** Output: none +** Returns: error code +** Purpose: runs a complete EPANET simulation +** +** The pviewprog() argument is a pointer to a callback function +** that takes a character string (char *) as its only parameter. +** The function would reside in and be used by the calling +** program to display the progress messages that EPANET generates +** as it carries out its computations. If this feature is not +** needed then the argument should be NULL. +**------------------------------------------------------------------------- +*/ { int errcode = 0; - EN_Project *p = NULL; - ERRCODE(EN_open(ph, f1, f2, f3)); - p = (EN_Project*)(ph); + // Read in project data from an input file + ERRCODE(EN_open(p, f1, f2, f3)); p->viewprog = pviewprog; - if (p->out_files.Hydflag != USE) { - ERRCODE(EN_solveH(ph)); + // Solve for system hydraulics + if (p->outfile.Hydflag != USE) + { + ERRCODE(EN_solveH(p)); } - ERRCODE(EN_solveQ(ph)); - ERRCODE(EN_report(ph)); + // Solve for system water quality + ERRCODE(EN_solveQ(p)); - EN_close(ph); + // Write a formatted output report + ERRCODE(EN_report(p)); + EN_close(p); + // Return any error or warning code if (p->Warnflag) errcode = MAX(errcode, p->Warnflag); return errcode; } -int DLLEXPORT EN_init(EN_ProjectHandle ph, const char *f2, const char *f3, +int DLLEXPORT EN_init(EN_Project p, const char *f2, const char *f3, EN_FlowUnits unitsType, EN_HeadLossType headLossType) /*---------------------------------------------------------------- - ** Input: - ** f2 = pointer to name of report file - ** f3 = pointer to name of binary output file - ** unitsType = flow units type - ** headLossType = type of head loss formula + ** Input: f2 = name of report file + ** f3 = name of binary output file + ** unitsType = type of flow units (see EN_FlowUnits) + ** headLossType = type of head loss formula (see EN_HeadLossType) ** Output: none ** Returns: error code ** Purpose: initializes an EPANET project that isn't opened with - ** an input file. + ** an input file **---------------------------------------------------------------- */ { int errcode = 0; - EN_Project *pr = (EN_Project*)ph; // Set system flags - pr->Openflag = TRUE; - pr->hydraulics.OpenHflag = FALSE; - pr->quality.OpenQflag = FALSE; - pr->save_options.SaveHflag = FALSE; - pr->save_options.SaveQflag = FALSE; - pr->Warnflag = FALSE; - pr->parser.Coordflag = TRUE; - pr->report.Messageflag = TRUE; - pr->report.Rptflag = 1; + p->Openflag = TRUE; + p->hydraul.OpenHflag = FALSE; + p->quality.OpenQflag = FALSE; + p->outfile.SaveHflag = FALSE; + p->outfile.SaveQflag = FALSE; + p->Warnflag = FALSE; + p->report.Messageflag = TRUE; + p->report.Rptflag = 1; // Open files - errcode = openfiles(pr, "", f2, f3); - + errcode = openfiles(p, "", f2, f3); + // Initialize memory used for project's data objects - initpointers(pr); - ERRCODE(netsize(pr)); - ERRCODE(allocdata(pr)); + initpointers(p); + ERRCODE(netsize(p)); + ERRCODE(allocdata(p)); if (errcode) return (errcode); // Set analysis options - setdefaults(pr); - pr->parser.Flowflag = unitsType; - pr->hydraulics.Formflag = headLossType; + setdefaults(p); + p->parser.Flowflag = unitsType; + p->hydraul.Formflag = headLossType; // Perform additional initializations - adjustdata(pr); - initreport(&pr->report); - initunits(pr); - inittanks(pr); - convertunits(pr); + adjustdata(p); + initreport(&p->report); + initunits(p); + inittanks(p); + convertunits(p); // Initialize the default demand pattern - pr->parser.MaxPats = 0; - getpatterns(pr); + p->parser.MaxPats = 0; + getpatterns(p); return errcode; } -int DLLEXPORT EN_open(EN_ProjectHandle ph, const char *f1, const char *f2, const char *f3) +int DLLEXPORT EN_open(EN_Project p, const char *f1, const char *f2, const char *f3) /*---------------------------------------------------------------- - ** Input: f1 = pointer to name of input file - ** f2 = pointer to name of report file - ** f3 = pointer to name of binary output file + ** Input: f1 = name of input file + ** f2 = name of report file + ** f3 = name of binary output file ** Output: none ** Returns: error code - ** Purpose: opens EPANET input file & reads in network data + ** Purpose: opens an EPANET input file & reads in network data **---------------------------------------------------------------- */ { int errcode = 0; -/*** Updated 9/7/00 ***/ -/* Reset math coprocessor */ -#ifdef DLL - _fpreset(); -#endif - - EN_Project *p = (EN_Project*)ph; - - /* Set system flags */ + // Set system flags p->Openflag = FALSE; - p->hydraulics.OpenHflag = FALSE; + p->hydraul.OpenHflag = FALSE; p->quality.OpenQflag = FALSE; - p->save_options.SaveHflag = FALSE; - p->save_options.SaveQflag = FALSE; + p->outfile.SaveHflag = FALSE; + p->outfile.SaveQflag = FALSE; p->Warnflag = FALSE; - p->parser.Coordflag = TRUE; - - /*** Updated 9/7/00 ***/ p->report.Messageflag = TRUE; p->report.Rptflag = 1; - /* Initialize global pointers to NULL. */ + // Initialize data arrays to NULL initpointers(p); - /* Open input & report files */ + // Open input & report files ERRCODE(openfiles(p, f1, f2, f3)); - if (errcode > 0) { + if (errcode > 0) + { errmsg(p, errcode); return errcode; } writelogo(p); - /* Find network size & allocate memory for data */ + // Allocate memory for project's data arrays writewin(p->viewprog, FMT100); ERRCODE(netsize(p)); ERRCODE(allocdata(p)); - /* Retrieve input data */ + // Read input data ERRCODE(getdata(p)); - /* Free temporary linked lists used for Patterns & Curves */ + // Free temporary linked lists used for Patterns & Curves freeTmplist(p->parser.Patlist); freeTmplist(p->parser.Curvelist); - /* If using previously saved hydraulics then open its file */ - if (p->out_files.Hydflag == USE) { - ERRCODE(openhydfile(p)); - } + // If using previously saved hydraulics file then open it + if (p->outfile.Hydflag == USE) ERRCODE(openhydfile(p)); - /* Write input summary to report file */ - if (!errcode) { - if (p->report.Summaryflag) { - writesummary(p); - } + // Write input summary to report file + if (!errcode) + { + if (p->report.Summaryflag) writesummary(p); writetime(p, FMT104); p->Openflag = TRUE; - } else - errmsg(p, errcode); + } + else errmsg(p, errcode); return errcode; } -int DLLEXPORT EN_saveinpfile(EN_ProjectHandle ph, const char *filename) +int DLLEXPORT EN_saveinpfile(EN_Project p, const char *filename) /*---------------------------------------------------------------- - ** Input: filename = name of INP file + ** Input: filename = name of file to which project is saved ** Output: none ** Returns: error code - ** Purpose: saves current data base to file + ** Purpose: saves project to an EPANET formatted file **---------------------------------------------------------------- */ { - EN_Project *p = (EN_Project*)ph; - if (!p->Openflag) return (102); + if (!p->Openflag) return 102; return saveinpfile(p, filename); } -int DLLEXPORT EN_close(EN_ProjectHandle ph) +int DLLEXPORT EN_close(EN_Project p) /*---------------------------------------------------------------- ** Input: none ** Output: none @@ -853,15 +882,14 @@ int DLLEXPORT EN_close(EN_ProjectHandle ph) **---------------------------------------------------------------- */ { - out_file_t *out; - EN_Project *p = (EN_Project*)ph; + Outfile *out; // Free all project data if (p->Openflag) writetime(p, FMT105); freedata(p); - + // Close output file - out = &p->out_files; + out = &p->outfile; if (out->TmpOutFile != out->OutFile) { if (out->TmpOutFile != NULL) fclose(out->TmpOutFile); @@ -896,20 +924,20 @@ int DLLEXPORT EN_close(EN_ProjectHandle ph) // Reset system flags p->Openflag = FALSE; - p->hydraulics.OpenHflag = FALSE; - p->save_options.SaveHflag = FALSE; + p->hydraul.OpenHflag = FALSE; + p->outfile.SaveHflag = FALSE; p->quality.OpenQflag = FALSE; - p->save_options.SaveQflag = FALSE; - return (0); + p->outfile.SaveQflag = FALSE; + return 0; } -/* - ---------------------------------------------------------------- - Functions for running a hydraulic analysis - ---------------------------------------------------------------- - */ +/******************************************************************** -int DLLEXPORT EN_solveH(EN_ProjectHandle ph) + Hydraulic Analysis Functions + + ********************************************************************/ + +int DLLEXPORT EN_solveH(EN_Project p) /*---------------------------------------------------------------- ** Input: none ** Output: none @@ -918,116 +946,97 @@ int DLLEXPORT EN_solveH(EN_ProjectHandle ph) **---------------------------------------------------------------- */ { - int errcode; - long t, tstep; + int errcode; + long t, tstep; - EN_Project *p = (EN_Project*)ph; - - /* Open hydraulics solver */ - errcode = EN_openH(ph); - if (!errcode) { - /* Initialize hydraulics */ - errcode = EN_initH(ph, EN_SAVE); - - /* Analyze each hydraulic period */ + // Open hydraulics solver + errcode = EN_openH(p); if (!errcode) - do { + { + // Initialize hydraulics + errcode = EN_initH(p, EN_SAVE); - /* Display progress message */ - sprintf(p->Msg, "%-10s", - clocktime(p->report.Atime, p->time_options.Htime)); - sprintf(p->Msg, FMT101, p->report.Atime); - writewin(p->viewprog, p->Msg); + // Analyze each hydraulic time period + if (!errcode) do + { + // Display progress message + sprintf(p->Msg, "%-10s", + clocktime(p->report.Atime, p->times.Htime)); + sprintf(p->Msg, FMT101, p->report.Atime); + writewin(p->viewprog, p->Msg); - /* Solve for hydraulics & advance to next time period */ - tstep = 0; - ERRCODE(EN_runH(ph, &t)); - ERRCODE(EN_nextH(ph, &tstep)); - } while (tstep > 0); - } + // Solve for hydraulics & advance to next time period + tstep = 0; + ERRCODE(EN_runH(p, &t)); + ERRCODE(EN_nextH(p, &tstep)); + } while (tstep > 0); + } - /* Close hydraulics solver */ - EN_closeH(ph); - errcode = MAX(errcode, p->Warnflag); - - return errcode; + // Close hydraulics solver + EN_closeH(p); + errcode = MAX(errcode, p->Warnflag); + return errcode; } -int DLLEXPORT EN_saveH(EN_ProjectHandle ph) +int DLLEXPORT EN_saveH(EN_Project p) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Returns: error code - ** Purpose: saves hydraulic results to binary file. + ** Purpose: saves hydraulic results to binary file ** - ** Must be called before ENreport() if no WQ simulation made. - ** Should not be called if ENsolveQ() will be used. + ** Must be called before EN_report() if no WQ simulation made. + ** Should not be called if EN_solveQ() will be used. **---------------------------------------------------------------- */ { - char tmpflag; - int errcode; + char tmpflag; + int errcode; - EN_Project *p = (EN_Project*)ph; + // Check if hydraulic results exist + if (!p->outfile.SaveHflag) return 104; - /* Check if hydraulic results exist */ - if (!p->save_options.SaveHflag) - return (104); + // Temporarily turn off WQ analysis + tmpflag = p->quality.Qualflag; + p->quality.Qualflag = NONE; - /* Temporarily turn off WQ analysis */ - tmpflag = p->quality.Qualflag; - p->quality.Qualflag = NONE; + // Call WQ solver to simply transfer results from Hydraulics file + // to Output file at fixed length reporting time intervals + errcode = EN_solveQ(p); - /* Call WQ solver to simply transfer results */ - /* from Hydraulics file to Output file at */ - /* fixed length reporting time intervals. */ - errcode = EN_solveQ(p); - - /* Restore WQ analysis option */ - p->quality.Qualflag = tmpflag; - if (errcode) { - errmsg(p, errcode); - } - return errcode; + // Restore WQ analysis option + p->quality.Qualflag = tmpflag; + if (errcode) errmsg(p, errcode); + return errcode; } -int DLLEXPORT EN_openH(EN_ProjectHandle ph) +int DLLEXPORT EN_openH(EN_Project p) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Returns: error code - ** Purpose: sets up data structures for hydraulic analysis + ** Purpose: opens EPANET's hydraulic solver **---------------------------------------------------------------- */ { - int errcode = 0; + int errcode = 0; - EN_Project *p = (EN_Project*)ph; + // Check that input data exists + p->hydraul.OpenHflag = FALSE; + p->outfile.SaveHflag = FALSE; + if (!p->Openflag) return 102; - /* Check that input data exists */ - p->hydraulics.OpenHflag = FALSE; - p->save_options.SaveHflag = FALSE; - if (!p->Openflag) { - return (102); - } + // Check that previously saved hydraulics file not in use + if (p->outfile.Hydflag == USE) return 107; - /* Check that previously saved hydraulics file not in use */ - if (p->out_files.Hydflag == USE) { - return (107); - } - - /* Open hydraulics solver */ - ERRCODE(openhyd(p)); - if (!errcode) - p->hydraulics.OpenHflag = TRUE; - else - errmsg(p, errcode); - - return errcode; + // Open hydraulics solver + ERRCODE(openhyd(p)); + if (!errcode) p->hydraul.OpenHflag = TRUE; + else errmsg(p, errcode); + return errcode; } -/*** Updated 3/1/01 ***/ -int DLLEXPORT EN_initH(EN_ProjectHandle ph, int flag) +int DLLEXPORT EN_initH(EN_Project p, int flag) /*---------------------------------------------------------------- ** Input: flag = 2-digit flag where 1st (left) digit indicates ** if link flows should be re-initialized (1) or @@ -1035,4289 +1044,1227 @@ int DLLEXPORT EN_initH(EN_ProjectHandle ph, int flag) ** results should be saved to file (1) or not (0) ** Output: none ** Returns: error code - ** Purpose: initializes hydraulic analysis + ** Purpose: initializes EPANET's hydraulic solver **---------------------------------------------------------------- */ { - int errcode = 0; - int sflag, fflag; + int errcode = 0; + int sflag, fflag; - EN_Project *p = (EN_Project*)ph; + // Reset status flags + p->outfile.SaveHflag = FALSE; + p->Warnflag = FALSE; - /* Reset status flags */ - p->save_options.SaveHflag = FALSE; - p->Warnflag = FALSE; + // Get values of save-to-file flag and reinitialize-flows flag + fflag = flag / EN_INITFLOW; + sflag = flag - fflag * EN_INITFLOW; - /* Get values of save-to-file flag and reinitialize-flows flag */ - fflag = flag / EN_INITFLOW; - sflag = flag - fflag * EN_INITFLOW; + // Check that hydraulics solver was opened + if (!p->hydraul.OpenHflag) return 103; - /* Check that hydraulics solver was opened */ - if (!p->hydraulics.OpenHflag) - return (103); - - /* Open hydraulics file */ - p->save_options.Saveflag = FALSE; - if (sflag > 0) { - errcode = openhydfile(p); - if (!errcode) - p->save_options.Saveflag = TRUE; - else { - errmsg(p, errcode); - return errcode; - } - } - - /* Initialize hydraulics */ - inithyd(p, fflag); - if (p->report.Statflag > 0) - writeheader(p, STATHDR, 0); - return errcode; -} - -int DLLEXPORT EN_runH(EN_ProjectHandle ph, long *t) { - int errcode; - - EN_Project *p = (EN_Project*)ph; - - *t = 0; - if (!p->hydraulics.OpenHflag) - return (103); - errcode = runhyd(p, t); - if (errcode) - errmsg(p, errcode); - return errcode; -} - -int DLLEXPORT EN_nextH(EN_ProjectHandle ph, long *tstep) { - int errcode; - - EN_Project *p = (EN_Project*)ph; - - *tstep = 0; - if (!p->hydraulics.OpenHflag) - return (103); - errcode = nexthyd(p, tstep); - if (errcode) - errmsg(p, errcode); - else if (p->save_options.Saveflag && *tstep == 0) - p->save_options.SaveHflag = TRUE; - return errcode; -} - -int DLLEXPORT EN_closeH(EN_ProjectHandle ph) -{ - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) { - return (102); - } - if (p->hydraulics.OpenHflag) { - closehyd(p); - } - p->hydraulics.OpenHflag = FALSE; - return (0); -} - -int DLLEXPORT EN_savehydfile(EN_ProjectHandle ph, char *filename) { - FILE *f; - int c; - FILE *HydFile; - - EN_Project *p = (EN_Project*)ph; - - /* Check that hydraulics results exist */ - if (p->out_files.HydFile == NULL || !p->save_options.SaveHflag) - return (104); - - /* Open file */ - if ((f = fopen(filename, "w+b")) == NULL) - return (305); - - /* Copy from HydFile to f */ - HydFile = p->out_files.HydFile; - fseek(HydFile, 0, SEEK_SET); - while ((c = fgetc(HydFile)) != EOF) { - fputc(c, f); - } - fclose(f); - return (0); -} - -int DLLEXPORT EN_usehydfile(EN_ProjectHandle ph, char *filename) { - int errcode; - - EN_Project *p = (EN_Project*)ph; - - /* Check that input data exists & hydraulics system closed */ - if (!p->Openflag) - return (102); - if (p->hydraulics.OpenHflag) - return (108); - - /* Try to open hydraulics file */ - strncpy(p->out_files.HydFname, filename, MAXFNAME); - p->out_files.Hydflag = USE; - p->save_options.SaveHflag = TRUE; - errcode = openhydfile(p); - - /* If error, then reset flags */ - if (errcode) { - strcpy(p->out_files.HydFname, ""); - p->out_files.Hydflag = SCRATCH; - p->save_options.SaveHflag = FALSE; - } - return errcode; -} - -/* - ---------------------------------------------------------------- - Functions for running a WQ analysis - ---------------------------------------------------------------- - */ - -int DLLEXPORT EN_solveQ(EN_ProjectHandle ph) { - int errcode; - long t, tstep; - - EN_Project *p = (EN_Project*)ph; - - /* Open WQ solver */ - errcode = EN_openQ(ph); - if (!errcode) { - /* Initialize WQ */ - errcode = EN_initQ(ph, EN_SAVE); - if (!p->quality.Qualflag) writewin(p->viewprog, FMT106); - - /* Analyze each hydraulic period */ - if (!errcode) - do { - - /* Display progress message */ - sprintf(p->Msg, "%-10s", - clocktime(p->report.Atime, p->time_options.Htime)); - if (p->quality.Qualflag) { - sprintf(p->Msg, FMT102, p->report.Atime); - writewin(p->viewprog, p->Msg); - } - - /* Retrieve current network solution & update WQ to next time period */ - tstep = 0; - ERRCODE(EN_runQ(ph, &t)); - ERRCODE(EN_nextQ(ph, &tstep)); - } while (tstep > 0); - } - - /* Close WQ solver */ - EN_closeQ(ph); - return errcode; -} - -int DLLEXPORT EN_openQ(EN_ProjectHandle ph) { - int errcode = 0; - - EN_Project *p = (EN_Project*)ph; - - /* Check that hydraulics results exist */ - p->quality.OpenQflag = FALSE; - p->save_options.SaveQflag = FALSE; - if (!p->Openflag) - return (102); - // !LT! todo - check for p->save_options.SaveHflag / set sequential/step mode - // if (!p->save_options.SaveHflag) return(104); - - /* Open WQ solver */ - ERRCODE(openqual(p)); - if (!errcode) - p->quality.OpenQflag = TRUE; - else - errmsg(p, errcode); - return errcode; -} - -int DLLEXPORT EN_initQ(EN_ProjectHandle ph, int saveflag) { - int errcode = 0; - - EN_Project *p = (EN_Project*)ph; - - if (!p->quality.OpenQflag) - return (105); - initqual(p); - p->save_options.SaveQflag = FALSE; - p->save_options.Saveflag = FALSE; - if (saveflag) { - errcode = openoutfile(p); - if (!errcode) - p->save_options.Saveflag = TRUE; - } - return errcode; -} - -int DLLEXPORT EN_runQ(EN_ProjectHandle ph, long *t) { - int errcode; - - EN_Project *p = (EN_Project*)ph; - - *t = 0; - if (!p->quality.OpenQflag) - return (105); - errcode = runqual(p, t); - if (errcode) - errmsg(p, errcode); - return errcode; -} - -int DLLEXPORT EN_nextQ(EN_ProjectHandle ph, long *tstep) { - int errcode; - - EN_Project *p = (EN_Project*)ph; - - *tstep = 0; - if (!p->quality.OpenQflag) - return (105); - errcode = nextqual(p, tstep); - if (!errcode && p->save_options.Saveflag && *tstep == 0) { - p->save_options.SaveQflag = TRUE; - } - if (errcode) - errmsg(p, errcode); - return errcode; -} - -int DLLEXPORT EN_stepQ(EN_ProjectHandle ph, long *tleft) { - int errcode; - - EN_Project *p = (EN_Project*)ph; - - *tleft = 0; - if (!p->quality.OpenQflag) - return (105); - errcode = stepqual(p, tleft); - if (!errcode && p->save_options.Saveflag && *tleft == 0) { - p->save_options.SaveQflag = TRUE; - } - if (errcode) - errmsg(p, errcode); - return errcode; -} - -int DLLEXPORT EN_closeQ(EN_ProjectHandle ph) { - - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return (102); - closequal(p); - p->quality.OpenQflag = FALSE; - return (0); -} - -/* - ---------------------------------------------------------------- - Functions for generating an output report - ---------------------------------------------------------------- - */ - -int DLLEXPORT EN_writeline(EN_ProjectHandle ph, char *line) { - - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return (102); - writeline(p, line); - return (0); -} - -int DLLEXPORT EN_report(EN_ProjectHandle ph) { - int errcode; - - EN_Project *p = (EN_Project*)ph; - - /* Check if results saved to binary output file */ - if (!p->save_options.SaveQflag) - return (106); - writewin(p->viewprog, FMT103); - errcode = writereport(p); - if (errcode) - errmsg(p, errcode); - return errcode; -} - -int DLLEXPORT EN_resetreport(EN_ProjectHandle ph) { - int i; - - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return (102); - initreport(&p->report); - for (i = 1; i <= p->network.Nnodes; i++) - p->network.Node[i].Rpt = 0; - for (i = 1; i <= p->network.Nlinks; i++) - p->network.Link[i].Rpt = 0; - return (0); -} - -int DLLEXPORT EN_setreport(EN_ProjectHandle ph, char *s) { - char s1[MAXLINE + 1]; - - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return (102); - if (strlen(s) >= MAXLINE) - return (250); - strcpy(s1, s); - strcat(s1, "\n"); - if (setreport(p, s1) > 0) - return (250); - else - return (0); -} - -/* - ---------------------------------------------------------------- - Functions for retrieving network information - ---------------------------------------------------------------- - */ - -/*** Updated 10/25/00 ***/ -int DLLEXPORT EN_getversion(int *v) -/*---------------------------------------------------------------- - ** Input: none - ** Output: *v = version number of the source code - ** Returns: error code (should always be 0) - ** Purpose: retrieves a number assigned to the most recent - ** update of the source code. This number, set by the - ** constant CODEVERSION found in TYPES.H, is to be - ** interpreted with implied decimals, i.e., "20100" == "2(.)01(.)00" - **---------------------------------------------------------------- - */ -{ - *v = CODEVERSION; - return 0; -} - -int DLLEXPORT EN_getcontrol(EN_ProjectHandle ph, int cindex, int *ctype, int *lindex, - EN_API_FLOAT_TYPE *setting, int *nindex, - EN_API_FLOAT_TYPE *level) { - double s, lvl; - - EN_Project *pr = (EN_Project*)ph; - - EN_Network *net = &pr->network; - - Scontrol *Control = net->Control; - Snode *Node = net->Node; - Slink *Link = net->Link; - - const int Njuncs = net->Njuncs; - double *Ucf = pr->Ucf; - - s = 0.0; - lvl = 0.0; - *ctype = 0; - *lindex = 0; - *nindex = 0; - if (!pr->Openflag) - return (102); - if (cindex < 1 || cindex > net->Ncontrols) - return (241); - *ctype = Control[cindex].Type; - *lindex = Control[cindex].Link; - s = Control[cindex].Setting; - if (Control[cindex].Setting != MISSING) { - switch (Link[*lindex].Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: - s *= Ucf[PRESSURE]; - break; - case EN_FCV: - s *= Ucf[FLOW]; - default: - break; - } - } else if (Control[cindex].Status == OPEN) { - s = 1.0; - } - - /*** Updated 3/1/01 ***/ - else - s = 0.0; - - *nindex = Control[cindex].Node; - if (*nindex > Njuncs) - lvl = (Control[cindex].Grade - Node[*nindex].El) * Ucf[ELEV]; - else if (*nindex > 0) - lvl = (Control[cindex].Grade - Node[*nindex].El) * Ucf[PRESSURE]; - else - lvl = (EN_API_FLOAT_TYPE)Control[cindex].Time; - *setting = (EN_API_FLOAT_TYPE)s; - *level = (EN_API_FLOAT_TYPE)lvl; - return (0); -} - -int DLLEXPORT EN_getcount(EN_ProjectHandle ph, EN_CountType code, int *count) { - - EN_Project *pr = (EN_Project*)ph; - - EN_Network *net = &pr->network; - - *count = 0; - if (!pr->Openflag) - return (102); - switch (code) { - case EN_NODECOUNT: - *count = net->Nnodes; - break; - case EN_TANKCOUNT: - *count = net->Ntanks; - break; - case EN_LINKCOUNT: - *count = net->Nlinks; - break; - case EN_PATCOUNT: - *count = net->Npats; - break; - case EN_CURVECOUNT: - *count = net->Ncurves; - break; - case EN_CONTROLCOUNT: - *count = net->Ncontrols; - break; - case EN_RULECOUNT: - *count = net->Nrules; - break; - default: - return (251); - } - return (0); -} - -int DLLEXPORT EN_getoption(EN_ProjectHandle ph, EN_Option code, - EN_API_FLOAT_TYPE *value) { - - EN_Project *pr = (EN_Project*)ph; - - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qu = &pr->quality; - double *Ucf = pr->Ucf; - - double v = 0.0; - *value = 0.0; - if (!pr->Openflag) - return (102); - switch (code) { - case EN_TRIALS: - v = (double)hyd->MaxIter; - break; - case EN_ACCURACY: - v = hyd->Hacc; - break; - case EN_TOLERANCE: - v = qu->Ctol * Ucf[QUALITY]; - break; - case EN_EMITEXPON: - if (hyd->Qexp > 0.0) - v = 1.0 / hyd->Qexp; - break; - case EN_DEMANDMULT: - v = hyd->Dmult; - break; - case EN_HEADERROR: - v = hyd->HeadErrorLimit * Ucf[HEAD]; - break; - case EN_FLOWCHANGE: - v = hyd->FlowChangeLimit * Ucf[FLOW]; - break; - case EN_DEMANDDEFPAT: - v = hyd->DefPat; - break; - case EN_HEADLOSSFORM: - v = hyd->Formflag; - break; - - default: - return (251); - } - *value = (EN_API_FLOAT_TYPE)v; - return (0); -} - -int DLLEXPORT EN_gettimeparam(EN_ProjectHandle ph, int code, long *value) { - int i; - - EN_Project *pr = (EN_Project*)ph; - - report_options_t *rep = &pr->report; - quality_t *qu = &pr->quality; - time_options_t *time = &pr->time_options; - - - *value = 0; - if (!pr->Openflag) - return (102); - if (code < EN_DURATION || code > EN_NEXTEVENTIDX) - return (251); - switch (code) { - case EN_DURATION: - *value = time->Dur; - break; - case EN_HYDSTEP: - *value = time->Hstep; - break; - case EN_QUALSTEP: - *value = qu->Qstep; - break; - case EN_PATTERNSTEP: - *value = time->Pstep; - break; - case EN_PATTERNSTART: - *value = time->Pstart; - break; - case EN_REPORTSTEP: - *value = time->Rstep; - break; - case EN_REPORTSTART: - *value = time->Rstart; - break; - case EN_STATISTIC: - *value = rep->Tstatflag; - break; - case EN_RULESTEP: - *value = time->Rulestep; - break; - case EN_PERIODS: - *value = rep->Nperiods; - break; - case EN_STARTTIME: - *value = time->Tstart; - break; /* Added TNT 10/2/2009 */ - case EN_HTIME: - *value = time->Htime; - break; - case EN_NEXTEVENT: - *value = time->Hstep; // find the lesser of the hydraulic time step length, or the - // time to next fill/empty - tanktimestep(pr,value); - break; - case EN_NEXTEVENTIDX: - *value = time->Hstep; - i = tanktimestep(pr, value); - *value = i; - break; - } - return (0); -} - -int DLLEXPORT EN_getflowunits(EN_ProjectHandle ph, int *code) { - - EN_Project *p = (EN_Project*)ph; - - *code = -1; - if (!p->Openflag) - return (102); - *code = p->parser.Flowflag; - return (0); -} - -int DLLEXPORT EN_setflowunits(EN_ProjectHandle ph, int code) { - int i, j; - double qfactor, vfactor, hfactor, efactor, xfactor, yfactor; - - EN_Project *p = (EN_Project*)ph; - - double *Ucf = p->Ucf; - EN_Network *net = &p->network; - - if (!p->Openflag) { - return (102); - } - - /* Determine unit system based on flow units */ - qfactor = Ucf[FLOW]; - vfactor = Ucf[VOLUME]; - hfactor = Ucf[HEAD]; - efactor = Ucf[ELEV]; - - p->parser.Flowflag = code; - switch (code) - { - case LPS: /* Liters/sec */ - case LPM: /* Liters/min */ - case MLD: /* megaliters/day */ - case CMH: /* cubic meters/hr */ - case CMD: /* cubic meters/day */ - p->parser.Unitsflag = SI; - break; - default: - p->parser.Unitsflag = US; - break; - } - - /* Revise pressure units depending on flow units */ - if (p->parser.Unitsflag != SI) { - p->parser.Pressflag = PSI; - } - else if (p->parser.Pressflag == PSI) { - p->parser.Pressflag = METERS; - } - - initunits(p); - - //update curves - for (i=1; i <= net->Ncurves; i++) - { - switch (net->Curve[i].Type) + // Open hydraulics file if requested + p->outfile.Saveflag = FALSE; + if (sflag > 0) { - case V_CURVE: - xfactor = efactor/Ucf[ELEV]; - yfactor = vfactor/Ucf[VOLUME]; - break; - case H_CURVE: - case P_CURVE: - xfactor = qfactor/Ucf[FLOW]; - yfactor = hfactor/Ucf[HEAD]; - break; - case E_CURVE: - xfactor = qfactor/Ucf[FLOW]; - yfactor = 1; - break; - default: - xfactor = 1; - yfactor = 1; - } - - for (j=0; j < net->Curve[i].Npts; j++) - { - net->Curve[i].X[j] = net->Curve[i].X[j]/xfactor; - net->Curve[i].Y[j] = net->Curve[i].Y[j]/yfactor; - } - } - - return (0); -} - -int DLLEXPORT EN_getdemandmodel(EN_ProjectHandle ph, int *type, EN_API_FLOAT_TYPE *pmin, - EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp) -{ - EN_Project *p = (EN_Project*)ph; - - *type = p->hydraulics.DemandModel; - *pmin = (EN_API_FLOAT_TYPE)(p->hydraulics.Pmin * p->Ucf[PRESSURE]); - *preq = (EN_API_FLOAT_TYPE)(p->hydraulics.Preq * p->Ucf[PRESSURE]); - *pexp = (EN_API_FLOAT_TYPE)(p->hydraulics.Pexp); - - return (0); -} - -int DLLEXPORT EN_setdemandmodel(EN_ProjectHandle ph, int type, EN_API_FLOAT_TYPE pmin, - EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp) -{ - EN_Project *p = (EN_Project*)ph; - - if (type < 0 || type > EN_PDA) return (251); - if (pmin > preq || pexp <= 0.0) return (202); - p->hydraulics.DemandModel = type; - p->hydraulics.Pmin = pmin / p->Ucf[PRESSURE]; - p->hydraulics.Preq = preq / p->Ucf[PRESSURE]; - p->hydraulics.Pexp = pexp; - - return (0); -} - - -int DLLEXPORT EN_getpatternindex(EN_ProjectHandle ph, char *id, int *index) { - int i; - - EN_Project *p = (EN_Project*)ph; - - *index = 0; - if (!p->Openflag) - return (102); - for (i = 1; i <= p->network.Npats; i++) { - if (strcmp(id, p->network.Pattern[i].ID) == 0) { - *index = i; - return (0); - } - } - *index = 0; - return (205); -} - -int DLLEXPORT EN_getpatternid(EN_ProjectHandle ph, int index, char *id) { - - EN_Project *p = (EN_Project*)ph; - - strcpy(id, ""); - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Npats) - return (205); - strcpy(id, p->network.Pattern[index].ID); - return (0); -} - -int DLLEXPORT EN_getpatternlen(EN_ProjectHandle ph, int index, int *len) { - - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Npats) - return (205); - *len = p->network.Pattern[index].Length; - return (0); -} - -int DLLEXPORT EN_getpatternvalue(EN_ProjectHandle ph, int index, int period, - EN_API_FLOAT_TYPE *value) { - - EN_Project *p = (EN_Project*)ph; - - *value = 0.0; - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Npats) - return (205); - if (period < 1 || period > p->network.Pattern[index].Length) - return (251); - *value = (EN_API_FLOAT_TYPE)p->network.Pattern[index].F[period - 1]; - return (0); -} - -int DLLEXPORT EN_getcurveindex(EN_ProjectHandle ph, char *id, int *index) { - int i; - - EN_Project *p = (EN_Project*)ph; - - *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); -} - -int DLLEXPORT EN_getcurveid(EN_ProjectHandle ph, int index, char *id) { - - EN_Project *p = (EN_Project*)ph; - - strcpy(id, ""); - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Ncurves) - return (206); - strcpy(id, p->network.Curve[index].ID); - return (0); -} - -int DLLEXPORT EN_getcurvelen(EN_ProjectHandle ph, int index, int *len) { - - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Ncurves) - return (206); - *len = p->network.Curve[index].Npts; - return (0); -} - -int DLLEXPORT EN_getcurvevalue(EN_ProjectHandle ph, int index, int pnt, - EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) { - - EN_Project *p = (EN_Project*)ph; - - *x = 0.0; - *y = 0.0; - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Ncurves) - return (206); - if (pnt < 1 || pnt > p->network.Curve[index].Npts) - return (251); - *x = (EN_API_FLOAT_TYPE)p->network.Curve[index].X[pnt - 1]; - *y = (EN_API_FLOAT_TYPE)p->network.Curve[index].Y[pnt - 1]; - return (0); -} - -int DLLEXPORT EN_getqualtype(EN_ProjectHandle ph, int *qualcode, int *tracenode) { - - EN_Project *p = (EN_Project*)ph; - - *tracenode = 0; - if (!p->Openflag) - return (102); - *qualcode = p->quality.Qualflag; - if (p->quality.Qualflag == TRACE) - *tracenode = p->quality.TraceNode; - return (0); -} - - -int DLLEXPORT EN_getqualinfo(EN_ProjectHandle ph, int *qualcode, char *chemname, - char *chemunits, int *tracenode) { - - EN_Project *p = (EN_Project*)ph; - - EN_getqualtype(ph, qualcode, tracenode); - - if (p->quality.Qualflag == TRACE) { - strncpy(chemname, "", MAXID); - strncpy(chemunits, "dimensionless", MAXID); - } else { - strncpy(chemname, p->quality.ChemName, MAXID); - strncpy(chemunits, p->quality.ChemUnits, MAXID); - } - return (0); -} - -void errorLookup(int errcode, char *dest_msg, int dest_len) -// Purpose: takes error code returns error message -{ - char *msg = NULL; - - switch (errcode) - { - case 1: msg = WARN1; - break; - case 2: msg = WARN2; - break; - case 3: msg = WARN3; - break; - case 4: msg = WARN4; - break; - case 5: msg = WARN5; - break; - case 6: msg = WARN6; - break; - default: - msg = geterrmsg(errcode, msg); - } - strncpy(dest_msg, msg, MAXMSG); -} - -int DLLEXPORT EN_geterror(int errcode, char *errmsg, int n) { - // Deprecate? - char newMsg[MAXMSG+1]; - - switch (errcode) { - case 1: - strncpy(errmsg, WARN1, n); - break; - case 2: - strncpy(errmsg, WARN2, n); - break; - case 3: - strncpy(errmsg, WARN3, n); - break; - case 4: - strncpy(errmsg, WARN4, n); - break; - case 5: - strncpy(errmsg, WARN5, n); - break; - case 6: - strncpy(errmsg, WARN6, n); - break; - default: - geterrmsg(errcode, newMsg); - strncpy(errmsg, newMsg, n); - } - if (strlen(errmsg) == 0) - return 251; - else - return 0; -} - -int DLLEXPORT EN_getstatistic(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE *value) { - - EN_Project *p = (EN_Project*)ph; - - switch (code) { - case EN_ITERATIONS: - *value = (EN_API_FLOAT_TYPE)p->hydraulics.Iterations; - break; - case EN_RELATIVEERROR: - *value = (EN_API_FLOAT_TYPE)p->hydraulics.RelativeError; - break; - case EN_MAXHEADERROR: - *value = (EN_API_FLOAT_TYPE)(p->hydraulics.MaxHeadError * p->Ucf[HEAD]); - break; - case EN_MAXFLOWCHANGE: - *value = (EN_API_FLOAT_TYPE)(p->hydraulics.MaxFlowChange * p->Ucf[FLOW]); - break; - case EN_MASSBALANCE: - *value = (EN_API_FLOAT_TYPE)(p->quality.massbalance.ratio); - break; - default: - break; - } - return (0); -} - -/* - ---------------------------------------------------------------- - Functions for retrieving node data - ---------------------------------------------------------------- - */ - -int DLLEXPORT EN_getnodeindex(EN_ProjectHandle ph, char *id, int *index) { - - EN_Project *p = (EN_Project*)ph; - - *index = 0; - if (!p->Openflag) - return (102); - *index = findnode(&p->network,id); - if (*index == 0) - return (203); - else - return (0); -} - -int DLLEXPORT EN_getnodeid(EN_ProjectHandle ph, int index, char *id) { - - EN_Project *p = (EN_Project*)ph; - - strcpy(id, ""); - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Nnodes) - return (203); - strcpy(id, p->network.Node[index].ID); - return (0); -} - -int DLLEXPORT EN_getnodetype(EN_ProjectHandle ph, int index, int *code) { - - EN_Project *p = (EN_Project*)ph; - - *code = -1; - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Nnodes) - return (203); - if (index <= p->network.Njuncs) - *code = EN_JUNCTION; - else { - if (p->network.Tank[index - p->network.Njuncs].A == 0.0) - *code = EN_RESERVOIR; - else - *code = EN_TANK; - } - return (0); -} - -int DLLEXPORT EN_getcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, - EN_API_FLOAT_TYPE *y) { - - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Nnodes) - return (203); - if (!p->parser.Coordflag) - return (255); - - // check if node have coords - if (p->network.Coord[index].HaveCoords == FALSE) - return (254); - - *x = (EN_API_FLOAT_TYPE)p->network.Coord[index].X; - *y = (EN_API_FLOAT_TYPE)p->network.Coord[index].Y; - - return (0); -} - -int DLLEXPORT EN_setcoord(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE x, - EN_API_FLOAT_TYPE y) { - - EN_Project *p = (EN_Project*)ph; - - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Nnodes) - return (203); - if (!p->parser.Coordflag) - return (255); - - p->network.Coord[index].X = x; - p->network.Coord[index].Y = y; - p->network.Coord[index].HaveCoords = TRUE; - return (0); -} - -int DLLEXPORT EN_getnodevalue(EN_ProjectHandle ph, int index, int code, - EN_API_FLOAT_TYPE *value) { - double v = 0.0; - Pdemand demand; - Psource source; - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - hydraulics_t *hyd = &p->hydraulics; - quality_t *qu = &p->quality; - - Snode *Node = net->Node; - Stank *Tank = net->Tank; - - const int Nnodes = net->Nnodes; - const int Njuncs = net->Njuncs; - - double *Ucf = p->Ucf; - double *NodeDemand = hyd->NodeDemand; - double *NodeQual = qu->NodeQual; - - - /* Check for valid arguments */ - *value = 0.0; - if (!p->Openflag) - return (102); - if (index <= 0 || index > Nnodes) - return (203); - - /* Retrieve called-for parameter */ - switch (code) { - case EN_ELEVATION: - v = Node[index].El * Ucf[ELEV]; - break; - - case EN_BASEDEMAND: - v = 0.0; - /* NOTE: primary demand category is last on demand list */ - if (index <= Njuncs) - for (demand = Node[index].D; demand != NULL; demand = demand->next) - v = (demand->Base); - v *= Ucf[FLOW]; - break; - - case EN_PATTERN: - v = 0.0; - /* NOTE: primary demand category is last on demand list */ - if (index <= Njuncs) { - for (demand = Node[index].D; demand != NULL; demand = demand->next) - v = (double)(demand->Pat); - } else - v = (double)(Tank[index - Njuncs].Pat); - break; - - case EN_EMITTER: - v = 0.0; - if (Node[index].Ke > 0.0) - v = Ucf[FLOW] / pow((Ucf[PRESSURE] * Node[index].Ke), (1.0 / hyd->Qexp)); - break; - - case EN_INITQUAL: - v = Node[index].C0 * Ucf[QUALITY]; - break; - - /*** Additional parameters added for retrieval ***/ - case EN_SOURCEQUAL: - case EN_SOURCETYPE: - case EN_SOURCEMASS: - case EN_SOURCEPAT: - source = Node[index].S; - if (source == NULL) - return (240); - if (code == EN_SOURCEQUAL) - v = source->C0; - else if (code == EN_SOURCEMASS) - v = source->Smass * 60.0; - else if (code == EN_SOURCEPAT) - v = source->Pat; - else - v = source->Type; - break; - - case EN_TANKLEVEL: - if (index <= Njuncs) - return (251); - v = (Tank[index - Njuncs].H0 - Node[index].El) * Ucf[ELEV]; - break; - - /*** New parameter added for retrieval ***/ - case EN_INITVOLUME: - v = 0.0; - if (index > Njuncs) - v = Tank[index - Njuncs].V0 * Ucf[VOLUME]; - break; - - /*** New parameter added for retrieval ***/ - case EN_MIXMODEL: - v = MIX1; - if (index > Njuncs) - v = Tank[index - Njuncs].MixModel; - break; - - /*** New parameter added for retrieval ***/ - case EN_MIXZONEVOL: - v = 0.0; - if (index > Njuncs) - v = Tank[index - Njuncs].V1max * Ucf[VOLUME]; - break; - - case EN_DEMAND: - v = NodeDemand[index] * Ucf[FLOW]; - break; - - case EN_HEAD: - v = hyd->NodeHead[index] * Ucf[HEAD]; - break; - - case EN_PRESSURE: - v = (hyd->NodeHead[index] - Node[index].El) * Ucf[PRESSURE]; - break; - - case EN_QUALITY: - v = NodeQual[index] * Ucf[QUALITY]; - break; - - /*** New parameters added for retrieval begins here ***/ - /*** (Thanks to Nicolas Basile of Ecole Polytechnique ***/ - /*** de Montreal for suggesting some of these.) ***/ - - case EN_TANKDIAM: - v = 0.0; - if (index > Njuncs) { - v = sqrt(4.0 / PI * Tank[index - Njuncs].A) * Ucf[ELEV]; - } - break; - - case EN_MINVOLUME: - v = 0.0; - if (index > Njuncs) - v = Tank[index - Njuncs].Vmin * Ucf[VOLUME]; - break; - - case EN_MAXVOLUME: // !sph - v = 0.0; - if (index > Njuncs) - v = Tank[index - Njuncs].Vmax * Ucf[VOLUME]; - break; - - case EN_VOLCURVE: - v = 0.0; - if (index > Njuncs) - v = Tank[index - Njuncs].Vcurve; - break; - - case EN_MINLEVEL: - v = 0.0; - if (index > Njuncs) { - v = (Tank[index - Njuncs].Hmin - Node[index].El) * Ucf[ELEV]; - } - break; - - case EN_MAXLEVEL: - v = 0.0; - if (index > Njuncs) { - v = (Tank[index - Njuncs].Hmax - Node[index].El) * Ucf[ELEV]; - } - break; - - case EN_MIXFRACTION: - v = 1.0; - if (index > Njuncs && Tank[index - Njuncs].Vmax > 0.0) { - v = Tank[index - Njuncs].V1max / Tank[index - Njuncs].Vmax; - } - break; - - case EN_TANK_KBULK: - v = 0.0; - if (index > Njuncs) - v = Tank[index - Njuncs].Kb * SECperDAY; - break; - - /*** New parameter additions ends here. ***/ - - case EN_TANKVOLUME: - if (index <= Njuncs) - return (251); - v = tankvolume(p, index - Njuncs, hyd->NodeHead[index]) * Ucf[VOLUME]; - break; - - default: - return (251); - } - *value = (EN_API_FLOAT_TYPE)v; - return (0); -} - -/* - ---------------------------------------------------------------- - Functions for retrieving link data - ---------------------------------------------------------------- - */ - -int DLLEXPORT EN_getlinkindex(EN_ProjectHandle ph, char *id, int *index) { - - EN_Project *p = (EN_Project*)ph; - - *index = 0; - if (!p->Openflag) - return (102); - *index = findlink(&p->network,id); - if (*index == 0) - return (204); - else - return (0); -} - -int DLLEXPORT EN_getlinkid(EN_ProjectHandle ph, int index, char *id) { - - EN_Project *p = (EN_Project*)ph; - - strcpy(id, ""); - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Nlinks) - return (204); - strcpy(id, p->network.Link[index].ID); - return (0); -} - -int DLLEXPORT EN_getlinktype(EN_ProjectHandle ph, int index, EN_LinkType *code) { - - EN_Project *p = (EN_Project*)ph; - - *code = -1; - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Nlinks) - return (204); - *code = p->network.Link[index].Type; - return (0); -} - -int DLLEXPORT EN_getlinknodes(EN_ProjectHandle ph, int index, int *node1, - int *node2) { - - EN_Project *p = (EN_Project*)ph; - - *node1 = 0; - *node2 = 0; - if (!p->Openflag) - return (102); - if (index < 1 || index > p->network.Nlinks) - return (204); - *node1 = p->network.Link[index].N1; - *node2 = p->network.Link[index].N2; - return (0); -} - -int DLLEXPORT EN_getlinkvalue(EN_ProjectHandle ph, int index, EN_LinkProperty code, - EN_API_FLOAT_TYPE *value) { - double a, h, q, v = 0.0; - int returnValue = 0; - int pmp; - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - hydraulics_t *hyd = &p->hydraulics; - - Slink *Link = net->Link; - Spump *Pump = net->Pump; - - const int Nlinks = net->Nlinks; - - double *Ucf = p->Ucf; - double *LinkFlows = hyd->LinkFlows; - double *LinkSetting = hyd->LinkSetting; - - /* Check for valid arguments */ - *value = 0.0; - if (!p->Openflag) - return (102); - if (index <= 0 || index > Nlinks) - return (204); - - /* Retrieve called-for parameter */ - switch (code) { - case EN_DIAMETER: - if (Link[index].Type == EN_PUMP) - v = 0.0; - else - v = Link[index].Diam * Ucf[DIAM]; - break; - - case EN_LENGTH: - v = Link[index].Len * Ucf[ELEV]; - break; - - case EN_ROUGHNESS: - if (Link[index].Type <= EN_PIPE) { - if (hyd->Formflag == DW) - v = Link[index].Kc * (1000.0 * Ucf[ELEV]); + errcode = openhydfile(p); + if (!errcode) p->outfile.Saveflag = TRUE; else - v = Link[index].Kc; - } else - v = 0.0; - break; - - case EN_MINORLOSS: - if (Link[index].Type != EN_PUMP) { - v = Link[index].Km; - v *= (SQR(Link[index].Diam) * SQR(Link[index].Diam) / 0.02517); - } else - v = 0.0; - break; - - case EN_INITSTATUS: - if (Link[index].Stat <= CLOSED) - v = 0.0; - else - v = 1.0; - break; - - case EN_INITSETTING: - if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) - return EN_getlinkvalue(p, index, EN_ROUGHNESS, value); - v = Link[index].Kc; - switch (Link[index].Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: - v *= Ucf[PRESSURE]; - break; - case EN_FCV: - v *= Ucf[FLOW]; - default: - break; - } - break; - - case EN_KBULK: - v = Link[index].Kb * SECperDAY; - break; - - case EN_KWALL: - v = Link[index].Kw * SECperDAY; - break; - - case EN_FLOW: - - /*** Updated 10/25/00 ***/ - if (hyd->LinkStatus[index] <= CLOSED) - v = 0.0; - else - v = LinkFlows[index] * Ucf[FLOW]; - break; - - case EN_VELOCITY: - if (Link[index].Type == EN_PUMP) { - v = 0.0; - } - - /*** Updated 11/19/01 ***/ - else if (hyd->LinkStatus[index] <= CLOSED) - v = 0.0; - - else { - q = ABS(LinkFlows[index]); - a = PI * SQR(Link[index].Diam) / 4.0; - v = q / a * Ucf[VELOCITY]; - } - break; - - case EN_HEADLOSS: - - /*** Updated 11/19/01 ***/ - if (hyd->LinkStatus[index] <= CLOSED) - v = 0.0; - - else { - h = hyd->NodeHead[Link[index].N1] - hyd->NodeHead[Link[index].N2]; - if (Link[index].Type != EN_PUMP) - h = ABS(h); - v = h * Ucf[HEADLOSS]; - } - break; - - case EN_STATUS: - if (hyd->LinkStatus[index] <= CLOSED) - v = 0.0; - else - v = 1.0; - break; - - case EN_STATE: - v = hyd->LinkStatus[index]; - - if (Link[index].Type == EN_PUMP) { - pmp = findpump(net, index); - if (hyd->LinkStatus[index] >= OPEN) { - if (hyd->LinkFlows[index] > hyd->LinkSetting[index] * Pump[pmp].Qmax) - v = XFLOW; - if (hyd->LinkFlows[index] < 0.0) - v = XHEAD; - } - } - break; - - case EN_CONST_POWER: - v = 0; - if (Link[index].Type == EN_PUMP) { - pmp = findpump(net, index); - if (Pump[pmp].Ptype == CONST_HP) { - v = Link[index].Km; // Power in HP - } - } - break; - - case EN_SPEED: - v = 0; - if (Link[index].Type == EN_PUMP) { - pmp = findpump(net, index); - v = Link[index].Kc; - } - break; - - case EN_SETTING: - if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) { - return EN_getlinkvalue(p, index, EN_ROUGHNESS, value); - } - if (LinkSetting[index] == MISSING) { - v = 0.0; - } else { - v = LinkSetting[index]; - } - switch (Link[index].Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: - v *= Ucf[PRESSURE]; - break; - case EN_FCV: - v *= Ucf[FLOW]; - default: - break; - } - break; - - case EN_ENERGY: - getenergy(p, index, &v, &a); - break; - - case EN_LINKQUAL: - v = avgqual(p, index) * Ucf[LINKQUAL]; - break; - - case EN_LINKPATTERN: - if (Link[index].Type == EN_PUMP) - v = (double)Pump[findpump(&p->network, index)].Upat; - break; - - case EN_EFFICIENCY: - getenergy(p, index, &a, &v); - break; - - case EN_PRICEPATTERN: - if (Link[index].Type == EN_PUMP) - v = (double)Pump[findpump(&p->network, index)].Epat; - break; - - case EN_HEADCURVE: - if (Link[index].Type == EN_PUMP) { - v = (double)Pump[findpump(&p->network, index)].Hcurve; - if (v == 0) { - returnValue = 226; - } - } - else { - v = 0; - returnValue = 211; - } - break; - - case EN_EFFICIENCYCURVE: - if (Link[index].Type == EN_PUMP) { - v = (double)Pump[findpump(&p->network, index)].Ecurve; - if (v == 0) { - returnValue = 268; - } - } - else { - v = 0; - returnValue = 211; - } - - default: - v = 0; - returnValue = 251; - } - *value = (EN_API_FLOAT_TYPE)v; - return returnValue; -} - -int DLLEXPORT EN_getcurve(EN_ProjectHandle ph, int curveIndex, char *id, int *nValues, - EN_API_FLOAT_TYPE **xValues, - EN_API_FLOAT_TYPE **yValues) { - int iPoint, nPoints; - Scurve curve; - EN_API_FLOAT_TYPE *pointX, *pointY; - - EN_Project *p = (EN_Project*)ph; - - /* Check that input file opened */ - if (!p->Openflag) - return (102); - - curve = p->network.Curve[curveIndex]; - nPoints = curve.Npts; - - pointX = calloc(nPoints, sizeof(EN_API_FLOAT_TYPE)); - pointY = calloc(nPoints, sizeof(EN_API_FLOAT_TYPE)); - - for (iPoint = 0; iPoint < nPoints; iPoint++) { - double x = curve.X[iPoint]; - double y = curve.Y[iPoint]; - pointX[iPoint] = (EN_API_FLOAT_TYPE)x; - pointY[iPoint] = (EN_API_FLOAT_TYPE)y; - } - - strncpy(id, "", MAXID); - strncpy(id, curve.ID, MAXID); - *nValues = nPoints; - *xValues = pointX; - *yValues = pointY; - - return (0); -} - -/* - ---------------------------------------------------------------- - Functions for changing network data - ---------------------------------------------------------------- - */ - -int DLLEXPORT EN_addcontrol(EN_ProjectHandle ph, int *cindex, int ctype, int lindex, - EN_API_FLOAT_TYPE setting, int nindex, - EN_API_FLOAT_TYPE level) { - char status = ACTIVE; - int i, n; - long t = 0, nControls; - double s = setting, lvl = level; - EN_Network *net; - Snode *Node; - Slink *Link; - Scontrol *Control; - Scontrol *tmpControl; - - int Nnodes; - int Njuncs; - int Nlinks; - double *Ucf; - - EN_Project *p = (EN_Project*)ph; - parser_data_t *par = &p->parser; - - /* Check that input file opened */ - if (!p->Openflag) - return (102); - - net = &p->network; - Node = net->Node; - Link = net->Link; - Control = net->Control; - - Nnodes = net->Nnodes; - Njuncs = net->Njuncs; - Nlinks = net->Nlinks; - nControls = net->Ncontrols; - - Ucf = p->Ucf; - - /* Check that controlled link exists */ - if (lindex < 0 || lindex > Nlinks) - return (204); - - /* Cannot control check valve. */ - if (Link[lindex].Type == EN_CVPIPE) - return (207); - - /* Check for valid parameters */ - if (ctype < 0 || ctype > EN_TIMEOFDAY) - return (251); - if (ctype == EN_LOWLEVEL || ctype == EN_HILEVEL) { - if (nindex < 1 || nindex > Nnodes) - return (203); - } else - nindex = 0; - if (s < 0.0 || lvl < 0.0) - return (202); - - /* Adjust units of control parameters */ - switch (Link[lindex].Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: - s /= Ucf[PRESSURE]; - break; - case EN_FCV: - s /= Ucf[FLOW]; - break; - case EN_GPV: - if (s == 0.0) - status = CLOSED; - else if (s == 1.0) - status = OPEN; - else - return (202); - s = Link[lindex].Kc; - break; - case EN_PIPE: - case EN_PUMP: - status = OPEN; - if (s == 0.0) - status = CLOSED; - default: - break; - } - - if (ctype == LOWLEVEL || ctype == HILEVEL) { - if (nindex > Njuncs) - lvl = Node[nindex].El + level / Ucf[ELEV]; - else - lvl = Node[nindex].El + level / Ucf[PRESSURE]; - } - if (ctype == TIMER) - t = (long)ROUND(lvl); - if (ctype == TIMEOFDAY) - t = (long)ROUND(lvl) % SECperDAY; - - //new control is good - /* Allocate memory for a new array of controls */ - n = nControls + 1; - tmpControl = (Scontrol *)calloc(n + 1, sizeof(Scontrol)); - if (tmpControl == NULL) - return (101); - - /* Copy contents of old controls array to new one */ - for (i = 0; i <= nControls; i++) { - tmpControl[i].Type = Control[i].Type; - tmpControl[i].Link = Control[i].Link; - tmpControl[i].Node = Control[i].Node; - tmpControl[i].Status = Control[i].Status; - tmpControl[i].Setting = Control[i].Setting; - tmpControl[i].Grade = Control[i].Grade; - tmpControl[i].Time = Control[i].Time; - } - - /* Add the new control to the new array of controls */ - tmpControl[n].Type = (char)ctype; - tmpControl[n].Link = lindex; - tmpControl[n].Node = nindex; - tmpControl[n].Status = status; - tmpControl[n].Setting = s; - tmpControl[n].Grade = lvl; - tmpControl[n].Time = t; - - // Replace old control array with new one - free(Control); - net->Control = tmpControl; - net->Ncontrols = n; - par->MaxControls = n; - - // return the new control index - *cindex = n; - - return (0); -} - -int DLLEXPORT EN_setcontrol(EN_ProjectHandle ph, int cindex, int ctype, int lindex, - EN_API_FLOAT_TYPE setting, int nindex, - EN_API_FLOAT_TYPE level) { - char status = ACTIVE; - long t = 0; - double s = setting, lvl = level; - EN_Network *net; - Snode *Node; - Slink *Link; - Scontrol *Control; - - int Nnodes; - int Njuncs; - int Nlinks; - double *Ucf; - - EN_Project *p = (EN_Project*)ph; - - /* Check that input file opened */ - if (!p->Openflag) - return (102); - - /* Check that control exists */ - if (cindex < 1 || cindex > p->network.Ncontrols) - return (241); - - net = &p->network; - - Node = net->Node; - Link = net->Link; - Control = net->Control; - - Nnodes = net->Nnodes; - Njuncs = net->Njuncs; - Nlinks = net->Nlinks; - - Ucf = p->Ucf; - - /* Check that controlled link exists */ - if (lindex == 0) { - Control[cindex].Link = 0; - return (0); - } - if (lindex < 0 || lindex > Nlinks) - return (204); - - /* Cannot control check valve. */ - if (Link[lindex].Type == EN_CVPIPE) - return (207); - - /* Check for valid parameters */ - if (ctype < 0 || ctype > EN_TIMEOFDAY) - return (251); - if (ctype == EN_LOWLEVEL || ctype == EN_HILEVEL) { - if (nindex < 1 || nindex > Nnodes) - return (203); - } else - nindex = 0; - if (s < 0.0 || lvl < 0.0) - return (202); - - /* Adjust units of control parameters */ - switch (Link[lindex].Type) { - case EN_PRV: - case EN_PSV: - case EN_PBV: - s /= Ucf[PRESSURE]; - break; - case EN_FCV: - s /= Ucf[FLOW]; - break; - - /*** Updated 9/7/00 ***/ - case EN_GPV: - if (s == 0.0) - status = CLOSED; - else if (s == 1.0) - status = OPEN; - else - return (202); - s = Link[lindex].Kc; - break; - - case EN_PIPE: - case EN_PUMP: - status = OPEN; - if (s == 0.0) - status = CLOSED; - default: - break; - } - if (ctype == LOWLEVEL || ctype == HILEVEL) { - if (nindex > Njuncs) - lvl = Node[nindex].El + level / Ucf[ELEV]; - else - lvl = Node[nindex].El + level / Ucf[PRESSURE]; - } - if (ctype == TIMER) - t = (long)ROUND(lvl); - if (ctype == TIMEOFDAY) - t = (long)ROUND(lvl) % SECperDAY; - - /* Reset control's parameters */ - Control[cindex].Type = (char)ctype; - Control[cindex].Link = lindex; - Control[cindex].Node = nindex; - Control[cindex].Status = status; - Control[cindex].Setting = s; - Control[cindex].Grade = lvl; - Control[cindex].Time = t; - return (0); -} - -int DLLEXPORT EN_setnodeid(EN_ProjectHandle ph, int index, char *newid) -{ - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; - size_t n; - - // Check for valid arguments - if (index <= 0 || index > net->Nnodes) - { - return (203); - } - n = strlen(newid); - if (n < 1 || n > MAXID) return (209); - if (strcspn(newid, " ;") < n) return (209); - - // Check if another node with same name exists - if (hashtable_find(net->NodeHashTable, newid) > 0) - { - return (215); - } - - // Replace the existing node ID with the new value - hashtable_delete(net->NodeHashTable, net->Node[index].ID); - strncpy(net->Node[index].ID, newid, MAXID); - hashtable_insert(net->NodeHashTable, net->Node[index].ID, index); - return (0); -} - -int DLLEXPORT EN_setnodevalue(EN_ProjectHandle ph, int index, int code, EN_API_FLOAT_TYPE v) -/*---------------------------------------------------------------- - ** Input: index = node index - ** code = node parameter code (see EPANET2.H) - ** value = parameter value - ** Output: none - ** Returns: error code - ** Purpose: sets input parameter value for a node - **---------------------------------------------------------------- - */ -{ - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - hydraulics_t *hyd = &p->hydraulics; - quality_t *qu = &p->quality; - - Snode *Node = net->Node; - Stank *Tank = net->Tank; - - const int Nnodes = net->Nnodes; - const int Njuncs = net->Njuncs; - const int Npats = net->Npats; - - double *Ucf = p->Ucf; - - int j; - Pdemand demand; - Psource source; - double Htmp; - double value = v; - - if (!p->Openflag) - return (102); - if (index <= 0 || index > Nnodes) - return (203); - switch (code) { - case EN_ELEVATION: - if (index <= Njuncs) - Node[index].El = value / Ucf[ELEV]; - else { - value = (value / Ucf[ELEV]) - Node[index].El; - j = index - Njuncs; - Tank[j].H0 += value; - Tank[j].Hmin += value; - Tank[j].Hmax += value; - Node[index].El += value; - hyd->NodeHead[index] += value; - } - break; - - case EN_BASEDEMAND: - /* NOTE: primary demand category is last on demand list */ - if (index <= Njuncs) { - for (demand = Node[index].D; demand != NULL; demand = demand->next) { - if (demand->next == NULL) - demand->Base = value / Ucf[FLOW]; - } - } - break; - - case EN_PATTERN: - /* NOTE: primary demand category is last on demand list */ - j = ROUND(value); - if (j < 0 || j > Npats) - return (205); - if (index <= Njuncs) { - for (demand = Node[index].D; demand != NULL; demand = demand->next) { - if (demand->next == NULL) - demand->Pat = j; - } - } else - Tank[index - Njuncs].Pat = j; - break; - - case EN_EMITTER: - if (index > Njuncs) - return (203); - if (value < 0.0) - return (202); - if (value > 0.0) - value = pow((Ucf[FLOW] / value), hyd->Qexp) / Ucf[PRESSURE]; - Node[index].Ke = value; - break; - - case EN_INITQUAL: - if (value < 0.0) - return (202); - Node[index].C0 = value / Ucf[QUALITY]; - if (index > Njuncs) - Tank[index - Njuncs].C = Node[index].C0; - break; - - case EN_SOURCEQUAL: - case EN_SOURCETYPE: - case EN_SOURCEPAT: - if (value < 0.0) - return (202); - source = Node[index].S; - if (source == NULL) { - source = (struct Ssource *)malloc(sizeof(struct Ssource)); - if (source == NULL) - return (101); - source->Type = CONCEN; - source->C0 = 0.0; - source->Pat = 0; - Node[index].S = source; - } - if (code == EN_SOURCEQUAL) { - source->C0 = value; - } else if (code == EN_SOURCEPAT) { - j = ROUND(value); - if (j < 0 || j > Npats) - return (205); - source->Pat = j; - } else // code == EN_SOURCETYPE - { - j = ROUND(value); - if (j < CONCEN || j > FLOWPACED) - return (251); - else - source->Type = (char)j; - } - return (0); - - case EN_TANKLEVEL: - if (index <= Njuncs) - return (251); - j = index - Njuncs; - if (Tank[j].A == 0.0) /* Tank is a reservoir */ - { - Tank[j].H0 = value / Ucf[ELEV]; - Tank[j].Hmin = Tank[j].H0; - Tank[j].Hmax = Tank[j].H0; - Node[index].El = Tank[j].H0; - hyd->NodeHead[index] = Tank[j].H0; - } else { - value = Node[index].El + value / Ucf[ELEV]; - if (value > Tank[j].Hmax || value < Tank[j].Hmin) - return (202); - Tank[j].H0 = value; - Tank[j].V0 = tankvolume(p, j, Tank[j].H0); - // Resetting Volume in addition to initial volume - Tank[j].V = Tank[j].V0; - hyd->NodeHead[index] = Tank[j].H0; - } - break; - - /*** New parameters added for retrieval begins here ***/ - /*** (Thanks to Nicolas Basile of Ecole Polytechnique ***/ - /*** de Montreal for suggesting some of these.) ***/ - - case EN_TANKDIAM: - if (value <= 0.0) - return (202); - if (index <= Njuncs) - return (251); - j = index - Njuncs; - if (j > 0 && Tank[j].A > 0.0) { - value /= Ucf[ELEV]; - Tank[j].A = PI * SQR(value) / 4.0; - Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); - Tank[j].V0 = tankvolume(p, j, Tank[j].H0); - Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); - } else { - return (251); - } - break; - - case EN_MINVOLUME: - if (value < 0.0) - return (202); - if (index <= Njuncs) - return (251); - j = index - Njuncs; - if (j > 0 && Tank[j].A > 0.0) { - Tank[j].Vmin = value / Ucf[VOLUME]; - Tank[j].V0 = tankvolume(p, j, Tank[j].H0); - Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); - } else { - return (251); - } - break; - - case EN_MINLEVEL: - if (value < 0.0) - return (202); - if (index <= Njuncs) - return (251); // not a tank or reservoir - j = index - Njuncs; - if (Tank[j].A == 0.0) - return (251); // node is a reservoir - Htmp = value / Ucf[ELEV] + Node[index].El; - if (Htmp < Tank[j].Hmax && Htmp <= Tank[j].H0) { - if (Tank[j].Vcurve > 0) - return (202); - Tank[j].Hmin = Htmp; - Tank[j].Vmin = (Tank[j].Hmin - Node[index].El) * Tank[j].A; - } else { - return (251); - } - break; - - case EN_MAXLEVEL: - if (value < 0.0) - return (202); - if (index <= Njuncs) - return (251); // not a tank or reservoir - j = index - Njuncs; - if (Tank[j].A == 0.0) - return (251); // node is a reservoir - Htmp = value / Ucf[ELEV] + Node[index].El; - if (Htmp > Tank[j].Hmin && Htmp >= Tank[j].H0) { - if (Tank[j].Vcurve > 0) - return (202); - Tank[j].Hmax = Htmp; - Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); - } else { - return (251); - } - break; - - case EN_MIXMODEL: - j = ROUND(value); - if (index <= Njuncs) - return (251); - if (j < MIX1 || j > LIFO) - return (202); - if (index > Njuncs && Tank[index - Njuncs].A > 0.0) { - Tank[index - Njuncs].MixModel = (char)j; - } else { - return (251); - } - break; - - case EN_MIXFRACTION: - if (value < 0.0 || value > 1.0) - return (202); - if (index <= Njuncs) - return (251); - j = index - Njuncs; - if (j > 0 && Tank[j].A > 0.0) { - Tank[j].V1max = value * Tank[j].Vmax; - } - break; - - case EN_TANK_KBULK: - if (index <= Njuncs) - return (251); - j = index - Njuncs; - if (j > 0 && Tank[j].A > 0.0) { - Tank[j].Kb = value / SECperDAY; - qu->Reactflag = 1; - } else { - return (251); - } - break; - - /*** New parameter additions ends here. ***/ - - default: - return (251); - } - return (0); -} - -int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid) -{ - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; - size_t n; - - // Check for valid arguments - if (index <= 0 || index > net->Nlinks) - { - return (204); - } - n = strlen(newid); - if (n < 1 || n > MAXID) return (211); - if (strcspn(newid, " ;") < n) return (211); - - // Check if another link with same name exists - if (hashtable_find(net->LinkHashTable, newid) > 0) - { - return (215); - } - - // Replace the existing link ID with the new value - hashtable_delete(net->LinkHashTable, net->Link[index].ID); - strncpy(net->Link[index].ID, newid, MAXID); - hashtable_insert(net->LinkHashTable, net->Link[index].ID, index); - return (0); -} - -int DLLEXPORT EN_setlinknodes(EN_ProjectHandle ph, int index, int node1, int node2) -{ - int type; - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; - - // Check that end and start nodes are not the same - if (node1 == node2) return (222); - - // Check that nodes exist - if (node1 < 0 || node1 > net->Nnodes) return (203); - if (node2 < 0 || node2 > net->Nnodes) return (203); - - // Check for illegal valve connection - type = net->Link[index].Type; - if (type == EN_PRV || type == EN_PSV || type == EN_FCV) - { - // Can't be connected to a fixed grade node - if (node1 > net->Njuncs || - node2 > net->Njuncs) return (219); - - // Can't be connected to another pressure/flow control valve - if (!valvecheck(p, type, node1, node2)) { - return (220); + errmsg(p, errcode); + return errcode; } } - // Assign new end nodes to link - net->Link[index].N1 = node1; - net->Link[index].N2 = node2; - return (0); + // Initialize hydraulics solver + inithyd(p, fflag); + if (p->report.Statflag > 0) writeheader(p, STATHDR, 0); + return errcode; } -int DLLEXPORT EN_setlinkvalue(EN_ProjectHandle ph, int index, int code, - EN_API_FLOAT_TYPE v) - +int DLLEXPORT EN_runH(EN_Project p, long *t) /*---------------------------------------------------------------- - ** Input: index = link index - ** code = link parameter code (see EPANET2.H) - ** v = parameter value - ** Output: none - ** Returns: error code - ** Purpose: sets input parameter value for a link - **---------------------------------------------------------------- - */ -{ - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - hydraulics_t *hyd = &p->hydraulics; - quality_t *qu = &p->quality; - - Slink *Link = net->Link; - - const int Nlinks = net->Nlinks; - - double *Ucf = p->Ucf; - double *LinkSetting = hyd->LinkSetting; - - char s; - double r, value = v; - - if (!p->Openflag) - return (102); - if (index <= 0 || index > Nlinks) - return (204); - switch (code) { - case EN_DIAMETER: - if (Link[index].Type != EN_PUMP) { - if (value <= 0.0) - return (202); - value /= Ucf[DIAM]; /* Convert to feet */ - r = Link[index].Diam / value; /* Ratio of old to new diam */ - Link[index].Km *= SQR(r) * SQR(r); /* Adjust minor loss factor */ - Link[index].Diam = value; /* Update diameter */ - resistcoeff(p, index); /* Update resistance coeff. */ - } - break; - - case EN_LENGTH: - if (Link[index].Type <= EN_PIPE) { - if (value <= 0.0) - return (202); - Link[index].Len = value / Ucf[ELEV]; - resistcoeff(p, index); - } - break; - - case EN_ROUGHNESS: - if (Link[index].Type <= EN_PIPE) { - if (value <= 0.0) - return (202); - Link[index].Kc = value; - if (hyd->Formflag == DW) - Link[index].Kc /= (1000.0 * Ucf[ELEV]); - resistcoeff(p, index); - } - break; - - case EN_MINORLOSS: - if (Link[index].Type != EN_PUMP) { - if (value <= 0.0) - return (202); - Link[index].Km = - 0.02517 * value / SQR(Link[index].Diam) / SQR(Link[index].Diam); - } - break; - - case EN_INITSTATUS: - case EN_STATUS: - /* Cannot set status for a check valve */ - if (Link[index].Type == EN_CVPIPE) - return (207); - s = (char)ROUND(value); - if (s < 0 || s > 1) - return (251); - if (code == EN_INITSTATUS) - setlinkstatus(p, index, s, &Link[index].Stat, &Link[index].Kc); - else - setlinkstatus(p, index, s, &hyd->LinkStatus[index], &LinkSetting[index]); - break; - - case EN_INITSETTING: - case EN_SETTING: - if (value < 0.0) - return (202); - if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) - return EN_setlinkvalue(p, index, EN_ROUGHNESS, v); - else { - switch (Link[index].Type) { - case EN_PUMP: - break; - case EN_PRV: - case EN_PSV: - case EN_PBV: - value /= Ucf[PRESSURE]; - break; - case EN_FCV: - value /= Ucf[FLOW]; - break; - case EN_TCV: - break; - - /*** Updated 9/7/00 ***/ - case EN_GPV: - return (202); /* Cannot modify setting for GPV */ - - default: - return (251); - } - if (code == EN_INITSETTING) - setlinksetting(p, index, value, &Link[index].Stat, &Link[index].Kc); - else - setlinksetting(p, index, value, &hyd->LinkStatus[index], - &LinkSetting[index]); - } - break; - - case EN_KBULK: - if (Link[index].Type <= EN_PIPE) { - Link[index].Kb = value / SECperDAY; - qu->Reactflag = 1; - } - break; - - case EN_KWALL: - if (Link[index].Type <= EN_PIPE) { - Link[index].Kw = value / SECperDAY; - qu->Reactflag = 1; - } - break; - - default: - return (251); - } - return (0); -} - -int DLLEXPORT EN_addpattern(EN_ProjectHandle ph, char *id) { - int i, j, n, err = 0; - Spattern *tmpPat; - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - parser_data_t *par = &p->parser; - hydraulics_t *hyd = &p->hydraulics; - Spattern *Pattern = net->Pattern; - - const int Npats = net->Npats; - - - /* Check if a pattern with same id already exists */ - - if (!p->Openflag) - return (102); - if (EN_getpatternindex(ph, id, &i) == 0) - return (215); - - /* Check that id name is not too long */ - - if (strlen(id) > MAXID) - return (250); - - /* Allocate memory for a new array of patterns */ - - n = Npats + 1; - tmpPat = (Spattern *)calloc(n + 1, sizeof(Spattern)); - if (tmpPat == NULL) - return (101); - - /* Copy contents of old pattern array to new one */ - - for (i = 0; i <= net->Npats; i++) { - strcpy(tmpPat[i].ID, Pattern[i].ID); - tmpPat[i].Length = Pattern[i].Length; - tmpPat[i].F = (double *)calloc(Pattern[i].Length, sizeof(double)); - if (tmpPat[i].F == NULL) - err = 1; - else - for (j = 0; j < Pattern[i].Length; j++) - tmpPat[i].F[j] = Pattern[i].F[j]; - } - - /* Add the new pattern to the new array of patterns */ - - strcpy(tmpPat[n].ID, id); - tmpPat[n].Length = 1; - tmpPat[n].F = (double *)calloc(tmpPat[n].Length, sizeof(double)); - if (tmpPat[n].F == NULL) - err = 1; - else - tmpPat[n].F[0] = 1.0; - - /* Abort if memory allocation error */ - - if (err) { - for (i = 0; i <= n; i++) - if (tmpPat[i].F) - free(tmpPat[i].F); - free(tmpPat); - return (101); - } - - // Replace old pattern array with new one - - for (i = 0; i <= Npats; i++) - free(Pattern[i].F); - free(Pattern); - net->Pattern = tmpPat; - net->Npats = n; - par->MaxPats = n; - - if (strcmp(id, par->DefPatID) == 0) { - hyd->DefPat = n; - } - return (0); -} - -int DLLEXPORT EN_setpattern(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *f, int n) { - int j; - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - Spattern *Pattern = net->Pattern; - const int Npats = net->Npats; - - - /* Check for valid arguments */ - if (!p->Openflag) - return (102); - if (index <= 0 || index > Npats) - return (205); - if (n <= 0) - return (202); - - /* Re-set number of time periods & reallocate memory for multipliers */ - Pattern[index].Length = n; - Pattern[index].F = (double *)realloc(Pattern[index].F, n * sizeof(double)); - if (Pattern[index].F == NULL) - return (101); - - /* Load multipliers into pattern */ - for (j = 0; j < n; j++) - Pattern[index].F[j] = f[j]; - return (0); -} - -int DLLEXPORT EN_setpatternvalue(EN_ProjectHandle ph, int index, int period, EN_API_FLOAT_TYPE value) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - Spattern *Pattern = net->Pattern; - - const int Npats = net->Npats; - - - if (!p->Openflag) - return (102); - if (index <= 0 || index > Npats) - return (205); - if (period <= 0 || period > Pattern[index].Length) - return (251); - Pattern[index].F[period - 1] = value; - return (0); -} - -int DLLEXPORT EN_addcurve(EN_ProjectHandle ph, char *id) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - parser_data_t *par = &p->parser; - Scurve *Curve = net->Curve; - - int i, j, n, err = 0; - Scurve *tmpCur; - - /* Check if a curve with same id already exists */ - - if (!p->Openflag) - return (102); - if (EN_getcurveindex(p, id, &i) == 0) - return (215); - - /* Check that id name is not too long */ - - if (strlen(id) > MAXID) - return (250); - - /* Allocate memory for a new array of curves */ - - n = net->Ncurves + 1; - tmpCur = (Scurve *)calloc(n + 1, sizeof(Scurve)); - if (tmpCur == NULL) - return (101); - - /* Copy contents of old curve array to new one */ - - for (i = 0; i <= net->Ncurves; i++) { - strcpy(tmpCur[i].ID, net->Curve[i].ID); - tmpCur[i].Npts = Curve[i].Npts; - tmpCur[i].X = (double *)calloc(net->Curve[i].Npts, sizeof(double)); - tmpCur[i].Y = (double *)calloc(net->Curve[i].Npts, sizeof(double)); - if (tmpCur[i].X == NULL) - err = 1; - else if (tmpCur[i].Y == NULL) - err = 1; - else - for (j = 0; j < Curve[i].Npts; j++) { - tmpCur[i].X[j] = net->Curve[i].X[j]; - tmpCur[i].Y[j] = net->Curve[i].Y[j]; - } - } - - /* Add the new Curve to the new array of curves */ - - strcpy(tmpCur[n].ID, id); - tmpCur[n].Npts = 1; - tmpCur[n].Type = G_CURVE; - tmpCur[n].X = (double *)calloc(tmpCur[n].Npts, sizeof(double)); - tmpCur[n].Y = (double *)calloc(tmpCur[n].Npts, sizeof(double)); - if (tmpCur[n].X == NULL) - err = 1; - else if (tmpCur[n].Y == NULL) - err = 1; - else { - tmpCur[n].X[0] = 1.0; - tmpCur[n].Y[0] = 1.0; - } - - /* Abort if memory allocation error */ - - if (err) { - for (i = 0; i <= n; i++) { - if (tmpCur[i].X) - free(tmpCur[i].X); - if (tmpCur[i].Y) - free(tmpCur[i].Y); - } - free(tmpCur); - return (101); - } - - // Replace old curves array with new one - - for (i = 0; i <= net->Ncurves; i++) { - free(net->Curve[i].X); - free(net->Curve[i].Y); - } - free(net->Curve); - net->Curve = tmpCur; - net->Ncurves = n; - par->MaxCurves = n; - return (0); -} - -int DLLEXPORT EN_setcurve(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int n) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - Scurve *Curve = net->Curve; - int j; - - /* Check for valid arguments */ - if (!p->Openflag) - return (102); - if (index <= 0 || index > net->Ncurves) - return (206); - if (n <= 0) - return (202); - - /* Re-set number of points & reallocate memory for values */ - Curve[index].Npts = n; - Curve[index].X = (double *)realloc(Curve[index].X, n * sizeof(double)); - Curve[index].Y = (double *)realloc(Curve[index].Y, n * sizeof(double)); - if (Curve[index].X == NULL) - return (101); - if (Curve[index].Y == NULL) - return (101); - - /* Load values into curve */ - for (j = 0; j < n; j++) { - Curve[index].X[j] = x[j]; - Curve[index].Y[j] = y[j]; - } - return (0); -} - -int DLLEXPORT EN_setcurvevalue(EN_ProjectHandle ph, int index, int pnt, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - Scurve *Curve = net->Curve; - const int Ncurves = net->Ncurves; - - if (!p->Openflag) - return (102); - if (index <= 0 || index > Ncurves) - return (206); - if (pnt <= 0 || pnt > Curve[index].Npts) - return (251); - Curve[index].X[pnt - 1] = x; - Curve[index].Y[pnt - 1] = y; - return (0); -} - -int DLLEXPORT EN_settimeparam(EN_ProjectHandle ph, int code, long value) -{ - EN_Project *p = (EN_Project*)ph; - - report_options_t *rep = &p->report; - quality_t *qu = &p->quality; - time_options_t *time = &p->time_options; - - if (!p->Openflag) - return (102); - if (p->hydraulics.OpenHflag || p->quality.OpenQflag) { - // --> there's nothing wrong with changing certain time parameters during a - // simulation run, or before the run has started. - // todo -- how to tell? - /* - if (code == EN_DURATION || code == EN_HTIME || code == EN_REPORTSTEP || - code == EN_DURATION || Htime == 0) { - // it's ok - } - else { - return(109); - } - */ - } - if (value < 0) - return (202); - switch (code) { - case EN_DURATION: - time->Dur = value; - if (time->Rstart > time->Dur) - time->Rstart = 0; - break; - - case EN_HYDSTEP: - if (value == 0) - return (202); - time->Hstep = value; - time->Hstep = MIN(time->Pstep, time->Hstep); - time->Hstep = MIN(time->Rstep, time->Hstep); - qu->Qstep = MIN(qu->Qstep, time->Hstep); - break; - - case EN_QUALSTEP: - if (value == 0) - return (202); - qu->Qstep = value; - qu->Qstep = MIN(qu->Qstep, time->Hstep); - break; - - case EN_PATTERNSTEP: - if (value == 0) - return (202); - time->Pstep = value; - if (time->Hstep > time->Pstep) - time->Hstep = time->Pstep; - break; - - case EN_PATTERNSTART: - time->Pstart = value; - break; - - case EN_REPORTSTEP: - if (value == 0) - return (202); - time->Rstep = value; - if (time->Hstep > time->Rstep) - time->Hstep = time->Rstep; - break; - - case EN_REPORTSTART: - if (time->Rstart > time->Dur) - return (202); - time->Rstart = value; - break; - - case EN_RULESTEP: - if (value == 0) - return (202); - time->Rulestep = value; - time->Rulestep = MIN(time->Rulestep, time->Hstep); - break; - - case EN_STATISTIC: - if (value > RANGE) - return (202); - rep->Tstatflag = (char)value; - break; - - case EN_HTIME: - time->Htime = value; - break; - - case EN_QTIME: - qu->Qtime = value; - break; - - default: - return (251); - } - return (0); -} - -int DLLEXPORT EN_setoption(EN_ProjectHandle ph, int code, EN_API_FLOAT_TYPE v) -/*---------------------------------------------------------------- - ** Input: code = option code (see EPANET2.H) - ** v = option value - ** Output: none - ** Returns: error code - ** Purpose: sets value for an analysis option - **---------------------------------------------------------------- - */ -{ - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - hydraulics_t *hyd = &p->hydraulics; - quality_t *qu = &p->quality; - - Snode *Node = net->Node; - const int Njuncs = net->Njuncs; - - double *Ucf = p->Ucf; - - int i, j; - int tmpPat, error; - char tmpId[MAXID+1]; - Pdemand demand; /* Pointer to demand record */ - double Ke, n, ucf, value = v; - if (!p->Openflag) - return (102); - switch (code) { - case EN_TRIALS: - if (value < 1.0) - return (202); - hyd->MaxIter = (int)value; - break; - case EN_ACCURACY: - if (value < 1.e-5 || value > 1.e-1) - return (202); - hyd->Hacc = value; - break; - case EN_TOLERANCE: - if (value < 0.0) - return (202); - qu->Ctol = value / Ucf[QUALITY]; - break; - case EN_EMITEXPON: - if (value <= 0.0) - return (202); - n = 1.0 / value; - ucf = pow(Ucf[FLOW], n) / Ucf[PRESSURE]; - for (i = 1; i <= Njuncs; i++) { - j = EN_getnodevalue(p, i, EN_EMITTER, &v); - Ke = v; - if (j == 0 && Ke > 0.0) - Node[i].Ke = ucf / pow(Ke, n); - } - hyd->Qexp = n; - break; - case EN_DEMANDMULT: - if (value <= 0.0) - return (202); - hyd->Dmult = value; - break; - case EN_HEADERROR: - if (value < 0.0) - return (202); - hyd->HeadErrorLimit = value / Ucf[HEAD]; - break; - case EN_FLOWCHANGE: - if (value < 0.0) - return (202); - hyd->FlowChangeLimit = value / Ucf[FLOW]; - break; - case EN_DEMANDDEFPAT: - //check that the pattern exists or is set to zero to delete the default pattern - if (value < 0 || value > net->Npats) - return (205); - tmpPat = hyd->DefPat; - //get the new pattern ID - if (value == 0) - { - strncpy(tmpId, "1", MAXID); // should be DEFPATID - } - else - { - error = EN_getpatternid(p, (int)value, tmpId); - if (error != 0) - return (error); - } - // replace node patterns for default - for (i = 1; i <= net->Nnodes; i++) { - Snode *node = &net->Node[i]; - for (demand = node->D; demand != NULL; demand = demand->next) { - if (demand->Pat == tmpPat) { - demand->Pat = (int)value; - strcpy(demand->Name, ""); - } - } - } - strncpy(p->parser.DefPatID, tmpId, MAXID); - hyd->DefPat = (int)value; - break; - - default: - return (251); - } - return (0); -} - -int DLLEXPORT EN_setstatusreport(EN_ProjectHandle ph, int code) { - int errcode = 0; - - EN_Project *p = (EN_Project*)ph; - - if (code >= EN_NO_REPORT && code <= EN_FULL_REPORT) - p->report.Statflag = (char)code; - else - errcode = 202; - - return (errcode); -} - -int DLLEXPORT EN_setqualtype(EN_ProjectHandle ph, int qualcode, char *chemname, char *chemunits, char *tracenode) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - report_options_t *rep = &p->report; - quality_t *qu = &p->quality; - - double *Ucf = p->Ucf; - int i; - - /*** Updated 3/1/01 ***/ - double ccf = 1.0; - - if (!p->Openflag) - return (102); - if (qualcode < EN_NONE || qualcode > EN_TRACE) - return (251); - qu->Qualflag = (char)qualcode; - qu->Ctol *= Ucf[QUALITY]; - if (qu->Qualflag == CHEM) /* Chemical constituent */ - { - strncpy(qu->ChemName, chemname, MAXID); - strncpy(qu->ChemUnits, chemunits, MAXID); - - /*** Updated 3/1/01 ***/ - strncpy(rep->Field[QUALITY].Units, qu->ChemUnits, MAXID); - strncpy(rep->Field[REACTRATE].Units, qu->ChemUnits, MAXID); - strcat(rep->Field[REACTRATE].Units, t_PERDAY); - ccf = 1.0 / LperFT3; - } - if (qu->Qualflag == TRACE) /* Source tracing option */ - { - qu->TraceNode = findnode(net,tracenode); - if (qu->TraceNode == 0) - return (203); - strncpy(qu->ChemName, u_PERCENT, MAXID); - strncpy(qu->ChemUnits, tracenode, MAXID); - - /*** Updated 3/1/01 ***/ - strcpy(rep->Field[QUALITY].Units, u_PERCENT); - } - if (qu->Qualflag == AGE) /* Water age analysis */ - { - strncpy(qu->ChemName, w_AGE, MAXID); - strncpy(qu->ChemUnits, u_HOURS, MAXID); - - /*** Updated 3/1/01 ***/ - strcpy(rep->Field[QUALITY].Units, u_HOURS); - } - - /* when changing from CHEM to AGE or TRACE, nodes initial quality values must be returned to their original ones */ - if ((qu->Qualflag == AGE || qu->Qualflag == TRACE) & (Ucf[QUALITY] != 1)) { - for (i=1; i<=p->network.Nnodes; i++) { - p->network.Node[i].C0 *= Ucf[QUALITY]; - } - } - - /*** Updated 3/1/01 ***/ - Ucf[QUALITY] = ccf; - Ucf[LINKQUAL] = ccf; - Ucf[REACTRATE] = ccf; - qu->Ctol /= Ucf[QUALITY]; - - return (0); -} - -int DLLEXPORT EN_getheadcurveindex(EN_ProjectHandle ph, int index, int *curveindex) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - Slink *Link = net->Link; - Spump *Pump = net->Pump; - const int Nlinks = net->Nlinks; - - if (!p->Openflag) - return (102); - if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) - return (204); - *curveindex = Pump[findpump(net, index)].Hcurve; - - return (0); -} - -int DLLEXPORT EN_setheadcurveindex(EN_ProjectHandle ph, int index, int curveindex) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - Slink *Link = net->Link; - const int Nlinks = net->Nlinks; - const int Ncurves = net->Ncurves; - - double *Ucf = p->Ucf; - int pIdx; - Spump *pump; - - if (!p->Openflag) - return (102); - if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) { - return (204); - } - if (curveindex <= 0 || curveindex > Ncurves) { - return (206); - } - pIdx = findpump(net, index); - pump = &p->network.Pump[pIdx]; - pump->Ptype = NOCURVE; - pump->Hcurve = curveindex; - // update pump parameters - updatepumpparams(p, pIdx); - // convert units - if (pump->Ptype == POWER_FUNC) { - pump->H0 /= Ucf[HEAD]; - pump->R *= (pow(Ucf[FLOW], pump->N) / Ucf[HEAD]); - } - /* Convert flow range & max. head units */ - pump->Q0 /= Ucf[FLOW]; - pump->Qmax /= Ucf[FLOW]; - pump->Hmax /= Ucf[HEAD]; - - p->network.Curve[curveindex].Type = P_CURVE; - - return (0); -} - -int DLLEXPORT EN_getpumptype(EN_ProjectHandle ph, int index, int *type) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - Slink *Link = net->Link; - Spump *Pump = net->Pump; - const int Nlinks = net->Nlinks; - - *type = -1; - if (!p->Openflag) - return (102); - if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) - return (204); - *type = Pump[findpump(&p->network, index)].Ptype; - return (0); -} - -int DLLEXPORT EN_getcurvetype(EN_ProjectHandle ph, int curveindex, int *type) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - if (!p->Openflag) - return (102); - if (curveindex < 1 || curveindex > net->Ncurves) - return (206); - *type = net->Curve[curveindex].Type; - - return (0); -} - -/* ----------------------------------------------------------------- - Functions for opening files ----------------------------------------------------------------- -*/ - -int openfiles(EN_Project *p, const char *f1, const char *f2, const char *f3) -/*---------------------------------------------------------------- -** Input: f1 = pointer to name of input file -** f2 = pointer to name of report file -** f3 = pointer to name of binary output file -** Output: none +** Input: none +** Output: t = elapsed time (sec) ** Returns: error code -** Purpose: opens input & report files +** Purpose: solves network hydraulics at current time point **---------------------------------------------------------------- */ { + int errcode; - out_file_t *out = &p->out_files; - report_options_t *rep = &p->report; - parser_data_t *par = &p->parser; - - /* Initialize file pointers to NULL */ - par->InFile = NULL; - rep->RptFile = NULL; - out->OutFile = NULL; - out->HydFile = NULL; - - /* Save file names */ - strncpy(par->InpFname, f1, MAXFNAME); - strncpy(rep->Rpt1Fname, f2, MAXFNAME); - strncpy(out->OutFname, f3, MAXFNAME); - if (strlen(f3) > 0) - out->Outflag = SAVE; - else - out->Outflag = SCRATCH; - - /* Check that file names are not identical */ - if (strcomp(f1, f2) || strcomp(f1, f3) || - (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) { - return 301; - } - - /* Attempt to open input and report files */ - if (strlen(f1) > 0) - { - if ((par->InFile = fopen(f1, "rt")) == NULL) return 302; - } - if (strlen(f2) == 0) - rep->RptFile = stdout; - else if ((rep->RptFile = fopen(f2, "wt")) == NULL) { - return 303; - } - - return 0; -} /* End of openfiles */ - -int openhydfile(EN_Project *p) -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Returns: error code -** Purpose: opens file that saves hydraulics solution -**---------------------------------------------------------------- -*/ -{ - - EN_Network *net = &p->network; - out_file_t *out = &p->out_files; - time_options_t *time = &p->time_options; - - const int Nnodes = net->Nnodes; - const int Ntanks = net->Ntanks; - const int Nlinks = net->Nlinks; - const int Nvalves = net->Nvalves; - const int Npumps = net->Npumps; - - INT4 nsize[6]; /* Temporary array */ - INT4 magic; - INT4 version; - int errcode = 0; - - /* If HydFile currently open, then close it if its not a scratch file */ - if (out->HydFile != NULL) { - if (out->Hydflag == SCRATCH) - return (0); - fclose(out->HydFile); - } - - /* Use Hydflag to determine the type of hydraulics file to use. */ - /* Write error message if the file cannot be opened. */ - out->HydFile = NULL; - switch (out->Hydflag) { - case SCRATCH: - strcpy(out->HydFname, p->TmpHydFname); - out->HydFile = fopen(out->HydFname, "w+b"); - break; - case SAVE: - out->HydFile = fopen(out->HydFname, "w+b"); - break; - case USE: - out->HydFile = fopen(out->HydFname, "rb"); - break; - } - if (out->HydFile == NULL) - return (305); - - /* If a previous hydraulics solution is not being used, then */ - /* save the current network size parameters to the file. */ - if (out->Hydflag != USE) { - magic = MAGICNUMBER; - version = ENGINE_VERSION; - nsize[0] = Nnodes; - nsize[1] = Nlinks; - nsize[2] = Ntanks; - nsize[3] = Npumps; - nsize[4] = Nvalves; - nsize[5] = (int)time->Dur; - fwrite(&magic, sizeof(INT4), 1, out->HydFile); - fwrite(&version, sizeof(INT4), 1, out->HydFile); - fwrite(nsize, sizeof(INT4), 6, out->HydFile); - } - - /* If a previous hydraulics solution is being used, then */ - /* make sure its network size parameters match those of */ - /* the current network. */ - if (out->Hydflag == USE) { - fread(&magic, sizeof(INT4), 1, out->HydFile); - if (magic != MAGICNUMBER) - return (306); - fread(&version, sizeof(INT4), 1, out->HydFile); - if (version != ENGINE_VERSION) - return (306); - if (fread(nsize, sizeof(INT4), 6, out->HydFile) < 6) - return (306); - if (nsize[0] != Nnodes || nsize[1] != Nlinks || nsize[2] != Ntanks || - nsize[3] != Npumps || nsize[4] != Nvalves || nsize[5] != time->Dur) - return (306); - p->save_options.SaveHflag = TRUE; - } - - /* Save current position in hydraulics file */ - /* where storage of hydraulic results begins */ - out->HydOffset = ftell(out->HydFile); - - return errcode; + *t = 0; + if (!p->hydraul.OpenHflag) return 103; + errcode = runhyd(p, t); + if (errcode) errmsg(p, errcode); + return errcode; } -int openoutfile(EN_Project *p) +int DLLEXPORT EN_nextH(EN_Project p, long *tstep) +/*---------------------------------------------------------------- +** Input: none +** Output: tstep = next hydraulic time step to take (sec) +** Returns: error code +** Purpose: determines the time step until the next hydraulic event +**---------------------------------------------------------------- +*/ +{ + int errcode; + + *tstep = 0; + if (!p->hydraul.OpenHflag) return 103; + errcode = nexthyd(p, tstep); + if (errcode) errmsg(p, errcode); + else if (p->outfile.Saveflag && *tstep == 0) p->outfile.SaveHflag = TRUE; + return errcode; +} + +int DLLEXPORT EN_closeH(EN_Project p) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Returns: error code -** Purpose: opens binary output file. +** Purpose: closes EPANET's hydraulic solver +**---------------------------------------------------------------- +*/ +{ + if (!p->Openflag) return 102; + if (p->hydraul.OpenHflag) closehyd(p); + p->hydraul.OpenHflag = FALSE; + return 0; +} + +int DLLEXPORT EN_savehydfile(EN_Project p, char *filename) +/*---------------------------------------------------------------- +** Input: filename = name of file to which hydraulic results are saved +** Output: none +** Returns: error code +** Purpose: saves results from a scratch hydraulics file to a +** permanent one. +**---------------------------------------------------------------- +*/ +{ + FILE *f; + FILE *HydFile; + int c; + + // Check that hydraulics results exist + if (p->outfile.HydFile == NULL || !p->outfile.SaveHflag) return 104; + + // Open the permanent hydraulics file + if ((f = fopen(filename, "w+b")) == NULL) return 305; + + // Copy from the scratch file to f + HydFile = p->outfile.HydFile; + fseek(HydFile, 0, SEEK_SET); + while ((c = fgetc(HydFile)) != EOF) fputc(c, f); + fclose(f); + return 0; +} + +int DLLEXPORT EN_usehydfile(EN_Project p, char *filename) +/*---------------------------------------------------------------- +** Input: filename = name of previously saved hydraulics file +** Output: none +** Returns: error code +** Purpose: uses contents of a previous saved hydraulics file to +** run a water quality analysis. +**---------------------------------------------------------------- +*/ +{ + int errcode; + + // Check that project was opened & hydraulic solver is closed + if (!p->Openflag) return 102; + if (p->hydraul.OpenHflag) return 108; + + // Try to open hydraulics file + strncpy(p->outfile.HydFname, filename, MAXFNAME); + p->outfile.Hydflag = USE; + p->outfile.SaveHflag = TRUE; + errcode = openhydfile(p); + + // If error, then reset flags + if (errcode) + { + strcpy(p->outfile.HydFname, ""); + p->outfile.Hydflag = SCRATCH; + p->outfile.SaveHflag = FALSE; + } + return errcode; +} + +/******************************************************************** + + Water Quality Analysis Functions + + ********************************************************************/ + +int DLLEXPORT EN_solveQ(EN_Project p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: solves for network water quality in all time periods +**---------------------------------------------------------------- +*/ +{ + int errcode; + long t, tstep; + + // Open WQ solver + errcode = EN_openQ(p); + if (!errcode) + { + // Initialize WQ solver + errcode = EN_initQ(p, EN_SAVE); + if (!p->quality.Qualflag) writewin(p->viewprog, FMT106); + + // Analyze each hydraulic period + if (!errcode) do + { + // Display progress message + sprintf(p->Msg, "%-10s", + clocktime(p->report.Atime, p->times.Htime)); + if (p->quality.Qualflag) + { + sprintf(p->Msg, FMT102, p->report.Atime); + writewin(p->viewprog, p->Msg); + } + + // Retrieve current hydraulic results & update water quality + // to start of next time period + tstep = 0; + ERRCODE(EN_runQ(p, &t)); + ERRCODE(EN_nextQ(p, &tstep)); + } while (tstep > 0); + } + + // Close WQ solver + EN_closeQ(p); + return errcode; +} + +int DLLEXPORT EN_openQ(EN_Project p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: opens EPANET's water quality solver **---------------------------------------------------------------- */ { int errcode = 0; - out_file_t *out = &p->out_files; - report_options_t *rep = &p->report; + // Check that hydraulics results exist + p->quality.OpenQflag = FALSE; + p->outfile.SaveQflag = FALSE; + if (!p->Openflag) return 102; + if (!p->hydraul.OpenHflag && !p->outfile.SaveHflag) return 104; - // Close output file if already opened - if (out->OutFile != NULL) fclose(out->OutFile); - out->OutFile = NULL; - if (out->TmpOutFile != NULL) fclose(out->TmpOutFile); - out->TmpOutFile = NULL; + // Open water quality solver + ERRCODE(openqual(p)); + if (!errcode) p->quality.OpenQflag = TRUE; + else errmsg(p, errcode); + return errcode; +} - // If output file name was supplied, then attempt to - // open it. Otherwise open a temporary output file. - if (out->Outflag == SAVE) +int DLLEXPORT EN_initQ(EN_Project p, int saveflag) +/*---------------------------------------------------------------- +** Input: saveflag = flag indicating if results should be saved +** to the binary output file or not +** Output: none +** Returns: error code +** Purpose: initializes the water quality solver +**---------------------------------------------------------------- +*/ +{ + int errcode = 0; + + if (!p->quality.OpenQflag) return 105; + initqual(p); + p->outfile.SaveQflag = FALSE; + p->outfile.Saveflag = FALSE; + if (saveflag) { - if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) errcode = 304; - } - - else - { - strcpy(out->OutFname, p->TmpOutFname); - if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) errcode = 304; - } - - // Save basic network data & energy usage results - ERRCODE(savenetdata(p)); - out->OutOffset1 = ftell(out->OutFile); - ERRCODE(saveenergy(p)); - out->OutOffset2 = ftell(out->OutFile); - - // Open temporary file if computing time series statistic - if (!errcode) - { - if (rep->Tstatflag != SERIES) - { - out->TmpOutFile = fopen(p->TmpStatFname, "w+b"); - if (out->TmpOutFile == NULL) errcode = 304; - } - else out->TmpOutFile = out->OutFile; + errcode = openoutfile(p); + if (!errcode) p->outfile.Saveflag = TRUE; } return errcode; } -/* ----------------------------------------------------------------- - Global memory management functions ----------------------------------------------------------------- -*/ - -void initpointers(EN_Project *p) +int DLLEXPORT EN_runQ(EN_Project p, long *t) /*---------------------------------------------------------------- ** Input: none -** Output: none -** Purpose: initializes global pointers to NULL -**---------------------------------------------------------------- -*/ -{ - - hydraulics_t *hyd = &p->hydraulics; - quality_t *q = &p->quality; - EN_Network *n = &p->network; - parser_data_t *pars = &p->parser; - solver_t *s = &p->hydraulics.solver; - - hyd->NodeDemand = NULL; - q->NodeQual = NULL; - hyd->NodeHead = NULL; - hyd->LinkFlows = NULL; - q->PipeRateCoeff = NULL; - hyd->LinkStatus = NULL; - hyd->LinkSetting = NULL; - hyd->OldStat = NULL; - - n->Node = NULL; - n->Link = NULL; - n->Tank = NULL; - n->Pump = NULL; - n->Valve = NULL; - n->Pattern = NULL; - n->Curve = NULL; - n->Control = NULL; - n->Coord = NULL; - - hyd->X_tmp = NULL; - - pars->Patlist = NULL; - pars->Curvelist = NULL; - n->Adjlist = NULL; - - s->Aii = NULL; - s->Aij = NULL; - s->F = NULL; - s->P = NULL; - s->Y = NULL; - s->Order = NULL; - s->Row = NULL; - s->Ndx = NULL; - s->XLNZ = NULL; - s->NZSUB = NULL; - s->LNZ = NULL; - - n->NodeHashTable = NULL; - n->LinkHashTable = NULL; - initrules(p); -} - -int allocdata(EN_Project *p) -/*---------------------------------------------------------------- -** Input: none -** Output: none +** Output: t = current simulation time (sec) ** Returns: error code -** Purpose: allocates memory for network data structures +** Purpose: retrieves current hydraulic results (at time t) +** and saves current results to file. **---------------------------------------------------------------- */ { - int n; - int errcode = 0; - EN_Network *net; - hydraulics_t *hyd; - quality_t *qu; - parser_data_t *par; - - /* Allocate node & link ID hash tables */ - p->network.NodeHashTable = hashtable_create(); - p->network.LinkHashTable = hashtable_create(); - ERRCODE(MEMCHECK(p->network.NodeHashTable)); - ERRCODE(MEMCHECK(p->network.LinkHashTable)); - - net = &p->network; - hyd = &p->hydraulics; - qu = &p->quality; - par = &p->parser; - - /* Allocate memory for network nodes */ - /************************************************************* - NOTE: Because network components of a given type are indexed - starting from 1, their arrays must be sized 1 - element larger than the number of components. - *************************************************************/ - if (!errcode) { - n = par->MaxNodes + 1; - net->Node = (Snode *)calloc(n, sizeof(Snode)); - hyd->NodeDemand = (double *)calloc(n, sizeof(double)); - qu->NodeQual = (double *)calloc(n, sizeof(double)); - hyd->NodeHead = (double *)calloc(n, sizeof(double)); - ERRCODE(MEMCHECK(net->Node)); - ERRCODE(MEMCHECK(hyd->NodeDemand)); - ERRCODE(MEMCHECK(qu->NodeQual)); - ERRCODE(MEMCHECK(hyd->NodeHead)); - } - - /* Allocate memory for network links */ - if (!errcode) { - n = par->MaxLinks + 1; - net->Link = (Slink *)calloc(n, sizeof(Slink)); - hyd->LinkFlows = (double *)calloc(n, sizeof(double)); - hyd->LinkSetting = (double *)calloc(n, sizeof(double)); - hyd->LinkStatus = (StatType *)calloc(n, sizeof(StatType)); - ERRCODE(MEMCHECK(net->Link)); - ERRCODE(MEMCHECK(hyd->LinkFlows)); - ERRCODE(MEMCHECK(hyd->LinkSetting)); - ERRCODE(MEMCHECK(hyd->LinkStatus)); - } - - /* Allocate memory for tanks, sources, pumps, valves, */ - /* controls, demands, time patterns, & operating curves */ - if (!errcode) { - net->Tank = (Stank *)calloc(par->MaxTanks + 1, sizeof(Stank)); - net->Pump = (Spump *)calloc(par->MaxPumps + 1, sizeof(Spump)); - net->Valve = (Svalve *)calloc(par->MaxValves + 1, sizeof(Svalve)); - net->Control = (Scontrol *)calloc(par->MaxControls + 1, sizeof(Scontrol)); - net->Pattern = (Spattern *)calloc(par->MaxPats + 1, sizeof(Spattern)); - net->Curve = (Scurve *)calloc(par->MaxCurves + 1, sizeof(Scurve)); - if (p->parser.Coordflag == TRUE) { - net->Coord = (Scoord *)calloc(par->MaxNodes + 1, sizeof(Scoord)); - } - ERRCODE(MEMCHECK(net->Tank)); - ERRCODE(MEMCHECK(net->Pump)); - ERRCODE(MEMCHECK(net->Valve)); - ERRCODE(MEMCHECK(net->Control)); - ERRCODE(MEMCHECK(net->Pattern)); - ERRCODE(MEMCHECK(net->Curve)); - if (p->parser.Coordflag == TRUE) - ERRCODE(MEMCHECK(net->Coord)); - } - - /* Initialize pointers used in patterns, curves, and demand category lists */ - if (!errcode) { - for (n = 0; n <= par->MaxPats; n++) { - net->Pattern[n].Length = 0; - net->Pattern[n].F = NULL; - } - for (n = 0; n <= par->MaxCurves; n++) { - net->Curve[n].Npts = 0; - net->Curve[n].Type = G_CURVE; - net->Curve[n].X = NULL; - net->Curve[n].Y = NULL; - } - - for (n = 0; n <= par->MaxNodes; n++) { - // node demand - net->Node[n].D = NULL; - /* ini coord data */ - if (p->parser.Coordflag == TRUE) { - net->Coord[n].X = 0; - net->Coord[n].Y = 0; - net->Coord[n].HaveCoords = FALSE; - } - } - } - - /* Allocate memory for rule base (see RULES.C) */ - if (!errcode) - errcode = allocrules(p); - - return errcode; -} /* End of allocdata */ - -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(EN_Project *p) -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: frees memory allocated for network data structures. -**---------------------------------------------------------------- -*/ -{ - - EN_Network *net = &p->network; - hydraulics_t *hyd = &p->hydraulics; - quality_t *qu = &p->quality; - parser_data_t *par = &p->parser; - - int j; - Pdemand demand, nextdemand; - Psource source; - - /* Free memory for computed results */ - free(hyd->NodeDemand); - free(qu->NodeQual); - free(hyd->NodeHead); - free(hyd->LinkFlows); - free(hyd->LinkSetting); - free(hyd->LinkStatus); - - /* Free memory for node data */ - if (net->Node != NULL) { - for (j = 0; j <= par->MaxNodes; j++) { - /* Free memory used for demand category list */ - demand = net->Node[j].D; - while (demand != NULL) { - nextdemand = demand->next; - free(demand); - demand = nextdemand; - } - /* Free memory used for WQ source data */ - source = net->Node[j].S; - if (source != NULL) - free(source); - } - free(net->Node); - } - - /* Free memory for other network objects */ - free(net->Link); - free(net->Tank); - free(net->Pump); - free(net->Valve); - free(net->Control); - - /* Free memory for time patterns */ - if (net->Pattern != NULL) { - for (j = 0; j <= par->MaxPats; j++) - free(net->Pattern[j].F); - free(net->Pattern); - } - - /* Free memory for curves */ - if (net->Curve != NULL) { - for (j = 0; j <= par->MaxCurves; j++) { - free(net->Curve[j].X); - free(net->Curve[j].Y); - } - free(net->Curve); - } - - /* Free memory for node coordinates */ - if (p->parser.Coordflag == TRUE) { - free(net->Coord); - } - - /* Free memory for rule base (see RULES.C) */ - freerules(p); - - /* Free hash table memory */ - if (net->NodeHashTable != NULL) hashtable_free(net->NodeHashTable); - - if (net->LinkHashTable != NULL) hashtable_free(net->LinkHashTable); -} - -/* ----------------------------------------------------------------- - General purpose functions ----------------------------------------------------------------- -*/ - -char *getTmpName(char *fname) -// -// Input: fname = file name string -// Output: returns pointer to file name -// Purpose: creates a temporary file name with path prepended to it. -// -{ - -#ifdef _WIN32 - - char* name = NULL; - - // --- use Windows _tempnam function to get a pointer to an - // unused file name that begins with "en" - name = _tempnam(NULL, "en"); - if (name == NULL) return NULL; - - // --- copy the file name to fname - if (strlen(name) < MAXFNAME) strncpy(fname, name, MAXFNAME); - else fname = NULL; - - // --- 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 - strcpy(fname, "enXXXXXX"); - mkstemp(fname); -#endif - return fname; -} - -int strcomp(const char *s1, const char *s2) -/*--------------------------------------------------------------- -** Input: s1 = character string -** s2 = character string -** Output: none -** Returns: 1 if s1 is same as s2, 0 otherwise -** Purpose: case insensitive comparison of strings s1 & s2 -**--------------------------------------------------------------- -*/ -{ - int i; - for (i = 0; UCHAR(s1[i]) == UCHAR(s2[i]); i++) - if (!s1[i + 1] && !s2[i + 1]) - return (1); - return (0); -} /* End of strcomp */ - -double interp(int n, double x[], double y[], double xx) -/*---------------------------------------------------------------- -** Input: n = number of data pairs defining a curve -** x = x-data values of curve -** y = y-data values of curve -** xx = specified x-value -** Output: none -** Returns: y-value on curve at x = xx -** Purpose: uses linear interpolation to find y-value on a -** data curve corresponding to specified x-value. -** NOTE: does not extrapolate beyond endpoints of curve. -**---------------------------------------------------------------- -*/ -{ - int k, m; - double dx, dy; - - m = n - 1; /* Highest data index */ - if (xx <= x[0]) - return (y[0]); /* xx off low end of curve */ - for (k = 1; k <= m; k++) /* Bracket xx on curve */ - { - if (x[k] >= xx) /* Interp. over interval */ - { - dx = x[k] - x[k - 1]; - dy = y[k] - y[k - 1]; - if (ABS(dx) < TINY) - return (y[k]); - else - return (y[k] - (x[k] - xx) * dy / dx); - } - } - return (y[m]); /* xx off high end of curve */ -} /* End of interp */ - -int findnode(EN_Network *n, char *id) -/*---------------------------------------------------------------- -** Input: id = node ID -** Output: none -** Returns: index of node with given ID, or 0 if ID not found -** Purpose: uses hash table to find index of node with given ID -**---------------------------------------------------------------- -*/ -{ - return (hashtable_find(n->NodeHashTable, id)); -} - -int findlink(EN_Network *n, char *id) -/*---------------------------------------------------------------- -** Input: id = link ID -** Output: none -** Returns: index of link with given ID, or 0 if ID not found -** Purpose: uses hash table to find index of link with given ID -**---------------------------------------------------------------- -*/ -{ - return (hashtable_find(n->LinkHashTable, id)); -} - -int findtank(EN_Network *n, int index) -/*---------------------------------------------------------------- -** Input: index = node index -** Output: none -** Returns: index of tank with given node id, or NOTFOUND if tank not found -** Purpose: for use in the deletenode function -**---------------------------------------------------------------- -*/ -{ - int i; - for (i = 1; i <= n->Ntanks; i++) { - if (n->Tank[i].Node == index) { - return (i); - } - } - return (NOTFOUND); -} - -int findpump(EN_Network *n, int index) -/*---------------------------------------------------------------- -** Input: index = link ID -** Output: none -** Returns: index of pump with given link id, or NOTFOUND if pump not found -** Purpose: for use in the deletelink function -**---------------------------------------------------------------- -*/ -{ - int i; - for (i = 1; i <= n->Npumps; i++) { - if (n->Pump[i].Link == index) { - return (i); - } - } - return (NOTFOUND); -} - -int findvalve(EN_Network *n, int index) -/*---------------------------------------------------------------- -** Input: index = link ID -** Output: none -** Returns: index of valve with given link id, or NOTFOUND if valve not found -** Purpose: for use in the deletelink function -**---------------------------------------------------------------- -*/ -{ - int i; - for (i = 1; i <= n->Nvalves; i++) { - if (n->Valve[i].Link == index) { - return (i); - } - } - return (NOTFOUND); -} - -char *geterrmsg(int errcode, char *msg) -/*---------------------------------------------------------------- -** Input: errcode = error code -** Output: none -** Returns: pointer to string with error message -** Purpose: retrieves text of error message -**---------------------------------------------------------------- -*/ -{ - switch (errcode) { /* Warnings */ -#define DAT(code,enumer,string) case code: strcpy(msg, string); break; -//#define DAT(code,enumer,string) case code: sprintf(msg, "Error %d: %s", code, string); break; -#include "errors.dat" -#undef DAT - default: - strcpy(msg, ""); - } - return (msg); -} - -void errmsg(EN_Project *p, int errcode) -/*---------------------------------------------------------------- -** Input: errcode = error code -** Output: none -** Purpose: writes error message to report file -**---------------------------------------------------------------- -*/ -{ - if (errcode == 309) /* Report file write error - */ - { /* Do not write msg to file. */ - - } else if (p->report.RptFile != NULL && p->report.Messageflag) { - writeline(p, geterrmsg(errcode,p->Msg)); - } -} - -void writewin(void (*vp)(char *), char *s) -/*---------------------------------------------------------------- -** Input: text string -** Output: none -** Purpose: passes character string to viewprog() in -** application which calls the EPANET DLL -**---------------------------------------------------------------- -*/ -{ - char progmsg[MAXMSG + 1]; - if (vp != NULL) { - strncpy(progmsg, s, MAXMSG); - vp(progmsg); - } -} - -int DLLEXPORT EN_getnumdemands(EN_ProjectHandle ph, int nodeIndex, int *numDemands) { - Pdemand d; - int n = 0; - - EN_Project *p = (EN_Project*)ph; - - /* Check for valid arguments */ - if (!p->Openflag) - return (102); - if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) - return (203); - for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next) - n++; - *numDemands = n; - return (0); -} - -int DLLEXPORT EN_getbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE *baseDemand) { - Pdemand d; - int n = 1; - - EN_Project *p = (EN_Project*)ph; - - /* Check for valid arguments */ - if (!p->Openflag) - return (102); - if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) - return (203); - if (nodeIndex <= p->network.Njuncs) { - for (d = p->network.Node[nodeIndex].D; n < demandIdx && d->next != NULL; d = d->next) { - n++; - } - if (n != demandIdx) { - return (253); - } - *baseDemand = (EN_API_FLOAT_TYPE)(d->Base * p->Ucf[FLOW]); - } else { - *baseDemand = (EN_API_FLOAT_TYPE)(0.0); - } - return (0); -} - -int DLLEXPORT EN_setbasedemand(EN_ProjectHandle ph, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand) { - - EN_Project *pr = (EN_Project*)ph; - - EN_Network *net = &pr->network; - Snode *Node = net->Node; - - const int Nnodes = net->Nnodes; - const int Njuncs = net->Njuncs; - - double *Ucf = pr->Ucf; - - Pdemand d; - int n = 1; - /* Check for valid arguments */ - if (!pr->Openflag) - return (102); - if (nodeIndex <= 0 || nodeIndex > Nnodes) - return (203); - if (nodeIndex <= Njuncs) { - for (d = Node[nodeIndex].D; n < demandIdx && d->next != NULL; d = d->next) - n++; - if (n != demandIdx) - return (253); - d->Base = baseDemand / Ucf[FLOW]; - } - return (0); -} - -int DLLEXPORT EN_getdemandname(EN_ProjectHandle ph, int nodeIndex, int demandIdx, char *demandName) { - Pdemand d; - int n = 1; - - EN_Project *p = (EN_Project*)ph; - - strcpy(demandName, ""); - /* Check for valid arguments */ - if (!p->Openflag) - return (102); - if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) - return (203); - for (d = p->network.Node[nodeIndex].D; n < demandIdx && d->next != NULL; d = d->next) { - n++; - } - if (n != demandIdx) { - return (253); - } - strcpy(demandName, d->Name); - return (0); -} - -int DLLEXPORT EN_setdemandname(EN_ProjectHandle ph, int nodeIndex, int demandIdx, char *demandName) { - - EN_Project *pr = (EN_Project*)ph; - - EN_Network *net = &pr->network; - Snode *Node = net->Node; - - const int Nnodes = net->Nnodes; - const int Njuncs = net->Njuncs; - - Pdemand d; - int n = 1; - /* Check for valid arguments */ - if (!pr->Openflag) - return (102); - if (nodeIndex <= 0 || nodeIndex > Njuncs) - return (203); - for (d = Node[nodeIndex].D; n < demandIdx && d->next != NULL; d = d->next) - n++; - if (n != demandIdx) - return (253); - strncpy(d->Name, demandName, MAXMSG); - return (0); -} - -int DLLEXPORT EN_setdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIdx, int patIndex) { - - EN_Project *pr = (EN_Project*)ph; - - EN_Network *net = &pr->network; - Snode *Node = net->Node; - - const int Nnodes = net->Nnodes; - const int Njuncs = net->Njuncs; - const int Npats = net->Npats; - - Pdemand d; - int n = 1; - /* Check for valid arguments */ - if (!pr->Openflag) - return (102); - if (nodeIndex <= 0 || nodeIndex > Nnodes) - return (203); - if (patIndex < 1 || patIndex > Npats) - return(205); - if (nodeIndex <= Njuncs) { - for (d = Node[nodeIndex].D; n < demandIdx && d->next != NULL; d = d->next) - n++; - if (n != demandIdx) - return (253); - d->Pat = patIndex; - } - return (0); -} - -int DLLEXPORT EN_getdemandpattern(EN_ProjectHandle ph, int nodeIndex, int demandIdx, int *pattIdx) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - Snode *Node = net->Node; - const int Nnodes = net->Nnodes; - - Pdemand d; - int n = 1; - /* Check for valid arguments */ - if (!p->Openflag) - return (102); - if (nodeIndex <= 0 || nodeIndex > Nnodes) - return (203); - for (d = Node[nodeIndex].D; n < demandIdx && d->next != NULL; d = d->next) - n++; - if (n != demandIdx) - return (253); - *pattIdx = d->Pat; - return (0); -} - -int DLLEXPORT EN_getaveragepatternvalue(EN_ProjectHandle ph, int index, EN_API_FLOAT_TYPE *value) { - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - - Spattern *Pattern = net->Pattern; - const int Npats = net->Npats; - - int i; - *value = 0.0; - if (!p->Openflag) - return (102); - if (index < 1 || index > Npats) - return (205); - // if (period < 1 || period > Pattern[index].Length) return(251); - for (i = 0; i < Pattern[index].Length; i++) { - *value += (EN_API_FLOAT_TYPE)Pattern[index].F[i]; - } - *value /= (EN_API_FLOAT_TYPE)Pattern[index].Length; - return (0); -} - -int DLLEXPORT EN_setlinktype(EN_ProjectHandle ph, int *index, EN_LinkType type, - int actionCode) -{ - int i = *index, n1, n2; - char id[MAXID+1]; - char id1[MAXID+1]; - char id2[MAXID+1]; int errcode; - EN_LinkType oldtype; - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; - // Check for valid input parameters - if (!p->Openflag) return (102); - if (type < 0 || type > GPV || actionCode < EN_UNCONDITIONAL || - actionCode > EN_CONDITIONAL) - { - return (251); - } - - // Check for valid link index - if (i <= 0 || i > net->Nlinks) return (204); - - // Check if current link type equals new type - EN_getlinktype(p, i, &oldtype); - if (oldtype == type) return (0); - - // Type change will be cancelled if link appears in any controls - if (actionCode == EN_CONDITIONAL) - { - actionCode = isInControls(p, LINK, i); - if (actionCode > 0) return (261); - } - - // Pipe changing from or to having a check valve - if (oldtype <= PIPE && type <= PIPE) - { - net->Link[i].Type = type; - if (type == CVPIPE) net->Link[i].Stat = OPEN; - return (0); - } - - // Get ID's of link & its end nodes - EN_getlinkid(ph, i, id); - EN_getlinknodes(ph, i, &n1, &n2); - EN_getnodeid(ph, n1, id1); - EN_getnodeid(ph, n2, id2); - - // Delete the original link (and any controls containing it) - EN_deletelink(ph, i, actionCode); - - // Create a new link of new type and old id - errcode = EN_addlink(ph, id, type, id1, id2); - - // Find the index of this new link - EN_getlinkindex(ph, id, index); - return (errcode); + *t = 0; + if (!p->quality.OpenQflag) return 105; + errcode = runqual(p, t); + if (errcode) errmsg(p, errcode); + return errcode; } -int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { - int i, nIdx; - int index; - struct Sdemand *demand; - - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; - hydraulics_t *hyd = &p->hydraulics; - quality_t *qu = &p->quality; - Stank *tank; - Snode *node; - Scoord *coord; - Scontrol *control; - - /* Check if a node with same id already exists */ - if (!p->Openflag) - return (102); - if (EN_getnodeindex(p, id, &i) == 0) - return (215); - - /* Check that id name is not too long */ - if (strlen(id) > MAXID) - return (250); - - /* Grow arrays to accomodate the new values */ - net->Node = (Snode *)realloc(net->Node, (net->Nnodes + 2) * sizeof(Snode)); - net->Coord = realloc(net->Coord, (net->Nnodes + 2) * sizeof(Scoord)); - hyd->NodeDemand = (double *)realloc(hyd->NodeDemand, (net->Nnodes + 2) * sizeof(double)); - qu->NodeQual = (double *)realloc(qu->NodeQual, (net->Nnodes + 2) * sizeof(double)); - hyd->NodeHead = (double *)realloc(hyd->NodeHead, (net->Nnodes + 2) * sizeof(double)); - - // Actions taken when a new Junction is added - if (nodeType == EN_JUNCTION) { - net->Njuncs++; - nIdx = net->Njuncs; - node = &net->Node[nIdx]; - coord = &net->Coord[nIdx]; - - demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); - demand->Base = 0.0; - demand->Pat = hyd->DefPat; // Use default pattern - strcpy(demand->Name, ""); - demand->next = NULL; - node->D = demand; - - // shift rest of Node array - for (index = net->Nnodes; index >= net->Njuncs; index--) { - hashtable_update(net->NodeHashTable, net->Node[index].ID, index + 1); - net->Node[index + 1] = net->Node[index]; - net->Coord[index + 1] = net->Coord[index]; - } - // shift indices of Tank array - for (index = 1; index <= net->Ntanks; index++) { - net->Tank[index].Node += 1; - } - - // shift indices of Links, if necessary - for (index = 1; index <= net->Nlinks; index++) { - if (net->Link[index].N1 > net->Njuncs - 1) { - net->Link[index].N1 += 1; - } - if (net->Link[index].N2 > net->Njuncs - 1) { - net->Link[index].N2 += 1; - } - } - - // shift indices of Controls, - // for high-index nodes (tanks/reservoirs) - for (index = 1; index <= net->Ncontrols; ++index) { - control = &net->Control[index]; - if (control->Node > net->Njuncs - 1) { - control->Node += 1; - } - } - - // adjust indices of tanks/reservoirs in Rule premises (see RULES.C) - adjusttankrules(p); - - // Actions taken when a new Tank/Reservoir is added - } else { - nIdx = net->Nnodes+1; - node = &net->Node[nIdx]; - coord = &net->Coord[nIdx]; - net->Ntanks++; - - /* resize tanks array */ - net->Tank = (Stank *)realloc(net->Tank, (net->Ntanks + 1) * sizeof(Stank)); - - tank = &net->Tank[net->Ntanks]; - - /* set default values for new tank or reservoir */ - tank->Node = nIdx; - tank->Pat = 0; - if (nodeType == EN_TANK) { - tank->A = 1.0; - } else { - tank->A = 0; - } - tank->Hmin = 0; - tank->Hmax = 0; - tank->H0 = 0; - tank->Vmin = 0; - tank->Vmax = 0; - tank->V0 = 0; - tank->Kb = 0; - tank->V = 0; - tank->C = 0; - tank->Pat = 0; - tank->Vcurve = 0; - tank->MixModel = 0; - tank->V1max = 10000; - } - - net->Nnodes++; - - /* set default values for new node */ - strncpy(node->ID, id, MAXID); - - node->El = 0; - node->S = NULL; - node->C0 = 0; - node->Ke = 0; - node->Rpt = 0; - strcpy(node->Comment, ""); - - coord->HaveCoords = FALSE; - coord->X = 0; - coord->Y = 0; - - /* Insert new node into hash table */ - hashtable_insert(net->NodeHashTable, node->ID, nIdx); /* see HASH.C */ - return (0); -} - -int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, char *fromNode, - char *toNode) { - int i, n; - int N1, N2; - - EN_Project *p = (EN_Project*)ph; - - EN_Network *net = &p->network; - hydraulics_t *hyd = &p->hydraulics; - Slink *link; - Spump *pump; - - /* Check if a link with same id already exists */ - if (!p->Openflag) - return (102); - if (EN_getlinkindex(p, id, &i) == 0) - return (215); - - /* Lookup the from and to nodes */ - N1 = hashtable_find(net->NodeHashTable, fromNode); - N2 = hashtable_find(net->NodeHashTable, toNode); - - if (N1 == 0 || N2 == 0) { - return (203); - } - - /* Check that id name is not too long */ - if (strlen(id) > MAXID) - return (250); - - net->Nlinks++; - n = net->Nlinks; - - /* Grow arrays to accomodate the new value */ - net->Link = (Slink *)realloc(net->Link, (net->Nlinks + 1) * sizeof(Slink)); - hyd->LinkFlows = (double *)realloc(hyd->LinkFlows, (net->Nlinks + 1) * sizeof(double)); - hyd->LinkSetting = (double *)realloc(hyd->LinkSetting, (net->Nlinks + 1) * sizeof(double)); - hyd->LinkStatus = (StatType *)realloc(hyd->LinkStatus, (net->Nlinks + 1) * sizeof(StatType)); - - link = &net->Link[net->Nlinks]; - - strncpy(net->Link[n].ID, id, MAXID); - - if (linkType <= EN_PIPE) { - net->Npipes++; - } else if (linkType == EN_PUMP) { - net->Npumps++; - /* Grow pump array to accomodate the new value */ - net->Pump = (Spump *)realloc(net->Pump, (net->Npumps + 1) * sizeof(Spump)); - pump = &net->Pump[net->Npumps]; - - pump->Link = n; - pump->Ptype = 0; - pump->Q0 = 0; - pump->Qmax = 0; - pump->Hmax = 0; - pump->H0 = 0; - pump->R = 0; - pump->N = 0; - pump->Hcurve = 0; - pump->Ecurve = 0; - pump->Upat = 0; - pump->Epat = 0; - pump->Ecost = 0; - pump->Energy[5] = MISSING; - - } else { - - /* Grow valve array to accomodate the new value */ - net->Nvalves++; - net->Valve = (Svalve *)realloc(net->Valve, (net->Nvalves + 1) * sizeof(Svalve)); - net->Valve[net->Nvalves].Link = n; - } - - link->Type = linkType; - link->N1 = N1; - link->N2 = N2; - link->Stat = OPEN; - - if (linkType == EN_PUMP) { - link->Kc = 1.0; // Speed factor - link->Km = 0.0; // Horsepower - link->Len = 0.0; - } else if (linkType <= EN_PIPE) { // pipe or cvpipe - link->Diam = 10 / p->Ucf[DIAM]; - link->Kc = 100; // Rough. coeff - link->Km = 0.0; // Loss coeff - link->Len = 1000; - } else { // Valve - link->Diam = 10 / p->Ucf[DIAM]; - link->Kc = 0.0; // Valve setting. - link->Km = 0.0; // Loss coeff - link->Len = 0.0; - link->Stat = ACTIVE; - } - link->Kb = 0; - link->Kw = 0; - link->R = 0; - link->Rc = 0; - link->Rpt = 0; - strcpy(link->Comment, ""); - - hashtable_insert(net->LinkHashTable, link->ID, n); - return (0); -} - -int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index, int actionCode) +int DLLEXPORT EN_nextQ(EN_Project p, long *tstep) /*---------------------------------------------------------------- -** Input: index = index of the link to delete -** actionCode = how to treat controls that contain the link: -** EN_UNCONDITIONAL deletes all such controls plus the link, -** EN_CONDITIONAL does not delete the link if it appears -** in a control and returns an error code +** Input: none +** Output: tstep = time step over which water quality is updated (sec) +** Returns: error code +** Purpose: updates water quality throughout the network until +** next hydraulic event occurs. +**---------------------------------------------------------------- +*/ +{ + int errcode; + + *tstep = 0; + if (!p->quality.OpenQflag) return 105; + errcode = nextqual(p, tstep); + if (!errcode && p->outfile.Saveflag && *tstep == 0) + { + p->outfile.SaveQflag = TRUE; + } + if (errcode) errmsg(p, errcode); + return errcode; +} + +int DLLEXPORT EN_stepQ(EN_Project p, long *tleft) +/*---------------------------------------------------------------- +** Input: none +** Output: tleft = amount of simulation time remaining (sec) +** Returns: error code +** Purpose: updates water quality throughout the network over +** fixed time step. +**---------------------------------------------------------------- +*/ +{ + int errcode; + + *tleft = 0; + if (!p->quality.OpenQflag) return 105; + errcode = stepqual(p, tleft); + if (!errcode && p->outfile.Saveflag && *tleft == 0) + { + p->outfile.SaveQflag = TRUE; + } + if (errcode) errmsg(p, errcode); + return errcode; +} + +int DLLEXPORT EN_closeQ(EN_Project p) +/*---------------------------------------------------------------- +** Input: none ** Output: none ** Returns: error code -** Purpose: deletes a link from a project. +** Purpose: closes EPANET's water quality solver +**---------------------------------------------------------------- +*/ +{ + if (!p->Openflag) return 102; + closequal(p); + p->quality.OpenQflag = FALSE; + return 0; +} + +/******************************************************************** + + Reporting Functions + + ********************************************************************/ + +int DLLEXPORT EN_writeline(EN_Project p, char *line) +/*---------------------------------------------------------------- +** Input: line = line of text +** Output: none +** Returns: error code +** Purpose: write a line of text to the project's report file +**---------------------------------------------------------------- +*/ +{ + if (!p->Openflag) return 102; + writeline(p, line); + return 0; +} + +int DLLEXPORT EN_report(EN_Project p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: writes formatted simulation results to the project's +** report file +**---------------------------------------------------------------- +*/ +{ + int errcode; + + // Check if results have been saved to binary output file + if (!p->outfile.SaveQflag) return 106; + writewin(p->viewprog, FMT103); + + // Write the formatted report + errcode = writereport(p); + if (errcode) errmsg(p, errcode); + return errcode; +} + +int DLLEXPORT EN_resetreport(EN_Project p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: resets reporting options to their default values **---------------------------------------------------------------- */ { int i; - int pumpindex; - int valveindex; - EN_LinkType linkType; - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; - Slink *link; - - // Check that link exists - if (!p->Openflag) return (102); - if (index <= 0 || index > net->Nlinks ) return (204); - if (actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) + if (!p->Openflag) return 102; + initreport(&p->report); + for (i = 1; i <= p->network.Nnodes; i++) { - return (251); + p->network.Node[i].Rpt = 0; } - - // Deletion will be cancelled if link appears in any controls - if (actionCode == EN_CONDITIONAL) + for (i = 1; i <= p->network.Nlinks; i++) { - actionCode = isInControls(p, LINK, index); - if (actionCode > 0) return (261); + p->network.Link[i].Rpt = 0; } - - // Get references to the link and its type - link = &net->Link[index]; - EN_getlinktype(p, index, &linkType); - - // Remove link from hash table - hashtable_delete(net->LinkHashTable, link->ID); - - // Shift position of higher entries in Link array down one - for (i = index; i <= net->Nlinks - 1; i++) - { - net->Link[i] = net->Link[i + 1]; - // ... update link's entry in the hash table - hashtable_update(net->LinkHashTable, net->Link[i].ID, i); - } - - // Adjust references to higher numbered links for pumps & valves - for (i = 1; i <= net->Npumps; i++) - { - if (net->Pump[i].Link > index) net->Pump[i].Link -= 1; - } - for (i = 1; i <= net->Nvalves; i++) - { - if (net->Valve[i].Link > index) net->Valve[i].Link -= 1; - } - - // Delete any pump associated with the deleted link - if (linkType == PUMP) - { - pumpindex = findpump(net,index); - for (i = pumpindex; i <= net->Npumps - 1; i++) - { - net->Pump[i] = net->Pump[i + 1]; - } - net->Npumps--; - } - - // Delete any valve (linkType > EN_PUMP) associated with the deleted link - if (linkType > PUMP) - { - valveindex = findvalve(net,index); - for (i = valveindex; i <= net->Nvalves - 1; i++) - { - net->Valve[i] = net->Valve[i + 1]; - } - net->Nvalves--; - } - - // Delete any control containing the link - for (i = net->Ncontrols; i >= 1; i--) - { - if (net->Control[i].Link == index) EN_deletecontrol(ph, i); - } - - // Adjust higher numbered link indices in remaining controls - for (i = 1; i <= net->Ncontrols; i++) - { - if (net->Control[i].Link > index) net->Control[i].Link--; - } - - // Make necessary adjustments to rule-based controls (r_LINK = 7) - adjustrules(p, 7, index); // see RULES.C - - // Reduce link count by one - net->Nlinks--; - return (0); + return 0; } -int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode) +int DLLEXPORT EN_setreport(EN_Project p, char *s) +/*---------------------------------------------------------------- +** Input: s = a report formatting command +** Output: none +** Returns: error code +** Purpose: sets a specific set of reporting options +**---------------------------------------------------------------- +*/ +{ + char s1[MAXLINE + 1]; + + if (!p->Openflag) return 102; + if (strlen(s) >= MAXLINE) return 250; + strcpy(s1, s); + strcat(s1, "\n"); + if (setreport(p, s1) > 0) return 250; + else return 0; +} + +int DLLEXPORT EN_setstatusreport(EN_Project p, int code) +/*---------------------------------------------------------------- +** Input: code = level of reporting to use (see EN_StatusReport) +** Output: none +** Returns: error code +** Purpose: sets the level of hydraulic status reporting +**---------------------------------------------------------------- +*/ +{ + int errcode = 0; + + if (code >= EN_NO_REPORT && code <= EN_FULL_REPORT) + { + p->report.Statflag = (char)code; + } + else errcode = 202; + return errcode; +} + +int DLLEXPORT EN_getversion(int *v) +/*---------------------------------------------------------------- +** Input: none +** Output: v = version number of the source code +** Returns: error code (should always be 0) +** Purpose: retrieves a number assigned to the most recent +** update of the source code. This number, set by the +** constant CODEVERSION found in TYPES.H, is to be +** interpreted with implied decimals, i.e., +** "20100" == "2(.)01(.)00" +**---------------------------------------------------------------- +*/ +{ + *v = CODEVERSION; + return 0; +} + +int DLLEXPORT EN_getcount(EN_Project p, EN_CountType code, int *count) +/*---------------------------------------------------------------- +** Input: code = type of component to count (see EN_CountType) +** Output: count = number of components of specified type +** Returns: error code +** Purpose: Retrieves number of network components of a given type +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + *count = 0; + if (!p->Openflag) return 102; + switch (code) + { + case EN_NODECOUNT: + *count = net->Nnodes; + break; + case EN_TANKCOUNT: + *count = net->Ntanks; + break; + case EN_LINKCOUNT: + *count = net->Nlinks; + break; + case EN_PATCOUNT: + *count = net->Npats; + break; + case EN_CURVECOUNT: + *count = net->Ncurves; + break; + case EN_CONTROLCOUNT: + *count = net->Ncontrols; + break; + case EN_RULECOUNT: + *count = net->Nrules; + break; + default: + return 251; + } + return 0; +} + +int DLLEXPORT EN_geterror(int errcode, char *errmsg, int n) +/*---------------------------------------------------------------- +** Input: errcode = an error or warnng code +** n = maximum characters that errmsg can hold +** Output: errmsg = text of error/warning message +** Returns: error code +** Purpose: retrieves the text of the message associated with +** a particular error/warning code +**---------------------------------------------------------------- +*/ +{ + char newMsg[MAXMSG + 1]; + + switch (errcode) + { + case 1: + strncpy(errmsg, WARN1, n); + break; + case 2: + strncpy(errmsg, WARN2, n); + break; + case 3: + strncpy(errmsg, WARN3, n); + break; + case 4: + strncpy(errmsg, WARN4, n); + break; + case 5: + strncpy(errmsg, WARN5, n); + break; + case 6: + strncpy(errmsg, WARN6, n); + break; + default: + geterrmsg(errcode, newMsg); + strncpy(errmsg, newMsg, n); + } + if (strlen(errmsg) == 0) + return 251; + else + return 0; +} + +int DLLEXPORT EN_getstatistic(EN_Project p, int code, EN_API_FLOAT_TYPE *value) +/*---------------------------------------------------------------- +** Input: code = simulation statistic (see EN_AnalysisStatistic) +** Output: value = simulation analysis statistic value +** Returns: error code +** Purpose: retrieves the value of a simulation analysis statistic +**---------------------------------------------------------------- +*/ +{ + switch (code) + { + case EN_ITERATIONS: + *value = (EN_API_FLOAT_TYPE)p->hydraul.Iterations; + break; + case EN_RELATIVEERROR: + *value = (EN_API_FLOAT_TYPE)p->hydraul.RelativeError; + break; + case EN_MAXHEADERROR: + *value = (EN_API_FLOAT_TYPE)(p->hydraul.MaxHeadError * p->Ucf[HEAD]); + break; + case EN_MAXFLOWCHANGE: + *value = (EN_API_FLOAT_TYPE)(p->hydraul.MaxFlowChange * p->Ucf[FLOW]); + break; + case EN_MASSBALANCE: + *value = (EN_API_FLOAT_TYPE)(p->quality.MassBalance.ratio); + break; + default: + break; + } + return 0; +} + +/******************************************************************** + + Analysis Options Functions + +********************************************************************/ + +int DLLEXPORT EN_getoption(EN_Project p, EN_Option code, + EN_API_FLOAT_TYPE *value) +/*---------------------------------------------------------------- +** Input: code = an analysis option code (see EN_Option) +** Output: value = analysis option value +** Returns: error code +** Purpose: retrieves the value of an analysis option +**---------------------------------------------------------------- +*/ +{ + Hydraul *hyd = &p->hydraul; + Quality *qual = &p->quality; + + double *Ucf = p->Ucf; + double v = 0.0; + + *value = 0.0; + if (!p->Openflag) return 102; + switch (code) + { + case EN_TRIALS: + v = (double)hyd->MaxIter; + break; + case EN_ACCURACY: + v = hyd->Hacc; + break; + case EN_TOLERANCE: + v = qual->Ctol * Ucf[QUALITY]; + break; + case EN_EMITEXPON: + if (hyd->Qexp > 0.0) + v = 1.0 / hyd->Qexp; + break; + case EN_DEMANDMULT: + v = hyd->Dmult; + break; + case EN_HEADERROR: + v = hyd->HeadErrorLimit * Ucf[HEAD]; + break; + case EN_FLOWCHANGE: + v = hyd->FlowChangeLimit * Ucf[FLOW]; + break; + case EN_DEMANDDEFPAT: + v = hyd->DefPat; + break; + case EN_HEADLOSSFORM: + v = hyd->Formflag; + break; + default: + return 251; + } + *value = (EN_API_FLOAT_TYPE)v; + return 0; +} + +int DLLEXPORT EN_setoption(EN_Project p, int code, EN_API_FLOAT_TYPE v) +/*---------------------------------------------------------------- +** Input: code = analysis option code (see EN_Option) +** v = analysis option value +** Output: none +** Returns: error code +** Purpose: sets the value for an analysis option +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Hydraul *hyd = &p->hydraul; + Quality *qual = &p->quality; + + Snode *node; + Pdemand demand; + const int Njuncs = net->Njuncs; + double *Ucf = p->Ucf; + int i, j; + int tmpPat, error; + char tmpId[MAXID + 1]; + double Ke, n, ucf, value = v; + + if (!p->Openflag) return 102; + switch (code) + { + case EN_TRIALS: + if (value < 1.0) return 202; + hyd->MaxIter = (int)value; + break; + + case EN_ACCURACY: + if (value < 1.e-5 || value > 1.e-1) return 202; + hyd->Hacc = value; + break; + + case EN_TOLERANCE: + if (value < 0.0) return 202; + qual->Ctol = value / Ucf[QUALITY]; + break; + + case EN_EMITEXPON: + if (value <= 0.0) return 202; + n = 1.0 / value; + ucf = pow(Ucf[FLOW], n) / Ucf[PRESSURE]; + for (i = 1; i <= Njuncs; i++) + { + j = EN_getnodevalue(p, i, EN_EMITTER, &v); + Ke = v; + if (j == 0 && Ke > 0.0) net->Node[i].Ke = ucf / pow(Ke, n); + } + hyd->Qexp = n; + break; + + case EN_DEMANDMULT: + if (value <= 0.0) return 202; + hyd->Dmult = value; + break; + + case EN_HEADERROR: + if (value < 0.0) return 202; + hyd->HeadErrorLimit = value / Ucf[HEAD]; + break; + + case EN_FLOWCHANGE: + if (value < 0.0) return 202; + hyd->FlowChangeLimit = value / Ucf[FLOW]; + break; + + case EN_DEMANDDEFPAT: + //check that the pattern exists or is set to zero to delete the default pattern + if (value < 0 || value > net->Npats) return 205; + tmpPat = hyd->DefPat; + //get the new pattern ID + if (value == 0) + { + strncpy(tmpId, p->parser.DefPatID, MAXID); + } + else + { + error = EN_getpatternid(p, (int)value, 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 = (int)value; + strcpy(demand->Name, ""); + } + } + } + strncpy(p->parser.DefPatID, tmpId, MAXID); + hyd->DefPat = (int)value; + break; + + default: + return 251; + } + return 0; +} + +int DLLEXPORT EN_getflowunits(EN_Project p, int *code) +/*---------------------------------------------------------------- +** Input: none +** Output: code = flow units code (see EN_FlowUnits) +** Returns: error code +** Purpose: retrieves the flow units used by a project +**---------------------------------------------------------------- +*/ +{ + *code = -1; + if (!p->Openflag) return 102; + *code = p->parser.Flowflag; + return 0; +} + +int DLLEXPORT EN_setflowunits(EN_Project p, int code) +/*---------------------------------------------------------------- +** Input: code = flow units code (see EN_FlowUnits) +** Output: none +** Returns: error code +** Purpose: sets the flow units used by a project +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + int i, j; + double qfactor, vfactor, hfactor, efactor, xfactor, yfactor; + double *Ucf = p->Ucf; + + if (!p->Openflag) return 102; + + // Determine unit system based on flow units + qfactor = Ucf[FLOW]; + vfactor = Ucf[VOLUME]; + hfactor = Ucf[HEAD]; + efactor = Ucf[ELEV]; + + p->parser.Flowflag = code; + switch (code) + { + case LPS: + case LPM: + case MLD: + case CMH: + case CMD: + p->parser.Unitsflag = SI; + break; + default: + p->parser.Unitsflag = US; + break; + } + + // Revise pressure units depending on flow units + if (p->parser.Unitsflag != SI) p->parser.Pressflag = PSI; + else if (p->parser.Pressflag == PSI) p->parser.Pressflag = METERS; + initunits(p); + + //update curves + for (i = 1; i <= net->Ncurves; i++) + { + switch (net->Curve[i].Type) + { + case V_CURVE: + xfactor = efactor / Ucf[ELEV]; + yfactor = vfactor / Ucf[VOLUME]; + break; + case H_CURVE: + case P_CURVE: + xfactor = qfactor / Ucf[FLOW]; + yfactor = hfactor / Ucf[HEAD]; + break; + case E_CURVE: + xfactor = qfactor / Ucf[FLOW]; + yfactor = 1; + break; + default: + xfactor = 1; + yfactor = 1; + } + + for (j = 0; j < net->Curve[i].Npts; j++) + { + net->Curve[i].X[j] = net->Curve[i].X[j] / xfactor; + net->Curve[i].Y[j] = net->Curve[i].Y[j] / yfactor; + } + } + return 0; +} + +int DLLEXPORT EN_gettimeparam(EN_Project p, int code, long *value) +/*---------------------------------------------------------------- +** Input: code = time parameter code (see EN_TimeProperty) +** Output: value = time parameter value +** Returns: error code +** Purpose: retrieves the value of a time parameter +**---------------------------------------------------------------- +*/ +{ + Report *rpt = &p->report; + Times *time = &p->times; + + int i; + + *value = 0; + if (!p->Openflag) return 102; + if (code < EN_DURATION || code > EN_NEXTEVENTIDX) return 251; + switch (code) + { + case EN_DURATION: + *value = time->Dur; + break; + case EN_HYDSTEP: + *value = time->Hstep; + break; + case EN_QUALSTEP: + *value = time->Qstep; + break; + case EN_PATTERNSTEP: + *value = time->Pstep; + break; + case EN_PATTERNSTART: + *value = time->Pstart; + break; + case EN_REPORTSTEP: + *value = time->Rstep; + break; + case EN_REPORTSTART: + *value = time->Rstart; + break; + case EN_STATISTIC: + *value = rpt->Tstatflag; + break; + case EN_RULESTEP: + *value = time->Rulestep; + break; + case EN_PERIODS: + *value = rpt->Nperiods; + break; + case EN_STARTTIME: + *value = time->Tstart; + break; + case EN_HTIME: + *value = time->Htime; + break; + case EN_NEXTEVENT: + *value = time->Hstep; // find the lesser of the hydraulic time step length, + // or the time to next full/empty tank + tanktimestep(p, value); + break; + case EN_NEXTEVENTIDX: + *value = time->Hstep; + i = tanktimestep(p, value); + *value = i; + break; + } + return 0; +} + +int DLLEXPORT EN_settimeparam(EN_Project p, int code, long value) +/*---------------------------------------------------------------- +** Input: code = time parameter code (see EN_TimeProperty) +** value = time parameter value +** Output: none +** Returns: error code +** Purpose: sets the value of a time parameter +**---------------------------------------------------------------- +*/ +{ + Report *rpt = &p->report; + Times *time = &p->times; + + if (!p->Openflag) return 102; + if (value < 0) return 202; + switch (code) + { + case EN_DURATION: + time->Dur = value; + if (time->Rstart > time->Dur) time->Rstart = 0; + break; + + case EN_HYDSTEP: + if (value == 0) return 202; + time->Hstep = value; + time->Hstep = MIN(time->Pstep, time->Hstep); + time->Hstep = MIN(time->Rstep, time->Hstep); + time->Qstep = MIN(time->Qstep, time->Hstep); + break; + + case EN_QUALSTEP: + if (value == 0) return 202; + time->Qstep = value; + time->Qstep = MIN(time->Qstep, time->Hstep); + break; + + case EN_PATTERNSTEP: + if (value == 0) return 202; + time->Pstep = value; + if (time->Hstep > time->Pstep) time->Hstep = time->Pstep; + break; + + case EN_PATTERNSTART: + time->Pstart = value; + break; + + case EN_REPORTSTEP: + if (value == 0) return 202; + time->Rstep = value; + if (time->Hstep > time->Rstep) time->Hstep = time->Rstep; + break; + + case EN_REPORTSTART: + if (time->Rstart > time->Dur) return 202; + time->Rstart = value; + break; + + case EN_RULESTEP: + if (value == 0) return 202; + time->Rulestep = value; + time->Rulestep = MIN(time->Rulestep, time->Hstep); + break; + + case EN_STATISTIC: + if (value > RANGE) return 202; + rpt->Tstatflag = (char)value; + break; + + case EN_HTIME: + time->Htime = value; + break; + + case EN_QTIME: + time->Qtime = value; + break; + + default: + return 251; + } + return 0; +} + +int DLLEXPORT EN_getqualinfo(EN_Project p, int *qualcode, char *chemname, + char *chemunits, int *tracenode) +/*---------------------------------------------------------------- +** Input: none +** Output: qualcode = quality analysis code (see EN_QualityType) +** chemname = name of chemical species +** chemunits = concentration units +** tracenode = index of node being traced +** Returns: error code +** Purpose: retrieves water quality analysis options +**---------------------------------------------------------------- +*/ +{ + EN_getqualtype(p, qualcode, tracenode); + if (p->quality.Qualflag == TRACE) + { + strncpy(chemname, "", MAXID); + strncpy(chemunits, "dimensionless", MAXID); + } + else + { + strncpy(chemname, p->quality.ChemName, MAXID); + strncpy(chemunits, p->quality.ChemUnits, MAXID); + } + return 0; +} + +int DLLEXPORT EN_getqualtype(EN_Project p, int *qualcode, int *tracenode) +/*---------------------------------------------------------------- +** Input: none +** Output: qualcode = quality analysis code (see EN_QualityType) +** tracenode = index of node being traced +** Output: none +** Returns: error code +** Purpose: retrieves type of quality analysis being made +**---------------------------------------------------------------- +*/ +{ + *tracenode = 0; + if (!p->Openflag) return 102; + *qualcode = p->quality.Qualflag; + if (p->quality.Qualflag == TRACE) *tracenode = p->quality.TraceNode; + return 0; +} + +int DLLEXPORT EN_setqualtype(EN_Project p, int qualcode, char *chemname, + char *chemunits, char *tracenode) +/*---------------------------------------------------------------- +** Input: qualcode = quality analysis code (see EN_QualityType) +** chemname = name of chemical species +** chemunits = concentration units +** tracenode = index of node being traced +** Output: none +** Returns: error code +** Purpose: sets water quality analysis options +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Report *rpt = &p->report; + Quality *qual = &p->quality; + + double *Ucf = p->Ucf; + int i; + double ccf = 1.0; + + if (!p->Openflag) return 102; + if (qualcode < EN_NONE || qualcode > EN_TRACE) return 251; + qual->Qualflag = (char)qualcode; + qual->Ctol *= Ucf[QUALITY]; + if (qual->Qualflag == CHEM) // Chemical analysis + { + strncpy(qual->ChemName, chemname, MAXID); + strncpy(qual->ChemUnits, chemunits, MAXID); + strncpy(rpt->Field[QUALITY].Units, qual->ChemUnits, MAXID); + strncpy(rpt->Field[REACTRATE].Units, qual->ChemUnits, MAXID); + strcat(rpt->Field[REACTRATE].Units, t_PERDAY); + ccf = 1.0 / LperFT3; + } + if (qual->Qualflag == TRACE) // Source trace analysis + { + qual->TraceNode = findnode(net, tracenode); + if (qual->TraceNode == 0) return 203; + strncpy(qual->ChemName, u_PERCENT, MAXID); + strncpy(qual->ChemUnits, tracenode, MAXID); + strcpy(rpt->Field[QUALITY].Units, u_PERCENT); + } + if (qual->Qualflag == AGE) // Water age analysis + { + strncpy(qual->ChemName, w_AGE, MAXID); + strncpy(qual->ChemUnits, u_HOURS, MAXID); + strcpy(rpt->Field[QUALITY].Units, u_HOURS); + } + + // 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)) + { + for (i = 1; i <= p->network.Nnodes; i++) + { + p->network.Node[i].C0 *= Ucf[QUALITY]; + } + } + + Ucf[QUALITY] = ccf; + Ucf[LINKQUAL] = ccf; + Ucf[REACTRATE] = ccf; + qual->Ctol /= Ucf[QUALITY]; + return 0; +} + +/******************************************************************** + + Node Functions + +********************************************************************/ + +int DLLEXPORT EN_addnode(EN_Project p, char *id, EN_NodeType nodeType) +/*---------------------------------------------------------------- +** Input: id = node ID name +** nodeType = type of node (see EN_NodeType) +** Output: none +** Returns: error code +** Purpose: adds a new node to a project +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Hydraul *hyd = &p->hydraul; + Quality *qual = &p->quality; + + int i, nIdx; + int index; + int size; + struct Sdemand *demand; + Stank *tank; + Snode *node; + Scontrol *control; + + // Check if a node with same id already exists + if (!p->Openflag) return 102; + if (EN_getnodeindex(p, id, &i) == 0) return 215; + + // Check that id name is not too long + if (strlen(id) > MAXID) return 250; + + // Grow node-related arrays to accomodate the new node + size = (net->Nnodes + 2) * sizeof(Snode); + net->Node = (Snode *)realloc(net->Node, size); + size = (net->Nnodes + 2) * sizeof(double); + hyd->NodeDemand = (double *)realloc(hyd->NodeDemand, size); + qual->NodeQual = (double *)realloc(qual->NodeQual, size); + hyd->NodeHead = (double *)realloc(hyd->NodeHead, size); + + // Actions taken when a new Junction is added + if (nodeType == EN_JUNCTION) + { + net->Njuncs++; + nIdx = net->Njuncs; + node = &net->Node[nIdx]; + + demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); + demand->Base = 0.0; + demand->Pat = hyd->DefPat; // Use default pattern + strcpy(demand->Name, ""); + demand->next = NULL; + node->D = demand; + + // shift rest of Node array + for (index = net->Nnodes; index >= net->Njuncs; index--) + { + hashtable_update(net->NodeHashTable, net->Node[index].ID, index + 1); + net->Node[index + 1] = net->Node[index]; + } + // shift indices of Tank array + for (index = 1; index <= net->Ntanks; index++) + { + net->Tank[index].Node += 1; + } + + // shift indices of Links, if necessary + for (index = 1; index <= net->Nlinks; index++) + { + if (net->Link[index].N1 > net->Njuncs - 1) net->Link[index].N1 += 1; + if (net->Link[index].N2 > net->Njuncs - 1) net->Link[index].N2 += 1; + } + + // shift indices of tanks/reservoir nodes in controls + for (index = 1; index <= net->Ncontrols; ++index) + { + control = &net->Control[index]; + if (control->Node > net->Njuncs - 1) control->Node += 1; + } + + // adjust indices of tanks/reservoirs in Rule premises (see RULES.C) + adjusttankrules(p); + } + + // Actions taken when a new Tank/Reservoir is added + else + { + nIdx = net->Nnodes + 1; + node = &net->Node[nIdx]; + net->Ntanks++; + + // resize tanks array + net->Tank = (Stank *)realloc(net->Tank, (net->Ntanks + 1) * sizeof(Stank)); + tank = &net->Tank[net->Ntanks]; + + // set default values for new tank or reservoir + tank->Node = nIdx; + tank->Pat = 0; + if (nodeType == EN_TANK) tank->A = 1.0; + else tank->A = 0; + tank->Hmin = 0; + tank->Hmax = 0; + tank->H0 = 0; + tank->Vmin = 0; + tank->Vmax = 0; + tank->V0 = 0; + tank->Kb = 0; + tank->V = 0; + tank->C = 0; + tank->Pat = 0; + tank->Vcurve = 0; + tank->MixModel = 0; + tank->V1max = 10000; + } + net->Nnodes++; + strncpy(node->ID, id, MAXID); + + // set default values for new node + node->El = 0; + node->S = NULL; + node->C0 = 0; + node->Ke = 0; + node->Rpt = 0; + node->X = MISSING; + node->Y = MISSING; + strcpy(node->Comment, ""); + + // Insert new node into hash table + hashtable_insert(net->NodeHashTable, node->ID, nIdx); + return 0; +} + +int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode) /*---------------------------------------------------------------- ** Input: index = index of the node to delete ** actionCode = how to treat controls that contain the link @@ -5331,41 +2278,38 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode) **---------------------------------------------------------------- */ { + Network *net = &p->network; + int i, nodeType, tankindex, numControls = 0; - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; Snode *node; Pdemand demand, nextdemand; Psource source; // Check that node exists - if (!p->Openflag) return (102); - if (index <= 0 || index > net->Nnodes) return (204); - if (actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) - { - return (251); - } + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Nnodes) return 204; + if (actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) return 251; // Can't delete a water quality trace node - if (index == p->quality.TraceNode) return (260); + if (index == p->quality.TraceNode) return 260; // Count number of simple & rule-based controls that contain the node if (actionCode == EN_CONDITIONAL) { - actionCode = isInControls(p, NODE, index); + actionCode = incontrols(p, NODE, index); for (i = 1; i <= net->Nlinks; i++) { if (net->Link[i].N1 == index || - net->Link[i].N2 == index) actionCode += isInControls(p, LINK, i); + net->Link[i].N2 == index) actionCode += incontrols(p, LINK, i); } - if (actionCode > 0) return (261); + if (actionCode > 0) return 261; } // Get a reference to the node & its type node = &net->Node[index]; - EN_getnodetype(ph, index, &nodeType); + EN_getnodetype(p, index, &nodeType); - // Remove node from hash table + // Remove node from its hash table hashtable_delete(net->NodeHashTable, node->ID); // Free memory allocated to node's demands & WQ source @@ -5383,7 +2327,6 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode) for (i = index; i <= net->Nnodes - 1; i++) { net->Node[i] = net->Node[i + 1]; - net->Coord[i] = net->Coord[i + 1]; // ... update node's entry in the hash table hashtable_update(net->NodeHashTable, net->Node[i].ID, i); } @@ -5413,7 +2356,7 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode) for (i = net->Nlinks; i >= 1; i--) { if (net->Link[i].N1 == index || - net->Link[i].N2 == index) EN_deletelink(ph, i, EN_UNCONDITIONAL); + net->Link[i].N2 == index) EN_deletelink(p, i, EN_UNCONDITIONAL); } // Adjust indices of all link end nodes @@ -5426,7 +2369,7 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode) // Delete any control containing the node for (i = net->Ncontrols; i >= 1; i--) { - if (net->Control[i].Node == index) EN_deletecontrol(ph, i); + if (net->Control[i].Node == index) EN_deletecontrol(p, i); } // Adjust higher numbered link indices in remaining controls @@ -5435,127 +2378,2503 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index, int actionCode) if (net->Control[i].Node > index) net->Control[i].Node--; } - // Make necessary adjustments to rule-based controls (r_NODE = 6) - adjustrules(p, 6, index); // see RULES.C + // Make necessary adjustments to rule-based controls + adjustrules(p, EN_R_NODE, index); // Reduce counts of node types if (nodeType == EN_JUNCTION) net->Njuncs--; else net->Ntanks--; net->Nnodes--; - return (0); + return 0; } -int isInControls(EN_Project *pr, int objType, int index) +int DLLEXPORT EN_getnodeindex(EN_Project p, char *id, int *index) /*---------------------------------------------------------------- -** Input: objType = type of object (either NODE or LINK) -** index = the object's index -** Output: none -** Returns: 1 if any controls contain the object; 0 if not -** Purpose: determines if nay simple or rule-based controls -** contain a particular node or link. +** Input: id = node ID name +** Output: index = node index +** Returns: error code +** Purpose: retrieves the index of a node **---------------------------------------------------------------- */ { - int i, ruleObject; - EN_Network *net = &pr->network; - rules_t *rules = &pr->rules; - Spremise *p; - Saction *a; + *index = 0; + if (!p->Openflag) return 102; + *index = findnode(&p->network, id); + if (*index == 0) return 203; + else return 0; +} - // Check simple controls - for (i = 1; i <= net->Ncontrols; i++) +int DLLEXPORT EN_getnodeid(EN_Project p, int index, char *id) +/*---------------------------------------------------------------- +** Input: index = node index +** Output: id = node ID name +** Returns: error code +** Purpose: retrieves the name of a node +**---------------------------------------------------------------- +*/ +{ + strcpy(id, ""); + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Nnodes) return 203; + strcpy(id, p->network.Node[index].ID); + return 0; +} + +int DLLEXPORT EN_setnodeid(EN_Project p, int index, char *newid) +/*---------------------------------------------------------------- +** Input: index = node index +** newid = new node ID name +** Output: none +** Returns: error code +** Purpose: sets the ID name of a node +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + size_t n; + + // Check for valid arguments + if (index <= 0 || index > net->Nnodes) return 203; + n = strlen(newid); + if (n < 1 || n > MAXID) return 209; + if (strcspn(newid, " ;") < n) return 209; + + // Check if another node with same name exists + if (hashtable_find(net->NodeHashTable, newid) > 0) return 215; + + // Replace the existing node ID with the new value + hashtable_delete(net->NodeHashTable, net->Node[index].ID); + strncpy(net->Node[index].ID, newid, MAXID); + hashtable_insert(net->NodeHashTable, net->Node[index].ID, index); + return 0; +} + +int DLLEXPORT EN_getnodetype(EN_Project p, int index, int *code) +/*---------------------------------------------------------------- +** Input: index = node index +** Output: code = node type (see EN_NodeType) +** Returns: error code +** Purpose: retrieves the type of a node +**---------------------------------------------------------------- +*/ +{ + *code = -1; + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Nnodes) return 203; + if (index <= p->network.Njuncs) *code = EN_JUNCTION; + else { - if (objType == NODE && net->Control[i].Node == index) return 1; - if (objType == LINK && net->Control[i].Link == index) return 1; - } - - // Check rule-based controls - for (i = 1; i <= net->Nrules; i++) - { - // Convert objType to a rule object type - if (objType == NODE) ruleObject = 6; - else ruleObject = 7; - - // Check rule's premises - p = net->Rule[i].Premises; - while (p != NULL) - { - if (ruleObject == p->object && p->index == index) return 1; - p = p->next; - } - - // Rule actions only need to be checked for link objects - if (objType == LINK) - { - // Check rule's THEN actions - a = net->Rule[i].ThenActions; - while (a != NULL) - { - if (a->link == index) return 1; - a = a->next; - } - - // Check rule's ELSE actions - a = net->Rule[i].ElseActions; - while (a != NULL) - { - if (a->link == index) return 1; - a = a->next; - } - } + if (p->network.Tank[index - p->network.Njuncs].A == 0.0) *code = EN_RESERVOIR; + else *code = EN_TANK; } return 0; } -int DLLEXPORT EN_deletecontrol(EN_ProjectHandle ph, int index) +int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int code, EN_API_FLOAT_TYPE *value) /*---------------------------------------------------------------- -** Input: index = index of the control +** Input: index = node index +** code = node property code (see EN_NodeProperty) +** Output: value = node property value +** Returns: error code +** Purpose: retrieves a property value for a node +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Hydraul *hyd = &p->hydraul; + Quality *qual = &p->quality; + + double v = 0.0; + Pdemand demand; + Psource source; + + Snode *Node = net->Node; + Stank *Tank = net->Tank; + + int nJuncs = net->Njuncs; + + double *Ucf = p->Ucf; + double *NodeDemand = hyd->NodeDemand; + double *NodeHead = hyd->NodeHead; + double *NodeQual = qual->NodeQual; + + // Check for valid arguments + *value = 0.0; + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Nnodes) return 203; + + // Retrieve requested property + switch (code) + { + case EN_ELEVATION: + v = Node[index].El * Ucf[ELEV]; + break; + + case EN_BASEDEMAND: + v = 0.0; + // NOTE: primary demand category is last on demand list + if (index <= nJuncs) + { + for (demand = Node[index].D; demand != NULL; demand = demand->next) + { + v = (demand->Base); + } + } + v *= Ucf[FLOW]; + break; + + case EN_PATTERN: + v = 0.0; + // NOTE: primary demand category is last on demand list + if (index <= nJuncs) + { + for (demand = Node[index].D; demand != NULL; demand = demand->next) + { + v = (double)(demand->Pat); + } + } + else v = (double)(Tank[index - nJuncs].Pat); + break; + + case EN_EMITTER: + v = 0.0; + if (Node[index].Ke > 0.0) + { + v = Ucf[FLOW] / pow((Ucf[PRESSURE] * Node[index].Ke), (1.0 / hyd->Qexp)); + } + break; + + case EN_INITQUAL: + v = Node[index].C0 * Ucf[QUALITY]; + break; + + case EN_SOURCEQUAL: + case EN_SOURCETYPE: + case EN_SOURCEMASS: + case EN_SOURCEPAT: + source = Node[index].S; + if (source == NULL) return 240; + if (code == EN_SOURCEQUAL) v = source->C0; + else if (code == EN_SOURCEMASS) v = source->Smass * 60.0; + else if (code == EN_SOURCEPAT) v = source->Pat; + else v = source->Type; + break; + + case EN_TANKLEVEL: + if (index <= nJuncs) return 251; + v = (Tank[index - nJuncs].H0 - Node[index].El) * Ucf[ELEV]; + break; + + case EN_INITVOLUME: + v = 0.0; + if (index > nJuncs) v = Tank[index - nJuncs].V0 * Ucf[VOLUME]; + break; + + case EN_MIXMODEL: + v = MIX1; + if (index > nJuncs) v = Tank[index - nJuncs].MixModel; + break; + + case EN_MIXZONEVOL: + v = 0.0; + if (index > nJuncs) v = Tank[index - nJuncs].V1max * Ucf[VOLUME]; + break; + + case EN_DEMAND: + v = NodeDemand[index] * Ucf[FLOW]; + break; + + case EN_HEAD: + v = NodeHead[index] * Ucf[HEAD]; + break; + + case EN_PRESSURE: + v = (NodeHead[index] - Node[index].El) * Ucf[PRESSURE]; + break; + + case EN_QUALITY: + v = NodeQual[index] * Ucf[QUALITY]; + break; + + case EN_TANKDIAM: + v = 0.0; + if (index > nJuncs) + { + v = sqrt(4.0 / PI * Tank[index - nJuncs].A) * Ucf[ELEV]; + } + break; + + case EN_MINVOLUME: + v = 0.0; + if (index > nJuncs) v = Tank[index - nJuncs].Vmin * Ucf[VOLUME]; + break; + + case EN_MAXVOLUME: + v = 0.0; + if (index > nJuncs) v = Tank[index - nJuncs].Vmax * Ucf[VOLUME]; + break; + + case EN_VOLCURVE: + v = 0.0; + if (index > nJuncs) v = Tank[index - nJuncs].Vcurve; + break; + + case EN_MINLEVEL: + v = 0.0; + if (index > nJuncs) + { + v = (Tank[index - nJuncs].Hmin - Node[index].El) * Ucf[ELEV]; + } + break; + + case EN_MAXLEVEL: + v = 0.0; + if (index > nJuncs) + { + v = (Tank[index - nJuncs].Hmax - Node[index].El) * Ucf[ELEV]; + } + break; + + case EN_MIXFRACTION: + v = 1.0; + if (index > nJuncs && Tank[index - nJuncs].Vmax > 0.0) + { + v = Tank[index - nJuncs].V1max / Tank[index - nJuncs].Vmax; + } + break; + + case EN_TANK_KBULK: + v = 0.0; + if (index > nJuncs) v = Tank[index - nJuncs].Kb * SECperDAY; + break; + + case EN_TANKVOLUME: + if (index <= nJuncs) return 251; + v = tankvolume(p, index - nJuncs, NodeHead[index]) * Ucf[VOLUME]; + break; + + default: + return 251; + } + *value = (EN_API_FLOAT_TYPE)v; + return 0; +} + +int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int code, EN_API_FLOAT_TYPE v) +/*---------------------------------------------------------------- +** Input: index = node index +** code = node property code (see EN_NodeProperty) +** value = node property value ** Output: none ** Returns: error code -** Purpose: deletes a simple control from a project. +** Purpose: sets a property value for a node +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Hydraul *hyd = &p->hydraul; + Quality *qual = &p->quality; + + Snode *Node = net->Node; + Stank *Tank = net->Tank; + + const int nNodes = net->Nnodes; + const int nJuncs = net->Njuncs; + const int nPats = net->Npats; + + double *Ucf = p->Ucf; + + int j; + Pdemand demand; + Psource source; + double hTmp; + double value = v; + + if (!p->Openflag) return 102; + if (index <= 0 || index > nNodes) return 203; + switch (code) + { + case EN_ELEVATION: + if (index <= nJuncs) Node[index].El = value / Ucf[ELEV]; + else + { + value = (value / Ucf[ELEV]) - Node[index].El; + j = index - nJuncs; + Tank[j].H0 += value; + Tank[j].Hmin += value; + Tank[j].Hmax += value; + Node[index].El += value; + hyd->NodeHead[index] += value; + } + break; + + case EN_BASEDEMAND: + // NOTE: primary demand category is last on demand list + if (index <= nJuncs) + { + for (demand = Node[index].D; demand != NULL; demand = demand->next) + { + if (demand->next == NULL) demand->Base = value / Ucf[FLOW]; + } + } + break; + + case EN_PATTERN: + // NOTE: primary demand category is last on demand list + j = ROUND(value); + if (j < 0 || j > nPats) return 205; + if (index <= nJuncs) + { + for (demand = Node[index].D; demand != NULL; demand = demand->next) + { + if (demand->next == NULL) demand->Pat = j; + } + } + else Tank[index - nJuncs].Pat = j; + break; + + case EN_EMITTER: + if (index > nJuncs) return 203; + if (value < 0.0) return 202; + if (value > 0.0) value = pow((Ucf[FLOW] / value), hyd->Qexp) / Ucf[PRESSURE]; + Node[index].Ke = value; + break; + + case EN_INITQUAL: + if (value < 0.0) return 202; + Node[index].C0 = value / Ucf[QUALITY]; + if (index > nJuncs) Tank[index - nJuncs].C = Node[index].C0; + break; + + case EN_SOURCEQUAL: + case EN_SOURCETYPE: + case EN_SOURCEPAT: + if (value < 0.0) return 202; + source = Node[index].S; + if (source == NULL) + { + source = (struct Ssource *)malloc(sizeof(struct Ssource)); + if (source == NULL) return 101; + source->Type = CONCEN; + source->C0 = 0.0; + source->Pat = 0; + Node[index].S = source; + } + if (code == EN_SOURCEQUAL) source->C0 = value; + else if (code == EN_SOURCEPAT) + { + j = ROUND(value); + if (j < 0 || j > nPats) return 205; + source->Pat = j; + } + else // code == EN_SOURCETYPE + { + j = ROUND(value); + if (j < CONCEN || j > FLOWPACED) return 251; + else source->Type = (char)j; + } + return 0; + + case EN_TANKLEVEL: + if (index <= nJuncs) return 251; + j = index - nJuncs; + if (Tank[j].A == 0.0) /* Tank is a reservoir */ + { + Tank[j].H0 = value / Ucf[ELEV]; + Tank[j].Hmin = Tank[j].H0; + Tank[j].Hmax = Tank[j].H0; + Node[index].El = Tank[j].H0; + hyd->NodeHead[index] = Tank[j].H0; + } + else + { + value = Node[index].El + value / Ucf[ELEV]; + if (value > Tank[j].Hmax || value < Tank[j].Hmin) return 202; + Tank[j].H0 = value; + Tank[j].V0 = tankvolume(p, j, Tank[j].H0); + // Resetting Volume in addition to initial volume + Tank[j].V = Tank[j].V0; + hyd->NodeHead[index] = Tank[j].H0; + } + break; + + case EN_TANKDIAM: + if (value <= 0.0) return 202; + if (index <= nJuncs) return 251; + j = index - nJuncs; + if (j > 0 && Tank[j].A > 0.0) + { + value /= Ucf[ELEV]; + Tank[j].A = PI * SQR(value) / 4.0; + Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); + Tank[j].V0 = tankvolume(p, j, Tank[j].H0); + Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); + } + else return 251; + break; + + case EN_MINVOLUME: + if (value < 0.0) return 202; + if (index <= nJuncs) return 251; + j = index - nJuncs; + if (j > 0 && Tank[j].A > 0.0) + { + Tank[j].Vmin = value / Ucf[VOLUME]; + Tank[j].V0 = tankvolume(p, j, Tank[j].H0); + Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); + } + else return 251; + break; + + case EN_MINLEVEL: + if (value < 0.0) return 202; + if (index <= nJuncs) return 251; // not a tank or reservoir + j = index - nJuncs; + if (Tank[j].A == 0.0) return 251; // node is a reservoir + hTmp = value / Ucf[ELEV] + Node[index].El; + if (hTmp < Tank[j].Hmax && hTmp <= Tank[j].H0) + { + if (Tank[j].Vcurve > 0) return 202; + Tank[j].Hmin = hTmp; + Tank[j].Vmin = (Tank[j].Hmin - Node[index].El) * Tank[j].A; + } + else return 251; + break; + + case EN_MAXLEVEL: + if (value < 0.0) return 202; + if (index <= nJuncs) return 251; // not a tank or reservoir + j = index - nJuncs; + if (Tank[j].A == 0.0) return 251; // node is a reservoir + hTmp = value / Ucf[ELEV] + Node[index].El; + if (hTmp > Tank[j].Hmin && hTmp >= Tank[j].H0) + { + if (Tank[j].Vcurve > 0) return 202; + Tank[j].Hmax = hTmp; + Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); + } + else return 251; + break; + + case EN_MIXMODEL: + j = ROUND(value); + if (index <= nJuncs) return 251; + if (j < MIX1 || j > LIFO) return 202; + if (index > nJuncs && Tank[index - nJuncs].A > 0.0) + { + Tank[index - nJuncs].MixModel = (char)j; + } + else return 251; + break; + + case EN_MIXFRACTION: + if (value < 0.0 || value > 1.0) return 202; + if (index <= nJuncs) return 251; + j = index - nJuncs; + if (j > 0 && Tank[j].A > 0.0) + { + Tank[j].V1max = value * Tank[j].Vmax; + } + break; + + case EN_TANK_KBULK: + if (index <= nJuncs) return 251; + j = index - nJuncs; + if (j > 0 && Tank[j].A > 0.0) + { + Tank[j].Kb = value / SECperDAY; + qual->Reactflag = 1; + } + else return 251; + break; + + default: + return 251; + } + return 0; +} + +int DLLEXPORT EN_getcoord(EN_Project p, int index, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y) +/*---------------------------------------------------------------- +** Input: index = node index +** Output: x = node x-coordinate +** y = node y-coordinate +** Returns: error code +** Purpose: retrieves the coordinates of a node +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Snode *node; + + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Nnodes) return 203; + + // check if node have coords + node = &net->Node[index]; + if (node->X == MISSING || + node->Y == MISSING) return 254; + + *x = (EN_API_FLOAT_TYPE)(node->X); + *y = (EN_API_FLOAT_TYPE)(node->Y); + return 0; +} + +int DLLEXPORT EN_setcoord(EN_Project p, int index, EN_API_FLOAT_TYPE x, + EN_API_FLOAT_TYPE y) +/*---------------------------------------------------------------- +** Input: index = node index +** x = node x-coordinate +** y = node y-coordinate +** Output: none +** Returns: error code +** Purpose: sets the coordinates of a node +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Snode *node; + + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Nnodes) return 203; + node = &net->Node[index]; + node->X = x; + node->Y = y; + return 0; +} + +/******************************************************************** + + Nodal Demand Functions + +********************************************************************/ + +int DLLEXPORT EN_getdemandmodel(EN_Project p, int *type, EN_API_FLOAT_TYPE *pmin, + EN_API_FLOAT_TYPE *preq, EN_API_FLOAT_TYPE *pexp) +/*---------------------------------------------------------------- +** Input: none +** Output: type = type of demand model (see EN_DemandModel) +** pmin = minimum pressure for any demand +** preq = required pressure for full demand +** pexp = exponent in pressure dependent demand formula +** Returns: error code +** Purpose: retrieves the parameters of a project's demand model +**---------------------------------------------------------------- +*/ +{ + *type = p->hydraul.DemandModel; + *pmin = (EN_API_FLOAT_TYPE)(p->hydraul.Pmin * p->Ucf[PRESSURE]); + *preq = (EN_API_FLOAT_TYPE)(p->hydraul.Preq * p->Ucf[PRESSURE]); + *pexp = (EN_API_FLOAT_TYPE)(p->hydraul.Pexp); + return 0; +} + +int DLLEXPORT EN_setdemandmodel(EN_Project p, int type, EN_API_FLOAT_TYPE pmin, + EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp) +/*---------------------------------------------------------------- +** Input: type = type of demand model (see EN_DemandModel) +** pmin = minimum pressure for any demand +** preq = required pressure for full demand +** pexp = exponent in pressure dependent demand formula +** Output: none +** Returns: error code +** Purpose: sets the parameters of a project's demand model +**---------------------------------------------------------------- +*/ +{ + if (type < 0 || type > EN_PDA) return 251; + if (pmin > preq || pexp <= 0.0) return 202; + p->hydraul.DemandModel = type; + p->hydraul.Pmin = pmin / p->Ucf[PRESSURE]; + p->hydraul.Preq = preq / p->Ucf[PRESSURE]; + p->hydraul.Pexp = pexp; + return 0; +} + +int DLLEXPORT EN_getnumdemands(EN_Project p, int nodeIndex, int *numDemands) +/*---------------------------------------------------------------- +** Input: nodeIndex = node index +** Output: numDemands = number of demand categories +** Returns: error code +** Purpose: retrieves the number of demand categories for a node +**---------------------------------------------------------------- +*/ +{ + Pdemand d; + int n = 0; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203; + + // Count the number of demand categories + for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next) n++; + *numDemands = n; + return 0; +} + +int DLLEXPORT EN_getbasedemand(EN_Project p, int nodeIndex, int demandIdx, + EN_API_FLOAT_TYPE *baseDemand) +/*---------------------------------------------------------------- +** Input: nodeIndex = node index +** demandIdx = demand category index +** Output: baseDemand = baseline demand value +** Returns: error code +** Purpose: retrieves the baseline value for a node's demand category +**---------------------------------------------------------------- +*/ +{ + Pdemand d; + int n = 1; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203; + + // Retrieve demand for specified category + if (nodeIndex <= p->network.Njuncs) + { + for (d = p->network.Node[nodeIndex].D; n < demandIdx && d->next != NULL; + d = d->next) n++; + if (n != demandIdx) return 253; + *baseDemand = (EN_API_FLOAT_TYPE)(d->Base * p->Ucf[FLOW]); + } + else *baseDemand = (EN_API_FLOAT_TYPE)(0.0); + return 0; +} + +int DLLEXPORT EN_setbasedemand(EN_Project p, int nodeIndex, int demandIdx, + EN_API_FLOAT_TYPE baseDemand) +/*---------------------------------------------------------------- +** Input: nodeIndex = node index +** demandIdx = demand category index +** baseDemand = baseline demand value +** Output: none +** Returns: error code +** Purpose: sets the baseline value for a node's demand category +**---------------------------------------------------------------- +*/ +{ + Pdemand d; + int n = 1; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203; + + // Set baseline demand for specified category + if (nodeIndex <= p->network.Njuncs) + { + for (d = p->network.Node[nodeIndex].D; n < demandIdx && d->next != NULL; + d = d->next) n++; + if (n != demandIdx) return 253; + d->Base = baseDemand / p->Ucf[FLOW]; + } + return 0; +} + +int DLLEXPORT EN_getdemandname(EN_Project p, int nodeIndex, int demandIdx, char *demandName) +/*---------------------------------------------------------------- +** Input: nodeIndex = node index +** demandIdx = demand category index +** Output: demandname = demand category name +** Returns: error code +** Purpose: retrieves the name assigned to a node's demand category +**---------------------------------------------------------------- +*/ +{ + Pdemand d; + int n = 1; + + strcpy(demandName, ""); + + // Check for valid arguments + if (!p->Openflag) return 102; + if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) return 203; + + // Locate demand category record and retrieve its name + for (d = p->network.Node[nodeIndex].D; + n < demandIdx && d->next != NULL; d = d->next) n++; + if (n != demandIdx) return 253; + strcpy(demandName, d->Name); + return 0; +} + +int DLLEXPORT EN_setdemandname(EN_Project p, int nodeIndex, int demandIdx, char *demandName) +/*---------------------------------------------------------------- +** Input: nodeIndex = node index +** demandIdx = demand category index +** demandName = name of demand category +** Output: none +** Returns: error code +** Purpose: assigns a name to a node's demand category +**---------------------------------------------------------------- +*/ +{ + Pdemand d; + int n = 1; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) return 203; + + // Locate demand category record and assign demandName to it + for (d = p->network.Node[nodeIndex].D; + n < demandIdx && d->next != NULL; d = d->next) n++; + if (n != demandIdx) return 253; + strncpy(d->Name, demandName, MAXMSG); + return 0; +} + +int DLLEXPORT EN_getdemandpattern(EN_Project p, int nodeIndex, int demandIdx, int *patIdx) +/*---------------------------------------------------------------- +** Input: nodeIndex = node index +** demandIdx = demand category index +** Output: patIdx = time pattern index +** Returns: error code +** Purpose: retrieves the time pattern assigned to a node's +** demand category +**---------------------------------------------------------------- +*/ +{ + Pdemand d; + int n = 1; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203; + for (d = p->network.Node[nodeIndex].D; + n < demandIdx && d->next != NULL; d = d->next) n++; + if (n != demandIdx) return 253; + *patIdx = d->Pat; + return 0; +} + +int DLLEXPORT EN_setdemandpattern(EN_Project p, int nodeIndex, int demandIdx, int patIdx) +/*---------------------------------------------------------------- +** Input: nodeIndex = node index +** demandIdx = demand category index +** patIdx = time pattern index +** Output: none +** Returns: error code +** Purpose: assigns a time pattern to a node's demand category +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + Pdemand d; + int n = 1; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (nodeIndex <= 0 || nodeIndex > net->Nnodes) return 203; + if (patIdx <= 0 || patIdx > net->Npats) return 205; + + // Locate demand category record and assign time pattern to it + if (nodeIndex <= net->Njuncs) + { + for (d = net->Node[nodeIndex].D; + n < demandIdx && d->next != NULL; d = d->next) n++; + if (n != demandIdx) return 253; + d->Pat = patIdx; + } + return 0; +} + +/******************************************************************** + + Link Functions + +********************************************************************/ + +int DLLEXPORT EN_addlink(EN_Project p, char *id, EN_LinkType linkType, + char *fromNode, char *toNode) +/*---------------------------------------------------------------- +** Input: id = link ID name +** type = link type (see EN_LinkType) +** fromNode = name of link's starting node +** toNode = name of link's ending node +** Output: none +** Returns: error code +** Purpose: adds a new link to a project +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Hydraul *hyd = &p->hydraul; + + int i, n, size; + int n1, n2; + Slink *link; + Spump *pump; + + // Check if a link with same id already exists + if (!p->Openflag) return 102; + if (EN_getlinkindex(p, id, &i) == 0) return 215; + + // Lookup the link's from and to nodes + n1 = hashtable_find(net->NodeHashTable, fromNode); + n2 = hashtable_find(net->NodeHashTable, toNode); + if (n1 == 0 || n2 == 0) return 203; + + // Check that id name is not too long + if (strlen(id) > MAXID) return 250; + + net->Nlinks++; + n = net->Nlinks; + + // Grow link-related arrays to accomodate the new link + size = (n + 1) * sizeof(Slink); + net->Link = (Slink *)realloc(net->Link, size); + size = (n + 1) * sizeof(double); + hyd->LinkFlow = (double *)realloc(hyd->LinkFlow, size); + hyd->LinkSetting = (double *)realloc(hyd->LinkSetting, size); + size = (n + 1) * sizeof(StatusType); + hyd->LinkStatus = (StatusType *)realloc(hyd->LinkStatus, size); + + link = &net->Link[n]; + strncpy(link->ID, id, MAXID); + + if (linkType <= EN_PIPE) net->Npipes++; + else if (linkType == EN_PUMP) + { + // Grow pump array to accomodate the new link + net->Npumps++; + size = (net->Npumps + 1) * sizeof(Spump); + net->Pump = (Spump *)realloc(net->Pump, size); + pump = &net->Pump[net->Npumps]; + pump->Link = n; + pump->Ptype = 0; + pump->Q0 = 0; + pump->Qmax = 0; + pump->Hmax = 0; + pump->H0 = 0; + pump->R = 0; + pump->N = 0; + pump->Hcurve = 0; + pump->Ecurve = 0; + pump->Upat = 0; + pump->Epat = 0; + pump->Ecost = 0; + pump->Energy.TotalCost = MISSING; + } + else + { + // Grow valve array to accomodate the new link + net->Nvalves++; + size = (net->Nvalves + 1) * sizeof(Svalve); + net->Valve = (Svalve *)realloc(net->Valve, size); + net->Valve[net->Nvalves].Link = n; + } + + link->Type = linkType; + link->N1 = n1; + link->N2 = n2; + link->Status = OPEN; + + if (linkType == EN_PUMP) + { + link->Kc = 1.0; // Speed factor + link->Km = 0.0; // Horsepower + link->Len = 0.0; + } + else if (linkType <= EN_PIPE) // pipe or cvpipe + { + link->Diam = 10 / p->Ucf[DIAM]; + link->Kc = 100; // Rough. coeff + link->Km = 0.0; // Loss coeff + link->Len = 1000; + } + else // Valve + { + link->Diam = 10 / p->Ucf[DIAM]; + link->Kc = 0.0; // Valve setting. + link->Km = 0.0; // Loss coeff + link->Len = 0.0; + link->Status = ACTIVE; + } + link->Kb = 0; + link->Kw = 0; + link->R = 0; + link->Rc = 0; + link->Rpt = 0; + strcpy(link->Comment, ""); + + hashtable_insert(net->LinkHashTable, link->ID, n); + return 0; +} + +int DLLEXPORT EN_deletelink(EN_Project p, int index, int actionCode) +/*---------------------------------------------------------------- +** Input: index = index of the link to delete +** actionCode = how to treat controls that contain the link: +** EN_UNCONDITIONAL deletes all such controls plus the link, +** EN_CONDITIONAL does not delete the link if it appears +** in a control and returns an error code +** Output: none +** Returns: error code +** Purpose: deletes a link from a project. +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + int i; + int pumpindex; + int valveindex; + EN_LinkType linkType; + Slink *link; + + // Check that link exists + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Nlinks) 204; + if (actionCode < EN_UNCONDITIONAL || actionCode > EN_CONDITIONAL) return 251; + + // Deletion will be cancelled if link appears in any controls + if (actionCode == EN_CONDITIONAL) + { + actionCode = incontrols(p, LINK, index); + if (actionCode > 0) return 261; + } + + // Get references to the link and its type + link = &net->Link[index]; + EN_getlinktype(p, index, &linkType); + + // Remove link from its hash table + hashtable_delete(net->LinkHashTable, link->ID); + + // Shift position of higher entries in Link array down one + for (i = index; i <= net->Nlinks - 1; i++) + { + net->Link[i] = net->Link[i + 1]; + // ... update link's entry in the hash table + hashtable_update(net->LinkHashTable, net->Link[i].ID, i); + } + + // Adjust references to higher numbered links for pumps & valves + for (i = 1; i <= net->Npumps; i++) + { + if (net->Pump[i].Link > index) net->Pump[i].Link -= 1; + } + for (i = 1; i <= net->Nvalves; i++) + { + if (net->Valve[i].Link > index) net->Valve[i].Link -= 1; + } + + // Delete any pump associated with the deleted link + if (linkType == PUMP) + { + pumpindex = findpump(net, index); + for (i = pumpindex; i <= net->Npumps - 1; i++) + { + net->Pump[i] = net->Pump[i + 1]; + } + net->Npumps--; + } + + // Delete any valve (linkType > EN_PUMP) associated with the deleted link + if (linkType > PUMP) + { + valveindex = findvalve(net, index); + for (i = valveindex; i <= net->Nvalves - 1; i++) + { + net->Valve[i] = net->Valve[i + 1]; + } + net->Nvalves--; + } + + // Delete any control containing the link + for (i = net->Ncontrols; i >= 1; i--) + { + if (net->Control[i].Link == index) EN_deletecontrol(p, i); + } + + // Adjust higher numbered link indices in remaining controls + for (i = 1; i <= net->Ncontrols; i++) + { + if (net->Control[i].Link > index) net->Control[i].Link--; + } + + // Make necessary adjustments to rule-based controls + adjustrules(p, EN_R_LINK, index); // see RULES.C + + // Reduce link count by one + net->Nlinks--; + return 0; +} + +int DLLEXPORT EN_getlinkindex(EN_Project p, char *id, int *index) +/*---------------------------------------------------------------- +** Input: id = link ID name +** Output: index = link index +** Returns: error code +** Purpose: retrieves the index of a link +**---------------------------------------------------------------- +*/ +{ + *index = 0; + if (!p->Openflag) return 102; + *index = findlink(&p->network, id); + if (*index == 0) return 204; + else return 0; +} + +int DLLEXPORT EN_getlinkid(EN_Project p, int index, char *id) +/*---------------------------------------------------------------- +** Input: index = link index +** Output: id = link ID name +** Returns: error code +** Purpose: retrieves the ID name of a link +**---------------------------------------------------------------- +*/ +{ + strcpy(id, ""); + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Nlinks) return 204; + strcpy(id, p->network.Link[index].ID); + return 0; +} + +int DLLEXPORT EN_setlinkid(EN_Project p, int index, char *newid) +/*---------------------------------------------------------------- +** Input: index = link index +** id = link ID name +** Output: none +** Returns: error code +** Purpose: sets the ID name of a link +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + size_t n; + + // Check for valid arguments + if (index <= 0 || index > net->Nlinks) return 204; + n = strlen(newid); + if (n < 1 || n > MAXID) return 211; + if (strcspn(newid, " ;") < n) return 211; + + // Check if another link with same name exists + if (hashtable_find(net->LinkHashTable, newid) > 0) return 215; + + // Replace the existing link ID with the new value + hashtable_delete(net->LinkHashTable, net->Link[index].ID); + strncpy(net->Link[index].ID, newid, MAXID); + hashtable_insert(net->LinkHashTable, net->Link[index].ID, index); + return 0; +} + +int DLLEXPORT EN_getlinktype(EN_Project p, int index, EN_LinkType *code) +/*---------------------------------------------------------------- +** Input: index = link index +** Output: code = link type (see EN_LinkType) +** Returns: error code +** Purpose: retrieves the type code of a link +**---------------------------------------------------------------- +*/ +{ + *code = -1; + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Nlinks) return 204; + *code = p->network.Link[index].Type; + return 0; +} + +int DLLEXPORT EN_setlinktype(EN_Project p, int *index, EN_LinkType type, int actionCode) +/*---------------------------------------------------------------- +** Input: index = link index +** type = link type (see EN_LinkType) +** actionCode = how to treat controls that contain the link: +** EN_UNCONDITIONAL deletes all such controls, +** EN_CONDITIONAL cancels the type change if the link appears +** in a control and returns an error code +** Output: none +** Returns: error code +** Purpose: retrieves the ID name of an indexed link +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + int i = *index, n1, n2; + char id[MAXID + 1]; + char id1[MAXID + 1]; + char id2[MAXID + 1]; + int errcode; + EN_LinkType oldtype; + + // Check for valid input parameters + if (!p->Openflag) return 102; + if (type < 0 || type > GPV || actionCode < EN_UNCONDITIONAL || + actionCode > EN_CONDITIONAL) + { + return 251; + } + + // Check for valid link index + if (i <= 0 || i > net->Nlinks) return 204; + + // Check if current link type equals new type + EN_getlinktype(p, i, &oldtype); + if (oldtype == type) return 0; + + // Type change will be cancelled if link appears in any controls + if (actionCode == EN_CONDITIONAL) + { + actionCode = incontrols(p, LINK, i); + if (actionCode > 0) return 261; + } + + // Pipe changing from or to having a check valve + if (oldtype <= PIPE && type <= PIPE) + { + net->Link[i].Type = type; + if (type == CVPIPE) net->Link[i].Status = OPEN; + return 0; + } + + // Get ID's of link & its end nodes + EN_getlinkid(p, i, id); + EN_getlinknodes(p, i, &n1, &n2); + EN_getnodeid(p, n1, id1); + EN_getnodeid(p, n2, id2); + + // Delete the original link (and any controls containing it) + EN_deletelink(p, i, actionCode); + + // Create a new link of new type and old id + errcode = EN_addlink(p, id, type, id1, id2); + + // Find the index of this new link + EN_getlinkindex(p, id, index); + return errcode; +} + +int DLLEXPORT EN_getlinknodes(EN_Project p, int index, int *node1, int *node2) +/*---------------------------------------------------------------- +** Input: index = link index +** Output: node1 = index of link's starting node +** node2 = index of link's ending node +** Returns: error code +** Purpose: retrieves the start and end nodes of a link +**---------------------------------------------------------------- +*/ +{ + *node1 = 0; + *node2 = 0; + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Nlinks) return 204; + *node1 = p->network.Link[index].N1; + *node2 = p->network.Link[index].N2; + return 0; +} + +int DLLEXPORT EN_setlinknodes(EN_Project p, int index, int node1, int node2) +/*---------------------------------------------------------------- +** Input: index = link index +** node1 = index of link's new starting node +** node2 = index of link's new ending node +** Returns: error code +** Purpose: sets the start and end nodes of a link +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + int type; + + // Check that nodes exist + if (node1 < 0 || node1 > net->Nnodes) return 203; + if (node2 < 0 || node2 > net->Nnodes) return 203; + + // Check for illegal valve connection + type = net->Link[index].Type; + if (type == EN_PRV || type == EN_PSV || type == EN_FCV) + { + // Can't be connected to a fixed grade node + if (node1 > net->Njuncs || + node2 > net->Njuncs) return 219; + + // Can't be connected to another pressure/flow control valve + if (!valvecheck(p, type, node1, node2)) return 220; + } + + // Assign new end nodes to link + net->Link[index].N1 = node1; + net->Link[index].N2 = node2; + return 0; +} + +int DLLEXPORT EN_getlinkvalue(EN_Project p, int index, EN_LinkProperty code, + EN_API_FLOAT_TYPE *value) +/*---------------------------------------------------------------- +** Input: index = link index +** code = link property code (see EN_LinkProperty) +** Output: value = link property value +** Returns: error code +** Purpose: retrieves a property value for a link +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Hydraul *hyd = &p->hydraul; + + double a, h, q, v = 0.0; + int returnValue = 0; + int pmp; + Slink *Link = net->Link; + Spump *Pump = net->Pump; + double *Ucf = p->Ucf; + double *LinkFlow = hyd->LinkFlow; + double *LinkSetting = hyd->LinkSetting; + + // Check for valid arguments + *value = 0.0; + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Nlinks) return 204; + + // Retrieve called-for property + switch (code) + { + case EN_DIAMETER: + if (Link[index].Type == EN_PUMP) v = 0.0; + else v = Link[index].Diam * Ucf[DIAM]; + break; + + case EN_LENGTH: + v = Link[index].Len * Ucf[ELEV]; + break; + + case EN_ROUGHNESS: + if (Link[index].Type <= EN_PIPE) + { + if (hyd->Formflag == DW) v = Link[index].Kc * (1000.0 * Ucf[ELEV]); + else v = Link[index].Kc; + } + else v = 0.0; + break; + + case EN_MINORLOSS: + if (Link[index].Type != EN_PUMP) + { + v = Link[index].Km; + v *= (SQR(Link[index].Diam) * SQR(Link[index].Diam) / 0.02517); + } + else v = 0.0; + break; + + case EN_INITSTATUS: + if (Link[index].Status <= CLOSED) v = 0.0; + else v = 1.0; + break; + + case EN_INITSETTING: + if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) + { + return EN_getlinkvalue(p, index, EN_ROUGHNESS, value); + } + v = Link[index].Kc; + switch (Link[index].Type) + { + case EN_PRV: + case EN_PSV: + case EN_PBV: + v *= Ucf[PRESSURE]; + break; + case EN_FCV: + v *= Ucf[FLOW]; + default: + break; + } + break; + + case EN_KBULK: + v = Link[index].Kb * SECperDAY; + break; + + case EN_KWALL: + v = Link[index].Kw * SECperDAY; + break; + + case EN_FLOW: + if (hyd->LinkStatus[index] <= CLOSED) v = 0.0; + else v = LinkFlow[index] * Ucf[FLOW]; + break; + + case EN_VELOCITY: + if (Link[index].Type == EN_PUMP) v = 0.0; + else if (hyd->LinkStatus[index] <= CLOSED) v = 0.0; + else + { + q = ABS(LinkFlow[index]); + a = PI * SQR(Link[index].Diam) / 4.0; + v = q / a * Ucf[VELOCITY]; + } + break; + + case EN_HEADLOSS: + if (hyd->LinkStatus[index] <= CLOSED) v = 0.0; + else + { + h = hyd->NodeHead[Link[index].N1] - hyd->NodeHead[Link[index].N2]; + if (Link[index].Type != EN_PUMP) h = ABS(h); + v = h * Ucf[HEADLOSS]; + } + break; + + case EN_STATUS: + if (hyd->LinkStatus[index] <= CLOSED) v = 0.0; + else v = 1.0; + break; + + case EN_STATE: + v = hyd->LinkStatus[index]; + + if (Link[index].Type == EN_PUMP) + { + pmp = findpump(net, index); + if (hyd->LinkStatus[index] >= OPEN) + { + if (hyd->LinkFlow[index] > hyd->LinkSetting[index] * Pump[pmp].Qmax) + { + v = XFLOW; + } + if (hyd->LinkFlow[index] < 0.0) v = XHEAD; + } + } + break; + + case EN_CONST_POWER: + v = 0; + if (Link[index].Type == EN_PUMP) + { + pmp = findpump(net, index); + if (Pump[pmp].Ptype == CONST_HP) v = Link[index].Km; // Power in HP + } + break; + + case EN_SPEED: + v = 0; + if (Link[index].Type == EN_PUMP) + { + pmp = findpump(net, index); + v = Link[index].Kc; + } + break; + + case EN_SETTING: + if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) + { + return EN_getlinkvalue(p, index, EN_ROUGHNESS, value); + } + if (LinkSetting[index] == MISSING) v = 0.0; + else v = LinkSetting[index]; + switch (Link[index].Type) + { + case EN_PRV: + case EN_PSV: + case EN_PBV: + v *= Ucf[PRESSURE]; + break; + case EN_FCV: + v *= Ucf[FLOW]; + default: + break; + } + break; + + case EN_ENERGY: + getenergy(p, index, &v, &a); + break; + + case EN_LINKQUAL: + v = avgqual(p, index) * Ucf[LINKQUAL]; + break; + + case EN_LINKPATTERN: + if (Link[index].Type == EN_PUMP) + { + v = (double)Pump[findpump(&p->network, index)].Upat; + } + break; + + case EN_EFFICIENCY: + getenergy(p, index, &a, &v); + break; + + case EN_PRICEPATTERN: + if (Link[index].Type == EN_PUMP) + { + v = (double)Pump[findpump(&p->network, index)].Epat; + } + break; + + case EN_HEADCURVE: + if (Link[index].Type == EN_PUMP) + { + v = (double)Pump[findpump(&p->network, index)].Hcurve; + if (v == 0) returnValue = 226; + } + else + { + v = 0; + returnValue = 211; + } + break; + + case EN_EFFICIENCYCURVE: + if (Link[index].Type == EN_PUMP) + { + v = (double)Pump[findpump(&p->network, index)].Ecurve; + if (v == 0) returnValue = 268; + } + else + { + v = 0; + returnValue = 211; + } + + default: + v = 0; + returnValue = 251; + } + *value = (EN_API_FLOAT_TYPE)v; + return returnValue; +} + +int DLLEXPORT EN_setlinkvalue(EN_Project p, int index, int code, EN_API_FLOAT_TYPE v) +/*---------------------------------------------------------------- +** Input: index = link index +** code = link property code (see EN_LinkProperty) +** v = property value +** Output: none +** Returns: error code +** Purpose: sets a property value for a link +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Hydraul *hyd = &p->hydraul; + Quality *qual = &p->quality; + + Slink *Link = net->Link; + double *Ucf = p->Ucf; + double *LinkSetting = hyd->LinkSetting; + char s; + double r, value = v; + + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Nlinks) return 204; + switch (code) + { + case EN_DIAMETER: + if (Link[index].Type != EN_PUMP) + { + if (value <= 0.0) return 202; + value /= Ucf[DIAM]; // Convert to feet + r = Link[index].Diam / value; // Ratio of old to new diam + Link[index].Km *= SQR(r) * SQR(r); // Adjust minor loss factor + Link[index].Diam = value; // Update diameter + resistcoeff(p, index); // Update resistance coeff. + } + break; + + case EN_LENGTH: + if (Link[index].Type <= EN_PIPE) + { + if (value <= 0.0) return 202; + Link[index].Len = value / Ucf[ELEV]; + resistcoeff(p, index); + } + break; + + case EN_ROUGHNESS: + if (Link[index].Type <= EN_PIPE) + { + if (value <= 0.0) return 202; + Link[index].Kc = value; + if (hyd->Formflag == DW) Link[index].Kc /= (1000.0 * Ucf[ELEV]); + resistcoeff(p, index); + } + break; + + case EN_MINORLOSS: + if (Link[index].Type != EN_PUMP) + { + if (value <= 0.0) return 202; + Link[index].Km = 0.02517 * value / SQR(Link[index].Diam) / + SQR(Link[index].Diam); + } + break; + + case EN_INITSTATUS: + case EN_STATUS: + // Cannot set status for a check valve + if (Link[index].Type == EN_CVPIPE) return 207; + s = (char)ROUND(value); + if (s < 0 || s > 1) return 251; + if (code == EN_INITSTATUS) + { + setlinkstatus(p, index, s, &Link[index].Status, &Link[index].Kc); + } + else + { + setlinkstatus(p, index, s, &hyd->LinkStatus[index], &LinkSetting[index]); + } + break; + + case EN_INITSETTING: + case EN_SETTING: + if (value < 0.0) return 202; + if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) + { + return EN_setlinkvalue(p, index, EN_ROUGHNESS, v); + } + else + { + switch (Link[index].Type) + { + case EN_PUMP: + break; + case EN_PRV: + case EN_PSV: + case EN_PBV: + value /= Ucf[PRESSURE]; + break; + case EN_FCV: + value /= Ucf[FLOW]; + break; + case EN_TCV: + break; + case EN_GPV: + return 202; // Cannot modify setting for GPV + default: + return 251; + } + if (code == EN_INITSETTING) + { + setlinksetting(p, index, value, &Link[index].Status, &Link[index].Kc); + } + else + { + setlinksetting(p, index, value, &hyd->LinkStatus[index], + &LinkSetting[index]); + } + } + break; + + case EN_KBULK: + if (Link[index].Type <= EN_PIPE) + { + Link[index].Kb = value / SECperDAY; + qual->Reactflag = 1; + } + break; + + case EN_KWALL: + if (Link[index].Type <= EN_PIPE) + { + Link[index].Kw = value / SECperDAY; + qual->Reactflag = 1; + } + break; + + default: + return 251; + } + return 0; +} + +/******************************************************************** + + Pump Functions + +********************************************************************/ + +int DLLEXPORT EN_getpumptype(EN_Project p, int index, int *type) +/*---------------------------------------------------------------- +** Input: index = index of a pump link +** Output: type = type of pump characteristic curve (see EN_PumpType) +** Returns: error code +** Purpose: retrieves the type of characteristic curve used by a pump +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + Slink *Link = net->Link; + Spump *Pump = net->Pump; + const int Nlinks = net->Nlinks; + + *type = -1; + if (!p->Openflag) return 102; + if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) return 204; + *type = Pump[findpump(&p->network, index)].Ptype; + return 0; +} + +int DLLEXPORT EN_getheadcurveindex(EN_Project p, int index, int *curveindex) +/*---------------------------------------------------------------- +** Input: index = index of a pump link +** Output: curveindex = index of a pump's characteristic curve +** Returns: error code +** Purpose: retrieves the index of a pump's characteristic curve +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + Slink *Link = net->Link; + Spump *Pump = net->Pump; + const int Nlinks = net->Nlinks; + + if (!p->Openflag) return 102; + if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) return 204; + *curveindex = Pump[findpump(net, index)].Hcurve; + return 0; +} + +int DLLEXPORT EN_setheadcurveindex(EN_Project p, int index, int curveindex) +/*---------------------------------------------------------------- +** Input: index = index of a pump link +** curveindex = index of a pump's characteristic curve +** Output: none +** Returns: error code +** Purpose: assigns a new characteristic curve to a pump +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + Slink *Link = net->Link; + const int Nlinks = net->Nlinks; + const int Ncurves = net->Ncurves; + double *Ucf = p->Ucf; + int pIdx; + Spump *pump; + + // Check for valid parameters + if (!p->Openflag) return 102; + if (index < 1 || index > net->Nlinks || EN_PUMP != net->Link[index].Type) return 204; + if (curveindex <= 0 || curveindex > net->Ncurves) return 206; + + // Assign the new curve to the pump + pIdx = findpump(net, index); + pump = &p->network.Pump[pIdx]; + pump->Ptype = NOCURVE; + pump->Hcurve = curveindex; + + // Update the pump curve's parameters and convert their units + updatepumpparams(p, pIdx); + if (pump->Ptype == POWER_FUNC) + { + pump->H0 /= Ucf[HEAD]; + pump->R *= (pow(Ucf[FLOW], pump->N) / Ucf[HEAD]); + } + pump->Q0 /= Ucf[FLOW]; + pump->Qmax /= Ucf[FLOW]; + pump->Hmax /= Ucf[HEAD]; + + // Designate the newly assigned curve as being a Pump Curve + p->network.Curve[curveindex].Type = P_CURVE; + return 0; +} + +/******************************************************************** + + Time Pattern Functions + +********************************************************************/ + +int DLLEXPORT EN_addpattern(EN_Project p, char *id) +/*---------------------------------------------------------------- +** Input: id = time pattern ID name +** Output: none +** Returns: error code +** Purpose: adds a new time pattern to a project +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Parser *parser = &p->parser; + Hydraul *hyd = &p->hydraul; + + int i, n, err = 0; + Spattern *pat; + + // Check if a pattern with same id already exists + if (!p->Openflag) return 102; + if (EN_getpatternindex(p, id, &i) == 0) return 215; + + // Check that id name is not too long + if (strlen(id) > MAXID) return 250; + + // Expand the project's array of patterns + n = net->Npats + 1; + net->Pattern = (Spattern *)realloc(net->Pattern, (n + 1) * sizeof(Spattern)); + + // Assign properties to the new pattern + pat = &net->Pattern[n]; + strcpy(pat->ID, id); + pat->Length = 1; + pat->F = (double *)calloc(1, sizeof(double)); + if (pat->F == NULL) err = 1; + else pat->F[0] = 1.0; + + // Abort if memory allocation error + if (err) + { + free(pat->F); + return 101; + } + + // 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; +} + +int DLLEXPORT EN_getpatternindex(EN_Project p, char *id, int *index) +/*---------------------------------------------------------------- +** Input: id = time pattern name +** Output: index = time pattern index +** Returns: error code +** Purpose: retrieves the index of a time pattern **---------------------------------------------------------------- */ { int i; - EN_Project *p = (EN_Project*)ph; - EN_Network *net = &p->network; - if (index <= 0 || index > net->Ncontrols) + *index = 0; + if (!p->Openflag) return 102; + for (i = 1; i <= p->network.Npats; i++) { - return (241); + if (strcmp(id, p->network.Pattern[i].ID) == 0) + { + *index = i; + return 0; + } } + *index = 0; + return 205; +} + +int DLLEXPORT EN_getpatternid(EN_Project p, int index, char *id) +/*---------------------------------------------------------------- +** Input: index = time pattern index +** Output: id = time pattern ID name +** Returns: error code +** Purpose: retrieves the ID name of a time pattern +**---------------------------------------------------------------- +*/ +{ + strcpy(id, ""); + if (!p->Openflag)return 102; + if (index < 1 || index > p->network.Npats) return 205; + strcpy(id, p->network.Pattern[index].ID); + return 0; +} + +int DLLEXPORT EN_getpatternlen(EN_Project p, int index, int *len) +/*---------------------------------------------------------------- +** Input: index = time pattern index +** Output: len = number of periods in a time pattern +** Returns: error code +** Purpose: retrieves the number of time periods in a time pattern +**---------------------------------------------------------------- +*/ +{ + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Npats) return 205; + *len = p->network.Pattern[index].Length; + return 0; +} + +int DLLEXPORT EN_getpatternvalue(EN_Project p, int index, int period, EN_API_FLOAT_TYPE *value) +/*---------------------------------------------------------------- +** Input: index = time pattern index +** period = time pattern period +** Output: value = multiplier for a particular time period +** Returns: error code +** Purpose: retrieves the multiplier for a specific time period +** in a time pattern +**---------------------------------------------------------------- +*/ +{ + *value = 0.0; + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Npats) return 205; + if (period < 1 || period > p->network.Pattern[index].Length) return 251; + *value = (EN_API_FLOAT_TYPE)p->network.Pattern[index].F[period - 1]; + return 0; +} + +int DLLEXPORT EN_setpatternvalue(EN_Project p, int index, int period, EN_API_FLOAT_TYPE value) +/*---------------------------------------------------------------- +** Input: index = time pattern index +** period = time pattern period +** value = multiplier for a particular time period +** Output: none +** Returns: error code +** Purpose: sets the multiplier for a specific time period +** in a time pattern +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Spattern *Pattern = net->Pattern; + + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Npats) return 205; + if (period <= 0 || period > Pattern[index].Length) return 251; + Pattern[index].F[period - 1] = value; + return 0; +} + +int DLLEXPORT EN_getaveragepatternvalue(EN_Project p, int index, EN_API_FLOAT_TYPE *value) +/*---------------------------------------------------------------- +** Input: index = time pattern index +** Output: value = average of a time pattern's multipliers +** Returns: error code +** Purpose: retrieves the average multiplier value for a time pattern +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + Spattern *Pattern = net->Pattern; + int i; + + *value = 0.0; + if (!p->Openflag) return 102; + if (index < 1 || index > net->Npats) return 205; + for (i = 0; i < Pattern[index].Length; i++) + { + *value += (EN_API_FLOAT_TYPE)Pattern[index].F[i]; + } + *value /= (EN_API_FLOAT_TYPE)Pattern[index].Length; + return 0; +} + +int DLLEXPORT EN_setpattern(EN_Project p, int index, EN_API_FLOAT_TYPE *f, int n) +/*---------------------------------------------------------------- +** Input: index = time pattern index +** f = an array of multiplier values +** n = number of values in f +** Output: none +** Returns: error code +** Purpose: replaces the multipliers in a time pattern +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + int j; + Spattern *Pattern = net->Pattern; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Npats) return 205; + if (n <= 0) return 202; + + // Re-set number of time periods & reallocate memory for multipliers + Pattern[index].Length = n; + Pattern[index].F = (double *)realloc(Pattern[index].F, n * sizeof(double)); + if (Pattern[index].F == NULL) return 101; + + // Load multipliers into pattern + for (j = 0; j < n; j++) Pattern[index].F[j] = f[j]; + return 0; +} + +/******************************************************************** + + Data Curve Functions + +********************************************************************/ + +int DLLEXPORT EN_addcurve(EN_Project p, char *id) +/*---------------------------------------------------------------- +** Input: id = data curve ID name +** Output: none +** Returns: error code +** Purpose: adds a new data curve to a project +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + int i, n, err = 0; + Scurve *curve; + + // Check if a curve with same id already exists + if (!p->Openflag) return 102; + if (EN_getcurveindex(p, id, &i) == 0) return 215; + + // Check that id name is not too long + if (strlen(id) > MAXID) return 250; + + // Expand the array of curves + n = net->Ncurves + 1; + net->Curve = (Scurve *) realloc(net->Curve, (n + 1) * sizeof(Scurve)); + + // Set the properties of the new curve + curve = &net->Curve[n]; + strcpy(curve->ID, id); + curve->Npts = 1; + curve->Type = G_CURVE; + curve->X = (double *)calloc(1, sizeof(double)); + curve->Y = (double *)calloc(1, sizeof(double)); + if (curve->X == NULL) err = 1; + else if (curve->Y == NULL) err = 1; + else + { + curve->X[0] = 1.0; + curve->Y[0] = 1.0; + } + + // Abort if memory allocation error + if (err) + { + free(curve->X); + free(curve->Y); + return 101; + } + + // Update the number of curves + net->Ncurves = n; + p->parser.MaxCurves = n; + return 0; +} + +int DLLEXPORT EN_getcurveindex(EN_Project p, char *id, int *index) +/*---------------------------------------------------------------- +** Input: id = data curve name +** Output: index = data curve index +** Returns: error code +** Purpose: retrieves the index of a data curve +**---------------------------------------------------------------- +*/ +{ + 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; +} + +int DLLEXPORT EN_getcurveid(EN_Project p, int index, char *id) +/*---------------------------------------------------------------- +** Input: index = data curve index +** Output: id = data curve ID name +** Returns: error code +** Purpose: retrieves the name of a data curve +**---------------------------------------------------------------- +*/ +{ + strcpy(id, ""); + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Ncurves) return 206; + strcpy(id, p->network.Curve[index].ID); + return 0; +} + +int DLLEXPORT EN_getcurvelen(EN_Project p, int index, int *len) +/*---------------------------------------------------------------- +** Input: index = data curve index +** Output: len = number of points in a data curve +** Returns: error code +** Purpose: retrieves the number of points in a data curve +**---------------------------------------------------------------- +*/ +{ + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Ncurves) return 206; + *len = p->network.Curve[index].Npts; + return 0; +} + +int DLLEXPORT EN_getcurvetype(EN_Project p, int curveindex, int *type) +/*---------------------------------------------------------------- +** Input: index = data curve index +** Output: type = type of data curve (see EN_CurveType) +** Returns: error code +** Purpose: retrieves the type assigned to a data curve +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + if (!p->Openflag) return 102; + if (curveindex < 1 || curveindex > net->Ncurves) return 206; + *type = net->Curve[curveindex].Type; + return 0; +} + +int DLLEXPORT EN_getcurvevalue(EN_Project p, int index, int pnt, + EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) +/*---------------------------------------------------------------- +** Input: index = data curve index +** pnt = index of data curve point +** Output: x = x-value of a point on the curve +** y = y-value of a point on the curve +** Returns: error code +** Purpose: retrieves the value of a specific point on a data curve +**---------------------------------------------------------------- +*/ +{ + *x = 0.0; + *y = 0.0; + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Ncurves) return 206; + if (pnt < 1 || pnt > p->network.Curve[index].Npts) return 251; + *x = (EN_API_FLOAT_TYPE)p->network.Curve[index].X[pnt - 1]; + *y = (EN_API_FLOAT_TYPE)p->network.Curve[index].Y[pnt - 1]; + return 0; +} + +int DLLEXPORT EN_setcurvevalue(EN_Project p, int index, int pnt, + EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) +/*---------------------------------------------------------------- +** Input: index = time pattern index +** pnt = index of data curve point +** x = new x-value for point on the curve +** y = new y-value for point on the curve +** Output: none +** Returns: error code +** Purpose: sets the value of a specific point on a data curve +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Scurve *curve; + + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Ncurves) return 206; + curve = &net->Curve[index]; + if (pnt <= 0 || pnt > curve->Npts) return 251; + curve->X[pnt - 1] = x; + curve->Y[pnt - 1] = y; + return 0; +} + +int DLLEXPORT EN_getcurve(EN_Project p, int index, char *id, int *nValues, + EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues) +/*---------------------------------------------------------------- +** Input: index = data curve index +** Output: id = ID name of data curve +** nValues = number of data points on the curve +** xValues = array of x-values for each data point +** yValues = array of y-values for each data point +** Returns: error code +** Purpose: retrieves the data associated with a data curve +** +** The calling program is responsible for making xValues and +** yValues large enough to hold nValues data points. +**---------------------------------------------------------------- +*/ +{ + int i; + Scurve *curve; + + if (!p->Openflag) return 102; + if (index <= 0 || index > p->network.Ncurves) return 206; + curve = &p->network.Curve[index]; + strncpy(id, curve->ID, MAXID); + *nValues = 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]; + } + return 0; +} + +int DLLEXPORT EN_setcurve(EN_Project p, int index, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y, int n) +/*---------------------------------------------------------------- +** Input: index = data curve index +** x = array of x-values +** y = array of y-values +** n = number of data points in the x and y arrays +** Returns: error code +** Purpose: replaces a curve's set of data points +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Scurve *curve; + int j; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Ncurves) return 206; + if (n <= 0) return 202; + + // Re-set number of points & reallocate memory for values + curve = &net->Curve[index]; + curve->Npts = n; + curve->X = (double *)realloc(curve->X, n * sizeof(double)); + curve->Y = (double *)realloc(curve->Y, n * sizeof(double)); + if (curve->X == NULL) return 101; + if (curve->Y == NULL) return 101; + + // Load values into curve + for (j = 0; j < n; j++) + { + curve->X[j] = x[j]; + curve->Y[j] = y[j]; + } + return 0; +} + +/******************************************************************** + + Simple Controls Functions + +********************************************************************/ + +int DLLEXPORT EN_addcontrol(EN_Project p, int *index, int ctype, int lindex, + EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level) +/*---------------------------------------------------------------- +** Input: ctype = type of control (see EN_ControlType) +** lindex = index of link being controlled +** setting = link control setting (e.g., pump speed) +** nindex = index of node controlling a link (for level controls) +** level = control activation level (pressure for junction nodes, +** water level for tank nodes or time value for time-based +** control) +** Output: index = the index of the new control +** Returns: error code +** Purpose: adds a new simple control to a project +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Parser *parser = &p->parser; + + char status = ACTIVE; + int n; + long t = 0; + double s = setting, lvl = level; + double *Ucf = p->Ucf; + Scontrol *control; + + + // Check that project exists + if (!p->Openflag) return 102; + + // Check that controlled link exists + if (lindex <= 0 || lindex > net->Nlinks) return 204; + + // Cannot control check valve + if (net->Link[lindex].Type == EN_CVPIPE) return 207; + + // Check for valid parameters + if (ctype < 0 || ctype > EN_TIMEOFDAY) return 251; + if (ctype == EN_LOWLEVEL || ctype == EN_HILEVEL) + { + if (nindex < 1 || nindex > net->Nnodes) return 203; + } + else nindex = 0; + if (s < 0.0 || lvl < 0.0) return 202; + + // Adjust units of control parameters + switch (net->Link[lindex].Type) + { + case EN_PRV: + case EN_PSV: + case EN_PBV: + s /= Ucf[PRESSURE]; + break; + case EN_FCV: + s /= Ucf[FLOW]; + break; + case EN_GPV: + if (s == 0.0) status = CLOSED; + else if (s == 1.0) status = OPEN; + else return 202; + s = net->Link[lindex].Kc; + break; + case EN_PIPE: + case EN_PUMP: + status = OPEN; + if (s == 0.0) status = CLOSED; + default: + break; + } + + if (ctype == LOWLEVEL || ctype == HILEVEL) + { + if (nindex > net->Njuncs) lvl = net->Node[nindex].El + level / Ucf[ELEV]; + else lvl = net->Node[nindex].El + level / Ucf[PRESSURE]; + } + if (ctype == TIMER) t = (long)ROUND(lvl); + if (ctype == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY; + + // Expand project's array of controls + n = net->Ncontrols + 1; + net->Control = (Scontrol *)realloc(net->Control, (n + 1) * sizeof(Scontrol)); + + // Set properties of the new control + control = &net->Control[n]; + control->Type = (char)ctype; + control->Link = lindex; + control->Node = nindex; + control->Status = status; + control->Setting = s; + control->Grade = lvl; + control->Time = t; + + // Update number of controls + net->Ncontrols = n; + parser->MaxControls = n; + + // Return the new control index + *index = n; + return 0; +} + +int DLLEXPORT EN_deletecontrol(EN_Project p, int index) +/*---------------------------------------------------------------- +** Input: index = index of the control +** Output: none +** Returns: error code +** Purpose: deletes a simple control from a project +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + int i; + + if (index <= 0 || index > net->Ncontrols) return 241; for (i = index; i <= net->Ncontrols - 1; i++) { net->Control[i] = net->Control[i + 1]; } net->Ncontrols--; - return (0); + return 0; } -/* - ---------------------------------------------------------------- - Functions for managing rule-based controls - ---------------------------------------------------------------- - */ - -int DLLEXPORT EN_addrule(EN_ProjectHandle ph, char *rule) +int DLLEXPORT EN_getcontrol(EN_Project p, int index, int *ctype, int *lindex, + EN_API_FLOAT_TYPE *setting, int *nindex, + EN_API_FLOAT_TYPE *level) /*---------------------------------------------------------------- -** Input: rule = text of rule being added in the format -** used for the [RULES] section of an INP file -** Output: none +** Input: index = index of the control +** Output: ctype = type of control (see EN_ControlType) +** lindex = index of link being controlled +** setting = link control setting (e.g., pump speed) +** nindex = index of node controlling a link (for level controls) +** level = control activation level (pressure for junction nodes, +** water level for tank nodes or time value for time-based +** control) ** Returns: error code -** Purpose: adds a new rule to the project. +** Purpose: Retrieves the properties of a simple control **---------------------------------------------------------------- */ { - EN_Project *p = (EN_Project*)ph; + Network *net = &p->network; + + double s, lvl; + double *Ucf = p->Ucf; + Scontrol *control; + Snode *node; + + // Set default return values + s = 0.0; + lvl = 0.0; + *ctype = 0; + *lindex = 0; + *nindex = 0; + + // Check for valid arguments + if (!p->Openflag) return 102; + if (index <= 0 || index > net->Ncontrols) return 241; + + // Retrieve control's type and link index + control = &net->Control[index]; + *ctype = control->Type; + *lindex = control->Link; + + // Retrieve control's setting + s = control->Setting; + if (control->Setting != MISSING) + { + switch (net->Link[*lindex].Type) + { + case EN_PRV: + case EN_PSV: + case EN_PBV: + s *= Ucf[PRESSURE]; + break; + case EN_FCV: + s *= Ucf[FLOW]; + default: + break; + } + } + else if (control->Status == OPEN) s = 1.0; + else s = 0.0; + + // Retrieve level value for a node level control + *nindex = control->Node; + if (*nindex > 0) + { + node = &net->Node[*nindex]; + if (*nindex > net->Njuncs) // Node is a tank + { + lvl = (control->Grade - node->El) * Ucf[ELEV]; + } + else // Node is a junction + { + lvl = (control->Grade - node->El) * Ucf[PRESSURE]; + } + } + + // Retrieve level value for a time-based control + else + { + lvl = (EN_API_FLOAT_TYPE)control->Time; + } + *setting = (EN_API_FLOAT_TYPE)s; + *level = (EN_API_FLOAT_TYPE)lvl; + return 0; +} + +int DLLEXPORT EN_setcontrol(EN_Project p, int index, int ctype, int lindex, + EN_API_FLOAT_TYPE setting, int nindex, + EN_API_FLOAT_TYPE level) +/*---------------------------------------------------------------- +** Input: index = index of the control +** ctype = type of control (see EN_ControlType) +** lindex = index of link being controlled +** setting = link control setting (e.g., pump speed) +** nindex = index of node controlling a link (for level controls) +** level = control activation level (pressure for junction nodes, +** water level for tank nodes or time value for time-based +** control) +** Output: none +** Returns: error code +** Purpose: Sets the properties of a simple control +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + + char status = ACTIVE; + long t = 0; + double s = setting, lvl = level; + double *Ucf = p->Ucf; + Slink *link; + Scontrol *control; + + // Check that project exists + if (!p->Openflag) return 102; + + // Check that control exists + if (index <= 0 || index > net->Ncontrols) return 241; + control = &net->Control[index]; + + // Check that controlled link exists + if (lindex == 0) + { + control->Link = 0; + return 0; + } + if (lindex < 0 || lindex > net->Nlinks) return 204; + + // Cannot control check valve + if (net->Link[lindex].Type == EN_CVPIPE) return 207; + + // Check for valid control properties + if (ctype < 0 || ctype > EN_TIMEOFDAY) return 251; + if (ctype == EN_LOWLEVEL || ctype == EN_HILEVEL) + { + if (nindex < 1 || nindex > net->Nnodes) return 203; + } + else nindex = 0; + if (s < 0.0 || lvl < 0.0) return 202; + + // Adjust units of control's properties + link = &net->Link[lindex]; + switch (link->Type) + { + case EN_PRV: + case EN_PSV: + case EN_PBV: + s /= Ucf[PRESSURE]; + break; + case EN_FCV: + s /= Ucf[FLOW]; + break; + case EN_GPV: + if (s == 0.0) status = CLOSED; + else if (s == 1.0) status = OPEN; + else return 202; + s = link->Kc; + break; + case EN_PIPE: + case EN_PUMP: + status = OPEN; + if (s == 0.0) status = CLOSED; + default: + break; + } + if (ctype == LOWLEVEL || ctype == HILEVEL) + { + if (nindex > net->Njuncs) lvl = net->Node[nindex].El + level / Ucf[ELEV]; + else lvl = net->Node[nindex].El + level / Ucf[PRESSURE]; + } + if (ctype == TIMER) t = (long)ROUND(lvl); + if (ctype == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY; + + /* Reset control's parameters */ + control->Type = (char)ctype; + control->Link = lindex; + control->Node = nindex; + control->Status = status; + control->Setting = s; + control->Grade = lvl; + control->Time = t; + return 0; +} + +/******************************************************************** + + Rule-Based Controls Functions + +********************************************************************/ + +int DLLEXPORT EN_addrule(EN_Project p, char *rule) +/*---------------------------------------------------------------- +** Input: rule = text of rule being added in the format +** used for the [RULES] section of an EPANET input file +** Output: none +** Returns: error code +** Purpose: adds a new rule to a project +**---------------------------------------------------------------- +*/ +{ + Network *net = &p->network; + Parser *parser = &p->parser; + Rules *rules = &p->rules; char *line; char *nextline; char line2[MAXLINE+1]; - EN_Network *net = &p->network; - parser_data_t *parser = &p->parser; - rules_t *rules = &p->rules; // Resize rules array net->Rule = (Srule *)realloc(net->Rule, (net->Nrules + 2)*sizeof(Srule)); @@ -5569,12 +4888,12 @@ int DLLEXPORT EN_addrule(EN_ProjectHandle ph, char *rule) // Find where current line ends and next one begins nextline = strchr(line, '\n'); if (nextline) *nextline = '\0'; - + // Copy and tokenize the current line strcpy(line2, line); strcat(line2, "\n"); // Tokenizer won't work without this parser->Ntokens = gettokens(line2, parser->Tok, MAXTOKS, parser->Comment); - + // Process the line to build up the rule's contents if (parser->Ntokens > 0 && *parser->Tok[0] != ';') { @@ -5595,12 +4914,25 @@ int DLLEXPORT EN_addrule(EN_ProjectHandle ph, char *rule) return rules->Errcode; } +int DLLEXPORT EN_deleterule(EN_Project p, int index) +/*---------------------------------------------------------------- +** Input: index = rule index +** Output: none +** Returns: error code +** Purpose: deletes a rule from a project. +**---------------------------------------------------------------- +*/ +{ + if (index < 1 || index > p->network.Nrules) return 257; + deleterule(p, index); + return 0; +} -int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, +int DLLEXPORT EN_getrule(EN_Project p, int index, int *nPremises, int *nThenActions, int *nElseActions, EN_API_FLOAT_TYPE *priority) /*---------------------------------------------------------------- -** Input: index = index of the rule +** Input: index = rule index ** Output: nPremises = number of premises conditions (IF AND OR) ** nThenActions = number of actions in THEN portion of rule ** nElseActions = number of actions in ELSE portion of rule @@ -5610,308 +4942,340 @@ int DLLEXPORT EN_getrule(EN_ProjectHandle ph, int index, int *nPremises, **---------------------------------------------------------------- */ { - EN_Project *pr = (EN_Project*)ph; + Network *net = &p->network; int count; - Spremise *p; - Saction *a; - EN_Network *net = &pr->network; - - if (index < 1 || index > net->Nrules) return (257); - *priority = (EN_API_FLOAT_TYPE)pr->network.Rule[index].priority; + Spremise *premise; + Saction *action; + if (index < 1 || index > net->Nrules) return 257; + *priority = (EN_API_FLOAT_TYPE)p->network.Rule[index].priority; + count = 1; - p = net->Rule[index].Premises; - while (p->next != NULL) + premise = net->Rule[index].Premises; + while (premise->next != NULL) { count++; - p = p->next; + premise = premise->next; } *nPremises = count; - + count = 1; - a = net->Rule[index].ThenActions; - while (a->next != NULL) + action = net->Rule[index].ThenActions; + while (action->next != NULL) { count++; - a = a->next; + action = action->next; } *nThenActions = count; - a = net->Rule[index].ElseActions; + action = net->Rule[index].ElseActions; count = 0; - if (a != NULL) + if (action != NULL) { count = 1; - while (a->next != NULL) + while (action->next != NULL) { count++; - a = a->next; + action = action->next; } } *nElseActions = count; - return (0); + return 0; } +int DLLEXPORT EN_getruleID(EN_Project p, int index, char *id) +/*---------------------------------------------------------------- +** Input: index = rule index +** Output: id = rule ID label +** Returns: error code +** Purpose: retrieves the ID label of a rule +**---------------------------------------------------------------- +*/ +{ + strcpy(id, ""); + if (!p->Openflag) return 102; + if (index < 1 || index > p->network.Nrules) return 257; + strcpy(id, p->network.Rule[index].label); + return 0; +} -int DLLEXPORT EN_getpremise(EN_ProjectHandle ph, int ruleIndex, int premiseIndex, +int DLLEXPORT EN_getpremise(EN_Project p, int ruleIndex, int premiseIndex, int *logop, int *object, int *objIndex, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value) -//----------------------------------------------------------------------------- -// Retrieve the properties of a particular rule premise. -//----------------------------------------------------------------------------- +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** premiseIndex = premise index +** Output: logop = logical operator (IF = 1, AND = 2, OR = 3) +** object = type of object appearing in the premise (see EN_RuleObject) +** objIndex = object's index in Node or Link array +** variable = object variable being tested (see EN_RuleVariable) +** relop = relational operator (see EN_RuleOperator) +** status = object status being tested against (see EN_RuleStatus)) +** value = variable value being tested against +** Returns: error code +** Purpose: retrieves the properties of a rule's premise +**---------------------------------------------------------------- +*/ { - EN_Project *pr = (EN_Project*)ph; Spremise *premises; - Spremise *p; + Spremise *premise; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } + premises = p->network.Rule[ruleIndex].Premises; + premise = getpremise(premises, premiseIndex); + if (premise == NULL) return 258; - premises = pr->network.Rule[ruleIndex].Premises; - p = getpremise(premises, premiseIndex); - if (p == NULL) return (258); - - *logop = p->logop; - *object = p->object; - *objIndex = p->index; - *variable = p->variable; - *relop = p->relop; - *status = p->status; - *value = (EN_API_FLOAT_TYPE)p->value; - return (0); + *logop = premise->logop; + *object = premise->object; + *objIndex = premise->index; + *variable = premise->variable; + *relop = premise->relop; + *status = premise->status; + *value = (EN_API_FLOAT_TYPE)premise->value; + return 0; } -int DLLEXPORT EN_setrulepriority(EN_ProjectHandle ph, int index, - EN_API_FLOAT_TYPE priority) -/*----------------------------------------------------------------------------- -** Input: index = index of the rule -** priority = rule priority +int DLLEXPORT EN_setpremise(EN_Project p, int ruleIndex, int premiseIndex, + int logop, int object, int objIndex, int variable, + int relop, int status, EN_API_FLOAT_TYPE value) +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** premiseIndex = premise index +** logop = logical operator (IF = 1, AND = 2, OR = 3) +** object = type of object appearing in the premise (see EN_RuleObject) +** objIndex = object's index in Node or Link array +** variable = object variable being tested (see EN_RuleVariable) +** relop = relational operator (see EN_RuleOperator) +** status = object status being tested against (see EN_RuleStatus)) +** value = variable value being tested against ** Output: none ** Returns: error code +** Purpose: sets the properties of a rule's premise +**---------------------------------------------------------------- +*/ +{ + Spremise *premises; + Spremise *premise; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; + + premises = p->network.Rule[ruleIndex].Premises; + premise = getpremise(premises, premiseIndex); + if (premise == NULL) return 258; + + premise->logop = logop; + premise->object = object; + premise->index = objIndex; + premise->variable = variable; + premise->relop = relop; + premise->status = status; + premise->value = value; + return 0; +} + +int DLLEXPORT EN_setpremiseindex(EN_Project p, int ruleIndex, int premiseIndex, + int objIndex) +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** premiseIndex = premise index +** objIndex = object's index in Node or Link array +** Output: none +** Returns: error code +** Purpose: sets the index of an object referred to in a rule's premise +**---------------------------------------------------------------- +*/ +{ + Spremise *premises; + Spremise *premise; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; + + premises = p->network.Rule[ruleIndex].Premises; + premise = getpremise(premises, premiseIndex); + if (premise == NULL) return 258; + + premise->index = objIndex; + return 0; +} + +int DLLEXPORT EN_setpremisestatus(EN_Project p, int ruleIndex, int premiseIndex, int status) +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** premiseIndex = premise index +** status = object status being tested against +** (see EN_RuleStatus)) +** Output: none +** Returns: error code +** Purpose: sets the status of an object being tested against +** in a rule's premise +**---------------------------------------------------------------- +*/ +{ + Spremise *premises; + Spremise *premise; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; + + premises = p->network.Rule[ruleIndex].Premises; + premise = getpremise(premises, ruleIndex); + if (premise == NULL) return 258; + + premise->status = status; + return 0; +} + +int DLLEXPORT EN_setpremisevalue(EN_Project p, int ruleIndex, int premiseIndex, EN_API_FLOAT_TYPE value) +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** premiseIndex = premise index +** value = value of object variable being tested against +** Output: none +** Returns: error code +** Purpose: sets the value of object's variable being tested against +** in a rule's premise +**---------------------------------------------------------------- +*/ +{ + Spremise *premises; + Spremise *premise; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; + + premises = p->network.Rule[ruleIndex].Premises; + premise = getpremise(premises, premiseIndex); + if (premise == NULL) return 258; + + premise->value = value; + return 0; +} + +int DLLEXPORT EN_getthenaction(EN_Project p, int ruleIndex, int actionIndex, + int *linkIndex, int *status, EN_API_FLOAT_TYPE *setting) +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** actionIndex = index of a rule's THEN actions +** Output: linkIndex = index of link appearing in the action +** status = status assigned to the link (see EN_RuleStatus)) +** setting = setting assigned to the link +** Returns: error code +** Purpose: retrieves the properties of a rule's THEN action +**---------------------------------------------------------------- +*/ +{ + Saction *actions; + Saction *action; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; + + actions = p->network.Rule[ruleIndex].ThenActions; + action = getaction(actions, actionIndex); + if (action == NULL) return 258; + + *linkIndex = action->link; + *status = action->status; + *setting = (EN_API_FLOAT_TYPE)action->setting; + return 0; +} + +int DLLEXPORT EN_setthenaction(EN_Project p, int ruleIndex, int actionIndex, + int linkIndex, int status, EN_API_FLOAT_TYPE setting) +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** actionIndex = index of a rule's THEN actions +** linkIndex = index of link appearing in the action +** status = status assigned to the link (see EN_RuleStatus)) +** setting = setting assigned to the link +** Returns: error code +** Purpose: sets the properties of a rule's THEN action +**---------------------------------------------------------------- +*/ +{ + Saction *actions; + Saction *action; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; + + actions = p->network.Rule[ruleIndex].ThenActions; + action = getaction(actions, actionIndex); + if (action == NULL) return 258; + + action->link = linkIndex; + action->status = status; + action->setting = setting; + return 0; +} + +int DLLEXPORT EN_getelseaction(EN_Project p, int ruleIndex, int actionIndex, + int *linkIndex, int *status, EN_API_FLOAT_TYPE *setting) +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** actionIndex = index of a rule's ELSE actions +** Output: linkIndex = index of link appearing in the action +** status = status assigned to the link (see EN_RuleStatus)) +** setting = setting assigned to the link +** Returns: error code +** Purpose: retrieves the properties of a rule's ELSE action +**---------------------------------------------------------------- +*/ +{ + + Saction *actions; + Saction *action; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; + + actions = p->network.Rule[ruleIndex].ThenActions; + action = getaction(actions, actionIndex); + if (action == NULL) return 258; + + *linkIndex = action->link; + *status = action->status; + *setting = (EN_API_FLOAT_TYPE)action->setting; + return 0; +} + +int DLLEXPORT EN_setelseaction(EN_Project p, int ruleIndex, int actionIndex, + int linkIndex, int status, EN_API_FLOAT_TYPE setting) +/*---------------------------------------------------------------- +** Input: ruleIndex = rule index +** actionIndex = index of a rule's ELSE actions +** linkIndex = index of link appearing in the action +** status = status assigned to the link (see EN_RuleStatus)) +** setting = setting assigned to the link +** Returns: error code +** Purpose: sets the properties of a rule's ELSE action +**---------------------------------------------------------------- +*/ +{ + Saction *actions; + Saction *action; + + if (ruleIndex < 1 || ruleIndex > p->network.Nrules) return 257; + + actions = p->network.Rule[ruleIndex].ThenActions; + action = getaction(actions, actionIndex); + if (action == NULL) return 258; + + action->link = linkIndex; + action->status = status; + action->setting = setting; + return 0; +} + +int DLLEXPORT EN_setrulepriority(EN_Project p, int index, EN_API_FLOAT_TYPE priority) +/*----------------------------------------------------------------------------- +** Input: index = rule index +** priority = rule priority level +** Output: none +** Returns: error code +** Purpose: sets the priority level for a rule **----------------------------------------------------------------------------- */ { - EN_Project *pr = (EN_Project*)ph; - - if (index <= 0 || index > pr->network.Nrules) - { - return (257); - } - pr->network.Rule[index].priority = priority; - return (0); + if (index <= 0 || index > p->network.Nrules) return 257; + p->network.Rule[index].priority = priority; + return 0; } - -int DLLEXPORT EN_setpremise(EN_ProjectHandle ph, int ruleIndex, int premiseIndex, - int logop, int object, int objIndex, int variable, - int relop, int status, EN_API_FLOAT_TYPE value) -//----------------------------------------------------------------------------- -// Sets the properties of a particular rule premise. -//----------------------------------------------------------------------------- -{ - EN_Project *pr = (EN_Project*)ph; - Spremise *premises; - Spremise *p; - - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } - - premises = pr->network.Rule[ruleIndex].Premises; - p = getpremise(premises, premiseIndex); - if (p == NULL) return (258); - - p->logop = logop; - p->object = object; - p->index = objIndex; - p->variable = variable; - p->relop = relop; - p->status = status; - p->value = value; - return (0); -} - -int DLLEXPORT EN_setpremiseindex(EN_ProjectHandle ph, int ruleIndex, - int premiseIndex, int objIndex) -{ - EN_Project *pr = (EN_Project*)ph; - Spremise *premises; - Spremise *p; - - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } - - premises = pr->network.Rule[ruleIndex].Premises; - p = getpremise(premises, premiseIndex); - if (p == NULL) return (258); - - p->index = objIndex; - return (0); -} - -int DLLEXPORT EN_setpremisestatus(EN_ProjectHandle ph, int ruleIndex, - int premiseIndex, int status) -{ - EN_Project *pr = (EN_Project*)ph; - Spremise *premises; - Spremise *p; - - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } - - premises = pr->network.Rule[ruleIndex].Premises; - p = getpremise(premises, ruleIndex); - if (p == NULL) return (258); - - p->status = status; - return (0); -} - -int DLLEXPORT EN_setpremisevalue(EN_ProjectHandle ph, int ruleIndex, - int premiseIndex, EN_API_FLOAT_TYPE value) -{ - EN_Project *pr = (EN_Project*)ph; - Spremise *premises; - Spremise *p; - - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } - - premises = pr->network.Rule[ruleIndex].Premises; - p = getpremise(premises, premiseIndex); - if (p == NULL) return (258); - - p->value = value; - return (0); -} - -int DLLEXPORT EN_getthenaction(EN_ProjectHandle ph, int ruleIndex, - int actionIndex, int *linkIndex, - int *status, EN_API_FLOAT_TYPE *setting) -{ - EN_Project *pr = (EN_Project*)ph; - Saction *actions; - Saction *a; - - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } - - actions = pr->network.Rule[ruleIndex].ThenActions; - a = getaction(actions, actionIndex); - if (a == NULL) return (258); - - *linkIndex = a->link; - *status = a->status; - *setting = (EN_API_FLOAT_TYPE)a->setting; - return (0); -} - -int DLLEXPORT EN_setthenaction(EN_ProjectHandle ph, int ruleIndex, - int actionIndex, int linkIndex, - int status, EN_API_FLOAT_TYPE setting) -{ - EN_Project *pr = (EN_Project*)ph; - Saction *actions; - Saction *a; - - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } - - actions = pr->network.Rule[ruleIndex].ThenActions; - a = getaction(actions, actionIndex); - if (a == NULL) return (258); - - a->link = linkIndex; - a->status = status; - a->setting = setting; - return (0); -} - -int DLLEXPORT EN_getelseaction(EN_ProjectHandle ph, int ruleIndex, - int actionIndex, int *linkIndex, - int *status, EN_API_FLOAT_TYPE *setting) -{ - - EN_Project *pr = (EN_Project*)ph; - Saction *actions; - Saction *a; - - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } - - actions = pr->network.Rule[ruleIndex].ThenActions; - a = getaction(actions, actionIndex); - if (a == NULL) return (258); - - *linkIndex = a->link; - *status = a->status; - *setting = (EN_API_FLOAT_TYPE)a->setting; - return (0); -} - -int DLLEXPORT EN_setelseaction(EN_ProjectHandle ph, int ruleIndex, - int actionIndex, int linkIndex, - int status, EN_API_FLOAT_TYPE setting) -{ - EN_Project *pr = (EN_Project*)ph; - Saction *actions; - Saction *a; - - if (ruleIndex < 1 || ruleIndex > pr->network.Nrules) - { - return (257); - } - - actions = pr->network.Rule[ruleIndex].ThenActions; - a = getaction(actions, actionIndex); - if (a == NULL) return (258); - - a->link = linkIndex; - a->status = status; - a->setting = setting; - return (0); -} - -int DLLEXPORT EN_getruleID(EN_ProjectHandle ph, int index, char *id) -{ - EN_Project *pr = (EN_Project*)ph; - - strcpy(id, ""); - if (!pr->Openflag) return (102); - if (index < 1 || index > pr->network.Nrules) - { - return (257); - } - strcpy(id, pr->network.Rule[index].label); - return (0); -} - -int DLLEXPORT EN_deleterule(EN_ProjectHandle ph, int index) -{ - EN_Project *pr = (EN_Project*)ph; - - if (index < 1 || index > pr->network.Nrules) - { - return (257); - } - deleterule(pr, index); - return (0); -} - -/*************************** END OF EPANET.C ***************************/ diff --git a/src/errors.dat b/src/errors.dat index 3741904..13a788f 100644 --- a/src/errors.dat +++ b/src/errors.dat @@ -57,7 +57,7 @@ DAT(257,ENERR_NO_RULE,"function applied to nonexistent rule") DAT(258,ENERR_NO_CONDITION_ACTION,"function applied to nonexistent rule clause") DAT(260,ENERR_DEL_TRACE_NODE,"cannot delete node assigned as a Trace Node") -DAT(261,ENERR_DEL_NODE_LINK, "cannot delete a node or link contained in a control or rule") +DAT(261,ENERR_DEL_NODE_LINK, "cannot delete a node or link contained in a control") DAT(301,ENERR_FILES_ARE_SAME,"identical file names") DAT(302,ENERR_CANT_OPEN_INP,"cannot open input file") @@ -69,9 +69,6 @@ DAT(307,ENERR_CANT_READ_HYD,"cannot read hydraulics file") DAT(308,ENERR_CANT_SAVE_RES,"cannot save results to file") DAT(309,ENERR_CANT_SAVE_RPT,"cannot save results to report file") -DAT(401,ENERR_QSTEP_NOT_DIVISIBLE,"Qstep is not dividable by Hstep") - - DAT(411,ENERR_NO_MEM_ALLOC,"no memory allocated for results") DAT(412,ENERR_NO_RESULTS_NO_FILE,"no results; binary file hasn't been opened") DAT(435,ENERR_RUN_TERMINATED,"run terminated; no results in binary file") diff --git a/src/funcs.h b/src/funcs.h index 799be27..abf66ec 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -1,227 +1,175 @@ /* -************************************************************************** - -FUNCS.H -- Function Prototypes for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 9/25/00 - 10/25/00 - 12/29/00 - 3/1/01 - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -************************************************************************** + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: funcs.h + Description: prototypes of external functions called by various modules + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ - -/*****************************************************************/ -/* Most float arguments have been changed to double - 7/3/07 */ -/*****************************************************************/ - -/* ------- EPANET.C --------------------*/ -/* -** NOTE: The exportable functions that can be called -** via the DLL are prototyped in TOOLKIT.H. -*/ - #ifndef FUNCS_H #define FUNCS_H -void initpointers(EN_Project *pr); /* Initializes pointers */ -int allocdata(EN_Project *pr); /* Allocates memory */ -void freeTmplist(STmplist *); /* Frees items in linked list */ -void freeFloatlist(SFloatlist *); /* Frees list of floats */ -void freedata(EN_Project *pr); /* Frees allocated memory */ -int openfiles(EN_Project *pr, const char *, - const char *,const char *); /* Opens input & report files */ -int openhydfile(EN_Project *pr); /* Opens hydraulics file */ -int openoutfile(EN_Project *pr); /* Opens binary output file */ -int strcomp(const char *, const char *); /* Compares two strings */ -char* getTmpName(char* fname); /* Gets temporary file name */ -double interp(int n, double x[], double y[], - double xx); /* Interpolates a data curve */ - -int findnode(EN_Network *n, char *); /* Finds node's index from ID */ -int findlink(EN_Network *n, char *); /* Finds link's index from ID */ -int findtank(EN_Network *n, int); /* Find tank index from node index */ // (AH) -int findvalve(EN_Network *n, int); /* Find valve index from node index */ // (AH) -int findpump(EN_Network *n, int); /* Find pump index from node index */ // (AH) -char *geterrmsg(int errcode, char *msg); /* Gets text of error message */ -void errmsg(EN_Project *p, int); /* Reports program error */ -void writewin(void (*vp)(char *), char *); /* Passes text to calling app */ +// ------- PROJECT.C ------------ -/* ------- INPUT1.C --------------------*/ -int getdata(EN_Project *pr); /* Gets network data */ -void setdefaults(EN_Project *pr); /* Sets default values */ -void initreport(report_options_t *r); /* Initializes report options */ -void adjustdata(EN_Project *pr); /* Adjusts input data */ -int inittanks(EN_Project *pr); /* Initializes tank levels */ -void initunits(EN_Project *pr); /* Determines reporting units */ -void convertunits(EN_Project *pr); /* Converts data to std. units*/ +void initpointers(Project *); +int allocdata(Project *); +void freeTmplist(STmplist *); +void freeFloatlist(SFloatlist *); +void freedata(Project *); -/* -------- INPUT2.C -------------------*/ -int netsize(EN_Project *pr); /* Determines network size */ -int readdata(EN_Project *pr); /* Reads in network data */ -int newline(EN_Project *pr, int, char *); /* Processes new line of data */ -int addnodeID(EN_Network *n, int, char *); /* Adds node ID to data base */ -int addlinkID(EN_Network *n, int, char *); /* Adds link ID to data base */ -int addpattern(parser_data_t *par, char *); /* Adds pattern to data base */ -int addcurve(parser_data_t *par, char *); /* Adds curve to data base */ -STmplist *findID(char *, STmplist *); /* Locates ID on linked list */ -int unlinked(EN_Project *pr); /* Checks for unlinked nodes */ -int getpumpparams(EN_Project *pr); /* Computes pump curve coeffs.*/ -int updatepumpparams(EN_Project *pr, int); // Updates pump curve coeffs. -int getpatterns(EN_Project *pr); /* Gets pattern data from list*/ -int getcurves(EN_Project *pr); /* Gets curve data from list */ -int findmatch(char *, char *[]); /* Finds keyword in line */ -int match(const char *, const char *); /* Checks for word match */ -int gettokens(char *s, char** Tok, int maxToks, - char *comment); /* Tokenizes input line */ -int getfloat(char *, double *); /* Converts string to double */ -double hour(char *, char *); /* Converts time to hours */ -int setreport(EN_Project *pr, char *); /* Processes reporting command*/ -void inperrmsg(EN_Project *pr, int,int,char *); /* Input error message */ +int openfiles(Project *, const char *, const char *,const char *); +int openhydfile(Project *); +int openoutfile(Project *); -/* ---------- INPUT3.C -----------------*/ -int juncdata(EN_Project *pr); /* Processes junction data */ -int tankdata(EN_Project *pr); /* Processes tank data */ -int pipedata(EN_Project *pr); /* Processes pipe data */ -int pumpdata(EN_Project *pr); /* Processes pump data */ -int valvedata(EN_Project *pr); /* Processes valve data */ -int patterndata(EN_Project *pr); /* Processes pattern data */ -int curvedata(EN_Project *pr); /* Processes curve data */ -int coordata(EN_Project *pr); /* Processes coordinate data */ -int demanddata(EN_Project *pr); /* Processes demand data */ -int controldata(EN_Project *pr); /* Processes simple controls */ -int energydata(EN_Project *pr); /* Processes energy data */ -int sourcedata(EN_Project *pr); /* Processes source data */ -int emitterdata(EN_Project *pr); /* Processes emitter data */ -int qualdata(EN_Project *pr); /* Processes quality data */ -int reactdata(EN_Project *pr); /* Processes reaction data */ -int mixingdata(EN_Project *pr); /* Processes tank mixing data */ -int statusdata(EN_Project *pr); /* Processes link status data */ -int reportdata(EN_Project *pr); /* Processes report options */ -int timedata(EN_Project *pr); /* Processes time options */ -int optiondata(EN_Project *pr); /* Processes analysis options */ -int optionchoice(EN_Project *pr, int); /* Processes option choices */ -int optionvalue(EN_Project *pr, int); /* Processes option values */ -int getpumpcurve(EN_Project *pr, int); /* Constructs a pump curve */ -int powercurve(double, double, double, /* Coeffs. of power pump curve*/ - double, double, double *, - double *, double *); -int valvecheck(EN_Project *pr, int, int, int); /* Checks valve placement */ -void changestatus(EN_Network *net, int, StatType, - double); /* Changes status of a link */ +int buildadjlists(Network *); +void freeadjlists(Network *); -/* -------------- RULES.C --------------*/ -void initrules(EN_Project *pr); /* Initializes rule base */ -void addrule(parser_data_t *par, char *); /* Adds rule to rule base */ -int allocrules(EN_Project *pr); /* Allocates memory for rule */ -void adjustrules(EN_Project *pr, int, int); // Shifts object indices down -void adjusttankrules(EN_Project *pr); // Shifts tank indices up -int ruledata(EN_Project *pr); /* Processes rule input data */ -int checkrules(EN_Project *pr, long); /* Checks all rules */ -void deleterule(EN_Project *pr, int); // Deletes a rule -void freerules(EN_Project *pr); /* Frees rule base memory */ -int writerule(EN_Project *pr, FILE *, int); /* Writes rule to an INP file */ -void ruleerrmsg(EN_Project *pr); /* Reports rule parser error */ -Spremise *getpremise(Spremise *, int); // Retrieves a rule's premise -Saction *getaction(Saction *, int); // Retrieves a rule's action +int incontrols(Project *, int, int); +int findnode(Network *, char *); +int findlink(Network *, char *); +int findtank(Network *, int); +int findvalve(Network *, int); +int findpump(Network *, int); -/* ------------- REPORT.C --------------*/ -int writereport(EN_Project *pr); /* Writes formatted report */ -void writelogo(EN_Project *pr); /* Writes program logo */ -void writesummary(EN_Project *pr); /* Writes network summary */ -void writehydstat(EN_Project *pr, int,double); /* Writes hydraulic status */ -void writeenergy(EN_Project *pr); /* Writes energy usage */ -int writeresults(EN_Project *pr); /* Writes node/link results */ -void writeheader(EN_Project *pr, int,int); /* Writes heading on report */ -void writeline(EN_Project *pr, char *); /* Writes line to report file */ -void writerelerr(EN_Project *pr, int, double); /* Writes convergence error */ -void writestatchange(EN_Project *pr, int,char,char); /* Writes link status change */ -void writecontrolaction(EN_Project *pr, int, int); /* Writes control action taken*/ -void writeruleaction(EN_Project *pr, int, char *); /* Writes rule action taken */ -int writehydwarn(EN_Project *pr, int,double); /* Writes hydraulic warnings */ -void writehyderr(EN_Project *pr, int); /* Writes hydraulic error msg.*/ -void writemassbalance(EN_Project *pr); // Writes mass balance ratio -int disconnected(EN_Project *pr); /* Checks for disconnections */ -void marknodes(EN_Project *pr, int, int *, char *); /* Identifies connected nodes */ -void getclosedlink(EN_Project *pr, int, char *); /* Finds a disconnecting link */ -void writelimits(EN_Project *pr, int,int); /* Writes reporting limits */ -int checklimits(report_options_t *rep, double *, - int,int); /* Checks variable limits */ -void writetime(EN_Project *pr, char *); /* Writes current clock time */ -char *clocktime(char *, long); /* Converts time to hrs:min */ -char *fillstr(char *, char, int); /* Fills string with character*/ -int getnodetype(EN_Network *net, int); /* Determines node type */ +char *getTmpName(char *); +int strcomp(const char *, const char *); +double interp(int, double [], double [], double); +char *geterrmsg(int, char *); +void errmsg(Project *, int); +void writewin(void (*vp)(char *), char *); -/* --------- HYDRAUL.C -----------------*/ -int openhyd(EN_Project *pr); /* Opens hydraulics solver */ -void inithyd(EN_Project *pr, int initFlags); /* Re-sets initial conditions */ -int runhyd(EN_Project *pr, long *); /* Solves 1-period hydraulics */ -int nexthyd(EN_Project *pr, long *); /* Moves to next time period */ -void closehyd(EN_Project *pr); /* Closes hydraulics solver */ -void setlinkstatus(EN_Project *pr, int, char, - StatType *, double *); /* Sets link status */ -void setlinksetting(EN_Project *pr, int, double, - StatType *, double *); /* Sets pump/valve setting */ -int tanktimestep(EN_Project *pr, long *); /* Time till tanks fill/drain */ -void getenergy(EN_Project *pr, int, double *, - double *); /* Computes link energy use */ -double tankvolume(EN_Project *pr, int, double); /* Finds tank vol. from grade */ -double tankgrade(EN_Project *pr, int, double); /* Finds tank grade from vol. */ +// ------- INPUT1.C ---------------- -/* ----------- HYDSOLVER.C - ----------*/ -int hydsolve(EN_Project *pr, int *,double *); /* Solves network equations */ +int getdata(Project *); +void setdefaults(Project *); +void initreport(Report *); +void adjustdata(Project *); +int inittanks(Project *); +void initunits(Project *); +void convertunits(Project *); -/* ----------- HYDCOEFFS.C --------------*/ -void resistcoeff(EN_Project *pr, int k); /* Finds pipe flow resistance */ -void headlosscoeffs(EN_Project *pr); // Finds link head loss coeffs. -void matrixcoeffs(EN_Project *pr); /* Finds hyd. matrix coeffs. */ -void emitheadloss(EN_Project *pr, int, // Finds emitter head loss - double *, double *); -double demandflowchange(EN_Project *pr, int, // Change in demand outflow - double, double); -void demandparams(EN_Project *pr, double *, // PDA function parameters - double *); +//-------- INPUT2.C ----------------- -/* ----------- SMATRIX.C ---------------*/ -int createsparse(EN_Project *pr); /* Creates sparse matrix */ -void freesparse(EN_Project *pr); /* Frees matrix memory */ -int linsolve(EN_Project *pr, int); /* Solves set of linear eqns. */ +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 *); +int getfloat(char *, double *); +double hour(char *, char *); +int setreport(Project *, char *); -/* ----------- QUALITY.C ---------------*/ -int openqual(EN_Project *pr); /* Opens WQ solver system */ -int initqual(EN_Project *pr); /* Initializes WQ solver */ -int runqual(EN_Project *pr, long *); /* Gets current WQ results */ -int nextqual(EN_Project *pr, long *); /* Updates WQ by hyd.timestep */ -int stepqual(EN_Project *pr, long *); /* Updates WQ by WQ time step */ -int closequal(EN_Project *pr); /* Closes WQ solver system */ -double avgqual(EN_Project *pr, int); /* Finds avg. quality in pipe */ +// ------- INPUT3.C ----------------- -/* ------------ OUTPUT.C ---------------*/ -int savenetdata(EN_Project *pr); /* Saves basic data to file */ -int savehyd(EN_Project *pr, long *); /* Saves hydraulic solution */ -int savehydstep(EN_Project *pr, long *); /* Saves hydraulic timestep */ -int saveenergy(EN_Project *pr); /* Saves energy usage */ -int readhyd(EN_Project *pr, long *); /* Reads hydraulics from file */ -int readhydstep(EN_Project *pr, long *); /* Reads time step from file */ -int saveoutput(EN_Project *pr); /* Saves results to file */ -int nodeoutput(EN_Project *pr, int, REAL4 *, - double); /* Saves node results to file */ -int linkoutput(EN_Project *pr, int, REAL4 *, - double); /* Saves link results to file */ -int savefinaloutput(EN_Project *pr); /* Finishes saving output */ -int savetimestat(EN_Project *pr, REAL4 *, - HdrType); /* Saves time stats to file */ -int savenetreacts(EN_Project *pr, double, - double,double, double); /* Saves react. rates to file */ -int saveepilog(EN_Project *pr); /* Saves output file epilog */ +int juncdata(Project *); +int tankdata(Project *); +int pipedata(Project *); +int pumpdata(Project *); +int valvedata(Project *); +int patterndata(Project *); +int curvedata(Project *); +int coordata(Project *); +int demanddata(Project *); +int controldata(Project *); +int energydata(Project *); +int sourcedata(Project *); +int emitterdata(Project *); +int qualdata(Project *); +int reactdata(Project *); +int mixingdata(Project *); +int statusdata(Project *); +int reportdata(Project *); +int timedata(Project *); +int optiondata(Project *); +int valvecheck(Project *, int, int, int); +// ------- RULES.C ------------------ -/* ------------ INPFILE.C --------------*/ -int saveinpfile(EN_Project *pr, const char *); /* Saves network to text file */ +void initrules(Project *); +void addrule(Parser *, char *); +void deleterule(Project *, int); +int allocrules(Project *); +void freerules(Project *); +int ruledata(Project *); +void ruleerrmsg(Project *); +void adjustrules(Project *, int, int); +void adjusttankrules(Project *); +Spremise *getpremise(Spremise *, int); +Saction *getaction(Saction *, int); +int writerule(Project *, FILE *, int); +int checkrules(Project *, long); + +// ------- REPORT.C ----------------- + +int writereport(Project *); +void writelogo(Project *); +void writesummary(Project *); +void writehydstat(Project *, int, double); +void writeheader(Project *, int,int); +void writeline(Project *, char *); +void writerelerr(Project *, int, double); +void writestatchange(Project *, int,char,char); +void writecontrolaction(Project *, int, int); +void writeruleaction(Project *, int, char *); +int writehydwarn(Project *, int,double); +void writehyderr(Project *, int); +void writemassbalance(Project *); +void writetime(Project *, char *); +char *clocktime(char *, long); + +// ------- HYDRAUL.C ----------------- + +int openhyd(Project *); +void inithyd(Project *, int initFlags); +int runhyd(Project *, long *); +int nexthyd(Project *, long *); +void closehyd(Project *); +void setlinkstatus(Project *, int, char, StatusType *, double *); +void setlinksetting(Project *, int, double, StatusType *, double *); +int tanktimestep(Project *, long *); +void getenergy(Project *, int, double *, double *); +double tankvolume(Project *, int, double); +double tankgrade(Project *, int, double); + +// ------- HYDCOEFFS.C ----------------- + +void resistcoeff(Project *, int); +void headlosscoeffs(Project *); +void matrixcoeffs(Project *); +void emitheadloss(Project *, int, double *, double *); +double demandflowchange(Project *, int, double, double); +void demandparams(Project *, double *, double *); + +// ------- QUALITY.C -------------------- + +int openqual(Project *); +int initqual(Project *); +int runqual(Project *, long *); +int nextqual(Project *, long *); +int stepqual(Project *, long *); +int closequal(Project *); +double avgqual(Project *, int); + +// ------- OUTPUT.C --------------------- + +int savenetdata(Project *); +int savehyd(Project *, long *); +int savehydstep(Project *, long *); +int saveenergy(Project *); +int readhyd(Project *, long *); +int readhydstep(Project *, long *); +int saveoutput(Project *); +int savefinaloutput(Project *); + +// ------- INPFILE.C -------------------- + +int saveinpfile(Project *, const char *); #endif diff --git a/src/genmmd.c b/src/genmmd.c index 2dccb2b..df70f2e 100644 --- a/src/genmmd.c +++ b/src/genmmd.c @@ -4,7 +4,6 @@ * */ - #include int genmmd(int* neqns, int* xadj, int* adjncy, int* invp, int* perm, diff --git a/src/hash.c b/src/hash.c index 99c4cb4..95ac8dd 100755 --- a/src/hash.c +++ b/src/hash.c @@ -1,20 +1,14 @@ -/*----------------------------------------------------------------------------- - ** hash.c - ** - ** Implementation of a simple Hash Table that uses a string as a key - ** and an associated integer as data. - ** - ** Written by L. Rossman - ** Last Updated on 10/19/18 - ** - ** Interface Functions: - ** hashtable_create - creates a hash table - ** hashtable_insert - inserts a string & its data value into a table - ** hashtable_find - retrieves the data value associated with a string - ** hashtable_findkey - retrieves the key associated with a data value - ** hashtable_delete - deletes an entry from a table - ** hashtable_free - frees a hash table - ** + /* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: hash.c + Description: implementation of a simple hash table + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ #ifndef __APPLE__ @@ -27,6 +21,7 @@ #define HASHTABLEMAXSIZE 128000 +// An entry in the hash table typedef struct DataEntryStruct { char *key; @@ -34,6 +29,7 @@ typedef struct DataEntryStruct struct DataEntryStruct *next; } DataEntry; +// Hash a string to an integer unsigned int gethash(char *str) { unsigned int hash = 5381; @@ -47,6 +43,7 @@ unsigned int gethash(char *str) return retHash; } +// Produce a duplicate string char *dupstr(const char *s) { size_t size = strlen(s) + 1; @@ -55,6 +52,7 @@ char *dupstr(const char *s) return p; } +// Create a hash table HashTable *hashtable_create() { int i; @@ -66,6 +64,7 @@ HashTable *hashtable_create() return ht; } +// Insert an entry into the hash table int hashtable_insert(HashTable *ht, char *key, int data) { unsigned int i = gethash(key); @@ -80,6 +79,7 @@ int hashtable_insert(HashTable *ht, char *key, int data) return 1; } +// Change the hash table's data entry for a particular key int hashtable_update(HashTable *ht, char *key, int new_data) { unsigned int i = gethash(key); @@ -99,6 +99,7 @@ int hashtable_update(HashTable *ht, char *key, int new_data) return NOTFOUND; } +// Delete an entry in the hash table int hashtable_delete(HashTable *ht, char *key) { unsigned int i = gethash(key); @@ -124,6 +125,7 @@ int hashtable_delete(HashTable *ht, char *key) return NOTFOUND; } +// Find the data for a particular key int hashtable_find(HashTable *ht, char *key) { unsigned int i = gethash(key); @@ -142,6 +144,7 @@ int hashtable_find(HashTable *ht, char *key) return NOTFOUND; } +// Find a particular key in the hash table char *hashtable_findkey(HashTable *ht, char *key) { unsigned int i = gethash(key); @@ -156,6 +159,7 @@ char *hashtable_findkey(HashTable *ht, char *key) return NULL; } +// Delete a hash table and free all of its memory void hashtable_free(HashTable *ht) { DataEntry *entry, *nextentry; diff --git a/src/hash.h b/src/hash.h index 612b8d3..c847e00 100755 --- a/src/hash.h +++ b/src/hash.h @@ -1,9 +1,15 @@ -/* HASH.H -** -** Header file for Hash Table module HASH.C -** +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: hash.h + Description: header for a simple hash table + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ - #ifndef HASH_H #define HASH_H diff --git a/src/hydcoeffs.c b/src/hydcoeffs.c index e168f12..675ac30 100644 --- a/src/hydcoeffs.c +++ b/src/hydcoeffs.c @@ -1,9 +1,14 @@ /* -********************************************************************* - -HYDCOEFFS.C -- hydraulic coefficients for the EPANET Program - -******************************************************************* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: hydcoeffs.c + Description: computes coefficients for a hydraulic solution matrix + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ #include @@ -29,40 +34,44 @@ const double AA = -1.5634601348517065795e+00; // -2*.9*2/ln(10) const double AB = 3.28895476345399058690e-03; // 5.74/(4000^.9) const double AC = -5.14214965799093883760e-03; // AA*AB -// External functions -//void resistcoeff(EN_Project *pr, int k); -//void headlosscoeffs(EN_Project *pr); -//void matrixcoeffs(EN_Project *pr); -//void emitheadloss(EN_Project *pr, int i, double *hloss, double *dhdq); -//double demandflowchange(EN_Project *pr, int i, double dp, double n); -//void demandparams(EN_Project *pr, double *dp, double *n); +// Definitions of very small and very big coefficients +const double CSMALL = 1.e-6; +const double CBIG = 1.e8; + +// Exported functions +//void resistcoeff(Project *, int ); +//void headlosscoeffs(Project *); +//void matrixcoeffs(Project *); +//void emitheadloss(Project *, int, double *, double *); +//double demandflowchange(Project *, int, double, double); +//void demandparams(Project *, double *, double *); // Local functions -static void linkcoeffs(EN_Project *pr); -static void nodecoeffs(EN_Project *pr); -static void valvecoeffs(EN_Project *pr); -static void emittercoeffs(EN_Project *pr); -static void demandcoeffs(EN_Project *pr); +static void linkcoeffs(Project *pr); +static void nodecoeffs(Project *pr); +static void valvecoeffs(Project *pr); +static void emittercoeffs(Project *pr); +static void demandcoeffs(Project *pr); static void demandheadloss(double d, double dfull, double dp, double n, double *hloss, double *hgrad); -static void pipecoeff(EN_Project *pr, int k); -static void DWpipecoeff(EN_Project *pr, int k); +static void pipecoeff(Project *pr, int k); +static void DWpipecoeff(Project *pr, int k); static double frictionFactor(double q, double e, double s, double *dfdq); -static void pumpcoeff(EN_Project *pr, int k); -static void curvecoeff(EN_Project *pr, int i, double q, double *h0, double *r); +static void pumpcoeff(Project *pr, int k); +static void curvecoeff(Project *pr, int i, double q, double *h0, double *r); -static void valvecoeff(EN_Project *pr, int k); -static void gpvcoeff(EN_Project *pr, int k); -static void pbvcoeff(EN_Project *pr, int k); -static void tcvcoeff(EN_Project *pr, int k); -static void prvcoeff(EN_Project *pr, int k, int n1, int n2); -static void psvcoeff(EN_Project *pr, int k, int n1, int n2); -static void fcvcoeff(EN_Project *pr, int k, int n1, int n2); +static void valvecoeff(Project *pr, int k); +static void gpvcoeff(Project *pr, int k); +static void pbvcoeff(Project *pr, int k); +static void tcvcoeff(Project *pr, int k); +static void prvcoeff(Project *pr, int k, int n1, int n2); +static void psvcoeff(Project *pr, int k, int n1, int n2); +static void fcvcoeff(Project *pr, int k, int n1, int n2); -void resistcoeff(EN_Project *pr, int k) +void resistcoeff(Project *pr, int k) /* **-------------------------------------------------------------------- ** Input: k = link index @@ -71,10 +80,10 @@ void resistcoeff(EN_Project *pr, int k) **-------------------------------------------------------------------- */ { - double e, d, L; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; + double e, d, L; Slink *link = &net->Link[k]; link->Qa = 0.0; @@ -121,7 +130,7 @@ void resistcoeff(EN_Project *pr, int k) } -void headlosscoeffs(EN_Project *pr) +void headlosscoeffs(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -131,9 +140,10 @@ void headlosscoeffs(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + int k; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; for (k = 1; k <= net->Nlinks; k++) { @@ -159,13 +169,13 @@ void headlosscoeffs(EN_Project *pr) case PRV: case PSV: if (hyd->LinkSetting[k] == MISSING) valvecoeff(pr, k); - else hyd->solver.P[k] = 0.0; + else hyd->P[k] = 0.0; } } } -void matrixcoeffs(EN_Project *pr) +void matrixcoeffs(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -174,16 +184,16 @@ void matrixcoeffs(EN_Project *pr) **-------------------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - EN_Network *net = &pr->network; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; // Reset values of all diagonal coeffs. (Aii), off-diagonal - // coeffs. (Aij), r.h.s. coeffs. (F) and node flow balance (X_tmp) - memset(sol->Aii, 0, (net->Nnodes + 1) * sizeof(double)); - memset(sol->Aij, 0, (hyd->Ncoeffs + 1) * sizeof(double)); - memset(sol->F, 0, (net->Nnodes + 1) * sizeof(double)); - memset(hyd->X_tmp, 0, (net->Nnodes + 1) * sizeof(double)); + // coeffs. (Aij), r.h.s. coeffs. (F) and node excess flow (Xflow) + memset(sm->Aii, 0, (net->Nnodes + 1) * sizeof(double)); + memset(sm->Aij, 0, (sm->Ncoeffs + 1) * sizeof(double)); + memset(sm->F, 0, (net->Nnodes + 1) * sizeof(double)); + memset(hyd->Xflow, 0, (net->Nnodes + 1) * sizeof(double)); // Compute matrix coeffs. from links, emitters, and nodal demands linkcoeffs(pr); @@ -199,7 +209,7 @@ void matrixcoeffs(EN_Project *pr) } -void linkcoeffs(EN_Project *pr) +void linkcoeffs(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -209,80 +219,81 @@ void linkcoeffs(EN_Project *pr) **-------------------------------------------------------------- */ { - int k, n1, n2; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; + int k, n1, n2; Slink *link; // Examine each link of network for (k = 1; k <= net->Nlinks; k++) { - if (sol->P[k] == 0.0) continue; + if (hyd->P[k] == 0.0) continue; link = &net->Link[k]; n1 = link->N1; // Start node of link n2 = link->N2; // End node of link - // Update nodal flow balance (X_tmp) + // Update nodal flow excess (Xflow) // (Flow out of node is (-), flow into node is (+)) - hyd->X_tmp[n1] -= hyd->LinkFlows[k]; - hyd->X_tmp[n2] += hyd->LinkFlows[k]; + hyd->Xflow[n1] -= hyd->LinkFlow[k]; + hyd->Xflow[n2] += hyd->LinkFlow[k]; // Add to off-diagonal coeff. of linear system matrix - sol->Aij[sol->Ndx[k]] -= sol->P[k]; + sm->Aij[sm->Ndx[k]] -= hyd->P[k]; // Update linear system coeffs. associated with start node n1 // ... node n1 is junction if (n1 <= net->Njuncs) { - sol->Aii[sol->Row[n1]] += sol->P[k]; // Diagonal coeff. - sol->F[sol->Row[n1]] += sol->Y[k]; // RHS coeff. + 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 sol->F[sol->Row[n2]] += (sol->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 if (n2 <= net->Njuncs) { - sol->Aii[sol->Row[n2]] += sol->P[k]; // Diagonal coeff. - sol->F[sol->Row[n2]] -= sol->Y[k]; // RHS coeff. + sm->Aii[sm->Row[n2]] += hyd->P[k]; // Diagonal coeff. + sm->F[sm->Row[n2]] -= hyd->Y[k]; // RHS coeff. } // ... node n2 is a tank/reservoir - else sol->F[sol->Row[n1]] += (sol->P[k] * hyd->NodeHead[n2]); + else sm->F[sm->Row[n1]] += (hyd->P[k] * hyd->NodeHead[n2]); } } -void nodecoeffs(EN_Project *pr) +void nodecoeffs(Project *pr) /* **---------------------------------------------------------------- ** Input: none ** Output: none ** Purpose: completes calculation of nodal flow balance array -** (X_tmp) & r.h.s. (F) of linearized hydraulic eqns. +** (Xflow) & r.h.s. (F) of linearized hydraulic eqns. **---------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; + int i; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - EN_Network *net = &pr->network; // For junction nodes, subtract demand flow from net - // flow balance & add flow balance to RHS array F + // flow excess & add flow excess to RHS array F for (i = 1; i <= net->Njuncs; i++) { - hyd->X_tmp[i] -= hyd->DemandFlows[i]; - sol->F[sol->Row[i]] += hyd->X_tmp[i]; + hyd->Xflow[i] -= hyd->DemandFlow[i]; + sm->F[sm->Row[i]] += hyd->Xflow[i]; } } -void valvecoeffs(EN_Project *pr) +void valvecoeffs(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -293,10 +304,10 @@ void valvecoeffs(EN_Project *pr) **-------------------------------------------------------------- */ { - int i, k, n1, n2; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; - hydraulics_t *hyd = &pr->hydraulics; - EN_Network *net = &pr->network; + int i, k, n1, n2; Slink *link; Svalve *valve; @@ -333,7 +344,7 @@ void valvecoeffs(EN_Project *pr) } -void emittercoeffs(EN_Project *pr) +void emittercoeffs(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -348,13 +359,13 @@ void emittercoeffs(EN_Project *pr) **-------------------------------------------------------------- */ { - int i, row; - double hloss, hgrad; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - EN_Network *net = &pr->network; - Snode *node; + int i, row; + double hloss, hgrad; + Snode *node; for (i = 1; i <= net->Njuncs; i++) { @@ -366,19 +377,19 @@ void emittercoeffs(EN_Project *pr) emitheadloss(pr, i, &hloss, &hgrad); // Row of solution matrix - row = sol->Row[i]; + row = sm->Row[i]; // Addition to matrix diagonal & r.h.s - sol->Aii[row] += 1.0 / hgrad; - sol->F[row] += (hloss + node->El) / hgrad; + sm->Aii[row] += 1.0 / hgrad; + sm->F[row] += (hloss + node->El) / hgrad; - // Update to node flow balance - hyd->X_tmp[i] -= hyd->EmitterFlows[i]; + // Update to node flow excess + hyd->Xflow[i] -= hyd->EmitterFlow[i]; } } -void emitheadloss(EN_Project *pr, int i, double *hloss, double *hgrad) +void emitheadloss(Project *pr, int i, double *hloss, double *hgrad) /* **------------------------------------------------------------- ** Input: i = node index @@ -388,10 +399,11 @@ void emitheadloss(EN_Project *pr, int i, double *hloss, double *hgrad) **------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + double ke; double q; double qa; - hydraulics_t *hyd = &pr->hydraulics; // Set adjusted emitter coeff. ke = MAX(CSMALL, pr->network.Node[i].Ke); @@ -400,7 +412,7 @@ void emitheadloss(EN_Project *pr, int i, double *hloss, double *hgrad) qa = pow(hyd->RQtol / ke / hyd->Qexp, 1.0 / (hyd->Qexp - 1.0)); // Use linear head loss relation for small flow - q = hyd->EmitterFlows[i]; + q = hyd->EmitterFlow[i]; if (fabs(q) <= qa) { *hgrad = hyd->RQtol; @@ -416,7 +428,7 @@ void emitheadloss(EN_Project *pr, int i, double *hloss, double *hgrad) } -void demandparams(EN_Project *pr, double *dp, double *n) +void demandparams(Project *pr, double *dp, double *n) /* **-------------------------------------------------------------- ** Input: none @@ -427,7 +439,7 @@ void demandparams(EN_Project *pr, double *dp, double *n) **-------------------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; // If required pressure equals minimum pressure, use a linear demand // curve with a 0.01 PSI pressure range to approximate an all or @@ -447,7 +459,7 @@ void demandparams(EN_Project *pr, double *dp, double *n) } -void demandcoeffs(EN_Project *pr) +void demandcoeffs(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -462,16 +474,16 @@ void demandcoeffs(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; + int i, row; 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) - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - EN_Network *net = &pr->network; - // Get demand function parameters if (hyd->DemandModel == DDA) return; demandparams(pr, &dp, &n); @@ -483,18 +495,18 @@ void demandcoeffs(EN_Project *pr) if (hyd->NodeDemand[i] <= 0.0) continue; // Find head loss for demand outflow at node's elevation - demandheadloss(hyd->DemandFlows[i], hyd->NodeDemand[i], dp, n, + demandheadloss(hyd->DemandFlow[i], hyd->NodeDemand[i], dp, n, &hloss, &hgrad); // Update row of solution matrix A & its r.h.s. F - row = sol->Row[i]; - sol->Aii[row] += 1.0 / hgrad; - sol->F[row] += (hloss + net->Node[i].El + hyd->Pmin) / hgrad; + row = sm->Row[i]; + sm->Aii[row] += 1.0 / hgrad; + sm->F[row] += (hloss + net->Node[i].El + hyd->Pmin) / hgrad; } } -double demandflowchange(EN_Project *pr, int i, double dp, double n) +double demandflowchange(Project *pr, int i, double dp, double n) /* **-------------------------------------------------------------- ** Input: i = node index @@ -506,16 +518,17 @@ double demandflowchange(EN_Project *pr, int i, double dp, double n) **-------------------------------------------------------------- */ { - double hloss, hgrad; - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; - demandheadloss(hyd->DemandFlows[i], hyd->NodeDemand[i], dp, n, &hloss, &hgrad); + double hloss, hgrad; + + demandheadloss(hyd->DemandFlow[i], hyd->NodeDemand[i], dp, n, &hloss, &hgrad); return (hloss - hyd->NodeHead[i] + pr->network.Node[i].El + hyd->Pmin) / hgrad; } void demandheadloss(double d, double dfull, double dp, double n, - double *hloss, double *hgrad) + double *hloss, double *hgrad) /* **-------------------------------------------------------------- ** Input: d = actual junction demand (cfs) @@ -563,7 +576,7 @@ void demandheadloss(double d, double dfull, double dp, double n, } -void pipecoeff(EN_Project *pr, int k) +void pipecoeff(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -575,20 +588,19 @@ void pipecoeff(EN_Project *pr, int k) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + double hloss, // Head loss hgrad, // Head loss gradient ml, // Minor loss coeff. q, // Abs. value of flow r; // Resistance coeff. - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - // For closed pipe use headloss formula: hloss = CBIG*q if (hyd->LinkStatus[k] <= CLOSED) { - sol->P[k] = 1.0 / CBIG; - sol->Y[k] = hyd->LinkFlows[k]; + hyd->P[k] = 1.0 / CBIG; + hyd->Y[k] = hyd->LinkFlow[k]; return; } @@ -599,7 +611,7 @@ void pipecoeff(EN_Project *pr, int k) return; } - q = ABS(hyd->LinkFlows[k]); + q = ABS(hyd->LinkFlow[k]); ml = pr->network.Link[k].Km; r = pr->network.Link[k].R; @@ -625,15 +637,15 @@ void pipecoeff(EN_Project *pr, int k) } // Adjust head loss sign for flow direction - hloss *= SGN(hyd->LinkFlows[k]); + hloss *= SGN(hyd->LinkFlow[k]); // P and Y coeffs. - sol->P[k] = 1.0 / hgrad; - sol->Y[k] = hloss / hgrad; + hyd->P[k] = 1.0 / hgrad; + hyd->Y[k] = hloss / hgrad; } -void DWpipecoeff(EN_Project *pr, int k) +void DWpipecoeff(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -643,16 +655,14 @@ void DWpipecoeff(EN_Project *pr, int k) **-------------------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - Slink *link = &pr->network.Link[k]; + Hydraul *hyd = &pr->hydraul; + Slink *link = &pr->network.Link[k]; - double q = ABS(hyd->LinkFlows[k]); + double q = ABS(hyd->LinkFlow[k]); 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 @@ -660,7 +670,7 @@ void DWpipecoeff(EN_Project *pr, int k) if (q <= A2 * s) { r = 16.0 * PI * s * r; - hloss = hyd->LinkFlows[k] * (r + ml * q); + hloss = hyd->LinkFlow[k] * (r + ml * q); hgrad = r + 2.0 * ml * q; } @@ -670,13 +680,13 @@ void DWpipecoeff(EN_Project *pr, int k) dfdq = 0.0; f = frictionFactor(q, e, s, &dfdq); r1 = f * r + ml; - hloss = r1 * q * hyd->LinkFlows[k]; + hloss = r1 * q * hyd->LinkFlow[k]; hgrad = (2.0 * r1 * q) + (dfdq * r * q * q); } // Compute P and Y coefficients - sol->P[k] = 1.0 / hgrad; - sol->Y[k] = hloss / hgrad; + hyd->P[k] = 1.0 / hgrad; + hyd->Y[k] = hloss / hgrad; } @@ -730,7 +740,7 @@ double frictionFactor(double q, double e, double s, double *dfdq) } -void pumpcoeff(EN_Project *pr, int k) +void pumpcoeff(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -739,6 +749,8 @@ void pumpcoeff(EN_Project *pr, int k) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + int p; // Pump index double h0, // Shutoff head q, // Abs. value of flow @@ -748,22 +760,19 @@ void pumpcoeff(EN_Project *pr, int k) qa, // Flow limit for linear head loss hloss, // Head loss across pump hgrad; // Head loss gradient - - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - Spump *pump; + Spump *pump; // Use high resistance pipe if pump closed or cannot deliver head setting = hyd->LinkSetting[k]; if (hyd->LinkStatus[k] <= CLOSED || setting == 0.0) { - sol->P[k] = 1.0 / CBIG; - sol->Y[k] = hyd->LinkFlows[k]; + hyd->P[k] = 1.0 / CBIG; + hyd->Y[k] = hyd->LinkFlow[k]; return; } // Obtain reference to pump object & its speed setting - q = ABS(hyd->LinkFlows[k]); + q = ABS(hyd->LinkFlow[k]); p = findpump(&pr->network, k); pump = &pr->network.Pump[p]; @@ -783,7 +792,7 @@ void pumpcoeff(EN_Project *pr, int k) // Compute head loss and its gradient hgrad = pump->R * setting ; - hloss = pump->H0 * SQR(setting) + hgrad * hyd->LinkFlows[k]; + hloss = pump->H0 * SQR(setting) + hgrad * hyd->LinkFlow[k]; } else { @@ -798,23 +807,23 @@ void pumpcoeff(EN_Project *pr, int k) if (q <= qa) { hgrad = hyd->RQtol; - hloss = h0 + hgrad * hyd->LinkFlows[k]; + hloss = h0 + hgrad * hyd->LinkFlow[k]; } // ... use original pump curve for normal flows else { hgrad = n * r * pow(q, n - 1.0); - hloss = h0 + hgrad * hyd->LinkFlows[k] / n; + hloss = h0 + hgrad * hyd->LinkFlow[k] / n; } } // P and Y coeffs. - sol->P[k] = 1.0 / hgrad; - sol->Y[k] = hloss / hgrad; + hyd->P[k] = 1.0 / hgrad; + hyd->Y[k] = hloss / hgrad; } -void curvecoeff(EN_Project *pr, int i, double q, double *h0, double *r) +void curvecoeff(Project *pr, int i, double q, double *h0, double *r) /* **------------------------------------------------------------------- ** Input: i = curve index @@ -854,7 +863,7 @@ void curvecoeff(EN_Project *pr, int i, double q, double *h0, double *r) } -void gpvcoeff(EN_Project *pr, int k) +void gpvcoeff(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -868,8 +877,7 @@ void gpvcoeff(EN_Project *pr, int k) r, // Slope of head loss curve segment q; // Abs. value of flow - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; + Hydraul *hyd = &pr->hydraul; // Treat as a pipe if valve closed if (hyd->LinkStatus[k] == CLOSED) valvecoeff(pr, k); @@ -883,7 +891,7 @@ void gpvcoeff(EN_Project *pr, int k) i = (int)ROUND(hyd->LinkSetting[k]); // Adjusted flow rate - q = ABS(hyd->LinkFlows[k]); + q = ABS(hyd->LinkFlow[k]); q = MAX(q, TINY); // Intercept and slope of curve segment containing q @@ -891,13 +899,13 @@ void gpvcoeff(EN_Project *pr, int k) r = MAX(r, TINY); // Resulting P and Y coeffs. - sol->P[k] = 1.0 / r; - sol->Y[k] = (h0 / r + q) * SGN(hyd->LinkFlows[k]); + hyd->P[k] = 1.0 / r; + hyd->Y[k] = (h0 / r + q) * SGN(hyd->LinkFlow[k]); } } -void pbvcoeff(EN_Project *pr, int k) +void pbvcoeff(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -906,8 +914,7 @@ void pbvcoeff(EN_Project *pr, int k) **-------------------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; + Hydraul *hyd = &pr->hydraul; Slink *link = &pr->network.Link[k]; // If valve fixed OPEN or CLOSED then treat as a pipe @@ -920,21 +927,21 @@ void pbvcoeff(EN_Project *pr, int k) else { // Treat as a pipe if minor loss > valve setting - if (link->Km * SQR(hyd->LinkFlows[k]) > hyd->LinkSetting[k]) + if (link->Km * SQR(hyd->LinkFlow[k]) > hyd->LinkSetting[k]) { valvecoeff(pr, k); } // Otherwise force headloss across valve to be equal to setting else { - sol->P[k] = CBIG; - sol->Y[k] = hyd->LinkSetting[k] * CBIG; + hyd->P[k] = CBIG; + hyd->Y[k] = hyd->LinkSetting[k] * CBIG; } } } -void tcvcoeff(EN_Project *pr, int k) +void tcvcoeff(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -944,7 +951,7 @@ void tcvcoeff(EN_Project *pr, int k) */ { double km; - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; Slink *link = &pr->network.Link[k]; // Save original loss coeff. for open valve @@ -964,7 +971,7 @@ void tcvcoeff(EN_Project *pr, int k) } -void prvcoeff(EN_Project *pr, int k, int n1, int n2) +void prvcoeff(Project *pr, int k, int n1, int n2) /* **-------------------------------------------------------------- ** Input: k = link index @@ -976,13 +983,14 @@ void prvcoeff(EN_Project *pr, int k, int n1, int n2) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; + int i, j; // Rows of solution matrix double hset; // Valve head setting - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - i = sol->Row[n1]; // Matrix rows of nodes - j = sol->Row[n2]; + i = sm->Row[n1]; // Matrix rows of nodes + j = sm->Row[n2]; hset = pr->network.Node[n2].El + hyd->LinkSetting[k]; // Valve setting @@ -991,15 +999,15 @@ void prvcoeff(EN_Project *pr, int k, int n1, int n2) // Set coeffs. to force head at downstream // node equal to valve setting & force flow - // to equal to flow imbalance at downstream node. + // to equal to flow excess at downstream node. - sol->P[k] = 0.0; - sol->Y[k] = hyd->LinkFlows[k] + hyd->X_tmp[n2]; // Force flow balance - sol->F[j] += (hset * CBIG); // Force head = hset - sol->Aii[j] += CBIG; // at downstream node - if (hyd->X_tmp[n2] < 0.0) + hyd->P[k] = 0.0; + hyd->Y[k] = hyd->LinkFlow[k] + hyd->Xflow[n2]; // Force flow balance + sm->F[j] += (hset * CBIG); // Force head = hset + sm->Aii[j] += CBIG; // at downstream node + if (hyd->Xflow[n2] < 0.0) { - sol->F[i] += hyd->X_tmp[n2]; + sm->F[i] += hyd->Xflow[n2]; } return; } @@ -1008,15 +1016,15 @@ void prvcoeff(EN_Project *pr, int k, int n1, int n2) // compute matrix coeffs. using the valvecoeff() function. valvecoeff(pr, k); - sol->Aij[sol->Ndx[k]] -= sol->P[k]; - sol->Aii[i] += sol->P[k]; - sol->Aii[j] += sol->P[k]; - sol->F[i] += (sol->Y[k] - hyd->LinkFlows[k]); - sol->F[j] -= (sol->Y[k] - hyd->LinkFlows[k]); + sm->Aij[sm->Ndx[k]] -= hyd->P[k]; + sm->Aii[i] += hyd->P[k]; + sm->Aii[j] += hyd->P[k]; + sm->F[i] += (hyd->Y[k] - hyd->LinkFlow[k]); + sm->F[j] -= (hyd->Y[k] - hyd->LinkFlow[k]); } -void psvcoeff(EN_Project *pr, int k, int n1, int n2) +void psvcoeff(Project *pr, int k, int n1, int n2) /* **-------------------------------------------------------------- ** Input: k = link index @@ -1028,13 +1036,14 @@ void psvcoeff(EN_Project *pr, int k, int n1, int n2) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; + int i, j; // Rows of solution matrix double hset; // Valve head setting - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - i = sol->Row[n1]; // Matrix rows of nodes - j = sol->Row[n2]; + i = sm->Row[n1]; // Matrix rows of nodes + j = sm->Row[n2]; hset = pr->network.Node[n1].El + hyd->LinkSetting[k]; // Valve setting @@ -1042,15 +1051,15 @@ void psvcoeff(EN_Project *pr, int k, int n1, int n2) { // Set coeffs. to force head at upstream // node equal to valve setting & force flow - // equal to flow imbalance at upstream node. + // equal to flow excess at upstream node. - sol->P[k] = 0.0; - sol->Y[k] = hyd->LinkFlows[k] - hyd->X_tmp[n1]; // Force flow balance - sol->F[i] += (hset * CBIG); // Force head = hset - sol->Aii[i] += CBIG; // at upstream node - if (hyd->X_tmp[n1] > 0.0) + hyd->P[k] = 0.0; + hyd->Y[k] = hyd->LinkFlow[k] - hyd->Xflow[n1]; // Force flow balance + sm->F[i] += (hset * CBIG); // Force head = hset + sm->Aii[i] += CBIG; // at upstream node + if (hyd->Xflow[n1] > 0.0) { - sol->F[j] += hyd->X_tmp[n1]; + sm->F[j] += hyd->Xflow[n1]; } return; } @@ -1059,15 +1068,15 @@ void psvcoeff(EN_Project *pr, int k, int n1, int n2) // compute matrix coeffs. using the valvecoeff() function. valvecoeff(pr, k); - sol->Aij[sol->Ndx[k]] -= sol->P[k]; - sol->Aii[i] += sol->P[k]; - sol->Aii[j] += sol->P[k]; - sol->F[i] += (sol->Y[k] - hyd->LinkFlows[k]); - sol->F[j] -= (sol->Y[k] - hyd->LinkFlows[k]); + sm->Aij[sm->Ndx[k]] -= hyd->P[k]; + sm->Aii[i] += hyd->P[k]; + sm->Aii[j] += hyd->P[k]; + sm->F[i] += (hyd->Y[k] - hyd->LinkFlow[k]); + sm->F[j] -= (hyd->Y[k] - hyd->LinkFlow[k]); } -void fcvcoeff(EN_Project *pr, int k, int n1, int n2) +void fcvcoeff(Project *pr, int k, int n1, int n2) /* **-------------------------------------------------------------- ** Input: k = link index @@ -1079,14 +1088,15 @@ void fcvcoeff(EN_Project *pr, int k, int n1, int n2) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; + int i, j; // Rows in solution matrix double q; // Valve flow setting - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; q = hyd->LinkSetting[k]; - i = hyd->solver.Row[n1]; - j = hyd->solver.Row[n2]; + i = sm->Row[n1]; + j = sm->Row[n2]; // If valve active, break network at valve and treat // flow setting as external demand at upstream node @@ -1094,15 +1104,15 @@ void fcvcoeff(EN_Project *pr, int k, int n1, int n2) if (hyd->LinkStatus[k] == ACTIVE) { - hyd->X_tmp[n1] -= q; - sol->F[i] -= q; - hyd->X_tmp[n2] += q; - sol->F[j] += q; - sol->P[k] = 1.0 / CBIG; - sol->Aij[sol->Ndx[k]] -= sol->P[k]; - sol->Aii[i] += sol->P[k]; - sol->Aii[j] += sol->P[k]; - sol->Y[k] = hyd->LinkFlows[k] - q; + hyd->Xflow[n1] -= q; + hyd->Xflow[n2] += q; + hyd->Y[k] = hyd->LinkFlow[k] - q; + sm->F[i] -= q; + sm->F[j] += q; + hyd->P[k] = 1.0 / CBIG; + sm->Aij[sm->Ndx[k]] -= hyd->P[k]; + sm->Aii[i] += hyd->P[k]; + sm->Aii[j] += hyd->P[k]; } // Otherwise treat valve as an open pipe @@ -1110,16 +1120,16 @@ void fcvcoeff(EN_Project *pr, int k, int n1, int n2) else { valvecoeff(pr, k); - sol->Aij[sol->Ndx[k]] -= sol->P[k]; - sol->Aii[i] += sol->P[k]; - sol->Aii[j] += sol->P[k]; - sol->F[i] += (sol->Y[k] - hyd->LinkFlows[k]); - sol->F[j] -= (sol->Y[k] - hyd->LinkFlows[k]); + sm->Aij[sm->Ndx[k]] -= hyd->P[k]; + sm->Aii[i] += hyd->P[k]; + sm->Aii[j] += hyd->P[k]; + sm->F[i] += (hyd->Y[k] - hyd->LinkFlow[k]); + sm->F[j] -= (hyd->Y[k] - hyd->LinkFlow[k]); } } -void valvecoeff(EN_Project *pr, int k) +void valvecoeff(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -1129,20 +1139,18 @@ void valvecoeff(EN_Project *pr, int k) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + Slink *link = &pr->network.Link[k]; + double flow, q, y, qa, hgrad; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - Slink *link = &net->Link[k]; - - flow = hyd->LinkFlows[k]; + flow = hyd->LinkFlow[k]; // Valve is closed. Use a very small matrix coeff. if (hyd->LinkStatus[k] <= CLOSED) { - sol->P[k] = 1.0 / CBIG; - sol->Y[k] = flow; + hyd->P[k] = 1.0 / CBIG; + hyd->Y[k] = flow; return; } @@ -1164,15 +1172,15 @@ void valvecoeff(EN_Project *pr, int k) } // P and Y coeffs. - sol->P[k] = 1.0 / hgrad; - sol->Y[k] = y; + hyd->P[k] = 1.0 / hgrad; + hyd->Y[k] = y; } // If no minor loss coeff. specified use a // low resistance linear head loss relation else { - sol->P[k] = 1.0 / CSMALL; - sol->Y[k] = flow; + hyd->P[k] = 1.0 / CSMALL; + hyd->Y[k] = flow; } } diff --git a/src/hydraul.c b/src/hydraul.c index 2ed43fc..188eb67 100755 --- a/src/hydraul.c +++ b/src/hydraul.c @@ -1,49 +1,14 @@ /* -********************************************************************* - -HYDRAUL.C -- Hydraulic Simulator for EPANET Program - -VERSION: 2.00 -DATE: 6/5/00 - 9/7/00 - 10/25/00 - 12/29/00 - 3/1/01 - 11/19/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - - This module contains the network hydraulic simulator. - It simulates the network's hydraulic behavior over an - an extended period of time and writes its results to the - binary file HydFile. - - The entry points for this module are: - openhyd() -- called from ENopenH() in EPANET.C - inithyd() -- called from ENinitH() in EPANET.C - runhyd() -- called from ENrunH() in EPANET.C - nexthyd() -- called from ENnextH() in EPANET.C - closehyd() -- called from ENcloseH() in EPANET.C - tankvolume() -- called from ENsetnodevalue() in EPANET.C - setlinkstatus(), - setlinksetting() -- all called from ENsetlinkvalue() in EPANET.C - - External functions called by this module are: - createsparse() -- see SMATRIX.C - freesparse() -- see SMATRIX.C - resistcoeff() -- see HYDCOEFFS.C - hydsolve() -- see HYDSOLVER.C - checkrules() -- see RULES.C - interp() -- see EPANET.C - savehyd() -- see OUTPUT.C - savehydstep() -- see OUTPUT.C - writehydstat() -- see REPORT.C - writehyderr() -- see REPORT.C - writehydwarn() -- see REPORT.C -******************************************************************* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: hydraul.c + Description: implements EPANET's hydraulic engine + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ #include @@ -59,23 +24,28 @@ AUTHOR: L. Rossman #include "funcs.h" #include "text.h" -#define QZERO 1.e-6 /* Equivalent to zero flow */ +const double QZERO = 1.e-6; // Equivalent to zero flow in cfs + +// Imported functions +extern int createsparse(Project *); +extern void freesparse(Project *); +extern int hydsolve(Project *, int *, double *); // Local functions -int allocmatrix(EN_Project *pr); -void freematrix(EN_Project *pr); -void initlinkflow(EN_Project *pr, int, char, double); -void setlinkflow(EN_Project *pr, int, double); -void demands(EN_Project *pr); -int controls(EN_Project *pr); -long timestep(EN_Project *pr); -void controltimestep(EN_Project *pr, long *); -void ruletimestep(EN_Project *pr, long *); -void addenergy(EN_Project *pr, long); -void tanklevels(EN_Project *pr, long); +int allocmatrix(Project *); +void freematrix(Project *); +void initlinkflow(Project *, int, char, double); +void setlinkflow(Project *, int, 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); -int openhyd(EN_Project *pr) +int openhyd(Project *pr) /* *-------------------------------------------------------------- * Input: none @@ -86,18 +56,24 @@ int openhyd(EN_Project *pr) { int i; int errcode = 0; - ERRCODE(createsparse(pr)); /* See SMATRIX.C */ - ERRCODE(allocmatrix(pr)); /* Allocate solution matrices */ - for (i=1; i <= pr->network.Nlinks; i++) { /* Initialize flows */ - Slink *link = &pr->network.Link[i]; - initlinkflow(pr, i, link->Stat, link->Kc); + Slink *link; + + // Allocate memory for sparse matrix structures (see SMATRIX.C) + ERRCODE(createsparse(pr)); + + // Allocate memory for hydraulic variables + ERRCODE(allocmatrix(pr)); + + // Initialize link flows + if (!errcode) for (i = 1; i <= pr->network.Nlinks; i++) + { + link = &pr->network.Link[i]; + initlinkflow(pr, i, link->Status, link->Kc); } - return(errcode); + return errcode; } - -/*** Updated 3/1/01 ***/ -void inithyd(EN_Project *pr, int initflag) +void inithyd(Project *pr, int initflag) /* **-------------------------------------------------------------- ** Input: initflag > 0 if link flows should be re-initialized @@ -107,76 +83,84 @@ void inithyd(EN_Project *pr, int initflag) **-------------------------------------------------------------- */ { - int i,j; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Outfile *out = &pr->outfile; + Times *time = &pr->times; - time_options_t *time = &pr->time_options; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - out_file_t *out = &pr->out_files; - Stank *tank; - Slink *link; - Spump *pump; + int i; + Stank *tank; + Slink *link; + Spump *pump; - /* Initialize tanks */ - for (i=1; i <= net->Ntanks; i++) { + // Initialize tanks + for (i = 1; i <= net->Ntanks; i++) + { tank = &net->Tank[i]; tank->V = tank->V0; hyd->NodeHead[tank->Node] = tank->H0; hyd->NodeDemand[tank->Node] = 0.0; - hyd->OldStat[net->Nlinks+i] = TEMPCLOSED; + hyd->OldStatus[net->Nlinks+i] = TEMPCLOSED; } - /* Initialize emitter flows */ - memset(hyd->EmitterFlows,0,(net->Nnodes+1)*sizeof(double)); - for (i=1; i <= net->Njuncs; i++) { - if (net->Node[i].Ke > 0.0) { - hyd->EmitterFlows[i] = 1.0; - } + // Initialize emitter flows + memset(hyd->EmitterFlow,0,(net->Nnodes+1)*sizeof(double)); + for (i = 1; i <= net->Njuncs; i++) + { + if (net->Node[i].Ke > 0.0) hyd->EmitterFlow[i] = 1.0; } - /* Initialize links */ - for (i=1; i <= net->Nlinks; i++) { + // Initialize links + for (i = 1; i <= net->Nlinks; i++) + { link = &net->Link[i]; - /* Initialize status and setting */ - hyd->LinkStatus[i] = link->Stat; + // Initialize status and setting + hyd->LinkStatus[i] = link->Status; hyd->LinkSetting[i] = link->Kc; - /* Compute flow resistance */ + // Compute flow resistance resistcoeff(pr, i); - /* Start active control valves in ACTIVE position */ + // Start active control valves in ACTIVE position if ( (link->Type == PRV || link->Type == PSV || link->Type == FCV) && (link->Kc != MISSING) ) hyd->LinkStatus[i] = ACTIVE; -/*** Updated 3/1/01 ***/ - /* Initialize flows if necessary */ - if (hyd->LinkStatus[i] <= CLOSED) hyd->LinkFlows[i] = QZERO; - else if (ABS(hyd->LinkFlows[i]) <= QZERO || initflag > 0) - initlinkflow(pr, i, hyd->LinkStatus[i], hyd->LinkSetting[i]); + // Initialize flows if necessary + if (hyd->LinkStatus[i] <= CLOSED) + { + hyd->LinkFlow[i] = QZERO; + } + else if (ABS(hyd->LinkFlow[i]) <= QZERO || initflag > 0) + { + initlinkflow(pr, i, hyd->LinkStatus[i], hyd->LinkSetting[i]); + } - /* Save initial status */ - hyd->OldStat[i] = hyd->LinkStatus[i]; + // Save initial status + hyd->OldStatus[i] = hyd->LinkStatus[i]; } - /* Initialize pump energy usage */ - for (i=1; i <= net->Npumps; i++) + // Initialize pump energy usage + for (i = 1; i <= net->Npumps; i++) { pump = &net->Pump[i]; - for (j = 0; j < MAX_ENERGY_STATS; j++) { - pump->Energy[j] = 0.0; - } + pump->Energy.Efficiency = 0.0; + pump->Energy.TimeOnLine = 0.0; + pump->Energy.KwHrs = 0.0; + pump->Energy.KwHrsPerFlow = 0.0; + pump->Energy.MaxKwatts = 0.0; + pump->Energy.TotalCost = 0.0; } - /* Re-position hydraulics file */ - if (pr->save_options.Saveflag) { + // Re-position hydraulics file + if (pr->outfile.Saveflag) + { fseek(out->HydFile,out->HydOffset,SEEK_SET); } -/*** Updated 3/1/01 ***/ - /* Initialize current time */ + // Initialize current time hyd->Haltflag = 0; time->Htime = 0; time->Hydstep = 0; @@ -184,7 +168,7 @@ void inithyd(EN_Project *pr, int initflag) } -int runhyd(EN_Project *pr, long *t) +int runhyd(Project *pr, long *t) /* **-------------------------------------------------------------- ** Input: none @@ -194,45 +178,41 @@ int runhyd(EN_Project *pr, long *t) **-------------------------------------------------------------- */ { - int iter; /* Iteration count */ - int errcode; /* Error code */ - double relerr; /* Solution accuracy */ - - hydraulics_t *hyd = &pr->hydraulics; - time_options_t *time = &pr->time_options; - report_options_t *rep = &pr->report; + Hydraul *hyd = &pr->hydraul; + Times *time = &pr->times; + Report *rpt = &pr->report; - /* Find new demands & control actions */ + int iter; // Iteration count + int errcode; // Error code + double relerr; // Solution accuracy + + // Find new demands & control actions *t = time->Htime; demands(pr); controls(pr); - /* Solve network hydraulic equations */ + // Solve network hydraulic equations errcode = hydsolve(pr,&iter,&relerr); - if (!errcode) { - /* Report new status & save results */ - if (rep->Statflag) { - writehydstat(pr,iter,relerr); - } + if (!errcode) + { + // Report new status & save results + if (rpt->Statflag) writehydstat(pr,iter,relerr); -/*** Updated 3/1/01 ***/ - /* If system unbalanced and no extra trials */ - /* allowed, then activate the Haltflag. */ - if (relerr > hyd->Hacc && hyd->ExtraIter == -1) { + // If system unbalanced and no extra trials + // allowed, then activate the Haltflag + if (relerr > hyd->Hacc && hyd->ExtraIter == -1) + { hyd->Haltflag = 1; } - /* Report any warning conditions */ - if (!errcode) { - errcode = writehydwarn(pr,iter,relerr); - } + // Report any warning conditions + if (!errcode) errcode = writehydwarn(pr,iter,relerr); } - return(errcode); -} /* end of runhyd */ + return errcode; +} - -int nexthyd(EN_Project *pr, long *tstep) +int nexthyd(Project *pr, long *tstep) /* **-------------------------------------------------------------- ** Input: none @@ -243,62 +223,49 @@ int nexthyd(EN_Project *pr, long *tstep) **-------------------------------------------------------------- */ { - long hydstep; /* Actual time step */ - int errcode = 0; /* Error code */ + Hydraul *hyd = &pr->hydraul; + Times *time = &pr->times; + + long hydstep; // Actual time step + int errcode = 0; // Error code - hydraulics_t *hyd = &pr->hydraulics; - time_options_t *top = &pr->time_options; + // Save current results to hydraulics file and + // force end of simulation if Haltflag is active + if (pr->outfile.Saveflag) errcode = savehyd(pr, &time->Htime); + if (hyd->Haltflag) time->Htime = time->Dur; -/*** Updated 3/1/01 ***/ - /* Save current results to hydraulics file and */ - /* force end of simulation if Haltflag is active */ - if (pr->save_options.Saveflag) { - errcode = savehyd(pr,&top->Htime); - } - if (hyd->Haltflag) { - top->Htime = top->Dur; - } + // Compute next time step & update tank levels + *tstep = 0; + hydstep = 0; + if (time->Htime < time->Dur) hydstep = timestep(pr); + if (pr->outfile.Saveflag) errcode = savehydstep(pr,&hydstep); - /* Compute next time step & update tank levels */ - *tstep = 0; - hydstep = 0; - if (top->Htime < top->Dur) { - hydstep = timestep(pr); - } - if (pr->save_options.Saveflag) { - errcode = savehydstep(pr,&hydstep); - } + // Compute pumping energy + if (time->Dur == 0) addenergy(pr,0); + else if (time->Htime < time->Dur) addenergy(pr,hydstep); - /* Compute pumping energy */ - if (top->Dur == 0) { - addenergy(pr,0); - } - else if (top->Htime < top->Dur) { - addenergy(pr,hydstep); - } + // More time remains - update current time + if (time->Htime < time->Dur) + { + time->Htime += hydstep; + if (!pr->quality.OpenQflag) + { + if (time->Htime >= time->Rtime) time->Rtime += time->Rstep; + } + } - /* More time remains - update current time. */ - if (top->Htime < top->Dur) - { - top->Htime += hydstep; - if (!pr->quality.OpenQflag) - { - if (top->Htime >= top->Rtime) top->Rtime += top->Rstep; - } - } - - /* No more time remains - force completion of analysis. */ - else - { - top->Htime++; - if (pr->quality.OpenQflag) pr->quality.Qtime++; - } - *tstep = hydstep; - return(errcode); + // No more time remains - force completion of analysis + else + { + time->Htime++; + if (pr->quality.OpenQflag) time->Qtime++; + } + *tstep = hydstep; + return errcode; } -void closehyd(EN_Project *pr) +void closehyd(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -307,12 +274,12 @@ void closehyd(EN_Project *pr) **-------------------------------------------------------------- */ { - freesparse(pr); /* see SMATRIX.C */ - freematrix(pr); + freesparse(pr); + freematrix(pr); } -int allocmatrix(EN_Project *pr) +int allocmatrix(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -321,34 +288,30 @@ int allocmatrix(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *s = &hyd->solver; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + + int errcode = 0; - int errcode = 0; - s->Aii = (double *) calloc(net->Nnodes+1,sizeof(double)); - s->Aij = (double *) calloc(hyd->Ncoeffs+1,sizeof(double)); - s->F = (double *) calloc(net->Nnodes+1,sizeof(double)); - hyd->DemandFlows = (double *)calloc(net->Nnodes + 1, sizeof(double)); - hyd->EmitterFlows = (double *) calloc(net->Nnodes+1,sizeof(double)); - s->P = (double *) calloc(net->Nlinks+1,sizeof(double)); - s->Y = (double *) calloc(net->Nlinks+1,sizeof(double)); - hyd->X_tmp = (double *) calloc(MAX((net->Nnodes+1),(net->Nlinks+1)),sizeof(double)); - hyd->OldStat = (StatType *) calloc(net->Nlinks+net->Ntanks+1, sizeof(StatType)); - ERRCODE(MEMCHECK(s->Aii)); - ERRCODE(MEMCHECK(s->Aij)); - ERRCODE(MEMCHECK(s->F)); - ERRCODE(MEMCHECK(hyd->DemandFlows)); - ERRCODE(MEMCHECK(hyd->EmitterFlows)); - ERRCODE(MEMCHECK(s->P)); - ERRCODE(MEMCHECK(s->Y)); - ERRCODE(MEMCHECK(hyd->X_tmp)); - ERRCODE(MEMCHECK(hyd->OldStat)); - return(errcode); -} /* end of allocmatrix */ + 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)); + hyd->EmitterFlow = (double *) calloc(net->Nnodes+1, sizeof(double)); + hyd->Xflow = (double *) calloc(MAX((net->Nnodes+1), (net->Nlinks+1)), + sizeof(double)); + hyd->OldStatus = (StatusType *) calloc(net->Nlinks+net->Ntanks+1, + sizeof(StatusType)); + ERRCODE(MEMCHECK(hyd->P)); + ERRCODE(MEMCHECK(hyd->Y)); + ERRCODE(MEMCHECK(hyd->DemandFlow)); + ERRCODE(MEMCHECK(hyd->EmitterFlow)); + ERRCODE(MEMCHECK(hyd->Xflow)); + ERRCODE(MEMCHECK(hyd->OldStatus)); + return errcode; +} -void freematrix(EN_Project *pr) +void freematrix(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -357,22 +320,18 @@ void freematrix(EN_Project *pr) **-------------------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; - solver_t *s = &hyd->solver; + Hydraul *hyd = &pr->hydraul; - free(s->Aii); - free(s->Aij); - free(s->F); - free(hyd->DemandFlows); - free(hyd->EmitterFlows); - free(s->P); - free(s->Y); - free(hyd->X_tmp); - free(hyd->OldStat); -} /* end of freematrix */ + free(hyd->P); + free(hyd->Y); + free(hyd->DemandFlow); + free(hyd->EmitterFlow); + free(hyd->Xflow); + free(hyd->OldStatus); +} -void initlinkflow(EN_Project *pr, int i, char s, double k) +void initlinkflow(Project *pr, int i, char s, double k) /* **-------------------------------------------------------------------- ** Input: i = link index @@ -385,102 +344,101 @@ void initlinkflow(EN_Project *pr, int i, char s, double k) **-------------------------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; - EN_Network *n = &pr->network; - Slink *link = &n->Link[i]; + Hydraul *hyd = &pr->hydraul; + Network *n = &pr->network; + + Slink *link = &n->Link[i]; - if (s == CLOSED) { - hyd->LinkFlows[i] = QZERO; - } - else if (link->Type == PUMP) { - hyd->LinkFlows[i] = k * n->Pump[findpump(n,i)].Q0; - } - else { - hyd->LinkFlows[i] = PI * SQR(link->Diam)/4.0; - } + if (s == CLOSED) + { + hyd->LinkFlow[i] = QZERO; + } + else if (link->Type == PUMP) + { + hyd->LinkFlow[i] = k * n->Pump[findpump(n,i)].Q0; + } + else + { + hyd->LinkFlow[i] = PI * SQR(link->Diam)/4.0; + } } -/*** Updated 9/7/00 ***/ -void setlinkflow(EN_Project *pr, int k, double dh) +void setlinkflow(Project *pr, int k, double dh) /* **-------------------------------------------------------------- ** Input: k = link index -** dh = headloss across link +** dh = head loss across link ** Output: none ** Purpose: sets flow in link based on current headloss **-------------------------------------------------------------- */ { - int i,p; - double h0; - double x,y; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - Slink *link = &net->Link[k]; - Scurve *curve; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + + int i, p; + double h0; + double x ,y; + Slink *link = &net->Link[k]; + Scurve *curve; - switch (link->Type) - { - case CVPIPE: - case PIPE: + switch (link->Type) + { + case CVPIPE: + case PIPE: + // For Darcy-Weisbach formula use approx. inverse of formula + if (hyd->Formflag == DW) + { + x = -log(hyd->LinkSetting[k] / 3.7 / link->Diam); + y = sqrt(ABS(dh) / link->R / 1.32547); + hyd->LinkFlow[k] = x * y; + } - /* For Darcy-Weisbach formula: */ - /* use approx. inverse of formula. */ - if (hyd->Formflag == DW) { - x = -log(hyd->LinkSetting[k]/3.7/link->Diam); - y = sqrt(ABS(dh)/link->R/1.32547); - hyd->LinkFlows[k] = x*y; - } + // For Hazen-Williams or Manning formulas use inverse of formula + else + { + x = ABS(dh) / link->R; + y = 1.0 / hyd->Hexp; + hyd->LinkFlow[k] = pow(x, y); + } - /* For Hazen-Williams or Manning formulas: */ - /* use inverse of formula. */ - else { - x = ABS(dh)/link->R; - y = 1.0/hyd->Hexp; - hyd->LinkFlows[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; - /* Change sign of flow to match sign of headloss */ - if (dh < 0.0) { - hyd->LinkFlows[k] = -hyd->LinkFlows[k]; - } + case PUMP: + // Convert headloss to pump head gain + dh = -dh; + p = findpump(net, k); - break; + // For custom pump curve, interpolate from curve + if (net->Pump[p].Ptype == CUSTOM) + { + dh = -dh * pr->Ucf[HEAD] / SQR(hyd->LinkSetting[k]); + i = net->Pump[p].Hcurve; + curve = &net->Curve[i]; + hyd->LinkFlow[k] = interp(curve->Npts, curve->Y, curve->X, dh) * + hyd->LinkSetting[k] / pr->Ucf[FLOW]; + } - 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) - { - dh = -dh * pr->Ucf[HEAD] / SQR(hyd->LinkSetting[k]); - i = net->Pump[p].Hcurve; - curve = &net->Curve[i]; - hyd->LinkFlows[k] = interp(curve->Npts,curve->Y,curve->X,dh) * - hyd->LinkSetting[k] / pr->Ucf[FLOW]; - } - - /* Otherwise use inverse of power curve */ - else - { - h0 = -SQR(hyd->LinkSetting[k])*net->Pump[p].H0; - x = pow(hyd->LinkSetting[k],2.0 - net->Pump[p].N); - x = ABS(h0-dh)/(net->Pump[p].R*x), - y = 1.0/net->Pump[p].N; - hyd->LinkFlows[k] = pow(x,y); - } - break; - default: - break; - } + // Otherwise use inverse of power curve + else + { + h0 = -SQR(hyd->LinkSetting[k]) * net->Pump[p].H0; + x = pow(hyd->LinkSetting[k], 2.0 - net->Pump[p].N); + x = ABS(h0 - dh) / (net->Pump[p].R * x), + y = 1.0 / net->Pump[p].N; + hyd->LinkFlow[k] = pow(x, y); + } + break; + + default: + break; + } } - -void setlinkstatus(EN_Project *pr, int index, char value, StatType *s, double *k) +void setlinkstatus(Project *pr, int index, char value, StatusType *s, double *k) /*---------------------------------------------------------------- ** Input: index = link index ** value = 0 (CLOSED) or 1 (OPEN) @@ -491,39 +449,37 @@ void setlinkstatus(EN_Project *pr, int index, char value, StatType *s, double * **---------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - Slink *link = &net->Link[index]; - LinkType t = link->Type; - - /* Status set to open */ - if (value == 1) { - /* Adjust link setting for pumps & valves */ - if (t == PUMP) { - *k = 1.0; - } - if (t > PUMP && t != GPV) { - *k = MISSING; - } - /* Reset link flow if it was originally closed */ - *s = OPEN; - } + Network *net = &pr->network; - /* Status set to closed */ - else if (value == 0) { - /* Adjust link setting for pumps & valves */ - if (t == PUMP) { - *k = 0.0; + Slink *link = &net->Link[index]; + LinkType t = link->Type; + + // Status set to open + if (value == 1) + { + // Adjust link setting for pumps & valves + if (t == PUMP) *k = 1.0; + if (t > PUMP && t != GPV) *k = MISSING; + + // Reset link flow if it was originally closed + *s = OPEN; } - if (t > PUMP && t != GPV) { - *k = MISSING; + + // Status set to closed + else if (value == 0) + { + // Adjust link setting for pumps & valves + if (t == PUMP) *k = 0.0; + if (t > PUMP && t != GPV) *k = MISSING; + + // Reset link flow if it was originally open + *s = CLOSED; } - /* Reset link flow if it was originally open */ - *s = CLOSED; - } } -void setlinksetting(EN_Project *pr, int index, double value, StatType *s, double *k) +void setlinksetting(Project *pr, int index, double value, StatusType *s, + double *k) /*---------------------------------------------------------------- ** Input: index = link index ** value = pump speed or valve setting @@ -535,39 +491,36 @@ void setlinksetting(EN_Project *pr, int index, double value, StatType *s, doubl **---------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - Slink *link = &net->Link[index]; - LinkType t = link->Type; + Network *net = &pr->network; + + Slink *link = &net->Link[index]; + LinkType t = link->Type; - /* For a pump, status is OPEN if speed > 0, CLOSED otherwise */ - if (t == PUMP) - { - *k = value; - if (value > 0 && *s <= CLOSED) { - *s = OPEN; - } - if (value == 0 && *s > CLOSED) { - *s = CLOSED; - } - } + // For a pump, status is OPEN if speed > 0, CLOSED otherwise + if (t == PUMP) + { + *k = value; + if (value > 0 && *s <= CLOSED) *s = OPEN; + if (value == 0 && *s > CLOSED) *s = CLOSED; + } - /* For FCV, activate it */ - else if (t == FCV) { - *k = value; - *s = ACTIVE; - } + // For FCV, activate it + else if (t == FCV) + { + *k = value; + *s = ACTIVE; + } - /* Open closed control valve with fixed status (setting = MISSING) */ - else { - if (*k == MISSING && *s <= CLOSED) { - *s = OPEN; - } - *k = value; - } + // Open closed control valve with fixed status (setting = MISSING) + else + { + if (*k == MISSING && *s <= CLOSED) *s = OPEN; + *k = value; + } } -void demands(EN_Project *pr) +void demands(Project *pr) /* **-------------------------------------------------------------------- ** Input: none @@ -576,70 +529,71 @@ void demands(EN_Project *pr) **-------------------------------------------------------------------- */ { - int i,j,n; - long k,p; - double djunc, sum; - Pdemand demand; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Times *time = &pr->times; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - time_options_t *top = &pr->time_options; + int i ,j, n; + long k, p; + double djunc, sum; + Pdemand demand; - /* Determine total elapsed number of pattern periods */ - p = (top->Htime + top->Pstart) / top->Pstep; + // Determine total elapsed number of pattern periods + p = (time->Htime + time->Pstart) / time->Pstep; - /* Update demand at each node according to its assigned pattern */ - hyd->Dsystem = 0.0; /* System-wide demand */ - for (i=1; i <= net->Njuncs; i++) { - sum = 0.0; - for (demand = net->Node[i].D; demand != NULL; demand = demand->next) { - /* - pattern period (k) = (elapsed periods) modulus - (periods per pattern) - */ - j = demand->Pat; - k = p % (long) net->Pattern[j].Length; - djunc = (demand->Base) * net->Pattern[j].F[k] * hyd->Dmult; - if (djunc > 0.0) { - hyd->Dsystem += djunc; - } - sum += djunc; - } - hyd->NodeDemand[i] = sum; - - // Initialize pressure dependent demand - hyd->DemandFlows[i] = sum; - } - - /* Update head at fixed grade nodes with time patterns. */ - for (n=1; n <= net->Ntanks; n++) { - Stank *tank = &net->Tank[n]; - if (tank->A == 0.0) { - j = tank->Pat; - if (j > 0) { + // Update demand at each node according to its assigned pattern + hyd->Dsystem = 0.0; // System-wide demand + for (i = 1; i <= net->Njuncs; i++) + { + sum = 0.0; + for (demand = net->Node[i].D; demand != NULL; demand = demand->next) + { + // pattern period (k) = (elapsed periods) modulus (periods per pattern) + j = demand->Pat; k = p % (long) net->Pattern[j].Length; - i = tank->Node; - hyd->NodeHead[i] = net->Node[i].El * net->Pattern[j].F[k]; - } - } - } + djunc = (demand->Base) * net->Pattern[j].F[k] * hyd->Dmult; + if (djunc > 0.0) hyd->Dsystem += djunc; + sum += djunc; + } + hyd->NodeDemand[i] = sum; - /* Update status of pumps with utilization patterns */ - for (n=1; n <= net->Npumps; n++) - { - Spump *pump = &net->Pump[n]; - j = pump->Upat; - if (j > 0) { - i = pump->Link; - k = p % (long) net->Pattern[j].Length; - setlinksetting(pr, i, net->Pattern[j].F[k], &hyd->LinkStatus[i], &hyd->LinkSetting[i]); - } - } + // Initialize pressure dependent demand + hyd->DemandFlow[i] = sum; + } -} /* End of demands */ + // Update head at fixed grade nodes with time patterns + for (n = 1; n <= net->Ntanks; n++) + { + Stank *tank = &net->Tank[n]; + if (tank->A == 0.0) + { + j = tank->Pat; + if (j > 0) + { + k = p % (long) net->Pattern[j].Length; + i = tank->Node; + hyd->NodeHead[i] = net->Node[i].El * net->Pattern[j].F[k]; + } + } + } + + // Update status of pumps with utilization patterns + for (n = 1; n <= net->Npumps; n++) + { + Spump *pump = &net->Pump[n]; + j = pump->Upat; + if (j > 0) + { + i = pump->Link; + k = p % (long) net->Pattern[j].Length; + setlinksetting(pr, i, net->Pattern[j].F[k], &hyd->LinkStatus[i], + &hyd->LinkSetting[i]); + } + } +} -int controls(EN_Project *pr) +int controls(Project *pr) /* **--------------------------------------------------------------------- ** Input: none @@ -648,86 +602,77 @@ int controls(EN_Project *pr) **--------------------------------------------------------------------- */ { - int i, k, n, reset, setsum; - double h, vplus; - double v1, v2; - double k1, k2; - char s1, s2; - Slink *link; - Scontrol *control; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Times *time = &pr->times; - EN_Network *net = &pr->network; - time_options_t *top = &pr->time_options; - hydraulics_t *hyd = &pr->hydraulics; + int i, k, n, reset, setsum; + double h, vplus; + double v1, v2; + double k1, k2; + char s1, s2; + Slink *link; + Scontrol *control; - /* Examine each control statement */ - setsum = 0; - for (i=1; i <= net->Ncontrols; i++) - { - control = &net->Control[i]; - /* Make sure that link is defined */ - reset = 0; - if ( (k = control->Link) <= 0) { - continue; - } - link = &net->Link[k]; - /* Link is controlled by tank level */ - if ((n = control->Node) > 0 && n > net->Njuncs) - { - h = hyd->NodeHead[n]; - vplus = ABS(hyd->NodeDemand[n]); - v1 = tankvolume(pr,n - net->Njuncs,h); - v2 = tankvolume(pr,n - net->Njuncs, control->Grade); - if (control->Type == LOWLEVEL && v1 <= v2 + vplus) + // Examine each control statement + setsum = 0; + for (i=1; i <= net->Ncontrols; i++) + { + // Make sure that link is defined + control = &net->Control[i]; + reset = 0; + if ( (k = control->Link) <= 0) continue; + link = &net->Link[k]; + + // Link is controlled by tank level + if ((n = control->Node) > 0 && n > net->Njuncs) + { + h = hyd->NodeHead[n]; + vplus = ABS(hyd->NodeDemand[n]); + v1 = tankvolume(pr,n - net->Njuncs,h); + v2 = tankvolume(pr,n - net->Njuncs, control->Grade); + if (control->Type == LOWLEVEL && v1 <= v2 + vplus) reset = 1; + if (control->Type == HILEVEL && v1 >= v2 - vplus) reset = 1; + } + + // Link is time-controlled + if (control->Type == TIMER) + { + if (control->Time == time->Htime) reset = 1; + } + + //* Link is time-of-day controlled + if (control->Type == TIMEOFDAY) + { + if ((time->Htime + time->Tstart) % SECperDAY == control->Time) + { reset = 1; - if (control->Type == HILEVEL && v1 >= v2 - vplus) - reset = 1; - } - - /* Link is time-controlled */ - if (control->Type == TIMER) - { - if (control->Time == top->Htime) reset = 1; - } - - /* Link is time-of-day controlled */ - if (control->Type == TIMEOFDAY) - { - if ((top->Htime + top->Tstart) % SECperDAY == control->Time) { - reset = 1; + } } - } - /* Update link status & pump speed or valve setting */ - if (reset == 1) - { - if (hyd->LinkStatus[k] <= CLOSED) { - s1 = CLOSED; - } - else { - s1 = OPEN; - } - s2 = control->Status; - k1 = hyd->LinkSetting[k]; - k2 = k1; - if (link->Type > PIPE) { - k2 = control->Setting; - } - if (s1 != s2 || k1 != k2) { - hyd->LinkStatus[k] = s2; - hyd->LinkSetting[k] = k2; - if (pr->report.Statflag) { - writecontrolaction(pr,k,i); - } - setsum++; - } - } - } - return(setsum); -} /* End of controls */ + // Update link status & pump speed or valve setting + if (reset == 1) + { + if (hyd->LinkStatus[k] <= CLOSED) s1 = CLOSED; + else s1 = OPEN; + s2 = control->Status; + k1 = hyd->LinkSetting[k]; + k2 = k1; + if (link->Type > PIPE) k2 = control->Setting; + if (s1 != s2 || k1 != k2) + { + hyd->LinkStatus[k] = s2; + hyd->LinkSetting[k] = k2; + if (pr->report.Statflag) writecontrolaction(pr,k,i); + setsum++; + } + } + } + return setsum; +} -long timestep(EN_Project *pr) +long timestep(Project *pr) /* **---------------------------------------------------------------- ** Input: none @@ -736,46 +681,38 @@ long timestep(EN_Project *pr) **---------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - time_options_t *time = &pr->time_options; + Network *net = &pr->network; + 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; - long n,t,tstep; + // Revise time step based on time until next reporting period + t = time->Rtime - time->Htime; + if (t > 0 && t < tstep) tstep = t; - /* Normal time step is hydraulic time step */ - tstep = time->Hstep; + // Revise time step based on smallest time to fill or drain a tank + tanktimestep(pr, &tstep); - /* Revise time step based on time until next demand period */ - n = ((time->Htime + time->Pstart) / time->Pstep) + 1; /* Next pattern period */ - t = n * time->Pstep - time->Htime; /* Time till next period */ - if (t > 0 && t < tstep) { - tstep = t; - } + // Revise time step based on smallest time to activate a control + controltimestep(pr, &tstep); - /* 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); - } - return(tstep); + // Evaluate rule-based controls (which will also update tank levels) + if (net->Nrules > 0) ruletimestep(pr, &tstep); + else tanklevels(pr, tstep); + return tstep; } -int tanktimestep(EN_Project *pr, long *tstep) +int tanktimestep(Project *pr, long *tstep) /* **----------------------------------------------------------------- ** Input: *tstep = current time step @@ -785,46 +722,45 @@ int tanktimestep(EN_Project *pr, long *tstep) **----------------------------------------------------------------- */ { - int i,n, tankIdx = 0; - double h,q,v; - long t; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - - /* (D[n] is net flow rate into (+) or out of (-) tank at node n) */ - for (i=1; i <= net->Ntanks; i++) - { - Stank *tank = &net->Tank[i]; - if (tank->A == 0.0) { - continue; /* Skip reservoirs */ + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + + int i, n, tankIdx = 0; + double h, q, v; + long t; + Stank *tank; + + // Examine each tank + for (i = 1; i <= net->Ntanks; i++) + { + // Skip reservoirs + tank = &net->Tank[i]; + if (tank->A == 0.0) continue; + + // Get current tank grade (h) & inflow (q) + n = tank->Node; + h = hyd->NodeHead[n]; + q = hyd->NodeDemand[n]; + if (ABS(q) <= QZERO) continue; + + // Find volume to fill/drain tank + if (q > 0.0 && h < tank->Hmax) v = tank->Vmax - tank->V; + else if (q < 0.0 && h > tank->Hmin) v = tank->Vmin - tank->V; + else continue; + + // Find time to fill/drain tank + t = (long)ROUND(v / q); + if (t > 0 && t < *tstep) + { + *tstep = t; + tankIdx = n; + } } - n = tank->Node; - h = hyd->NodeHead[n]; /* Current tank grade */ - q = hyd->NodeDemand[n]; /* Flow into tank */ - if (ABS(q) <= QZERO) { - continue; - } - if (q > 0.0 && h < tank->Hmax) { - v = tank->Vmax - tank->V; /* Volume to fill */ - } - else if (q < 0.0 && h > tank->Hmin) { - v = tank->Vmin - tank->V; /* Volume to drain (-) */ - } - else { - continue; - } - t = (long)ROUND(v/q); /* Time to fill/drain */ - if (t > 0 && t < *tstep) { - *tstep = t; - tankIdx = n; - } - } - return tankIdx; + return tankIdx; } -void controltimestep(EN_Project *pr, long *tstep) +void controltimestep(Project *pr, long *tstep) /* **------------------------------------------------------------------ ** Input: *tstep = current time step @@ -834,74 +770,73 @@ void controltimestep(EN_Project *pr, long *tstep) **------------------------------------------------------------------ */ { - int i,j,k,n; - double h,q,v; - long t,t1,t2; - Slink *link; - Scontrol *control; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; + int i, j, k, n; + double h, q, v; + long t, t1, t2; + Slink *link; + Scontrol *control; - for (i=1; i <= net->Ncontrols; i++) - { - t = 0; - control = &net->Control[i]; - if ( (n = control->Node) > 0) /* Node control: */ - { - if ((j = n - net->Njuncs) <= 0) { - continue; /* Node is a tank */ + // Examine each control + for (i = 1; i <= net->Ncontrols; i++) + { + t = 0; + control = &net->Control[i]; + + // 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) ) + { + v = tankvolume(pr, j, control->Grade) - net->Tank[j].V; + t = (long)ROUND(v/q); + } } - h = hyd->NodeHead[n]; /* Current tank grade */ - q = hyd->NodeDemand[n]; /* Flow into tank */ - if (ABS(q) <= QZERO) { - continue; + + // Control is based on elapsed time + if (control->Type == TIMER) + { + if (control->Time > pr->times.Htime) + { + t = control->Time - pr->times.Htime; + } } - if - ( (h < control->Grade && - control->Type == HILEVEL && /* Tank below hi level */ - q > 0.0) /* & is filling */ - || (h > control->Grade && - control->Type == LOWLEVEL && /* Tank above low level */ - q < 0.0) /* & is emptying */ - ) - { /* Time to reach level */ - v = tankvolume(pr, j, control->Grade) - net->Tank[j].V; - t = (long)ROUND(v/q); - } - } - if (control->Type == TIMER) /* Time control: */ - { - if (control->Time > pr->time_options.Htime) - t = control->Time - pr->time_options.Htime; - } + // Control is based on time of day + if (control->Type == TIMEOFDAY) + { + t1 = (pr->times.Htime + pr->times.Tstart) % SECperDAY; + t2 = control->Time; + if (t2 >= t1) t = t2 - t1; + else t = SECperDAY - t1 + t2; + } - if (control->Type == TIMEOFDAY) /* Time-of-day control: */ - { - t1 = (pr->time_options.Htime + pr->time_options.Tstart) % SECperDAY; - t2 = control->Time; - if (t2 >= t1) - t = t2 - t1; - else - t = SECperDAY - t1 + t2; - } - - if (t > 0 && t < *tstep) /* Revise time step */ - { - /* Check if rule actually changes link status or setting */ - k = control->Link; - link = &net->Link[k]; - if ( (link->Type > PIPE && hyd->LinkSetting[k] != control->Setting) - || (hyd->LinkStatus[k] != control->Status) ) { - *tstep = t; - } - } - } -} /* End of timestep */ + // Revise the current estimated next time step + if (t > 0 && t < *tstep) + { + // Check if rule actually changes link status or setting + k = control->Link; + link = &net->Link[k]; + if ( (link->Type > PIPE && hyd->LinkSetting[k] != control->Setting) + || (hyd->LinkStatus[k] != control->Status) ) *tstep = t; + } + } +} -void ruletimestep(EN_Project *pr, long *tstep) +void ruletimestep(Project *pr, long *tstep) /* **-------------------------------------------------------------- ** Input: *tstep = current time step (sec) @@ -911,69 +846,67 @@ void ruletimestep(EN_Project *pr, long *tstep) **-------------------------------------------------------------- */ { - long tnow, /* Start of time interval for rule evaluation */ - tmax, /* End of time interval for rule evaluation */ - dt, /* Normal time increment for rule evaluation */ - dt1; /* Actual time increment for rule evaluation */ - - EN_Network *net = &pr->network; - time_options_t *time = &pr->time_options; + Network *net = &pr->network; + Times *time = &pr->times; - /* Find interval of time for rule evaluation */ - tnow = pr->time_options.Htime; - tmax = tnow + *tstep; + long tnow, // Start of time interval for rule evaluation + tmax, // End of time interval for rule evaluation + dt, // Normal time increment for rule evaluation + dt1; // Actual time increment for rule evaluation - /* If no rules, then time increment equals current time step */ - if (net->Nrules == 0) { - dt = *tstep; - dt1 = dt; - } + // Find interval of time for rule evaluation + tnow = time->Htime; + tmax = tnow + *tstep; - /* Otherwise, time increment equals rule evaluation time step and */ - /* first actual increment equals time until next even multiple of */ - /* Rulestep occurs. */ - else - { - dt = pr->time_options.Rulestep; - dt1 = pr->time_options.Rulestep - (tnow % pr->time_options.Rulestep); - } + // If no rules, then time increment equals current time step + if (net->Nrules == 0) + { + dt = *tstep; + dt1 = dt; + } - /* Make sure time increment is no larger than current time step */ - dt = MIN(dt, *tstep); - dt1 = MIN(dt1, *tstep); - if (dt1 == 0) { - dt1 = dt; - } + // Otherwise, time increment equals rule evaluation time step and + // first actual increment equals time until next even multiple of + // Rulestep occurs. + else + { + dt = time->Rulestep; + dt1 = time->Rulestep - (tnow % time->Rulestep); + } - /* Step through time, updating tank levels, until either */ - /* a rule fires or we reach the end of evaluation period. */ - /* - ** Note: we are updating the global simulation time (Htime) - ** here because it is used by functions in RULES.C - ** to evaluate rules when checkrules() is called. - ** It is restored to its original value after the - ** rule evaluation process is completed (see below). - ** Also note that dt1 will equal dt after the first - ** time increment is taken. - */ - do { - pr->time_options.Htime += dt1; /* Update simulation clock */ - tanklevels(pr,dt1); /* Find new tank levels */ - if (checkrules(pr,dt1)) { - break; /* Stop if rules fire */ - } - dt = MIN(dt, tmax - time->Htime); /* Update time increment */ - dt1 = dt; /* Update actual increment */ - } while (dt > 0); /* Stop if no time left */ + // Make sure time increment is no larger than current time step + dt = MIN(dt, *tstep); + dt1 = MIN(dt1, *tstep); + if (dt1 == 0) dt1 = dt; - /* Compute an updated simulation time step (*tstep) */ - /* and return simulation time to its original value */ - *tstep = pr->time_options.Htime - tnow; - pr->time_options.Htime = tnow; + // Step through time, updating tank levels, until either + // a rule fires or we reach the end of evaluation period. + // + // Note: we are updating the global simulation time (Htime) + // here because it is used by functions in RULES.C + // to evaluate rules when checkrules() is called. + // It is restored to its original value after the + // rule evaluation process is completed (see below). + // Also note that dt1 will equal dt after the first + // time increment is taken. + // + do + { + time->Htime += dt1; // Update simulation clock + tanklevels(pr, dt1); // Find new tank levels + if (checkrules(pr, dt1)) break; // Stop if any rule fires + dt = MIN(dt, tmax - time->Htime); // Update time increment + dt1 = dt; // Update actual increment + } while (dt > 0); // Stop if no time left + + // Compute an updated simulation time step (*tstep) + // and return simulation time to its original value + *tstep = time->Htime - tnow; + time->Htime = tnow; } -void addenergy(EN_Project *pr, long hstep) +void addenergy(Project *pr, long hstep) /* **------------------------------------------------------------- ** Input: hstep = time step (sec) @@ -982,91 +915,78 @@ void addenergy(EN_Project *pr, long hstep) **------------------------------------------------------------- */ { - int i,j,k; - long m,n; - double c0,c, /* Energy cost (cost/kwh) */ - f0, /* Energy cost factor */ - dt, /* Time interval (hr) */ - e, /* Pump efficiency (fraction) */ - q, /* Pump flow (cfs) */ - p, /* Pump energy (kw) */ - psum = 0.0; /* Total energy (kw) */ - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - Spump *pump; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Times *time = &pr->times; - /* Determine current time interval in hours */ - if (pr->time_options.Dur == 0) { - dt = 1.0; - } - else if (pr->time_options.Htime < pr->time_options.Dur) { - dt = (double) hstep / 3600.0; - } - else { - dt = 0.0; - } - - if (dt == 0.0) { - return; - } - n = (pr->time_options.Htime + pr->time_options.Pstart) / pr->time_options.Pstep; + int i, j, k; + long m, n; + double c0, c, // Energy cost (cost/kwh) + f0, // Energy cost factor + dt, // Time interval (hr) + e, // Pump efficiency (fraction) + q, // Pump flow (cfs) + p, // Pump energy (kw) + psum = 0.0; // Total energy (kw) + Spump *pump; - /* Compute default energy cost at current time */ - c0 = hyd->Ecost; - f0 = 1.0; - if (hyd->Epat > 0) - { - m = n % (long)net->Pattern[hyd->Epat].Length; - f0 = net->Pattern[hyd->Epat].F[m]; - } + // Determine current time interval in hours + if (time->Dur == 0) dt = 1.0; + else if (time->Htime < time->Dur) + { + dt = (double) hstep / 3600.0; + } + else dt = 0.0; + if (dt == 0.0) return; + n = (time->Htime + time->Pstart) / time->Pstep; - /* Examine each pump */ - for (j=1; j <= net->Npumps; j++) - { - /* Skip closed pumps */ - pump = &net->Pump[j]; - k = pump->Link; - if (hyd->LinkStatus[k] <= CLOSED) { - continue; - } - q = MAX(QZERO, ABS(hyd->LinkFlows[k])); + // Compute default energy cost at current time + c0 = hyd->Ecost; + f0 = 1.0; + if (hyd->Epat > 0) + { + m = n % (long)net->Pattern[hyd->Epat].Length; + f0 = net->Pattern[hyd->Epat].F[m]; + } - /* Find pump-specific energy cost */ - if (pump->Ecost > 0.0) { - c = pump->Ecost; - } - else { - c = c0; - } + // Examine each pump + for (j = 1; j <= net->Npumps; j++) + { + // Skip closed pumps + pump = &net->Pump[j]; + k = pump->Link; + if (hyd->LinkStatus[k] <= CLOSED) continue; + q = MAX(QZERO, ABS(hyd->LinkFlow[k])); - if ( (i = pump->Epat) > 0) { - m = n % (long)net->Pattern[i].Length; - c *= net->Pattern[i].F[m]; - } - else { - c *= f0; - } + // Find pump-specific energy cost + if (pump->Ecost > 0.0) c = pump->Ecost; + else c = c0; + if ( (i = pump->Epat) > 0) + { + m = n % (long)net->Pattern[i].Length; + c *= net->Pattern[i].F[m]; + } + else c *= f0; - /* Find pump energy & efficiency */ - getenergy(pr,k,&p,&e); - psum += p; + // Find pump energy & efficiency + getenergy(pr, k, &p, &e); + psum += p; - /* Update pump's cumulative statistics */ - pump->Energy[PCNT_ONLINE] += dt; - pump->Energy[PCNT_EFFIC] += e*dt; - pump->Energy[KWH_PER_FLOW] += p/q*dt; - pump->Energy[TOTAL_KWH] += p*dt; - pump->Energy[MAX_KW] = MAX(pump->Energy[MAX_KW],p); - pump->Energy[TOTAL_COST] += c*p*dt; - } + // Update pump's cumulative statistics + pump->Energy.TimeOnLine += dt; + pump->Energy.Efficiency += e * dt; + pump->Energy.KwHrsPerFlow += p / q * dt; + pump->Energy.KwHrs += p * dt; + pump->Energy.MaxKwatts = MAX(pump->Energy.MaxKwatts, p); + pump->Energy.TotalCost += c * p * dt; + } - /* Update maximum kw value */ - hyd->Emax = MAX(hyd->Emax,psum); -} /* End of pumpenergy */ + // Update maximum kw value + hyd->Emax = MAX(hyd->Emax, psum); +} -void getenergy(EN_Project *pr, int k, double *kw, double *eff) +void getenergy(Project *pr, int k, double *kw, double *eff) /* **---------------------------------------------------------------- ** Input: k = link index @@ -1076,60 +996,59 @@ void getenergy(EN_Project *pr, int k, double *kw, double *eff) **---------------------------------------------------------------- */ { - int i, // efficiency curve index - j; // pump index - double dh, // head across pump (ft) - q, // flow through pump (cfs) - e; // pump efficiency - double q4eff; // flow at nominal pump speed of 1.0 - double speed; // current speed setting - Scurve *curve; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - Slink *link = &net->Link[k]; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + + int i, // efficiency curve index + j; // pump index + double dh, // head across pump (ft) + q, // flow through pump (cfs) + e; // pump efficiency + double q4eff; // flow at nominal pump speed of 1.0 + double speed; // current speed setting + Scurve *curve; + Slink *link = &net->Link[k]; -/*** Updated 6/24/02 ***/ - /* No energy if link is closed */ - if (hyd->LinkStatus[k] <= CLOSED) - { - *kw = 0.0; - *eff = 0.0; - return; - } -/*** End of update ***/ + // No energy if link is closed + if (hyd->LinkStatus[k] <= CLOSED) + { + *kw = 0.0; + *eff = 0.0; + return; + } - /* Determine flow and head difference */ - q = ABS(hyd->LinkFlows[k]); - dh = ABS(hyd->NodeHead[link->N1] - hyd->NodeHead[link->N2]); + // Determine flow and head difference + q = ABS(hyd->LinkFlow[k]); + dh = ABS(hyd->NodeHead[link->N1] - hyd->NodeHead[link->N2]); - /* For pumps, find effic. at current flow */ - if (link->Type == PUMP) - { - j = findpump(net,k); - e = hyd->Epump; - speed = hyd->LinkSetting[k]; - if ( (i = net->Pump[j].Ecurve) > 0) - { - q4eff = q / speed * pr->Ucf[FLOW]; - curve = &net->Curve[i]; - e = interp(curve->Npts,curve->X, curve->Y, q4eff); - /* Sarbu and Borza pump speed adjustment */ - e = 100.0 - ((100.0-e) * pow(1.0/speed, 0.1)); - } - e = MIN(e, 100.0); - e = MAX(e, 1.0); - e /= 100.0; - } - else e = 1.0; + // For pumps, find effic. at current flow + if (link->Type == PUMP) + { + j = findpump(net, k); + e = hyd->Epump; + speed = hyd->LinkSetting[k]; + if ((i = net->Pump[j].Ecurve) > 0) + { + q4eff = q / speed * pr->Ucf[FLOW]; + curve = &net->Curve[i]; + e = interp(curve->Npts,curve->X, curve->Y, q4eff); - /* Compute energy */ - *kw = dh * q * hyd->SpGrav / 8.814 / e * KWperHP; - *eff = e; + // Sarbu and Borza pump speed adjustment + e = 100.0 - ((100.0-e) * pow(1.0/speed, 0.1)); + } + e = MIN(e, 100.0); + e = MAX(e, 1.0); + e /= 100.0; + } + else e = 1.0; + + // Compute energy + *kw = dh * q * hyd->SpGrav / 8.814 / e * KWperHP; + *eff = e; } -void tanklevels(EN_Project *pr, long tstep) +void tanklevels(Project *pr, long tstep) /* **---------------------------------------------------------------- ** Input: tstep = current time step @@ -1139,38 +1058,37 @@ void tanklevels(EN_Project *pr, long tstep) **---------------------------------------------------------------- */ { - int i,n; - double dv; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + + int i, n; + double dv; - for (i=1; i<=net->Ntanks; i++) - { - Stank *tank = &net->Tank[i]; - /* Skip reservoirs */ - if (tank->A == 0.0) { - continue; - } + 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 */ - n = tank->Node; - dv = hyd->NodeDemand[n] * tstep; - tank->V += dv; + // Update the tank's volume & water elevation + n = tank->Node; + dv = hyd->NodeDemand[n] * tstep; + tank->V += dv; - /*** Updated 6/24/02 ***/ - /* Check if tank full/empty within next second */ - if (tank->V + hyd->NodeDemand[n] >= tank->Vmax) { - tank->V = tank->Vmax; + // Check if tank full/empty within next second + if (tank->V + hyd->NodeDemand[n] >= tank->Vmax) + { + tank->V = tank->Vmax; + } + else if (tank->V - hyd->NodeDemand[n] <= tank->Vmin) + { + tank->V = tank->Vmin; + } + hyd->NodeHead[n] = tankgrade(pr, i, tank->V); } - else if (tank->V - hyd->NodeDemand[n] <= tank->Vmin) { - tank->V = tank->Vmin; - } - hyd->NodeHead[n] = tankgrade(pr, i, tank->V); - } -} /* End of tanklevels */ +} -double tankvolume(EN_Project *pr, int i, double h) +double tankvolume(Project *pr, int i, double h) /* **-------------------------------------------------------------------- ** Input: i = tank index @@ -1180,30 +1098,30 @@ double tankvolume(EN_Project *pr, int i, double h) **-------------------------------------------------------------------- */ { - int j; - EN_Network *net = &pr->network; - Stank *tank = &net->Tank[i]; - Scurve *curve; + Network *net = &pr->network; - /* Use level*area if no volume curve */ - j = tank->Vcurve; - if (j == 0) { - return(tank->Vmin + (h - tank->Hmin) * tank->A); - } + int j; + double y, v; + Stank *tank = &net->Tank[i]; + Scurve *curve; + + // 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 { - curve = &net->Curve[j]; - return(interp(curve->Npts, curve->X, curve->Y, - (h - net->Node[tank->Node].El) * - pr->Ucf[HEAD]) / pr->Ucf[VOLUME]); - } - -} /* End of tankvolume */ + // If curve exists, interpolate on h to find volume v + // remembering that volume curve is in original units. + else + { + curve = &net->Curve[j]; + y = (h - net->Node[tank->Node].El) * pr->Ucf[HEAD]; + v = interp(curve->Npts, curve->X, curve->Y, y) / pr->Ucf[VOLUME]; + return v; + } +} -double tankgrade(EN_Project *pr, int i, double v) +double tankgrade(Project *pr, int i, double v) /* **------------------------------------------------------------------- ** Input: i = tank index @@ -1213,24 +1131,24 @@ double tankgrade(EN_Project *pr, int i, double v) **------------------------------------------------------------------- */ { - int j; - EN_Network *net = &pr->network; - Stank *tank = &net->Tank[i]; - /* 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. */ - else { - Scurve *curve = &net->Curve[j]; - return(net->Node[tank->Node].El + interp(curve->Npts, curve->Y, curve->X, v * pr->Ucf[VOLUME]) / pr->Ucf[HEAD]); - } - -} /* End of tankgrade */ + Network *net = &pr->network; + int j; + double y, h; + Stank *tank = &net->Tank[i]; -/**************** END OF HYDRAUL.C ***************/ + // 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. + else + { + Scurve *curve = &net->Curve[j]; + y = interp(curve->Npts, curve->Y, curve->X, v * pr->Ucf[VOLUME]); + h = net->Node[tank->Node].El + y / pr->Ucf[HEAD]; + return h; + } +} diff --git a/src/hydsolver.c b/src/hydsolver.c index f6c872d..021d776 100644 --- a/src/hydsolver.c +++ b/src/hydsolver.c @@ -1,13 +1,15 @@ /* -********************************************************************* - -HYDSOLVER.C -- Equilibrium hydraulic solver for the EPANET Program - -This module contains a pipe network hydraulic solver that computes -flows and pressures within the network at a specific point in time. - -The solver implements Todini's Global Gradient Algorithm. -******************************************************************* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: hydsolver.c + Description: computes flows and pressures throughout a pipe network using + Todini's Global Gradient Algorithm + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ #include @@ -33,32 +35,29 @@ typedef struct { int maxflowlink; } Hydbalance; -// External functions -//int hydsolve(EN_Project *pr, int *iter, double *relerr); -//void headlosscoeffs(EN_Project *pr); -//void matrixcoeffs(EN_Project *pr); +// Exported functions +int hydsolve(Project *, int *, double *); -extern int valvestatus(EN_Project *pr); //(see HYDSTATUS.C) -extern int linkstatus(EN_Project *pr); //(see HYDSTATUS.C) +// Imported functions +extern int linsolve(Smatrix *, int); //(see SMATRIX.C) +extern int valvestatus(Project *); //(see HYDSTATUS.C) +extern int linkstatus(Project *); //(see HYDSTATUS.C) // Local functions -static int badvalve(EN_Project *pr, int); -static int pswitch(EN_Project *pr); +static int badvalve(Project *, int); +static int pswitch(Project *); -static double newflows(EN_Project *pr, Hydbalance *hbal); -static void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, - double *dqsum); -static void newemitterflows(EN_Project *pr, Hydbalance *hbal, double *qsum, - double *dqsum); -static void newdemandflows(EN_Project *pr, Hydbalance *hbal, double *qsum, - double *dqsum); +static double newflows(Project *, Hydbalance *); +static void newlinkflows(Project *, Hydbalance *, double *, double *); +static void newemitterflows(Project *, Hydbalance *, double *, double *); +static void newdemandflows(Project *, Hydbalance *, double *, double *); -static void checkhydbalance(EN_Project *pr, Hydbalance *hbal); -static int hasconverged(EN_Project *pr, double *relerr, Hydbalance *hbal); -static void reporthydbal(EN_Project *pr, Hydbalance *hbal); +static void checkhydbalance(Project *, Hydbalance *); +static int hasconverged(Project *, double *, Hydbalance *); +static void reporthydbal(Project *, Hydbalance *); -int hydsolve(EN_Project *pr, int *iter, double *relerr) +int hydsolve(Project *pr, int *iter, double *relerr) /* **------------------------------------------------------------------- ** Input: none @@ -68,11 +67,8 @@ int hydsolve(EN_Project *pr, int *iter, double *relerr) ** Purpose: solves network nodal equations for heads and flows ** using Todini's Gradient algorithm ** -*** Updated 9/7/00 *** -*** Updated 2.00.11 *** -*** Updated 2.00.12 *** ** Notes: Status checks on CVs, pumps and pipes to tanks are made -** every hyd->CheckFreq iteration, up until MaxCheck iterations +** every CheckFreq iteration, up until MaxCheck iterations ** are reached. Status checks on control valves are made ** every iteration if DampLimit = 0 or only when the ** convergence error is at or below DampLimit. If DampLimit @@ -87,6 +83,11 @@ int hydsolve(EN_Project *pr, int *iter, double *relerr) **------------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Smatrix *sm = &hyd->smatrix; + Report *rpt = &pr->report; + int i; // Node index int errcode = 0; // Node causing solution error int nextcheck; // Next status check trial @@ -96,47 +97,32 @@ int hydsolve(EN_Project *pr, int *iter, double *relerr) int statChange; // Non-valve status change flag Hydbalance hydbal; // Hydraulic balance errors - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - report_options_t *rep = &pr->report; - // Initialize status checking & relaxation factor nextcheck = hyd->CheckFreq; hyd->RelaxFactor = 1.0; // Repeat iterations until convergence or trial limit is exceeded. // (ExtraIter used to increase trials in case of status cycling.) - if (pr->report.Statflag == FULL) - { - writerelerr(pr, 0, 0); - } + if (rpt->Statflag == FULL) writerelerr(pr, 0, 0); maxtrials = hyd->MaxIter; - if (hyd->ExtraIter > 0) - { - maxtrials += hyd->ExtraIter; - } + if (hyd->ExtraIter > 0) maxtrials += hyd->ExtraIter; *iter = 1; while (*iter <= maxtrials) { - /* - ** Compute coefficient matrices A & F and solve A*H = F - ** where H = heads, A = Jacobian coeffs. derived from - ** head loss gradients, & F = flow correction terms. - ** Solution for H is returned in F from call to linsolve(). - */ + // Compute coefficient matrices A & F and solve A*H = F + // where H = heads, A = Jacobian coeffs. derived from + // head loss gradients, & F = flow correction terms. + // Solution for H is returned in F from call to linsolve(). + headlosscoeffs(pr); matrixcoeffs(pr); - errcode = linsolve(pr, net->Njuncs); - - // Quit if memory allocation error - if (errcode < 0) break; + errcode = linsolve(sm, net->Njuncs); // Matrix ill-conditioning problem - if control valve causing problem, // fix its status & continue, otherwise quit with no solution. if (errcode > 0) { - if (badvalve(pr, sol->Order[errcode])) continue; + if (badvalve(pr, sm->Order[errcode])) continue; else break; } @@ -144,13 +130,13 @@ int hydsolve(EN_Project *pr, int *iter, double *relerr) // (Row[i] = row of solution matrix corresponding to node i) for (i = 1; i <= net->Njuncs; i++) { - hyd->NodeHead[i] = sol->F[sol->Row[i]]; // Update heads + hyd->NodeHead[i] = sm->F[sm->Row[i]]; // Update heads } - newerr = newflows(pr, &hydbal); // Update flows + newerr = newflows(pr, &hydbal); // Update flows *relerr = newerr; // Write convergence error to status report if called for - if (rep->Statflag == FULL) + if (rpt->Statflag == FULL) { writerelerr(pr, *iter, *relerr); } @@ -199,17 +185,16 @@ int hydsolve(EN_Project *pr, int *iter, double *relerr) } // Iterations ended - report any errors. - if (errcode < 0) errcode = 101; // Memory allocation error - else if (errcode > 0) + if (errcode > 0) { - writehyderr(pr, sol->Order[errcode]); // Ill-conditioned matrix error + writehyderr(pr, sm->Order[errcode]); // Ill-conditioned matrix error errcode = 110; } // Replace junction demands with total outflow values for (i = 1; i <= net->Njuncs; i++) { - hyd->NodeDemand[i] = hyd->DemandFlows[i] + hyd->EmitterFlows[i]; + hyd->NodeDemand[i] = hyd->DemandFlow[i] + hyd->EmitterFlow[i]; } // Save convergence info @@ -218,11 +203,11 @@ int hydsolve(EN_Project *pr, int *iter, double *relerr) hyd->MaxFlowChange = hydbal.maxflowchange; hyd->Iterations = *iter; - return(errcode); + return errcode; } -int badvalve(EN_Project *pr, int n) +int badvalve(Project *pr, int n) /* **----------------------------------------------------------------- ** Input: n = node index @@ -235,12 +220,12 @@ int badvalve(EN_Project *pr, int n) **----------------------------------------------------------------- */ { - int i, k, n1, n2; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; + Times *time = &pr->times; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; + int i, k, n1, n2; Slink *link; LinkType t; @@ -257,19 +242,14 @@ int badvalve(EN_Project *pr, int n) { if (hyd->LinkStatus[k] == ACTIVE) { - if (rep->Statflag == FULL) + if (rpt->Statflag == FULL) { - sprintf(pr->Msg, FMT61, clocktime(rep->Atime, time->Htime), link->ID); + sprintf(pr->Msg, FMT61, + clocktime(rpt->Atime, time->Htime), link->ID); writeline(pr, pr->Msg); } - if (link->Type == FCV) - { - hyd->LinkStatus[k] = XFCV; - } - else - { - hyd->LinkStatus[k] = XPRESSURE; - } + if (link->Type == FCV) hyd->LinkStatus[k] = XFCV; + else hyd->LinkStatus[k] = XPRESSURE; return 1; } } @@ -280,7 +260,7 @@ int badvalve(EN_Project *pr, int n) } -int pswitch(EN_Project *pr) +int pswitch(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -290,6 +270,10 @@ int pswitch(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; + int i, // Control statement index k, // Index of link being controlled n, // Node controlling link k @@ -297,10 +281,6 @@ int pswitch(EN_Project *pr) change, // Flag for status or setting change anychange = 0; // Flag for 1 or more control actions char s; // Current link status - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; Slink *link; // Check each control statement @@ -358,7 +338,7 @@ int pswitch(EN_Project *pr) { hyd->LinkSetting[k] = net->Control[i].Setting; } - if (rep->Statflag == FULL) + if (rpt->Statflag == FULL) { writestatchange(pr, k, s, hyd->LinkStatus[k]); } @@ -366,11 +346,11 @@ int pswitch(EN_Project *pr) } } } - return(anychange); + return anychange; } -double newflows(EN_Project *pr, Hydbalance *hbal) +double newflows(Project *pr, Hydbalance *hbal) /* **---------------------------------------------------------------- ** Input: hbal = ptr. to hydraulic balance information @@ -380,12 +360,12 @@ double newflows(EN_Project *pr, Hydbalance *hbal) **---------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + double dqsum, // Network flow change qsum; // Network total flow - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - // Initialize sum of flows & corrections qsum = 0.0; dqsum = 0.0; @@ -404,7 +384,7 @@ double newflows(EN_Project *pr, Hydbalance *hbal) } -void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum) +void newlinkflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum) /* **---------------------------------------------------------------- ** Input: hbal = ptr. to hydraulic balance information @@ -415,14 +395,13 @@ void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum **---------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + double dh, /* Link head loss */ dq; /* Link flow change */ int k, n, n1, n2; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; - Slink *link; + Slink *link; // Initialize net inflows (i.e., demands) at fixed grade nodes for (n = net->Njuncs + 1; n <= net->Nnodes; n++) @@ -445,7 +424,7 @@ void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum // where P & Y were computed in hlosscoeff() in hydcoeffs.c dh = hyd->NodeHead[n1] - hyd->NodeHead[n2]; - dq = sol->Y[k] - sol->P[k] * dh; + dq = hyd->Y[k] - hyd->P[k] * dh; // Adjust flow change by the relaxation factor dq *= hyd->RelaxFactor; @@ -454,15 +433,15 @@ void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum if (link->Type == PUMP) { n = findpump(net, k); - if (net->Pump[n].Ptype == CONST_HP && dq > hyd->LinkFlows[k]) + if (net->Pump[n].Ptype == CONST_HP && dq > hyd->LinkFlow[k]) { - dq = hyd->LinkFlows[k] / 2.0; + dq = hyd->LinkFlow[k] / 2.0; } } // Update link flow and system flow summation - hyd->LinkFlows[k] -= dq; - *qsum += ABS(hyd->LinkFlows[k]); + hyd->LinkFlow[k] -= dq; + *qsum += ABS(hyd->LinkFlow[k]); *dqsum += ABS(dq); // Update identity of element with max. flow change @@ -476,14 +455,14 @@ void newlinkflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum // Update net flows to fixed grade nodes if (hyd->LinkStatus[k] > CLOSED) { - if (n1 > net->Njuncs) hyd->NodeDemand[n1] -= hyd->LinkFlows[k]; - if (n2 > net->Njuncs) hyd->NodeDemand[n2] += hyd->LinkFlows[k]; + if (n1 > net->Njuncs) hyd->NodeDemand[n1] -= hyd->LinkFlow[k]; + if (n2 > net->Njuncs) hyd->NodeDemand[n2] += hyd->LinkFlow[k]; } } } -void newemitterflows(EN_Project *pr, Hydbalance *hbal, double *qsum, +void newemitterflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum) /* **---------------------------------------------------------------- @@ -495,10 +474,11 @@ void newemitterflows(EN_Project *pr, Hydbalance *hbal, double *qsum, **---------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + int i; double hloss, hgrad, dh, dq; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; // Examine each network junction for (i = 1; i <= net->Njuncs; i++) @@ -512,10 +492,10 @@ void newemitterflows(EN_Project *pr, Hydbalance *hbal, double *qsum, // Find emitter flow change dh = hyd->NodeHead[i] - net->Node[i].El; dq = (hloss - dh) / hgrad; - hyd->EmitterFlows[i] -= dq; + hyd->EmitterFlow[i] -= dq; // Update system flow summation - *qsum += ABS(hyd->EmitterFlows[i]); + *qsum += ABS(hyd->EmitterFlow[i]); *dqsum += ABS(dq); // Update identity of element with max. flow change @@ -529,7 +509,7 @@ void newemitterflows(EN_Project *pr, Hydbalance *hbal, double *qsum, } -void newdemandflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsum) +void newdemandflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum) /* **---------------------------------------------------------------- ** Input: hbal = ptr. to hydraulic balance information @@ -541,12 +521,13 @@ void newdemandflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsu **---------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + double dp, // pressure range over which demand can vary (ft) dq, // change in demand flow (cfs) n; // exponent in head loss v. demand function int k; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; // Get demand function parameters if (hyd->DemandModel == DDA) return; @@ -560,10 +541,10 @@ void newdemandflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsu // Find change in demand flow (see hydcoeffs.c) dq = demandflowchange(pr, k, dp, n); - hyd->DemandFlows[k] -= dq; + hyd->DemandFlow[k] -= dq; // Update system flow summation - *qsum += ABS(hyd->DemandFlows[k]); + *qsum += ABS(hyd->DemandFlow[k]); *dqsum += ABS(dq); // Update identity of element with max. flow change @@ -577,7 +558,7 @@ void newdemandflows(EN_Project *pr, Hydbalance *hbal, double *qsum, double *dqsu } -void checkhydbalance(EN_Project *pr, Hydbalance *hbal) +void checkhydbalance(Project *pr, Hydbalance *hbal) /* **-------------------------------------------------------------- ** Input: hbal = hydraulic balance errors @@ -586,25 +567,25 @@ void checkhydbalance(EN_Project *pr, Hydbalance *hbal) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + int k, n1, n2; double dh, headerror, headloss; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *sol = &hyd->solver; Slink *link; + hbal->maxheaderror = 0.0; hbal->maxheadlink = 1; headlosscoeffs(pr); for (k = 1; k <= net->Nlinks; k++) { if (hyd->LinkStatus[k] <= CLOSED) continue; - if (sol->P[k] == 0.0) continue; + if (hyd->P[k] == 0.0) continue; link = &net->Link[k]; n1 = link->N1; n2 = link->N2; dh = hyd->NodeHead[n1] - hyd->NodeHead[n2]; - headloss = sol->Y[k] / sol->P[k]; + headloss = hyd->Y[k] / hyd->P[k]; headerror = ABS(dh - headloss); if (headerror > hbal->maxheaderror) { @@ -615,7 +596,7 @@ void checkhydbalance(EN_Project *pr, Hydbalance *hbal) } -int hasconverged(EN_Project *pr, double *relerr, Hydbalance *hbal) +int hasconverged(Project *pr, double *relerr, Hydbalance *hbal) /* **-------------------------------------------------------------- ** Input: relerr = current total relative flow change @@ -626,7 +607,7 @@ int hasconverged(EN_Project *pr, double *relerr, Hydbalance *hbal) **-------------------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; if (*relerr > hyd->Hacc) return 0; checkhydbalance(pr, hbal); @@ -642,7 +623,7 @@ int hasconverged(EN_Project *pr, double *relerr, Hydbalance *hbal) } -void reporthydbal(EN_Project *pr, Hydbalance *hbal) +void reporthydbal(Project *pr, Hydbalance *hbal) /* **-------------------------------------------------------------- ** Input: hbal = current hydraulic balance errors diff --git a/src/hydstatus.c b/src/hydstatus.c index 8a742f9..f7edb6b 100644 --- a/src/hydstatus.c +++ b/src/hydstatus.c @@ -1,29 +1,34 @@ /* -********************************************************************* - -HYDSTATUS.C -- Hydraulic status updating for the EPANET Program - -******************************************************************* +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: hydstatus.c +Description: updates hydraulic status of network elements +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/27/2018 +****************************************************************************** */ #include #include "types.h" #include "funcs.h" -// External functions -int valvestatus(EN_Project *pr); -int linkstatus(EN_Project *pr); +// Exported functions +int valvestatus(Project *); +int linkstatus(Project *); // Local functions -static StatType cvstatus(EN_Project *pr, StatType, double, double); -static StatType pumpstatus(EN_Project *pr, int, double); -static StatType prvstatus(EN_Project *pr, int, StatType, double, double, double); -static StatType psvstatus(EN_Project *pr, int, StatType, double, double, double); -static StatType fcvstatus(EN_Project *pr, int, StatType, double, double); -static void tankstatus(EN_Project *pr, int, int, int); +static StatusType cvstatus(Project *, StatusType, double, double); +static StatusType pumpstatus(Project *, int, double); +static StatusType prvstatus(Project *, int, StatusType, double, double, double); +static StatusType psvstatus(Project *, int, StatusType, double, double, double); +static StatusType fcvstatus(Project *, int, StatusType, double, double); +static void tankstatus(Project *, int, int, int); -int valvestatus(EN_Project *pr) +int valvestatus(Project *pr) /* **----------------------------------------------------------------- ** Input: none @@ -34,16 +39,16 @@ int valvestatus(EN_Project *pr) **----------------------------------------------------------------- */ { - int change = FALSE, // Status change flag - i, k, // Valve & link indexes - n1, n2; // Start & end nodes - StatType status; // Valve status settings - double hset; // Valve head setting - Slink *link; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; + int change = FALSE, // Status change flag + i, k, // Valve & link indexes + n1, n2; // Start & end nodes + double hset; // Valve head setting + StatusType status; // Valve status settings + Slink *link; // Examine each valve for (i = 1; i <= net->Nvalves; i++) @@ -80,7 +85,7 @@ int valvestatus(EN_Project *pr) // Check for a status change if (status != hyd->LinkStatus[k]) { - if (rep->Statflag == FULL) + if (rpt->Statflag == FULL) { writestatchange(pr, k, status, hyd->LinkStatus[k]); } @@ -88,10 +93,10 @@ int valvestatus(EN_Project *pr) } } return change; -} /* End of valvestatus() */ +} -int linkstatus(EN_Project *pr) +int linkstatus(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -101,16 +106,16 @@ int linkstatus(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; + int change = FALSE, // Status change flag k, // Link index n1, // Start node index n2; // End node index double dh; // Head difference across link - StatType status; // Current status - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; + StatusType status; // Current status Slink *link; // Examine each link @@ -132,7 +137,7 @@ int linkstatus(EN_Project *pr) if (link->Type == CVPIPE) { hyd->LinkStatus[k] = cvstatus(pr, hyd->LinkStatus[k], dh, - hyd->LinkFlows[k]); + hyd->LinkFlow[k]); } if (link->Type == PUMP && hyd->LinkStatus[k] >= OPEN && hyd->LinkSetting[k] > 0.0) @@ -157,7 +162,7 @@ int linkstatus(EN_Project *pr) if (status != hyd->LinkStatus[k]) { change = TRUE; - if (rep->Statflag == FULL) + if (rpt->Statflag == FULL) { writestatchange(pr, k, status, hyd->LinkStatus[k]); } @@ -167,7 +172,7 @@ int linkstatus(EN_Project *pr) } -StatType cvstatus(EN_Project *pr, StatType s, double dh, double q) +StatusType cvstatus(Project *pr, StatusType s, double dh, double q) /* **-------------------------------------------------- ** Input: s = current link status @@ -178,7 +183,7 @@ StatType cvstatus(EN_Project *pr, StatType s, double dh, double q) **-------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; // Prevent reverse flow through CVs if (ABS(dh) > hyd->Htol) @@ -195,7 +200,7 @@ StatType cvstatus(EN_Project *pr, StatType s, double dh, double q) } -StatType pumpstatus(EN_Project *pr, int k, double dh) +StatusType pumpstatus(Project *pr, int k, double dh) /* **-------------------------------------------------- ** Input: k = link index @@ -205,10 +210,11 @@ StatType pumpstatus(EN_Project *pr, int k, double dh) **-------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + Network *net = &pr->network; + int p; double hmax; - hydraulics_t *hyd = &pr->hydraulics; - EN_Network *net = &pr->network; // Find maximum head (hmax) pump can deliver p = findpump(net, k); @@ -231,7 +237,7 @@ StatType pumpstatus(EN_Project *pr, int k, double dh) } -StatType prvstatus(EN_Project *pr, int k, StatType s, double hset, +StatusType prvstatus(Project *pr, int k, StatusType s, double hset, double h1, double h2) /* **----------------------------------------------------------- @@ -245,28 +251,31 @@ StatType prvstatus(EN_Project *pr, int k, StatType s, double hset, **----------------------------------------------------------- */ { - StatType status; // Valve's new status - double hml; // Head loss when fully opened - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; - double htol = hyd->Htol; - Slink *link = &pr->network.Link[k]; + StatusType status; // Valve's new status + double hml; // Head loss when fully opened + double htol; + Slink *link; + + htol = hyd->Htol; + link = &pr->network.Link[k]; // Head loss when fully open - hml = link->Km * SQR(hyd->LinkFlows[k]); + hml = link->Km * SQR(hyd->LinkFlow[k]); // Rules for updating valve's status from current value s status = s; switch (s) { case ACTIVE: - if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + if (hyd->LinkFlow[k] < -hyd->Qtol) status = CLOSED; else if (h1 - hml < hset - htol) status = OPEN; else status = ACTIVE; break; case OPEN: - if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + if (hyd->LinkFlow[k] < -hyd->Qtol) status = CLOSED; else if (h2 >= hset + htol) status = ACTIVE; else status = OPEN; break; @@ -278,7 +287,7 @@ StatType prvstatus(EN_Project *pr, int k, StatType s, double hset, break; case XPRESSURE: - if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + if (hyd->LinkFlow[k] < -hyd->Qtol) status = CLOSED; break; default: @@ -288,7 +297,7 @@ StatType prvstatus(EN_Project *pr, int k, StatType s, double hset, } -StatType psvstatus(EN_Project *pr, int k, StatType s, double hset, +StatusType psvstatus(Project *pr, int k, StatusType s, double hset, double h1, double h2) /* **----------------------------------------------------------- @@ -302,28 +311,31 @@ StatType psvstatus(EN_Project *pr, int k, StatType s, double hset, **----------------------------------------------------------- */ { - StatType status; // Valve's new status - double hml; // Head loss when fully opened - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; - double htol = hyd->Htol; - Slink *link = &pr->network.Link[k]; + StatusType status; // Valve's new status + double hml; // Head loss when fully opened + double htol; + Slink *link; + + htol = hyd->Htol; + link = &pr->network.Link[k]; // Head loss when fully open - hml = link->Km * SQR(hyd->LinkFlows[k]); + hml = link->Km * SQR(hyd->LinkFlow[k]); // Rules for updating valve's status from current value s status = s; switch (s) { case ACTIVE: - if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + if (hyd->LinkFlow[k] < -hyd->Qtol) status = CLOSED; else if (h2 + hml > hset + htol) status = OPEN; else status = ACTIVE; break; case OPEN: - if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + if (hyd->LinkFlow[k] < -hyd->Qtol) status = CLOSED; else if (h1 < hset - htol) status = ACTIVE; else status = OPEN; break; @@ -335,7 +347,7 @@ StatType psvstatus(EN_Project *pr, int k, StatType s, double hset, break; case XPRESSURE: - if (hyd->LinkFlows[k] < -hyd->Qtol) status = CLOSED; + if (hyd->LinkFlow[k] < -hyd->Qtol) status = CLOSED; break; default: @@ -345,7 +357,7 @@ StatType psvstatus(EN_Project *pr, int k, StatType s, double hset, } -StatType fcvstatus(EN_Project *pr, int k, StatType s, double h1, double h2) +StatusType fcvstatus(Project *pr, int k, StatusType s, double h1, double h2) /* **----------------------------------------------------------- ** Input: k = link index @@ -364,19 +376,19 @@ StatType fcvstatus(EN_Project *pr, int k, StatType s, double h1, double h2) **----------------------------------------------------------- */ { - StatType status; // New valve status - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; + StatusType status; // New valve status status = s; if (h1 - h2 < -hyd->Htol) { status = XFCV; } - else if (hyd->LinkFlows[k] < -hyd->Qtol) + else if (hyd->LinkFlow[k] < -hyd->Qtol) { status = XFCV; } - else if (s == XFCV && hyd->LinkFlows[k] >= hyd->LinkSetting[k]) + else if (s == XFCV && hyd->LinkFlow[k] >= hyd->LinkSetting[k]) { status = ACTIVE; } @@ -384,7 +396,7 @@ StatType fcvstatus(EN_Project *pr, int k, StatType s, double h1, double h2) } -void tankstatus(EN_Project *pr, int k, int n1, int n2) +void tankstatus(Project *pr, int k, int n1, int n2) /* **---------------------------------------------------------------- ** Input: k = link index @@ -395,19 +407,19 @@ void tankstatus(EN_Project *pr, int k, int n1, int n2) **---------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + int i, n; double h, q; Stank *tank; - - hydraulics_t *hyd = &pr->hydraulics; - EN_Network *net = &pr->network; Slink *link = &net->Link[k]; // Return if link is closed if (hyd->LinkStatus[k] <= CLOSED) return; // Make node n1 be the tank, reversing flow (q) if need be - q = hyd->LinkFlows[k]; + q = hyd->LinkFlow[k]; i = n1 - net->Njuncs; if (i <= 0) { diff --git a/src/inpfile.c b/src/inpfile.c index cbce693..d13c012 100644 --- a/src/inpfile.c +++ b/src/inpfile.c @@ -1,22 +1,14 @@ /* -********************************************************************* - -INPFILE.C -- Save Input Function for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 3/1/01 - 11/19/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -This module contains the function saveinpfile() which saves the -data describing a piping network to a file in EPANET's text format. - -******************************************************************** +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: inpfile.c +Description: saves network data to an EPANET formatted text file +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/27/2018 +****************************************************************************** */ #include @@ -33,7 +25,7 @@ data describing a piping network to a file in EPANET's text format. #include "hash.h" #include "text.h" -/* Defined in enumstxt.h in EPANET.C */ +// Defined in enumstxt.h in EPANET.C extern char *LinkTxt[]; extern char *FormTxt[]; extern char *StatTxt[]; @@ -46,675 +38,734 @@ extern char *TstatTxt[]; extern char *RptFlagTxt[]; extern char *SectTxt[]; -void saveauxdata(parser_data_t *parser, FILE *f) +void saveauxdata(Parser *parser, FILE *f) /* ------------------------------------------------------------ Writes auxilary data from original input file to new file. ------------------------------------------------------------ */ { - int sect, newsect; - char *tok; - char line[MAXLINE + 1]; - char s[MAXLINE + 1]; - FILE *InFile = parser->InFile; - char Coordflag = parser->Coordflag; + int sect, newsect; + char *tok; + char line[MAXLINE + 1]; + char s[MAXLINE + 1]; + FILE *InFile = parser->InFile; - sect = -1; - if (InFile == NULL) { - return; - } - rewind(InFile); - while (fgets(line, MAXLINE, InFile) != NULL) { - /* Check if line begins with a new section heading */ - strcpy(s, line); - tok = strtok(s, SEPSTR); - if (tok != NULL && *tok == '[') { - newsect = findmatch(tok, SectTxt); - if (newsect >= 0) { - sect = newsect; - if (sect == _END) - break; - switch (sect) { - // case _RULES: - case _COORDS: - if (Coordflag == FALSE) { - fprintf(f, "%s", line); - } - break; - case _VERTICES: - case _LABELS: - case _BACKDROP: - case _TAGS: - fprintf(f, "%s", line); - } - continue; - } else - continue; - } + sect = -1; + if (InFile == NULL) return; + rewind(InFile); + while (fgets(line, MAXLINE, InFile) != NULL) + { + strcpy(s, line); + tok = strtok(s, SEPSTR); + if (tok == NULL) continue; - /* Write lines appearing in the section to file */ - switch (sect) { - // case _RULES: - case _COORDS: - if (Coordflag == FALSE) { - fprintf(f, "%s", line); - } - break; - case _VERTICES: - case _LABELS: - case _BACKDROP: - case _TAGS: - fprintf(f, "%s", line); + // Check if line begins with a new section heading + if (*tok == '[') + { + newsect = findmatch(tok, SectTxt); + if (newsect >= 0) + { + sect = newsect; + if (sect == _END) break; + } + } + + // Write line of auxilary data to file + switch (sect) + { + case _VERTICES: + case _LABELS: + case _BACKDROP: + case _TAGS: + fprintf(f, "%s", line); + break; + default: + break; + } } - } } -//// This function was heavily modified. //// - -int saveinpfile(EN_Project *pr, const char *fname) +int saveinpfile(Project *pr, const char *fname) /* ------------------------------------------------- Writes network data to text file. ------------------------------------------------- */ { - - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - report_options_t *rep = &pr->report; - out_file_t *out = &pr->out_files; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qu = &pr->quality; - time_options_t *time = &pr->time_options; - - int i, j, n; - int errcode; - double d, kc, ke, km, ucf; - char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1]; - Pdemand demand; - Psource source; - FILE *f; - Slink *link; - Stank *tank; - Snode *node; - Spump *pump; - Scontrol *control; - Scurve *curve; - Scoord *coord; + Network *net = &pr->network; + Parser *parser = &pr->parser; + Report *rpt = &pr->report; + Outfile *out = &pr->outfile; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Times *time = &pr->times; - /* Open the new text file */ + int i, j, n; + int errcode; + double d, kc, ke, km, ucf; + char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1]; + Pdemand demand; + Psource source; + FILE *f; + Slink *link; + Stank *tank; + Snode *node; + Spump *pump; + Scontrol *control; + Scurve *curve; - if ((f = fopen(fname, "wt")) == NULL) { - return (308); - } + // Open the new text file + if ((f = fopen(fname, "wt")) == NULL) return (308); - /* Write [TITLE] section */ - - fprintf(f, s_TITLE); - for (i = 0; i < 3; i++) { - if (strlen(pr->Title[i]) > 0) { - fprintf(f, "\n%s", pr->Title[i]); - } - } - - /* Write [JUNCTIONS] section */ - /* (Leave demands for [DEMANDS] section) */ - - fprintf(f, "\n\n"); - fprintf(f, s_JUNCTIONS); - for (i = 1; i <= net->Njuncs; i++) { - node = &net->Node[i]; - fprintf(f, "\n %-31s %12.4f ;%s", node->ID, node->El * pr->Ucf[ELEV], node->Comment); - } - /* Write [RESERVOIRS] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_RESERVOIRS); - for (i = 1; i <= net->Ntanks; i++) { - tank = &net->Tank[i]; - if (tank->A == 0.0) { - node = &net->Node[tank->Node]; - sprintf(s, " %-31s %12.4f", node->ID, node->El * pr->Ucf[ELEV]); - if ((j = tank->Pat) > 0) { - sprintf(s1, " %-31s", net->Pattern[j].ID); - } - else { - strcpy(s1, ""); - } - fprintf(f, "\n%s %s ;%s", s, s1, node->Comment); - } - } - - /* Write [TANKS] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_TANKS); - for (i = 1; i <= net->Ntanks; i++) { - tank = &net->Tank[i]; - if (tank->A > 0.0) { - node = &net->Node[tank->Node]; - sprintf(s, " %-31s %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f", node->ID, - node->El * pr->Ucf[ELEV], (tank->H0 - node->El) * pr->Ucf[ELEV], - (tank->Hmin - node->El) * pr->Ucf[ELEV], - (tank->Hmax - node->El) * pr->Ucf[ELEV], - sqrt(4.0 * tank->A / PI) * pr->Ucf[ELEV], - tank->Vmin * SQR(pr->Ucf[ELEV]) * pr->Ucf[ELEV]); - if ((j = tank->Vcurve) > 0) - sprintf(s1, "%-31s", net->Curve[j].ID); - else - strcpy(s1, ""); - fprintf(f, "\n%s %s ;%s", s, s1, node->Comment); - } - } - - /* Write [PIPES] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_PIPES); - for (i = 1; i <= net->Nlinks; i++) { - link = &net->Link[i]; - if (link->Type <= PIPE) { - d = link->Diam; - kc = link->Kc; - if (hyd->Formflag == DW) - kc = kc * pr->Ucf[ELEV] * 1000.0; - km = link->Km * SQR(d) * SQR(d) / 0.02517; - sprintf(s, " %-31s %-31s %-31s %12.4f %12.4f", link->ID, - net->Node[link->N1].ID, net->Node[link->N2].ID, - link->Len * pr->Ucf[LENGTH], d * pr->Ucf[DIAM]); - if (hyd->Formflag == DW) - sprintf(s1, "%12.4f %12.4f", kc, km); - else - sprintf(s1, "%12.4f %12.4f", kc, km); - if (link->Type == CVPIPE) - sprintf(s2, "CV"); - else if (link->Stat == CLOSED) - sprintf(s2, "CLOSED"); - else - strcpy(s2, ""); - fprintf(f, "\n%s %s %s ;%s", s, s1, s2, link->Comment); - } - } - - /* Write [PUMPS] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_PUMPS); - for (i = 1; i <= net->Npumps; i++) { - n = net->Pump[i].Link; - link = &net->Link[n]; - pump = &net->Pump[i]; - sprintf(s, " %-31s %-31s %-31s", link->ID, net->Node[link->N1].ID, net->Node[link->N2].ID); - - /* Pump has constant power */ - if (pump->Ptype == CONST_HP) - sprintf(s1, " POWER %.4f", link->Km); - - /* Pump has a head curve */ - else if ((j = pump->Hcurve) > 0) - sprintf(s1, " HEAD %s", net->Curve[j].ID); - - /* Old format used for pump curve */ - else { - fprintf(f, "\n%s %12.4f %12.4f %12.4f 0.0 %12.4f", s, - -pump->H0 * pr->Ucf[HEAD], - (-pump->H0 - pump->R * pow(pump->Q0, pump->N)) * - pr->Ucf[HEAD], - pump->Q0 * pr->Ucf[FLOW], pump->Qmax * pr->Ucf[FLOW]); - continue; - } - strcat(s, s1); - - if ((j = pump->Upat) > 0) - sprintf(s1, " PATTERN %s", net->Pattern[j].ID); - else - strcpy(s1, ""); - strcat(s, s1); - - if (link->Kc != 1.0) - sprintf(s1, " SPEED %.4f", link->Kc); - else - strcpy(s1, ""); - strcat(s, s1); - - fprintf(f, "\n%s ;%s", s, link->Comment); - } - - /* Write [VALVES] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_VALVES); - for (i = 1; i <= net->Nvalves; i++) { - n = net->Valve[i].Link; - link = &net->Link[n]; - d = link->Diam; - kc = link->Kc; - if (kc == MISSING) - kc = 0.0; - switch (link->Type) { - case FCV: - kc *= pr->Ucf[FLOW]; - break; - case PRV: - case PSV: - case PBV: - kc *= pr->Ucf[PRESSURE]; - break; - default: - break; - // not a valve... pipe/pump/cv - } - km = link->Km * SQR(d) * SQR(d) / 0.02517; - - sprintf(s, " %-31s %-31s %-31s %12.4f %5s", link->ID, net->Node[link->N1].ID, - net->Node[link->N2].ID, d * pr->Ucf[DIAM], LinkTxt[link->Type]); - - if (link->Type == GPV && (j = ROUND(link->Kc)) > 0) - sprintf(s1, "%-31s %12.4f", net->Curve[j].ID, km); - else - sprintf(s1, "%12.4f %12.4f", kc, km); - - fprintf(f, "\n%s %s ;%s", s, s1, link->Comment); - } - - /* Write [DEMANDS] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_DEMANDS); - ucf = pr->Ucf[DEMAND]; - for (i = 1; i <= net->Njuncs; i++) { - node = &net->Node[i]; - for (demand = node->D; demand != NULL; demand = demand->next) { - sprintf(s, " %-31s %14.6f", node->ID, ucf * demand->Base); - if ((j = demand->Pat) > 0) - sprintf(s1, " %s", net->Pattern[j].ID); - else - strcpy(s1, ""); - fprintf(f, "\n%s %s ;%s", s, s1, demand->Name); - } - } - - /* Write [EMITTERS] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_EMITTERS); - for (i = 1; i <= net->Njuncs; i++) { - node = &net->Node[i]; - if (node->Ke == 0.0) - continue; - ke = pr->Ucf[FLOW] / pow(pr->Ucf[PRESSURE] * node->Ke, (1.0 / hyd->Qexp)); - fprintf(f, "\n %-31s %14.6f", node->ID, ke); - } - - /* Write [STATUS] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_STATUS); - for (i = 1; i <= net->Nlinks; i++) { - link = &net->Link[i]; - if (link->Type <= PUMP) { - if (link->Stat == CLOSED) - fprintf(f, "\n %-31s %s", link->ID, StatTxt[CLOSED]); - - /* Write pump speed here for pumps with old-style pump curve input */ - else if (link->Type == PUMP) { - n = findpump(net, i); - pump = &net->Pump[n]; - if (pump->Hcurve == 0 && pump->Ptype != CONST_HP && - link->Kc != 1.0) - fprintf(f, "\n %-31s %-.4f", link->ID, link->Kc); - } + // Write [TITLE] section + fprintf(f, s_TITLE); + for (i = 0; i < 3; i++) + { + if (strlen(pr->Title[i]) > 0) fprintf(f, "\n%s", pr->Title[i]); } - /* Write fixed-status PRVs & PSVs (setting = MISSING) */ - else if (link->Kc == MISSING) { - if (link->Stat == OPEN) - fprintf(f, "\n %-31s %s", link->ID, StatTxt[OPEN]); - if (link->Stat == CLOSED) - fprintf(f, "\n%-31s %s", link->ID, StatTxt[CLOSED]); + // Write [JUNCTIONS] section + // (Leave demands for [DEMANDS] section) + fprintf(f, "\n\n"); + fprintf(f, s_JUNCTIONS); + for (i = 1; i <= net->Njuncs; i++) + { + node = &net->Node[i]; + fprintf(f, "\n %-31s %12.4f ;%s", node->ID, node->El * pr->Ucf[ELEV], + node->Comment); } - } - /* Write [PATTERNS] section */ - /* (Use 6 pattern factors per line) */ - - fprintf(f, "\n\n"); - fprintf(f, s_PATTERNS); - for (i = 1; i <= net->Npats; i++) { - for (j = 0; j < net->Pattern[i].Length; j++) { - if (j % 6 == 0) - fprintf(f, "\n %-31s", net->Pattern[i].ID); - fprintf(f, " %12.4f", net->Pattern[i].F[j]); + // Write [RESERVOIRS] section + fprintf(f, "\n\n"); + fprintf(f, s_RESERVOIRS); + for (i = 1; i <= net->Ntanks; i++) + { + tank = &net->Tank[i]; + if (tank->A == 0.0) + { + node = &net->Node[tank->Node]; + sprintf(s, " %-31s %12.4f", node->ID, node->El * pr->Ucf[ELEV]); + if ((j = tank->Pat) > 0) sprintf(s1, " %-31s", net->Pattern[j].ID); + else strcpy(s1, ""); + fprintf(f, "\n%s %s ;%s", s, s1, node->Comment); + } } - } - /* Write [CURVES] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_CURVES); - for (i = 1; i <= net->Ncurves; i++) { - for (j = 0; j < net->Curve[i].Npts; j++) { - curve = &net->Curve[i]; - fprintf(f, "\n %-31s %12.4f %12.4f", curve->ID, curve->X[j], curve->Y[j]); + // Write [TANKS] section + fprintf(f, "\n\n"); + fprintf(f, s_TANKS); + for (i = 1; i <= net->Ntanks; i++) + { + tank = &net->Tank[i]; + if (tank->A > 0.0) + { + node = &net->Node[tank->Node]; + sprintf(s, " %-31s %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f", + node->ID, node->El * pr->Ucf[ELEV], + (tank->H0 - node->El) * pr->Ucf[ELEV], + (tank->Hmin - node->El) * pr->Ucf[ELEV], + (tank->Hmax - node->El) * pr->Ucf[ELEV], + sqrt(4.0 * tank->A / PI) * pr->Ucf[ELEV], + tank->Vmin * SQR(pr->Ucf[ELEV]) * pr->Ucf[ELEV]); + if ((j = tank->Vcurve) > 0) sprintf(s1, "%-31s", net->Curve[j].ID); + else strcpy(s1, ""); + fprintf(f, "\n%s %s ;%s", s, s1, node->Comment); + } } - } - /* Write [CONTROLS] section */ + // Write [PIPES] section + fprintf(f, "\n\n"); + fprintf(f, s_PIPES); + for (i = 1; i <= net->Nlinks; i++) + { + link = &net->Link[i]; + if (link->Type <= PIPE) + { + d = link->Diam; + kc = link->Kc; + if (hyd->Formflag == DW) kc = kc * pr->Ucf[ELEV] * 1000.0; + km = link->Km * SQR(d) * SQR(d) / 0.02517; - fprintf(f, "\n\n"); - fprintf(f, s_CONTROLS); - for (i = 1; i <= net->Ncontrols; i++) { - control = &net->Control[i]; - /* Check that controlled link exists */ - if ((j = control->Link) <= 0) - continue; - link = &net->Link[j]; + sprintf(s, " %-31s %-31s %-31s %12.4f %12.4f", link->ID, + net->Node[link->N1].ID, net->Node[link->N2].ID, + link->Len * pr->Ucf[LENGTH], d * pr->Ucf[DIAM]); - /* Get text of control's link status/setting */ - if (control->Setting == MISSING) - sprintf(s, " LINK %s %s ", link->ID, StatTxt[control->Status]); - else { - kc = control->Setting; - switch (link->Type) { - case PRV: - case PSV: - case PBV: - kc *= pr->Ucf[PRESSURE]; + if (hyd->Formflag == DW) sprintf(s1, "%12.4f %12.4f", kc, km); + else sprintf(s1, "%12.4f %12.4f", kc, km); + + if (link->Type == CVPIPE) sprintf(s2, "CV"); + else if (link->Status == CLOSED) sprintf(s2, "CLOSED"); + else strcpy(s2, ""); + fprintf(f, "\n%s %s %s ;%s", s, s1, s2, link->Comment); + } + } + + // Write [PUMPS] section + fprintf(f, "\n\n"); + fprintf(f, s_PUMPS); + for (i = 1; i <= net->Npumps; i++) + { + n = net->Pump[i].Link; + link = &net->Link[n]; + pump = &net->Pump[i]; + sprintf(s, " %-31s %-31s %-31s", link->ID, net->Node[link->N1].ID, + net->Node[link->N2].ID); + + // Pump has constant power + if (pump->Ptype == CONST_HP) sprintf(s1, " POWER %.4f", link->Km); + + // Pump has a head curve + else if ((j = pump->Hcurve) > 0) + { + sprintf(s1, " HEAD %s", net->Curve[j].ID); + } + + // Old format used for pump curve + else + { + fprintf(f, "\n%s %12.4f %12.4f %12.4f 0.0 %12.4f", s, + -pump->H0 * pr->Ucf[HEAD], + (-pump->H0 - pump->R * pow(pump->Q0, pump->N)) * pr->Ucf[HEAD], + pump->Q0 * pr->Ucf[FLOW], pump->Qmax * pr->Ucf[FLOW]); + continue; + } + strcat(s, s1); + + // Optional speed pattern + if ((j = pump->Upat) > 0) + { + sprintf(s1, " PATTERN %s", net->Pattern[j].ID); + strcat(s, s1); + } + + // Optional speed setting + if (link->Kc != 1.0) + { + sprintf(s1, " SPEED %.4f", link->Kc); + strcat(s, s1); + } + + fprintf(f, "\n%s ;%s", s, link->Comment); + } + + // Write [VALVES] section + fprintf(f, "\n\n"); + fprintf(f, s_VALVES); + for (i = 1; i <= net->Nvalves; i++) + { + n = net->Valve[i].Link; + link = &net->Link[n]; + d = link->Diam; + + // Valve setting + kc = link->Kc; + if (kc == MISSING) kc = 0.0; + switch (link->Type) + { + case FCV: + kc *= pr->Ucf[FLOW]; + break; + case PRV: + case PSV: + case PBV: + kc *= pr->Ucf[PRESSURE]; + break; + default: + break; + } + km = link->Km * SQR(d) * SQR(d) / 0.02517; + + sprintf(s, " %-31s %-31s %-31s %12.4f %5s", + link->ID, net->Node[link->N1].ID, + net->Node[link->N2].ID, d * pr->Ucf[DIAM], + LinkTxt[link->Type]); + + // For GPV, setting = head curve index + if (link->Type == GPV && (j = ROUND(link->Kc)) > 0) + { + sprintf(s1, "%-31s %12.4f", net->Curve[j].ID, km); + } + else sprintf(s1, "%12.4f %12.4f", kc, km); + fprintf(f, "\n%s %s ;%s", s, s1, link->Comment); + } + + // Write [DEMANDS] section + fprintf(f, "\n\n"); + fprintf(f, s_DEMANDS); + ucf = pr->Ucf[DEMAND]; + for (i = 1; i <= net->Njuncs; i++) + { + node = &net->Node[i]; + for (demand = node->D; demand != NULL; demand = demand->next) + { + sprintf(s, " %-31s %14.6f", node->ID, ucf * demand->Base); + if ((j = demand->Pat) > 0) sprintf(s1, " %s", net->Pattern[j].ID); + else strcpy(s1, ""); + fprintf(f, "\n%s %s ;%s", s, s1, demand->Name); + } + } + + // Write [EMITTERS] section + fprintf(f, "\n\n"); + fprintf(f, s_EMITTERS); + for (i = 1; i <= net->Njuncs; i++) + { + node = &net->Node[i]; + if (node->Ke == 0.0) continue; + ke = pr->Ucf[FLOW] / pow(pr->Ucf[PRESSURE] * node->Ke, (1.0 / hyd->Qexp)); + fprintf(f, "\n %-31s %14.6f", node->ID, ke); + } + + // Write [STATUS] section + fprintf(f, "\n\n"); + fprintf(f, s_STATUS); + for (i = 1; i <= net->Nlinks; i++) + { + link = &net->Link[i]; + if (link->Type <= PUMP) + { + if (link->Status == CLOSED) + { + fprintf(f, "\n %-31s %s", link->ID, StatTxt[CLOSED]); + } + + // Write pump speed here for pumps with old-style pump curve input + else if (link->Type == PUMP) + { + n = findpump(net, i); + pump = &net->Pump[n]; + if (pump->Hcurve == 0 && pump->Ptype != CONST_HP && + link->Kc != 1.0) + { + fprintf(f, "\n %-31s %-.4f", link->ID, link->Kc); + } + } + } + + // Write fixed-status PRVs & PSVs (setting = MISSING) + else if (link->Kc == MISSING) + { + if (link->Status == OPEN) + { + fprintf(f, "\n %-31s %s", link->ID, StatTxt[OPEN]); + } + if (link->Status == CLOSED) + { + fprintf(f, "\n%-31s %s", link->ID, StatTxt[CLOSED]); + } + } + } + + // Write [PATTERNS] section + // (Use 6 pattern factors per line) + fprintf(f, "\n\n"); + fprintf(f, s_PATTERNS); + for (i = 1; i <= net->Npats; i++) + { + for (j = 0; j < net->Pattern[i].Length; j++) + { + if (j % 6 == 0) fprintf(f, "\n %-31s", net->Pattern[i].ID); + fprintf(f, " %12.4f", net->Pattern[i].F[j]); + } + } + + // Write [CURVES] section + fprintf(f, "\n\n"); + fprintf(f, s_CURVES); + for (i = 1; i <= net->Ncurves; i++) + { + for (j = 0; j < net->Curve[i].Npts; j++) + { + curve = &net->Curve[i]; + fprintf(f, "\n %-31s %12.4f %12.4f", curve->ID, curve->X[j], + curve->Y[j]); + } + } + + // Write [CONTROLS] section + fprintf(f, "\n\n"); + fprintf(f, s_CONTROLS); + for (i = 1; i <= net->Ncontrols; i++) + { + // Check that controlled link exists + control = &net->Control[i]; + if ((j = control->Link) <= 0) continue; + link = &net->Link[j]; + + // Get text of control's link status/setting + if (control->Setting == MISSING) + { + sprintf(s, " LINK %s %s ", link->ID, StatTxt[control->Status]); + } + else + { + kc = control->Setting; + switch (link->Type) + { + case PRV: + case PSV: + case PBV: + kc *= pr->Ucf[PRESSURE]; + break; + case FCV: + kc *= pr->Ucf[FLOW]; + break; + default: + break; + } + sprintf(s, " LINK %s %.4f", link->ID, kc); + } + + switch (control->Type) + { + // Print level control + case LOWLEVEL: + case HILEVEL: + n = control->Node; + node = &net->Node[n]; + kc = control->Grade - node->El; + if (n > net->Njuncs) kc *= pr->Ucf[HEAD]; + else kc *= pr->Ucf[PRESSURE]; + fprintf(f, "\n%s IF NODE %s %s %.4f", s, node->ID, + ControlTxt[control->Type], kc); + break; + + // Print timer control + case TIMER: + fprintf(f, "\n%s AT %s %.4f HOURS", s, ControlTxt[TIMER], + control->Time / 3600.); + break; + + // Print time-of-day control + case TIMEOFDAY: + fprintf(f, "\n%s AT %s %s", s, ControlTxt[TIMEOFDAY], + clocktime(rpt->Atime, control->Time)); + break; + } + } + + // Write [RULES] section + fprintf(f, "\n\n"); + fprintf(f, s_RULES); + for (i = 1; i <= net->Nrules; i++) + { + fprintf(f, "\nRULE %s", pr->network.Rule[i].label); + errcode = writerule(pr, f, i); // see RULES.C + fprintf(f, "\n"); + } + + // Write [QUALITY] section + // (Skip nodes with default quality of 0) + fprintf(f, "\n\n"); + fprintf(f, s_QUALITY); + for (i = 1; i <= net->Nnodes; i++) + { + node = &net->Node[i]; + if (node->C0 == 0.0) continue; + fprintf(f, "\n %-31s %14.6f", node->ID, node->C0 * pr->Ucf[QUALITY]); + } + + // Write [SOURCES] section + fprintf(f, "\n\n"); + fprintf(f, s_SOURCES); + for (i = 1; i <= net->Nnodes; i++) + { + node = &net->Node[i]; + source = node->S; + if (source == NULL) continue; + sprintf(s, " %-31s %-8s %14.6f", node->ID, SourceTxt[source->Type], + source->C0); + if ((j = source->Pat) > 0) + { + sprintf(s1, "%s", net->Pattern[j].ID); + } + else strcpy(s1, ""); + fprintf(f, "\n%s %s", s, s1); + } + + // Write [MIXING] section + fprintf(f, "\n\n"); + fprintf(f, s_MIXING); + for (i = 1; i <= net->Ntanks; i++) + { + Stank *tank = &net->Tank[i]; + if (tank->A == 0.0) continue; + fprintf(f, "\n %-31s %-8s %12.4f", net->Node[tank->Node].ID, + MixTxt[tank->MixModel], (tank->V1max / tank->Vmax)); + } + + // Write [REACTIONS] section + fprintf(f, "\n\n"); + fprintf(f, s_REACTIONS); + + // General parameters + fprintf(f, "\n ORDER BULK %-.2f", qual->BulkOrder); + fprintf(f, "\n ORDER WALL %-.0f", qual->WallOrder); + 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); + } + 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++) + { + link = &net->Link[i]; + if (link->Type > PIPE) continue; + if (link->Kb != qual->Kbulk) + { + fprintf(f, "\n BULK %-31s %-.6f", link->ID, link->Kb * SECperDAY); + } + if (link->Kw != qual->Kwall) + { + fprintf(f, "\n WALL %-31s %-.6f", link->ID, link->Kw * SECperDAY); + } + } + + // Tank parameters + for (i = 1; i <= net->Ntanks; i++) + { + tank = &net->Tank[i]; + if (tank->A == 0.0) continue; + if (tank->Kb != qual->Kbulk) + { + fprintf(f, "\n TANK %-31s %-.6f", net->Node[tank->Node].ID, + tank->Kb * SECperDAY); + } + } + + // Write [ENERGY] section + fprintf(f, "\n\n"); + fprintf(f, s_ENERGY); + + // General parameters + if (hyd->Ecost != 0.0) + { + fprintf(f, "\n GLOBAL PRICE %-.4f", hyd->Ecost); + } + if (hyd->Epat != 0) + { + fprintf(f, "\n GLOBAL PATTERN %s", net->Pattern[hyd->Epat].ID); + } + fprintf(f, "\n GLOBAL EFFIC %-.4f", hyd->Epump); + fprintf(f, "\n DEMAND CHARGE %-.4f", hyd->Dcost); + + // Pump-specific parameters + for (i = 1; i <= net->Npumps; i++) + { + pump = &net->Pump[i]; + if (pump->Ecost > 0.0) + { + fprintf(f, "\n PUMP %-31s PRICE %-.4f", net->Link[pump->Link].ID, + pump->Ecost); + } + if (pump->Epat > 0.0) + { + fprintf(f, "\n PUMP %-31s PATTERN %s", net->Link[pump->Link].ID, + net->Pattern[pump->Epat].ID); + } + if (pump->Ecurve > 0.0) + { + fprintf(f, "\n PUMP %-31s EFFIC %s", net->Link[pump->Link].ID, + net->Curve[pump->Ecurve].ID); + } + } + + // Write [TIMES] section + fprintf(f, "\n\n"); + fprintf(f, s_TIMES); + fprintf(f, "\n DURATION %s", clocktime(rpt->Atime, time->Dur)); + fprintf(f, "\n HYDRAULIC TIMESTEP %s", clocktime(rpt->Atime, time->Hstep)); + fprintf(f, "\n QUALITY TIMESTEP %s", clocktime(rpt->Atime, time->Qstep)); + fprintf(f, "\n REPORT TIMESTEP %s", clocktime(rpt->Atime, time->Rstep)); + fprintf(f, "\n REPORT START %s", clocktime(rpt->Atime, time->Rstart)); + fprintf(f, "\n PATTERN TIMESTEP %s", clocktime(rpt->Atime, time->Pstep)); + fprintf(f, "\n PATTERN START %s", clocktime(rpt->Atime, time->Pstart)); + fprintf(f, "\n RULE TIMESTEP %s", clocktime(rpt->Atime, time->Rulestep)); + fprintf(f, "\n START CLOCKTIME %s", clocktime(rpt->Atime, time->Tstart)); + fprintf(f, "\n STATISTIC %s", TstatTxt[rpt->Tstatflag]); + + // Write [OPTIONS] section + fprintf(f, "\n\n"); + fprintf(f, s_OPTIONS); + 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: + fprintf(f, "\n HYDRAULICS USE %s", out->HydFname); break; - case FCV: - kc *= pr->Ucf[FLOW]; + case SAVE: + fprintf(f, "\n HYDRAULICS SAVE %s", out->HydFname); + break; + } + if (hyd->ExtraIter == -1) + { + fprintf(f, "\n UNBALANCED STOP"); + } + if (hyd->ExtraIter >= 0) + { + fprintf(f, "\n UNBALANCED CONTINUE %d", hyd->ExtraIter); + } + + switch (qual->Qualflag) + { + case CHEM: + fprintf(f, "\n QUALITY %s %s", + qual->ChemName, qual->ChemUnits); + break; + case TRACE: + fprintf(f, "\n QUALITY TRACE %-31s", + net->Node[qual->TraceNode].ID); + break; + case AGE: + fprintf(f, "\n QUALITY AGE"); + break; + case NONE: + fprintf(f, "\n QUALITY NONE"); + break; + } + + fprintf(f, "\n DEMAND MULTIPLIER %-.4f", hyd->Dmult); + fprintf(f, "\n EMITTER EXPONENT %-.4f", 1.0 / hyd->Qexp); + fprintf(f, "\n VISCOSITY %-.6f", hyd->Viscos / VISCOS); + fprintf(f, "\n DIFFUSIVITY %-.6f", qual->Diffus / DIFFUS); + fprintf(f, "\n SPECIFIC GRAVITY %-.6f", hyd->SpGrav); + fprintf(f, "\n TRIALS %-d", hyd->MaxIter); + fprintf(f, "\n ACCURACY %-.8f", hyd->Hacc); + fprintf(f, "\n TOLERANCE %-.8f", qual->Ctol * pr->Ucf[QUALITY]); + fprintf(f, "\n CHECKFREQ %-d", hyd->CheckFreq); + fprintf(f, "\n MAXCHECK %-d", hyd->MaxCheck); + fprintf(f, "\n DAMPLIMIT %-.8f", hyd->DampLimit); + if (hyd->HeadErrorLimit > 0.0) + { + fprintf(f, "\n HEADERROR %-.8f", + hyd->HeadErrorLimit * pr->Ucf[HEAD]); + } + if (hyd->FlowChangeLimit > 0.0) + { + fprintf(f, "\n FLOWCHANGE %-.8f", + hyd->FlowChangeLimit * pr->Ucf[FLOW]); + } + if (hyd->DemandModel == PDA) + { + fprintf(f, "\n DEMAND MODEL PDA"); + fprintf(f, "\n MINIMUM PRESSURE %-.4f", hyd->Pmin * pr->Ucf[PRESSURE]); + fprintf(f, "\n REQUIRED PRESSURE %-.4f", hyd->Preq * pr->Ucf[PRESSURE]); + fprintf(f, "\n PRESSURE EXPONENT %-.4f", hyd->Pexp); + } + + // Write [REPORT] section + fprintf(f, "\n\n"); + fprintf(f, s_REPORT); + + // General options + fprintf(f, "\n PAGESIZE %d", rpt->PageSize); + fprintf(f, "\n STATUS %s", RptFlagTxt[rpt->Statflag]); + fprintf(f, "\n SUMMARY %s", RptFlagTxt[rpt->Summaryflag]); + fprintf(f, "\n ENERGY %s", RptFlagTxt[rpt->Energyflag]); + fprintf(f, "\n MESSAGES %s", RptFlagTxt[rpt->Messageflag]); + if (strlen(rpt->Rpt2Fname) > 0) + { + fprintf(f, "\n FILE %s", rpt->Rpt2Fname); + } + + // Node reporting + switch (rpt->Nodeflag) + { + case 0: + fprintf(f, "\n NODES NONE"); + break; + case 1: + fprintf(f, "\n NODES ALL"); break; default: + j = 0; + for (i = 1; i <= net->Nnodes; i++) + { + node = &net->Node[i]; + if (node->Rpt == 1) + { + if (j % 5 == 0) fprintf(f, "\n NODES "); + fprintf(f, "%s ", node->ID); + j++; + } + } + } + + // Link reporting + switch (rpt->Linkflag) + { + case 0: + fprintf(f, "\n LINKS NONE"); break; - } - sprintf(s, " LINK %s %.4f", link->ID, kc); + case 1: + fprintf(f, "\n LINKS ALL"); + break; + default: + j = 0; + for (i = 1; i <= net->Nlinks; i++) + { + Slink *link = &net->Link[i]; + if (link->Rpt == 1) + { + if (j % 5 == 0) fprintf(f, "\n LINKS "); + fprintf(f, "%s ", link->ID); + j++; + } + } } - switch (control->Type) { - /* Print level control */ - case LOWLEVEL: - case HILEVEL: - n = control->Node; - node = &net->Node[n]; - kc = control->Grade - node->El; - if (n > net->Njuncs) - kc *= pr->Ucf[HEAD]; - else - kc *= pr->Ucf[PRESSURE]; - fprintf(f, "\n%s IF NODE %s %s %.4f", s, node->ID, - ControlTxt[control->Type], kc); - break; - - /* Print timer control */ - case TIMER: - fprintf(f, "\n%s AT %s %.4f HOURS", s, ControlTxt[TIMER], - control->Time / 3600.); - break; - - /* Print time-of-day control */ - case TIMEOFDAY: - fprintf(f, "\n%s AT %s %s", s, ControlTxt[TIMEOFDAY], - clocktime(rep->Atime, control->Time)); - break; + // Field formatting options + for (i = 0; i < FRICTION; i++) + { + SField *field = &rpt->Field[i]; + if (field->Enabled == TRUE) + { + fprintf(f, "\n %-20sPRECISION %d", field->Name, field->Precision); + if (field->RptLim[LOW] < BIG) + { + fprintf(f, "\n %-20sBELOW %.6f", field->Name, field->RptLim[LOW]); + } + if (field->RptLim[HI] > -BIG) + { + fprintf(f, "\n %-20sABOVE %.6f", field->Name, field->RptLim[HI]); + } + } + else fprintf(f, "\n %-20sNO",field->Name); } - } + fprintf(f, "\n\n"); - /* Write [RULES] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_RULES); - for (i = 1; i <= net->Nrules; i++) { - fprintf(f, "\nRULE %s", pr->network.Rule[i].label); - errcode = writerule(pr, f, i); - fprintf(f, "\n"); - } - - /* Write [QUALITY] section */ - /* (Skip nodes with default quality of 0) */ - - fprintf(f, "\n\n"); - fprintf(f, s_QUALITY); - for (i = 1; i <= net->Nnodes; i++) { - node = &net->Node[i]; - if (node->C0 == 0.0) - continue; - fprintf(f, "\n %-31s %14.6f", node->ID, node->C0 * pr->Ucf[QUALITY]); - } - - /* Write [SOURCES] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_SOURCES); - for (i = 1; i <= net->Nnodes; i++) { - node = &net->Node[i]; - source = node->S; - if (source == NULL) - continue; - sprintf(s, " %-31s %-8s %14.6f", node->ID, SourceTxt[source->Type], source->C0); - if ((j = source->Pat) > 0) - sprintf(s1, "%s", net->Pattern[j].ID); - else - strcpy(s1, ""); - fprintf(f, "\n%s %s", s, s1); - } - - /* Write [MIXING] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_MIXING); - for (i = 1; i <= net->Ntanks; i++) { - Stank *tank = &net->Tank[i]; - if (tank->A == 0.0) - continue; - fprintf(f, "\n %-31s %-8s %12.4f", net->Node[tank->Node].ID, - MixTxt[tank->MixModel], (tank->V1max / tank->Vmax)); - } - - /* Write [REACTIONS] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_REACTIONS); - fprintf(f, "\n ORDER BULK %-.2f", qu->BulkOrder); - fprintf(f, "\n ORDER WALL %-.0f", qu->WallOrder); - fprintf(f, "\n ORDER TANK %-.2f", qu->TankOrder); - fprintf(f, "\n GLOBAL BULK %-.6f", qu->Kbulk * SECperDAY); - fprintf(f, "\n GLOBAL WALL %-.6f", qu->Kwall * SECperDAY); - if (qu->Climit > 0.0) - fprintf(f, "\n LIMITING POTENTIAL %-.6f", qu->Climit); - if (qu->Rfactor != MISSING && qu->Rfactor != 0.0) - fprintf(f, "\n ROUGHNESS CORRELATION %-.6f", qu->Rfactor); - for (i = 1; i <= net->Nlinks; i++) { - link = &net->Link[i]; - if (link->Type > PIPE) - continue; - if (link->Kb != qu->Kbulk) - fprintf(f, "\n BULK %-31s %-.6f", link->ID, link->Kb * SECperDAY); - if (link->Kw != qu->Kwall) - fprintf(f, "\n WALL %-31s %-.6f", link->ID, link->Kw * SECperDAY); - } - for (i = 1; i <= net->Ntanks; i++) { - tank = &net->Tank[i]; - if (tank->A == 0.0) - continue; - if (tank->Kb != qu->Kbulk) - fprintf(f, "\n TANK %-31s %-.6f", net->Node[tank->Node].ID, - tank->Kb * SECperDAY); - } - - /* Write [ENERGY] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_ENERGY); - if (hyd->Ecost != 0.0) - fprintf(f, "\n GLOBAL PRICE %-.4f", hyd->Ecost); - if (hyd->Epat != 0) - fprintf(f, "\n GLOBAL PATTERN %s", net->Pattern[hyd->Epat].ID); - fprintf(f, "\n GLOBAL EFFIC %-.4f", hyd->Epump); - fprintf(f, "\n DEMAND CHARGE %-.4f", hyd->Dcost); - for (i = 1; i <= net->Npumps; i++) { - pump = &net->Pump[i]; - if (pump->Ecost > 0.0) - fprintf(f, "\n PUMP %-31s PRICE %-.4f", net->Link[pump->Link].ID, - pump->Ecost); - if (pump->Epat > 0.0) - fprintf(f, "\n PUMP %-31s PATTERN %s", net->Link[pump->Link].ID, - net->Pattern[pump->Epat].ID); - if (pump->Ecurve > 0.0) - fprintf(f, "\n PUMP %-31s EFFIC %s", net->Link[pump->Link].ID, - net->Curve[pump->Ecurve].ID); - } - - /* Write [TIMES] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_TIMES); - fprintf(f, "\n DURATION %s", clocktime(rep->Atime, time->Dur)); - fprintf(f, "\n HYDRAULIC TIMESTEP %s", clocktime(rep->Atime, time->Hstep)); - fprintf(f, "\n QUALITY TIMESTEP %s", clocktime(rep->Atime, qu->Qstep)); - fprintf(f, "\n REPORT TIMESTEP %s", clocktime(rep->Atime, time->Rstep)); - fprintf(f, "\n REPORT START %s", clocktime(rep->Atime, time->Rstart)); - fprintf(f, "\n PATTERN TIMESTEP %s", clocktime(rep->Atime, time->Pstep)); - fprintf(f, "\n PATTERN START %s", clocktime(rep->Atime, time->Pstart)); - fprintf(f, "\n RULE TIMESTEP %s", clocktime(rep->Atime, time->Rulestep)); - fprintf(f, "\n START CLOCKTIME %s", clocktime(rep->Atime, time->Tstart)); - fprintf(f, "\n STATISTIC %s", TstatTxt[rep->Tstatflag]); - - /* Write [OPTIONS] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_OPTIONS); - fprintf(f, "\n UNITS %s", FlowUnitsTxt[par->Flowflag]); - fprintf(f, "\n PRESSURE %s", PressUnitsTxt[par->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); - if (out->Hydflag == USE) - fprintf(f, "\n HYDRAULICS USE %s", out->HydFname); - if (out->Hydflag == SAVE) - fprintf(f, "\n HYDRAULICS SAVE %s", out->HydFname); - if (hyd->ExtraIter == -1) - fprintf(f, "\n UNBALANCED STOP"); - if (hyd->ExtraIter >= 0) - fprintf(f, "\n UNBALANCED CONTINUE %d", hyd->ExtraIter); - if (qu->Qualflag == CHEM) - fprintf(f, "\n QUALITY %s %s", qu->ChemName, qu->ChemUnits); - if (qu->Qualflag == TRACE) - fprintf(f, "\n QUALITY TRACE %-31s", net->Node[qu->TraceNode].ID); - if (qu->Qualflag == AGE) - fprintf(f, "\n QUALITY AGE"); - if (qu->Qualflag == NONE) - fprintf(f, "\n QUALITY NONE"); - fprintf(f, "\n DEMAND MULTIPLIER %-.4f", hyd->Dmult); - fprintf(f, "\n EMITTER EXPONENT %-.4f", 1.0 / hyd->Qexp); - fprintf(f, "\n VISCOSITY %-.6f", hyd->Viscos / VISCOS); - fprintf(f, "\n DIFFUSIVITY %-.6f", qu->Diffus / DIFFUS); - fprintf(f, "\n SPECIFIC GRAVITY %-.6f", hyd->SpGrav); - fprintf(f, "\n TRIALS %-d", hyd->MaxIter); - fprintf(f, "\n ACCURACY %-.8f", hyd->Hacc); - fprintf(f, "\n TOLERANCE %-.8f", qu->Ctol * pr->Ucf[QUALITY]); - fprintf(f, "\n CHECKFREQ %-d", hyd->CheckFreq); - fprintf(f, "\n MAXCHECK %-d", hyd->MaxCheck); - fprintf(f, "\n DAMPLIMIT %-.8f", hyd->DampLimit); - if (hyd->HeadErrorLimit > 0.0) { - fprintf(f, "\n HEADERROR %-.8f", hyd->HeadErrorLimit * pr->Ucf[HEAD]); - } - if (hyd->FlowChangeLimit > 0.0) { - fprintf(f, "\n FLOWCHANGE %-.8f", hyd->FlowChangeLimit * pr->Ucf[FLOW]); - } - if (hyd->DemandModel == PDA) - { - fprintf(f, "\n DEMAND MODEL PDA"); - fprintf(f, "\n MINIMUM PRESSURE %-.4f", hyd->Pmin * pr->Ucf[PRESSURE]); - fprintf(f, "\n REQUIRED PRESSURE %-.4f", hyd->Preq * pr->Ucf[PRESSURE]); - fprintf(f, "\n PRESSURE EXPONENT %-.4f", hyd->Pexp); - } - - /* Write [REPORT] section */ - - fprintf(f, "\n\n"); - fprintf(f, s_REPORT); - fprintf(f, "\n PAGESIZE %d", rep->PageSize); - fprintf(f, "\n STATUS %s", RptFlagTxt[rep->Statflag]); - fprintf(f, "\n SUMMARY %s", RptFlagTxt[rep->Summaryflag]); - fprintf(f, "\n ENERGY %s", RptFlagTxt[rep->Energyflag]); - fprintf(f, "\n MESSAGES %s", RptFlagTxt[rep->Messageflag]); - if (strlen(rep->Rpt2Fname) > 0) - fprintf(f, "\n FILE %s", rep->Rpt2Fname); - switch (rep->Nodeflag) { - case 0: - fprintf(f, "\n NODES NONE"); - break; - case 1: - fprintf(f, "\n NODES ALL"); - break; - default: - j = 0; - for (i = 1; i <= net->Nnodes; i++) { - node = &net->Node[i]; - if (node->Rpt == 1) { - if (j % 5 == 0) - fprintf(f, "\n NODES "); - fprintf(f, "%s ", node->ID); - j++; - } - } - } - switch (rep->Linkflag) { - case 0: - fprintf(f, "\n LINKS NONE"); - break; - case 1: - fprintf(f, "\n LINKS ALL"); - break; - default: - j = 0; - for (i = 1; i <= net->Nlinks; i++) { - Slink *link = &net->Link[i]; - if (link->Rpt == 1) { - if (j % 5 == 0) - fprintf(f, "\n LINKS "); - fprintf(f, "%s ", link->ID); - j++; - } - } - } - for (i = 0; i < FRICTION; i++) { - SField *field = &rep->Field[i]; - if (field->Enabled == TRUE) { - fprintf(f, "\n %-20sPRECISION %d", field->Name, field->Precision); - if (field->RptLim[LOW] < BIG) - fprintf(f, "\n %-20sBELOW %.6f", field->Name, field->RptLim[LOW]); - if (field->RptLim[HI] > -BIG) - fprintf(f, "\n %-20sABOVE %.6f", field->Name, field->RptLim[HI]); - } else - fprintf(f, "\n %-20sNO",field->Name); - } - fprintf(f, "\n\n"); - - /* Write [COORDINATES] section */ - - if (par->Coordflag == TRUE) { + // Write [COORDINATES] section fprintf(f, "\n\n"); fprintf(f, s_COORDS); - for (i = 1; i <= net->Nnodes; i++) { - node = &net->Node[i]; - coord = &net->Coord[i]; - if (coord->HaveCoords == TRUE) { - fprintf(f, "\n %-31s %14.6f %14.6f", node->ID, coord->X, coord->Y); - } + for (i = 1; i <= net->Nnodes; i++) + { + node = &net->Node[i]; + if (node->X == MISSING || node->Y == MISSING) continue; + fprintf(f, "\n %-31s %14.6f %14.6f", node->ID, node->X, node->Y); } fprintf(f, "\n\n"); - } - /* Save auxilary data to new input file */ + // Save auxilary data to new input file + saveauxdata(parser, f); - saveauxdata(par,f); - - /* Close the new input file */ - - fprintf(f, "\n"); - fprintf(f, s_END); - fclose(f); - return (0); + // Close the new input file + fprintf(f, "\n"); + fprintf(f, s_END); + fclose(f); + return 0; } diff --git a/src/input1.c b/src/input1.c index ce16fca..3f7879b 100644 --- a/src/input1.c +++ b/src/input1.c @@ -1,24 +1,14 @@ /* -********************************************************************* - -INPUT1.C -- Input Functions for EPANET Program - -VERSION: 2.00 -DATE: 5/30/00 - 9/7/00 - 11/19/01 - 6/24/02 - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - - This module initializes, retrieves, and adjusts the input - data for a network simulation. - - The entry point for this module is: - getdata() -- called from ENopen() in EPANET.C. - -********************************************************************* +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: input1.c +Description: retrieves network data from an EPANET input file +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/10/2018 +****************************************************************************** */ #include @@ -34,37 +24,27 @@ AUTHOR: L. Rossman #include "text.h" #include -/* - --------------------- Module Global Variables ---------------------- -*/ +// Default values +#define MAXITER 200 // Default max. # hydraulic iterations +#define HACC 0.001 // Default hydraulics convergence ratio +#define HTOL 0.0005 // Default hydraulic head tolerance (ft) +#define QTOL 0.0001 // Default flow rate tolerance (cfs) +#define AGETOL 0.01 // Default water age tolerance (hrs) +#define CHEMTOL 0.01 // Default concentration tolerance +#define PAGESIZE 0 // Default uses no page breaks +#define SPGRAV 1.0 // Default specific gravity +#define EPUMP 75 // Default pump efficiency +#define DEFPATID "1" // Default demand pattern ID +#define RQTOL 1E-7 // Default low flow resistance tolerance +#define CHECKFREQ 2 // Default status check frequency +#define MAXCHECK 10 // Default # iterations for status checks +#define DAMPLIMIT 0 // Default damping threshold -#define MAXITER 200 /* Default max. # hydraulic iterations */ -#define HACC 0.001 /* Default hydraulics convergence ratio */ -#define HTOL 0.0005 /* Default hydraulic head tolerance (ft) */ - -/*** Updated 11/19/01 ***/ -#define QTOL 0.0001 /* Default flow rate tolerance (cfs) */ - -#define AGETOL 0.01 /* Default water age tolerance (hrs) */ -#define CHEMTOL 0.01 /* Default concentration tolerance */ -#define PAGESIZE 0 /* Default uses no page breaks */ -#define SPGRAV 1.0 /* Default specific gravity */ -#define EPUMP 75 /* Default pump efficiency */ -#define DEFPATID "1" /* Default demand pattern ID */ - -/* - These next three parameters are used in the hydraulics solver: -*/ - -#define RQTOL 1E-7 /* Default low flow resistance tolerance */ -#define CHECKFREQ 2 /* Default status check frequency */ -#define MAXCHECK 10 /* Default # iterations for status checks */ -#define DAMPLIMIT 0 /* Default damping threshold */ - -extern char *Fldname[]; /* Defined in enumstxt.h in EPANET.C */ +// Defined in ENUMSTXT.H +extern char *Fldname[]; extern char *RptFlowUnitsTxt[]; -int getdata(EN_Project *pr) +int getdata(Project *pr) /* **---------------------------------------------------------------- ** Input: none @@ -73,103 +53,108 @@ int getdata(EN_Project *pr) **---------------------------------------------------------------- */ { - int errcode = 0; - setdefaults(pr); /* Assign default data values */ - initreport(&pr->report); /* Initialize reporting options */ - rewind(pr->parser.InFile); /* Rewind input file */ - ERRCODE(readdata(pr)); /* Read in network data */ - if (!errcode) - adjustdata(pr); /* Adjust data for default values */ - if (!errcode) - initunits(pr); /* Initialize units on input data */ - ERRCODE(inittanks(pr)); /* Initialize tank volumes */ - if (!errcode) - convertunits(pr); /* Convert units on input data */ - return (errcode); -} /* End of getdata */ + int errcode = 0; -void setdefaults(EN_Project *pr) + // Assign default data values & reporting options + setdefaults(pr); + initreport(&pr->report); + + // Read in network data + rewind(pr->parser.InFile); + ERRCODE(readdata(pr)); + + // Adjust data and convert it to internal units + if (!errcode) adjustdata(pr); + if (!errcode) initunits(pr); + ERRCODE(inittanks(pr)); + if (!errcode) convertunits(pr); + return errcode; +} + +void setdefaults(Project *pr) /* **---------------------------------------------------------------- ** Input: none ** Output: none -** Purpose: assigns default values to global variables +** Purpose: assigns default values to a project's variables **---------------------------------------------------------------- */ { - parser_data_t *par = &pr->parser; - report_options_t *rep = &pr->report; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qu = &pr->quality; - time_options_t *time = &pr->time_options; - out_file_t *out = &pr->out_files; + Parser *parser = &pr->parser; + Report *rpt = &pr->report; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Times *time = &pr->times; + Outfile *out = &pr->outfile; - strncpy(pr->Title[0], "", TITLELEN); - strncpy(pr->Title[1], "", TITLELEN); - strncpy(pr->Title[2], "", TITLELEN); - strncpy(out->HydFname, "", MAXFNAME); - strncpy(pr->MapFname, "", MAXFNAME); - strncpy(qu->ChemName, t_CHEMICAL, MAXID); - strncpy(qu->ChemUnits, u_MGperL, MAXID); - strncpy(par->DefPatID, DEFPATID, MAXID); - out->Hydflag = SCRATCH; /* No external hydraulics file */ - qu->Qualflag = NONE; /* No quality simulation */ - hyd->Formflag = HW; /* Use Hazen-Williams formula */ - par->Unitsflag = US; /* US unit system */ - par->Flowflag = GPM; /* Flow units are gpm */ - par->Pressflag = PSI; /* Pressure units are psi */ - rep->Tstatflag = SERIES; /* Generate time series output */ - pr->Warnflag = FALSE; /* Warning flag is off */ - hyd->Htol = HTOL; /* Default head tolerance */ - hyd->Qtol = QTOL; /* Default flow tolerance */ - hyd->Hacc = HACC; /* Default hydraulic accuracy */ + strncpy(pr->Title[0], "", TITLELEN); + strncpy(pr->Title[1], "", TITLELEN); + strncpy(pr->Title[2], "", TITLELEN); + strncpy(out->HydFname, "", MAXFNAME); + strncpy(pr->MapFname, "", MAXFNAME); + strncpy(qual->ChemName, t_CHEMICAL, MAXID); + strncpy(qual->ChemUnits, u_MGperL, MAXID); + strncpy(parser->DefPatID, DEFPATID, MAXID); - hyd->FlowChangeLimit = 0.0; // Default flow change limit - hyd->HeadErrorLimit = 0.0; // Default head error limit - hyd->DemandModel = DDA; // Demand driven analysis - hyd->Pmin = 0.0; // Minimum demand pressure (ft) - hyd->Preq = 0.0; // Required demand pressure (ft) - hyd->Pexp = 0.5; // Pressure function exponent + pr->Warnflag = FALSE; // Warning flag is off + parser->Unitsflag = US; // US unit system + parser->Flowflag = GPM; // Flow units are gpm + parser->Pressflag = PSI; // Pressure units are psi + 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 + hyd->Hacc = HACC; // Default hydraulic accuracy + hyd->FlowChangeLimit = 0.0; // Default flow change limit + hyd->HeadErrorLimit = 0.0; // Default head error limit + hyd->DemandModel = DDA; // Demand driven analysis + hyd->Pmin = 0.0; // Minimum demand pressure (ft) + hyd->Preq = 0.0; // Required demand pressure (ft) + hyd->Pexp = 0.5; // Pressure function exponent + hyd->MaxIter = MAXITER; // Default max. hydraulic trials + 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 + hyd->Epump = EPUMP; // Default pump efficiency + hyd->Emax = 0.0; // Zero peak energy usage + hyd->Qexp = 2.0; // Flow exponent for emitters + hyd->Dmult = 1.0; // Demand multiplier + hyd->RQtol = RQTOL; // Default hydraulics parameters + hyd->CheckFreq = CHECKFREQ; + hyd->MaxCheck = MAXCHECK; + hyd->DampLimit = DAMPLIMIT; - qu->Ctol = MISSING; /* No pre-set quality tolerance */ - hyd->MaxIter = MAXITER; /* Default max. hydraulic trials */ - hyd->ExtraIter = -1; /* Stop if network unbalanced */ - time->Dur = 0; /* 0 sec duration (steady state) */ - time->Tstart = 0; /* Starting time of day */ - time->Pstart = 0; /* Starting pattern period */ - time->Hstep = 3600; /* 1 hr hydraulic time step */ - qu->Qstep = 0; /* No pre-set quality time step */ - time->Pstep = 3600; /* 1 hr time pattern period */ - time->Rstep = 3600; /* 1 hr reporting period */ - time->Rulestep = 0; /* No pre-set rule time step */ - time->Rstart = 0; /* Start reporting at time 0 */ - qu->TraceNode = 0; /* No source tracing */ - qu->BulkOrder = 1.0; /* 1st-order bulk reaction rate */ - qu->WallOrder = 1.0; /* 1st-order wall reaction rate */ - qu->TankOrder = 1.0; /* 1st-order tank reaction rate */ - qu->Kbulk = 0.0; /* No global bulk reaction */ - qu->Kwall = 0.0; /* No global wall reaction */ - qu->Climit = 0.0; /* No limiting potential quality */ - qu->Diffus = MISSING; /* Temporary diffusivity */ - qu->Rfactor = 0.0; /* No roughness-reaction factor */ - 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 */ - hyd->Epump = EPUMP; /* Default pump efficiency */ - hyd->Emax = 0.0; /* Zero peak energy usage */ - hyd->Qexp = 2.0; /* Flow exponent for emitters */ - hyd->Dmult = 1.0; /* Demand multiplier */ - hyd->RQtol = RQTOL; /* Default hydraulics parameters */ - hyd->CheckFreq = CHECKFREQ; - hyd->MaxCheck = MAXCHECK; - hyd->DampLimit = DAMPLIMIT; - qu->massbalance.ratio = 0.0; -} /* End of setdefaults */ + qual->Qualflag = NONE; // No quality simulation + qual->Ctol = MISSING; // No pre-set quality tolerance + qual->TraceNode = 0; // No source tracing + qual->BulkOrder = 1.0; // 1st-order bulk reaction rate + qual->WallOrder = 1.0; // 1st-order wall reaction rate + qual->TankOrder = 1.0; // 1st-order tank reaction rate + qual->Kbulk = 0.0; // No global bulk reaction + qual->Kwall = 0.0; // No global wall reaction + qual->Climit = 0.0; // No limiting potential quality + qual->Diffus = MISSING; // Temporary diffusivity + qual->Rfactor = 0.0; // No roughness-reaction factor + qual->MassBalance.ratio = 0.0; -void initreport(report_options_t *r) + time->Dur = 0; // 0 sec duration (steady state) + time->Tstart = 0; // Starting time of day + time->Pstart = 0; // Starting pattern period + time->Hstep = 3600; // 1 hr hydraulic time step + time->Qstep = 0; // No pre-set quality time step + time->Pstep = 3600; // 1 hr time pattern period + time->Rstep = 3600; // 1 hr reporting period + time->Rulestep = 0; // No pre-set rule time step + time->Rstart = 0; // Start reporting at time 0 +} + +void initreport(Report *rpt) /* **---------------------------------------------------------------------- ** Input: none @@ -178,194 +163,187 @@ void initreport(report_options_t *r) **---------------------------------------------------------------------- */ { - int i; - strncpy(r->Rpt2Fname, "", MAXFNAME); - r->PageSize = PAGESIZE; /* Default page size for report */ - r->Summaryflag = TRUE; /* Write summary report */ - r->Messageflag = TRUE; /* Report error/warning messages */ - r->Statflag = FALSE; /* No hydraulic status reports */ - r->Energyflag = FALSE; /* No energy usage report */ - r->Nodeflag = 0; /* No reporting on nodes */ - r->Linkflag = 0; /* No reporting on links */ - for (i = 0; i < MAXVAR; i++) /* For each reporting variable: */ - { - strncpy(r->Field[i].Name, Fldname[i], MAXID); - r->Field[i].Enabled = FALSE; /* Not included in report */ - r->Field[i].Precision = 2; /* 2 decimal precision */ + int i; + strncpy(rpt->Rpt2Fname, "", MAXFNAME); - /*** Updated 6/24/02 ***/ - r->Field[i].RptLim[LOW] = SQR(BIG); /* No reporting limits */ - r->Field[i].RptLim[HI] = -SQR(BIG); - } - r->Field[FRICTION].Precision = 3; - for (i = DEMAND; i <= QUALITY; i++) { - r->Field[i].Enabled = TRUE; - } - for (i = FLOW; i <= HEADLOSS; i++) { - r->Field[i].Enabled = TRUE; - } + // Initialize general reporting options + rpt->PageSize = PAGESIZE; // Default page size for report + rpt->Summaryflag = TRUE; // Write summary report + rpt->Messageflag = TRUE; // Report error/warning messages + rpt->Statflag = FALSE; // No hydraulic status reports + rpt->Energyflag = FALSE; // No energy usage report + rpt->Nodeflag = 0; // No reporting on nodes + rpt->Linkflag = 0; // No reporting on links + + // Initialize options for each reported variable field + for (i = 0; i < MAXVAR; i++) + { + strncpy(rpt->Field[i].Name, Fldname[i], MAXID); + rpt->Field[i].Enabled = FALSE; // Not included in report + rpt->Field[i].Precision = 2; // 2 decimal precision + rpt->Field[i].RptLim[LOW] = SQR(BIG); // No reporting limits + rpt->Field[i].RptLim[HI] = -SQR(BIG); + } + rpt->Field[FRICTION].Precision = 3; + + // Set default set of variables reported on + for (i = DEMAND; i <= QUALITY; i++) + { + rpt->Field[i].Enabled = TRUE; + } + for (i = FLOW; i <= HEADLOSS; i++) + { + rpt->Field[i].Enabled = TRUE; + } } -void adjustdata(EN_Project *pr) +void adjustdata(Project *pr) /* **---------------------------------------------------------------------- ** Input: none ** Output: none -** Purpose: adjusts data after input file has been processed +** Purpose: adjusts project data after input file has been processed **---------------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qu = &pr->quality; - time_options_t *time = &pr->time_options; - parser_data_t *par = &pr->parser; - report_options_t *rep = &pr->report; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Times *time = &pr->times; + Parser *parser = &pr->parser; + Report *rpt = &pr->report; - int i; - double ucf; /* Unit conversion factor */ - Pdemand demand; /* Pointer to demand record */ + int i; + double ucf; // Unit conversion factor + Pdemand demand; // Pointer to demand record + Slink *link; + Snode *node; + Stank *tank; - /* Use 1 hr pattern & report time step if none specified */ - if (time->Pstep <= 0) - time->Pstep = 3600; - if (time->Rstep == 0) - time->Rstep = time->Pstep; + // Use 1 hr pattern & report time step if none specified + if (time->Pstep <= 0) time->Pstep = 3600; + if (time->Rstep == 0) time->Rstep = time->Pstep; - /* Hydraulic time step cannot be greater than pattern or report time step */ - if (time->Hstep <= 0) - time->Hstep = 3600; - if (time->Hstep > time->Pstep) - time->Hstep = time->Pstep; - if (time->Hstep > time->Rstep) - time->Hstep = time->Rstep; + // Hydraulic time step cannot be greater than pattern or report time step + if (time->Hstep <= 0) time->Hstep = 3600; + if (time->Hstep > time->Pstep) time->Hstep = time->Pstep; + if (time->Hstep > time->Rstep) time->Hstep = time->Rstep; - /* Report start time cannot be greater than simulation duration */ - if (time->Rstart > time->Dur) - time->Rstart = 0; + // Report start time cannot be greater than simulation duration + if (time->Rstart > time->Dur) time->Rstart = 0; - /* No water quality analysis for steady state run */ - if (time->Dur == 0) - qu->Qualflag = NONE; + // No water quality analysis for single period run + if (time->Dur == 0) qual->Qualflag = NONE; - /* If no quality timestep, then make it 1/10 of hydraulic timestep */ - if (qu->Qstep == 0) - qu->Qstep = time->Hstep / 10; + // If no quality timestep, then make it 1/10 of hydraulic timestep + if (time->Qstep == 0) time->Qstep = time->Hstep / 10; - /* If no rule time step then make it 1/10 of hydraulic time step; */ - /* Rule time step cannot be greater than hydraulic time step */ - if (time->Rulestep == 0) - time->Rulestep = time->Hstep / 10; - time->Rulestep = MIN(time->Rulestep, time->Hstep); + // If no rule time step then make it 1/10 of hydraulic time step + // but not greater than hydraulic time step + if (time->Rulestep == 0) time->Rulestep = time->Hstep / 10; + time->Rulestep = MIN(time->Rulestep, time->Hstep); - /* Quality timestep cannot exceed hydraulic timestep */ - qu->Qstep = MIN(qu->Qstep, time->Hstep); + // Quality timestep cannot exceed hydraulic timestep + time->Qstep = MIN(time->Qstep, time->Hstep); - /* If no quality tolerance, then use default values */ - if (qu->Ctol == MISSING) { - if (qu->Qualflag == AGE) - qu->Ctol = AGETOL; - else - qu->Ctol = CHEMTOL; - } - - /* Determine unit system based on flow units */ - switch (par->Flowflag) { - case LPS: /* Liters/sec */ - case LPM: /* Liters/min */ - case MLD: /* megaliters/day */ - case CMH: /* cubic meters/hr */ - case CMD: /* cubic meters/day */ - par->Unitsflag = SI; - break; - default: - par->Unitsflag = US; - } - - /* Revise pressure units depending on flow units */ - if (par->Unitsflag != SI) - par->Pressflag = PSI; - else if (par->Pressflag == PSI) - par->Pressflag = METERS; - - /* Store value of viscosity & diffusivity */ - ucf = 1.0; - if (par->Unitsflag == SI) - ucf = SQR(MperFT); - - if (hyd->Viscos == MISSING) /* No value supplied */ - hyd->Viscos = VISCOS; - else if (hyd->Viscos > 1.e-3) /* Multiplier supplied */ - hyd->Viscos = hyd->Viscos * VISCOS; - else /* Actual value supplied */ - hyd->Viscos = hyd->Viscos / ucf; - - if (qu->Diffus == MISSING) - qu->Diffus = DIFFUS; - else if (qu->Diffus > 1.e-4) - qu->Diffus = qu->Diffus * DIFFUS; - else - qu->Diffus = qu->Diffus / ucf; - - /* - Set exponent in head loss equation and adjust flow-resistance tolerance. - */ - if (hyd->Formflag == HW) - hyd->Hexp = 1.852; - else - hyd->Hexp = 2.0; - - /*** Updated 9/7/00 ***/ - /*** No adjustment made to flow-resistance tolerance ***/ - /*hyd->RQtol = hyd->RQtol/Hexp;*/ - - /* See if default reaction coeffs. apply */ - for (i = 1; i <= net->Nlinks; i++) { - Slink *link = &net->Link[i]; - if (link->Type > PIPE) - continue; - if (link->Kb == MISSING) - link->Kb = qu->Kbulk; /* Bulk coeff. */ - if (link->Kw == MISSING) /* Wall coeff. */ + // If no quality tolerance, then use default values + if (qual->Ctol == MISSING) { - /* Rfactor is the pipe roughness correlation factor */ - if (qu->Rfactor == 0.0) - link->Kw = qu->Kwall; - else if ((link->Kc > 0.0) && (link->Diam > 0.0)) { - if (hyd->Formflag == HW) - link->Kw = qu->Rfactor / link->Kc; - if (hyd->Formflag == DW) - link->Kw = qu->Rfactor / ABS(log(link->Kc / link->Diam)); - if (hyd->Formflag == CM) - link->Kw = qu->Rfactor * link->Kc; - } else - link->Kw = 0.0; + if (qual->Qualflag == AGE) qual->Ctol = AGETOL; + else qual->Ctol = CHEMTOL; } - } - for (i = 1; i <= net->Ntanks; i++) { - Stank *tank = &net->Tank[i]; - if (tank->Kb == MISSING) { - tank->Kb = qu->Kbulk; + + // Determine units system based on flow units + switch (parser->Flowflag) + { + case LPS: // Liters/sec + case LPM: // Liters/min + case MLD: // megaliters/day + case CMH: // cubic meters/hr + case CMD: // cubic meters/day + parser->Unitsflag = SI; + break; + default: + parser->Unitsflag = US; } - } - /* Use default pattern if none assigned to a demand */ - for (i = 1; i <= net->Nnodes; i++) { - Snode *node = &net->Node[i]; - for (demand = node->D; demand != NULL; demand = demand->next) { - if (demand->Pat == 0) { - demand->Pat = hyd->DefPat; - strcpy(demand->Name, ""); - } + + // Revise pressure units depending on flow units + if (parser->Unitsflag != SI) parser->Pressflag = PSI; + else if (parser->Pressflag == PSI) parser->Pressflag = METERS; + + // Store value of viscosity & diffusivity + ucf = 1.0; + if (parser->Unitsflag == SI) ucf = SQR(MperFT); + if (hyd->Viscos == MISSING) + { + hyd->Viscos = VISCOS; // No viscosity supplied } - } + else if (hyd->Viscos > 1.e-3) + { + hyd->Viscos = hyd->Viscos * VISCOS; // Multiplier supplied + } + else hyd->Viscos = hyd->Viscos / ucf; // Actual value supplied + if (qual->Diffus == MISSING) + { + qual->Diffus = DIFFUS; // No viscosity supplied + } + else if (qual->Diffus > 1.e-4) + { + qual->Diffus = qual->Diffus * DIFFUS; // Multiplier supplied + } + else qual->Diffus = qual->Diffus / ucf; // Actual value supplied - /* Remove QUALITY as a reporting variable if no WQ analysis */ - if (qu->Qualflag == NONE) - rep->Field[QUALITY].Enabled = FALSE; + // Set exponent in head loss equation and adjust flow-resistance tolerance. + if (hyd->Formflag == HW) hyd->Hexp = 1.852; + else hyd->Hexp = 2.0; -} /* End of adjustdata */ + // See if default reaction coeffs. apply + for (i = 1; i <= net->Nlinks; i++) + { + link = &net->Link[i]; + if (link->Type > PIPE) continue; + if (link->Kb == MISSING) link->Kb = qual->Kbulk; // Bulk coeff. + if (link->Kw == MISSING) // Wall coeff. + { + // Rfactor is the pipe roughness correlation factor + if (qual->Rfactor == 0.0) link->Kw = qual->Kwall; + else if ((link->Kc > 0.0) && (link->Diam > 0.0)) + { + if (hyd->Formflag == HW) link->Kw = qual->Rfactor / link->Kc; + if (hyd->Formflag == DW) + { + link->Kw = qual->Rfactor / ABS(log(link->Kc / link->Diam)); + } + if (hyd->Formflag == CM) link->Kw = qual->Rfactor * link->Kc; + } + else link->Kw = 0.0; + } + } + for (i = 1; i <= net->Ntanks; i++) + { + tank = &net->Tank[i]; + if (tank->Kb == MISSING) tank->Kb = qual->Kbulk; + } -int inittanks(EN_Project *pr) + // Use default pattern if none assigned to a demand + 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; + strcpy(demand->Name, ""); + } + } + } + + // Remove QUALITY as a reporting variable if no WQ analysis + if (qual->Qualflag == NONE) rpt->Field[QUALITY].Enabled = FALSE; +} + +int inittanks(Project *pr) /* **--------------------------------------------------------------- ** Input: none @@ -374,59 +352,62 @@ int inittanks(EN_Project *pr) **--------------------------------------------------------------- */ { - EN_Network *net = &pr->network; + Network *net = &pr->network; - int i, j, n = 0; - double a; - int errcode = 0, levelerr; + int i, j, n = 0; + double a; + int errcode = 0, levelerr; + Stank *tank; + Scurve *curve; - for (j = 1; j <= net->Ntanks; j++) { - Stank *tank = &net->Tank[j]; - /* Skip reservoirs */ - if (tank->A == 0.0) - continue; + for (j = 1; j <= net->Ntanks; j++) + { + tank = &net->Tank[j]; + if (tank->A == 0.0) continue; // Skip reservoirs - /* Check for valid lower/upper tank levels */ - levelerr = 0; - if (tank->H0 > tank->Hmax || tank->Hmin > tank->Hmax || - tank->H0 < tank->Hmin) { - levelerr = 1; + // Check for valid lower/upper tank levels + levelerr = 0; + if (tank->H0 > tank->Hmax || + tank->Hmin > tank->Hmax || + tank->H0 < tank->Hmin + ) levelerr = 1; + + // Check that tank heights are within volume curve + i = tank->Vcurve; + if (i > 0) + { + curve = &net->Curve[i]; + n = curve->Npts - 1; + if (tank->Hmin < curve->X[0] || tank->Hmax > curve->X[n]) + { + levelerr = 1; + } + + // Report error in levels if found + if (levelerr) + { + sprintf(pr->Msg, "%s node: %s", geterrmsg(225, pr->Msg), + net->Node[tank->Node].ID); + writeline(pr, pr->Msg); + errcode = 200; + } + else + { + // Find min., max., and initial volumes from curve + tank->Vmin = interp(curve->Npts, curve->X, curve->Y, tank->Hmin); + tank->Vmax = interp(curve->Npts, curve->X, curve->Y, tank->Hmax); + tank->V0 = interp(curve->Npts, curve->X, curve->Y, tank->H0); + + // Find a "nominal" diameter for tank + a = (curve->Y[n] - curve->Y[0]) / (curve->X[n] - curve->X[0]); + tank->A = sqrt(4.0 * a / PI); + } + } } + return errcode; +} - /* Check that tank heights are within volume curve */ - i = tank->Vcurve; - if (i > 0) { - Scurve *curve = &net->Curve[i]; - n = curve->Npts - 1; - if (tank->Hmin < curve->X[0] || tank->Hmax > curve->X[n]) - levelerr = 1; - } - - /* Report error in levels if found */ - if (levelerr) { - sprintf(pr->Msg, "%s node: %s", geterrmsg(225, pr->Msg), - net->Node[tank->Node].ID); - writeline(pr, pr->Msg); - errcode = 200; - } - - /* Else if tank has a volume curve, */ - else if (i > 0) { - Scurve *curve = &net->Curve[i]; - /* Find min., max., and initial volumes from curve */ - tank->Vmin = interp(curve->Npts, curve->X, curve->Y, tank->Hmin); - tank->Vmax = interp(curve->Npts, curve->X, curve->Y, tank->Hmax); - tank->V0 = interp(curve->Npts, curve->X, curve->Y, tank->H0); - - /* Find a "nominal" diameter for tank */ - a = (curve->Y[n] - curve->Y[0]) / (curve->X[n] - curve->X[0]); - tank->A = sqrt(4.0 * a / PI); - } - } - return (errcode); -} /* End of inittanks */ - -void initunits(EN_Project *pr) +void initunits(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -435,116 +416,113 @@ void initunits(EN_Project *pr) **-------------------------------------------------------------- */ { - parser_data_t *par = &pr->parser; - report_options_t *rep = &pr->report; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qu = &pr->quality; - time_options_t *time = &pr->time_options; + Parser *parser = &pr->parser; + Report *rpt = &pr->report; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Times *time = &pr->times; - double dcf, /* distance conversion factor */ - ccf, /* concentration conversion factor */ - qcf, /* flow conversion factor */ - hcf, /* head conversion factor */ - pcf, /* pressure conversion factor */ - wcf; /* energy conversion factor */ + double dcf, // distance conversion factor + ccf, // concentration conversion factor + qcf, // flow conversion factor + hcf, // head conversion factor + pcf, // pressure conversion factor + wcf; // energy conversion factor - if (par->Unitsflag == SI) /* SI units */ - { - strcpy(rep->Field[DEMAND].Units, RptFlowUnitsTxt[par->Flowflag]); - strcpy(rep->Field[ELEV].Units, u_METERS); - strcpy(rep->Field[HEAD].Units, u_METERS); - if (par->Pressflag == METERS) - strcpy(rep->Field[PRESSURE].Units, u_METERS); + if (parser->Unitsflag == SI) // SI units + { + strcpy(rpt->Field[DEMAND].Units, RptFlowUnitsTxt[parser->Flowflag]); + strcpy(rpt->Field[ELEV].Units, u_METERS); + strcpy(rpt->Field[HEAD].Units, u_METERS); + if (parser->Pressflag == METERS) strcpy(rpt->Field[PRESSURE].Units, u_METERS); + else strcpy(rpt->Field[PRESSURE].Units, u_KPA); + strcpy(rpt->Field[LENGTH].Units, u_METERS); + strcpy(rpt->Field[DIAM].Units, u_MMETERS); + strcpy(rpt->Field[FLOW].Units, RptFlowUnitsTxt[parser->Flowflag]); + strcpy(rpt->Field[VELOCITY].Units, u_MperSEC); + strcpy(rpt->Field[HEADLOSS].Units, u_per1000M); + strcpy(rpt->Field[FRICTION].Units, ""); + strcpy(rpt->Field[POWER].Units, u_KW); + + dcf = 1000.0 * MperFT; + qcf = LPSperCFS; + if (parser->Flowflag == LPM) qcf = LPMperCFS; + if (parser->Flowflag == MLD) qcf = MLDperCFS; + if (parser->Flowflag == CMH) qcf = CMHperCFS; + if (parser->Flowflag == CMD) qcf = CMDperCFS; + + hcf = MperFT; + if (parser->Pressflag == METERS) pcf = MperFT * hyd->SpGrav; + else pcf = KPAperPSI * PSIperFT * hyd->SpGrav; + wcf = KWperHP; + } + else // US units + { + strcpy(rpt->Field[DEMAND].Units, RptFlowUnitsTxt[parser->Flowflag]); + strcpy(rpt->Field[ELEV].Units, u_FEET); + strcpy(rpt->Field[HEAD].Units, u_FEET); + strcpy(rpt->Field[PRESSURE].Units, u_PSI); + strcpy(rpt->Field[LENGTH].Units, u_FEET); + strcpy(rpt->Field[DIAM].Units, u_INCHES); + strcpy(rpt->Field[FLOW].Units, RptFlowUnitsTxt[parser->Flowflag]); + strcpy(rpt->Field[VELOCITY].Units, u_FTperSEC); + strcpy(rpt->Field[HEADLOSS].Units, u_per1000FT); + strcpy(rpt->Field[FRICTION].Units, ""); + strcpy(rpt->Field[POWER].Units, u_HP); + + dcf = 12.0; + qcf = 1.0; + if (parser->Flowflag == GPM) qcf = GPMperCFS; + if (parser->Flowflag == MGD) qcf = MGDperCFS; + if (parser->Flowflag == IMGD) qcf = IMGDperCFS; + if (parser->Flowflag == AFD) qcf = AFDperCFS; + hcf = 1.0; + pcf = PSIperFT * hyd->SpGrav; + wcf = 1.0; + } + + strcpy(rpt->Field[QUALITY].Units, ""); + ccf = 1.0; + if (qual->Qualflag == CHEM) + { + ccf = 1.0 / LperFT3; + strncpy(rpt->Field[QUALITY].Units, qual->ChemUnits, MAXID); + strncpy(rpt->Field[REACTRATE].Units, qual->ChemUnits, MAXID); + strcat(rpt->Field[REACTRATE].Units, t_PERDAY); + } + else if (qual->Qualflag == AGE) strcpy(rpt->Field[QUALITY].Units, u_HOURS); + else if (qual->Qualflag == TRACE) strcpy(rpt->Field[QUALITY].Units, u_PERCENT); + + pr->Ucf[DEMAND] = qcf; + pr->Ucf[ELEV] = hcf; + pr->Ucf[HEAD] = hcf; + pr->Ucf[PRESSURE] = pcf; + pr->Ucf[QUALITY] = ccf; + pr->Ucf[LENGTH] = hcf; + pr->Ucf[DIAM] = dcf; + pr->Ucf[FLOW] = qcf; + pr->Ucf[VELOCITY] = hcf; + pr->Ucf[HEADLOSS] = hcf; + pr->Ucf[LINKQUAL] = ccf; + pr->Ucf[REACTRATE] = ccf; + pr->Ucf[FRICTION] = 1.0; + pr->Ucf[POWER] = wcf; + pr->Ucf[VOLUME] = hcf * hcf * hcf; + + // Report time in minutes if hyd. time step < 1/2 hr. + if (time->Hstep < 1800) + { + pr->Ucf[TIME] = 1.0 / 60.0; + strcpy(rpt->Field[TIME].Units, u_MINUTES); + } else - strcpy(rep->Field[PRESSURE].Units, u_KPA); - strcpy(rep->Field[LENGTH].Units, u_METERS); - strcpy(rep->Field[DIAM].Units, u_MMETERS); - strcpy(rep->Field[FLOW].Units, RptFlowUnitsTxt[par->Flowflag]); - strcpy(rep->Field[VELOCITY].Units, u_MperSEC); - strcpy(rep->Field[HEADLOSS].Units, u_per1000M); - strcpy(rep->Field[FRICTION].Units, ""); - strcpy(rep->Field[POWER].Units, u_KW); - dcf = 1000.0 * MperFT; - qcf = LPSperCFS; - if (par->Flowflag == LPM) - qcf = LPMperCFS; - if (par->Flowflag == MLD) - qcf = MLDperCFS; - if (par->Flowflag == CMH) - qcf = CMHperCFS; - if (par->Flowflag == CMD) - qcf = CMDperCFS; - hcf = MperFT; - if (par->Pressflag == METERS) - pcf = MperFT * hyd->SpGrav; - else - pcf = KPAperPSI * PSIperFT * hyd->SpGrav; - wcf = KWperHP; - } else /* US units */ - { - strcpy(rep->Field[DEMAND].Units, RptFlowUnitsTxt[par->Flowflag]); - strcpy(rep->Field[ELEV].Units, u_FEET); - strcpy(rep->Field[HEAD].Units, u_FEET); - strcpy(rep->Field[PRESSURE].Units, u_PSI); - strcpy(rep->Field[LENGTH].Units, u_FEET); - strcpy(rep->Field[DIAM].Units, u_INCHES); - strcpy(rep->Field[FLOW].Units, RptFlowUnitsTxt[par->Flowflag]); - strcpy(rep->Field[VELOCITY].Units, u_FTperSEC); - strcpy(rep->Field[HEADLOSS].Units, u_per1000FT); - strcpy(rep->Field[FRICTION].Units, ""); - strcpy(rep->Field[POWER].Units, u_HP); - dcf = 12.0; - qcf = 1.0; - if (par->Flowflag == GPM) - qcf = GPMperCFS; - if (par->Flowflag == MGD) - qcf = MGDperCFS; - if (par->Flowflag == IMGD) - qcf = IMGDperCFS; - if (par->Flowflag == AFD) - qcf = AFDperCFS; - hcf = 1.0; - pcf = PSIperFT * hyd->SpGrav; - wcf = 1.0; - } - strcpy(rep->Field[QUALITY].Units, ""); - ccf = 1.0; - if (qu->Qualflag == CHEM) { - ccf = 1.0 / LperFT3; - strncpy(rep->Field[QUALITY].Units, qu->ChemUnits, MAXID); - strncpy(rep->Field[REACTRATE].Units, qu->ChemUnits, MAXID); - strcat(rep->Field[REACTRATE].Units, t_PERDAY); - } else if (qu->Qualflag == AGE) - strcpy(rep->Field[QUALITY].Units, u_HOURS); - else if (qu->Qualflag == TRACE) - strcpy(rep->Field[QUALITY].Units, u_PERCENT); - pr->Ucf[DEMAND] = qcf; - pr->Ucf[ELEV] = hcf; - pr->Ucf[HEAD] = hcf; - pr->Ucf[PRESSURE] = pcf; - pr->Ucf[QUALITY] = ccf; - pr->Ucf[LENGTH] = hcf; - pr->Ucf[DIAM] = dcf; - pr->Ucf[FLOW] = qcf; - pr->Ucf[VELOCITY] = hcf; - pr->Ucf[HEADLOSS] = hcf; - pr->Ucf[LINKQUAL] = ccf; - pr->Ucf[REACTRATE] = ccf; - pr->Ucf[FRICTION] = 1.0; - pr->Ucf[POWER] = wcf; - pr->Ucf[VOLUME] = hcf * hcf * hcf; - if (time->Hstep < 1800) /* Report time in mins. */ - { /* if hydraulic time step */ - pr->Ucf[TIME] = 1.0 / 60.0; /* is less than 1/2 hour. */ - strcpy(rep->Field[TIME].Units, u_MINUTES); - } else { - pr->Ucf[TIME] = 1.0 / 3600.0; - strcpy(rep->Field[TIME].Units, u_HOURS); - } + { + pr->Ucf[TIME] = 1.0 / 3600.0; + strcpy(rpt->Field[TIME].Units, u_HOURS); + } +} -} /* End of initunits */ - -void convertunits(EN_Project *pr) +void convertunits(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -553,178 +531,179 @@ void convertunits(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qu = &pr->quality; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Parser *parser = &pr->parser; - int i, j, k; - double ucf; /* Unit conversion factor */ - Pdemand demand; /* Pointer to demand record */ - Snode *node; - Stank *tank; - Slink *link; - Spump *pump; - Scontrol *control; + int i, j, k; + double ucf; // Unit conversion factor + Pdemand demand; // Pointer to demand record + Snode *node; + Stank *tank; + 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++) { - node = &net->Node[i]; - node->El /= pr->Ucf[ELEV]; - node->C0 /= pr->Ucf[QUALITY]; - } - - /* Convert demands */ - for (i = 1; i <= net->Njuncs; i++) { - node = &net->Node[i]; - for (demand = node->D; demand != NULL; demand = demand->next) { - demand->Base /= pr->Ucf[DEMAND]; - } - } - hyd->Pmin /= pr->Ucf[PRESSURE]; - hyd->Preq /= pr->Ucf[PRESSURE]; - - /* Convert emitter discharge coeffs. to head loss coeff. */ - ucf = pow(pr->Ucf[FLOW], hyd->Qexp) / pr->Ucf[PRESSURE]; - for (i = 1; i <= net->Njuncs; i++) { - node = &net->Node[i]; - if (node->Ke > 0.0) { - node->Ke = ucf / pow(node->Ke, hyd->Qexp); - } - } - /* Initialize tank variables (convert tank levels to elevations) */ - for (j = 1; j <= net->Ntanks; j++) { - tank = &net->Tank[j]; - i = tank->Node; - node = &net->Node[i]; - tank->H0 = node->El + tank->H0 / pr->Ucf[ELEV]; - tank->Hmin = node->El + tank->Hmin / pr->Ucf[ELEV]; - tank->Hmax = node->El + tank->Hmax / pr->Ucf[ELEV]; - tank->A = PI * SQR(tank->A / pr->Ucf[ELEV]) / 4.0; - tank->V0 /= pr->Ucf[VOLUME]; - tank->Vmin /= pr->Ucf[VOLUME]; - tank->Vmax /= pr->Ucf[VOLUME]; - tank->Kb /= SECperDAY; - tank->V = tank->V0; - tank->C = node->C0; - tank->V1max *= tank->Vmax; - } - - /* Convert hydraulic convergence criteria */ - hyd->FlowChangeLimit /= pr->Ucf[FLOW]; - hyd->HeadErrorLimit /= pr->Ucf[HEAD]; - - /* Convert WQ option concentration units */ - qu->Climit /= pr->Ucf[QUALITY]; - qu->Ctol /= pr->Ucf[QUALITY]; - - /* Convert global reaction coeffs. */ - qu->Kbulk /= SECperDAY; - qu->Kwall /= SECperDAY; - - /* Convert units of link parameters */ - for (k = 1; k <= net->Nlinks; k++) { - link = &net->Link[k]; - if (link->Type <= PIPE) { - /* Convert pipe parameter units: */ - /* - for Darcy-Weisbach formula, convert roughness */ - /* from millifeet (or mm) to ft (or m) */ - /* - for US units, convert diameter from inches to ft */ - if (hyd->Formflag == DW) - link->Kc /= (1000.0 * pr->Ucf[ELEV]); - link->Diam /= pr->Ucf[DIAM]; - link->Len /= pr->Ucf[LENGTH]; - - /* Convert minor loss coeff. from V^2/2g basis to Q^2 basis */ - link->Km = 0.02517 * link->Km / SQR(link->Diam) / SQR(link->Diam); - - /* Convert units on reaction coeffs. */ - link->Kb /= SECperDAY; - link->Kw /= SECperDAY; + // Convert nodal elevations & initial WQ + // (WQ source units are converted in QUALITY.C + for (i = 1; i <= net->Nnodes; i++) + { + node = &net->Node[i]; + node->El /= pr->Ucf[ELEV]; + node->C0 /= pr->Ucf[QUALITY]; } - else if (link->Type == PUMP) { - /* Convert units for pump curve parameters */ - i = findpump(net, k); - pump = &net->Pump[i]; - if (pump->Ptype == CONST_HP) { - /* For constant hp pump, convert kw to hp */ - if (par->Unitsflag == SI) - pump->R /= pr->Ucf[POWER]; - } else { - /* For power curve pumps, convert */ - /* shutoff head and flow coefficient */ - if (pump->Ptype == POWER_FUNC) { - pump->H0 /= pr->Ucf[HEAD]; - pump->R *= (pow(pr->Ucf[FLOW], pump->N) / pr->Ucf[HEAD]); + // Convert demands + for (i = 1; i <= net->Njuncs; i++) + { + node = &net->Node[i]; + for (demand = node->D; demand != NULL; demand = demand->next) + { + demand->Base /= pr->Ucf[DEMAND]; } - /* Convert flow range & max. head units */ - pump->Q0 /= pr->Ucf[FLOW]; - pump->Qmax /= pr->Ucf[FLOW]; - pump->Hmax /= pr->Ucf[HEAD]; - } + } + hyd->Pmin /= pr->Ucf[PRESSURE]; + hyd->Preq /= pr->Ucf[PRESSURE]; + + // Convert emitter discharge coeffs. to head loss coeff. + ucf = pow(pr->Ucf[FLOW], hyd->Qexp) / pr->Ucf[PRESSURE]; + for (i = 1; i <= net->Njuncs; i++) + { + node = &net->Node[i]; + if (node->Ke > 0.0) node->Ke = ucf / pow(node->Ke, hyd->Qexp); } - else { - /* 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); - if (link->Kc != MISSING) - switch (link->Type) { - case FCV: - link->Kc /= pr->Ucf[FLOW]; - break; - case PRV: - case PSV: - case PBV: - link->Kc /= pr->Ucf[PRESSURE]; - break; - default: - break; + // Initialize tank variables (convert tank levels to elevations) + for (j = 1; j <= net->Ntanks; j++) + { + tank = &net->Tank[j]; + i = tank->Node; + node = &net->Node[i]; + tank->H0 = node->El + tank->H0 / pr->Ucf[ELEV]; + tank->Hmin = node->El + tank->Hmin / pr->Ucf[ELEV]; + tank->Hmax = node->El + tank->Hmax / pr->Ucf[ELEV]; + tank->A = PI * SQR(tank->A / pr->Ucf[ELEV]) / 4.0; + tank->V0 /= pr->Ucf[VOLUME]; + tank->Vmin /= pr->Ucf[VOLUME]; + tank->Vmax /= pr->Ucf[VOLUME]; + tank->Kb /= SECperDAY; + tank->V = tank->V0; + tank->C = node->C0; + tank->V1max *= tank->Vmax; + } + + // Convert hydraulic convergence criteria + hyd->FlowChangeLimit /= pr->Ucf[FLOW]; + hyd->HeadErrorLimit /= pr->Ucf[HEAD]; + + // Convert water quality concentration options + qual->Climit /= pr->Ucf[QUALITY]; + qual->Ctol /= pr->Ucf[QUALITY]; + + // Convert global reaction coeffs. + qual->Kbulk /= SECperDAY; + qual->Kwall /= SECperDAY; + + // Convert units of link parameters + for (k = 1; k <= net->Nlinks; k++) + { + link = &net->Link[k]; + if (link->Type <= PIPE) + { + // Convert D-W roughness from millifeet (or mm) to ft + if (hyd->Formflag == DW) link->Kc /= (1000.0 * pr->Ucf[ELEV]); + link->Diam /= pr->Ucf[DIAM]; + link->Len /= pr->Ucf[LENGTH]; + + // Convert minor loss coeff. from V^2/2g basis to Q^2 basis + link->Km = 0.02517 * link->Km / SQR(link->Diam) / SQR(link->Diam); + + // Convert units on reaction coeffs. + link->Kb /= SECperDAY; + link->Kw /= SECperDAY; + } + + else if (link->Type == PUMP) + { + // Convert units for pump curve parameters + i = findpump(net, k); + pump = &net->Pump[i]; + if (pump->Ptype == CONST_HP) + { + // For constant hp pump, convert kw to hp + if (parser->Unitsflag == SI) pump->R /= pr->Ucf[POWER]; + } + else + { + // For power curve pumps, convert shutoff head and flow coeff. + if (pump->Ptype == POWER_FUNC) + { + 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]; + pump->Hmax /= pr->Ucf[HEAD]; + } + } + else + { + // 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); + if (link->Kc != MISSING) switch (link->Type) + { + case FCV: + link->Kc /= pr->Ucf[FLOW]; + break; + case PRV: + case PSV: + case PBV: + link->Kc /= pr->Ucf[PRESSURE]; + break; + default: + break; + } } - } -////// Moved to inithyd() in hydraul.c /////// - /* Compute flow resistances */ - //resistance(pr, k); - } + // Convert units on control settings + for (i = 1; i <= net->Ncontrols; i++) + { + control = &net->Control[i]; + if ((k = control->Link) == 0) continue; + link = &net->Link[k]; + if ((j = control->Node) > 0) + { + node = &net->Node[j]; + // control is based on tank level + if (j > net->Njuncs) + { + control->Grade = node->El + control->Grade / pr->Ucf[ELEV]; + } + // control is based on nodal pressure + else control->Grade = node->El + control->Grade / pr->Ucf[PRESSURE]; + } - /* Convert units on control settings */ - for (i = 1; i <= net->Ncontrols; i++) { - control = &net->Control[i]; - if ((k = control->Link) == 0) { - continue; + // Convert units on valve settings + if (control->Setting != MISSING) + { + switch (link->Type) + { + case PRV: + case PSV: + case PBV: + control->Setting /= pr->Ucf[PRESSURE]; + break; + case FCV: + control->Setting /= pr->Ucf[FLOW]; + default: + break; + } + } } - link = &net->Link[k]; - if ((j = control->Node) > 0) { - node = &net->Node[j]; - if (j > net->Njuncs) { - /* j > Njuncs, then control is based on tank level */ - control->Grade = node->El + control->Grade / pr->Ucf[ELEV]; - } else { - /* otherwise control is based on nodal pressure */ - control->Grade = node->El + control->Grade / pr->Ucf[PRESSURE]; - } - } - - /* Convert units on valve settings */ - if (control->Setting != MISSING) { - switch (link->Type) { - case PRV: - case PSV: - case PBV: - control->Setting /= pr->Ucf[PRESSURE]; - break; - case FCV: - control->Setting /= pr->Ucf[FLOW]; - default: - break; - } - } - } -} /* End of convertunits */ - -/************************ END OF INPUT1.C ************************/ +} diff --git a/src/input2.c b/src/input2.c index 846c0a1..02470d8 100644 --- a/src/input2.c +++ b/src/input2.c @@ -1,28 +1,14 @@ /* -********************************************************************** - -INPUT2.C -- Input data file interpreter for EPANET - -VERSION: 2.00 -DATE: 5/30/00 - 9/7/00 - 10/25/00 -AUTHOR: L. Rossman - US EPA - NRMRL - -This module reads and interprets the input data from file InFile. - -The entry points for this module are: - netsize() -- called from ENopen() in EPANET.C - readdata() -- called from getdata() in INPUT1.C - -The following utility functions are all called from INPUT3.C - addnodeID() - addlinkID() - findID() - getfloat() - -********************************************************************** +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: input2.c +Description: reads and interprets network data from an EPANET input file +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/10/2018 +****************************************************************************** */ #include @@ -38,117 +24,111 @@ The following utility functions are all called from INPUT3.C #include "hash.h" #include "text.h" -#define MAXERRS 10 /* Max. input errors reported */ +#define MAXERRS 10 // Max. input errors reported -/* Defined in enumstxt.h in EPANET.C */ -extern char *SectTxt[]; /* Input section keywords */ +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 *, + double *, double *); + +// Local functions +static int newline(Project *, int, char *); +static int addpattern(Parser *, char *); +static int addcurve(Parser *, char *); +static int unlinked(Project *); +static int getpumpparams(Project *); +static void inperrmsg(Project *, int, int, char *); -int netsize(EN_Project *pr) +int netsize(Project *pr) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code -** Purpose: determines number of system components +** Purpose: determines number of network objects **-------------------------------------------------------------- */ { - parser_data_t *par = &pr->parser; + 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 */ + 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 - /* Initialize network component counts */ - par->MaxJuncs = 0; - par->MaxTanks = 0; - par->MaxPipes = 0; - par->MaxPumps = 0; - par->MaxValves = 0; - par->MaxControls = 0; - par->MaxRules = 0; - par->MaxCurves = 0; - sect = -1; + // Initialize object counts + parser->MaxJuncs = 0; + parser->MaxTanks = 0; + parser->MaxPipes = 0; + parser->MaxPumps = 0; + parser->MaxValves = 0; + parser->MaxControls = 0; + parser->MaxRules = 0; + parser->MaxCurves = 0; + sect = -1; - /* Add a default pattern 0 */ - par->MaxPats = -1; - addpattern(par,""); + // Add a default demand pattern + parser->MaxPats = -1; + addpattern(parser,""); - if (par->InFile == NULL) { - return (0); - } + if (parser->InFile == NULL) return 0; - /* Make pass through data file counting number of each component */ - while (fgets(line, MAXLINE, par->InFile) != NULL) { - /* Skip blank lines & those beginning with a comment */ - tok = strtok(line, SEPSTR); - if (tok == NULL) - continue; - if (*tok == ';') - continue; + // Make a pass through input file counting number of each object + while (fgets(line, MAXLINE, parser->InFile) != NULL) + { + // Skip blank lines & those beginning with a comment + tok = strtok(line, SEPSTR); + if (tok == NULL) continue; + if (*tok == ';') continue; - /* Check if line begins with a new section heading */ - if (tok[0] == '[') { - newsect = findmatch(tok, SectTxt); - if (newsect >= 0) { - sect = newsect; - if (sect == _END) - break; - continue; - } else - continue; + // Check if line begins with a new section heading + if (tok[0] == '[') + { + newsect = findmatch(tok, SectTxt); + if (newsect >= 0) + { + sect = newsect; + if (sect == _END) break; + continue; + } + else continue; + } + + // Add to count of current object + switch (sect) + { + case _JUNCTIONS: parser->MaxJuncs++; break; + case _RESERVOIRS: + case _TANKS: parser->MaxTanks++; break; + case _PIPES: parser->MaxPipes++; break; + case _PUMPS: parser->MaxPumps++; break; + 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; + } + if (errcode) break; } - /* Add to count of current component */ - switch (sect) { - case _JUNCTIONS: - par->MaxJuncs++; - break; - case _RESERVOIRS: - case _TANKS: - par->MaxTanks++; - break; - case _PIPES: - par->MaxPipes++; - break; - case _PUMPS: - par->MaxPumps++; - break; - case _VALVES: - par->MaxValves++; - break; - case _CONTROLS: - par->MaxControls++; - break; - case _RULES: - addrule(par,tok); - break; /* See RULES.C */ - case _PATTERNS: - errcode = addpattern(par, tok); - break; - case _CURVES: - errcode = addcurve(par, tok); - break; + parser->MaxNodes = parser->MaxJuncs + parser->MaxTanks; + parser->MaxLinks = parser->MaxPipes + parser->MaxPumps + parser->MaxValves; + if (parser->MaxPats < 1) parser->MaxPats = 1; + if (!errcode) + { + if (parser->MaxJuncs < 1) errcode = 223; // Not enough nodes + else if (parser->MaxTanks == 0) errcode = 224; // No tanks } - if (errcode) - break; - } + return errcode; +} - par->MaxNodes = par->MaxJuncs + par->MaxTanks; - par->MaxLinks = par->MaxPipes + par->MaxPumps + par->MaxValves; - if (par->MaxPats < 1) - par->MaxPats = 1; - if (!errcode) { - if (par->MaxJuncs < 1) - errcode = 223; /* Not enough nodes */ - else if (par->MaxTanks == 0) - errcode = 224; /* No tanks */ - } - return (errcode); -} /* End of netsize */ - -int readdata(EN_Project *pr) +int readdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -157,23 +137,22 @@ int readdata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Parser *parser = &pr->parser; - char line[MAXLINE + 1], /* Line from input data file */ - wline[MAXLINE + 1]; /* Working copy of input line */ - int sect, newsect, /* Data sections */ - errcode = 0, /* Error code */ - inperr, errsum; /* Error code & total error count */ + char line[MAXLINE + 1], // Line from input data file + wline[MAXLINE + 1]; // Working copy of input line + int sect, newsect, // Data sections + errcode = 0, // Error code + inperr, errsum; // Error code & total error count - /* Allocate input buffer */ - par->X = (double *)calloc(MAXTOKS, sizeof(double)); - ERRCODE(MEMCHECK(par->X)); + // Allocate input buffer + parser->X = (double *)calloc(MAXTOKS, sizeof(double)); + ERRCODE(MEMCHECK(parser->X)); + if (errcode) return errcode; - if (!errcode) { - - /* Initialize number of network components */ - par->Ntitle = 0; + // Initialize actual number of network components + parser->Ntitle = 0; net->Nnodes = 0; net->Njuncs = 0; net->Ntanks = 0; @@ -183,185 +162,161 @@ int readdata(EN_Project *pr) net->Nvalves = 0; net->Ncontrols = 0; net->Nrules = 0; - net->Ncurves = par->MaxCurves; - net->Npats = par->MaxPats; - par->PrevPat = NULL; - par->PrevCurve = NULL; + net->Ncurves = parser->MaxCurves; + net->Npats = parser->MaxPats; + parser->PrevPat = NULL; + parser->PrevCurve = NULL; sect = -1; errsum = 0; - /* Read each line from input file. */ - while (fgets(line, MAXLINE, par->InFile) != NULL) { + // Read each line from input file + while (fgets(line, MAXLINE, parser->InFile) != NULL) + { + // Make copy of line and scan for tokens + strcpy(wline, line); + parser->Ntokens = gettokens(wline, parser->Tok, MAXTOKS, + parser->Comment); - /* Make copy of line and scan for tokens */ - strcpy(wline, line); - par->Ntokens = gettokens(wline, par->Tok, MAXTOKS, par->Comment); + // Skip blank lines and comments + if (parser->Ntokens == 0) continue; + if (*parser->Tok[0] == ';') continue; - /* Skip blank lines and comments */ - if (par->Ntokens == 0) - continue; - if (*par->Tok[0] == ';') - continue; - - /* Check if max. length exceeded */ - if (strlen(line) >= MAXLINE) { - sprintf(pr->Msg, "%s section: %s", geterrmsg(214, pr->Msg), SectTxt[sect]); - writeline(pr, pr->Msg); - writeline(pr, line); - errsum++; - } - - /* Check if at start of a new input section */ - if (par->Tok[0][0] == '[') { - newsect = findmatch(par->Tok[0], SectTxt); - if (newsect >= 0) { - sect = newsect; - if (sect == _END) - break; - continue; - } else { - inperrmsg(pr, 201, sect, line); - errsum++; - break; - } - } - - /* Otherwise process next line of input in current section */ - else { - if (sect >= 0) // for cases were no section is present on the top of the - // input file + // Check if max. line length exceeded + if (strlen(line) >= MAXLINE) { - inperr = newline(pr, sect, line); - if (inperr > 0) { - inperrmsg(pr,inperr, sect, line); + sprintf(pr->Msg, "%s section: %s", geterrmsg(214, pr->Msg), + SectTxt[sect]); + writeline(pr, pr->Msg); + writeline(pr, line); errsum++; - } - } else { - errcode = 200; - break; } - } - /* Stop if reach end of file or max. error count */ - if (errsum == MAXERRS) - break; - } /* End of while */ + // Check if at start of a new input section + if (parser->Tok[0][0] == '[') + { + newsect = findmatch(parser->Tok[0], SectTxt); + if (newsect >= 0) + { + sect = newsect; + if (sect == _END) break; + continue; + } + else + { + inperrmsg(pr, 201, sect, line); + errsum++; + break; + } + } - /* Check for errors */ - if (errsum > 0) - errcode = 200; - } + // Otherwise process next line of input in current section + else + { + if (sect >= 0) + { + inperr = newline(pr, sect, line); + if (inperr > 0) + { + inperrmsg(pr,inperr, sect, line); + errsum++; + } + } + else + { + errcode = 200; + break; + } + } - /* Check for unlinked nodes */ - if (!errcode) - errcode = unlinked(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 temp. lists */ - if (!errcode) - errcode = getpatterns(pr); - if (!errcode) - errcode = getcurves(pr); - if (!errcode) - errcode = getpumpparams(pr); + // Get pattern & curve data from temporary lists + if (!errcode) errcode = getpatterns(pr); + if (!errcode) errcode = getcurves(pr); + if (!errcode) errcode = getpumpparams(pr); - /* Free input buffer */ - free(par->X); - return (errcode); + // Free input buffer + free(parser->X); + return errcode; +} -} /* End of readdata */ - -int newline(EN_Project *pr, int sect, char *line) +int newline(Project *pr, int sect, char *line) /* **-------------------------------------------------------------- ** Input: sect = current section of input file ** *line = line read from input file ** Output: returns error code or 0 if no error found ** Purpose: processes a new line of data from input file +** +** Note: The xxxdata() functions appear in INPUT3.c. **-------------------------------------------------------------- */ { - parser_data_t *par = &pr->parser; - - int n; - switch (sect) { - case _TITLE: - if (par->Ntitle < 3) { - n = (int)strlen(line); - if (line[n - 1] == 10) - line[n - 1] = ' '; - strncpy(pr->Title[par->Ntitle], line, TITLELEN); - par->Ntitle++; - } - return (0); - case _JUNCTIONS: - return (juncdata(pr)); - case _RESERVOIRS: - case _TANKS: - return (tankdata(pr)); - case _PIPES: - return (pipedata(pr)); - case _PUMPS: - return (pumpdata(pr)); - case _VALVES: - return (valvedata(pr)); - case _PATTERNS: - return (patterndata(pr)); - case _CURVES: - return (curvedata(pr)); - case _DEMANDS: - return (demanddata(pr)); - case _CONTROLS: - return (controldata(pr)); - case _RULES: - if (ruledata(pr) > 0) + Parser *parser = &pr->parser; + int n; + + switch (sect) { - ruleerrmsg(pr); - return 200; + case _TITLE: + if (parser->Ntitle < 3) + { + n = (int)strlen(line); + if (line[n - 1] == 10) + line[n - 1] = ' '; + strncpy(pr->Title[parser->Ntitle], line, TITLELEN); + parser->Ntitle++; + } + return 0; + case _JUNCTIONS: return (juncdata(pr)); + case _RESERVOIRS: + case _TANKS: return (tankdata(pr)); + case _PIPES: return (pipedata(pr)); + case _PUMPS: return (pumpdata(pr)); + case _VALVES: return (valvedata(pr)); + case _PATTERNS: return (patterndata(pr)); + case _CURVES: return (curvedata(pr)); + case _DEMANDS: return (demanddata(pr)); + case _CONTROLS: return (controldata(pr)); + case _RULES: + if (ruledata(pr) > 0) + { + ruleerrmsg(pr); + return 200; + } + else return 0; + case _SOURCES: return (sourcedata(pr)); + case _EMITTERS: return (emitterdata(pr)); + case _QUALITY: return (qualdata(pr)); + case _STATUS: return (statusdata(pr)); + case _ROUGHNESS: return (0); + case _ENERGY: return (energydata(pr)); + case _REACTIONS: return (reactdata(pr)); + case _MIXING: return (mixingdata(pr)); + case _REPORT: return (reportdata(pr)); + case _TIMES: return (timedata(pr)); + case _OPTIONS: return (optiondata(pr)); + case _COORDS: return (coordata(pr)); + + // Data in these sections are not used for any computations + case _LABELS: + case _TAGS: + case _VERTICES: + case _BACKDROP: + return (0); } - else return 0; - case _SOURCES: - return (sourcedata(pr)); - case _EMITTERS: - return (emitterdata(pr)); - case _QUALITY: - return (qualdata(pr)); - case _STATUS: - return (statusdata(pr)); - case _ROUGHNESS: - return (0); - case _ENERGY: - return (energydata(pr)); - case _REACTIONS: - return (reactdata(pr)); - case _MIXING: - return (mixingdata(pr)); - case _REPORT: - return (reportdata(pr)); - case _TIMES: - return (timedata(pr)); - case _OPTIONS: - return (optiondata(pr)); + return 201; +} - /* Data in these sections are not used for any computations */ - case _COORDS: - if (par->Coordflag == TRUE) { - return (coordata(pr)); - } else - return (0); - case _LABELS: - return (0); - case _TAGS: - return (0); - case _VERTICES: - return (0); - case _BACKDROP: - return (0); - } - return (201); -} /* end of newline */ - -int getpumpparams(EN_Project *pr) +int getpumpparams(Project *pr) /* **------------------------------------------------------------- ** Input: none @@ -370,8 +325,8 @@ int getpumpparams(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; int i, k, errcode = 0; - EN_Network *net = &pr->network; for (i = 1; i <= net->Npumps; i++) { @@ -388,7 +343,7 @@ int getpumpparams(EN_Project *pr) return 0; } -int updatepumpparams(EN_Project *pr, int pumpindex) +int updatepumpparams(Project *pr, int pumpindex) /* **------------------------------------------------------------- ** Input: pumpindex = index of a pump @@ -397,16 +352,16 @@ int updatepumpparams(EN_Project *pr, int pumpindex) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Spump *pump; + Scurve *curve; + int m; int curveindex; int npts = 0; int errcode = 0; double a, b, c, h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0; - EN_Network *net = &pr->network; - Spump *pump; - Scurve *curve; - pump = &net->Pump[pumpindex]; if (pump->Ptype == CONST_HP) // Constant Hp pump { @@ -481,7 +436,7 @@ int updatepumpparams(EN_Project *pr, int pumpindex) } -int addnodeID(EN_Network *net, int n, char *id) +int addnodeID(Network *net, int n, char *id) /* **------------------------------------------------------------- ** Input: n = node index @@ -491,15 +446,13 @@ int addnodeID(EN_Network *net, int n, char *id) **-------------------------------------------------------------- */ { - if (findnode(net,id)) { - return (0); /* see EPANET.C */ - } - strncpy(net->Node[n].ID, id, MAXID); - hashtable_insert(net->NodeHashTable, net->Node[n].ID, n); /* see HASH.C */ - return (1); + if (findnode(net,id)) return 0; + strncpy(net->Node[n].ID, id, MAXID); + hashtable_insert(net->NodeHashTable, net->Node[n].ID, n); + return 1; } -int addlinkID(EN_Network *net, int n, char *id) +int addlinkID(Network *net, int n, char *id) /* **------------------------------------------------------------- ** Input: n = link index @@ -509,15 +462,13 @@ int addlinkID(EN_Network *net, int n, char *id) **-------------------------------------------------------------- */ { - if (findlink(net,id)) { - return (0); /* see EPANET.C */ - } - strncpy(net->Link[n].ID, id, MAXID); - hashtable_insert(net->LinkHashTable, net->Link[n].ID, n); /* see HASH.C */ - return (1); + if (findlink(net,id)) return 0; + strncpy(net->Link[n].ID, id, MAXID); + hashtable_insert(net->LinkHashTable, net->Link[n].ID, n); + return 1; } -int addpattern(parser_data_t *par, char *id) +int addpattern(Parser *parser, char *id) /* **------------------------------------------------------------- ** Input: id = pattern ID label @@ -526,37 +477,34 @@ int addpattern(parser_data_t *par, char *id) **-------------------------------------------------------------- */ { - - STmplist *p; + STmplist *patlist; - /* Check if ID is same as last one processed */ - if (par->Patlist != NULL && strcmp(id, par->Patlist->ID) == 0) { - return (0); - } + // 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 (findID(id, par->Patlist) == NULL) { + // Check that pattern was not already created + if (getlistitem(id, parser->Patlist) == NULL) + { + // Update pattern count & create new list element + (parser->MaxPats)++; + patlist = (STmplist *)malloc(sizeof(STmplist)); + if (patlist == NULL) return 101; - /* Update pattern count & create new list element */ - (par->MaxPats)++; - p = (STmplist *)malloc(sizeof(STmplist)); - if (p == NULL) - return (101); - - /* Initialize list element properties */ - else { - p->i = par->MaxPats; - strncpy(p->ID, id, MAXID); - p->x = NULL; - p->y = NULL; - p->next = par->Patlist; - par->Patlist = p; + // 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; + } } - } - return (0); + return 0; } -int addcurve(parser_data_t *par, char *id) +int addcurve(Parser *parser, char *id) /* **------------------------------------------------------------- ** Input: id = curve ID label @@ -565,35 +513,34 @@ int addcurve(parser_data_t *par, char *id) **-------------------------------------------------------------- */ { - STmplist *c; + STmplist *curvelist; - /* Check if ID is same as last one processed */ - if (par->Curvelist != NULL && strcmp(id, par->Curvelist->ID) == 0) - return (0); + // 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 (findID(id, par->Curvelist) == NULL) { + // Check that curve was not already created + if (getlistitem(id, parser->Curvelist) == NULL) + { + // Update curve count & create new list element + (parser->MaxCurves)++; + curvelist = (STmplist *)malloc(sizeof(STmplist)); + if (curvelist == NULL) return 101; - /* Update curve count & create new list element */ - (par->MaxCurves)++; - c = (STmplist *)malloc(sizeof(STmplist)); - if (c == NULL) - return (101); - - /* Initialize list element properties */ - else { - c->i = par->MaxCurves; - strncpy(c->ID, id, MAXID); - c->x = NULL; - c->y = NULL; - c->next = par->Curvelist; - par->Curvelist = c; + // 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; + } } - } - return (0); + return 0; } -STmplist *findID(char *id, STmplist *list) +STmplist *getlistitem(char *id, STmplist *list) /* **------------------------------------------------------------- ** Input: id = ID label @@ -603,16 +550,15 @@ STmplist *findID(char *id, STmplist *list) **------------------------------------------------------------- */ { - STmplist *item; - for (item = list; item != NULL; item = item->next) { - if (strcmp(item->ID, id) == 0) { - return (item); + STmplist *item; + for (item = list; item != NULL; item = item->next) + { + if (strcmp(item->ID, id) == 0) return item; } - } - return (NULL); + return NULL; } -int unlinked(EN_Project *pr) +int unlinked(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -623,40 +569,45 @@ int unlinked(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - int *marked; - int i, err, errcode; + Network *net = &pr->network; + int *marked; + int i, err, errcode; - errcode = 0; - err = 0; - marked = (int *)calloc(net->Nnodes + 1, sizeof(int)); - ERRCODE(MEMCHECK(marked)); - if (!errcode) { - memset(marked, 0, (net->Nnodes + 1) * sizeof(int)); - for (i = 1; i <= net->Nlinks; i++) /* Mark end nodes of each link */ - { - marked[net->Link[i].N1]++; - marked[net->Link[i].N2]++; - } - for (i = 1; i <= net->Njuncs; i++) /* Check each junction */ - { - if (marked[i] == 0) /* If not marked then error */ - { - err++; - sprintf(pr->Msg, "%s link: %s", geterrmsg(233, pr->Msg), net->Node[i].ID); - writeline(pr, pr->Msg); - } - if (err >= MAXERRS) - break; - } - if (err > 0) - errcode = 200; - } - free(marked); - return (errcode); -} /* End of unlinked */ + errcode = 0; + err = 0; -int getpatterns(EN_Project *pr) + // Create an array to record number of links incident on each node + marked = (int *)calloc(net->Nnodes + 1, sizeof(int)); + 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) + { + err++; + sprintf(pr->Msg, "%s link: %s", geterrmsg(233, pr->Msg), + net->Node[i].ID); + writeline(pr, pr->Msg); + } + if (err >= MAXERRS) break; + } + if (err > 0) errcode = 200; + free(marked); + return errcode; +} + +int getpatterns(Project *pr) /* **----------------------------------------------------------- ** Input: none @@ -665,64 +616,62 @@ int getpatterns(EN_Project *pr) **------------------------------------------------------------- */ { - int i, j; - SFloatlist *f; - STmplist *pat; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Parser *parser = &pr->parser; + + int i, j; + SFloatlist *f; + STmplist *tmppattern; + Spattern *pattern; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - parser_data_t *par = &pr->parser; + // Start at head of the list of patterns + tmppattern = parser->Patlist; - /* Start at head of list */ - pat = par->Patlist; + // Traverse list of temporary patterns + while (tmppattern != NULL) + { + // Get index of temporary pattern in network's Pattern array + i = tmppattern->i; - /* Traverse list of patterns */ - while (pat != NULL) { + // Check if this is the default pattern + if (strcmp(tmppattern->ID, parser->DefPatID) == 0) hyd->DefPat = i; - /* Get index of current pattern in Pattern array */ - i = pat->i; + // Copy temporary patttern to network's pattern + if (i >= 0 && i <= parser->MaxPats) + { + pattern = &net->Pattern[i]; + strcpy(pattern->ID, tmppattern->ID); - /* Check if this is the default pattern */ - if (strcmp(pat->ID, par->DefPatID) == 0) { - hyd->DefPat = i; - } - if (i >= 0 && i <= par->MaxPats) { - /* Save pattern ID */ - - Spattern *pattern = &net->Pattern[i]; - - strcpy(pattern->ID, pat->ID); + /* Give pattern a length of at least 1 */ + if (pattern->Length == 0) pattern->Length = 1; - /* Give pattern a length of at least 1 */ - if (pattern->Length == 0) - pattern->Length = 1; - pattern->F = (double *)calloc(pattern->Length, sizeof(double)); - if (pattern->F == NULL) - return (101); + // Allocate array of pattern factors + pattern->F = (double *)calloc(pattern->Length, sizeof(double)); + if (pattern->F == NULL) return 101; - /* Start at head of pattern multiplier list */ - /* (which holds multipliers in reverse order)*/ - f = pat->x; - j = pattern->Length - 1; + // 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; + // Use at least one multiplier equal to 1.0 + if (f == NULL) pattern->F[0] = 1.0; - /* Traverse list, storing multipliers in Pattern array */ - else - while (f != NULL && j >= 0) { - pattern->F[j] = f->value; - f = f->next; - j--; + // 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; } - pat = pat->next; - } - return (0); + return 0; } -int getcurves(EN_Project *pr) +int getcurves(Project *pr) /* **----------------------------------------------------------- ** Input: none @@ -731,65 +680,70 @@ int getcurves(EN_Project *pr) **----------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Parser *parser = &pr->parser; - int i, j; - double x; - SFloatlist *fx, *fy; - STmplist *c; + int i, j; + double x; + SFloatlist *fx, *fy; + STmplist *tmpcurve; + Scurve *curve; - /* Start at head of curve list */ - c = par->Curvelist; + // Traverse list of temporary curves + tmpcurve = parser->Curvelist; + while (tmpcurve != NULL) + { + // Get index of temporary curve in network's Curve array + i = tmpcurve->i; - /* Traverse list of curves */ - while (c != NULL) { - i = c->i; - if (i >= 1 && i <= par->MaxCurves) { - Scurve *curve = &net->Curve[i]; - - /* Save curve ID */ - strcpy(curve->ID, c->ID); + // Copy temporary curve to network's curve + if (i >= 1 && i <= parser->MaxCurves) + { + curve = &net->Curve[i]; + strcpy(curve->ID, tmpcurve->ID); - /* Check that curve has data points */ - if (curve->Npts <= 0) { - sprintf(pr->Msg, "%s link: %s", geterrmsg(230, pr->Msg), curve->ID); - writeline(pr, pr->Msg); - return (200); - } + // Check that network curve has data points + if (curve->Npts <= 0) + { + sprintf(pr->Msg, "%s link: %s", geterrmsg(230, pr->Msg), + curve->ID); + writeline(pr, pr->Msg); + return (200); + } - /* Allocate memory for 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); + // 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 = c->x; - fy = c->y; - j = curve->Npts - 1; - while (fx != NULL && fy != NULL && j >= 0) { + // 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, "%s link: %s", geterrmsg(230, pr->Msg), + curve->ID); + writeline(pr, pr->Msg); + return (200); + } + x = fx->value; - /* Check that x data is in ascending order */ - if (fx->value >= x) { - sprintf(pr->Msg, "%s link: %s", geterrmsg(230, pr->Msg), curve->ID); - writeline(pr, pr->Msg); - return (200); + // 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--; + } } - x = fx->value; - - /* Save x,y data in Curve structure */ - curve->X[j] = fx->value; - fx = fx->next; - curve->Y[j] = fy->value; - fy = fy->next; - j--; - } + tmpcurve = tmpcurve->next; } - c = c->next; - } - return (0); + return 0; } int findmatch(char *line, char *keyword[]) @@ -803,14 +757,14 @@ int findmatch(char *line, char *keyword[]) **-------------------------------------------------------------- */ { - int i = 0; - while (keyword[i] != NULL) { - if (match(line, keyword[i])) - return (i); - i++; - } - return (-1); -} /* end of findmatch */ + int i = 0; + while (keyword[i] != NULL) + { + if (match(line, keyword[i])) return i; + i++; + } + return -1; +} int match(const char *str, const char *substr) /* @@ -824,25 +778,24 @@ int match(const char *str, const char *substr) **-------------------------------------------------------------- */ { - int i, j; + int i, j; - /*** Updated 9/7/00 ***/ - /* Fail if substring is empty */ - if (!substr[0]) - return (0); + // Fail if substring is empty + if (!substr[0]) return 0; - /* Skip leading blanks of str. */ - for (i = 0; str[i]; i++) - if (str[i] != ' ') - break; + // Skip leading blanks of str + for (i = 0; str[i]; i++) + { + if (str[i] != ' ') break; + } - /* Check if substr matches remainder of str. */ - for (j = 0; substr[j]; i++, j++) - if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j])) - return (0); - - return (1); -} /* end of match */ + // Check if substr matches remainder of str + for (j = 0; substr[j]; i++, j++) + { + if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j])) return 0; + } + return 1; +} int gettokens(char *s, char** Tok, int maxToks, char *comment) /* @@ -858,57 +811,58 @@ int gettokens(char *s, char** Tok, int maxToks, char *comment) **-------------------------------------------------------------- */ { - int m, n; - size_t len; - char *c, *c2; + int m, n; + size_t len; + char *c, *c2; - // clear comment - comment[0] = '\0'; + // clear comment + comment[0] = '\0'; - /* Begin with no tokens */ - for (n=0; n 0) { - len = strcspn(c2, "\n\r"); - len = MIN(len, MAXMSG); - strncpy(comment, c2, len); - comment[MIN(len,MAXMSG)] = '\0'; - } - } - *c = '\0'; - } - len = (int)strlen(s); - - /* Scan s for tokens until nothing left */ - while (len > 0 && n < MAXTOKS) - { - m = (int)strcspn(s,SEPSTR); /* Find token length */ - len -= m+1; /* Update length of s */ - if (m == 0) s++; /* No token found */ - else + // Truncate s at start of comment + c = strchr(s,';'); + if (c) { - if (*s == '"') /* Token begins with quote */ - { - 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 */ - s += m+1; /* Begin next token */ + c2 = c+1; + if (c2) + { + // there is a comment here, after the semi-colon. + len = strlen(c2); + if (len > 0) + { + len = strcspn(c2, "\n\r"); + len = MIN(len, MAXMSG); + strncpy(comment, c2, len); + comment[MIN(len,MAXMSG)] = '\0'; + } + } + *c = '\0'; } - } - return(n); + len = (int)strlen(s); + + // Scan s for tokens until nothing left + while (len > 0 && n < MAXTOKS) + { + m = (int)strcspn(s,SEPSTR); // Find token length + len -= m+1; // Update length of s + if (m == 0) s++; // No token found + else + { + if (*s == '"') // Token begins with quote + { + 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 + s += m+1; // Begin next token + } + } + return n; } double hour(char *time, char *units) @@ -922,63 +876,51 @@ double hour(char *time, char *units) **--------------------------------------------------------- */ { - int n; - double y[3]; - char *s; + int n; + double y[3]; + char *s; - /* Separate clock time into hrs, min, sec. */ - for (n = 0; n < 3; n++) - y[n] = 0.0; - n = 0; - s = strtok(time, ":"); - while (s != NULL && n <= 3) { - if (!getfloat(s, &y[n])) - return (-1.0); - s = strtok(NULL, ":"); - n++; - } + // Separate clock time into hrs, min, sec + for (n = 0; n < 3; n++) y[n] = 0.0; + n = 0; + s = strtok(time, ":"); + while (s != NULL && n <= 3) + { + if (!getfloat(s, &y[n])) return -1.0; + s = strtok(NULL, ":"); + n++; + } - /* If decimal time with units attached then convert to hours. */ - if (n == 1) { - /*if (units[0] == '\0') return(y[0]);*/ - if (strlen(units) == 0) - return (y[0]); - if (match(units, w_SECONDS)) - return (y[0] / 3600.0); - if (match(units, w_MINUTES)) - return (y[0] / 60.0); - if (match(units, w_HOURS)) - return (y[0]); - if (match(units, w_DAYS)) - return (y[0] * 24.0); - } + // If decimal time with units attached then convert to hours + if (n == 1) + { + if (strlen(units) == 0) return (y[0]); + if (match(units, w_SECONDS)) return (y[0] / 3600.0); + if (match(units, w_MINUTES)) return (y[0] / 60.0); + if (match(units, w_HOURS)) return (y[0]); + if (match(units, w_DAYS)) return (y[0] * 24.0); + } - /* Convert hh:mm:ss format to decimal hours */ - if (n > 1) - y[0] = y[0] + y[1] / 60.0 + y[2] / 3600.0; + // 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 */ - /* (12 am is midnight, 12 pm is noon) */ - if (units[0] == '\0') - return (y[0]); - if (match(units, w_AM)) { - if (y[0] >= 13.0) - return (-1.0); - if (y[0] >= 12.0) - return (y[0] - 12.0); - else - return (y[0]); - } - if (match(units, w_PM)) { - if (y[0] >= 13.0) - return (-1.0); - if (y[0] >= 12.0) - return (y[0]); - else - return (y[0] + 12.0); - } - return (-1.0); -} /* end of hour */ + // If am/pm attached then adjust hour accordingly + // (12 am is midnight, 12 pm is noon) + if (units[0] == '\0') return y[0]; + if (match(units, w_AM)) + { + if (y[0] >= 13.0) return -1.0; + if (y[0] >= 12.0) return (y[0] - 12.0); + else return (y[0]); + } + if (match(units, w_PM)) + { + if (y[0] >= 13.0) return -1.0; + if (y[0] >= 12.0) return y[0]; + else return (y[0] + 12.0); + } + return -1.0; +} int getfloat(char *s, double *y) /* @@ -990,14 +932,13 @@ int getfloat(char *s, double *y) **----------------------------------------------------------- */ { - char *endptr; - *y = (double)strtod(s, &endptr); - if (*endptr > 0) - return (0); - return (1); + char *endptr; + *y = (double)strtod(s, &endptr); + if (*endptr > 0) return 0; + return 1; } -int setreport(EN_Project *pr, char *s) +int setreport(Project *pr, char *s) /* **----------------------------------------------------------- ** Input: *s = report format command @@ -1008,55 +949,51 @@ int setreport(EN_Project *pr, char *s) **----------------------------------------------------------- */ { - parser_data_t *par = &pr->parser; - par->Ntokens = gettokens(s, par->Tok, MAXTOKS, par->Comment); - return (reportdata(pr)); + Parser *parser = &pr->parser; + parser->Ntokens = gettokens(s, parser->Tok, MAXTOKS, parser->Comment); + return reportdata(pr); } -void inperrmsg(EN_Project *pr, int err, int sect, char *line) +void inperrmsg(Project *pr, int err, int sect, char *line) /* **------------------------------------------------------------- -** Input: err = error code -** sect = input data section -** *line = line from input file +** Input: err = error code +** sect = input data section +** *line = line from input file ** Output: none -** Purpose: displays input error message +** Purpose: displays input reader error message **------------------------------------------------------------- */ { - parser_data_t *par = &pr->parser; + Parser *parser = &pr->parser; - char errStr[MAXMSG + 1]; - char id[MAXMSG + 1]; + char errStr[MAXMSG + 1]; + char id[MAXMSG + 1]; - /* get text for error message */ - sprintf(pr->Msg, "%s - section: %s", geterrmsg(err, errStr), SectTxt[sect]); + // get text for error message + sprintf(pr->Msg, "%s - section: %s", geterrmsg(err, errStr), SectTxt[sect]); - // append ID? - /* Retrieve ID label of object with input error */ - /* (No ID used for CONTROLS or REPORT sections).*/ - switch (sect) { - case _CONTROLS: - case _REPORT: - // don't append - break; - case _ENERGY: - sprintf(id, " id: %s", par->Tok[1]); - break; - default: - sprintf(id, " id: %s", par->Tok[0]); - break; - } + // Retrieve ID label of object with input error + // (No ID used for CONTROLS or REPORT sections) + switch (sect) + { + case _CONTROLS: + case _REPORT: + // don't append + break; + case _ENERGY: + sprintf(id, " id: %s", parser->Tok[1]); + break; + default: + sprintf(id, " id: %s", parser->Tok[0]); + break; + } - strcat(pr->Msg, id); - writeline(pr, pr->Msg); + strcat(pr->Msg, id); + writeline(pr, pr->Msg); - /* Echo input line for syntax errors, and */ - /* errors in CONTROLS and OPTIONS sections. */ - if (sect == _CONTROLS || err == 201 || err == 213) - writeline(pr, line); - else - writeline(pr, ""); + // Echo input line for syntax errors, and + // errors in CONTROLS and OPTIONS sections + if (sect == _CONTROLS || err == 201 || err == 213) writeline(pr, line); + else writeline(pr, ""); } - -/********************** END OF INPUT2.C ************************/ diff --git a/src/input3.c b/src/input3.c index f18667f..9e4de54 100644 --- a/src/input3.c +++ b/src/input3.c @@ -1,23 +1,14 @@ /* -********************************************************************** - -INPUT3.C -- Input data parser for EPANET - -VERSION: 2.00 -DATE: 5/30/00 - 9/7/00 - 10/25/00 - 3/1/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -This module parses data from each line of input from file InFile. -All functions in this module are called from newline() in INPUT2.C. - -********************************************************************** +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: input3.c +Description: parses network data from a line of an EPANET input file +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/10/2018 +****************************************************************************** */ #include @@ -33,14 +24,28 @@ All functions in this module are called from newline() in INPUT2.C. #include "hash.h" #include "text.h" -/* Defined in enumstxt.h in EPANET.C */ +// Defined in ENUMSTXT.H extern char *MixTxt[]; extern char *Fldname[]; extern char *DemandModelTxt[]; -/* Defined in INPUT2.C */ +// Exported functions +int powercurve(double, double, double, double, double, double *, double *, + double *); -int juncdata(EN_Project *pr) +// 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); +static int optionvalue(Project *, int); +static int getpumpcurve(Project *, int); +static void changestatus(Network *, int, StatusType, double); + + +int juncdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -52,74 +57,64 @@ int juncdata(EN_Project *pr) **-------------------------------------------------------------- */ { - int p = 0; - double el, y = 0.0; - Pdemand demand; - STmplist *pat; + Network *net = &pr->network; + Parser *parser = &pr->parser; + Hydraul *hyd = &pr->hydraul; - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - hydraulics_t *hyd = &pr->hydraulics; - int n; - int Njuncs; - Snode *node; + int p = 0; // time pattern index + int n; // number of tokens + int njuncs; // number of network junction nodes + 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 */ - n = par->Ntokens; - if (net->Nnodes == par->MaxNodes) { - return (200); - } - net->Njuncs++; - net->Nnodes++; + // Add new junction to data base + n = parser->Ntokens; + if (net->Nnodes == parser->MaxNodes) return 200; + net->Njuncs++; + net->Nnodes++; + njuncs = net->Njuncs; + if (!addnodeID(net, net->Njuncs, parser->Tok[0])) return 215; - Njuncs = net->Njuncs; + // Check for valid data + if (n < 2) return 201; + if (!getfloat(parser->Tok[1], &el)) return 202; + if (n >= 3 && !getfloat(parser->Tok[2], &y)) return 202; + if (n >= 4) + { + patlist = getlistitem(parser->Tok[3], parser->Patlist); + if (patlist == NULL) return 205; + p = patlist->i; + } - if (!addnodeID(net, net->Njuncs, par->Tok[0])) { - return (215); - } - /* Check for valid data */ - if (n < 2) - return (201); - if (!getfloat(par->Tok[1], &el)) - return (202); - if (n >= 3 && !getfloat(par->Tok[2], &y)) - return (202); - if (n >= 4) { - pat = findID(par->Tok[3], par->Patlist); - if (pat == NULL) - return (205); - p = pat->i; - } - - /* Save junction data */ - node = &net->Node[Njuncs]; - node->El = el; - node->C0 = 0.0; - node->S = NULL; - node->Ke = 0.0; - node->Rpt = 0; - node->Type = JUNCTION; - strcpy(node->Comment, par->Comment); + // Save junction data + node = &net->Node[njuncs]; + node->X = MISSING; + node->Y = MISSING; + node->El = el; + node->C0 = 0.0; + node->S = NULL; + node->Ke = 0.0; + node->Rpt = 0; + node->Type = JUNCTION; + strcpy(node->Comment, parser->Comment); - // create a demand record, even if no demand is specified here. - // perhaps the [DEMANDS] section contains data, but not always. - - demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); - if (demand == NULL) { - return(101); - } - demand->Base = y; - demand->Pat = p; - strncpy(demand->Name, "", MAXMSG); - demand->next = NULL; - node->D = demand; - hyd->NodeDemand[Njuncs] = y; - - return (0); -} /* end of juncdata */ + // create a demand record, even if no demand is specified here. + demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); + if (demand == NULL) return 101; + demand->Base = y; + demand->Pat = p; + strncpy(demand->Name, "", MAXMSG); + demand->next = NULL; + node->D = demand; + hyd->NodeDemand[njuncs] = y; + return 0; +} -int tankdata(EN_Project *pr) +int tankdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -134,116 +129,108 @@ int tankdata(EN_Project *pr) **-------------------------------------------------------------- */ { - int n, /* # data items */ - p = 0, /* Fixed grade time pattern index */ - vcurve = 0; /* Volume curve index */ - double el = 0.0, /* Elevation */ - initlevel = 0.0, /* Initial level */ - minlevel = 0.0, /* Minimum level */ - maxlevel = 0.0, /* Maximum level */ - minvol = 0.0, /* Minimum volume */ - diam = 0.0, /* Diameter */ - area; /* X-sect. area */ - STmplist *t; + Network *net = &pr->network; + Parser *parser = &pr->parser; - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - Snode *node; - Stank *tank; - int i; + int i, // Node index + n, // # data items + pattern = 0, // Time pattern index + curve = 0; // Curve index + double el = 0.0, // Elevation + initlevel = 0.0, // Initial level + minlevel = 0.0, // Minimum level + maxlevel = 0.0, // Maximum level + minvol = 0.0, // Minimum volume + diam = 0.0, // Diameter + area; // X-sect. area + STmplist *tmplist; + Snode *node; + Stank *tank; - /* Add new tank to data base */ - n = par->Ntokens; - if (net->Ntanks == par->MaxTanks || net->Nnodes == par->MaxNodes) { - return (200); - } - net->Ntanks++; - net->Nnodes++; + // Add new tank to data base + n = parser->Ntokens; + if (net->Ntanks == parser->MaxTanks || + net->Nnodes == parser->MaxNodes) return 200; + net->Ntanks++; + net->Nnodes++; - i = par->MaxJuncs + net->Ntanks; /* i = node index. */ - if (!addnodeID(net, i, par->Tok[0])) { - return (215); /* Add ID to database. */ - } + i = parser->MaxJuncs + net->Ntanks; + if (!addnodeID(net, i, parser->Tok[0])) return 215; - /* Check for valid data */ - if (n < 2) - return (201); /* Too few fields. */ - if (!getfloat(par->Tok[1], &el)) - return (202); /* Read elevation */ + // Check for valid data + if (n < 2) return (201); + if (!getfloat(parser->Tok[1], &el)) return 202; - if (n <= 3) { /* Tank is reservoir.*/ - if (n == 3) { /* Pattern supplied */ - t = findID(par->Tok[2], par->Patlist); - if (t == NULL) - return (205); - p = t->i; + // Tank is reservoir + if (n <= 3) + { + // Head pattern supplied + if (n == 3) + { + tmplist = getlistitem(parser->Tok[2], parser->Patlist); + if (tmplist == NULL) return 205; + pattern = tmplist->i; + } } - } else if (n < 6) { - return (201); /* Too few fields for tank.*/ - } else { - /* Check for valid input data */ - if (!getfloat(par->Tok[2], &initlevel)) - return (202); - if (!getfloat(par->Tok[3], &minlevel)) - return (202); - if (!getfloat(par->Tok[4], &maxlevel)) - return (202); - if (!getfloat(par->Tok[5], &diam)) - return (202); - if (diam < 0.0) - return (202); - if (n >= 7 && !getfloat(par->Tok[6], &minvol)) - return (202); + else if (n < 6) return 201; + + // Tank is a storage tank + else + { + if (!getfloat(parser->Tok[2], &initlevel)) return 202; + if (!getfloat(parser->Tok[3], &minlevel)) return 202; + if (!getfloat(parser->Tok[4], &maxlevel)) return 202; + if (!getfloat(parser->Tok[5], &diam)) return 202; + if (diam < 0.0) return 202; + if (n >= 7 && !getfloat(parser->Tok[6], &minvol)) return 202; - /* If volume curve supplied check it exists */ - if (n == 8) { - t = findID(par->Tok[7], par->Curvelist); - if (t == NULL) { - return (202); - } - vcurve = t->i; - net->Curve[t->i].Type = V_CURVE; + // If volume curve supplied check it exists + if (n == 8) + { + tmplist = getlistitem(parser->Tok[7], parser->Curvelist); + if (tmplist == NULL) return 202; + curve = tmplist->i; + net->Curve[curve].Type = V_CURVE; + } } - } + node = &net->Node[i]; + tank = &net->Tank[net->Ntanks]; - node = &net->Node[i]; - tank = &net->Tank[net->Ntanks]; + node->X = MISSING; + node->Y = MISSING; + node->Rpt = 0; + node->El = el; + node->C0 = 0.0; + node->S = NULL; + node->Ke = 0.0; + node->Type = (diam == 0) ? RESERVOIR : TANK; + strcpy(node->Comment, parser->Comment); + tank->Node = i; + tank->H0 = initlevel; + tank->Hmin = minlevel; + tank->Hmax = maxlevel; + tank->A = diam; + tank->Pat = pattern; + tank->Kb = MISSING; + + //******************************************************************* + // NOTE: The min, max, & initial volumes set here are based on a + // nominal tank diameter. They will be modified in INPUT1.C if + // a volume curve is supplied for this tank. + //******************************************************************* + area = PI * SQR(diam) / 4.0; + tank->Vmin = area * minlevel; + if (minvol > 0.0) tank->Vmin = minvol; + tank->V0 = tank->Vmin + area * (initlevel - minlevel); + tank->Vmax = tank->Vmin + area * (maxlevel - minlevel); - node->Rpt = 0; - node->El = el; /* Elevation. */ - node->C0 = 0.0; /* Init. quality. */ - node->S = NULL; /* WQ source data */ - node->Ke = 0.0; /* Emitter coeff. */ - node->Type = (diam == 0) ? RESERVOIR : TANK; - strcpy(node->Comment, par->Comment); - tank->Node = i; /* Node index. */ - tank->H0 = initlevel; /* Init. level. */ - tank->Hmin = minlevel; /* Min. level. */ - tank->Hmax = maxlevel; /* Max level. */ - tank->A = diam; /* Diameter. */ - tank->Pat = p; /* Fixed grade pattern. */ - tank->Kb = MISSING; /* Reaction coeff. */ - /* - ******************************************************************* - NOTE: The min, max, & initial volumes set here are based on a - nominal tank diameter. They will be modified in INPUT1.C if - a volume curve is supplied for this tank. - ******************************************************************* - */ - area = PI * SQR(diam) / 4.0; - tank->Vmin = area * minlevel; - if (minvol > 0.0) - tank->Vmin = minvol; - tank->V0 = tank->Vmin + area * (initlevel - minlevel); - tank->Vmax = tank->Vmin + area * (maxlevel - minlevel); + tank->Vcurve = curve; + tank->MixModel = MIX1; // Completely mixed + tank->V1max = 1.0; // Mixing compartment size fraction + return 0; +} - tank->Vcurve = vcurve; /* Volume curve */ - tank->MixModel = MIX1; /* Completely mixed */ - tank->V1max = 1.0; /* Compart. size ratio */ - return (0); -} /* end of tankdata */ - -int pipedata(EN_Project *pr) +int pipedata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -255,93 +242,76 @@ int pipedata(EN_Project *pr) **-------------------------------------------------------------- */ { - int j1, /* Start-node index */ - j2, /* End-node index */ - n; /* # data items */ - LinkType type = PIPE; /* Link type */ - StatType status = OPEN; /* Link status */ - double length, /* Link length */ - diam, /* Link diameter */ - rcoeff, /* Roughness coeff. */ - lcoeff = 0.0; /* Minor loss coeff. */ + Network *net = &pr->network; + Parser *parser = &pr->parser; - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - Slink *link; + int j1, // Start-node index + j2, // End-node index + n; // # data items + double length, // Pipe length + diam, // Pipe diameter + rcoeff, // Roughness coeff. + lcoeff = 0.0; // Minor loss coeff + LinkType type = PIPE; // Link type + StatusType status = OPEN; // Link status + Slink *link; - /* Add new pipe to data base */ - n = par->Ntokens; - if (net->Nlinks == par->MaxLinks) - return (200); - net->Npipes++; - net->Nlinks++; - if (!addlinkID(net, net->Nlinks, par->Tok[0])) - return (215); + // Add new pipe to data base + n = parser->Ntokens; + if (net->Nlinks == parser->MaxLinks) return 200; + net->Npipes++; + net->Nlinks++; + if (!addlinkID(net, net->Nlinks, parser->Tok[0])) return 215; - /* Check for valid data */ - if (n < 6) - return (201); - if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) - return (203); + // Check for valid data + if (n < 6) return 201; + if ((j1 = findnode(net, parser->Tok[1])) == 0 || + (j2 = findnode(net, parser->Tok[2])) == 0) return 203; + if (j1 == j2) return 222; - /*** Updated 10/25/00 ***/ - if (j1 == j2) - return (222); + if (!getfloat(parser->Tok[3], &length) || + !getfloat(parser->Tok[4], &diam) || + !getfloat(parser->Tok[5], &rcoeff)) return 202; + if (length <= 0.0 || diam <= 0.0 || rcoeff <= 0.0) return 202; - if (!getfloat(par->Tok[3], &length) || !getfloat(par->Tok[4], &diam) || - !getfloat(par->Tok[5], &rcoeff)) - return (202); + // Either a loss coeff. or a status is supplied + if (n == 7) + { + if (match(parser->Tok[6], w_CV)) type = CVPIPE; + else if (match(parser->Tok[6], w_CLOSED)) status = CLOSED; + else if (match(parser->Tok[6], w_OPEN)) status = OPEN; + else if (!getfloat(parser->Tok[6], &lcoeff)) return (202); + } - if (length <= 0.0 || diam <= 0.0 || rcoeff <= 0.0) - return (202); + // Both a loss coeff. and a status is supplied + if (n == 8) + { + if (!getfloat(parser->Tok[6], &lcoeff)) return 202; + if (match(parser->Tok[7], w_CV)) type = CVPIPE; + else if (match(parser->Tok[7], w_CLOSED)) status = CLOSED; + else if (match(parser->Tok[7], w_OPEN)) status = OPEN; + else return 202; + } + if (lcoeff < 0.0) return 202; - /* Case where either loss coeff. or status supplied */ - if (n == 7) { - if (match(par->Tok[6], w_CV)) - type = CVPIPE; - else if (match(par->Tok[6], w_CLOSED)) - status = CLOSED; - else if (match(par->Tok[6], w_OPEN)) - status = OPEN; - else if (!getfloat(par->Tok[6], &lcoeff)) - return (202); - } + // Save pipe data + link = &net->Link[net->Nlinks]; + link->N1 = j1; + link->N2 = j2; + link->Len = length; + link->Diam = diam; + link->Kc = rcoeff; + link->Km = lcoeff; + link->Kb = MISSING; + link->Kw = MISSING; + link->Type = type; + link->Status = status; + link->Rpt = 0; + strcpy(link->Comment, parser->Comment); + return 0; +} - /* Case where both loss coeff. and status supplied */ - if (n == 8) { - if (!getfloat(par->Tok[6], &lcoeff)) - return (202); - if (match(par->Tok[7], w_CV)) - type = CVPIPE; - else if (match(par->Tok[7], w_CLOSED)) - status = CLOSED; - else if (match(par->Tok[7], w_OPEN)) - status = OPEN; - else - return (202); - } - if (lcoeff < 0.0) - return (202); - - /* Save pipe data */ - link = &net->Link[net->Nlinks]; - - link->N1 = j1; /* Start-node index */ - link->N2 = j2; /* End-node index */ - link->Len = length; /* Length */ - link->Diam = diam; /* Diameter */ - link->Kc = rcoeff; /* Rough. coeff */ - link->Km = lcoeff; /* Loss coeff */ - link->Kb = MISSING; /* Bulk coeff */ - link->Kw = MISSING; /* Wall coeff */ - link->Type = type; /* Link type */ - link->Stat = status; /* Link status */ - link->Rpt = 0; /* Report flag */ - strcpy(link->Comment, par->Comment); - return (0); -} /* end of pipedata */ - -int pumpdata(EN_Project *pr) +int pumpdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -359,116 +329,106 @@ int pumpdata(EN_Project *pr) **-------------------------------------------------------------- */ { - int j, j1, /* Start-node index */ - j2, /* End-node index */ - m, n; /* # data items */ - double y; - STmplist *t; /* Pattern record */ + Network *net = &pr->network; + Parser *parser = &pr->parser; - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - Slink *link; - Spump *pump; + int j, + j1, // Start-node index + j2, // End-node index + m, n; // # data items + double y; + STmplist *tmplist; // Temporary list + Slink *link; + Spump *pump; - /* Add new pump to data base */ - n = par->Ntokens; - if (net->Nlinks == par->MaxLinks || net->Npumps == par->MaxPumps) - return (200); - net->Nlinks++; - net->Npumps++; - if (!addlinkID(net, net->Nlinks, par->Tok[0])) - return (215); + /* Add new pump to data base */ + n = parser->Ntokens; + if (net->Nlinks == parser->MaxLinks || + net->Npumps == parser->MaxPumps) return 200; + net->Nlinks++; + net->Npumps++; + if (!addlinkID(net, net->Nlinks, parser->Tok[0])) return 215; - /* Check for valid data */ - if (n < 4) - return (201); - if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) - return (203); + // Check for valid data + if (n < 4) return 201; + if ((j1 = findnode(net, parser->Tok[1])) == 0 || + (j2 = findnode(net, parser->Tok[2])) == 0) return 203; + if (j1 == j2) return 222; - /*** Updated 10/25/00 ***/ - if (j1 == j2) - return (222); - - /* Save pump data */ - link = &net->Link[net->Nlinks]; - pump = &net->Pump[net->Npumps]; + // Save pump data + link = &net->Link[net->Nlinks]; + pump = &net->Pump[net->Npumps]; - link->N1 = j1; /* Start-node index. */ - link->N2 = j2; /* End-node index. */ - link->Diam = 0; /* no longer Pump index. */ - link->Len = 0.0; /* Link length. */ - link->Kc = 1.0; /* Speed factor. */ - link->Km = 0.0; /* Horsepower. */ - link->Kb = 0.0; - link->Kw = 0.0; - link->Type = PUMP; /* Link type. */ - link->Stat = OPEN; /* Link status. */ - link->Rpt = 0; /* Report flag. */ - strcpy(link->Comment, par->Comment); - pump->Link = net->Nlinks; /* Link index. */ - pump->Ptype = NOCURVE; /* Type of pump curve -- "NOCURVE" is a placeholder. this may be modified in getpumpparams() */ - pump->Hcurve = 0; /* Pump curve index */ - pump->Ecurve = 0; /* Effic. curve index */ - pump->Upat = 0; /* Utilization pattern*/ - pump->Ecost = 0.0; /* Unit energy cost */ - pump->Epat = 0; /* Energy cost pattern*/ + link->N1 = j1; + link->N2 = j2; + link->Diam = 0; + link->Len = 0.0; + link->Kc = 1.0; + link->Km = 0.0; + link->Kb = 0.0; + link->Kw = 0.0; + link->Type = PUMP; + link->Status = OPEN; + link->Rpt = 0; + strcpy(link->Comment, parser->Comment); + pump->Link = net->Nlinks; + pump->Ptype = NOCURVE; // NOCURVE is a placeholder + pump->Hcurve = 0; + pump->Ecurve = 0; + pump->Upat = 0; + pump->Ecost = 0.0; + pump->Epat = 0; - /* If 4-th token is a number then input follows Version 1.x format */ - /* so retrieve pump curve parameters */ - if (getfloat(par->Tok[3], &par->X[0])) { - m = 1; - for (j = 4; j < n; j++) { - if (!getfloat(par->Tok[j], &par->X[m])) { - return (202); - } - m++; + // If 4-th token is a number then input follows Version 1.x format + // so retrieve pump curve parameters + if (getfloat(parser->Tok[3], &parser->X[0])) + { + m = 1; + for (j = 4; j < n; j++) + { + if (!getfloat(parser->Tok[j], &parser->X[m])) return 202; + m++; + } + return (getpumpcurve(pr,m)); } - return (getpumpcurve(pr,m)); /* Get pump curve params */ - } - /* Otherwise input follows Version 2 format */ - /* so retrieve keyword/value pairs. */ - m = 4; - while (m < n) { - if (match(par->Tok[m - 1], w_POWER)) /* Const. HP curve */ + // Otherwise input follows Version 2 format + // so retrieve keyword/value pairs + m = 4; + while (m < n) { - y = atof(par->Tok[m]); - if (y <= 0.0) - return (202); - pump->Ptype = CONST_HP; - link->Km = y; - } - else if (match(par->Tok[m - 1], w_HEAD)) /* Custom pump curve */ - { - t = findID(par->Tok[m], par->Curvelist); - if (t == NULL) - return (206); - pump->Hcurve = t->i; - } - else if (match(par->Tok[m - 1], w_PATTERN)) /* Speed/status pattern */ - { - t = findID(par->Tok[m], par->Patlist); - if (t == NULL) - return (205); - pump->Upat = t->i; - } - else if (match(par->Tok[m - 1], w_SPEED)) /* Speed setting */ - { - if (!getfloat(par->Tok[m], &y)) - return (202); - if (y < 0.0) - return (202); - link->Kc = y; - } - else { - return (201); + if (match(parser->Tok[m - 1], w_POWER)) // Const. HP curve + { + y = atof(parser->Tok[m]); + if (y <= 0.0) return (202); + pump->Ptype = CONST_HP; + link->Km = y; + } + else if (match(parser->Tok[m - 1], w_HEAD)) // Custom pump curve + { + tmplist = getlistitem(parser->Tok[m], parser->Curvelist); + if (tmplist == NULL) return 206; + pump->Hcurve = tmplist->i; + } + else if (match(parser->Tok[m - 1], w_PATTERN)) // Speed/status pattern + { + tmplist = getlistitem(parser->Tok[m], parser->Patlist); + if (tmplist == NULL) return 205; + pump->Upat = tmplist->i; + } + else if (match(parser->Tok[m - 1], w_SPEED)) // Speed setting + { + if (!getfloat(parser->Tok[m], &y)) return 202; + if (y < 0.0) return 202; + link->Kc = y; + } + else return 201; + m = m + 2; // Move to next keyword token } - m = m + 2; /* Skip to next keyword token */ - } - return (0); -} /* end of pumpdata */ + return 0; +} -int valvedata(EN_Project *pr) +int valvedata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -480,99 +440,82 @@ int valvedata(EN_Project *pr) **-------------------------------------------------------------- */ { - int j1, /* Start-node index */ - j2, /* End-node index */ - n; /* # data items */ - char status = ACTIVE, /* Valve status */ - type; /* Valve type */ - double diam = 0.0, /* Valve diameter */ - setting, /* Valve setting */ - lcoeff = 0.0; /* Minor loss coeff. */ - STmplist *t; /* Curve record */ + Network *net = &pr->network; + Parser *parser = &pr->parser; - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - Slink *link; + int j1, // Start-node index + j2, // End-node index + n; // # data items + char status = ACTIVE, // Valve status + type; // Valve type + 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 */ - n = par->Ntokens; - if (net->Nlinks == par->MaxLinks || net->Nvalves == par->MaxValves) - return (200); - net->Nvalves++; - net->Nlinks++; - if (!addlinkID(net, net->Nlinks, par->Tok[0])) - return (215); + // Add new valve to data base + n = parser->Ntokens; + if (net->Nlinks == parser->MaxLinks || + net->Nvalves == parser->MaxValves) return 200; + net->Nvalves++; + net->Nlinks++; + if (!addlinkID(net, net->Nlinks, parser->Tok[0])) return 215; - /* Check for valid data */ - if (n < 6) - return (201); - if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) - return (203); + // Check for valid data + if (n < 6) return 201; + if ((j1 = findnode(net, parser->Tok[1])) == 0 || + (j2 = findnode(net, parser->Tok[2])) == 0) return (203); + if (j1 == j2) return 222; - /*** Updated 10/25/00 ***/ - if (j1 == j2) - return (222); + if (match(parser->Tok[4], w_PRV)) type = PRV; + else if (match(parser->Tok[4], w_PSV)) type = PSV; + else if (match(parser->Tok[4], w_PBV)) type = PBV; + else if (match(parser->Tok[4], w_FCV)) type = FCV; + else if (match(parser->Tok[4], w_TCV)) type = TCV; + else if (match(parser->Tok[4], w_GPV)) type = GPV; + else return 201; - if (match(par->Tok[4], w_PRV)) - type = PRV; - else if (match(par->Tok[4], w_PSV)) - type = PSV; - else if (match(par->Tok[4], w_PBV)) - type = PBV; - else if (match(par->Tok[4], w_FCV)) - type = FCV; - else if (match(par->Tok[4], w_TCV)) - type = TCV; - else if (match(par->Tok[4], w_GPV)) - type = GPV; - else - return (201); /* Illegal valve type.*/ - if (!getfloat(par->Tok[3], &diam)) - return (202); - if (diam <= 0.0) - return (202); /* Illegal diameter.*/ - if (type == GPV) { /* Headloss curve for GPV */ - t = findID(par->Tok[5], par->Curvelist); - if (t == NULL) { - return (206); + if (!getfloat(parser->Tok[3], &diam)) return 202; + if (diam <= 0.0) return 202; + + // Find headloss curve for GPV + if (type == GPV) + { + tmplist = getlistitem(parser->Tok[5], parser->Curvelist); + if (tmplist == NULL) return 206; + setting = tmplist->i; + net->Curve[tmplist->i].Type = H_CURVE; + status = OPEN; } - setting = t->i; - net->Curve[t->i].Type = H_CURVE; + else if (!getfloat(parser->Tok[5], &setting)) return 202; + if (n >= 7 && !getfloat(parser->Tok[6], &lcoeff)) return 202; - /*** Updated 9/7/00 ***/ - status = OPEN; - } else if (!getfloat(par->Tok[5], &setting)) - return (202); - if (n >= 7 && !getfloat(par->Tok[6], &lcoeff)) - return (202); + // Check that PRV, PSV, or FCV not connected to a tank & + // check for illegal connections between pairs of valves + if ((j1 > net->Njuncs || j2 > net->Njuncs) && + (type == PRV || type == PSV || type == FCV)) return 219; + if (!valvecheck(pr, type, j1, j2)) return 220; - /* Check that PRV, PSV, or FCV not connected to a tank & */ - /* check for illegal connections between pairs of valves.*/ - if ((j1 > net->Njuncs || j2 > net->Njuncs) && - (type == PRV || type == PSV || type == FCV)) - return (219); - if (!valvecheck(pr, type, j1, j2)) - return (220); + // Save valve data + link = &net->Link[net->Nlinks]; + link->N1 = j1; + link->N2 = j2; + link->Diam = diam; + link->Len = 0.0; + link->Kc = setting; + link->Km = lcoeff; + link->Kb = 0.0; + link->Kw = 0.0; + link->Type = type; + link->Status = status; + link->Rpt = 0; + strcpy(link->Comment, parser->Comment); + net->Valve[net->Nvalves].Link = net->Nlinks; + return 0; +} - /* Save valve data */ - link = &net->Link[net->Nlinks]; - link->N1 = j1; /* Start-node index. */ - link->N2 = j2; /* End-node index. */ - link->Diam = diam; /* Valve diameter. */ - link->Len = 0.0; /* Link length. */ - link->Kc = setting; /* Valve setting. */ - link->Km = lcoeff; /* Loss coeff */ - link->Kb = 0.0; - link->Kw = 0.0; - link->Type = type; /* Valve type. */ - link->Stat = status; /* Valve status. */ - link->Rpt = 0; /* Report flag. */ - strcpy(link->Comment, par->Comment); - net->Valve[net->Nvalves].Link = net->Nlinks; /* Link index. */ - return (0); -} /* end of valvedata */ - -int patterndata(EN_Project *pr) +int patterndata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -584,40 +527,43 @@ int patterndata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - - int i, n; - double x; - SFloatlist *f; - STmplist *p; - n = par->Ntokens - 1; - if (n < 1) - return (201); /* Too few values */ - if ( /* Check for new pattern */ - par->PrevPat != NULL && strcmp(par->Tok[0], par->PrevPat->ID) == 0) - p = par->PrevPat; - else - p = findID(par->Tok[0], par->Patlist); - if (p == NULL) - return (205); - for (i = 1; i <= n; i++) /* Add multipliers to list */ - { - if (!getfloat(par->Tok[i], &x)) - return (202); - f = (SFloatlist *)malloc(sizeof(SFloatlist)); - if (f == NULL) - return (101); - f->value = x; - f->next = p->x; - p->x = f; - } - net->Pattern[p->i].Length += n; /* Save # multipliers for pattern */ - par->PrevPat = p; /* Set previous pattern pointer */ - return (0); -} /* end of patterndata */ + Network *net = &pr->network; + Parser *parser = &pr->parser; -int curvedata(EN_Project *pr) + int i, n; + double x; + SFloatlist *f; + STmplist *p; + + 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; + else p = getlistitem(parser->Tok[0], parser->Patlist); + if (p == NULL) return 205; + + // Add parsed multipliers to the pattern + for (i = 1; i <= n; i++) + { + if (!getfloat(parser->Tok[i], &x)) return 202; + f = (SFloatlist *)malloc(sizeof(SFloatlist)); + if (f == NULL) return 101; + f->value = x; + f->next = p->x; + p->x = f; + } + + // Save # multipliers for pattern + net->Pattern[p->i].Length += n; + + // Set previous pattern pointer + parser->PrevPat = p; + return (0); +} + +int curvedata(Project *pr) /* **------------------------------------------------------ ** Input: none @@ -629,49 +575,47 @@ int curvedata(EN_Project *pr) **------------------------------------------------------ */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - - double x, y; - SFloatlist *fx, *fy; - STmplist *c; + Network *net = &pr->network; + Parser *parser = &pr->parser; - /* Check for valid curve ID */ - if (par->Ntokens < 3) - return (201); - if (par->PrevCurve != NULL && strcmp(par->Tok[0], par->PrevCurve->ID) == 0) - c = par->PrevCurve; - else - c = findID(par->Tok[0], par->Curvelist); - if (c == NULL) - return (205); + double x, y; + SFloatlist *fx, *fy; + STmplist *c; - /* Check for valid data */ - if (!getfloat(par->Tok[1], &x)) - return (202); - if (!getfloat(par->Tok[2], &y)) - return (202); + // 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 205; - /* Add new data point to curve's linked list */ - fx = (SFloatlist *)malloc(sizeof(SFloatlist)); - fy = (SFloatlist *)malloc(sizeof(SFloatlist)); - if (fx == NULL || fy == NULL) { - return (101); - } - 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++; + // Check for valid data + if (!getfloat(parser->Tok[1], &x)) return 202; + if (!getfloat(parser->Tok[2], &y)) return 202; - /* Save the pointer to this curve */ - par->PrevCurve = c; - return (0); + // Add new data point to curve + fx = (SFloatlist *)malloc(sizeof(SFloatlist)); + if (fx == NULL) return 101; + fy = (SFloatlist *)malloc(sizeof(SFloatlist)); + if (fy == NULL) + { + free(fx); + return 101; + } + 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; + return 0; } -int coordata(EN_Project *pr) +int coordata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -683,38 +627,29 @@ int coordata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Parser *parser = &pr->parser; + + int j; + double x, y; + Snode *node; - double x, y; - int j; - Scoord *coord; - Snode *node; + // Check for valid node ID + if (parser->Ntokens < 3) return 201; + if ((j = findnode(net, parser->Tok[0])) == 0) return 203; - /* Check for valid node ID */ - if (par->Ntokens < 3) - return (201); + // Check for valid data + if (!getfloat(parser->Tok[1], &x)) return 202; + if (!getfloat(parser->Tok[2], &y)) return 202; - /* Check for valid data */ - if ((j = findnode(net, par->Tok[0])) == 0) - return (203); - if (!getfloat(par->Tok[1], &x)) - return (202); - if (!getfloat(par->Tok[2], &y)) - return (202); + // Save coord data + node = &net->Node[j]; + node->X = x; + node->Y = y; + return 0; +} - /* Save coord data */ - coord = &net->Coord[j]; - node = &net->Node[j]; - strncpy(coord->ID, node->ID, MAXID); - coord->X = x; - coord->Y = y; - coord->HaveCoords = TRUE; - - return (0); -} /* end of coordata */ - -int demanddata(EN_Project *pr) +int demanddata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -730,73 +665,68 @@ int demanddata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - parser_data_t *par = &pr->parser; - - int j, n, p = 0; - double y; - Pdemand demand; - Pdemand cur_demand; - STmplist *pat; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Parser *parser = &pr->parser; - /* Extract data from tokens */ - n = par->Ntokens; - if (n < 2) - return (201); - if (!getfloat(par->Tok[1], &y)) - return (202); + int j, n, p = 0; + double y; + Pdemand demand; + Pdemand cur_demand; + STmplist *patlist; - /* If MULTIPLY command, save multiplier */ - if (match(par->Tok[0], w_MULTIPLY)) { - if (y <= 0.0) - return (202); - else - hyd->Dmult = y; - return (0); - } + // Extract data from tokens + n = parser->Ntokens; + if (n < 2) return 201; + if (!getfloat(parser->Tok[1], &y)) return 202; - /* Otherwise find node (and pattern) being referenced */ - if ((j = findnode(net, par->Tok[0])) == 0) - return (208); - if (j > net->Njuncs) - return (208); - if (n >= 3) { - pat = findID(par->Tok[2], par->Patlist); - if (pat == NULL) - return (205); - p = pat->i; - } - - /* Replace any demand entered in [JUNCTIONS] section */ - - demand = net->Node[j].D; - if (hyd->NodeDemand[j] != MISSING) { - // first category encountered will overwrite "dummy" demand category - // with what is specified in this section - demand->Base = y; - demand->Pat = p; - strncpy(demand->Name, par->Comment, MAXMSG); - hyd->NodeDemand[j] = MISSING; // marker - next iteration will append a new category. - } - else { // add new demand to junction - cur_demand = net->Node[j].D; - while (cur_demand->next != NULL) { - cur_demand = cur_demand->next; + // If MULTIPLY command, save multiplier + if (match(parser->Tok[0], w_MULTIPLY)) + { + if (y <= 0.0) return 202; + else hyd->Dmult = y; + return 0; } - demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); - if (demand == NULL) - return (101); - demand->Base = y; - demand->Pat = p; - strncpy(demand->Name, par->Comment, MAXMSG); - demand->next = NULL; - cur_demand->next = demand; - } - return (0); -} /* end of demanddata */ -int controldata(EN_Project *pr) + // Otherwise find node (and pattern) being referenced + if ((j = findnode(net, parser->Tok[0])) == 0) return 208; + if (j > net->Njuncs) return 208; + if (n >= 3) + { + patlist = getlistitem(parser->Tok[2], parser->Patlist); + if (patlist == NULL) return 205; + p = patlist->i; + } + + // Replace any demand entered in [JUNCTIONS] section + demand = net->Node[j].D; + if (hyd->NodeDemand[j] != MISSING) + { + // First category encountered will overwrite "dummy" demand category + // with what is specified in this section + demand->Base = y; + demand->Pat = p; + strncpy(demand->Name, parser->Comment, MAXMSG); + hyd->NodeDemand[j] = MISSING; // marker - next iteration will append a new category. + } + + // Otherwise add new demand to junction + else + { + cur_demand = net->Node[j].D; + while (cur_demand->next != NULL) cur_demand = cur_demand->next; + demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); + if (demand == NULL) return 101; + demand->Base = y; + demand->Pat = p; + strncpy(demand->Name, parser->Comment, MAXMSG); + demand->next = NULL; + cur_demand->next = demand; + } + return 0; +} + +int controldata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -811,126 +741,103 @@ int controldata(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Parser *parser = &pr->parser; - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + int i = 0, // Node index + k, // Link index + n; // # data items + double setting = MISSING, // Link setting + time = 0.0, // Simulation time + level = 0.0; // Pressure or tank level + StatusType status = ACTIVE; // Link status + ControlType ctltype; // Control type + LinkType linktype; // Link type + Scontrol *control; - int i = 0, /* Node index */ - k, /* Link index */ - n; /* # data items */ - StatType status = ACTIVE; /* Link status */ - ControlType c_type; /* control type */ - LinkType l_type; /* Link Type */ - double setting = MISSING, /* Link setting */ - time = 0.0, /* Simulation time */ - level = 0.0; /* Pressure or tank level */ - Scontrol *control; - - /* Check for sufficient number of input tokens */ - n = par->Ntokens; - if (n < 6) - return (201); + // Check for sufficient number of input tokens + n = parser->Ntokens; + if (n < 6) return 201; - /* Check that controlled link exists */ - k = findlink(net,par->Tok[1]); - if (k == 0) - return (204); - l_type = net->Link[k].Type; - if (l_type == CVPIPE) { - return (207); /* Cannot control check valve. */ - } - /*** Updated 9/7/00 ***/ - /* Parse control setting into a status level or numerical setting. */ - if (match(par->Tok[2], w_OPEN)) { - status = OPEN; - if (l_type == PUMP) - setting = 1.0; - if (l_type == GPV) - setting = net->Link[k].Kc; - } else if (match(par->Tok[2], w_CLOSED)) { - status = CLOSED; - if (l_type == PUMP) - setting = 0.0; - if (l_type == GPV) - setting = net->Link[k].Kc; - } else if (l_type == GPV) { - return (206); - } else if (!getfloat(par->Tok[2], &setting)) { - return (202); - } + // Check that controlled link exists + k = findlink(net, parser->Tok[1]); + if (k == 0) return 204; - /*** Updated 3/1/01 ***/ - /* Set status for pump in case speed setting was supplied */ - /* or for pipe if numerical setting was supplied */ + // Cannot control a check valve + linktype = net->Link[k].Type; + if (linktype == CVPIPE) return 207; - if (l_type == PUMP || l_type == PIPE) { - if (setting != MISSING) { - if (setting < 0.0) - return (202); - else if (setting == 0.0) - status = CLOSED; - else + // Parse control setting into a status level or numerical setting + if (match(parser->Tok[2], w_OPEN)) + { status = OPEN; + if (linktype == PUMP) setting = 1.0; + if (linktype == GPV) setting = net->Link[k].Kc; } - } + else if (match(parser->Tok[2], w_CLOSED)) + { + status = CLOSED; + if (linktype == PUMP) setting = 0.0; + if (linktype == GPV) setting = net->Link[k].Kc; + } + else if (linktype == GPV) return 206; + else if (!getfloat(parser->Tok[2], &setting)) return 202; - /* Determine type of control */ - if (match(par->Tok[4], w_TIME)) { - c_type = TIMER; - } else if (match(par->Tok[4], w_CLOCKTIME)) { - c_type = TIMEOFDAY; - } else { - if (n < 8) - return (201); + // Set status for pump in case speed setting was supplied + // or for pipe if numerical setting was supplied + if (linktype == PUMP || linktype == PIPE) + { + if (setting != MISSING) + { + if (setting < 0.0) return 202; + else if (setting == 0.0) status = CLOSED; + else status = OPEN; + } + } - if ((i = findnode(net, par->Tok[5])) == 0) - return (203); - - if (match(par->Tok[6], w_BELOW)) - c_type = LOWLEVEL; - else if (match(par->Tok[6], w_ABOVE)) - c_type = HILEVEL; + // Determine type of control + if (match(parser->Tok[4], w_TIME)) ctltype = TIMER; + else if (match(parser->Tok[4], w_CLOCKTIME)) ctltype = TIMEOFDAY; else - return (201); + { + if (n < 8) return 201; + if ((i = findnode(net, parser->Tok[5])) == 0) return 203; + if (match(parser->Tok[6], w_BELOW)) ctltype = LOWLEVEL; + else if (match(parser->Tok[6], w_ABOVE)) ctltype = HILEVEL; + else return 201; } - /* Parse control level or time */ - switch (c_type) { - case TIMER: - case TIMEOFDAY: - if (n == 6) - time = hour(par->Tok[5], ""); - if (n == 7) - time = hour(par->Tok[5], par->Tok[6]); - if (time < 0.0) - return (201); - break; - case LOWLEVEL: - case HILEVEL: - if (!getfloat(par->Tok[7], &level)) - return (202); - break; - } + // Parse control level or time + switch (ctltype) + { + case TIMER: + case TIMEOFDAY: + if (n == 6) time = hour(parser->Tok[5], ""); + if (n == 7) time = hour(parser->Tok[5], parser->Tok[6]); + if (time < 0.0) return 201; + break; + case LOWLEVEL: + case HILEVEL: + if (!getfloat(parser->Tok[7], &level)) return 202; + break; + } - /* Fill in fields of control data structure */ - net->Ncontrols++; - if (net->Ncontrols > par->MaxControls) - return (200); - - control = &net->Control[net->Ncontrols]; - control->Link = k; - control->Node = i; - control->Type = c_type; - control->Status = status; - control->Setting = setting; - control->Time = (long)(3600.0 * time); - if (c_type == TIMEOFDAY) - control->Time %= SECperDAY; - control->Grade = level; - return (0); -} /* end of controldata */ + // Fill in fields of control data structure + net->Ncontrols++; + if (net->Ncontrols > parser->MaxControls) return 200; + control = &net->Control[net->Ncontrols]; + control->Link = k; + control->Node = i; + control->Type = ctltype; + control->Status = status; + control->Setting = setting; + control->Time = (long)(3600.0 * time); + if (ctltype == TIMEOFDAY) control->Time %= SECperDAY; + control->Grade = level; + return 0; +} -int sourcedata(EN_Project *pr) +int sourcedata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -944,59 +851,59 @@ int sourcedata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Parser *parser = &pr->parser; - int i, /* Token with quality value */ - j, /* Node index */ - n, /* # data items */ - p = 0; /* Time pattern */ - char type = CONCEN; /* Source type */ - double c0 = 0; /* Init. quality */ - STmplist *pat; - Psource source; + int i, // Token with quality value + j, // Node index + n, // # data items + p = 0; // Time pattern index + char type = CONCEN; // Source type + double c0 = 0; // Initial quality + STmplist *patlist; + Psource source; - n = par->Ntokens; - if (n < 2) - return (201); - if ((j = findnode(net, par->Tok[0])) == 0) - return (203); - /* NOTE: Under old format, SourceType not supplied so let */ - /* i = index of token that contains quality value. */ - i = 2; - if (match(par->Tok[1], w_CONCEN)) - type = CONCEN; - else if (match(par->Tok[1], w_MASS)) - type = MASS; - else if (match(par->Tok[1], w_SETPOINT)) - type = SETPOINT; - else if (match(par->Tok[1], w_FLOWPACED)) - type = FLOWPACED; - else - i = 1; - if (!getfloat(par->Tok[i], &c0)) - return (202); /* Illegal WQ value */ + // Check for enough tokens & that source node exists + n = parser->Ntokens; + if (n < 2) return 201; + if ((j = findnode(net, parser->Tok[0])) == 0) return 203; - if (n > i + 1 && strlen(par->Tok[i + 1]) > 0 && - strcmp(par->Tok[i + 1], "*") != 0) - { - pat = findID(par->Tok[i + 1], par->Patlist); - if (pat == NULL) - return (205); /* Illegal pattern. */ - p = pat->i; - } + // Parse source type + // NOTE: Under old 1.1 format, SourceType not supplied so + // let i = index of token that contains quality value + i = 2; + if (match(parser->Tok[1], w_CONCEN)) type = CONCEN; + else if (match(parser->Tok[1], w_MASS)) type = MASS; + else if (match(parser->Tok[1], w_SETPOINT)) type = SETPOINT; + else if (match(parser->Tok[1], w_FLOWPACED)) type = FLOWPACED; + else i = 1; - source = (struct Ssource *)malloc(sizeof(struct Ssource)); - if (source == NULL) - return (101); - source->C0 = c0; - source->Pat = p; - source->Type = type; - net->Node[j].S = source; - return (0); -} /* end of sourcedata */ + // Parse source quality + if (!getfloat(parser->Tok[i], &c0)) return 202; -int emitterdata(EN_Project *pr) + // Parse optional source time pattern + 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 (205); + p = patlist->i; + } + + // Destroy any existing source assigned to node + if (net->Node[j].S != NULL) free(net->Node[j].S); + + // Create a new source & assign it to the node + source = (struct Ssource *)malloc(sizeof(struct Ssource)); + if (source == NULL) return 101; + source->C0 = c0; + source->Pat = p; + source->Type = type; + net->Node[j].S = source; + return 0; +} + +int emitterdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -1008,29 +915,27 @@ int emitterdata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - - int j, /* Node index */ - n; /* # data items */ - double k; /* Flow coeff, */ + Network *net = &pr->network; + Parser *parser = &pr->parser; + + int j, // Node index + n; // # data items + double k; // Flow coeff. - n = par->Ntokens; - if (n < 2) - return (201); - if ((j = findnode(net,par->Tok[0])) == 0) - return (203); - if (j > net->Njuncs) - return (209); /* Not a junction.*/ - if (!getfloat(par->Tok[1], &k)) - return (202); - if (k < 0.0) - return (202); - net->Node[j].Ke = k; - return (0); + // Check that node exists & is a junction + n = parser->Ntokens; + if (n < 2) return 201; + if ((j = findnode(net, parser->Tok[0])) == 0) return 203; + if (j > net->Njuncs) return 209; + + // Parse emitter flow coeff. + if (!getfloat(parser->Tok[1], &k)) return 202; + if (k < 0.0) return 202; + net->Node[j].Ke = k; + return 0; } -int qualdata(EN_Project *pr) +int qualdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -1043,49 +948,59 @@ int qualdata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - Snode *Node = net->Node; + Network *net = &pr->network; + Parser *parser = &pr->parser; + + int j, n; + long i, i1, i2; + double c0; + Snode *Node = net->Node; - int j, n; - long i, i0, i1; - double c0; + if (net->Nnodes == 0) return 208; // No nodes defined yet + n = parser->Ntokens; + if (n < 2) return 0; - if (net->Nnodes == 0) - return (208); /* No nodes defined yet */ - n = par->Ntokens; - if (n < 2) - return (0); - if (n == 2) /* Single node entered */ - { - if ((j = findnode(net,par->Tok[0])) == 0) - return (0); - if (!getfloat(par->Tok[1], &c0)) - return (209); - Node[j].C0 = c0; - } else /* Node range entered */ - { - if (!getfloat(par->Tok[2], &c0)) - return (209); - - /* If numerical range supplied, then use numerical comparison */ - if ((i0 = atol(par->Tok[0])) > 0 && (i1 = atol(par->Tok[1])) > 0) { - for (j = 1; j <= net->Nnodes; j++) { - i = atol(Node[j].ID); - if (i >= i0 && i <= i1) - Node[j].C0 = c0; - } - } else { - for (j = 1; j <= net->Nnodes; j++) - if ((strcmp(par->Tok[0], Node[j].ID) <= 0) && - (strcmp(par->Tok[1], Node[j].ID) >= 0)) - Node[j].C0 = c0; + // Single node name supplied + if (n == 2) + { + if ((j = findnode(net,parser->Tok[0])) == 0) return 0; + if (!getfloat(parser->Tok[1], &c0)) return 209; + Node[j].C0 = c0; } - } - return (0); -} /* end of qualdata */ -int reactdata(EN_Project *pr) + // Range of node names supplied + else + { + // Parse quality value + if (!getfloat(parser->Tok[2], &c0)) return 209; + + // If numerical node names supplied, then use numerical comparison + // to find which nodes are assigned the quality value + if ((i1 = atol(parser->Tok[0])) > 0 && + (i2 = atol(parser->Tok[1])) > 0) + { + for (j = 1; j <= net->Nnodes; j++) + { + i = atol(Node[j].ID); + if (i >= i1 && i <= i2) Node[j].C0 = c0; + } + } + + // Otherwise use lexicographic comparison + else + { + for (j = 1; j <= net->Nnodes; j++) + { + if ((strcmp(parser->Tok[0], Node[j].ID) <= 0) && + (strcmp(parser->Tok[1], Node[j].ID) >= 0) + ) Node[j].C0 = c0; + } + } + } + return 0; +} + +int reactdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -1104,139 +1019,147 @@ int reactdata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - quality_t *qu = &pr->quality; - parser_data_t *par = &pr->parser; - - int item, j, n; - long i, i1, i2; - double y; + Network *net = &pr->network; + Quality *qual = &pr->quality; + Parser *parser = &pr->parser; - /* Skip line if insufficient data */ - n = par->Ntokens; - if (n < 3) - return (0); + int item, j, n; + long i, i1, i2; + double y; - /* Process input depending on keyword */ - if (match(par->Tok[0], w_ORDER)) /* Reaction order */ - { - if (!getfloat(par->Tok[n - 1], &y)) - return (213); - if (match(par->Tok[1], w_BULK)) - qu->BulkOrder = y; - else if (match(par->Tok[1], w_TANK)) - qu->TankOrder = y; - else if (match(par->Tok[1], w_WALL)) { - if (y == 0.0) - qu->WallOrder = 0.0; - else if (y == 1.0) - qu->WallOrder = 1.0; - else - return (213); - } else - return (213); - return (0); - } - if (match(par->Tok[0], w_ROUGHNESS)) /* Roughness factor */ - { - if (!getfloat(par->Tok[n - 1], &y)) - return (213); - qu->Rfactor = y; - return (0); - } - if (match(par->Tok[0], w_LIMITING)) /* Limiting potential */ - { - if (!getfloat(par->Tok[n - 1], &y)) - return (213); - /*if (y < 0.0) return(213);*/ - qu->Climit = y; - return (0); - } - if (match(par->Tok[0], w_GLOBAL)) /* Global rates */ - { - if (!getfloat(par->Tok[n - 1], &y)) - return (213); - if (match(par->Tok[1], w_BULK)) - qu->Kbulk = y; - else if (match(par->Tok[1], w_WALL)) - qu->Kwall = y; + // Skip line if insufficient data + n = parser->Ntokens; + if (n < 3) return 0; + + // Keyword is ORDER + if (match(parser->Tok[0], w_ORDER)) + { + if (!getfloat(parser->Tok[n - 1], &y)) return 213; + if (match(parser->Tok[1], w_BULK)) qual->BulkOrder = y; + else if (match(parser->Tok[1], w_TANK)) qual->TankOrder = y; + else if (match(parser->Tok[1], w_WALL)) + { + if (y == 0.0) qual->WallOrder = 0.0; + else if (y == 1.0) qual->WallOrder = 1.0; + else return 213; + } + else return 213; + return 0; + } + + // Keyword is ROUGHNESS + if (match(parser->Tok[0], w_ROUGHNESS)) + { + if (!getfloat(parser->Tok[n - 1], &y)) return 213; + qual->Rfactor = y; + return 0; + } + + // Keyword is LIMITING + if (match(parser->Tok[0], w_LIMITING)) + { + if (!getfloat(parser->Tok[n - 1], &y)) return 213; + qual->Climit = y; + return 0; + } + + // Keyword is GLOBAL + if (match(parser->Tok[0], w_GLOBAL)) + { + if (!getfloat(parser->Tok[n - 1], &y)) return 213; + if (match(parser->Tok[1], w_BULK)) qual->Kbulk = y; + else if (match(parser->Tok[1], w_WALL)) qual->Kwall = y; + else return 201; + return 0; + } + + // Keyword is BULK, WALL or TANK + if (match(parser->Tok[0], w_BULK)) item = 1; + else if (match(parser->Tok[0], w_WALL)) item = 2; + else if (match(parser->Tok[0], w_TANK)) item = 3; + else return 201; + + // Save the first link/node ID in the first token + strcpy(parser->Tok[0], parser->Tok[1]); + + // Case where tank rate coeffs. are being set + if (item == 3) + { + // Get the rate coeff. value + if (!getfloat(parser->Tok[n - 1], &y)) return 209; + + // Case where just a single tank is specified + if (n == 3) + { + if ((j = findnode(net,parser->Tok[1])) <= net->Njuncs) return 0; + net->Tank[j - net->Njuncs].Kb = y; + } + + // Case where a numerical range of tank IDs is specified + else if ((i1 = atol(parser->Tok[1])) > 0 && + (i2 = atol(parser->Tok[2])) > 0) + { + for (j = net->Njuncs + 1; j <= net->Nnodes; j++) + { + i = atol(net->Node[j].ID); + if (i >= i1 && i <= i2) net->Tank[j - net->Njuncs].Kb = y; + } + } + + // Case where a general range of tank IDs is specified + else for (j = net->Njuncs + 1; j <= net->Nnodes; j++) + { + if ((strcmp(parser->Tok[1], net->Node[j].ID) <= 0) && + (strcmp(parser->Tok[2], net->Node[j].ID) >= 0) + ) net->Tank[j - net->Njuncs].Kb = y; + } + } + + // Case where pipe rate coeffs. are being set else - return (201); - return (0); - } - if (match(par->Tok[0], w_BULK)) - item = 1; /* Individual rates */ - else if (match(par->Tok[0], w_WALL)) - item = 2; - else if (match(par->Tok[0], w_TANK)) - item = 3; - else - return (201); - strcpy(par->Tok[0], par->Tok[1]); /* Save id in par->Tok[0] */ - if (item == 3) /* Tank rates */ - { - if (!getfloat(par->Tok[n - 1], &y)) - return (209); /* Rate coeff. */ - if (n == 3) { - if ((j = findnode(net,par->Tok[1])) <= net->Njuncs) - return (0); - net->Tank[j - net->Njuncs].Kb = y; - } else { - /* If numerical range supplied, then use numerical comparison */ - if ((i1 = atol(par->Tok[1])) > 0 && (i2 = atol(par->Tok[2])) > 0) { - for (j = net->Njuncs + 1; j <= net->Nnodes; j++) { - i = atol(net->Node[j].ID); - if (i >= i1 && i <= i2) - net->Tank[j - net->Njuncs].Kb = y; - } - } else - for (j = net->Njuncs + 1; j <= net->Nnodes; j++) - if ((strcmp(par->Tok[1], net->Node[j].ID) <= 0) && - (strcmp(par->Tok[2], net->Node[j].ID) >= 0)) - net->Tank[j - net->Njuncs].Kb = y; - } - } else /* Link rates */ - { - if (!getfloat(par->Tok[n - 1], &y)) - return (211); /* Rate coeff. */ - if (net->Nlinks == 0) - return (0); - if (n == 3) /* Single link */ { - if ((j = findlink(net, par->Tok[1])) == 0) - return (0); - if (item == 1) - net->Link[j].Kb = y; - else - net->Link[j].Kw = y; - } else /* Range of links */ - { - /* If numerical range supplied, then use numerical comparison */ - if ((i1 = atol(par->Tok[1])) > 0 && (i2 = atol(par->Tok[2])) > 0) { - for (j = 1; j <= net->Nlinks; j++) { - i = atol(net->Link[j].ID); - if (i >= i1 && i <= i2) { - if (item == 1) - net->Link[j].Kb = y; - else - net->Link[j].Kw = y; - } - } - } else - for (j = 1; j <= net->Nlinks; j++) - if ((strcmp(par->Tok[1], net->Link[j].ID) <= 0) && - (strcmp(par->Tok[2], net->Link[j].ID) >= 0)) { - if (item == 1) - net->Link[j].Kb = y; - else - net->Link[j].Kw = y; - } - } - } - return (0); -} /* end of reactdata */ + // Get the rate coeff. value + if (!getfloat(parser->Tok[n - 1], &y)) return 211; /* Rate coeff. */ + if (net->Nlinks == 0) return 0; -int mixingdata(EN_Project *pr) + // Case where just a single link is specified + if (n == 3) + { + if ((j = findlink(net, parser->Tok[1])) == 0) return 0; + if (item == 1) net->Link[j].Kb = y; + else net->Link[j].Kw = y; + } + + // Case where a numerical range of link IDs is specified + else if ((i1 = atol(parser->Tok[1])) > 0 && + (i2 = atol(parser->Tok[2])) > 0) + { + for (j = 1; j <= net->Nlinks; j++) + { + i = atol(net->Link[j].ID); + if (i >= i1 && i <= i2) + { + if (item == 1) net->Link[j].Kb = y; + else net->Link[j].Kw = y; + } + } + } + + // Case where a general range of link IDs is specified + else for (j = 1; j <= net->Nlinks; j++) + { + if ((strcmp(parser->Tok[1], net->Link[j].ID) <= 0) && + (strcmp(parser->Tok[2], net->Link[j].ID) >= 0)) + { + if (item == 1) net->Link[j].Kb = y; + else net->Link[j].Kw = y; + } + } + } + return 0; +} + +int mixingdata(Project *pr) /* **------------------------------------------------------------- ** Input: none @@ -1248,38 +1171,36 @@ int mixingdata(EN_Project *pr) **------------------------------------------------------------- */ { + Network *net = &pr->network; + Parser *parser = &pr->parser; + + int i, // Tank index + j, // Node index + m, // Type of mixing model + n; // Number of data items + double v; // Mixing zone volume fraction - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + // Check for valid data + if (net->Nnodes == 0) return 208; + n = parser->Ntokens; + if (n < 2) return 0; + if ((j = findnode(net, parser->Tok[0])) <= net->Njuncs) return 0; + if ((m = findmatch(parser->Tok[1], MixTxt)) < 0) return 201; - int i, j, n; - double v; + // Find mixing zone volume fraction (which can't be 0) + v = 1.0; + if ((m == MIX2) && (n == 3) && (!getfloat(parser->Tok[2], &v))) return 209; + if (v == 0.0) v = 1.0; - if (net->Nnodes == 0) - return (208); /* No nodes defined yet */ - n = par->Ntokens; - if (n < 2) - return (0); - if ((j = findnode(net, par->Tok[0])) <= net->Njuncs) - return (0); - if ((i = findmatch(par->Tok[1], MixTxt)) < 0) - return (201); - v = 1.0; - if ((i == MIX2) && (n == 3) && - (!getfloat(par->Tok[2], &v)) /* Get frac. vol. for 2COMP model */ - ) - return (209); - if (v == 0.0) - v = 1.0; /* v can't be zero */ - n = j - net->Njuncs; - if (net->Tank[n].A == 0.0) - return (0); /* Tank is a reservoir */ - net->Tank[n].MixModel = (char)i; - net->Tank[n].V1max = v; - return (0); + // Assign mixing data to tank (return if tank is a reservoir) + i = j - net->Njuncs; + if (net->Tank[i].A == 0.0) return 0; + net->Tank[i].MixModel = (char)m; + net->Tank[i].V1max = v; + return 0; } -int statusdata(EN_Project *pr) +int statusdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -1292,65 +1213,59 @@ int statusdata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Parser *parser = &pr->parser; + + int j, n; + long i, i1, i2; + double y = 0.0; + char status = ACTIVE; - int j, n; - long i, i0, i1; - double y = 0.0; - char status = ACTIVE; + if (net->Nlinks == 0) return 210; + n = parser->Ntokens - 1; + if (n < 1) return 201; - if (net->Nlinks == 0) - return (210); - n = par->Ntokens - 1; - if (n < 1) - return (201); + // Check for legal status setting + if (match(parser->Tok[n], w_OPEN)) status = OPEN; + else if (match(parser->Tok[n], w_CLOSED)) status = CLOSED; + else if (!getfloat(parser->Tok[n], &y)) return 211; + if (y < 0.0) return 211; - /* Check for legal status setting */ - if (match(par->Tok[n], w_OPEN)) - status = OPEN; - else if (match(par->Tok[n], w_CLOSED)) - status = CLOSED; - else if (!getfloat(par->Tok[n], &y)) - return (211); - if (y < 0.0) - return (211); + // A single link ID was supplied + if (n == 1) + { + if ((j = findlink(net, parser->Tok[0])) == 0) return 0; + + // Cannot change status of a Check Valve + if (net->Link[j].Type == CVPIPE) return 211; - /* Single link ID supplied */ - if (n == 1) { - if ((j = findlink(net, par->Tok[0])) == 0) - return (0); - /* Cannot change status of a Check Valve */ - if (net->Link[j].Type == CVPIPE) - return (211); + // Cannot change setting for a GPV + if (net->Link[j].Type == GPV && status == ACTIVE) return 211; + changestatus(net, j, status, y); + } - /*** Updated 9/7/00 ***/ - /* Cannot change setting for a GPV */ - if (net->Link[j].Type == GPV && status == ACTIVE) - return (211); + // A range of numerical link ID's was supplied + else if ((i1 = atol(parser->Tok[0])) > 0 && + (i2 = atol(parser->Tok[1])) > 0) + { + for (j = 1; j <= net->Nlinks; j++) + { + i = atol(net->Link[j].ID); + if (i >= i1 && i <= i2) changestatus(net, j, status, y); + } + } + + // A range of general link ID's was supplied + else for (j = 1; j <= net->Nlinks; j++) + { + if ((strcmp(parser->Tok[0], net->Link[j].ID) <= 0) && + (strcmp(parser->Tok[1], net->Link[j].ID) >= 0) + ) changestatus(net, j, status, y); + } + return 0; +} - changestatus(net, j, status, y); - } - - /* Range of ID's supplied */ - else { - /* Numerical range supplied */ - if ((i0 = atol(par->Tok[0])) > 0 && (i1 = atol(par->Tok[1])) > 0) { - for (j = 1; j <= net->Nlinks; j++) { - i = atol(net->Link[j].ID); - if (i >= i0 && i <= i1) - changestatus(net, j, status, y); - } - } else - for (j = 1; j <= net->Nlinks; j++) - if ((strcmp(par->Tok[0], net->Link[j].ID) <= 0) && - (strcmp(par->Tok[1], net->Link[j].ID) >= 0)) - changestatus(net, j, status, y); - } - return (0); -} /* end of statusdata */ - -int energydata(EN_Project *pr) +int energydata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -1364,95 +1279,95 @@ int energydata(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Parser *parser = &pr->parser; - Slink *Link = net->Link; - Spump *Pump = net->Pump; + int j, k, n; + double y; + + STmplist *listitem; + Slink *Link = net->Link; + Spump *Pump = net->Pump; - int j, k, n; - double y; - STmplist *t; + // Check for sufficient data + n = parser->Ntokens; + if (n < 3) return 201; - /* Check for sufficient data */ - n = par->Ntokens; - if (n < 3) - return (201); + // First keyword is DEMAND + if (match(parser->Tok[0], w_DMNDCHARGE)) + { + if (!getfloat(parser->Tok[2], &y)) return 213; + hyd->Dcost = y; + return 0; + } - /* Check first keyword */ - if (match(par->Tok[0], w_DMNDCHARGE)) /* Demand charge */ - { - if (!getfloat(par->Tok[2], &y)) - return (213); - hyd->Dcost = y; - return (0); - } - if (match(par->Tok[0], w_GLOBAL)) /* Global parameter */ - { - j = 0; - } else if (match(par->Tok[0], w_PUMP)) /* Pump-specific parameter */ - { - if (n < 4) - return (201); - k = findlink(net,par->Tok[1]); /* Check that pump exists */ - if (k == 0) - return (216); - if (Link[k].Type != PUMP) - return (216); - j = findpump(net, k); - } else - return (201); + // First keyword is GLOBAL (remaining data refer to global options) + if (match(parser->Tok[0], w_GLOBAL)) + { + j = 0; + } + + // First keyword is PUMP (remaining data refer to a specific pump) + else if (match(parser->Tok[0], w_PUMP)) + { + if (n < 4) return 201; + k = findlink(net,parser->Tok[1]); + if (k == 0) return 216; + if (Link[k].Type != PUMP) return 216; + j = findpump(net, k); + } + else return 201; - /* Find type of energy parameter */ - if (match(par->Tok[n - 2], w_PRICE)) /* Energy price */ - { - if (!getfloat(par->Tok[n - 1], &y)) { - if (j == 0) - return (213); - else - return (217); + // PRICE parameter being set + if (match(parser->Tok[n - 2], w_PRICE)) + { + if (!getfloat(parser->Tok[n - 1], &y)) + { + if (j == 0) return 213; + else return 217; + } + if (j == 0) hyd->Ecost = y; + else Pump[j].Ecost = y; + return 0; } - if (j == 0) - hyd->Ecost = y; - else - Pump[j].Ecost = y; - return (0); - } else if (match(par->Tok[n - 2], w_PATTERN)) /* Price pattern */ - { - t = findID(par->Tok[n - 1], par->Patlist); /* Check if pattern exists */ - if (t == NULL) { - if (j == 0) - return (213); - else - return (217); + + // Price PATTERN being set + else if (match(parser->Tok[n - 2], w_PATTERN)) + { + listitem = getlistitem(parser->Tok[n - 1], parser->Patlist); + if (listitem == NULL) + { + if (j == 0) return 213; + else return 217; + } + if (j == 0) hyd->Epat = listitem->i; + else Pump[j].Epat = listitem->i; + return 0; } - if (j == 0) - hyd->Epat = t->i; - else - Pump[j].Epat = t->i; - return (0); - } else if (match(par->Tok[n - 2], w_EFFIC)) /* Pump efficiency */ - { - if (j == 0) { - if (!getfloat(par->Tok[n - 1], &y)) - return (213); - if (y <= 0.0) - return (213); - hyd->Epump = y; - } else { - t = findID(par->Tok[n - 1], par->Curvelist); /* Check if curve exists */ - if (t == NULL) - return (217); - Pump[j].Ecurve = t->i; - net->Curve[t->i].Type = E_CURVE; + + // Pump EFFIC being set + else if (match(parser->Tok[n - 2], w_EFFIC)) + { + if (j == 0) + { + if (!getfloat(parser->Tok[n - 1], &y)) return 213; + if (y <= 0.0) return 213; + hyd->Epump = y; + } + else + { + listitem = getlistitem(parser->Tok[n - 1], parser->Curvelist); + if (listitem == NULL) return 217; + Pump[j].Ecurve = listitem->i; + net->Curve[listitem->i].Type = E_CURVE; + } + return 0; } - return (0); - } - return (201); + return 201; } -int reportdata(EN_Project *pr) +int reportdata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -1474,158 +1389,143 @@ int reportdata(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Report *rpt = &pr->report; + Parser *parser = &pr->parser; + + int i, j, n; + double y; - EN_Network *net = &pr->network; - report_options_t *rep = &pr->report; - parser_data_t *par = &pr->parser; + n = parser->Ntokens - 1; + if (n < 1) return 201; - - int i, j, n; - double y; - - n = par->Ntokens - 1; - if (n < 1) - return (201); - - /* Value for page size */ - if (match(par->Tok[0], w_PAGE)) { - if (!getfloat(par->Tok[n], &y)) - return (213); - if (y < 0.0 || y > 255.0) - return (213); - rep->PageSize = (int)y; - return (0); - } - - /* Request that status reports be written */ - if (match(par->Tok[0], w_STATUS)) { - if (match(par->Tok[n], w_NO)) - rep->Statflag = FALSE; - if (match(par->Tok[n], w_YES)) - rep->Statflag = TRUE; - if (match(par->Tok[n], w_FULL)) - rep->Statflag = FULL; - return (0); - } - - /* Request summary report */ - if (match(par->Tok[0], w_SUMMARY)) { - if (match(par->Tok[n], w_NO)) - rep->Summaryflag = FALSE; - if (match(par->Tok[n], w_YES)) - rep->Summaryflag = TRUE; - return (0); - } - - /* Request error/warning message reporting */ - if (match(par->Tok[0], w_MESSAGES)) { - if (match(par->Tok[n], w_NO)) - rep->Messageflag = FALSE; - if (match(par->Tok[n], w_YES)) - rep->Messageflag = TRUE; - return (0); - } - - /* Request an energy usage report */ - if (match(par->Tok[0], w_ENERGY)) { - if (match(par->Tok[n], w_NO)) - rep->Energyflag = FALSE; - if (match(par->Tok[n], w_YES)) - rep->Energyflag = TRUE; - return (0); - } - - /* Particular reporting nodes specified */ - if (match(par->Tok[0], w_NODE)) { - if (match(par->Tok[n], w_NONE)) - rep->Nodeflag = 0; /* No nodes */ - else if (match(par->Tok[n], w_ALL)) - rep->Nodeflag = 1; /* All nodes */ - else { - if (net->Nnodes == 0) - return (208); - for (i = 1; i <= n; i++) { - if ((j = findnode(net,par->Tok[i])) == 0) - return (208); - net->Node[j].Rpt = 1; - } - rep->Nodeflag = 2; + // Value for page size + if (match(parser->Tok[0], w_PAGE)) + { + if (!getfloat(parser->Tok[n], &y)) return 213; + if (y < 0.0 || y > 255.0) return 213; + rpt->PageSize = (int)y; + return 0; } - return (0); - } - /* Particular reporting links specified */ - if (match(par->Tok[0], w_LINK)) { - if (match(par->Tok[n], w_NONE)) - rep->Linkflag = 0; - else if (match(par->Tok[n], w_ALL)) - rep->Linkflag = 1; - else { - if (net->Nlinks == 0) - return (210); - for (i = 1; i <= n; i++) { - if ((j = findlink(net,par->Tok[i])) == 0) - return (210); - net->Link[j].Rpt = 1; - } - rep->Linkflag = 2; + // Request that status reports be written + if (match(parser->Tok[0], w_STATUS)) + { + if (match(parser->Tok[n], w_NO)) rpt->Statflag = FALSE; + if (match(parser->Tok[n], w_YES)) rpt->Statflag = TRUE; + if (match(parser->Tok[n], w_FULL)) rpt->Statflag = FULL; + return 0; } - return (0); - } - /* Check if input is a reporting criterion. */ - - /*** Special case needed to distinguish "HEAD" from "HEADLOSS" ***/ //(2.00.11 - //- LR) - if (strcomp(par->Tok[0], t_HEADLOSS)) - i = HEADLOSS; - else - i = findmatch(par->Tok[0], Fldname); - if (i >= 0) - /*****************************************************************/ //(2.00.11 - //- LR) - { - if (i > FRICTION) - return (201); - if (par->Ntokens == 1 || match(par->Tok[1], w_YES)) { - rep->Field[i].Enabled = TRUE; - return (0); + // Request summary report + if (match(parser->Tok[0], w_SUMMARY)) + { + if (match(parser->Tok[n], w_NO)) rpt->Summaryflag = FALSE; + if (match(parser->Tok[n], w_YES)) rpt->Summaryflag = TRUE; + return 0; } - if (match(par->Tok[1], w_NO)) { - rep->Field[i].Enabled = FALSE; - return (0); + + // Request error/warning message reporting + if (match(parser->Tok[0], w_MESSAGES)) + { + if (match(parser->Tok[n], w_NO)) rpt->Messageflag = FALSE; + if (match(parser->Tok[n], w_YES)) rpt->Messageflag = TRUE; + return 0; } - if (par->Ntokens < 3) - return (201); - if (match(par->Tok[1], w_BELOW)) - j = LOW; /* Get relation operator */ - else if (match(par->Tok[1], w_ABOVE)) - j = HI; /* or precision keyword */ - else if (match(par->Tok[1], w_PRECISION)) - j = PREC; - else - return (201); - if (!getfloat(par->Tok[2], &y)) - return (201); - if (j == PREC) { - rep->Field[i].Enabled = TRUE; - rep->Field[i].Precision = ROUND(y); - } else - rep->Field[i].RptLim[j] = y; /* Report limit value */ - return (0); - } - /* Name of external report file */ - if (match(par->Tok[0], w_FILE)) { - strncpy(rep->Rpt2Fname, par->Tok[1], MAXFNAME); - return (0); - } + // Request an energy usage report + if (match(parser->Tok[0], w_ENERGY)) + { + if (match(parser->Tok[n], w_NO)) rpt->Energyflag = FALSE; + if (match(parser->Tok[n], w_YES)) rpt->Energyflag = TRUE; + return 0; + } - /* If get to here then return error condition */ - return (201); -} /* end of reportdata */ + // Particular reporting nodes specified + if (match(parser->Tok[0], w_NODE)) + { + if (match(parser->Tok[n], w_NONE)) rpt->Nodeflag = 0; // No nodes + else if (match(parser->Tok[n], w_ALL)) rpt->Nodeflag = 1; // All nodes + else + { + if (net->Nnodes == 0) return 208; + for (i = 1; i <= n; i++) + { + if ((j = findnode(net, parser->Tok[i])) == 0) return 208; + net->Node[j].Rpt = 1; + } + rpt->Nodeflag = 2; + } + return 0; + } -int timedata(EN_Project *pr) + // Particular reporting links specified + if (match(parser->Tok[0], w_LINK)) + { + if (match(parser->Tok[n], w_NONE)) rpt->Linkflag = 0; + else if (match(parser->Tok[n], w_ALL)) rpt->Linkflag = 1; + else + { + if (net->Nlinks == 0) return 210; + for (i = 1; i <= n; i++) + { + if ((j = findlink(net, parser->Tok[i])) == 0) return 210; + net->Link[j].Rpt = 1; + } + rpt->Linkflag = 2; + } + return 0; + } + + // Report fields specified + // Special case needed to distinguish "HEAD" from "HEADLOSS" + if (strcomp(parser->Tok[0], t_HEADLOSS)) i = HEADLOSS; + else i = findmatch(parser->Tok[0], Fldname); + if (i >= 0) + { + if (i > FRICTION) return 201; + if (parser->Ntokens == 1 || match(parser->Tok[1], w_YES)) + { + rpt->Field[i].Enabled = TRUE; + return 0; + } + + if (match(parser->Tok[1], w_NO)) + { + rpt->Field[i].Enabled = FALSE; + return 0; + } + + // Get field qualifier type + if (parser->Ntokens < 3) return (201); + if (match(parser->Tok[1], w_BELOW)) j = LOW; + else if (match(parser->Tok[1], w_ABOVE)) j = HI; + else if (match(parser->Tok[1], w_PRECISION)) j = PREC; + else return 201; + + // Get field qualifier value + if (!getfloat(parser->Tok[2], &y)) return 201; + if (j == PREC) + { + rpt->Field[i].Enabled = TRUE; + rpt->Field[i].Precision = ROUND(y); + } + else rpt->Field[i].RptLim[j] = y; + return (0); + } + + // Name of external report file + if (match(parser->Tok[0], w_FILE)) + { + strncpy(rpt->Rpt2Fname, parser->Tok[1], MAXFNAME); + return 0; + } + + // If get to here then return error condition + return 201; +} + +int timedata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -1646,89 +1546,70 @@ int timedata(EN_Project *pr) **------------------------------------------------------------- */ { + Report *rpt = &pr->report; + Parser *parser = &pr->parser; + Times *time = &pr->times; - report_options_t *rep = &pr->report; - quality_t *qu = &pr->quality; - parser_data_t *par = &pr->parser; - time_options_t *time = &pr->time_options; + int n; + long t; + double y; - - int n; - long t; - double y; + n = parser->Ntokens - 1; + if (n < 1) return 201; - n = par->Ntokens - 1; - if (n < 1) - return (201); - - /* Check if setting time statistic flag */ - if (match(par->Tok[0], w_STATISTIC)) { - if (match(par->Tok[n], w_NONE)) - rep->Tstatflag = SERIES; - else if (match(par->Tok[n], w_NO)) - rep->Tstatflag = SERIES; - else if (match(par->Tok[n], w_AVG)) - rep->Tstatflag = AVG; - else if (match(par->Tok[n], w_MIN)) - rep->Tstatflag = MIN; - else if (match(par->Tok[n], w_MAX)) - rep->Tstatflag = MAX; - else if (match(par->Tok[n], w_RANGE)) - rep->Tstatflag = RANGE; - else - return (201); - return (0); - } - - /* Convert text time value to numerical value in seconds */ - /* Examples: - ** 5 = 5 * 3600 sec - ** 5 MINUTES = 5 * 60 sec - ** 13:50 = 13*3600 + 50*60 sec - ** 1:50 pm = (12+1)*3600 + 50*60 sec - */ - - if (!getfloat(par->Tok[n], &y)) { - if ((y = hour(par->Tok[n], "")) < 0.0) { - if ((y = hour(par->Tok[n - 1], par->Tok[n])) < 0.0) - return (213); + // Check if setting report time statistic flag + if (match(parser->Tok[0], w_STATISTIC)) + { + if (match(parser->Tok[n], w_NONE)) rpt->Tstatflag = SERIES; + else if (match(parser->Tok[n], w_NO)) rpt->Tstatflag = SERIES; + else if (match(parser->Tok[n], w_AVG)) rpt->Tstatflag = AVG; + else if (match(parser->Tok[n], w_MIN)) rpt->Tstatflag = MIN; + else if (match(parser->Tok[n], w_MAX)) rpt->Tstatflag = MAX; + else if (match(parser->Tok[n], w_RANGE)) rpt->Tstatflag = RANGE; + else return 201; + return 0; } - } - t = (long)(3600.0 * y + 0.5); - /* Process the value assigned to the matched parameter */ - if (match(par->Tok[0], w_DURATION)) - time->Dur = t; /* Simulation duration */ - else if (match(par->Tok[0], w_HYDRAULIC)) - time->Hstep = t; /* Hydraulic time step */ - else if (match(par->Tok[0], w_QUALITY)) - qu->Qstep = t; /* Quality time step */ - else if (match(par->Tok[0], w_RULE)) - time->Rulestep = t; /* Rule time step */ - else if (match(par->Tok[0], w_MINIMUM)) - return (0); /* Not used anymore */ - else if (match(par->Tok[0], w_PATTERN)) { - if (match(par->Tok[1], w_TIME)) - time->Pstep = t; /* Pattern time step */ - else if (match(par->Tok[1], w_START)) - time->Pstart = t; /* Pattern start time */ - else - return (201); - } else if (match(par->Tok[0], w_REPORT)) { - if (match(par->Tok[1], w_TIME)) - time->Rstep = t; /* Reporting time step */ - else if (match(par->Tok[1], w_START)) - time->Rstart = t; /* Reporting start time */ - else - return (201); - } /* Simulation start time*/ - else if (match(par->Tok[0], w_START)) - time->Tstart = t % SECperDAY; - else - return (201); - return (0); -} /* end of timedata */ -int optiondata(EN_Project *pr) + // Convert text time value to numerical value in seconds + // Examples: + // 5 = 5 * 3600 sec + // 5 MINUTES = 5 * 60 sec + // 13:50 = 13*3600 + 50*60 sec + // 1:50 pm = (12+1)*3600 + 50*60 sec + + if (!getfloat(parser->Tok[n], &y)) + { + if ((y = hour(parser->Tok[n], "")) < 0.0) + { + if ((y = hour(parser->Tok[n - 1], parser->Tok[n])) < 0.0) return 213; + } + } + t = (long)(3600.0 * y + 0.5); + + /// Process the value assigned to the matched parameter + if (match(parser->Tok[0], w_DURATION)) time->Dur = t; + else if (match(parser->Tok[0], w_HYDRAULIC)) time->Hstep = t; + else if (match(parser->Tok[0], w_QUALITY) ) time->Qstep = t; + else if (match(parser->Tok[0], w_RULE)) time->Rulestep = t; + else if (match(parser->Tok[0], w_MINIMUM)) return 0; // Not used anymore + else if (match(parser->Tok[0], w_PATTERN)) + { + if (match(parser->Tok[1], w_TIME)) time->Pstep = t; + else if (match(parser->Tok[1], w_START)) time->Pstart = t; + else return 201; + } + else if (match(parser->Tok[0], w_REPORT)) + { + if (match(parser->Tok[1], w_TIME)) time->Rstep = t; + else if (match(parser->Tok[1], w_START)) time->Rstart = t; + else return 201; + } + else if (match(parser->Tok[0], w_START)) time->Tstart = t % SECperDAY; + else return 201; + return 0; +} + +int optiondata(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -1737,18 +1618,19 @@ int optiondata(EN_Project *pr) **-------------------------------------------------------------- */ { - parser_data_t *par = &pr->parser; - - int i, n; + int i, n; + Parser *parser = &pr->parser; - n = par->Ntokens - 1; - i = optionchoice(pr,n); /* Option is a named choice */ - if (i >= 0) - return (i); - return (optionvalue(pr,n)); /* Option is a numerical value */ -} /* end of optiondata */ + // Option is a named choice + n = parser->Ntokens - 1; + i = optionchoice(pr, n); + if (i >= 0) return i; -int optionchoice(EN_Project *pr, int n) + // Option is a numerical value + return (optionvalue(pr, n)); +} + +int optionchoice(Project *pr, int n) /* **-------------------------------------------------------------- ** Input: n = index of last input token saved in par->Tok[] @@ -1769,134 +1651,147 @@ int optionchoice(EN_Project *pr, int n) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qu = &pr->quality; - parser_data_t *par = &pr->parser; - out_file_t *out = &pr->out_files; - int choice; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Parser *parser = &pr->parser; + Outfile *out = &pr->outfile; - /* Check if 1st token matches a parameter name and */ - /* process the input for the matched parameter */ - if (n < 0) return (201); - if (match(par->Tok[0], w_UNITS)) - { - if (n < 1) return (0); - else if (match(par->Tok[1], w_CFS)) par->Flowflag = CFS; - else if (match(par->Tok[1], w_GPM)) par->Flowflag = GPM; - else if (match(par->Tok[1], w_AFD)) par->Flowflag = AFD; - else if (match(par->Tok[1], w_MGD)) par->Flowflag = MGD; - else if (match(par->Tok[1], w_IMGD)) par->Flowflag = IMGD; - else if (match(par->Tok[1], w_LPS)) par->Flowflag = LPS; - else if (match(par->Tok[1], w_LPM)) par->Flowflag = LPM; - else if (match(par->Tok[1], w_CMH)) par->Flowflag = CMH; - else if (match(par->Tok[1], w_CMD)) par->Flowflag = CMD; - else if (match(par->Tok[1], w_MLD)) par->Flowflag = MLD; - else if (match(par->Tok[1], w_SI)) par->Flowflag = LPS; - else return (201); - } + int choice; - else if (match(par->Tok[0], w_PRESSURE)) - { - if (n < 1) return (0); - else if (match(par->Tok[1], w_EXPONENT)) return -1; - else if (match(par->Tok[1], w_PSI)) par->Pressflag = PSI; - else if (match(par->Tok[1], w_KPA)) par->Pressflag = KPA; - else if (match(par->Tok[1], w_METERS)) par->Pressflag = METERS; - else return (201); - } - - else if (match(par->Tok[0], w_HEADLOSS)) - { - if (n < 1) return (0); - else if (match(par->Tok[1], w_HW)) hyd->Formflag = HW; - else if (match(par->Tok[1], w_DW)) hyd->Formflag = DW; - else if (match(par->Tok[1], w_CM)) hyd->Formflag = CM; - else return (201); - } - - else if (match(par->Tok[0], w_HYDRAULIC)) - { - if (n < 2) return (0); - else if (match(par->Tok[1], w_USE)) out->Hydflag = USE; - else if (match(par->Tok[1], w_SAVE)) out->Hydflag = SAVE; - else return (201); - strncpy(out->HydFname, par->Tok[2], MAXFNAME); - } + // Check if 1st token matches a parameter name and + // process the input for the matched parameter + if (n < 0) return 201; - else if (match(par->Tok[0], w_QUALITY)) - { - if (n < 1) return (0); - else if (match(par->Tok[1], w_NONE)) qu->Qualflag = NONE; - else if (match(par->Tok[1], w_CHEM)) qu->Qualflag = CHEM; - else if (match(par->Tok[1], w_AGE)) qu->Qualflag = AGE; - else if (match(par->Tok[1], w_TRACE)) qu->Qualflag = TRACE; - else + // Flow UNITS + if (match(parser->Tok[0], w_UNITS)) { - qu->Qualflag = CHEM; - strncpy(qu->ChemName, par->Tok[1], MAXID); - if (n >= 2) strncpy(qu->ChemUnits, par->Tok[2], MAXID); + if (n < 1) return 0; + else if (match(parser->Tok[1], w_CFS)) parser->Flowflag = CFS; + else if (match(parser->Tok[1], w_GPM)) parser->Flowflag = GPM; + else if (match(parser->Tok[1], w_AFD)) parser->Flowflag = AFD; + else if (match(parser->Tok[1], w_MGD)) parser->Flowflag = MGD; + else if (match(parser->Tok[1], w_IMGD)) parser->Flowflag = IMGD; + else if (match(parser->Tok[1], w_LPS)) parser->Flowflag = LPS; + else if (match(parser->Tok[1], w_LPM)) parser->Flowflag = LPM; + else if (match(parser->Tok[1], w_CMH)) parser->Flowflag = CMH; + else if (match(parser->Tok[1], w_CMD)) parser->Flowflag = CMD; + else if (match(parser->Tok[1], w_MLD)) parser->Flowflag = MLD; + else if (match(parser->Tok[1], w_SI)) parser->Flowflag = LPS; + else return 201; } - if (qu->Qualflag == TRACE) /* Source tracing option */ - { - /* Copy Trace Node ID to par->Tok[0] for error reporting */ - strcpy(par->Tok[0], ""); - if (n < 2) return (212); - strcpy(par->Tok[0], par->Tok[2]); - qu->TraceNode = findnode(net,par->Tok[2]); - if (qu->TraceNode == 0) return (212); - strncpy(qu->ChemName, u_PERCENT, MAXID); - strncpy(qu->ChemUnits, par->Tok[2], MAXID); - } - if (qu->Qualflag == AGE) - { - strncpy(qu->ChemName, w_AGE, MAXID); - strncpy(qu->ChemUnits, u_HOURS, MAXID); - } - } - - else if (match(par->Tok[0], w_MAP)) - { - if (n < 1) return (0); - strncpy(pr->MapFname, par->Tok[1], MAXFNAME); /* Map file name */ - } - - else if (match(par->Tok[0], w_VERIFY)) - { - /* Backward compatibility for verification file */ - } - - else if (match(par->Tok[0], w_UNBALANCED)) /* Unbalanced option */ - { - if (n < 1) return (0); - if (match(par->Tok[1], w_STOP)) hyd->ExtraIter = -1; - else if (match(par->Tok[1], w_CONTINUE)) - { - if (n >= 2) hyd->ExtraIter = atoi(par->Tok[2]); - else hyd->ExtraIter = 0; - } - else return (201); - } - - else if (match(par->Tok[0], w_PATTERN)) /* Pattern option */ - { - if (n < 1) return (0); - strncpy(par->DefPatID, par->Tok[1], MAXID); - } - else if (match(par->Tok[0], w_DEMAND)) - { - if (n < 2) return 0; - if (!match(par->Tok[1], w_MODEL)) return -1; - choice = findmatch(par->Tok[2], DemandModelTxt); - if (choice < 0) return 201; - hyd->DemandModel = choice; - } - else return (-1); - return (0); -} /* end of optionchoice */ + // PRESSURE units + else if (match(parser->Tok[0], w_PRESSURE)) + { + if (n < 1) return 0; + else if (match(parser->Tok[1], w_EXPONENT)) return -1; + else if (match(parser->Tok[1], w_PSI)) parser->Pressflag = PSI; + else if (match(parser->Tok[1], w_KPA)) parser->Pressflag = KPA; + else if (match(parser->Tok[1], w_METERS)) parser->Pressflag = METERS; + else return 201; + } + + // HEADLOSS formula + else if (match(parser->Tok[0], w_HEADLOSS)) + { + if (n < 1) return 0; + else if (match(parser->Tok[1], w_HW)) hyd->Formflag = HW; + else if (match(parser->Tok[1], w_DW)) hyd->Formflag = DW; + else if (match(parser->Tok[1], w_CM)) hyd->Formflag = CM; + else return 201; + } + + // HYDRUALICS USE/SAVE file option + else if (match(parser->Tok[0], w_HYDRAULIC)) + { + if (n < 2) return 0; + else if (match(parser->Tok[1], w_USE)) out->Hydflag = USE; + else if (match(parser->Tok[1], w_SAVE)) out->Hydflag = SAVE; + else return 201; + strncpy(out->HydFname, parser->Tok[2], MAXFNAME); + } -int optionvalue(EN_Project *pr, int n) + // Water QUALITY option + else if (match(parser->Tok[0], w_QUALITY)) + { + if (n < 1) return (0); + else if (match(parser->Tok[1], w_NONE)) qual->Qualflag = NONE; + else if (match(parser->Tok[1], w_CHEM)) qual->Qualflag = CHEM; + else if (match(parser->Tok[1], w_AGE)) qual->Qualflag = AGE; + else if (match(parser->Tok[1], w_TRACE)) qual->Qualflag = TRACE; + else + { + qual->Qualflag = CHEM; + strncpy(qual->ChemName, parser->Tok[1], MAXID); + if (n >= 2) strncpy(qual->ChemUnits, parser->Tok[2], MAXID); + } + if (qual->Qualflag == TRACE) + { + // Copy Trace Node ID to parser->Tok[0] for error reporting + strcpy(parser->Tok[0], ""); + if (n < 2) return 212; + strcpy(parser->Tok[0], parser->Tok[2]); + qual->TraceNode = findnode(net, parser->Tok[2]); + if (qual->TraceNode == 0) return 212; + strncpy(qual->ChemName, u_PERCENT, MAXID); + strncpy(qual->ChemUnits, parser->Tok[2], MAXID); + } + if (qual->Qualflag == AGE) + { + strncpy(qual->ChemName, w_AGE, MAXID); + strncpy(qual->ChemUnits, u_HOURS, MAXID); + } + } + + // MAP file name + else if (match(parser->Tok[0], w_MAP)) + { + if (n < 1) return 0; + strncpy(pr->MapFname, parser->Tok[1], MAXFNAME); + } + + else if (match(parser->Tok[0], w_VERIFY)) + { + // Deprecated + } + + // Hydraulics UNBALANCED option + else if (match(parser->Tok[0], w_UNBALANCED)) + { + if (n < 1) return 0; + if (match(parser->Tok[1], w_STOP)) hyd->ExtraIter = -1; + else if (match(parser->Tok[1], w_CONTINUE)) + { + if (n >= 2) hyd->ExtraIter = atoi(parser->Tok[2]); + else hyd->ExtraIter = 0; + } + else return 201; + } + + // Default demand PATTERN + else if (match(parser->Tok[0], w_PATTERN)) + { + if (n < 1) return 0; + strncpy(parser->DefPatID, parser->Tok[1], MAXID); + } + + // DEMAND model + else if (match(parser->Tok[0], w_DEMAND)) + { + if (n < 2) return 0; + if (!match(parser->Tok[1], w_MODEL)) return -1; + choice = findmatch(parser->Tok[2], DemandModelTxt); + if (choice < 0) return 201; + hyd->DemandModel = choice; + } + + // Return -1 if keyword did not match any option + else return -1; + return 0; +} + +int optionvalue(Project *pr, int n) /* **------------------------------------------------------------- ** Input: *line = line read from input file @@ -1929,119 +1824,116 @@ int optionvalue(EN_Project *pr, int n) **-------------------------------------------------------------- */ { - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qu = &pr->quality; - parser_data_t *par = &pr->parser; - char* tok0 = par->Tok[0]; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Parser *parser = &pr->parser; + int nvalue = 1; // Index of token with numerical value + double y; + char* tok0 = parser->Tok[0]; + + // Check for deprecated SEGMENTS keyword + if (match(tok0, w_SEGMENTS)) return 0; + + // Check for missing value (which is permissible) + if (match(tok0, w_SPECGRAV) || match(tok0, w_EMITTER) || + match(tok0, w_DEMAND) || match(tok0, w_MINIMUM) || + match(tok0, w_REQUIRED) || match(tok0, w_PRESSURE) || + match(tok0, w_PRECISION) + ) nvalue = 2; + if (n < nvalue) return 0; + + // Check for valid numerical input + if (!getfloat(parser->Tok[nvalue], &y)) return 213; + + // Quality tolerance option (which can be 0) + if (match(tok0, w_TOLERANCE)) + { + if (y < 0.0) return 213; + qual->Ctol = y; + return 0; + } + + // Diffusivity + if (match(tok0, w_DIFFUSIVITY)) + { + if (y < 0.0) return 213; + qual->Diffus = y; + return 0; + } + + // Hydraulic damping limit option */ + if (match(tok0, w_DAMPLIMIT)) + { + hyd->DampLimit = y; + return 0; + } - int nvalue = 1; /* Index of token with numerical value */ - double y; + // Flow change limit + else if (match(tok0, w_FLOWCHANGE)) + { + if (y < 0.0) return 213; + hyd->FlowChangeLimit = y; + return 0; + } - /* Check for obsolete SEGMENTS keyword */ - if (match(tok0, w_SEGMENTS)) return (0); + // Head loss error limit + else if (match(tok0, w_HEADERROR)) + { + if (y < 0.0) return 213; + hyd->HeadErrorLimit = y; + return 0; + } - /* Check for missing value (which is permissible) */ - if (match(tok0, w_SPECGRAV) || match(tok0, w_EMITTER) || - match(tok0, w_DEMAND) || match(tok0, w_MINIMUM) || - match(tok0, w_REQUIRED) || match(tok0, w_PRESSURE) || - match(tok0, w_PRECISION)) - { - nvalue = 2; - } - if (n < nvalue) return (0); + // Pressure dependent demand parameters + else if (match(tok0, w_MINIMUM)) + { + if (y < 0.0) return 213; + hyd->Pmin = y; + return 0; + } + else if (match(tok0, w_REQUIRED)) + { + if (y < 0.0) return 213; + hyd->Preq = y; + return 0; + } + else if (match(tok0, w_PRESSURE)) + { + if (y < 0.0) return 213; + hyd->Pexp = y; + return 0; + } - /* Check for valid numerical input */ - if (!getfloat(par->Tok[nvalue], &y)) return (213); + // All other options must be > 0 + if (y <= 0.0) return 213; - /* Check for WQ tolerance option (which can be 0) */ - if (match(tok0, w_TOLERANCE)) - { - if (y < 0.0) return (213); - qu->Ctol = y; /* Quality tolerance*/ - return (0); - } + // Assign value to all other options + if (match(tok0, w_VISCOSITY)) hyd->Viscos = y; + else if (match(tok0, w_SPECGRAV)) hyd->SpGrav = y; + else if (match(tok0, w_TRIALS)) hyd->MaxIter = (int)y; + else if (match(tok0, w_ACCURACY)) + { + y = MAX(y, 1.e-5); + y = MIN(y, 1.e-1); + hyd->Hacc = y; + } + else if (match(tok0, w_HTOL)) hyd->Htol = y; + else if (match(tok0, w_QTOL)) hyd->Qtol = y; + else if (match(tok0, w_RQTOL)) + { + if (y >= 1.0) return 213; + hyd->RQtol = y; + } + else if (match(tok0, w_CHECKFREQ)) hyd->CheckFreq = (int)y; + else if (match(tok0, w_MAXCHECK)) hyd->MaxCheck = (int)y; + else if (match(tok0, w_EMITTER)) hyd->Qexp = 1.0 / y; + else if (match(tok0, w_DEMAND)) hyd->Dmult = y; + else return 201; + return 0; +} - /* Check for Diffusivity option */ - if (match(tok0, w_DIFFUSIVITY)) - { - if (y < 0.0) return (213); - qu->Diffus = y; - return (0); - } - - /* Check for Damping Limit option */ - if (match(tok0, w_DAMPLIMIT)) - { - hyd->DampLimit = y; - return (0); - } - - /* Check for flow change limit*/ - else if (match(tok0, w_FLOWCHANGE)) - { - if (y < 0.0) return 213; - hyd->FlowChangeLimit = y; - return 0; - } - - /* Check for head error limit*/ - else if (match(tok0, w_HEADERROR)) - { - if (y < 0.0) return 213; - hyd->HeadErrorLimit = y; - return 0; - } - - /* Check for pressure dependent demand params */ - else if (match(tok0, w_MINIMUM)) - { - if (y < 0.0) return 213; - hyd->Pmin = y; - return 0; - } - else if (match(tok0, w_REQUIRED)) - { - if (y < 0.0) return 213; - hyd->Preq = y; - return 0; - } - else if (match(tok0, w_PRESSURE)) - { - if (y < 0.0) return 213; - hyd->Pexp = y; - return 0; - } - - /* All other options must be > 0 */ - if (y <= 0.0) return (213); - - /* Assign value to specified option */ - if (match(tok0, w_VISCOSITY)) hyd->Viscos = y; /* Viscosity */ - else if (match(tok0, w_SPECGRAV)) hyd->SpGrav = y; /* Spec. gravity */ - else if (match(tok0, w_TRIALS)) hyd->MaxIter = (int)y; /* Max. trials */ - else if (match(tok0, w_ACCURACY)) /* Accuracy */ - { - y = MAX(y, 1.e-5); - y = MIN(y, 1.e-1); - hyd->Hacc = y; - } - else if (match(tok0, w_HTOL)) hyd->Htol = y; - else if (match(tok0, w_QTOL)) hyd->Qtol = y; - else if (match(tok0, w_RQTOL)) - { - if (y >= 1.0) return (213); - hyd->RQtol = y; - } - else if (match(tok0, w_CHECKFREQ)) hyd->CheckFreq = (int)y; - else if (match(tok0, w_MAXCHECK)) hyd->MaxCheck = (int)y; - else if (match(tok0, w_EMITTER)) hyd->Qexp = 1.0 / y; - else if (match(tok0, w_DEMAND)) hyd->Dmult = y; - else return (201); - return (0); -} /* end of optionvalue */ - -int getpumpcurve(EN_Project *pr, int n) +int getpumpcurve(Project *pr, int n) /* **-------------------------------------------------------- ** Input: n = number of parameters for pump curve @@ -2051,55 +1943,60 @@ int getpumpcurve(EN_Project *pr, int n) ** Notes: ** 1. Called by pumpdata() in INPUT3.C ** 2. Current link index & pump index of pump being -** processed is found in global variables Nlinks +** processed is found in network variables Nlinks ** and Npumps, respectively ** 3. Curve data read from input line is found in -** global variables X[0],...X[n-1] +** parser's array X[0],...X[n-1] **--------------------------------------------------------- */ { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Parser *parser = &pr->parser; - double a, b, c, h0, h1, h2, q1, q2; - - Spump *pump = &net->Pump[net->Npumps]; + double a, b, c, h0, h1, h2, q1, q2; + Spump *pump = &net->Pump[net->Npumps]; - if (n == 1) /* Const. HP curve */ - { - if (par->X[0] <= 0.0) - return (202); - pump->Ptype = CONST_HP; - net->Link[net->Nlinks].Km = par->X[0]; - } else { - if (n == 2) /* Generic power curve */ + // Constant HP curve + if (n == 1) { - q1 = par->X[1]; - h1 = par->X[0]; - h0 = 1.33334 * h1; - q2 = 2.0 * q1; - h2 = 0.0; - } - else if (n >= 5) /* 3-pt. power curve */ + if (parser->X[0] <= 0.0) return 202; + pump->Ptype = CONST_HP; + net->Link[net->Nlinks].Km = parser->X[0]; + } + + // Power function curve + else { - h0 = par->X[0]; - h1 = par->X[1]; - q1 = par->X[2]; - h2 = par->X[3]; - q2 = par->X[4]; - } else - return (202); - pump->Ptype = POWER_FUNC; - if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) - return (206); - pump->H0 = -a; - pump->R = -b; - pump->N = c; - pump->Q0 = q1; - pump->Qmax = pow((-a / b), (1.0 / c)); - pump->Hmax = h0; - } - return (0); + // Single point power curve + if (n == 2) + { + q1 = parser->X[1]; + h1 = parser->X[0]; + h0 = 1.33334 * h1; + q2 = 2.0 * q1; + h2 = 0.0; + } + + // 3-point power curve + else if (n >= 5) + { + h0 = parser->X[0]; + h1 = parser->X[1]; + q1 = parser->X[2]; + h2 = parser->X[3]; + q2 = parser->X[4]; + } + else return 202; + pump->Ptype = POWER_FUNC; + if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) return 206; + pump->H0 = -a; + pump->R = -b; + pump->N = c; + pump->Q0 = q1; + pump->Qmax = pow((-a / b), (1.0 / c)); + pump->Hmax = h0; + } + return 0; } int powercurve(double h0, double h1, double h2, double q1, double q2, @@ -2117,26 +2014,22 @@ int powercurve(double h0, double h1, double h2, double q1, double q2, **---------------------------------------------------------- */ { - double h4, h5; - if (h0 < TINY || h0 - h1 < TINY || h1 - h2 < TINY || q1 < TINY || - q2 - q1 < TINY) - return (0); - *a = h0; - h4 = h0 - h1; - h5 = h0 - h2; - *c = log(h5 / h4) / log(q2 / q1); - if (*c <= 0.0 || *c > 20.0) - return (0); - *b = -h4 / pow(q1, *c); + double h4, h5; - /*** Updated 6/24/02 ***/ - if (*b >= 0.0) - return (0); - - return (1); + if (h0 < TINY || h0 - h1 < TINY || h1 - h2 < TINY || + q1 < TINY || q2 - q1 < TINY + ) return 0; + *a = h0; + h4 = h0 - h1; + h5 = h0 - h2; + *c = log(h5 / h4) / log(q2 / q1); + if (*c <= 0.0 || *c > 20.0) return 0; + *b = -h4 / pow(q1, *c); + if (*b >= 0.0) return 0; + return 1; } -int valvecheck(EN_Project *pr, int type, int j1, int j2) +int valvecheck(Project *pr, int type, int j1, int j2) /* **-------------------------------------------------------------- ** Input: type = valve type @@ -2147,55 +2040,49 @@ int valvecheck(EN_Project *pr, int type, int j1, int j2) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - - int k, vj1, vj2; - LinkType vtype; + Network *net = &pr->network; - /* Examine each existing valve */ - for (k = 1; k <= net->Nvalves; k++) { - Svalve *valve = &net->Valve[k]; - Slink *link = &net->Link[valve->Link]; - vj1 = link->N1; - vj2 = link->N2; - vtype = link->Type; + int k, vj1, vj2; + LinkType vtype; + Slink *link; + Svalve *valve; - /* Cannot have two PRVs sharing downstream nodes or in series */ - if (vtype == PRV && type == PRV) { - if (vj2 == j2 || vj2 == j1 || vj1 == j2) - return (0); + // Examine each existing valve + for (k = 1; k <= net->Nvalves; k++) + { + valve = &net->Valve[k]; + link = &net->Link[valve->Link]; + vj1 = link->N1; + vj2 = link->N2; + vtype = link->Type; + + // Cannot have two PRVs sharing downstream nodes or in series + if (vtype == PRV && type == PRV) + { + if (vj2 == j2 || vj2 == j1 || vj1 == j2) return 0; + } + + // Cannot have two PSVs sharing upstream nodes or in series + if (vtype == PSV && type == PSV) + { + if (vj1 == j1 || vj1 == j2 || vj2 == j1) return 0; + } + + // Cannot have PSV connected to downstream node of PRV + if (vtype == PSV && type == PRV && vj1 == j2) return 0; + if (vtype == PRV && type == PSV && vj2 == j1) return 0; + + // Cannot have PSV connected to downstream node of FCV + // nor have PRV connected to upstream node of FCV + if (vtype == FCV && type == PSV && vj2 == j1) return 0; + if (vtype == FCV && type == PRV && vj1 == j2) return 0; + if (vtype == PSV && type == FCV && vj1 == j2) return 0; + if (vtype == PRV && type == FCV && vj2 == j1) return 0; } + return 1; +} - /* Cannot have two PSVs sharing upstream nodes or in series */ - if (vtype == PSV && type == PSV) { - if (vj1 == j1 || vj1 == j2 || vj2 == j1) - return (0); - } - - /* Cannot have PSV connected to downstream node of PRV */ - if (vtype == PSV && type == PRV && vj1 == j2) - return (0); - if (vtype == PRV && type == PSV && vj2 == j1) - return (0); - - /*** Updated 3/1/01 ***/ - /* Cannot have PSV connected to downstream node of FCV */ - /* nor have PRV connected to upstream node of FCV */ - if (vtype == FCV && type == PSV && vj2 == j1) - return (0); - if (vtype == FCV && type == PRV && vj1 == j2) - return (0); - - /*** Updated 4/14/05 ***/ - if (vtype == PSV && type == FCV && vj1 == j2) - return (0); - if (vtype == PRV && type == FCV && vj2 == j1) - return (0); - } - return (1); -} /* End of valvecheck */ - -void changestatus(EN_Network *net, int j, StatType status, double y) +void changestatus(Network *net, int j, StatusType status, double y) /* **-------------------------------------------------------------- ** Input: j = link index @@ -2211,28 +2098,27 @@ void changestatus(EN_Network *net, int j, StatType status, double y) **-------------------------------------------------------------- */ { - Slink *link = &net->Link[j]; + Slink *link = &net->Link[j]; - if (link->Type == PIPE || link->Type == GPV) { - if (status != ACTIVE) - link->Stat = status; - } else if (link->Type == PUMP) { - if (status == ACTIVE) { - link->Kc = y; - status = OPEN; - if (y == 0.0) - status = CLOSED; - } else if (status == OPEN) { - link->Kc = 1.0; + if (link->Type == PIPE || link->Type == GPV) + { + if (status != ACTIVE) link->Status = status; } - link->Stat = status; - } else if (link->Type >= PRV) { - link->Kc = y; - link->Stat = status; - if (status != ACTIVE) { - link->Kc = MISSING; + else if (link->Type == PUMP) + { + if (status == ACTIVE) + { + link->Kc = y; + status = OPEN; + if (y == 0.0) status = CLOSED; + } + else if (status == OPEN) link->Kc = 1.0; + link->Status = status; } - } -} /* end of changestatus */ - -/********************** END OF INPUT3.C ************************/ + else if (link->Type >= PRV) + { + link->Kc = y; + link->Status = status; + if (status != ACTIVE) link->Kc = MISSING; + } +} diff --git a/src/mempool.c b/src/mempool.c index f77d8ec..55552ed 100755 --- a/src/mempool.c +++ b/src/mempool.c @@ -1,10 +1,17 @@ -/* mempool.c -** -** A simple fast pooled memory allocation package. -** -** Based on code by Steve Hill in Graphics Gems III, -** David Kirk (ed.), Academic Press, Boston, MA, 1992 -** +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: mempool.c + Description: a simple fast poooled memory allocation package + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + + This module is based code by Steve Hill in Graphics Gems III, + David Kirk (ed.), Academic Press, Boston, MA, 1992 + ****************************************************************************** */ #include diff --git a/src/mempool.h b/src/mempool.h index 002272f..35a0210 100755 --- a/src/mempool.h +++ b/src/mempool.h @@ -1,9 +1,14 @@ /* -** mempool.h -** -** Header for mempool.c -** -** A simple pooled memory allocator + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: mempool.h + Description: header for a simple pooled memory allocator + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ #ifndef MEMPOOL_H diff --git a/src/output.c b/src/output.c index db5920b..fd92077 100644 --- a/src/output.c +++ b/src/output.c @@ -1,15 +1,14 @@ /* -********************************************************************* - -OUTPUT.C -- Binary File Transfer Routines for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 8/15/07 (2.00.11) -AUTHOR: L. Rossman - US EPA - NRMRL - -******************************************************************** +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: output.c +Description: binary file read/write routines +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/27/2018 +****************************************************************************** */ #include @@ -26,13 +25,24 @@ AUTHOR: L. Rossman #include "hash.h" #include "text.h" -/* write x[1] to x[n] to file */ -size_t f_save(REAL4 *x, int n, FILE *file) { - return fwrite(x + 1, sizeof(REAL4), n, file); +// Local functions +static int nodeoutput(Project *, int, REAL4 *, double); +static int linkoutput(Project *, int, REAL4 *, double); +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 +size_t f_save(REAL4 *x, int n, FILE *file) +{ + return fwrite(x + 1, sizeof(REAL4), n, file); +} +size_t f_read(REAL4 *x, int n, FILE *file) +{ + return fread(x + 1, sizeof(REAL4), n, file); } - -int savenetdata(EN_Project *pr) +int savenetdata(Project *pr) /* **--------------------------------------------------------------- ** Input: none @@ -42,119 +52,121 @@ int savenetdata(EN_Project *pr) **--------------------------------------------------------------- */ { - int i, nmax; - INT4 *ibuf; - REAL4 *x; - int errcode = 0; + Network *net = &pr->network; + Outfile *out = &pr->outfile; + Report *rpt = &pr->report; + Quality *qual = &pr->quality; + Parser *parser = &pr->parser; + Times *time = &pr->times; - EN_Network *net = &pr->network; - out_file_t *out = &pr->out_files; - report_options_t *rep = &pr->report; - quality_t *qu = &pr->quality; - parser_data_t *par = &pr->parser; - time_options_t *time = &pr->time_options; - FILE *outFile = out->OutFile; + int i, nmax; + int errcode = 0; + INT4 *ibuf; + REAL4 *x; + Snode *node; + FILE *outFile = out->OutFile; - /* Allocate buffer arrays */ - nmax = MAX(net->Nnodes, net->Nlinks) + 1; - nmax = MAX(nmax, 15); - ibuf = (INT4 *)calloc(nmax, sizeof(INT4)); - x = (REAL4 *)calloc(nmax, sizeof(REAL4)); - ERRCODE(MEMCHECK(ibuf)); - ERRCODE(MEMCHECK(x)); + // Allocate buffer arrays + nmax = MAX(net->Nnodes, net->Nlinks) + 1; + nmax = MAX(nmax, 15); + ibuf = (INT4 *)calloc(nmax, sizeof(INT4)); + x = (REAL4 *)calloc(nmax, sizeof(REAL4)); + ERRCODE(MEMCHECK(ibuf)); + ERRCODE(MEMCHECK(x)); - if (!errcode) { - /* Write integer variables to outFile */ - ibuf[0] = MAGICNUMBER; + // Write prolog section of binary output file + if (!errcode) + { + // Write integer variables to outFile + ibuf[0] = MAGICNUMBER; + 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; + ibuf[5] = net->Npumps; + ibuf[6] = net->Nvalves; + ibuf[7] = qual->Qualflag; + ibuf[8] = qual->TraceNode; + ibuf[9] = parser->Flowflag; + ibuf[10] = parser->Pressflag; + ibuf[11] = rpt->Tstatflag; + ibuf[12] = (INT4)time->Rstart; + ibuf[13] = (INT4)time->Rstep; + ibuf[14] = (INT4)time->Dur; + fwrite(ibuf, sizeof(INT4), 15, outFile); - /*** CODEVERSION replaces VERSION ***/ - //ibuf[1] = CODEVERSION; - ibuf[1] = 20012; // keep version at 2.00.12 so that GUI will run + // Write string variables to outFile + fwrite(pr->Title[0], sizeof(char), TITLELEN + 1, outFile); + fwrite(pr->Title[1], sizeof(char), TITLELEN + 1, outFile); + fwrite(pr->Title[2], sizeof(char), TITLELEN + 1, outFile); + fwrite(parser->InpFname, sizeof(char), MAXFNAME + 1, outFile); + fwrite(rpt->Rpt2Fname, sizeof(char), MAXFNAME + 1, outFile); + fwrite(qual->ChemName, sizeof(char), MAXID + 1, outFile); + fwrite(rpt->Field[QUALITY].Units, sizeof(char), MAXID + 1, outFile); - ibuf[2] = net->Nnodes; - ibuf[3] = net->Ntanks; - ibuf[4] = net->Nlinks; - ibuf[5] = net->Npumps; - ibuf[6] = net->Nvalves; - ibuf[7] = qu->Qualflag; - ibuf[8] = qu->TraceNode; - ibuf[9] = par->Flowflag; - ibuf[10] = par->Pressflag; - ibuf[11] = rep->Tstatflag; - ibuf[12] = (INT4)time->Rstart; - ibuf[13] = (INT4)time->Rstep; - ibuf[14] = (INT4)time->Dur; - fwrite(ibuf, sizeof(INT4), 15, outFile); + // Write node ID information to outFile + for (i = 1; i <= net->Nnodes; i++) + { + node = &net->Node[i]; + fwrite(node->ID, MAXID + 1, 1, outFile); + } - /* Write string variables to outFile */ - fwrite(pr->Title[0], sizeof(char), TITLELEN + 1, outFile); - fwrite(pr->Title[1], sizeof(char), TITLELEN + 1, outFile); - fwrite(pr->Title[2], sizeof(char), TITLELEN + 1, outFile); - fwrite(par->InpFname, sizeof(char), MAXFNAME + 1, outFile); - fwrite(rep->Rpt2Fname, sizeof(char), MAXFNAME + 1, outFile); - fwrite(qu->ChemName, sizeof(char), MAXID + 1, outFile); - fwrite(rep->Field[QUALITY].Units, sizeof(char), MAXID + 1, outFile); + // Write link information to outFile + // (Note: first transfer values to buffer array, + // then fwrite buffer array at offset of 1 ) + for (i = 1; i <= net->Nlinks; i++) + { + fwrite(net->Link[i].ID, MAXID + 1, 1, outFile); + } - /* Write node ID information to outFile */ - for (i = 1; i <= net->Nnodes; i++) { - Snode *node = &net->Node[i]; - fwrite(node->ID, MAXID + 1, 1, outFile); - } + for (i = 1; i <= net->Nlinks; i++) ibuf[i] = net->Link[i].N1; + fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); - /* Write link information to outFile */ - /* (Note: first transfer values to buffer array,*/ - /* then fwrite buffer array at offset of 1 ) */ - for (i = 1; i <= net->Nlinks; i++) - fwrite(net->Link[i].ID, MAXID + 1, 1, outFile); + for (i = 1; i <= net->Nlinks; i++) ibuf[i] = net->Link[i].N2; + fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); - for (i = 1; i <= net->Nlinks; i++) - ibuf[i] = net->Link[i].N1; - fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); + for (i = 1; i <= net->Nlinks; i++) ibuf[i] = net->Link[i].Type; + fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); - for (i = 1; i <= net->Nlinks; i++) - ibuf[i] = net->Link[i].N2; - fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); + // Write tank information to outFile + for (i = 1; i <= net->Ntanks; i++) ibuf[i] = net->Tank[i].Node; + fwrite(ibuf + 1, sizeof(INT4), net->Ntanks, outFile); - for (i = 1; i <= net->Nlinks; i++) - ibuf[i] = net->Link[i].Type; - fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); + for (i = 1; i <= net->Ntanks; i++) x[i] = (REAL4)net->Tank[i].A; + f_save(x, net->Ntanks, outFile); - /* Write tank information to outFile.*/ - for (i = 1; i <= net->Ntanks; i++) - ibuf[i] = net->Tank[i].Node; - fwrite(ibuf + 1, sizeof(INT4), net->Ntanks, outFile); - - for (i = 1; i <= net->Ntanks; i++) - x[i] = (REAL4)net->Tank[i].A; - f_save(x, net->Ntanks, outFile); - - /* Save node elevations to outFile.*/ - for (i = 1; i <= net->Nnodes; i++) - x[i] = (REAL4)(net->Node[i].El * pr->Ucf[ELEV]); - f_save(x, net->Nnodes, outFile); + // Save node elevations to outFile + for (i = 1; i <= net->Nnodes; i++) + { + 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++) - x[i] = (REAL4)(net->Link[i].Len * pr->Ucf[ELEV]); - f_save(x, net->Nlinks, outFile); + // Save link lengths & diameters to outFile + for (i = 1; i <= net->Nlinks; i++) + { + x[i] = (REAL4)(net->Link[i].Len * pr->Ucf[ELEV]); + } + f_save(x, net->Nlinks, outFile); - for (i = 1; i <= net->Nlinks; i++) { - if (net->Link[i].Type != PUMP) - x[i] = (REAL4)(net->Link[i].Diam * pr->Ucf[DIAM]); - else - x[i] = 0.0f; + for (i = 1; i <= net->Nlinks; i++) + { + if (net->Link[i].Type != PUMP) + { + x[i] = (REAL4)(net->Link[i].Diam * pr->Ucf[DIAM]); + } + else x[i] = 0.0f; + } + if (f_save(x, net->Nlinks, outFile) < (unsigned)net->Nlinks) errcode = 308; } - if (f_save(x, net->Nlinks, outFile) < (unsigned)net->Nlinks) - errcode = 308; - } - /* Free memory used for buffer arrays */ - free(ibuf); - free(x); - return (errcode); + // Free memory used for buffer arrays + free(ibuf); + free(x); + return errcode; } -int savehyd(EN_Project *pr, long *htime) +int savehyd(Project *pr, long *htime) /* **-------------------------------------------------------------- ** Input: *htime = current time @@ -164,60 +176,61 @@ int savehyd(EN_Project *pr, long *htime) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - out_file_t *out = &pr->out_files; - FILE *HydFile = out->HydFile; - hydraulics_t *hyd = &pr->hydraulics; + Network *net = &pr->network; + Outfile *out = &pr->outfile; + Hydraul *hyd = &pr->hydraul; + + int i; + INT4 t; + int errcode = 0; + REAL4 *x; + FILE *HydFile = out->HydFile; - int i; - INT4 t; - int errcode = 0; - REAL4 *x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); - if (x == NULL) - return 101; + x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); + if (x == NULL) return 101; - /* Save current time (htime) */ - t = (INT4)(*htime); - fwrite(&t, sizeof(INT4), 1, HydFile); + // Save current time (htime) + t = (INT4)(*htime); + fwrite(&t, sizeof(INT4), 1, HydFile); - /* Save current nodal demands (D) */ - for (i = 1; i <= net->Nnodes; i++) - x[i] = (REAL4)hyd->NodeDemand[i]; - fwrite(x + 1, sizeof(REAL4), net->Nnodes, HydFile); + // Save current nodal demands (D) + for (i = 1; i <= net->Nnodes; i++) x[i] = (REAL4)hyd->NodeDemand[i]; + fwrite(x + 1, sizeof(REAL4), net->Nnodes, HydFile); + //f_save(x, net->Nnodes, HydFile); - /* Copy heads (H) to buffer of floats (x) and save buffer */ - for (i = 1; i <= net->Nnodes; i++) - x[i] = (REAL4)hyd->NodeHead[i]; - fwrite(x + 1, sizeof(REAL4), net->Nnodes, HydFile); + // Save current nodal heads + for (i = 1; i <= net->Nnodes; i++) x[i] = (REAL4)hyd->NodeHead[i]; + fwrite(x + 1, sizeof(REAL4), net->Nnodes, HydFile); + //f_save(x, net->Nnodes, HydFile); - /* Force flow in closed links to be zero then save flows */ - for (i = 1; i <= net->Nlinks; i++) { - if (hyd->LinkStatus[i] <= CLOSED) - x[i] = 0.0f; - else - x[i] = (REAL4)hyd->LinkFlows[i]; - } - fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile); + // Force flow in closed links to be zero then save flows + for (i = 1; i <= net->Nlinks; i++) + { + if (hyd->LinkStatus[i] <= CLOSED) x[i] = 0.0f; + else x[i] = (REAL4)hyd->LinkFlow[i]; + } + fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile); + //f_save(x, net->Nlinks, HydFile); - /* Copy link status to buffer of floats (x) & write buffer */ - for (i = 1; i <= net->Nlinks; i++) - x[i] = (REAL4)hyd->LinkStatus[i]; - fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile); + // 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, */ - /* 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) < - (unsigned)net->Nlinks) - errcode = 308; - free(x); - fflush(HydFile); /* added TNT */ - return (errcode); -} /* End of savehyd */ + // 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) < + (unsigned)net->Nlinks + ) errcode = 308; + //if (f_save(x, net->Nlinks, HydFile) < (unsigned)net->Nlinks) errcode = 308; + free(x); + fflush(HydFile); + return errcode; +} -int savehydstep(EN_Project *pr, long *hydstep) +int savehydstep(Project *pr, long *hydstep) /* **-------------------------------------------------------------- ** Input: *hydstep = next time step @@ -227,19 +240,19 @@ int savehydstep(EN_Project *pr, long *hydstep) **-------------------------------------------------------------- */ { - out_file_t *out = &pr->out_files; - INT4 t; - int errcode = 0; - t = (INT4)(*hydstep); - if (fwrite(&t, sizeof(INT4), 1, out->HydFile) < 1) - errcode = 308; - if (t == 0) - fputc(EOFMARK, out->HydFile); - fflush(out->HydFile); /* added TNT */ - return (errcode); + Outfile *out = &pr->outfile; + + INT4 t; + int errcode = 0; + + t = (INT4)(*hydstep); + if (fwrite(&t, sizeof(INT4), 1, out->HydFile) < 1) errcode = 308; + if (t == 0) fputc(EOFMARK, out->HydFile); + fflush(out->HydFile); + return errcode; } -int saveenergy(EN_Project *pr) +int saveenergy(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -249,80 +262,76 @@ int saveenergy(EN_Project *pr) **-------------------------------------------------------------- */ { - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - out_file_t *out = &pr->out_files; - parser_data_t *par = &pr->parser; - time_options_t *time = &pr->time_options; - FILE *outFile = out->OutFile; - Spump *pump; - - int i, j; - INT4 index; - REAL4 x[MAX_ENERGY_STATS]; // work array - double hdur, // total simulation duration in hours - t; // total pumping time duration + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Outfile *out = &pr->outfile; + Parser *parser = &pr->parser; + Times *time = &pr->times; - hdur = time->Dur / 3600.0; - for (i = 1; i <= net->Npumps; i++) { - pump = &net->Pump[i]; - if (hdur == 0.0) { - pump->Energy[TOTAL_COST] *= 24.0; - } - else { - // ... convert total hrs. online to fraction of total time online - t = pump->Energy[PCNT_ONLINE]; //currently holds total hrs. online - pump->Energy[PCNT_ONLINE] = t / hdur; + int i; + INT4 index; + REAL4 x[6]; // work array + double hdur, // total simulation duration in hours + t; // total pumping time duration + Spump *pump; + FILE *outFile = out->OutFile; - // ... convert cumulative values to time-averaged ones - if (t > 0.0) { - pump->Energy[PCNT_EFFIC] /= t; - pump->Energy[KWH_PER_FLOW] /= t; - pump->Energy[TOTAL_KWH] /= t; + hdur = time->Dur / 3600.0; + for (i = 1; i <= net->Npumps; i++) + { + pump = &net->Pump[i]; + if (hdur == 0.0) pump->Energy.TotalCost *= 24.0; + else + { + // ... convert total hrs. online to fraction of total time online + t = pump->Energy.TimeOnLine; //currently holds total hrs. online + pump->Energy.TimeOnLine = t / hdur; + + // ... convert cumulative values to time-averaged ones + if (t > 0.0) + { + pump->Energy.Efficiency /= t; + pump->Energy.KwHrsPerFlow /= t; + pump->Energy.KwHrs /= t; + } + + // ... convert total cost to cost per day + pump->Energy.TotalCost *= 24.0 / hdur; } - // ... convert total cost to cost per day - pump->Energy[TOTAL_COST] *= 24.0 / hdur; + // ... express time online and avg. efficiency as percentages + pump->Energy.TimeOnLine *= 100.0; + pump->Energy.Efficiency *= 100.0; + + // ... compute KWH per Million Gallons or per Cubic Meter + if (parser->Unitsflag == SI) + { + pump->Energy.KwHrsPerFlow *= (1000. / LPSperCFS / 3600.); + } + else pump->Energy.KwHrsPerFlow *= (1.0e6 / GPMperCFS / 60.); + + // ... save energy stats to REAL4 work array + x[0] = (REAL4)pump->Energy.TimeOnLine; + x[1] = (REAL4)pump->Energy.Efficiency; + x[2] = (REAL4)pump->Energy.KwHrsPerFlow; + x[3] = (REAL4)pump->Energy.KwHrs; + x[4] = (REAL4)pump->Energy.MaxKwatts; + x[5] = (REAL4)pump->Energy.TotalCost; + + // ... save energy results to output file + index = pump->Link; + if (fwrite(&index, sizeof(INT4), 1, outFile) < 1) return 308; + if (fwrite(x, sizeof(REAL4), 6, outFile) < 6) return 308; } - // ... express time online and avg. efficiency as percentages - pump->Energy[PCNT_ONLINE] *= 100.0; - pump->Energy[PCNT_EFFIC] *= 100.0; - - // ... compute KWH per Million Gallons or per Cubic Meter - if (par->Unitsflag == SI) { - pump->Energy[KWH_PER_FLOW] *= (1000. / LPSperCFS / 3600.); - } - else { - pump->Energy[KWH_PER_FLOW] *= (1.0e6 / GPMperCFS / 60.); - } - - // ... save energy stats to REAL4 work array - for (j = 0; j < MAX_ENERGY_STATS; j++) { - x[j] = (REAL4)pump->Energy[j]; - } - - // ... save energy results to output file - index = pump->Link; - if (fwrite(&index, sizeof(INT4), 1, outFile) < 1) { - return (308); - } - if (fwrite(x, sizeof(REAL4), MAX_ENERGY_STATS, outFile) < MAX_ENERGY_STATS) { - return (308); - } - } - - // ... compute and save demand charge - hyd->Emax = hyd->Emax * hyd->Dcost; - x[0] = (REAL4)hyd->Emax; - if (fwrite(&x[0], sizeof(REAL4), 1, outFile) < 1) { - return (308); - } - return (0); + // ... compute and save demand charge + hyd->Emax = hyd->Emax * hyd->Dcost; + x[0] = (REAL4)hyd->Emax; + if (fwrite(&x[0], sizeof(REAL4), 1, outFile) < 1) return 308; + return (0); } -int readhyd(EN_Project *pr, long *hydtime) +int readhyd(Project *pr, long *hydtime) /* **-------------------------------------------------------------- ** Input: none @@ -336,57 +345,42 @@ int readhyd(EN_Project *pr, long *hydtime) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - out_file_t *out = &pr->out_files; - FILE *HydFile = out->HydFile; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Outfile *out = &pr->outfile; + + int i; + INT4 t; + int result = 1; + REAL4 *x; + FILE *HydFile = out->HydFile; - int i; - INT4 t; - int result = 1; - REAL4 *x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); - if (x == NULL) - return 0; + x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); + if (x == NULL) return 0; - if (fread(&t, sizeof(INT4), 1, HydFile) < 1) - result = 0; - *hydtime = t; + if (fread(&t, sizeof(INT4), 1, HydFile) < 1) result = 0; + *hydtime = t; - if (fread(x + 1, sizeof(REAL4), net->Nnodes, HydFile) < (unsigned)net->Nnodes) - result = 0; - else - for (i = 1; i <= net->Nnodes; i++) - hyd->NodeDemand[i] = x[i]; + if (f_read(x, net->Nnodes, HydFile) < (unsigned)net->Nnodes) result = 0; + else for (i = 1; i <= net->Nnodes; i++) hyd->NodeDemand[i] = x[i]; - if (fread(x + 1, sizeof(REAL4), net->Nnodes, HydFile) < (unsigned)net->Nnodes) - result = 0; - else - for (i = 1; i <= net->Nnodes; i++) - hyd->NodeHead[i] = x[i]; + if (f_read(x, net->Nnodes, HydFile) < (unsigned)net->Nnodes) result = 0; + else for (i = 1; i <= net->Nnodes; i++) hyd->NodeHead[i] = x[i]; - if (fread(x + 1, sizeof(REAL4), net->Nlinks, HydFile) < (unsigned)net->Nlinks) - result = 0; - else - for (i = 1; i <= net->Nlinks; i++) - hyd->LinkFlows[i] = x[i]; + if (f_read(x, net->Nlinks, HydFile) < (unsigned)net->Nlinks) result = 0; + else for (i = 1; i <= net->Nlinks; i++) hyd->LinkFlow[i] = x[i]; - if (fread(x + 1, sizeof(REAL4), net->Nlinks, HydFile) < (unsigned)net->Nlinks) - result = 0; - else - for (i = 1; i <= net->Nlinks; i++) - hyd->LinkStatus[i] = (char)x[i]; + if (f_read(x, net->Nlinks, HydFile) < (unsigned)net->Nlinks) result = 0; + else for (i = 1; i <= net->Nlinks; i++) hyd->LinkStatus[i] = (char)x[i]; - if (fread(x + 1, sizeof(REAL4), net->Nlinks, HydFile) < (unsigned)net->Nlinks) - result = 0; - else - for (i = 1; i <= net->Nlinks; i++) - hyd->LinkSetting[i] = x[i]; + if (f_read(x, net->Nlinks, HydFile) < (unsigned)net->Nlinks) result = 0; + else for (i = 1; i <= net->Nlinks; i++) hyd->LinkSetting[i] = x[i]; - free(x); - return result; -} /* End of readhyd */ + free(x); + return result; +} -int readhydstep(EN_Project *pr, long *hydstep) +int readhydstep(Project *pr, long *hydstep) /* **-------------------------------------------------------------- ** Input: none @@ -396,14 +390,15 @@ int readhydstep(EN_Project *pr, long *hydstep) **-------------------------------------------------------------- */ { - INT4 t; - FILE *hydFile = pr->out_files.HydFile; - if (fread(&t, sizeof(INT4), 1, hydFile) < 1) return (0); - *hydstep = t; - return (1); -} /* End of readhydstep */ + FILE *hydFile = pr->outfile.HydFile; + INT4 t; -int saveoutput(EN_Project *pr) + if (fread(&t, sizeof(INT4), 1, hydFile) < 1) return 0; + *hydstep = t; + return 1; +} + +int saveoutput(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -412,24 +407,23 @@ int saveoutput(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - - int j; - int errcode = 0; - REAL4 *x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); - if (x == NULL) - return 101; + Network *net = &pr->network; - /* Write out node results, then link results */ - for (j = DEMAND; j <= QUALITY; j++) - ERRCODE(nodeoutput(pr, j, x, pr->Ucf[j])); - for (j = FLOW; j <= FRICTION; j++) - ERRCODE(linkoutput(pr, j, x, pr->Ucf[j])); - free(x); - return (errcode); -} /* End of saveoutput */ + int j; + int errcode = 0; + REAL4 *x; -int nodeoutput(EN_Project *pr, int j, REAL4 *x, double ucf) + x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); + if (x == NULL) return 101; + + // Write out node results, then link results + for (j = DEMAND; j <= QUALITY; j++) ERRCODE(nodeoutput(pr, j, x, pr->Ucf[j])); + for (j = FLOW; j <= FRICTION; j++) ERRCODE(linkoutput(pr, j, x, pr->Ucf[j])); + free(x); + return errcode; +} + +int nodeoutput(Project *pr, int j, REAL4 *x, double ucf) /* **-------------------------------------------------------------- ** Input: j = type of node variable @@ -440,40 +434,51 @@ int nodeoutput(EN_Project *pr, int j, REAL4 *x, double ucf) **----------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - out_file_t *out = &pr->out_files; - quality_t *qu = &pr->quality; - - int i; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Outfile *out = &pr->outfile; - /* Load computed results (in proper units) into buffer x */ - switch (j) { - case DEMAND: - for (i = 1; i <= net->Nnodes; i++) - x[i] = (REAL4)(hyd->NodeDemand[i] * ucf); - break; - case HEAD: - for (i = 1; i <= net->Nnodes; i++) - x[i] = (REAL4)(hyd->NodeHead[i] * ucf); - break; - case PRESSURE: - for (i = 1; i <= net->Nnodes; i++) - x[i] = (REAL4)((hyd->NodeHead[i] - net->Node[i].El) * ucf); - break; - case QUALITY: - for (i = 1; i <= net->Nnodes; i++) - x[i] = (REAL4)(qu->NodeQual[i] * ucf); - } + int i; + FILE *outFile = out->TmpOutFile; - /* Write x[1] to x[net->Nnodes] to output file */ - if (fwrite(x + 1, sizeof(REAL4), net->Nnodes, out->TmpOutFile) < (unsigned)net->Nnodes) { - return (308); - } - return (0); -} /* End of nodeoutput */ + // Load computed results (in proper units) into buffer x + switch (j) + { + case DEMAND: + for (i = 1; i <= net->Nnodes; i++) + { + x[i] = (REAL4)(hyd->NodeDemand[i] * ucf); + } + break; -int linkoutput(EN_Project *pr, int j, REAL4 *x, double ucf) + case HEAD: + for (i = 1; i <= net->Nnodes; i++) + { + x[i] = (REAL4)(hyd->NodeHead[i] * ucf); + } + break; + + case PRESSURE: + for (i = 1; i <= net->Nnodes; i++) + { + x[i] = (REAL4)((hyd->NodeHead[i] - net->Node[i].El) * ucf); + } + break; + + case QUALITY: + for (i = 1; i <= net->Nnodes; i++) + { + x[i] = (REAL4)(qual->NodeQual[i] * ucf); + } + } + + // Write x[1] to x[net->Nnodes] to output file + if (f_save(x, net->Nnodes, outFile) < (unsigned)net->Nnodes) return 308; + return 0; +} + +int linkoutput(Project *pr, int j, REAL4 *x, double ucf) /* **---------------------------------------------------------------- ** Input: j = type of link variable @@ -484,113 +489,131 @@ int linkoutput(EN_Project *pr, int j, REAL4 *x, double ucf) **---------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - out_file_t *out = &pr->out_files; - quality_t *qu = &pr->quality; - - int i; - double a, h, q, f; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Outfile *out = &pr->outfile; - /* Load computed results (in proper units) into buffer x */ - switch (j) { - case FLOW: - for (i = 1; i <= net->Nlinks; i++) - x[i] = (REAL4)(hyd->LinkFlows[i] * ucf); - break; - case VELOCITY: - for (i = 1; i <= net->Nlinks; i++) { - if (net->Link[i].Type == PUMP) - x[i] = 0.0f; - else { - q = ABS(hyd->LinkFlows[i]); - a = PI * SQR(net->Link[i].Diam) / 4.0; - x[i] = (REAL4)(q / a * ucf); - } - } - break; - case HEADLOSS: - for (i = 1; i <= net->Nlinks; i++) { - if (hyd->LinkStatus[i] <= CLOSED) - x[i] = 0.0f; - else { - h = hyd->NodeHead[net->Link[i].N1] - hyd->NodeHead[net->Link[i].N2]; - if (net->Link[i].Type != PUMP) - h = ABS(h); - if (net->Link[i].Type <= PIPE) - x[i] = (REAL4)(1000.0 * h / net->Link[i].Len); - else - x[i] = (REAL4)(h * ucf); - } - } - break; - case LINKQUAL: - for (i = 1; i <= net->Nlinks; i++) - x[i] = (REAL4)(avgqual(pr,i) * ucf); - break; - case STATUS: - for (i = 1; i <= net->Nlinks; i++) - x[i] = (REAL4)hyd->LinkStatus[i]; - break; - case SETTING: - for (i = 1; i <= net->Nlinks; i++) { - double setting = hyd->LinkSetting[i]; - if (setting != MISSING) - switch (net->Link[i].Type) { - case CVPIPE: - case PIPE: - x[i] = (REAL4)setting; - break; - case PUMP: - x[i] = (REAL4)setting; - break; - case PRV: - case PSV: - case PBV: - x[i] = (REAL4)(setting * pr->Ucf[PRESSURE]); - break; - case FCV: - x[i] = (REAL4)(setting * pr->Ucf[FLOW]); - break; - case TCV: - x[i] = (REAL4)setting; - break; - default: - x[i] = 0.0f; + int i; + double a, h, q, f, setting; + FILE *outFile = out->TmpOutFile; + + // Load computed results (in proper units) into buffer x + switch (j) + { + case FLOW: + for (i = 1; i <= net->Nlinks; i++) + { + x[i] = (REAL4)(hyd->LinkFlow[i] * ucf); } - else - x[i] = 0.0f; - } - break; - case REACTRATE: /* Overall reaction rate in mass/L/day */ - if (qu->Qualflag == NONE) - memset(x, 0, (net->Nlinks + 1) * sizeof(REAL4)); - else - for (i = 1; i <= net->Nlinks; i++) - x[i] = (REAL4)(qu->PipeRateCoeff[i] * ucf); - break; - case FRICTION: /* f = 2ghd/(Lu^2) where f = friction factor */ - /* u = velocity, g = grav. accel., h = head */ - /*loss, d = diam., & L = pipe length */ - for (i = 1; i <= net->Nlinks; i++) { - if (net->Link[i].Type <= PIPE && ABS(hyd->LinkFlows[i]) > TINY) { - h = ABS(hyd->NodeHead[net->Link[i].N1] - hyd->NodeHead[net->Link[i].N2]); - f = 39.725 * h * pow(net->Link[i].Diam, 5) / net->Link[i].Len / SQR(hyd->LinkFlows[i]); - x[i] = (REAL4)f; - } else - x[i] = 0.0f; - } - break; - } + break; - /* Write x[1] to x[net->Nlinks] to output file */ - if (fwrite(x + 1, sizeof(REAL4), net->Nlinks, out->TmpOutFile) < - (unsigned)net->Nlinks) - return (308); - return (0); -} /* End of linkoutput */ + case VELOCITY: + for (i = 1; i <= net->Nlinks; i++) + { + if (net->Link[i].Type == PUMP) x[i] = 0.0f; + else + { + q = ABS(hyd->LinkFlow[i]); + a = PI * SQR(net->Link[i].Diam) / 4.0; + x[i] = (REAL4)(q / a * ucf); + } + } + break; -int savefinaloutput(EN_Project *pr) + case HEADLOSS: + for (i = 1; i <= net->Nlinks; i++) + { + if (hyd->LinkStatus[i] <= CLOSED) x[i] = 0.0f; + else + { + h = hyd->NodeHead[net->Link[i].N1] - + hyd->NodeHead[net->Link[i].N2]; + if (net->Link[i].Type != PUMP) h = ABS(h); + if (net->Link[i].Type <= PIPE) + { + x[i] = (REAL4)(1000.0 * h / net->Link[i].Len); + } + else x[i] = (REAL4)(h * ucf); + } + } + break; + + case LINKQUAL: + for (i = 1; i <= net->Nlinks; i++) + { + x[i] = (REAL4)(avgqual(pr,i) * ucf); + } + break; + + case STATUS: + for (i = 1; i <= net->Nlinks; i++) + { + x[i] = (REAL4)hyd->LinkStatus[i]; + } + break; + + case SETTING: + for (i = 1; i <= net->Nlinks; i++) + { + setting = hyd->LinkSetting[i]; + if (setting != MISSING) switch (net->Link[i].Type) + { + case CVPIPE: + case PIPE: + x[i] = (REAL4)setting; break; + case PUMP: + x[i] = (REAL4)setting; break; + case PRV: + case PSV: + case PBV: + x[i] = (REAL4)(setting * pr->Ucf[PRESSURE]); break; + case FCV: + x[i] = (REAL4)(setting * pr->Ucf[FLOW]); break; + case TCV: + x[i] = (REAL4)setting; break; + default: x[i] = 0.0f; + } + else x[i] = 0.0f; + } + break; + + case REACTRATE: // Overall reaction rate in mass/L/day + if (qual->Qualflag == NONE) + { + memset(x, 0, (net->Nlinks + 1) * sizeof(REAL4)); + } + else for (i = 1; i <= net->Nlinks; i++) + { + x[i] = (REAL4)(qual->PipeRateCoeff[i] * ucf); + } + break; + + case FRICTION: // Friction factor + // f = 2ghd/(Lu^2) where f = friction factor + // u = velocity, g = grav. accel., h = head loss + //loss, d = diam., & L = pipe length + for (i = 1; i <= net->Nlinks; i++) + { + if (net->Link[i].Type <= PIPE && ABS(hyd->LinkFlow[i]) > TINY) + { + h = ABS(hyd->NodeHead[net->Link[i].N1] - + hyd->NodeHead[net->Link[i].N2]); + f = 39.725 * h * pow(net->Link[i].Diam, 5) / + net->Link[i].Len / SQR(hyd->LinkFlow[i]); + x[i] = (REAL4)f; + } + else x[i] = 0.0f; + } + break; + } + + // Write x[1] to x[net->Nlinks] to output file + if (f_save(x, net->Nlinks, outFile) < (unsigned)net->Nlinks) return 308; + return 0; +} + +int savefinaloutput(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -600,38 +623,39 @@ int savefinaloutput(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - out_file_t *out = &pr->out_files; - report_options_t *rep = &pr->report; - quality_t *qu = &pr->quality; - FILE *outFile = out->OutFile; + Network *net = &pr->network; + Outfile *out = &pr->outfile; + Report *rpt = &pr->report; + Quality *qual = &pr->quality; + + int errcode = 0; + REAL4 *x; + FILE *outFile = out->OutFile; - int errcode = 0; - REAL4 *x; + // Save time series statistic if computed + if (rpt->Tstatflag != SERIES && out->TmpOutFile != NULL) + { + x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); + if (x == NULL) return 101; + ERRCODE(savetimestat(pr, x, NODEHDR)); + ERRCODE(savetimestat(pr, x, LINKHDR)); + if (!errcode) rpt->Nperiods = 1; + fclose(out->TmpOutFile); + out->TmpOutFile = NULL; + free(x); + } - /* Save time series statistic if computed */ - if (rep->Tstatflag != SERIES && out->TmpOutFile != NULL) { - x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); - if (x == NULL) - return 101; - ERRCODE(savetimestat(pr, x, NODEHDR)); - ERRCODE(savetimestat(pr, x, LINKHDR)); - if (!errcode) - rep->Nperiods = 1; - fclose(out->TmpOutFile); - out->TmpOutFile = NULL; - free(x); - } - - /* Save avg. reaction rates & file epilog */ - if (outFile != NULL) { - ERRCODE(savenetreacts(pr, qu->Wbulk, qu->Wwall, qu->Wtank, qu->Wsource)); - ERRCODE(saveepilog(pr)); - } - return (errcode); + // Save avg. reaction rates & file epilog + if (outFile != NULL) + { + ERRCODE(savenetreacts(pr, qual->Wbulk, qual->Wwall, qual->Wtank, + qual->Wsource)); + ERRCODE(saveepilog(pr)); + } + return errcode; } -int savetimestat(EN_Project *pr, REAL4 *x, HdrType objtype) +int savetimestat(Project *pr, REAL4 *x, HdrType objtype) /* **-------------------------------------------------------------- ** Input: *x = buffer for node values @@ -645,158 +669,144 @@ int savetimestat(EN_Project *pr, REAL4 *x, HdrType objtype) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - out_file_t *out = &pr->out_files; - report_options_t *rep = &pr->report; - quality_t *qu = &pr->quality; - FILE *outFile = out->OutFile; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Outfile *out = &pr->outfile; + Report *rpt = &pr->report; + + int n, n1, n2; + int i, j, p, errcode = 0; + long startbyte, skipbytes; + float *stat1, *stat2, xx; + FILE *outFile = out->OutFile; - int n, n1, n2; - int i, j, p, errcode = 0; - long startbyte, skipbytes; - float *stat1, *stat2, xx; - - /* - 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) { - /* - For nodes, we start at 0 and skip over node output for all - node variables minus 1 plus link output for all link variables. - */ - startbyte = 0; - skipbytes = (net->Nnodes * (QUALITY - DEMAND) + - net->Nlinks * (FRICTION - FLOW + 1)) * - sizeof(REAL4); - n = net->Nnodes; - n1 = DEMAND; - n2 = QUALITY; - } else { - /* - For links, we start at the end of all node variables and skip - over node output for all node variables plus link output for - all link variables minus 1. - */ - startbyte = net->Nnodes * (QUALITY - DEMAND + 1) * sizeof(REAL4); - skipbytes = (net->Nnodes * (QUALITY - DEMAND + 1) + - net->Nlinks * (FRICTION - FLOW)) * - sizeof(REAL4); - n = net->Nlinks; - n1 = FLOW; - n2 = FRICTION; - } - stat1 = (float *)calloc(n + 1, sizeof(float)); - stat2 = (float *)calloc(n + 1, sizeof(float)); - ERRCODE(MEMCHECK(stat1)); - ERRCODE(MEMCHECK(stat2)); - - /* Process each output reporting variable */ - if (!errcode) { - for (j = n1; j <= n2; j++) { - - /* Initialize stat arrays */ - if (rep->Tstatflag == AVG) - memset(stat1, 0, (n + 1) * sizeof(float)); - else - for (i = 1; i <= n; i++) { - stat1[i] = -MISSING; /* +1E10 */ - stat2[i] = MISSING; /* -1E10 */ - } - - /* Position temp output file at start of output */ - fseek(out->TmpOutFile, startbyte + (j - n1) * n * sizeof(REAL4), SEEK_SET); - - /* Process each time period */ - for (p = 1; p <= rep->Nperiods; p++) { - - /* Get output results for time period & update stats */ - fread(x + 1, sizeof(REAL4), n, out->TmpOutFile); - for (i = 1; i <= n; i++) { - xx = x[i]; - if (objtype == LINKHDR) { - if (j == FLOW) - xx = ABS(xx); - if (j == STATUS) { - if (xx >= OPEN) - xx = 1.0; - else - xx = 0.0; - } - } - if (rep->Tstatflag == AVG) - stat1[i] += xx; - else { - stat1[i] = MIN(stat1[i], xx); - stat2[i] = MAX(stat2[i], xx); - } - } - - /* Advance file to next period */ - if (p < rep->Nperiods) - fseek(out->TmpOutFile, skipbytes, SEEK_CUR); - } - - /* Compute resultant stat & save to regular output file */ - switch (rep->Tstatflag) { - case AVG: - for (i = 1; i <= n; i++) - x[i] = stat1[i] / (float)rep->Nperiods; - break; - case MIN: - for (i = 1; i <= n; i++) - x[i] = stat1[i]; - break; - case MAX: - for (i = 1; i <= n; i++) - x[i] = stat2[i]; - break; - case RANGE: - for (i = 1; i <= n; i++) - x[i] = stat2[i] - stat1[i]; - break; - } - if (objtype == LINKHDR && j == STATUS) { - for (i = 1; i <= n; i++) { - if (x[i] < 0.5f) - x[i] = CLOSED; - else - x[i] = OPEN; - } - } - if (fwrite(x + 1, sizeof(REAL4), n, outFile) < (unsigned)n) - errcode = 308; - - /* Update internal output variables where applicable */ - if (objtype == NODEHDR) - switch (j) { - case DEMAND: - for (i = 1; i <= n; i++) - hyd->NodeDemand[i] = x[i] / pr->Ucf[DEMAND]; - break; - case HEAD: - for (i = 1; i <= n; i++) - hyd->NodeHead[i] = x[i] / pr->Ucf[HEAD]; - break; - case QUALITY: - for (i = 1; i <= n; i++) - qu->NodeQual[i] = x[i] / pr->Ucf[QUALITY]; - break; - } - else if (j == FLOW) - for (i = 1; i <= n; i++) - hyd->LinkFlows[i] = x[i] / pr->Ucf[FLOW]; + // 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) + { + // For nodes, we start at 0 and skip over node output for all + // node variables minus 1 plus link output for all link variables. + startbyte = 0; + skipbytes = (net->Nnodes * (QUALITY - DEMAND) + + net->Nlinks * (FRICTION - FLOW + 1)) * sizeof(REAL4); + n = net->Nnodes; + n1 = DEMAND; + n2 = QUALITY; } - } + else + { + // For links, we start at the end of all node variables and skip + // over node output for all node variables plus link output for + // all link variables minus 1 + startbyte = net->Nnodes * (QUALITY - DEMAND + 1) * sizeof(REAL4); + skipbytes = (net->Nnodes * (QUALITY - DEMAND + 1) + + net->Nlinks * (FRICTION - FLOW)) * sizeof(REAL4); + n = net->Nlinks; + n1 = FLOW; + n2 = FRICTION; + } + stat1 = (float *)calloc(n + 1, sizeof(float)); + stat2 = (float *)calloc(n + 1, sizeof(float)); + ERRCODE(MEMCHECK(stat1)); + ERRCODE(MEMCHECK(stat2)); - /* Free allocated memory */ - free(stat1); - free(stat2); - return (errcode); + // Process each output reporting variable + if (!errcode) for (j = n1; j <= n2; j++) + { + // Initialize stat arrays + if (rpt->Tstatflag == AVG) memset(stat1, 0, (n + 1) * sizeof(float)); + else for (i = 1; i <= n; i++) + { + stat1[i] = -MISSING; + stat2[i] = MISSING; + } + + // Position temp output file at start of output + fseek(out->TmpOutFile, startbyte + (j - n1) * n * sizeof(REAL4), + SEEK_SET); + + // Process each time period + for (p = 1; p <= rpt->Nperiods; p++) + { + // Get output results for time period & update stats + f_read(x, n, out->TmpOutFile); + for (i = 1; i <= n; i++) + { + xx = x[i]; + if (objtype == LINKHDR) + { + if (j == FLOW) xx = ABS(xx); + if (j == STATUS) + { + if (xx >= OPEN) xx = 1.0; + else xx = 0.0; + } + } + if (rpt->Tstatflag == AVG) stat1[i] += xx; + else + { + stat1[i] = MIN(stat1[i], xx); + stat2[i] = MAX(stat2[i], xx); + } + } + + // Advance file to next period + if (p < rpt->Nperiods) fseek(out->TmpOutFile, skipbytes, SEEK_CUR); + } + + // Compute resultant stat & save to regular output file + switch (rpt->Tstatflag) + { + case AVG: + for (i = 1; i <= n; i++) x[i] = stat1[i] / (float)rpt->Nperiods; + break; + case MIN: + for (i = 1; i <= n; i++) x[i] = stat1[i]; + break; + case MAX: + for (i = 1; i <= n; i++) x[i] = stat2[i]; + break; + case RANGE: + for (i = 1; i <= n; i++) x[i] = stat2[i] - stat1[i]; + break; + } + if (objtype == LINKHDR && j == STATUS) + { + for (i = 1; i <= n; i++) + { + if (x[i] < 0.5f) x[i] = CLOSED; + else x[i] = OPEN; + } + } + if (f_save(x, n, outFile) < (unsigned)n) errcode = 308; + + // Update internal output variables where applicable + if (objtype == NODEHDR) switch (j) + { + case DEMAND: + for (i = 1; i <= n; i++) hyd->NodeDemand[i] = x[i] / pr->Ucf[DEMAND]; + break; + case HEAD: + for (i = 1; i <= n; i++) hyd->NodeHead[i] = x[i] / pr->Ucf[HEAD]; + break; + case QUALITY: + for (i = 1; i <= n; i++) qual->NodeQual[i] = x[i] / pr->Ucf[QUALITY]; + break; + } + else if (j == FLOW) + { + for (i = 1; i <= n; i++) hyd->LinkFlow[i] = x[i] / pr->Ucf[FLOW]; + } + } + + // Free allocated memory + free(stat1); + free(stat2); + return errcode; } -int savenetreacts(EN_Project *pr, double wbulk, double wwall, double wtank, double wsource) +int savenetreacts(Project *pr, double wbulk, double wwall, double wtank, double wsource) /* **----------------------------------------------------- ** Writes average network-wide reaction rates (in @@ -804,27 +814,25 @@ int savenetreacts(EN_Project *pr, double wbulk, double wwall, double wtank, doub **----------------------------------------------------- */ { - out_file_t *out = &pr->out_files; - time_options_t *time = &pr->time_options; - FILE *outFile = out->OutFile; + Outfile *out = &pr->outfile; + Times *time = &pr->times; + + int errcode = 0; + double t; + REAL4 w[4]; + FILE *outFile = out->OutFile; - int errcode = 0; - double t; - REAL4 w[4]; - if (time->Dur > 0) - t = (double)time->Dur / 3600.; - else - t = 1.; - w[0] = (REAL4)(wbulk / t); - w[1] = (REAL4)(wwall / t); - w[2] = (REAL4)(wtank / t); - w[3] = (REAL4)(wsource / t); - if (fwrite(w, sizeof(REAL4), 4, outFile) < 4) - errcode = 308; - return (errcode); + if (time->Dur > 0) t = (double)time->Dur / 3600.; + else t = 1.; + w[0] = (REAL4)(wbulk / t); + w[1] = (REAL4)(wwall / t); + w[2] = (REAL4)(wtank / t); + w[3] = (REAL4)(wsource / t); + if (fwrite(w, sizeof(REAL4), 4, outFile) < 4) errcode = 308; + return errcode; } -int saveepilog(EN_Project *pr) +int saveepilog(Project *pr) /* **------------------------------------------------- ** Writes Nperiods, Warnflag, & Magic Number to @@ -832,22 +840,17 @@ int saveepilog(EN_Project *pr) **------------------------------------------------- */ { - out_file_t *out = &pr->out_files; - report_options_t *rep = &pr->report; - FILE *outFile = out->OutFile; - - int errcode = 0; - INT4 i; - i = rep->Nperiods; - if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) - errcode = 308; - i = pr->Warnflag; - if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) - errcode = 308; - i = MAGICNUMBER; - if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) - errcode = 308; - return (errcode); -} + Outfile *out = &pr->outfile; + Report *rpt = &pr->report; -/********************** END OF OUTPUT.C **********************/ + 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; + if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) errcode = 308; + i = MAGICNUMBER; if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) errcode = 308; + return errcode; +} diff --git a/src/project.c b/src/project.c new file mode 100644 index 0000000..0ed2ba2 --- /dev/null +++ b/src/project.c @@ -0,0 +1,838 @@ +/* + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: project.c + Description: project data management routines + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** +*/ + +#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif + +//*** Need to define WINDOWS to use the getTmpName function ***// +// --- define WINDOWS +#undef WINDOWS +#ifdef _WIN32 +#define WINDOWS +#endif +#ifdef __WIN32__ +#define WINDOWS +#endif +#ifdef WINDOWS +#include +#endif + +#include "types.h" +#include "funcs.h" + +int openfiles(Project *pr, const char *f1, const char *f2, const char *f3) +/*---------------------------------------------------------------- +** Input: f1 = pointer to name of input file +** f2 = pointer to name of report file +** f3 = pointer to name of binary output file +** Output: none +** Returns: error code +** Purpose: opens input & report files +**---------------------------------------------------------------- +*/ +{ + // Initialize file pointers to NULL + pr->parser.InFile = NULL; + pr->report.RptFile = NULL; + pr->outfile.OutFile = NULL; + pr->outfile.HydFile = NULL; + + // Save file names + strncpy(pr->parser.InpFname, f1, MAXFNAME); + strncpy(pr->report.Rpt1Fname, f2, MAXFNAME); + strncpy(pr->outfile.OutFname, f3, MAXFNAME); + if (strlen(f3) > 0) pr->outfile.Outflag = SAVE; + else pr->outfile.Outflag = SCRATCH; + + // Check that file names are not identical + if (strcomp(f1, f2) || strcomp(f1, f3) || + (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) return 301; + + // Attempt to open input and report files + if (strlen(f1) > 0) + { + if ((pr->parser.InFile = fopen(f1, "rt")) == NULL) return 302; + } + if (strlen(f2) == 0) pr->report.RptFile = stdout; + else if ((pr->report.RptFile = fopen(f2, "wt")) == NULL) return 303; + return 0; +} + +int openhydfile(Project *pr) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: opens file that saves hydraulics solution +**---------------------------------------------------------------- +*/ +{ + const int Nnodes = pr->network.Nnodes; + const int Ntanks = pr->network.Ntanks; + const int Nlinks = pr->network.Nlinks; + const int Nvalves = pr->network.Nvalves; + const int Npumps = pr->network.Npumps; + + INT4 nsize[6]; // Temporary array + INT4 magic; + INT4 version; + int errcode = 0; + + // If HydFile currently open, then close it if its not a scratch file + if (pr->outfile.HydFile != NULL) + { + if (pr->outfile.Hydflag == SCRATCH) return 0; + fclose(pr->outfile.HydFile); + } + + // Use Hydflag to determine the type of hydraulics file to use. + // Write error message if the file cannot be opened. + pr->outfile.HydFile = NULL; + switch (pr->outfile.Hydflag) + { + case SCRATCH: + strcpy(pr->outfile.HydFname, pr->TmpHydFname); + pr->outfile.HydFile = fopen(pr->outfile.HydFname, "w+b"); + break; + case SAVE: + pr->outfile.HydFile = fopen(pr->outfile.HydFname, "w+b"); + break; + case USE: + pr->outfile.HydFile = fopen(pr->outfile.HydFname, "rb"); + break; + } + if (pr->outfile.HydFile == NULL) return 305; + + // If a previous hydraulics solution is not being used, then + // save the current network size parameters to the file. + if (pr->outfile.Hydflag != USE) + { + magic = MAGICNUMBER; + version = ENGINE_VERSION; + nsize[0] = Nnodes; + nsize[1] = Nlinks; + nsize[2] = Ntanks; + nsize[3] = Npumps; + nsize[4] = Nvalves; + nsize[5] = (int)pr->times.Dur; + fwrite(&magic, sizeof(INT4), 1, pr->outfile.HydFile); + fwrite(&version, sizeof(INT4), 1, pr->outfile.HydFile); + fwrite(nsize, sizeof(INT4), 6, pr->outfile.HydFile); + } + + // If a previous hydraulics solution is being used, then + // make sure its network size parameters match those of + // the current network + if (pr->outfile.Hydflag == USE) + { + fread(&magic, sizeof(INT4), 1, pr->outfile.HydFile); + if (magic != MAGICNUMBER) return 306; + fread(&version, sizeof(INT4), 1, pr->outfile.HydFile); + if (version != ENGINE_VERSION) return 306; + if (fread(nsize, sizeof(INT4), 6, pr->outfile.HydFile) < 6) return 306; + if (nsize[0] != Nnodes || nsize[1] != Nlinks || nsize[2] != Ntanks || + nsize[3] != Npumps || nsize[4] != Nvalves || + nsize[5] != pr->times.Dur + ) return 306; + pr->outfile.SaveHflag = TRUE; + } + + // Save current position in hydraulics file + // where storage of hydraulic results begins + pr->outfile.HydOffset = ftell(pr->outfile.HydFile); + return errcode; +} + +int openoutfile(Project *pr) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: opens binary output file. +**---------------------------------------------------------------- +*/ +{ + int errcode = 0; + + // Close output file if already opened + if (pr->outfile.OutFile != NULL) + { + fclose(pr->outfile.OutFile); + pr->outfile.OutFile = NULL; + } + if (pr->outfile.TmpOutFile != NULL) + { + fclose(pr->outfile.TmpOutFile); + pr->outfile.TmpOutFile = NULL; + } + + // If output file name was supplied, then attempt to + // open it. Otherwise open a temporary output file. + if (pr->outfile.Outflag == SAVE) + { + pr->outfile.OutFile = fopen(pr->outfile.OutFname, "w+b"); + if (pr->outfile.OutFile == NULL) errcode = 304; + } + else + { + strcpy(pr->outfile.OutFname, pr->TmpOutFname); + pr->outfile.OutFile = fopen(pr->outfile.OutFname, "w+b"); + if (pr->outfile.OutFile == NULL) errcode = 304; + } + + // Save basic network data & energy usage results + ERRCODE(savenetdata(pr)); + pr->outfile.OutOffset1 = ftell(pr->outfile.OutFile); + ERRCODE(saveenergy(pr)); + pr->outfile.OutOffset2 = ftell(pr->outfile.OutFile); + + // Open temporary file if computing time series statistic + if (!errcode) + { + if (pr->report.Tstatflag != SERIES) + { + pr->outfile.TmpOutFile = fopen(pr->TmpStatFname, "w+b"); + if (pr->outfile.TmpOutFile == NULL) errcode = 304; + } + else pr->outfile.TmpOutFile = pr->outfile.OutFile; + } + return errcode; +} + +void initpointers(Project *pr) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: initializes data array pointers to NULL +**---------------------------------------------------------------- +*/ +{ + pr->hydraul.NodeDemand = NULL; + pr->hydraul.NodeHead = NULL; + pr->hydraul.LinkFlow = NULL; + pr->hydraul.LinkStatus = NULL; + pr->hydraul.LinkSetting = NULL; + pr->hydraul.OldStatus = NULL; + pr->hydraul.P = NULL; + pr->hydraul.Y = NULL; + pr->hydraul.Xflow = NULL; + + pr->quality.NodeQual = NULL; + pr->quality.PipeRateCoeff = NULL; + + pr->network.Node = NULL; + pr->network.Link = NULL; + pr->network.Tank = NULL; + pr->network.Pump = NULL; + pr->network.Valve = NULL; + pr->network.Pattern = NULL; + pr->network.Curve = NULL; + pr->network.Control = NULL; + pr->network.Adjlist = NULL; + 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; + pr->hydraul.smatrix.Order = NULL; + pr->hydraul.smatrix.Row = NULL; + pr->hydraul.smatrix.Ndx = NULL; + pr->hydraul.smatrix.XLNZ = NULL; + pr->hydraul.smatrix.NZSUB = NULL; + pr->hydraul.smatrix.LNZ = NULL; + + initrules(pr); +} + +int allocdata(Project *pr) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: allocates memory for network data structures +**---------------------------------------------------------------- +*/ +{ + int n; + int errcode = 0; + + // Allocate node & link ID hash tables + pr->network.NodeHashTable = hashtable_create(); + pr->network.LinkHashTable = hashtable_create(); + ERRCODE(MEMCHECK(pr->network.NodeHashTable)); + ERRCODE(MEMCHECK(pr->network.LinkHashTable)); + + // Allocate memory for network nodes + //************************************************************* + // NOTE: Because network components of a given type are indexed + // starting from 1, their arrays must be sized 1 + // element larger than the number of components. + //************************************************************* + if (!errcode) + { + n = pr->parser.MaxNodes + 1; + 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)); + ERRCODE(MEMCHECK(pr->network.Node)); + ERRCODE(MEMCHECK(pr->hydraul.NodeDemand)); + ERRCODE(MEMCHECK(pr->hydraul.NodeHead)); + ERRCODE(MEMCHECK(pr->quality.NodeQual)); + } + + // Allocate memory for network links + if (!errcode) + { + n = pr->parser.MaxLinks + 1; + 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)); + ERRCODE(MEMCHECK(pr->hydraul.LinkFlow)); + ERRCODE(MEMCHECK(pr->hydraul.LinkSetting)); + ERRCODE(MEMCHECK(pr->hydraul.LinkStatus)); + } + + // Allocate memory for tanks, sources, pumps, valves, + // controls, demands, time patterns, & operating curves + if (!errcode) + { + pr->network.Tank = + (Stank *)calloc(pr->parser.MaxTanks + 1, sizeof(Stank)); + pr->network.Pump = + (Spump *)calloc(pr->parser.MaxPumps + 1, sizeof(Spump)); + pr->network.Valve = + (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 + if (!errcode) + { + for (n = 0; n <= pr->parser.MaxPats; n++) + { + pr->network.Pattern[n].Length = 0; + pr->network.Pattern[n].F = NULL; + } + for (n = 0; n <= pr->parser.MaxCurves; n++) + { + pr->network.Curve[n].Npts = 0; + pr->network.Curve[n].Type = G_CURVE; + pr->network.Curve[n].X = NULL; + pr->network.Curve[n].Y = NULL; + } + for (n = 0; n <= pr->parser.MaxNodes; n++) + { + pr->network.Node[n].D = NULL; // node demand + } + } + + // Allocate memory for rule base (see RULES.C) + if (!errcode) errcode = allocrules(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 +** Output: none +** Purpose: frees memory allocated for network data structures. +**---------------------------------------------------------------- +*/ +{ + int j; + Pdemand demand, nextdemand; + Psource source; + + // Free memory for computed results + free(pr->hydraul.NodeDemand); + free(pr->hydraul.NodeHead); + free(pr->hydraul.LinkFlow); + free(pr->hydraul.LinkSetting); + free(pr->hydraul.LinkStatus); + free(pr->quality.NodeQual); + + // Free memory used for nodal adjacency lists + freeadjlists(&pr->network); + + // Free memory for node data + if (pr->network.Node != NULL) + { + for (j = 0; j <= pr->parser.MaxNodes; j++) + { + // Free memory used for demand category list + demand = pr->network.Node[j].D; + while (demand != NULL) + { + nextdemand = demand->next; + free(demand); + demand = nextdemand; + } + // Free memory used for WQ source data + source = pr->network.Node[j].S; + if (source != NULL) free(source); + } + free(pr->network.Node); + } + + // Free memory for other network objects + free(pr->network.Link); + free(pr->network.Tank); + free(pr->network.Pump); + free(pr->network.Valve); + free(pr->network.Control); + + // Free memory for time patterns + if (pr->network.Pattern != NULL) + { + for (j = 0; j <= pr->parser.MaxPats; j++) free(pr->network.Pattern[j].F); + free(pr->network.Pattern); + } + + // Free memory for curves + if (pr->network.Curve != NULL) + { + for (j = 0; j <= pr->parser.MaxCurves; j++) + { + free(pr->network.Curve[j].X); + free(pr->network.Curve[j].Y); + } + free(pr->network.Curve); + } + + // Free memory for rule base (see RULES.C) + freerules(pr); + + // Free hash table memory + if (pr->network.NodeHashTable != NULL) + { + hashtable_free(pr->network.NodeHashTable); + } + if (pr->network.LinkHashTable != NULL) + { + hashtable_free(pr->network.LinkHashTable); + } +} + + +int buildadjlists(Network *net) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: builds linked list of links adjacent to each node +**-------------------------------------------------------------- +*/ +{ + int i, j, k; + int errcode = 0; + Padjlist alink; + + // Create an array of adjacency lists + freeadjlists(net); + net->Adjlist = (Padjlist *)calloc(net->Nnodes + 1, sizeof(Padjlist)); + if (net->Adjlist == NULL) return 101; + for (i = 0; i <= net->Nnodes; i++) net->Adjlist[i] = NULL; + + // For each link, update adjacency lists of its end nodes + for (k = 1; k <= net->Nlinks; k++) + { + i = net->Link[k].N1; + j = net->Link[k].N2; + + // Include link in start node i's list + alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); + if (alink == NULL) + { + errcode = 101; + break; + } + alink->node = j; + alink->link = k; + alink->next = net->Adjlist[i]; + net->Adjlist[i] = alink; + + // Include link in end node j's list + alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); + if (alink == NULL) + { + errcode = 101; + break; + } + alink->node = i; + alink->link = k; + alink->next = net->Adjlist[j]; + net->Adjlist[j] = alink; + } + if (errcode) freeadjlists(net); + return errcode; +} + + +void freeadjlists(Network *net) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: frees memory used for nodal adjacency lists +**-------------------------------------------------------------- +*/ +{ + int i; + Padjlist alink; + + if (net->Adjlist == NULL) return; + for (i = 0; i <= net->Nnodes; i++) + { + for (alink = net->Adjlist[i]; alink != NULL; alink = net->Adjlist[i]) + { + net->Adjlist[i] = alink->next; + free(alink); + } + } + FREE(net->Adjlist); +} + +int incontrols(Project *pr, int objType, int index) +/*---------------------------------------------------------------- +** Input: objType = type of object (either NODE or LINK) +** index = the object's index +** Output: none +** Returns: 1 if any controls contain the object; 0 if not +** Purpose: determines if any simple or rule-based controls +** contain a particular node or link. +**---------------------------------------------------------------- +*/ +{ + Network *net = &pr->network; + Rules *rules = &pr->rules; + + int i, ruleObject; + Spremise *premise; + Saction *action; + + // Check simple controls + for (i = 1; i <= net->Ncontrols; i++) + { + if (objType == NODE && net->Control[i].Node == index) return 1; + if (objType == LINK && net->Control[i].Link == index) return 1; + } + + // Check rule-based controls + for (i = 1; i <= net->Nrules; i++) + { + // Convert objType to a rule object type + if (objType == NODE) ruleObject = 6; + else ruleObject = 7; + + // Check rule's premises + premise = net->Rule[i].Premises; + while (premise != NULL) + { + if (ruleObject == premise->object && premise->index == index) return 1; + premise = premise->next; + } + + // Rule actions only need to be checked for link objects + if (objType == LINK) + { + // Check rule's THEN actions + action = net->Rule[i].ThenActions; + while (action != NULL) + { + if (action->link == index) return 1; + action = action->next; + } + + // Check rule's ELSE actions + action = net->Rule[i].ElseActions; + while (action != NULL) + { + if (action->link == index) return 1; + action = action->next; + } + } + } + return 0; +} + +int findnode(Network *network, char *id) +/*---------------------------------------------------------------- +** Input: id = node ID +** Output: none +** Returns: index of node with given ID, or 0 if ID not found +** Purpose: uses hash table to find index of node with given ID +**---------------------------------------------------------------- +*/ +{ + return (hashtable_find(network->NodeHashTable, id)); +} + +int findlink(Network *network, char *id) +/*---------------------------------------------------------------- +** Input: id = link ID +** Output: none +** Returns: index of link with given ID, or 0 if ID not found +** Purpose: uses hash table to find index of link with given ID +**---------------------------------------------------------------- +*/ +{ + return (hashtable_find(network->LinkHashTable, id)); +} + +int findtank(Network *network, int index) +/*---------------------------------------------------------------- +** Input: index = node index +** Output: none +** Returns: index of tank with given node id, or NOTFOUND if tank not found +** Purpose: for use in the deletenode function +**---------------------------------------------------------------- +*/ +{ + int i; + for (i = 1; i <= network->Ntanks; i++) + { + if (network->Tank[i].Node == index) return i; + } + return NOTFOUND; +} + +int findpump(Network *network, int index) +/*---------------------------------------------------------------- +** Input: index = link ID +** Output: none +** Returns: index of pump with given link id, or NOTFOUND if pump not found +** Purpose: for use in the deletelink function +**---------------------------------------------------------------- +*/ +{ + int i; + for (i = 1; i <= network->Npumps; i++) + { + if (network->Pump[i].Link == index) return i; + } + return NOTFOUND; +} + +int findvalve(Network *network, int index) +/*---------------------------------------------------------------- +** Input: index = link ID +** Output: none +** Returns: index of valve with given link id, or NOTFOUND if valve not found +** Purpose: for use in the deletelink function +**---------------------------------------------------------------- +*/ +{ + int i; + for (i = 1; i <= network->Nvalves; i++) + { + if (network->Valve[i].Link == index) return i; + } + return NOTFOUND; +} + +char *getTmpName(char *fname) +//---------------------------------------------------------------- +// Input: fname = file name string +// Output: returns pointer to file name +// Purpose: creates a temporary file name with path prepended to it. +//---------------------------------------------------------------- +{ +#ifdef _WIN32 + + char* name = NULL; + + // --- use Windows _tempnam function to get a pointer to an + // unused file name that begins with "en" + name = _tempnam(NULL, "en"); + if (name == NULL) return NULL; + + // --- copy the file name to fname + if (strlen(name) < MAXFNAME) strncpy(fname, name, MAXFNAME); + else fname = NULL; + + // --- 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 + strcpy(fname, "enXXXXXX"); + mkstemp(fname); +#endif + return fname; +} + +int strcomp(const char *s1, const char *s2) +/*--------------------------------------------------------------- +** Input: s1 = character string +** s2 = character string +** Output: none +** Returns: 1 if s1 is same as s2, 0 otherwise +** Purpose: case insensitive comparison of strings s1 & s2 +**--------------------------------------------------------------- +*/ +{ + int i; + for (i = 0; UCHAR(s1[i]) == UCHAR(s2[i]); i++) + { + if (!s1[i + 1] && !s2[i + 1]) return 1; + } + return 0; +} + +double interp(int n, double x[], double y[], double xx) +/*---------------------------------------------------------------- +** Input: n = number of data pairs defining a curve +** x = x-data values of curve +** y = y-data values of curve +** xx = specified x-value +** Output: none +** Returns: y-value on curve at x = xx +** Purpose: uses linear interpolation to find y-value on a +** data curve corresponding to specified x-value. +** NOTE: does not extrapolate beyond endpoints of curve. +**---------------------------------------------------------------- +*/ +{ + int k, m; + double dx, dy; + + m = n - 1; // Highest data index + if (xx <= x[0]) return (y[0]); // xx off low end of curve + for (k = 1; k <= m; k++) // Bracket xx on curve + { + if (x[k] >= xx) // Interp. over interval + { + dx = x[k] - x[k - 1]; + dy = y[k] - y[k - 1]; + if (ABS(dx) < TINY) return (y[k]); + else return (y[k] - (x[k] - xx) * dy / dx); + } + } + return (y[m]); // xx off high end of curve +} + +char *geterrmsg(int errcode, char *msg) +/*---------------------------------------------------------------- +** Input: errcode = error code +** Output: none +** Returns: pointer to string with error message +** Purpose: retrieves text of error message +**---------------------------------------------------------------- +*/ +{ + switch (errcode) { /* Warnings */ +#define DAT(code,enumer,string) case code: strcpy(msg, string); break; +//#define DAT(code,enumer,string) case code: sprintf(msg, "Error %d: %s", code, string); break; +#include "errors.dat" +#undef DAT + default: + strcpy(msg, ""); + } + return (msg); +} + +void errmsg(Project *pr, int errcode) +/*---------------------------------------------------------------- +** Input: errcode = error code +** Output: none +** Purpose: writes error message to report file +**---------------------------------------------------------------- +*/ +{ + if (errcode == 309) /* Report file write error - */ + { /* Do not write msg to file. */ + + } + else if (pr->report.RptFile != NULL && pr->report.Messageflag) + { + writeline(pr, geterrmsg(errcode, pr->Msg)); + } +} + +void writewin(void(*vp)(char *), char *s) +/*---------------------------------------------------------------- +** Input: text string +** Output: none +** Purpose: passes character string to viewprog() in +** application which calls the EPANET DLL +**---------------------------------------------------------------- +*/ +{ + char progmsg[MAXMSG + 1]; + if (vp != NULL) + { + strncpy(progmsg, s, MAXMSG); + vp(progmsg); + } +} diff --git a/src/quality.c b/src/quality.c index 6bb82df..ad05246 100644 --- a/src/quality.c +++ b/src/quality.c @@ -1,13 +1,14 @@ /* -********************************************************************* - -QUALITY.C -- water quality engine module for the EPANET program - -This module works together with QUALROUTE.C and QUALREACT.C to -compute transport and reaction of a water quality constituent within -a pipe network over a series of time steps. - -********************************************************************* +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: quality.c +Description: implements EPANET's water quality engine +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/27/2018 +****************************************************************************** */ #include @@ -24,36 +25,35 @@ a pipe network over a series of time steps. #include "funcs.h" // Stagnant flow tolerance -const double QZERO = 0.005 / GPMperCFS; // 0.005 gpm = 1.114e-5 cfs +const double Q_STAGNANT = 0.005 / GPMperCFS; // 0.005 gpm = 1.114e-5 cfs -// Exported Functions (declared in FUNCS.H) -//int openqual(EN_Project *pr); -//void initqual(EN_Project *pr); -//int runqual(EN_Project *pr, long *); -//int nextqual(EN_Project *pr, long *); -//int stepqual(EN_Project *pr, long *); -//int closequal(EN_Project *pr); -//double avgqual(EN_Project *pr, int); -double findsourcequal(EN_Project *pr, int, double, double, long); +// Exported functions (declared in FUNCS.H) +//int openqual(Project *); +//void initqual(Project *); +//int runqual(Project *, long *); +//int nextqual(Project *, long *); +//int stepqual(Project *, long *); +//int closequal(Project *); +//double avgqual(Project *, int); +double findsourcequal(Project *, int, double, double, long); -// Imported Functions -extern char setreactflag(EN_Project *pr); +// Imported functions +extern char setreactflag(Project *); extern double getucf(double); -extern void ratecoeffs(EN_Project *pr); -extern int buildilists(EN_Project *pr); -extern void initsegs(EN_Project *pr); -extern void reversesegs(EN_Project *pr, int); -extern int sortnodes(EN_Project *pr); -extern void transport(EN_Project *pr, long); +extern void ratecoeffs(Project *); +extern void initsegs(Project *); +extern void reversesegs(Project *, int); +extern int sortnodes(Project *); +extern void transport(Project *, long); -// Local Functions -static double sourcequal(EN_Project *pr, Psource); -static void evalmassbalance(EN_Project *pr); -static double findstoredmass(EN_Project *pr); -static int flowdirchanged(EN_Project *pr); +// Local functions +static double sourcequal(Project *, Psource); +static void evalmassbalance(Project *); +static double findstoredmass(Project *); +static int flowdirchanged(Project *); -int openqual(EN_Project *pr) +int openqual(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -62,16 +62,23 @@ int openqual(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + int errcode = 0; int n; - quality_t *qual = &pr->quality; - EN_Network *net = &pr->network; + // Build nodal adjacency lists if they don't already exist + if (net->Adjlist == NULL) + { + errcode = buildadjlists(net); + if (errcode ) return errcode; + } // Create a memory pool for water quality segments qual->OutOfMemory = FALSE; qual->SegPool = mempool_create(); - if (qual->SegPool == NULL) errcode = 101;; + if (qual->SegPool == NULL) errcode = 101; // Allocate arrays for link flow direction & reaction rates n = net->Nlinks + 1; @@ -83,34 +90,19 @@ int openqual(EN_Project *pr) qual->FirstSeg = (Pseg *)calloc(n, sizeof(Pseg)); qual->LastSeg = (Pseg *)calloc(n, sizeof(Pseg)); - // Allocate memory for sorted nodes and link incidence lists - // ... Ilist contains the list of link indexes that are incident - // on each node in compact format - n = 2 * net->Nlinks + 2; - qual->Ilist = (int *)calloc(n, sizeof(int)); - // ... IlistPtr holds the position in Ilist where the - // incidence list for each node begins - n = net->Nnodes + 1; - qual->IlistPtr = (int *)calloc(n + 2, sizeof(int)); - // ... SortedNodes contains the list of node indexes in topological - // order + // Allocate memory for topologically sorted nodes qual->SortedNodes = (int *)calloc(n, sizeof(int)); ERRCODE(MEMCHECK(qual->FlowDir)); ERRCODE(MEMCHECK(qual->PipeRateCoeff)); ERRCODE(MEMCHECK(qual->FirstSeg)); ERRCODE(MEMCHECK(qual->LastSeg)); - ERRCODE(MEMCHECK(qual->Ilist)); - ERRCODE(MEMCHECK(qual->IlistPtr)); ERRCODE(MEMCHECK(qual->SortedNodes)); - - // Build link incidence lists - if (!errcode) errcode = buildilists(pr); return errcode; } -int initqual(EN_Project *pr) +int initqual(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -119,22 +111,24 @@ int initqual(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Times *time = &pr->times; + int i; int errcode = 0; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; // Re-position hydraulics file if (!hyd->OpenHflag) { - fseek(pr->out_files.HydFile, pr->out_files.HydOffset, SEEK_SET); + fseek(pr->outfile.HydFile, pr->outfile.HydOffset, SEEK_SET); } // Set elapsed times to zero - qual->Qtime = 0; - pr->time_options.Htime = 0; - pr->time_options.Rtime = pr->time_options.Rstart; + time->Qtime = 0; + time->Htime = 0; + time->Rtime = time->Rstart; pr->report.Nperiods = 0; // Initialize node quality @@ -183,17 +177,17 @@ int initqual(EN_Project *pr) qual->Wsource = 0.0; // Initialize mass balance components - qual->massbalance.initial = findstoredmass(pr); - qual->massbalance.inflow = 0.0; - qual->massbalance.outflow = 0.0; - qual->massbalance.reacted = 0.0; - qual->massbalance.final = 0.0; - qual->massbalance.ratio = 0.0; + qual->MassBalance.initial = findstoredmass(pr); + qual->MassBalance.inflow = 0.0; + qual->MassBalance.outflow = 0.0; + qual->MassBalance.reacted = 0.0; + qual->MassBalance.final = 0.0; + qual->MassBalance.ratio = 0.0; return errcode; } -int runqual(EN_Project *pr, long *t) +int runqual(Project *pr, long *t) /* **-------------------------------------------------------------- ** Input: none @@ -204,19 +198,19 @@ int runqual(EN_Project *pr, long *t) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Times *time = &pr->times; + long hydtime; // Hydraulic solution time long hydstep; // Hydraulic time step int errcode = 0; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - time_options_t *time = &pr->time_options; - // Update reported simulation time - *t = qual->Qtime; + *t = time->Qtime; // Read hydraulic solution from hydraulics file - if (qual->Qtime == time->Htime) + if (time->Qtime == time->Htime) { // Read hydraulic results from file if (!hyd->OpenHflag) @@ -229,7 +223,7 @@ int runqual(EN_Project *pr, long *t) // Save current results to output file if (time->Htime >= time->Rtime) { - if (pr->save_options.Saveflag) + if (pr->outfile.Saveflag) { errcode = saveoutput(pr); pr->report.Nperiods++; @@ -239,7 +233,7 @@ int runqual(EN_Project *pr, long *t) if (errcode) return errcode; // If simulating water quality - if (qual->Qualflag != NONE && qual->Qtime < time->Dur) + if (qual->Qualflag != NONE && time->Qtime < time->Dur) { // ... compute reaction rate coeffs. if (qual->Reactflag && qual->Qualflag != AGE) ratecoeffs(pr); @@ -256,7 +250,7 @@ int runqual(EN_Project *pr, long *t) } -int nextqual(EN_Project *pr, long *tstep) +int nextqual(Project *pr, long *tstep) /* **-------------------------------------------------------------- ** Input: none @@ -267,17 +261,17 @@ int nextqual(EN_Project *pr, long *tstep) **-------------------------------------------------------------- */ { + Quality *qual = &pr->quality; + Times *time = &pr->times; + long hydstep; // Time step until next hydraulic event long dt, qtime; int errcode = 0; - quality_t *qual = &pr->quality; - time_options_t *time = &pr->time_options; - // Find time step till next hydraulic event *tstep = 0; hydstep = 0; - if (time->Htime <= time->Dur) hydstep = time->Htime - qual->Qtime; + if (time->Htime <= time->Dur) hydstep = time->Htime - time->Qtime; // Perform water quality routing over this time step if (qual->Qualflag != NONE && hydstep > 0) @@ -286,7 +280,7 @@ int nextqual(EN_Project *pr, long *tstep) qtime = 0; while (!qual->OutOfMemory && qtime < hydstep) { - dt = MIN(qual->Qstep, hydstep - qtime); + dt = MIN(time->Qstep, hydstep - qtime); qtime += dt; transport(pr, dt); } @@ -298,7 +292,7 @@ int nextqual(EN_Project *pr, long *tstep) // Update current time if (!errcode) *tstep = hydstep; - qual->Qtime += hydstep; + time->Qtime += hydstep; // If no more time steps remain if (!errcode && *tstep == 0) @@ -310,13 +304,13 @@ int nextqual(EN_Project *pr, long *tstep) } // ... write the final portion of the binary output file - if (pr->save_options.Saveflag) errcode = savefinaloutput(pr); + if (pr->outfile.Saveflag) errcode = savefinaloutput(pr); } return errcode; } -int stepqual(EN_Project *pr, long *tleft) +int stepqual(Project *pr, long *tleft) /* **-------------------------------------------------------------- ** Input: none @@ -327,19 +321,20 @@ int stepqual(EN_Project *pr, long *tleft) **-------------------------------------------------------------- */ { + Quality *qual = &pr->quality; + Times *time = &pr->times; + long dt, hstep, t, tstep; int errcode = 0; - quality_t *qual = &pr->quality; - - tstep = qual->Qstep; + tstep = time->Qstep; do { // Set local time step to quality time step dt = tstep; // Find time step until next hydraulic event - hstep = pr->time_options.Htime - qual->Qtime; + hstep = time->Htime - time->Qtime; // If next hydraulic event occurs before end of local time step if (hstep < dt) @@ -349,21 +344,21 @@ int stepqual(EN_Project *pr, long *tleft) // ... transport quality over local time step if (qual->Qualflag != NONE) transport(pr, dt); - qual->Qtime += dt; + time->Qtime += dt; // ... quit if running quality concurrently with hydraulics - if (pr->hydraulics.OpenHflag) break; + if (pr->hydraul.OpenHflag) break; // ... otherwise call runqual() to update hydraulics errcode = runqual(pr, &t); - qual->Qtime = t; + time->Qtime = t; } // Otherwise transport quality over current local time step else { if (qual->Qualflag != NONE) transport(pr, dt); - qual->Qtime += dt; + time->Qtime += dt; } // Reduce quality time step by local time step @@ -376,7 +371,7 @@ int stepqual(EN_Project *pr, long *tleft) evalmassbalance(pr); // Update total simulation time left - *tleft = pr->time_options.Dur - qual->Qtime; + *tleft = time->Dur - time->Qtime; // If no more time steps remain if (!errcode && *tleft == 0) @@ -388,13 +383,13 @@ int stepqual(EN_Project *pr, long *tleft) } // ... write the final portion of the binary output file - if (pr->save_options.Saveflag) errcode = savefinaloutput(pr); + if (pr->outfile.Saveflag) errcode = savefinaloutput(pr); } return errcode; } -int closequal(EN_Project *pr) +int closequal(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -403,7 +398,7 @@ int closequal(EN_Project *pr) **-------------------------------------------------------------- */ { - quality_t *qual = &pr->quality; + Quality *qual = &pr->quality; int errcode = 0; if (qual->SegPool) mempool_delete(qual->SegPool); @@ -411,14 +406,12 @@ int closequal(EN_Project *pr) FREE(qual->LastSeg); FREE(qual->PipeRateCoeff); FREE(qual->FlowDir); - FREE(qual->Ilist); - FREE(qual->IlistPtr); FREE(qual->SortedNodes); return errcode; } -double avgqual(EN_Project *pr, int k) +double avgqual(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -427,12 +420,12 @@ double avgqual(EN_Project *pr, int k) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + double vsum = 0.0, msum = 0.0; Pseg seg; - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - if (qual->Qualflag == NONE) return 0.0; // Sum up the quality and volume in each segment of the link @@ -459,7 +452,7 @@ double avgqual(EN_Project *pr, int k) } -double findsourcequal(EN_Project *pr, int n, double volin, double volout, long tstep) +double findsourcequal(Project *pr, int n, double volin, double volout, long tstep) /* **--------------------------------------------------------------------- ** Input: n = node index @@ -472,14 +465,14 @@ double findsourcequal(EN_Project *pr, int n, double volin, double volout, long t **--------------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Times *time = &pr->times; + double massadded = 0.0, c; Psource source; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - time_options_t *time = &pr->time_options; - // Sources only apply to CHEMICAL analyses if (qual->Qualflag != CHEM) return 0.0; @@ -487,7 +480,7 @@ double findsourcequal(EN_Project *pr, int n, double volin, double volout, long t source = net->Node[n].S; if (source == NULL) return 0.0; if (source->C0 == 0.0) return 0.0; - if (volout / tstep <= QZERO) return 0.0; + if (volout / tstep <= Q_STAGNANT) return 0.0; // Added source concentration depends on source type c = sourcequal(pr, source); @@ -540,7 +533,7 @@ double findsourcequal(EN_Project *pr, int n, double volin, double volout, long t } -double sourcequal(EN_Project *pr, Psource source) +double sourcequal(Project *pr, Psource source) /* **-------------------------------------------------------------- ** Input: source = a water quality source object @@ -549,14 +542,14 @@ double sourcequal(EN_Project *pr, Psource source) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + Times *time = &pr->times; + int i; long k; double c; - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - time_options_t *time = &pr->time_options; - // Get source concentration (or mass flow) in original units c = source->C0; @@ -568,13 +561,13 @@ double sourcequal(EN_Project *pr, Psource source) // Apply time pattern if assigned i = source->Pat; if (i == 0) return c; - k = ((qual->Qtime + time->Pstart) / time->Pstep) % + k = ((time->Qtime + time->Pstart) / time->Pstep) % (long)net->Pattern[i].Length; return (c * net->Pattern[i].F[k]); } -void evalmassbalance(EN_Project *pr) +void evalmassbalance(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -584,27 +577,28 @@ void evalmassbalance(EN_Project *pr) **-------------------------------------------------------------- */ { + Quality *qual = &pr->quality; + double massin; double massout; double massreacted; - quality_t *qual = &pr->quality; - if (qual->Qualflag == NONE) qual->massbalance.ratio = 1.0; + if (qual->Qualflag == NONE) qual->MassBalance.ratio = 1.0; else { - qual->massbalance.final = findstoredmass(pr); - massin = qual->massbalance.initial + qual->massbalance.inflow; - massout = qual->massbalance.outflow + qual->massbalance.final; - massreacted = qual->massbalance.reacted; + qual->MassBalance.final = findstoredmass(pr); + massin = qual->MassBalance.initial + qual->MassBalance.inflow; + massout = qual->MassBalance.outflow + qual->MassBalance.final; + massreacted = qual->MassBalance.reacted; if (massreacted > 0.0) massout += massreacted; else massin -= massreacted; - if (massin == 0.0) qual->massbalance.ratio = 1.0; - else qual->massbalance.ratio = massout / massin; + if (massin == 0.0) qual->MassBalance.ratio = 1.0; + else qual->MassBalance.ratio = massout / massin; } } -double findstoredmass(EN_Project *pr) +double findstoredmass(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -614,13 +608,13 @@ double findstoredmass(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + int i, k; double totalmass = 0.0; Pseg seg; - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - // Mass residing in each pipe for (k = 1; k <= net->Nlinks; k++) { @@ -654,7 +648,7 @@ double findstoredmass(EN_Project *pr) return totalmass; } -int flowdirchanged(EN_Project *pr) +int flowdirchanged(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -663,25 +657,25 @@ int flowdirchanged(EN_Project *pr) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + int k; int result = FALSE; int newdir; int olddir; double q; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - // Examine each network link for (k = 1; k <= pr->network.Nlinks; k++) { // Determine sign (+1 or -1) of new flow rate olddir = qual->FlowDir[k]; - q = (hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlows[k]; + q = (hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlow[k]; newdir = SGN(q); // Indicate if flow is negligible - if (fabs(q) < QZERO) newdir = 0; + if (fabs(q) < Q_STAGNANT) newdir = 0; // Reverse link's volume segments if flow direction changes sign if (newdir * olddir < 0) reversesegs(pr, k); @@ -696,5 +690,3 @@ int flowdirchanged(EN_Project *pr) } return result; } - -/************************* End of QUALITY.C ***************************/ diff --git a/src/qualreact.c b/src/qualreact.c index 54ef567..f728ea7 100644 --- a/src/qualreact.c +++ b/src/qualreact.c @@ -1,40 +1,45 @@ /* -********************************************************************* - -QUALREACT.C -- water quality reaction module for the EPANET program - -********************************************************************* +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: qualreact.c +Description: computes water quality reactions within pipes and tanks +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/27/2018 +****************************************************************************** */ #include #include #include "types.h" -// Exported Functions -char setreactflag(EN_Project *pr); +// Exported functions +char setreactflag(Project *); double getucf(double); -void ratecoeffs(EN_Project *pr); -void reactpipes(EN_Project *pr, long); -void reacttanks(EN_Project *pr, long); -double mixtank(EN_Project *pr, int, double, double ,double); +void ratecoeffs(Project *); +void reactpipes(Project *, long); +void reacttanks(Project *, long); +double mixtank(Project *, int, double, double ,double); -// Imported Functions -extern void addseg(EN_Project *pr, int, double, double); +// Imported functions +extern void addseg(Project *, int, double, double); -// Local Functions -static double piperate(EN_Project *pr, int); -static double pipereact(EN_Project *pr, int, double, double, long); -static double tankreact(EN_Project *pr, double, double, double, long); -static double bulkrate(EN_Project *pr, double, double, double); -static double wallrate(EN_Project *pr, double, double, double, double); +// Local functions +static double piperate(Project *, int); +static double pipereact(Project *, int, double, double, long); +static double tankreact(Project *, double, double, double, long); +static double bulkrate(Project *, double, double, double); +static double wallrate(Project *, double, double, double, double); -static void tankmix1(EN_Project *pr, int, double, double, double); -static void tankmix2(EN_Project *pr, int, double, double, double); -static void tankmix3(EN_Project *pr, int, double, double, double); -static void tankmix4(EN_Project *pr, int, double, double, double); +static void tankmix1(Project *, int, double, double, double); +static void tankmix2(Project *, int, double, double, double); +static void tankmix3(Project *, int, double, double, double); +static void tankmix4(Project *, int, double, double, double); -char setreactflag(EN_Project *pr) +char setreactflag(Project *pr) /* **----------------------------------------------------------- ** Input: none @@ -43,8 +48,8 @@ char setreactflag(EN_Project *pr) **----------------------------------------------------------- */ { + Network *net = &pr->network; int i; - EN_Network *net = &pr->network; if (pr->quality.Qualflag == TRACE) return 0; else if (pr->quality.Qualflag == AGE) return 1; @@ -82,7 +87,7 @@ double getucf(double order) } -void ratecoeffs(EN_Project *pr) +void ratecoeffs(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -91,12 +96,12 @@ void ratecoeffs(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + int k; double kw; - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - for (k = 1; k <= net->Nlinks; k++) { kw = net->Link[k].Kw; @@ -107,7 +112,7 @@ void ratecoeffs(EN_Project *pr) } -void reactpipes(EN_Project *pr, long dt) +void reactpipes(Project *pr, long dt) /* **-------------------------------------------------------------- ** Input: dt = time step @@ -116,13 +121,13 @@ void reactpipes(EN_Project *pr, long dt) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + int k; Pseg seg; double cseg, rsum, vsum; - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - // Examine each link in network for (k = 1; k <= net->Nlinks; k++) { @@ -140,7 +145,7 @@ void reactpipes(EN_Project *pr, long dt) seg->c = pipereact(pr, k, seg->c, seg->v, dt); // Update reaction component of mass balance - qual->massbalance.reacted += (cseg - seg->c) * seg->v; + qual->MassBalance.reacted += (cseg - seg->c) * seg->v; // Accumulate volume-weighted reaction rate if (qual->Qualflag == CHEM) @@ -158,7 +163,7 @@ void reactpipes(EN_Project *pr, long dt) } -void reacttanks(EN_Project *pr, long dt) +void reacttanks(Project *pr, long dt) /* **-------------------------------------------------------------- ** Input: dt = time step @@ -167,13 +172,13 @@ void reacttanks(EN_Project *pr, long dt) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + int i, k; double c; Pseg seg; - - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - Stank *tank; + Stank *tank; // Examine each tank in network for (i = 1; i <= net->Ntanks; i++) @@ -191,14 +196,14 @@ void reacttanks(EN_Project *pr, long dt) { c = seg->c; seg->c = tankreact(pr, seg->c, seg->v, tank->Kb, dt); - qual->massbalance.reacted += (c - seg->c) * seg->v; + qual->MassBalance.reacted += (c - seg->c) * seg->v; seg = seg->prev; } } } -double piperate(EN_Project *pr, int k) +double piperate(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -209,11 +214,11 @@ double piperate(EN_Project *pr, int k) **-------------------------------------------------------------- */ { - double a, d, u, q, kf, kw, y, Re, Sh; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; + double a, d, u, q, kf, kw, y, Re, Sh; d = net->Link[k].Diam; // Pipe diameter, ft @@ -226,7 +231,7 @@ double piperate(EN_Project *pr, int k) // Compute Reynolds No. // Flow rate made consistent with how its saved to hydraulics file - q = (hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlows[k]; + q = (hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlow[k]; a = PI * d * d / 4.0; // pipe area u = fabs(q) / a; // flow velocity Re = u * d / hyd->Viscos; // Reynolds number @@ -258,7 +263,7 @@ double piperate(EN_Project *pr, int k) } -double pipereact(EN_Project *pr, int k, double c, double v, long dt) +double pipereact(Project *pr, int k, double c, double v, long dt) /* **------------------------------------------------------------ ** Input: k = link index @@ -271,10 +276,10 @@ double pipereact(EN_Project *pr, int k, double c, double v, long dt) **------------------------------------------------------------ */ { - double cnew, dc, dcbulk, dcwall, rbulk, rwall; + Network *net = &pr->network; + Quality *qual = &pr->quality; - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; + double cnew, dc, dcbulk, dcwall, rbulk, rwall; // For water age (hrs), update concentration by timestep if (qual->Qualflag == AGE) @@ -294,7 +299,7 @@ double pipereact(EN_Project *pr, int k, double c, double v, long dt) dcwall = rwall * (double)dt; // Update cumulative mass reacted - if (pr->time_options.Htime >= pr->time_options.Rstart) + if (pr->times.Htime >= pr->times.Rstart) { qual->Wbulk += fabs(dcbulk) * v; qual->Wwall += fabs(dcwall) * v; @@ -308,7 +313,7 @@ double pipereact(EN_Project *pr, int k, double c, double v, long dt) } -double tankreact(EN_Project *pr, double c, double v, double kb, long dt) +double tankreact(Project *pr, double c, double v, double kb, long dt) /* **------------------------------------------------------- ** Input: c = current quality in tank @@ -321,8 +326,9 @@ double tankreact(EN_Project *pr, double c, double v, double kb, long dt) **------------------------------------------------------- */ { + Quality *qual = &pr->quality; + double cnew, dc, rbulk; - quality_t *qual = &pr->quality; // For water age, update concentration by timestep if (qual->Qualflag == AGE) @@ -338,7 +344,7 @@ double tankreact(EN_Project *pr, double c, double v, double kb, long dt) // Find concentration change & update quality dc = rbulk * (double)dt; - if (pr->time_options.Htime >= pr->time_options.Rstart) + if (pr->times.Htime >= pr->times.Rstart) { qual->Wtank += fabs(dc) * v; } @@ -349,7 +355,7 @@ double tankreact(EN_Project *pr, double c, double v, double kb, long dt) } -double bulkrate(EN_Project *pr, double c, double kb, double order) +double bulkrate(Project *pr, double c, double kb, double order) /* **----------------------------------------------------------- ** Input: c = current WQ concentration @@ -360,8 +366,9 @@ double bulkrate(EN_Project *pr, double c, double kb, double order) **----------------------------------------------------------- */ { + Quality *qual = &pr->quality; + double c1; - quality_t *qual = &pr->quality; // Find bulk reaction potential taking into account // limiting potential & reaction order. @@ -396,7 +403,7 @@ double bulkrate(EN_Project *pr, double c, double kb, double order) } -double wallrate(EN_Project *pr, double c, double d, double kw, double kf) +double wallrate(Project *pr, double c, double d, double kw, double kf) /* **------------------------------------------------------------ ** Input: c = current WQ concentration @@ -410,7 +417,8 @@ double wallrate(EN_Project *pr, double c, double d, double kw, double kf) **------------------------------------------------------------ */ { - quality_t *qual = &pr->quality; + Quality *qual = &pr->quality; + if (kw == 0.0 || d == 0.0) return (0.0); if (qual->WallOrder == 0.0) // 0-order reaction */ @@ -424,7 +432,7 @@ double wallrate(EN_Project *pr, double c, double d, double kw, double kf) } -double mixtank(EN_Project *pr, int n, double volin, double massin, double volout) +double mixtank(Project *pr, int n, double volin, double massin, double volout) /* **------------------------------------------------------------ ** Input: n = node index @@ -436,11 +444,11 @@ double mixtank(EN_Project *pr, int n, double volin, double massin, double volout **------------------------------------------------------------ */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + int i; double vnet; - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - i = n - net->Njuncs; vnet = volin - volout; switch (net->Tank[i].MixModel) @@ -454,7 +462,7 @@ double mixtank(EN_Project *pr, int n, double volin, double massin, double volout } -void tankmix1(EN_Project *pr, int i, double vin, double win, double vnet) +void tankmix1(Project *pr, int i, double vin, double win, double vnet) /* **--------------------------------------------- ** Input: i = tank index @@ -466,14 +474,14 @@ void tankmix1(EN_Project *pr, int i, double vin, double win, double vnet) **--------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + int k; double vnew; Pseg seg; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - Stank *tank = &net->Tank[i]; + Stank *tank = &net->Tank[i]; k = net->Nlinks + i; seg = qual->FirstSeg[k]; @@ -488,7 +496,7 @@ void tankmix1(EN_Project *pr, int i, double vin, double win, double vnet) } -void tankmix2(EN_Project *pr, int i, double vin, double win, double vnet) +void tankmix2(Project *pr, int i, double vin, double win, double vnet) /* **------------------------------------------------ ** Input: i = tank index @@ -500,15 +508,15 @@ void tankmix2(EN_Project *pr, int i, double vin, double win, double vnet) **------------------------------------------------ */ { - int k; + Network *net = &pr->network; + Quality *qual = &pr->quality; + + int k; double vt, // Transferred volume vmz; // Full mixing zone volume Pseg mixzone, // Mixing zone segment stagzone; // Stagnant zone segment - - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - Stank *tank = &pr->network.Tank[i]; + Stank *tank = &pr->network.Tank[i]; // Identify segments for each compartment k = net->Nlinks + i; @@ -568,7 +576,7 @@ void tankmix2(EN_Project *pr, int i, double vin, double win, double vnet) } -void tankmix3(EN_Project *pr, int i, double vin, double win, double vnet) +void tankmix3(Project *pr, int i, double vin, double win, double vnet) /* **---------------------------------------------------------- ** Input: i = tank index @@ -580,15 +588,15 @@ void tankmix3(EN_Project *pr, int i, double vin, double win, double vnet) **---------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + int k; double vout, vseg; double cin, vsum, wsum; Pseg seg; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - Stank *tank = &pr->network.Tank[i]; + Stank *tank = &pr->network.Tank[i]; k = net->Nlinks + i; if (qual->LastSeg[k] == NULL || qual->FirstSeg[k] == NULL) return; @@ -639,7 +647,7 @@ void tankmix3(EN_Project *pr, int i, double vin, double win, double vnet) } -void tankmix4(EN_Project *pr, int i, double vin, double win, double vnet) +void tankmix4(Project *pr, int i, double vin, double win, double vnet) /* **---------------------------------------------------------- ** Input: i = tank index @@ -651,13 +659,13 @@ void tankmix4(EN_Project *pr, int i, double vin, double win, double vnet) **---------------------------------------------------------- */ { + Network *net = &pr->network; + Quality *qual = &pr->quality; + int k, n; double cin, vsum, wsum, vseg; Pseg seg; - - EN_Network *net = &pr->network; - quality_t *qual = &pr->quality; - Stank *tank = &pr->network.Tank[i]; + Stank *tank = &pr->network.Tank[i]; k = net->Nlinks + i; if (qual->LastSeg[k] == NULL || qual->FirstSeg[k] == NULL) return; diff --git a/src/qualroute.c b/src/qualroute.c index 6806b80..8ee2d5d 100644 --- a/src/qualroute.c +++ b/src/qualroute.c @@ -1,46 +1,55 @@ /* -********************************************************************* - -QUALROUTE.C -- water quality routing module for the EPANET program - -********************************************************************* +****************************************************************************** +Project: OWA EPANET +Version: 2.2 +Module: qualroute.c +Description: computes water quality transport over a single time step +Authors: see AUTHORS +Copyright: see AUTHORS +License: see LICENSE +Last Updated: 11/27/2018 +****************************************************************************** */ -#include -#include -#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif +#include #include "mempool.h" #include "types.h" // Macro to compute the volume of a link #define LINKVOL(k) (0.785398 * net->Link[(k)].Len * SQR(net->Link[(k)].Diam)) + // Macro to get link flow compatible with flow saved to hydraulics file -#define LINKFLOW(k) ((hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlows[k]) +#define LINKFLOW(k) ((hyd->LinkStatus[k] <= CLOSED) ? 0.0 : hyd->LinkFlow[k]) -// Exported Functions -int buildilists(EN_Project *pr); -int sortnodes(EN_Project *pr); -void transport(EN_Project *pr, long); -void initsegs(EN_Project *pr); -void reversesegs(EN_Project *pr, int); -void addseg(EN_Project *pr, int, double, double); +// Exported functions +int sortnodes(Project *); +void transport(Project *, long); +void initsegs(Project *); +void reversesegs(Project *, int); +void addseg(Project *, int, double, double); -// Imported Functions -extern double findsourcequal(EN_Project *pr, int, double, double, long); -extern void reactpipes(EN_Project *pr, long); -extern void reacttanks(EN_Project *pr, long); -extern double mixtank(EN_Project *pr, int, double, double, double); +// Imported functions +extern double findsourcequal(Project *, int, double, double, long); +extern void reactpipes(Project *, long); +extern void reacttanks(Project *, long); +extern double mixtank(Project *, int, double, double, double); -// Local Functions -static void evalnodeinflow(EN_Project *pr, int, long, double *, double *); -static void evalnodeoutflow(EN_Project *pr, int, double, long); -static double findnodequal(EN_Project *pr, int, double, double, double, long); -static double noflowqual(EN_Project *pr, int); -static void updatemassbalance(EN_Project *pr, int, double, double, long); -static int selectnonstacknode(EN_Project *pr, int, int *); +// Local functions +static void evalnodeinflow(Project *, int, long, double *, double *); +static void evalnodeoutflow(Project *, int, double, long); +static double findnodequal(Project *, int, double, double, double, long); +static double noflowqual(Project *, int); +static void updatemassbalance(Project *, int, double, double, long); +static int selectnonstacknode(Project *, int, int *); -void transport(EN_Project *pr, long tstep) +void transport(Project *pr, long tstep) /* **-------------------------------------------------------------- ** Input: tstep = length of current time step @@ -50,12 +59,13 @@ void transport(EN_Project *pr, long tstep) **-------------------------------------------------------------- */ { - int i, j, k, m, n; - double volin, massin, volout, nodequal; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - EN_Network *net = &pr->network; + int j, k, m, n; + double volin, massin, volout, nodequal; + Padjlist alink; // React contents of each pipe and tank if (qual->Reactflag) @@ -76,10 +86,10 @@ void transport(EN_Project *pr, long tstep) volout = 0.0; // ... examine each link with flow into the node - for (i = qual->IlistPtr[n]; i < qual->IlistPtr[n + 1]; i++) + for (alink = net->Adjlist[n]; alink != NULL; alink = alink->next) { // ... k is index of next link incident on node n - k = qual->Ilist[i]; + k = alink->link; // ... link has flow into node - add it to node's inflow // (m is index of link's downstream node) @@ -107,10 +117,10 @@ void transport(EN_Project *pr, long tstep) nodequal = findnodequal(pr, n, volin, massin, volout, tstep); // ... examine each link with flow out of the node - for (i = qual->IlistPtr[n]; i < qual->IlistPtr[n + 1]; i++) + for (alink = net->Adjlist[n]; alink != NULL; alink = alink->next) { // ... link k incident on node n has upstream node m equal to n - k = qual->Ilist[i]; + k = alink->link; m = net->Link[k].N1; if (qual->FlowDir[k] < 0) m = net->Link[k].N2; if (m == n) @@ -123,27 +133,27 @@ void transport(EN_Project *pr, long tstep) } } -void evalnodeinflow(EN_Project *pr, int k, long tstep, double *volin, - double *massin) - /* - **-------------------------------------------------------------- - ** Input: k = link index - ** tstep = quality routing time step - ** Output: volin = flow volume entering a node - ** massin = constituent mass entering a node - ** Purpose: adds the contribution of a link's outflow volume - ** and constituent mass to the total inflow into its - ** downstream node over a time step. - **-------------------------------------------------------------- - */ +void evalnodeinflow(Project *pr, int k, long tstep, double *volin, + double *massin) +/* +**-------------------------------------------------------------- +** Input: k = link index +** tstep = quality routing time step +** Output: volin = flow volume entering a node +** massin = constituent mass entering a node +** Purpose: adds the contribution of a link's outflow volume +** and constituent mass to the total inflow into its +** downstream node over a time step. +**-------------------------------------------------------------- +*/ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + double q, v, vseg; Pseg seg; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - // Get flow rate (q) and flow volume (v) through link q = LINKFLOW(k); v = fabs(q) * tstep; @@ -185,24 +195,24 @@ void evalnodeinflow(EN_Project *pr, int k, long tstep, double *volin, } -double findnodequal(EN_Project *pr, int n, double volin, - double massin, double volout, long tstep) - /* - **-------------------------------------------------------------- - ** Input: n = node index - ** volin = flow volume entering node - ** massin = mass entering node - ** volout = flow volume leaving node - ** tstep = length of current time step - ** Output: returns water quality in a node's outflow - ** Purpose: computes a node's new quality from its inflow - ** volume and mass, including any source contribution. - **-------------------------------------------------------------- - */ +double findnodequal(Project *pr, int n, double volin, + double massin, double volout, long tstep) +/* +**-------------------------------------------------------------- +** Input: n = node index +** volin = flow volume entering node +** massin = mass entering node +** volout = flow volume leaving node +** tstep = length of current time step +** Output: returns water quality in a node's outflow +** Purpose: computes a node's new quality from its inflow +** volume and mass, including any source contribution. +**-------------------------------------------------------------- +*/ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; // Node is a junction - update its water quality if (net->Node[n].Type == JUNCTION) @@ -262,7 +272,7 @@ double findnodequal(EN_Project *pr, int n, double volin, } -double noflowqual(EN_Project *pr, int n) +double noflowqual(Project *pr, int n) /* **-------------------------------------------------------------- ** Input: n = node index @@ -274,33 +284,34 @@ double noflowqual(EN_Project *pr, int n) **-------------------------------------------------------------- */ { - int i, k, inflow, kount = 0; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + + int k, inflow, kount = 0; double c = 0.0; FlowDirection dir; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; + Padjlist alink; // Examine each link incident on the node - for (i = qual->IlistPtr[n]; i < qual->IlistPtr[n + 1]; i++) + for (alink = net->Adjlist[n]; alink != NULL; alink = alink->next) { // ... index of an incident link - k = qual->Ilist[i]; + k = alink->link; dir = qual->FlowDir[k]; - // Node n is link's downstream node - add quality + // Node n is link's downstream node - add quality // of link's first segment to average if (net->Link[k].N2 == n && dir >= 0) inflow = TRUE; else if (net->Link[k].N1 == n && dir < 0) inflow = TRUE; - else inflow = FALSE; + else inflow = FALSE; if (inflow == TRUE && qual->FirstSeg[k] != NULL) { c += qual->FirstSeg[k]->c; kount++; } - // Node n is link's upstream node - add quality + // Node n is link's upstream node - add quality // of link's last segment to average else if (inflow == FALSE && qual->LastSeg[k] != NULL) { @@ -313,7 +324,7 @@ double noflowqual(EN_Project *pr, int n) } -void evalnodeoutflow(EN_Project *pr, int k, double c, long tstep) +void evalnodeoutflow(Project *pr, int k, double c, long tstep) /* **-------------------------------------------------------------- ** Input: k = link index @@ -325,12 +336,12 @@ void evalnodeoutflow(EN_Project *pr, int k, double c, long tstep) **-------------------------------------------------------------- */ { + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + double v; Pseg seg; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - // Find flow volume (v) released over time step v = fabs(LINKFLOW(k)) * tstep; if (v == 0.0) return; @@ -358,27 +369,26 @@ void evalnodeoutflow(EN_Project *pr, int k, double c, long tstep) } -void updatemassbalance(EN_Project *pr, int n, double massin, - double volout, long tstep) - /* - **-------------------------------------------------------------- - ** Input: n = node index - ** massin = mass inflow to node - ** volout = outflow volume from node - ** Output: none - ** Purpose: Adds a node's external mass inflow and outflow - ** over the current time step to the network's - ** overall mass balance. - **-------------------------------------------------------------- - */ +void updatemassbalance(Project *pr, int n, double massin, + double volout, long tstep) +/* +**-------------------------------------------------------------- +** Input: n = node index +** massin = mass inflow to node +** volout = outflow volume from node +** Output: none +** Purpose: Adds a node's external mass inflow and outflow +** over the current time step to the network's +** overall mass balance. +**-------------------------------------------------------------- +*/ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + double masslost = 0.0, - massadded = 0.0; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - + massadded = 0.0; switch (net->Node[n].Type) { @@ -401,82 +411,12 @@ void updatemassbalance(EN_Project *pr, int n, double massin, massadded = qual->SourceQual * volout; break; } - qual->massbalance.outflow += masslost; - qual->massbalance.inflow += massadded; + qual->MassBalance.outflow += masslost; + qual->MassBalance.inflow += massadded; } - -int buildilists(EN_Project *pr) -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns an error code -** Purpose: Creates link incidence lists for each node stored -** in compact form. -**-------------------------------------------------------------- -*/ -{ - int i, j, k, n, n1, n2; - int *degree = NULL; - - quality_t *qual = &pr->quality; - EN_Network *net = &pr->network; - - // Allocate an array to count # links incident on each node - n = net->Nnodes + 1; - degree = (int *)calloc(n, sizeof(int)); - if (degree == NULL) return 101; - - // Count # links incident on each node - for (k = 1; k <= net->Nlinks; k++) - { - degree[net->Link[k].N1]++; - degree[net->Link[k].N2]++; - } - - // Use incidence counts to determine start position of - // each node's incidence list in Xilist - qual->IlistPtr[1] = 1; - for (i = 1; i <= n; i++) - { - qual->IlistPtr[i + 1] = qual->IlistPtr[i] + degree[i]; - } - - // Add each link to the incidence lists of its start & end nodes - for (i = 1; i <= net->Nnodes; i++) degree[i] = 0; - for (k = 1; k <= net->Nlinks; k++) - { - // j is index of next unused location in link's start node list - n1 = net->Link[k].N1; - j = qual->IlistPtr[n1] + degree[n1]; - qual->Ilist[j] = k; - degree[n1]++; - - // Repeat same for end node - n2 = net->Link[k].N2; - j = qual->IlistPtr[n2] + degree[n2]; - qual->Ilist[j] = k; - degree[n2]++; - } - free(degree); - - /*//////// QA CHECK - for (i = 1; i <= net->Nnodes; i++) - { - printf("\nNode %s: ", net->Node[i].ID); - for (j = qual->IlistPtr[i]; j < qual->IlistPtr[i + 1]; j++) - { - printf(" %s,", net->Link[qual->Ilist[j]].ID); - } - } - */ - return 0; -} - - - -int sortnodes(EN_Project *pr) +int sortnodes(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -487,6 +427,10 @@ int sortnodes(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + int i, j, k, n; int *indegree = NULL; int *stack = NULL; @@ -494,10 +438,7 @@ int sortnodes(EN_Project *pr) int numsorted = 0; int errcode = 0; FlowDirection dir; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; + Padjlist alink; // Allocate an array to count # links with inflow to each node // and for a stack to hold nodes waiting to be processed @@ -548,10 +489,10 @@ int sortnodes(EN_Project *pr) // ... for each outflow link from this node reduce the in-degree // of its downstream node - for (j = qual->IlistPtr[i]; j < qual->IlistPtr[i + 1]; j++) + for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) { // ... k is the index of the next link incident on node i - k = qual->Ilist[j]; + k = alink->link; // ... skip link if flow is negligible if (qual->FlowDir[k] == 0) continue; @@ -579,25 +520,11 @@ int sortnodes(EN_Project *pr) if (numsorted < net->Nnodes) errcode = 120; FREE(indegree); FREE(stack); - /* - /////////////////// QA CHECK - snprintf(pr->Msg, MAXMSG, "\n\nSorted Nodes:"); - writeline(pr, pr->Msg); - for (i = 1; i <= net->Nnodes; i++) - { - j = qual->SortedNodes[i]; - snprintf(pr->Msg, MAXMSG, "%s", net->Node[j].ID); - writeline(pr, pr->Msg); - } - //printf("\n"); - //system("pause"); - ///////////////// - */ return errcode; } -int selectnonstacknode(EN_Project *pr, int numsorted, int *indegree) +int selectnonstacknode(Project *pr, int numsorted, int *indegree) /* **-------------------------------------------------------------- ** Input: numsorted = number of nodes that have been sorted @@ -607,24 +534,21 @@ int selectnonstacknode(EN_Project *pr, int numsorted, int *indegree) **-------------------------------------------------------------- */ { - int i, j, k, m, n; + Network *net = &pr->network; + Quality *qual = &pr->quality; - quality_t *qual = &pr->quality; - EN_Network *net = &pr->network; + int i, m, n; + Padjlist alink; // Examine each sorted node in last in - first out order for (i = numsorted; i > 0; i--) { // For each link connected to the sorted node m = qual->SortedNodes[i]; - for (j = qual->IlistPtr[m]; j < qual->IlistPtr[m + 1]; j++) + for (alink = net->Adjlist[m]; alink != NULL; alink = alink->next) { - // ... k is index of next link incident on node m - k = qual->Ilist[j]; - // ... n is the node of link k opposite to node m - n = net->Link[k].N2; - if (n == m) n = net->Link[k].N1; + n = alink->node; // ... select node n if it still has inflow links if (indegree[n] > 0) return n; @@ -643,7 +567,7 @@ int selectnonstacknode(EN_Project *pr, int numsorted, int *indegree) } -void initsegs(EN_Project *pr) +void initsegs(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -653,13 +577,13 @@ void initsegs(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + int j, k; double c, v, v1; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - quality_t *qual = &pr->quality; - // Add one segment with assigned downstream node quality to each pipe for (k = 1; k <= net->Nlinks; k++) { @@ -706,7 +630,7 @@ void initsegs(EN_Project *pr) } -void reversesegs(EN_Project *pr, int k) +void reversesegs(Project *pr, int k) /* **-------------------------------------------------------------- ** Input: k = link index @@ -715,8 +639,8 @@ void reversesegs(EN_Project *pr, int k) **-------------------------------------------------------------- */ { - Pseg seg, nseg, pseg; - quality_t *qual = &pr->quality; + Quality *qual = &pr->quality; + Pseg seg, nseg, pseg; seg = qual->FirstSeg[k]; qual->FirstSeg[k] = qual->LastSeg[k]; @@ -732,7 +656,7 @@ void reversesegs(EN_Project *pr, int k) } -void addseg(EN_Project *pr, int k, double v, double c) +void addseg(Project *pr, int k, double v, double c) /* **------------------------------------------------------------- ** Input: k = segment chain index @@ -744,8 +668,8 @@ void addseg(EN_Project *pr, int k, double v, double c) **------------------------------------------------------------- */ { + Quality *qual = &pr->quality; Pseg seg; - quality_t *qual = &pr->quality; // Grab the next free segment from the segment pool if available if (qual->FreeSeg != NULL) diff --git a/src/report.c b/src/report.c index 0669f3f..817ccce 100644 --- a/src/report.c +++ b/src/report.c @@ -1,28 +1,14 @@ /* -********************************************************************* - -REPORT.C -- Reporting Routines for EPANET Program - -VERSION: 2.00 -DATE: 5/30/00 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -This module contains various procedures (all beginning with -'write') that are called from other modules to write formatted -output to a report file. - -It also contains function disconnected(), called from writehydwarn() -and writehyderr(), that checks if a hydraulic solution causes a -network to become disconnected. - -The function writeline(pr, S) is used throughout to write a -formatted string S to the report file. - -******************************************************************** + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: report.c + Description: procedures for writing formatted text to a report file + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ #include @@ -32,6 +18,13 @@ formatted string S to the report file. #else #include #endif + +#undef WINDOWS +#ifdef _WIN32 +#define WINDOWS +#define snprintf _snprintf +#endif + #include #include @@ -40,15 +33,9 @@ formatted string S to the report file. #include "hash.h" #include "text.h" -#undef WINDOWS -#ifdef _WIN32 -#define WINDOWS -#define snprintf _snprintf -#endif +#define MAXCOUNT 10 // Max. # of disconnected nodes listed -#define MAXCOUNT 10 /* Max. # of disconnected nodes listed */ - -/* Defined in enumstxt.h */ +// Defined in ENUMSTXT.H extern char *NodeTxt[]; extern char *LinkTxt[]; extern char *StatTxt[]; @@ -56,83 +43,92 @@ extern char *TstatTxt[]; extern char *RptFormTxt[]; extern char *DemandModelTxt[]; +// Local functions typedef REAL4 *Pfloat; -void writenodetable(EN_Project *pr, Pfloat *); -void writelinktable(EN_Project *pr, Pfloat *); +static void writenodetable(Project *, Pfloat *); +static void writelinktable(Project *, Pfloat *); +static void writeenergy(Project *); +static int writeresults(Project *); +static int disconnected(Project *); +static void marknodes(Project *, int, int *, char *); +static void getclosedlink(Project *, int, char *); +static void writelimits(Project *, int, int); +static int checklimits(Report *, double *, int, int); +static char *fillstr(char *, char, int); +static int getnodetype(Network *, int); -int writereport(EN_Project *pr) + +int writereport(Project *pr) /* **------------------------------------------------------ ** Input: none ** Output: returns error code ** Purpose: writes formatted output report to file -** -** Calls strcomp() from the EPANET.C module. **------------------------------------------------------ */ { - report_options_t *rep = &pr->report; - parser_data_t *par = &pr->parser; + Report *rpt = &pr->report; + Parser *parser = &pr->parser; - char tflag; - FILE *tfile; - int errcode = 0; + char tflag; + FILE *tfile; + int errcode = 0; - /* If no secondary report file specified then */ - /* write formatted output to primary report file. */ - rep->Fprinterr = FALSE; - if (rep->Rptflag && strlen(rep->Rpt2Fname) == 0 && rep->RptFile != NULL) { - if (rep->Energyflag) - writeenergy(pr); - errcode = writeresults(pr); - } - - /* A secondary report file was specified */ - else if (strlen(rep->Rpt2Fname) > 0) { - - /* If secondary report file has same name as either input */ - /* or primary report file then use primary report file. */ - if (strcomp(rep->Rpt2Fname, par->InpFname) || strcomp(rep->Rpt2Fname, rep->Rpt1Fname)) { - if (rep->Energyflag) - writeenergy(pr); - errcode = writeresults(pr); - } - - /* Otherwise write report to secondary report file. */ - else { - - /* Try to open file */ - tfile = rep->RptFile; - tflag = rep->Rptflag; - if ((rep->RptFile = fopen(rep->Rpt2Fname, "wt")) == NULL) { - rep->RptFile = tfile; - rep->Rptflag = tflag; - errcode = 303; - } - - /* Write full formatted report to file */ - else { - rep->Rptflag = 1; - writelogo(pr); - if (rep->Summaryflag) - writesummary(pr); - if (rep->Energyflag) - writeenergy(pr); + // If no secondary report file specified then + // write formatted output to primary report file + rpt->Fprinterr = FALSE; + if (rpt->Rptflag && strlen(rpt->Rpt2Fname) == 0 && rpt->RptFile != NULL) + { + if (rpt->Energyflag) writeenergy(pr); errcode = writeresults(pr); - fclose(rep->RptFile); - rep->RptFile = tfile; - rep->Rptflag = tflag; - } } - } + + // A secondary report file was specified + else if (strlen(rpt->Rpt2Fname) > 0) + { + // If secondary report file has same name as either input + // or primary report file then use primary report file. + if (strcomp(rpt->Rpt2Fname, parser->InpFname) || + strcomp(rpt->Rpt2Fname, rpt->Rpt1Fname)) + { + if (rpt->Energyflag) writeenergy(pr); + errcode = writeresults(pr); + } - /* Special error handler for write-to-file error */ - if (rep->Fprinterr) - errmsg(pr,309); - return (errcode); -} /* End of writereport */ + // Otherwise write report to secondary report file + else + { + // Try to open file + tfile = rpt->RptFile; + tflag = rpt->Rptflag; + if ((rpt->RptFile = fopen(rpt->Rpt2Fname, "wt")) == NULL) + { + rpt->RptFile = tfile; + rpt->Rptflag = tflag; + errcode = 303; + } -void writelogo(EN_Project *pr) + // Write full formatted report to file + else + { + rpt->Rptflag = 1; + writelogo(pr); + if (rpt->Summaryflag) writesummary(pr); + if (rpt->Energyflag) writeenergy(pr); + errcode = writeresults(pr); + fclose(rpt->RptFile); + rpt->RptFile = tfile; + rpt->Rptflag = tflag; + } + } + } + + // Special error handler for write-to-file error + if (rpt->Fprinterr) errmsg(pr, 309); + return errcode; +} + +void writelogo(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -141,36 +137,36 @@ void writelogo(EN_Project *pr) **-------------------------------------------------------------- */ { - report_options_t *rep = &pr->report; - - int version; - int major; - int minor; - char s[80]; - time_t timer; /* time_t structure & functions time() & */ - /* ctime() are defined in time.h */ + Report *rpt = &pr->report; - version = CODEVERSION; - major = version / 10000; - minor = (version % 10000) / 100; + int version; + int major; + int minor; + char s[80]; + time_t timer; // time_t structure & functions time() & + // ctime() are defined in time.h + + version = CODEVERSION; + major = version / 10000; + minor = (version % 10000) / 100; - time(&timer); - strcpy(rep->DateStamp, ctime(&timer)); - rep->PageNum = 1; - rep->LineNum = 2; - fprintf(rep->RptFile, FMT18); - fprintf(rep->RptFile, "%s", rep->DateStamp); - writeline(pr, LOGO1); - writeline(pr, LOGO2); - writeline(pr, LOGO3); - writeline(pr, LOGO4); - sprintf(s, LOGO5, major, minor); - writeline(pr, s); - writeline(pr, LOGO6); - writeline(pr, ""); -} /* End of writelogo */ + time(&timer); + strcpy(rpt->DateStamp, ctime(&timer)); + rpt->PageNum = 1; + rpt->LineNum = 2; + fprintf(rpt->RptFile, FMT18); + fprintf(rpt->RptFile, "%s", rpt->DateStamp); + writeline(pr, LOGO1); + writeline(pr, LOGO2); + writeline(pr, LOGO3); + writeline(pr, LOGO4); + sprintf(s, LOGO5, major, minor); + writeline(pr, s); + writeline(pr, LOGO6); + writeline(pr, ""); +} -void writesummary(EN_Project *pr) +void writesummary(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -179,32 +175,31 @@ void writesummary(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - quality_t *qu = &pr->quality; - parser_data_t *par = &pr->parser; - time_options_t *time = &pr->time_options; - time_options_t *ti = &pr->time_options; - + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Quality *qual = &pr->quality; + Report *rpt = &pr->report; + Parser *parser = &pr->parser; + Times *time = &pr->times; + char s[MAXFNAME + 1]; int i; int nres = 0; - for (i = 0; i < 3; i++) { - if (strlen(pr->Title[i]) > 0) { + for (i = 0; i < 3; i++) + { + if (strlen(pr->Title[i]) > 0) + { sprintf(s, "%-.70s", pr->Title[i]); writeline(pr, s); } } writeline(pr, " "); - sprintf(s, FMT19, par->InpFname); + sprintf(s, FMT19, parser->InpFname); writeline(pr, s); sprintf(s, FMT20, net->Njuncs); writeline(pr, s); - for (i = 1; i <= net->Ntanks; i++) - if (net->Tank[i].A == 0.0) - nres++; + for (i = 1; i <= net->Ntanks; i++) if (net->Tank[i].A == 0.0) nres++; sprintf(s, FMT21a, nres); writeline(pr, s); sprintf(s, FMT21b, net->Ntanks - nres); @@ -219,18 +214,20 @@ void writesummary(EN_Project *pr) writeline(pr, s); sprintf(s, FMT25a, DemandModelTxt[hyd->DemandModel]); writeline(pr, s); - sprintf(s, FMT26, time->Hstep * pr->Ucf[TIME], rep->Field[TIME].Units); + sprintf(s, FMT26, time->Hstep * pr->Ucf[TIME], rpt->Field[TIME].Units); writeline(pr, s); sprintf(s, FMT27, hyd->Hacc); writeline(pr, s); - if (hyd->HeadErrorLimit > 0.0) { - sprintf(s, FMT27d, hyd->HeadErrorLimit*pr->Ucf[HEAD], rep->Field[HEAD].Units); - writeline(pr, s); + if (hyd->HeadErrorLimit > 0.0) + { + sprintf(s, FMT27d, hyd->HeadErrorLimit*pr->Ucf[HEAD], rpt->Field[HEAD].Units); + writeline(pr, s); } - if (hyd->FlowChangeLimit > 0.0) { - sprintf(s, FMT27e, hyd->FlowChangeLimit*pr->Ucf[FLOW], rep->Field[FLOW].Units); - writeline(pr, s); + if (hyd->FlowChangeLimit > 0.0) + { + sprintf(s, FMT27e, hyd->FlowChangeLimit*pr->Ucf[FLOW], rpt->Field[FLOW].Units); + writeline(pr, s); } sprintf(s, FMT27a, hyd->CheckFreq); @@ -239,56 +236,50 @@ void writesummary(EN_Project *pr) writeline(pr, s); sprintf(s, FMT27c, hyd->DampLimit); writeline(pr, s); - sprintf(s, FMT28, hyd->MaxIter); writeline(pr, s); - if (qu->Qualflag == NONE || time->Dur == 0.0) - sprintf(s, FMT29); - else if (qu->Qualflag == CHEM) - sprintf(s, FMT30, qu->ChemName); - else if (qu->Qualflag == TRACE) - sprintf(s, FMT31, net->Node[qu->TraceNode].ID); - else if (qu->Qualflag == AGE) - sprintf(s, FMT32); + + if (qual->Qualflag == NONE || time->Dur == 0.0) sprintf(s, FMT29); + else if (qual->Qualflag == CHEM) sprintf(s, FMT30, qual->ChemName); + else if (qual->Qualflag == TRACE) sprintf(s, FMT31, net->Node[qual->TraceNode].ID); + else if (qual->Qualflag == AGE) printf(s, FMT32); writeline(pr, s); - if (qu->Qualflag != NONE && ti->Dur > 0) { - sprintf(s, FMT33, (float)qu->Qstep / 60.0); + if (qual->Qualflag != NONE && time->Dur > 0) + { + sprintf(s, FMT33, (float)time->Qstep / 60.0); writeline(pr, s); - sprintf(s, FMT34, qu->Ctol * pr->Ucf[QUALITY], rep->Field[QUALITY].Units); + sprintf(s, FMT34, qual->Ctol * pr->Ucf[QUALITY], rpt->Field[QUALITY].Units); writeline(pr, s); } + sprintf(s, FMT36, hyd->SpGrav); writeline(pr, s); sprintf(s, FMT37a, hyd->Viscos / VISCOS); writeline(pr, s); - sprintf(s, FMT37b, qu->Diffus / DIFFUS); + sprintf(s, FMT37b, qual->Diffus / DIFFUS); writeline(pr, s); sprintf(s, FMT38, hyd->Dmult); writeline(pr, s); - sprintf(s, FMT39, time->Dur * pr->Ucf[TIME], rep->Field[TIME].Units); + sprintf(s, FMT39, time->Dur * pr->Ucf[TIME], rpt->Field[TIME].Units); writeline(pr, s); - if (rep->Rptflag) { + + if (rpt->Rptflag) + { sprintf(s, FMT40); writeline(pr, s); - if (rep->Nodeflag == 0) - writeline(pr, FMT41); - if (rep->Nodeflag == 1) - writeline(pr, FMT42); - if (rep->Nodeflag == 2) - writeline(pr, FMT43); + if (rpt->Nodeflag == 0) writeline(pr, FMT41); + if (rpt->Nodeflag == 1) writeline(pr, FMT42); + if (rpt->Nodeflag == 2) writeline(pr, FMT43); writelimits(pr, DEMAND, QUALITY); - if (rep->Linkflag == 0) - writeline(pr, FMT44); - if (rep->Linkflag == 1) - writeline(pr, FMT45); - if (rep->Linkflag == 2) - writeline(pr, FMT46); + if (rpt->Linkflag == 0) writeline(pr, FMT44); + if (rpt->Linkflag == 1) writeline(pr, FMT45); + if (rpt->Linkflag == 2) writeline(pr, FMT46); writelimits(pr, DIAM, HEADLOSS); } writeline(pr, " "); -} /* End of writesummary */ +} -void writehydstat(EN_Project *pr, int iter, double relerr) +void writehydstat(Project *pr, int iter, double relerr) /* **-------------------------------------------------------------- ** Input: iter = # iterations to find hydraulic solution @@ -299,79 +290,77 @@ void writehydstat(EN_Project *pr, int iter, double relerr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; + Times *time = &pr->times; - - double *NodeDemand = hyd->NodeDemand; + int i, n; + double *NodeDemand; + char s1[MAXLINE + 1]; + char atime[13]; + StatusType newstat; Stank *Tank = net->Tank; Slink *Link = net->Link; - - int i, n; - StatType newstat; - char s1[MAXLINE + 1]; - /*** Updated 6/24/02 ***/ - char atime[13]; - - /* Display system status */ - strcpy(atime, clocktime(rep->Atime, time->Htime)); - if (iter > 0) { - if (relerr <= hyd->Hacc) - sprintf(s1, FMT58, atime, iter); - else - sprintf(s1, FMT59, atime, iter, relerr); + // Display system status + strcpy(atime, clocktime(rpt->Atime, time->Htime)); + if (iter > 0) + { + if (relerr <= hyd->Hacc) sprintf(s1, FMT58, atime, iter); + else sprintf(s1, FMT59, atime, iter, relerr); writeline(pr, s1); } - /* - Display status changes for tanks. - D[n] is net inflow to tank at node n. - Old tank status is stored in OldStat[] - at indexes Nlinks+1 to Nlinks+Ntanks. - */ - for (i = 1; i <= net->Ntanks; i++) { + // Display status changes for tanks: + // D[n] is net inflow to tank at node n; + // old tank status is stored in OldStatus[] + // at indexes Nlinks+1 to Nlinks+Ntanks. + for (i = 1; i <= net->Ntanks; i++) + { n = net->Tank[i].Node; - if (ABS(NodeDemand[n]) < 0.001) - newstat = CLOSED; - else if (NodeDemand[n] > 0.0) - newstat = FILLING; - else if (NodeDemand[n] < 0.0) - newstat = EMPTYING; - else - newstat = hyd->OldStat[net->Nlinks + i]; - if (newstat != hyd->OldStat[net->Nlinks + i]) { + NodeDemand = hyd->NodeDemand; + if (ABS(NodeDemand[n]) < 0.001) newstat = CLOSED; + else if (NodeDemand[n] > 0.0) newstat = FILLING; + else if (NodeDemand[n] < 0.0) newstat = EMPTYING; + else newstat = hyd->OldStatus[net->Nlinks + i]; + if (newstat != hyd->OldStatus[net->Nlinks + i]) + { if (Tank[i].A > 0.0) + { snprintf(s1, MAXLINE, FMT50, atime, net->Node[n].ID, StatTxt[newstat], (hyd->NodeHead[n] - net->Node[n].El) * pr->Ucf[HEAD], - rep->Field[HEAD].Units); + rpt->Field[HEAD].Units); + } else + { snprintf(s1, MAXLINE, FMT51, atime, net->Node[n].ID, StatTxt[newstat]); + } writeline(pr, s1); - hyd->OldStat[net->Nlinks + i] = newstat; + hyd->OldStatus[net->Nlinks + i] = newstat; } } - /* Display status changes for links */ - for (i = 1; i <= net->Nlinks; i++) { - if (hyd->LinkStatus[i] != hyd->OldStat[i]) { + // Display status changes for links + for (i = 1; i <= net->Nlinks; i++) + { + if (hyd->LinkStatus[i] != hyd->OldStatus[i]) + { if (time->Htime == 0) - sprintf(s1, FMT52, atime, LinkTxt[(int)net->Link[i].Type], net->Link[i].ID, - StatTxt[(int)hyd->LinkStatus[i]]); - else - sprintf(s1, FMT53, atime, LinkTxt[Link[i].Type], net->Link[i].ID, - StatTxt[hyd->OldStat[i]], StatTxt[hyd->LinkStatus[i]]); + { + sprintf(s1, FMT52, atime, LinkTxt[(int)net->Link[i].Type], + net->Link[i].ID, StatTxt[(int)hyd->LinkStatus[i]]); + } + else sprintf(s1, FMT53, atime, LinkTxt[Link[i].Type], net->Link[i].ID, + StatTxt[hyd->OldStatus[i]], StatTxt[hyd->LinkStatus[i]]); writeline(pr, s1); - hyd->OldStat[i] = hyd->LinkStatus[i]; + hyd->OldStatus[i] = hyd->LinkStatus[i]; } } writeline(pr, " "); -} /* End of writehydstat */ +} - -void writemassbalance(EN_Project *pr) +void writemassbalance(Project *pr) /* **------------------------------------------------------------- ** Input: none @@ -382,10 +371,11 @@ void writemassbalance(EN_Project *pr) **------------------------------------------------------------- */ { + Quality *qual = &pr->quality; + char s1[MAXMSG+1]; char *units[] = {"", " (mg)", " (ug)", " (hrs)"}; int kunits = 0; - quality_t *qual = &pr->quality; if (qual->Qualflag == TRACE) kunits = 1; else if (qual->Qualflag == AGE) kunits = 3; @@ -399,24 +389,23 @@ void writemassbalance(EN_Project *pr) writeline(pr, s1); snprintf(s1, MAXMSG, "================================"); writeline(pr, s1); - snprintf(s1, MAXMSG, "Initial Mass: %12.5e", qual->massbalance.initial); + snprintf(s1, MAXMSG, "Initial Mass: %12.5e", qual->MassBalance.initial); writeline(pr, s1); - snprintf(s1, MAXMSG, "Mass Inflow: %12.5e", qual->massbalance.inflow); + snprintf(s1, MAXMSG, "Mass Inflow: %12.5e", qual->MassBalance.inflow); writeline(pr, s1); - snprintf(s1, MAXMSG, "Mass Outflow: %12.5e", qual->massbalance.outflow); + snprintf(s1, MAXMSG, "Mass Outflow: %12.5e", qual->MassBalance.outflow); writeline(pr, s1); - snprintf(s1, MAXMSG, "Mass Reacted: %12.5e", qual->massbalance.reacted); + snprintf(s1, MAXMSG, "Mass Reacted: %12.5e", qual->MassBalance.reacted); writeline(pr, s1); - snprintf(s1, MAXMSG, "Final Mass: %12.5e", qual->massbalance.final); + snprintf(s1, MAXMSG, "Final Mass: %12.5e", qual->MassBalance.final); writeline(pr, s1); - snprintf(s1, MAXMSG, "Mass Ratio: %-0.5f", qual->massbalance.ratio); + snprintf(s1, MAXMSG, "Mass Ratio: %-0.5f", qual->MassBalance.ratio); writeline(pr, s1); snprintf(s1, MAXMSG, "================================\n"); writeline(pr, s1); } - -void writeenergy(EN_Project *pr) +void writeenergy(Project *pr) /* **------------------------------------------------------------- ** Input: none @@ -425,44 +414,44 @@ void writeenergy(EN_Project *pr) **------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - Spump *pump; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; - int j; - double csum; - char s[MAXLINE + 1]; - if (net->Npumps == 0) - return; - writeline(pr, " "); - writeheader(pr,ENERHDR, 0); - csum = 0.0; - for (j = 1; j <= net->Npumps; j++) { - pump = &net->Pump[j]; - csum += pump->Energy[TOTAL_COST]; - if (rep->LineNum == (long)rep->PageSize) - writeheader(pr, ENERHDR, 1); - sprintf(s, "%-8s %6.2f %6.2f %9.2f %9.2f %9.2f %9.2f", - net->Link[pump->Link].ID, pump->Energy[PCNT_ONLINE], pump->Energy[PCNT_EFFIC], - pump->Energy[KWH_PER_FLOW], pump->Energy[TOTAL_KWH], pump->Energy[MAX_KW], - pump->Energy[TOTAL_COST]); + int j; + double csum; + char s[MAXLINE + 1]; + Spump *pump; + + if (net->Npumps == 0) return; + writeline(pr, " "); + writeheader(pr,ENERHDR, 0); + + csum = 0.0; + for (j = 1; j <= net->Npumps; j++) + { + pump = &net->Pump[j]; + csum += pump->Energy.TotalCost; + if (rpt->LineNum == (long)rpt->PageSize) writeheader(pr, ENERHDR, 1); + + sprintf(s, "%-8s %6.2f %6.2f %9.2f %9.2f %9.2f %9.2f", + net->Link[pump->Link].ID, pump->Energy.TimeOnLine, + pump->Energy.Efficiency, pump->Energy.KwHrsPerFlow, + pump->Energy.KwHrs, pump->Energy.MaxKwatts, + pump->Energy.TotalCost); + writeline(pr, s); + } + + fillstr(s, '-', 63); writeline(pr, s); - } - fillstr(s, '-', 63); - writeline(pr, s); + sprintf(s, FMT74, "", hyd->Emax * hyd->Dcost); + writeline(pr, s); + sprintf(s, FMT75, "", csum + hyd->Emax * hyd->Dcost); + writeline(pr, s); + writeline(pr, " "); +} - /*** Updated 6/24/02 ***/ - sprintf(s, FMT74, "", hyd->Emax * hyd->Dcost); - writeline(pr, s); - sprintf(s, FMT75, "", csum + hyd->Emax * hyd->Dcost); - /*** End of update ***/ - - writeline(pr, s); - writeline(pr, " "); -} /* End of writeenergy */ - -int writeresults(EN_Project *pr) +int writeresults(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -471,99 +460,87 @@ int writeresults(EN_Project *pr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - out_file_t *out = &pr->out_files; - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; - FILE *outFile = out->OutFile; + Network *net = &pr->network; + Outfile *out = &pr->outfile; + Report *rpt = &pr->report; + Times *time = &pr->times; - - Pfloat *x; /* Array of pointers to floats */ - int j, m, n, np, nnv, nlv; - int errcode = 0; + int j, m, n, + np, // Reporting period counter + nnv, // # node variables reported on + nlv; // # link variables reported on + int errcode = 0; + Pfloat *x; // Array of pointers to floats (i.e., a 2-D array) + FILE *outFile = out->OutFile; - /* - **----------------------------------------------------------- - ** NOTE: The OutFile contains results for 4 node variables - ** (demand, head, pressure, & quality) and 8 link - ** variables (flow, velocity, headloss, quality, - ** status, setting, reaction rate & friction factor) - ** at each reporting time. - **----------------------------------------------------------- - */ + //----------------------------------------------------------- + // NOTE: The OutFile contains results for 4 node variables + // (demand, head, pressure, & quality) and 8 link + // variables (flow, velocity, headloss, quality, + // status, setting, reaction rate & friction factor) + // at each reporting time. + //----------------------------------------------------------- - /* Return if no output file */ - if (outFile == NULL) - return (106); + // Return if no output file + if (outFile == NULL) return 106; - /* Return if no nodes or links selected for reporting */ - /* or if no node or link report variables enabled. */ - if (!rep->Nodeflag && !rep->Linkflag) { - return (errcode); - } - nnv = 0; - for (j = ELEV; j <= QUALITY; j++) { - nnv += rep->Field[j].Enabled; - } - nlv = 0; - for (j = LENGTH; j <= FRICTION; j++) { - nlv += rep->Field[j].Enabled; - } - if (nnv == 0 && nlv == 0) { - return (errcode); - } + // Return if no nodes or links selected for reporting + // or if no node or link report variables enabled + if (!rpt->Nodeflag && !rpt->Linkflag) return errcode; - /* Allocate memory for output variables. */ - /* m = larger of # node variables & # link variables */ - /* n = larger of # nodes & # links */ - m = MAX((QUALITY - DEMAND + 1), (FRICTION - FLOW + 1)); - n = MAX((net->Nnodes + 1), (net->Nlinks + 1)); - x = (Pfloat *)calloc(m, sizeof(Pfloat)); - ERRCODE(MEMCHECK(x)); - if (errcode) - return (errcode); - for (j = 0; j < m; j++) { - x[j] = (REAL4 *)calloc(n, sizeof(REAL4)); - ERRCODE(MEMCHECK(x[j])); - } - if (errcode) - return (errcode); + nnv = 0; + for (j = ELEV; j <= QUALITY; j++) nnv += rpt->Field[j].Enabled; + nlv = 0; + for (j = LENGTH; j <= FRICTION; j++) nlv += rpt->Field[j].Enabled; + if (nnv == 0 && nlv == 0) return errcode; - /* Re-position output file & initialize report time. */ - fseek(outFile, out->OutOffset2, SEEK_SET); - time->Htime = time->Rstart; - - /* For each reporting time: */ - for (np = 1; np <= rep->Nperiods; np++) { - - /* Read in node results & write node table. */ - /* (Remember to offset x[j] by 1 because array is zero-based). */ - for (j = DEMAND; j <= QUALITY; j++) { - fread((x[j - DEMAND]) + 1, sizeof(REAL4), net->Nnodes, outFile); + // Allocate memory for output variables: + // m = larger of # node variables & # link variables + // n = larger of # nodes & # links + m = MAX((QUALITY - DEMAND + 1), (FRICTION - FLOW + 1)); + n = MAX((net->Nnodes + 1), (net->Nlinks + 1)); + x = (Pfloat *)calloc(m, sizeof(Pfloat)); + ERRCODE(MEMCHECK(x)); + if (errcode) return errcode; + for (j = 0; j < m; j++) + { + x[j] = (REAL4 *)calloc(n, sizeof(REAL4)); + if (x[j] == NULL) errcode = 101; } - if (nnv > 0 && rep->Nodeflag > 0) { - writenodetable(pr,x); + if (!errcode) + { + // Re-position output file & initialize report time + fseek(outFile, out->OutOffset2, SEEK_SET); + time->Htime = time->Rstart; + + // For each reporting time: + for (np = 1; np <= rpt->Nperiods; np++) + { + // Read in node results & write node table + // (Remember to offset x[j] by 1 because array is zero-based) + for (j = DEMAND; j <= QUALITY; j++) + { + fread((x[j - DEMAND]) + 1, sizeof(REAL4), net->Nnodes, outFile); + } + if (nnv > 0 && rpt->Nodeflag > 0) writenodetable(pr, x); + + // Read in link results & write link table + for (j = FLOW; j <= FRICTION; j++) + { + fread((x[j - FLOW]) + 1, sizeof(REAL4), net->Nlinks, outFile); + } + if (nlv > 0 && rpt->Linkflag > 0) writelinktable(pr, x); + time->Htime += time->Rstep; + } } - /* Read in link results & write link table. */ - for (j = FLOW; j <= FRICTION; j++) { - fread((x[j - FLOW]) + 1, sizeof(REAL4), net->Nlinks, outFile); - } - if (nlv > 0 && rep->Linkflag > 0) { - writelinktable(pr,x); - } - time->Htime += time->Rstep; - } + // Free allocated memory + for (j = 0; j < m; j++) free(x[j]); + free(x); + return errcode; +} - /* Free allocated memory */ - for (j = 0; j < m; j++) { - free(x[j]); - } - free(x); - return (errcode); -} /* End of writereport */ - -void writenodetable(EN_Project *pr, Pfloat *x) +void writenodetable(Project *pr, Pfloat *x) /* **--------------------------------------------------------------- ** Input: x = pointer to node results for current time @@ -572,61 +549,59 @@ void writenodetable(EN_Project *pr, Pfloat *x) **--------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - report_options_t *rep = &pr->report; + Network *net = &pr->network; + Report *rpt = &pr->report; - int i, j; - char s[MAXLINE + 1], s1[16]; - double y[MAXVAR]; + int i, j; + char s[MAXLINE + 1], s1[16]; + double y[MAXVAR]; + Snode *node; - /* Write table header */ - writeheader(pr, NODEHDR, 0); + // Write table header + writeheader(pr, NODEHDR, 0); - /* For each node: */ - for (i = 1; i <= net->Nnodes; i++) { - Snode *node = &net->Node[i]; - /* Place results for each node variable in y */ - y[ELEV] = node->El * pr->Ucf[ELEV]; - for (j = DEMAND; j <= QUALITY; j++) - y[j] = *((x[j - DEMAND]) + i); + // For each node: + for (i = 1; i <= net->Nnodes; i++) + { + // Place node's results for each variable in y + node = &net->Node[i]; + y[ELEV] = node->El * pr->Ucf[ELEV]; + for (j = DEMAND; j <= QUALITY; j++) y[j] = *((x[j - DEMAND]) + i); - /* Check if node gets reported on */ - if ((rep->Nodeflag == 1 || node->Rpt) && checklimits(rep, y, ELEV, QUALITY)) { + // Check if node gets reported on + if ((rpt->Nodeflag == 1 || node->Rpt) && + checklimits(rpt, y, ELEV, QUALITY)) + { + // Check if new page needed + if (rpt->LineNum == (long)rpt->PageSize) writeheader(pr, NODEHDR, 1); - /* Check if new page needed */ - if (rep->LineNum == (long)rep->PageSize) - writeheader(pr, NODEHDR, 1); + // Add node ID and each reported field to string s + sprintf(s, "%-15s", node->ID); + for (j = ELEV; j <= QUALITY; j++) + { + if (rpt->Field[j].Enabled == TRUE) + { + if (fabs(y[j]) > 1.e6) sprintf(s1, "%10.2e", y[j]); + else sprintf(s1, "%10.*f", rpt->Field[j].Precision, y[j]); + strcat(s, s1); + } + } - /* Add node ID and each reported field to string s */ - sprintf(s, "%-15s", node->ID); - for (j = ELEV; j <= QUALITY; j++) { - if (rep->Field[j].Enabled == TRUE) { + // Note if node is a reservoir/tank + if (i > net->Njuncs) + { + strcat(s, " "); + strcat(s, NodeTxt[getnodetype(net, i)]); + } - /*** Updated 6/24/02 ***/ - if (fabs(y[j]) > 1.e6) - sprintf(s1, "%10.2e", y[j]); - else - sprintf(s1, "%10.*f", rep->Field[j].Precision, y[j]); - /*** End of update ***/ - - strcat(s, s1); + // Write results for node to report file + writeline(pr, s); } - } - - /* Note if node is a reservoir/tank */ - if (i > net->Njuncs) { - strcat(s, " "); - strcat(s, NodeTxt[getnodetype(net,i)]); - } - - /* Write results for node */ - writeline(pr, s); } - } - writeline(pr, " "); + writeline(pr, " "); } -void writelinktable(EN_Project *pr, Pfloat *x) +void writelinktable(Project *pr, Pfloat *x) /* **--------------------------------------------------------------- ** Input: x = pointer to link results for current time @@ -635,76 +610,69 @@ void writelinktable(EN_Project *pr, Pfloat *x) **--------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - report_options_t *rep = &pr->report; - Slink *Link = net->Link; - double *Ucf = pr->Ucf; + Network *net = &pr->network; + Report *rpt = &pr->report; - - int i, j, k; - char s[MAXLINE + 1], s1[16]; - double y[MAXVAR]; + int i, j, k; + char s[MAXLINE + 1], s1[16]; + double y[MAXVAR]; + double *Ucf = pr->Ucf; + Slink *Link = net->Link; + + // Write table header + writeheader(pr, LINKHDR, 0); - /* Write table header */ - writeheader(pr,LINKHDR, 0); + // For each link: + for (i = 1; i <= net->Nlinks; i++) + { + // Place results for each link variable in y + y[LENGTH] = Link[i].Len * Ucf[LENGTH]; + y[DIAM] = Link[i].Diam * Ucf[DIAM]; + for (j = FLOW; j <= FRICTION; j++) y[j] = *((x[j - FLOW]) + i); - /* For each link: */ - for (i = 1; i <= net->Nlinks; i++) { + // Check if link gets reported on + if ((rpt->Linkflag == 1 || Link[i].Rpt) && checklimits(rpt, y, DIAM, FRICTION)) + { + // Check if new page needed + if (rpt->LineNum == (long)rpt->PageSize) writeheader(pr, LINKHDR, 1); - /* Place results for each link variable in y */ - y[LENGTH] = Link[i].Len * Ucf[LENGTH]; - y[DIAM] = Link[i].Diam * Ucf[DIAM]; - for (j = FLOW; j <= FRICTION; j++) - y[j] = *((x[j - FLOW]) + i); + // Add link ID and each reported field to string s + sprintf(s, "%-15s", Link[i].ID); + for (j = LENGTH; j <= FRICTION; j++) + { + if (rpt->Field[j].Enabled == TRUE) + { + if (j == STATUS) + { + if (y[j] <= CLOSED) k = CLOSED; + else if (y[j] == ACTIVE) k = ACTIVE; + else k = OPEN; + sprintf(s1, "%10s", StatTxt[k]); + } + else + { + if (fabs(y[j]) > 1.e6) sprintf(s1, "%10.2e", y[j]); + else sprintf(s1, "%10.*f", rpt->Field[j].Precision, y[j]); + } + strcat(s, s1); + } + } - /* Check if link gets reported on */ - if ((rep->Linkflag == 1 || Link[i].Rpt) && checklimits(rep, y, DIAM, FRICTION)) { + // Note if link is a pump or valve + if ((j = Link[i].Type) > PIPE) + { + strcat(s, " "); + strcat(s, LinkTxt[j]); + } - /* Check if new page needed */ - if (rep->LineNum == (long)rep->PageSize) - writeheader(pr,LINKHDR, 1); - - /* Add link ID and each reported field to string s */ - sprintf(s, "%-15s", Link[i].ID); - for (j = LENGTH; j <= FRICTION; j++) { - if (rep->Field[j].Enabled == TRUE) { - if (j == STATUS) { - if (y[j] <= CLOSED) - k = CLOSED; - else if (y[j] == ACTIVE) - k = ACTIVE; - else - k = OPEN; - sprintf(s1, "%10s", StatTxt[k]); - } - - /*** Updated 6/24/02 ***/ - else { - if (fabs(y[j]) > 1.e6) - sprintf(s1, "%10.2e", y[j]); - else - sprintf(s1, "%10.*f", rep->Field[j].Precision, y[j]); - } - /*** End of update ***/ - - strcat(s, s1); + // Write results for link + writeline(pr, s); } - } - - /* Note if link is a pump or valve */ - if ((j = Link[i].Type) > PIPE) { - strcat(s, " "); - strcat(s, LinkTxt[j]); - } - - /* Write results for link */ - writeline(pr, s); } - } - writeline(pr, " "); + writeline(pr, " "); } -void writeheader(EN_Project *pr, int type, int contin) +void writeheader(Project *pr, int type, int contin) /* **-------------------------------------------------------------- ** Input: type = table type @@ -714,124 +682,131 @@ void writeheader(EN_Project *pr, int type, int contin) **-------------------------------------------------------------- */ { - report_options_t *rep = &pr->report; - quality_t *qu = &pr->quality; - parser_data_t *par = &pr->parser; - time_options_t *time = &pr->time_options; + Report *rpt = &pr->report; + Quality *qual = &pr->quality; + Parser *parser = &pr->parser; + Times *time = &pr->times; - char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1], s3[MAXLINE + 1]; - int i, n; + char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1], s3[MAXLINE + 1]; + int i, n; - /* Move to next page if < 11 lines remain on current page. */ - if (rep->Rptflag && rep->LineNum + 11 > (long)rep->PageSize) { - while (rep->LineNum < (long)rep->PageSize) - writeline(pr, " "); - } - writeline(pr, " "); - - /* Hydraulic Status Table */ - if (type == STATHDR) { - sprintf(s, FMT49); - if (contin) - strcat(s, t_CONTINUED); - writeline(pr, s); - fillstr(s, '-', 70); - writeline(pr, s); - } - - /* Energy Usage Table */ - if (type == ENERHDR) { - if (par->Unitsflag == SI) - strcpy(s1, t_perM3); - else - strcpy(s1, t_perMGAL); - sprintf(s, FMT71); - if (contin) - strcat(s, t_CONTINUED); - writeline(pr, s); - fillstr(s, '-', 63); - writeline(pr, s); - sprintf(s, FMT72); - writeline(pr, s); - sprintf(s, FMT73, s1); - writeline(pr, s); - fillstr(s, '-', 63); - writeline(pr, s); - } - - /* Node Results Table */ - if (type == NODEHDR) { - if (rep->Tstatflag == RANGE) - sprintf(s, FMT76, t_DIFFER); - else if (rep->Tstatflag != SERIES) - sprintf(s, FMT76, TstatTxt[rep->Tstatflag]); - else if (time->Dur == 0) - sprintf(s, FMT77); - else - sprintf(s, FMT78, clocktime(rep->Atime, time->Htime)); - if (contin) - strcat(s, t_CONTINUED); - writeline(pr, s); - n = 15; - sprintf(s2, "%15s", ""); - strcpy(s, t_NODEID); - sprintf(s3, "%-15s", s); - for (i = ELEV; i < QUALITY; i++) - if (rep->Field[i].Enabled == TRUE) { - n += 10; - sprintf(s, "%10s", rep->Field[i].Name); - strcat(s2, s); - sprintf(s, "%10s", rep->Field[i].Units); - strcat(s3, s); - } - if (rep->Field[QUALITY].Enabled == TRUE) { - n += 10; - sprintf(s, "%10s", qu->ChemName); - strcat(s2, s); - sprintf(s, "%10s", qu->ChemUnits); - strcat(s3, s); + // Move to next page if < 11 lines remain on current page + if (rpt->Rptflag && rpt->LineNum + 11 > (long)rpt->PageSize) + { + while (rpt->LineNum < (long)rpt->PageSize) writeline(pr, " "); } - fillstr(s1, '-', n); - writeline(pr, s1); - writeline(pr, s2); - writeline(pr, s3); - writeline(pr, s1); - } + writeline(pr, " "); - /* Link Results Table */ - if (type == LINKHDR) { - if (rep->Tstatflag == RANGE) - sprintf(s, FMT79, t_DIFFER); - else if (rep->Tstatflag != SERIES) - sprintf(s, FMT79, TstatTxt[rep->Tstatflag]); - else if (time->Dur == 0) - sprintf(s, FMT80); - else - sprintf(s, FMT81, clocktime(rep->Atime, time->Htime)); - if (contin) - strcat(s, t_CONTINUED); - writeline(pr, s); - n = 15; - sprintf(s2, "%15s", ""); - strcpy(s, t_LINKID); - sprintf(s3, "%-15s", s); - for (i = LENGTH; i <= FRICTION; i++) - if (rep->Field[i].Enabled == TRUE) { - n += 10; - sprintf(s, "%10s", rep->Field[i].Name); - strcat(s2, s); - sprintf(s, "%10s", rep->Field[i].Units); - strcat(s3, s); - } - fillstr(s1, '-', n); - writeline(pr, s1); - writeline(pr, s2); - writeline(pr, s3); - writeline(pr, s1); - } -} /* End of writeheader */ + // Hydraulic Status Table + if (type == STATHDR) + { + sprintf(s, FMT49); + if (contin) strcat(s, t_CONTINUED); + writeline(pr, s); + fillstr(s, '-', 70); + writeline(pr, s); + } -void writeline(EN_Project *pr, char *s) + // Energy Usage Table + if (type == ENERHDR) + { + if (parser->Unitsflag == SI) strcpy(s1, t_perM3); + else strcpy(s1, t_perMGAL); + sprintf(s, FMT71); + if (contin) strcat(s, t_CONTINUED); + writeline(pr, s); + fillstr(s, '-', 63); + writeline(pr, s); + sprintf(s, FMT72); + writeline(pr, s); + sprintf(s, FMT73, s1); + writeline(pr, s); + fillstr(s, '-', 63); + writeline(pr, s); + } + + // Node Results Table + if (type == NODEHDR) + { + if (rpt->Tstatflag == RANGE) sprintf(s, FMT76, t_DIFFER); + else if (rpt->Tstatflag != SERIES) + { + sprintf(s, FMT76, TstatTxt[rpt->Tstatflag]); + } + else if (time->Dur == 0) sprintf(s, FMT77); + else sprintf(s, FMT78, clocktime(rpt->Atime, time->Htime)); + if (contin) strcat(s, t_CONTINUED); + writeline(pr, s); + + n = 15; + sprintf(s2, "%15s", ""); + strcpy(s, t_NODEID); + sprintf(s3, "%-15s", s); + + for (i = ELEV; i < QUALITY; i++) + { + if (rpt->Field[i].Enabled == TRUE) + { + n += 10; + sprintf(s, "%10s", rpt->Field[i].Name); + strcat(s2, s); + sprintf(s, "%10s", rpt->Field[i].Units); + strcat(s3, s); + } + } + + if (rpt->Field[QUALITY].Enabled == TRUE) + { + n += 10; + sprintf(s, "%10s", qual->ChemName); + strcat(s2, s); + sprintf(s, "%10s", qual->ChemUnits); + strcat(s3, s); + } + fillstr(s1, '-', n); + writeline(pr, s1); + writeline(pr, s2); + writeline(pr, s3); + writeline(pr, s1); + } + + // Link Results Table + if (type == LINKHDR) + { + if (rpt->Tstatflag == RANGE) sprintf(s, FMT79, t_DIFFER); + else if (rpt->Tstatflag != SERIES) + { + sprintf(s, FMT79, TstatTxt[rpt->Tstatflag]); + } + else if (time->Dur == 0) sprintf(s, FMT80); + else sprintf(s, FMT81, clocktime(rpt->Atime, time->Htime)); + if (contin) strcat(s, t_CONTINUED); + writeline(pr, s); + + n = 15; + sprintf(s2, "%15s", ""); + strcpy(s, t_LINKID); + sprintf(s3, "%-15s", s); + for (i = LENGTH; i <= FRICTION; i++) + { + if (rpt->Field[i].Enabled == TRUE) + { + n += 10; + sprintf(s, "%10s", rpt->Field[i].Name); + strcat(s2, s); + sprintf(s, "%10s", rpt->Field[i].Units); + strcat(s3, s); + } + } + fillstr(s1, '-', n); + writeline(pr, s1); + writeline(pr, s2); + writeline(pr, s3); + writeline(pr, s1); + } +} + +void writeline(Project *pr, char *s) /* **-------------------------------------------------------------- ** Input: *s = text string @@ -840,27 +815,26 @@ void writeline(EN_Project *pr, char *s) **-------------------------------------------------------------- */ { - report_options_t *rpt = &pr->report; + Report *rpt = &pr->report; - if (rpt->RptFile == NULL) { - return; - } - if (rpt->Rptflag) { - if (rpt->LineNum == (long)rpt->PageSize) { - rpt->PageNum++; - if (fprintf(rpt->RptFile, FMT82, (int)rpt->PageNum, pr->Title[0]) == EOF) { - rpt->Fprinterr = TRUE; - } - rpt->LineNum = 3; + if (rpt->RptFile == NULL) return; + if (rpt->Rptflag) + { + if (rpt->LineNum == (long)rpt->PageSize) + { + rpt->PageNum++; + if (fprintf(rpt->RptFile, FMT82, (int)rpt->PageNum, pr->Title[0]) < 0) + { + rpt->Fprinterr = TRUE; + } + rpt->LineNum = 3; + } } - } - if (fprintf(rpt->RptFile, "\n %s", s) == EOF) { - rpt->Fprinterr = TRUE; - } - rpt->LineNum++; -} /* End of writeline */ + if (fprintf(rpt->RptFile, "\n %s", s) < 0) rpt->Fprinterr = TRUE; + rpt->LineNum++; +} -void writerelerr(EN_Project *pr, int iter, double relerr) +void writerelerr(Project *pr, int iter, double relerr) /* **----------------------------------------------------------------- ** Input: iter = current iteration of hydraulic solution @@ -870,19 +844,22 @@ void writerelerr(EN_Project *pr, int iter, double relerr) **----------------------------------------------------------------- */ { - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; + Report *rpt = &pr->report; + Times *time = &pr->times; - if (iter == 0) { - sprintf(pr->Msg, FMT64, clocktime(rep->Atime, time->Htime)); - writeline(pr, pr->Msg); - } else { - sprintf(pr->Msg, FMT65, iter, relerr); - writeline(pr, pr->Msg); - } -} /* End of writerelerr */ + if (iter == 0) + { + sprintf(pr->Msg, FMT64, clocktime(rpt->Atime, time->Htime)); + writeline(pr, pr->Msg); + } + else + { + sprintf(pr->Msg, FMT65, iter, relerr); + writeline(pr, pr->Msg); + } +} -void writestatchange(EN_Project *pr, int k, char s1, char s2) +void writestatchange(Project *pr, int k, char s1, char s2) /* **-------------------------------------------------------------- ** Input: k = link index @@ -893,58 +870,53 @@ void writestatchange(EN_Project *pr, int k, char s1, char s2) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - Slink *Link = net->Link; - double *Ucf = pr->Ucf; - double *LinkSetting = hyd->LinkSetting; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + + int j1, j2; + double setting; + double *Ucf = pr->Ucf; + double *LinkSetting = hyd->LinkSetting; + Slink *Link = net->Link; - int j1, j2; - double setting; - - /* We have a pump/valve setting change instead of a status change */ - if (s1 == s2) { - - /*** Updated 10/25/00 ***/ - setting = LinkSetting[k]; // Link[k].Kc; - - switch (Link[k].Type) { - case PRV: - case PSV: - case PBV: - setting *= Ucf[PRESSURE]; - break; - case FCV: - setting *= Ucf[FLOW]; - default: - break; + // We have a pump/valve setting change instead of a status change + if (s1 == s2) + { + setting = LinkSetting[k]; + switch (Link[k].Type) + { + case PRV: + case PSV: + case PBV: + setting *= Ucf[PRESSURE]; + break; + case FCV: + setting *= Ucf[FLOW]; + break; + default: + break; + } + sprintf(pr->Msg, FMT56, LinkTxt[Link[k].Type], Link[k].ID, setting); + writeline(pr, pr->Msg); + return; } - sprintf(pr->Msg, FMT56, LinkTxt[Link[k].Type], Link[k].ID, setting); - writeline(pr, pr->Msg); - return; - } - /* We have a status change. Write the old & new status types. */ - if (s1 == ACTIVE) - j1 = ACTIVE; - else if (s1 <= CLOSED) - j1 = CLOSED; - else - j1 = OPEN; - if (s2 == ACTIVE) - j2 = ACTIVE; - else if (s2 <= CLOSED) - j2 = CLOSED; - else - j2 = OPEN; - if (j1 != j2) { - sprintf(pr->Msg, FMT57, LinkTxt[Link[k].Type], Link[k].ID, StatTxt[j1], - StatTxt[j2]); - writeline(pr, pr->Msg); - } -} /* End of writestatchange */ + // We have a status change - write the old & new status types + if (s1 == ACTIVE) j1 = ACTIVE; + else if (s1 <= CLOSED) j1 = CLOSED; + else j1 = OPEN; + if (s2 == ACTIVE) j2 = ACTIVE; + else if (s2 <= CLOSED) j2 = CLOSED; + else j2 = OPEN; + if (j1 != j2) + { + sprintf(pr->Msg, FMT57, LinkTxt[Link[k].Type], Link[k].ID, StatTxt[j1], + StatTxt[j2]); + writeline(pr, pr->Msg); + } +} -void writecontrolaction(EN_Project *pr, int k, int i) +void writecontrolaction(Project *pr, int k, int i) /* ---------------------------------------------------------------- ** Input: k = link index @@ -954,32 +926,37 @@ void writecontrolaction(EN_Project *pr, int k, int i) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; - Snode *Node = net->Node; - Slink *Link = net->Link; - Scontrol *Control = net->Control; + Network *net = &pr->network; + Report *rpt = &pr->report; + Times *time = &pr->times; + + int n; + Snode *Node = net->Node; + Slink *Link = net->Link; + Scontrol *Control = net->Control; - int n; - switch (Control[i].Type) { - case LOWLEVEL: - case HILEVEL: - n = Control[i].Node; - sprintf(pr->Msg, FMT54, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type], - Link[k].ID, NodeTxt[getnodetype(net,n)], Node[n].ID); - break; - case TIMER: - case TIMEOFDAY: - sprintf(pr->Msg, FMT55, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type], Link[k].ID); - break; - default: - return; - } - writeline(pr, pr->Msg); + switch (Control[i].Type) + { + case LOWLEVEL: + case HILEVEL: + n = Control[i].Node; + sprintf(pr->Msg, FMT54, clocktime(rpt->Atime, time->Htime), + LinkTxt[Link[k].Type], Link[k].ID, + NodeTxt[getnodetype(net, n)], Node[n].ID); + break; + + case TIMER: + case TIMEOFDAY: + sprintf(pr->Msg, FMT55, clocktime(rpt->Atime, time->Htime), + LinkTxt[Link[k].Type], Link[k].ID); + break; + default: + return; + } + writeline(pr, pr->Msg); } -void writeruleaction(EN_Project *pr, int k, char *ruleID) +void writeruleaction(Project *pr, int k, char *ruleID) /* **-------------------------------------------------------------- ** Input: k = link index @@ -989,17 +966,18 @@ void writeruleaction(EN_Project *pr, int k, char *ruleID) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; - Slink *Link = net->Link; + Network *net = &pr->network; + Report *rpt = &pr->report; + Times *time = &pr->times; - sprintf(pr->Msg, FMT63, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type], - Link[k].ID, ruleID); - writeline(pr, pr->Msg); + Slink *Link = net->Link; + + sprintf(pr->Msg, FMT63, clocktime(rpt->Atime, time->Htime), + LinkTxt[Link[k].Type], Link[k].ID, ruleID); + writeline(pr, pr->Msg); } -int writehydwarn(EN_Project *pr, int iter, double relerr) +int writehydwarn(Project *pr, int iter, double relerr) /* **-------------------------------------------------------------- ** Input: iter = # iterations to find hydraulic solution @@ -1016,99 +994,96 @@ int writehydwarn(EN_Project *pr, int iter, double relerr) **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; + Times *time = &pr->times; + + int i, j; + char flag = 0; + char s; + Snode *Node = net->Node; + Slink *Link = net->Link; + Spump *Pump = net->Pump; + Svalve *Valve = net->Valve; + const int Njuncs = net->Njuncs; + double *NodeDemand = hyd->NodeDemand; + double *LinkFlow = hyd->LinkFlow; + double *LinkSetting = hyd->LinkSetting; - Snode *Node = net->Node; - Slink *Link = net->Link; - Spump *Pump = net->Pump; - Svalve *Valve = net->Valve; - - const int Njuncs = net->Njuncs; - double *NodeDemand = hyd->NodeDemand; - double *LinkFlows = hyd->LinkFlows; - double *LinkSetting = hyd->LinkSetting; - - int i, j; - char flag = 0; - char s; - - /* Check if system unstable */ - if (iter > hyd->MaxIter && relerr <= hyd->Hacc) { - sprintf(pr->Msg, WARN02, clocktime(rep->Atime, time->Htime)); - if (rep->Messageflag) - writeline(pr, pr->Msg); - flag = 2; - } - - /* Check for negative pressures */ - for (i = 1; i <= Njuncs; i++) { - Snode *node = &Node[i]; - if (hyd->NodeHead[i] < node->El && NodeDemand[i] > 0.0) { - sprintf(pr->Msg, WARN06, clocktime(rep->Atime, time->Htime)); - if (rep->Messageflag) { - writeline(pr, pr->Msg); - } - flag = 6; - break; - } - } - - /* Check for abnormal valve condition */ - for (i = 1; i <= net->Nvalves; i++) { - j = Valve[i].Link; - if (hyd->LinkStatus[j] >= XFCV) { - sprintf(pr->Msg, WARN05, LinkTxt[Link[j].Type], Link[j].ID, - StatTxt[hyd->LinkStatus[j]], clocktime(rep->Atime, time->Htime)); - if (rep->Messageflag) - writeline(pr, pr->Msg); - flag = 5; - } - } - - /* Check for abnormal pump condition */ - for (i = 1; i <= net->Npumps; i++) { - j = Pump[i].Link; - s = hyd->LinkStatus[j]; - if (hyd->LinkStatus[j] >= OPEN) - { - if (LinkFlows[j] > LinkSetting[j] * Pump[i].Qmax) - s = XFLOW; - if (LinkFlows[j] < 0.0) - s = XHEAD; - } - if (s == XHEAD || s == XFLOW) + // Check if system unstable + if (iter > hyd->MaxIter && relerr <= hyd->Hacc) { - sprintf(pr->Msg, WARN04, Link[j].ID, StatTxt[s], - clocktime(rep->Atime, time->Htime)); - if (rep->Messageflag) - writeline(pr, pr->Msg); - flag = 4; + sprintf(pr->Msg, WARN02, clocktime(rpt->Atime, time->Htime)); + if (rpt->Messageflag) writeline(pr, pr->Msg); + flag = 2; } - } - /* Check if system is unbalanced */ - if (iter > hyd->MaxIter && relerr > hyd->Hacc) { - sprintf(pr->Msg, WARN01, clocktime(rep->Atime, time->Htime)); - if (hyd->ExtraIter == -1) - strcat(pr->Msg, t_HALTED); - if (rep->Messageflag) - writeline(pr, pr->Msg); - flag = 1; - } + // Check for negative pressures + for (i = 1; i <= Njuncs; i++) + { + Snode *node = &Node[i]; + if (hyd->NodeHead[i] < node->El && NodeDemand[i] > 0.0) + { + sprintf(pr->Msg, WARN06, clocktime(rpt->Atime, time->Htime)); + if (rpt->Messageflag) writeline(pr, pr->Msg); + flag = 6; + break; + } + } - /* Check for disconnected network */ - /* & update global warning flag */ - if (flag > 0) { - disconnected(pr); - pr->Warnflag = flag; - } - return (flag); -} /* End of writehydwarn */ + // Check for abnormal valve condition + for (i = 1; i <= net->Nvalves; i++) + { + j = Valve[i].Link; + if (hyd->LinkStatus[j] >= XFCV) + { + sprintf(pr->Msg, WARN05, LinkTxt[Link[j].Type], Link[j].ID, + StatTxt[hyd->LinkStatus[j]], + clocktime(rpt->Atime, time->Htime)); + if (rpt->Messageflag) writeline(pr, pr->Msg); + flag = 5; + } + } -void writehyderr(EN_Project *pr, int errnode) + // Check for abnormal pump condition + for (i = 1; i <= net->Npumps; i++) + { + j = Pump[i].Link; + s = hyd->LinkStatus[j]; + if (hyd->LinkStatus[j] >= OPEN) + { + if (LinkFlow[j] > LinkSetting[j] * Pump[i].Qmax) s = XFLOW; + if (LinkFlow[j] < 0.0) s = XHEAD; + } + if (s == XHEAD || s == XFLOW) + { + sprintf(pr->Msg, WARN04, Link[j].ID, StatTxt[s], + clocktime(rpt->Atime, time->Htime)); + if (rpt->Messageflag) writeline(pr, pr->Msg); + flag = 4; + } + } + + // Check if system is unbalanced + if (iter > hyd->MaxIter && relerr > hyd->Hacc) + { + sprintf(pr->Msg, WARN01, clocktime(rpt->Atime, time->Htime)); + if (hyd->ExtraIter == -1) strcat(pr->Msg, t_HALTED); + if (rpt->Messageflag) writeline(pr, pr->Msg); + flag = 1; + } + + // Check for disconnected network & update project's warning flag + if (flag > 0) + { + disconnected(pr); + pr->Warnflag = flag; + } + return flag; +} + +void writehyderr(Project *pr, int errnode) /* **----------------------------------------------------------- ** Input: none @@ -1118,19 +1093,20 @@ void writehyderr(EN_Project *pr, int errnode) **----------------------------------------------------------- */ { - EN_Network *net = &pr->network; - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; - Snode *Node = net->Node; - - sprintf(pr->Msg, FMT62, clocktime(rep->Atime, time->Htime), Node[errnode].ID); - if (rep->Messageflag) - writeline(pr, pr->Msg); - writehydstat(pr, 0, 0); - disconnected(pr); -} /* End of writehyderr */ + Network *net = &pr->network; + Report *rpt = &pr->report; + Times *time = &pr->times; -int disconnected(EN_Project *pr) + Snode *Node = net->Node; + + sprintf(pr->Msg, FMT62, clocktime(rpt->Atime, time->Htime), + Node[errnode].ID); + if (rpt->Messageflag) writeline(pr, pr->Msg); + writehydstat(pr, 0, 0); + disconnected(pr); +} + +int disconnected(Project *pr) /* **------------------------------------------------------------------- ** Input: None @@ -1140,76 +1116,93 @@ int disconnected(EN_Project *pr) **------------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - time_options_t *time = &pr->time_options; - - int i, j; - int count, mcount; - int errcode = 0; - int *nodelist; - char *marked; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; + Times *time = &pr->times; - /* Allocate memory for node list & marked list */ - nodelist = (int *)calloc(net->Nnodes + 1, sizeof(int)); - marked = (char *)calloc(net->Nnodes + 1, sizeof(char)); - ERRCODE(MEMCHECK(nodelist)); - ERRCODE(MEMCHECK(marked)); - if (errcode) - return (0); + int i, j; + int count, mcount; + int errcode = 0; + int *nodelist; + char *marked; + Snode *node; - /* Place tanks on node list and marked list */ - for (i = 1; i <= net->Ntanks; i++) { - j = net->Njuncs + i; - nodelist[i] = j; - marked[j] = 1; - } + // Allocate memory for node list & marked list + nodelist = (int *)calloc(net->Nnodes + 1, sizeof(int)); + marked = (char *)calloc(net->Nnodes + 1, sizeof(char)); + ERRCODE(MEMCHECK(nodelist)); + ERRCODE(MEMCHECK(marked)); - /* Place junctions with negative demands on the lists */ - mcount = net->Ntanks; - for (i = 1; i <= net->Njuncs; i++) { - if (hyd->NodeDemand[i] < 0.0) { - mcount++; - nodelist[mcount] = i; - marked[i] = 1; + // If allocation fails return with 0 nodes disconnected + if (errcode) + { + free(nodelist); + free(marked); + return (0); } - } - /* Mark all nodes that can be connected to tanks */ - /* and count number of nodes remaining unmarked. */ - marknodes(pr, mcount, nodelist, marked); - j = 0; - count = 0; - for (i = 1; i <= net->Njuncs; i++) { - Snode *node = &net->Node[i]; - if (!marked[i] && hyd->NodeDemand[i] != 0.0) { - count++; - if (count <= MAXCOUNT && rep->Messageflag) { - sprintf(pr->Msg, WARN03a, node->ID, clocktime(rep->Atime, time->Htime)); - writeline(pr, pr->Msg); - } - j = i; /* Last unmarked node */ + // Place tanks on node list and marked list + for (i = 1; i <= net->Ntanks; i++) + { + j = net->Njuncs + i; + nodelist[i] = j; + marked[j] = 1; } - } - /* Report number of unmarked nodes and find closed link */ - /* on path from node j back to a tank. */ - if (count > 0 && rep->Messageflag) { - if (count > MAXCOUNT) { - sprintf(pr->Msg, WARN03b, count - MAXCOUNT, clocktime(rep->Atime, time->Htime)); - writeline(pr, pr->Msg); + // Place junctions with negative demands on the lists + mcount = net->Ntanks; + for (i = 1; i <= net->Njuncs; i++) + { + if (hyd->NodeDemand[i] < 0.0) + { + mcount++; + nodelist[mcount] = i; + marked[i] = 1; + } } - getclosedlink(pr, j, marked); - } - /* Free allocated memory */ - free(nodelist); - free(marked); - return (count); -} /* End of disconnected() */ + // Mark all nodes that can be connected to tanks + // and count number of nodes remaining unmarked + marknodes(pr, mcount, nodelist, marked); + j = 0; + count = 0; + for (i = 1; i <= net->Njuncs; i++) + { + node = &net->Node[i]; + if (!marked[i] && hyd->NodeDemand[i] != 0.0) + { + count++; + if (count <= MAXCOUNT && rpt->Messageflag) + { + sprintf(pr->Msg, WARN03a, node->ID, + clocktime(rpt->Atime, time->Htime)); + writeline(pr, pr->Msg); + } + j = i; // Last unmarked node + } + } -void marknodes(EN_Project *pr, int m, int *nodelist, char *marked) + // Report number of unmarked nodes and find closed link + // on path from node j back to a tank + if (count > 0 && rpt->Messageflag) + { + if (count > MAXCOUNT) + { + sprintf(pr->Msg, WARN03b, count - MAXCOUNT, + clocktime(rpt->Atime, time->Htime)); + writeline(pr, pr->Msg); + } + getclosedlink(pr, j, marked); + } + + // Free allocated memory + free(nodelist); + free(marked); + return count; +} + +void marknodes(Project *pr, int m, int *nodelist, char *marked) /* **---------------------------------------------------------------- ** Input: m = number of source nodes @@ -1220,51 +1213,50 @@ void marknodes(EN_Project *pr, int m, int *nodelist, char *marked) **---------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + + int i, j, k, n; + Padjlist alink; - int i, j, k, n; - Padjlist alink; + // Scan each successive entry of node list + n = 1; + while (n <= m) + { + // Scan all nodes connected to current node + i = nodelist[n]; + for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) + { + // Get indexes of connecting link and node + k = alink->link; + j = alink->node; + if (marked[j]) continue; - /* Scan each successive entry of node list */ - n = 1; - while (n <= m) { + // Check if valve connection is in correct direction + switch (net->Link[k].Type) + { + case CVPIPE: + case PRV: + case PSV: + if (j == net->Link[k].N1) continue; + break; + default: + break; + } - /* Scan all nodes connected to current node */ - i = nodelist[n]; - for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) { - - /* Get indexes of connecting link and node */ - k = alink->link; - j = alink->node; - if (marked[j]) { - continue; - } - - /* Check if valve connection is in correct direction */ - switch (net->Link[k].Type) { - case CVPIPE: - case PRV: - case PSV: - if (j == net->Link[k].N1) { - continue; - } - default: - break; - } - - /* Mark connection node if link not closed */ - if (hyd->LinkStatus[k] > CLOSED) { - marked[j] = 1; - m++; - nodelist[m] = j; - } + // Mark connection node if link not closed + if (hyd->LinkStatus[k] > CLOSED) + { + marked[j] = 1; + m++; + nodelist[m] = j; + } + } + n++; } - n++; - } -} /* End of marknodes() */ +} -void getclosedlink(EN_Project *pr, int i, char *marked) +void getclosedlink(Project *pr, int i, char *marked) /* **---------------------------------------------------------------- ** Input: i = junction index @@ -1274,26 +1266,28 @@ void getclosedlink(EN_Project *pr, int i, char *marked) **---------------------------------------------------------------- */ { - EN_Network *net = &pr->network; + Network *net = &pr->network; - int j, k; - Padjlist alink; - marked[i] = 2; - for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) { - k = alink->link; - j = alink->node; - if (marked[j] == 2) - continue; - if (marked[j] == 1) { - sprintf(pr->Msg, WARN03c, net->Link[k].ID); - writeline(pr, pr->Msg); - return; - } else - getclosedlink(pr, j, marked); - } + int j, k; + Padjlist alink; + + marked[i] = 2; + for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) + { + k = alink->link; + j = alink->node; + if (marked[j] == 2) continue; + if (marked[j] == 1) + { + sprintf(pr->Msg, WARN03c, net->Link[k].ID); + writeline(pr, pr->Msg); + return; + } + else getclosedlink(pr, j, marked); + } } -void writelimits(EN_Project *pr, int j1, int j2) +void writelimits(Project *pr, int j1, int j2) /* **-------------------------------------------------------------- ** Input: j1 = index of first output variable @@ -1303,24 +1297,29 @@ void writelimits(EN_Project *pr, int j1, int j2) **-------------------------------------------------------------- */ { - report_options_t *rep = &pr->report; + Report *rpt = &pr->report; + int j; - int j; - for (j = j1; j <= j2; j++) { - if (rep->Field[j].RptLim[LOW] < BIG) { - sprintf(pr->Msg, FMT47, rep->Field[j].Name, rep->Field[j].RptLim[LOW], - rep->Field[j].Units); - writeline(pr, pr->Msg); + for (j = j1; j <= j2; j++) + { + if (rpt->Field[j].RptLim[LOW] < BIG) + { + sprintf(pr->Msg, FMT47, rpt->Field[j].Name, + rpt->Field[j].RptLim[LOW], + rpt->Field[j].Units); + writeline(pr, pr->Msg); + } + if (rpt->Field[j].RptLim[HI] > -BIG) + { + sprintf(pr->Msg, FMT48, rpt->Field[j].Name, + rpt->Field[j].RptLim[HI], + rpt->Field[j].Units); + writeline(pr, pr->Msg); + } } - if (rep->Field[j].RptLim[HI] > -BIG) { - sprintf(pr->Msg, FMT48, rep->Field[j].Name, rep->Field[j].RptLim[HI], - rep->Field[j].Units); - writeline(pr, pr->Msg); - } - } -} /* End of writelimits */ +} -int checklimits(report_options_t *rep, double *y, int j1, int j2) +int checklimits(Report *rpt, double *y, int j1, int j2) /* **-------------------------------------------------------------- ** Input: *y = array of output results @@ -1331,15 +1330,17 @@ int checklimits(report_options_t *rep, double *y, int j1, int j2) **-------------------------------------------------------------- */ { - int j; - for (j = j1; j <= j2; j++) { - if (y[j] > rep->Field[j].RptLim[LOW] || y[j] < rep->Field[j].RptLim[HI]) - return (0); - } - return (1); -} /* End of checklim */ + int j; + for (j = j1; j <= j2; j++) + { + if (y[j] > rpt->Field[j].RptLim[LOW] || + y[j] < rpt->Field[j].RptLim[HI] + ) return 0; + } + return 1; +} -void writetime(EN_Project *pr, char *fmt) +void writetime(Project *pr, char *fmt) /* **---------------------------------------------------------------- ** Input: fmt = format string @@ -1348,10 +1349,10 @@ void writetime(EN_Project *pr, char *fmt) **---------------------------------------------------------------- */ { - time_t timer; - time(&timer); - sprintf(pr->Msg, fmt, ctime(&timer)); - writeline(pr, pr->Msg); + time_t timer; + time(&timer); + sprintf(pr->Msg, fmt, ctime(&timer)); + writeline(pr, pr->Msg); } char *clocktime(char *atime, long seconds) @@ -1364,14 +1365,13 @@ char *clocktime(char *atime, long seconds) **-------------------------------------------------------------- */ { - /*** Updated 6/24/02 ***/ - long h, m, s; - h = seconds / 3600; - m = seconds % 3600 / 60; - s = seconds - 3600 * h - 60 * m; - sprintf(atime, "%01d:%02d:%02d", (int)h, (int)m, (int)s); - return (atime); -} /* End of clocktime */ + long h, m, s; + h = seconds / 3600; + m = seconds % 3600 / 60; + s = seconds - 3600 * h - 60 * m; + sprintf(atime, "%01d:%02d:%02d", (int)h, (int)m, (int)s); + return atime; +} char *fillstr(char *s, char ch, int n) /* @@ -1381,14 +1381,13 @@ char *fillstr(char *s, char ch, int n) **--------------------------------------------------------- */ { - int i; - for (i = 0; i <= n; i++) - s[i] = ch; - s[n + 1] = '\0'; - return (s); + int i; + for (i = 0; i <= n; i++) s[i] = ch; + s[n + 1] = '\0'; + return (s); } -int getnodetype(EN_Network *net, int i) +int getnodetype(Network *net, int i) /* **--------------------------------------------------------- ** Determines type of node with index i @@ -1396,11 +1395,7 @@ int getnodetype(EN_Network *net, int i) **--------------------------------------------------------- */ { - if (i <= net->Njuncs) - return (0); - if (net->Tank[i - net->Njuncs].A == 0.0) - return (1); - return (2); + if (i <= net->Njuncs) return 0; + if (net->Tank[i - net->Njuncs].A == 0.0) return 1; + return 2; } - -/********************* END OF REPORT.C ********************/ diff --git a/src/rules.c b/src/rules.c index d443b7e..eb93fc1 100644 --- a/src/rules.c +++ b/src/rules.c @@ -1,27 +1,16 @@ /* -********************************************************************** - -RULES.C -- Rule processor module for EPANET - -VERSION: 2.00 -DATE: 5/8/00 - 9/7/00 - 10/25/00 - 3/1/01 - 8/15/07 (2.00.11) -AUTHOR: L. Rossman - US EPA - NRMRL - - The entry points for this module are: - initrules() -- called from ENopen() in EPANET.C - addrule() -- called from netsize() in INPUT2.C - allocrules() -- called from allocdata() in EPANET.C - ruledata() -- called from newline() in INPUT2.C - freerules() -- called from freedata() in EPANET.C - checkrules() -- called from ruletimestep() in HYDRAUL.C - -********************************************************************** + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: rules.c + Description: implements rule-based controls + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ + #include #include #ifndef __APPLE__ @@ -81,8 +70,7 @@ enum Objects { char *Object[] = {w_JUNC, w_RESERV, w_TANK, w_PIPE, w_PUMP, w_VALVE, w_NODE, w_LINK, w_SYSTEM, NULL}; -/* NOTE: place "<=" & ">=" before "<" & ">" so that findmatch() works correctly. - */ +// NOTE: place "<=" & ">=" before "<" & ">" so that findmatch() works correctly. enum Operators { EQ, NE, LE, GE, LT, GT, IS, NOT, BELOW, ABOVE }; char *Operator[] = {"=", "<>", "<=", ">=", "<", ">", w_IS, w_NOT, w_BELOW, w_ABOVE, NULL}; @@ -90,144 +78,113 @@ char *Operator[] = {"=", "<>", "<=", ">=", "<", ">", enum Values { IS_NUMBER, IS_OPEN, IS_CLOSED, IS_ACTIVE }; char *Value[] = {"XXXX", w_OPEN, w_CLOSED, w_ACTIVE, NULL}; -// Local Functions -static void newrule(EN_Project *pr); -static int newpremise(EN_Project *pr, int); -static int newaction(EN_Project *pr); -static int newpriority(EN_Project *pr); -static int evalpremises(EN_Project *pr, int); -static void updateactionlist(EN_Project *pr, int, Saction *); -static int onactionlist(EN_Project *pr, int, Saction *); -static int checkpremise(EN_Project *pr, Spremise *); -static int checktime(EN_Project *pr, Spremise *); -static int checkstatus(EN_Project *pr, Spremise *); -static int checkvalue(EN_Project *pr, Spremise *); -static int takeactions(EN_Project *pr); -static void clearactionlist(rules_t *rules); -static void clearrule(EN_Project *pr, int); +// Local functions +static void newrule(Project *); +static int newpremise(Project *, int); +static int newaction(Project *); +static int newpriority(Project *); -static void writepremise(Spremise *p, FILE *f, EN_Network *net); -static void writeaction(Saction *a, FILE *f, EN_Network *net); -static void getobjtxt(int objtype, int subtype, char *objtxt); -static void gettimetxt(double hrs, char *timetxt); +static int evalpremises(Project *, int); +static int checkpremise(Project *, Spremise *); +static int checktime(Project *, Spremise *); +static int checkstatus(Project *, Spremise *); +static int checkvalue(Project *, Spremise *); + +static int onactionlist(Project *, int, Saction *); +static void updateactionlist(Project *, int, Saction *); +static int takeactions(Project *); +static void clearactionlist(Rules *); +static void clearrule(Project *, int); + +static void writepremise(Spremise *, FILE *, Network *); +static void writeaction(Saction *, FILE *, Network *); +static void getobjtxt(int, int, char *); +static void gettimetxt(double, char *); -void initrules(EN_Project *pr) -/* -**-------------------------------------------------------------- -** Initializes rule base. -** Called by ENopen() in EPANET.C module -**-------------------------------------------------------------- -*/ +void initrules(Project *pr) +//-------------------------------------------------------------- +// Initializes rule base. +//-------------------------------------------------------------- { - pr->rules.RuleState = r_PRIORITY; - pr->network.Rule = NULL; + pr->rules.RuleState = r_PRIORITY; + pr->network.Rule = NULL; } -void addrule(parser_data_t *par, char *tok) -/* -**-------------------------------------------------------------- -** Updates rule count if RULE keyword found in line of input. -** Called by netsize() in INPUT2.C module. -**-------------------------------------------------------------- -*/ +void addrule(Parser *parser, char *tok) +//-------------------------------------------------------------- +// Updates rule count if RULE keyword found in line of input. +//-------------------------------------------------------------- { - if (match(tok, w_RULE)) { - par->MaxRules++; - } + if (match(tok, w_RULE)) parser->MaxRules++; } -int allocrules(EN_Project *pr) -/* -**-------------------------------------------------------------- -** Allocates memory for rule-based controls. -** Called by allocdata() in EPANET.C module. -**-------------------------------------------------------------- -*/ +void deleterule(Project *pr, int index) +//----------------------------------------------------------- +// Deletes a specific rule +//----------------------------------------------------------- { - EN_Network *net = &pr->network; + Network *net = &pr->network; + + int i; + Srule *lastRule; + + // Free memory allocated to rule's premises & actions + clearrule(pr, index); + + // Shift position of higher indexed rules down one + for (i = index; i <= net->Nrules - 1; i++) + { + net->Rule[i] = net->Rule[i + 1]; + } + + // Remove premises & actions from last (inactive) entry in Rule array + lastRule = &net->Rule[net->Nrules]; + lastRule->Premises = NULL; + lastRule->ThenActions = NULL; + lastRule->ElseActions = NULL; + + // Reduce active rule count by one + net->Nrules--; +} + +int allocrules(Project *pr) +//-------------------------------------------------------------- +// Allocates memory for rule-based controls. +//-------------------------------------------------------------- +{ + Network *net = &pr->network; int n = pr->parser.MaxRules + 1; net->Rule = (Srule *)calloc(n, sizeof(Srule)); - if (net->Rule == NULL) return (101); + if (net->Rule == NULL) return 101; return 0; } -void freerules(EN_Project *pr) -/* -**-------------------------------------------------------------- -** Frees memory used for rule-based controls. -** Called by freedata() in EPANET.C module. -**-------------------------------------------------------------- -*/ +void freerules(Project *pr) +//-------------------------------------------------------------- +// Frees memory used for rule-based controls. +//-------------------------------------------------------------- { int i; for (i = 1; i <= pr->network.Nrules; i++) clearrule(pr, i); free(pr->network.Rule); } -int checkrules(EN_Project *pr, long dt) -/* -**----------------------------------------------------- -** Checks which rules should fire at current time. -** Called by ruletimestep() in HYDRAUL.C. -**----------------------------------------------------- -*/ +int ruledata(Project *pr) +//-------------------------------------------------------------- +// Parses a line from [RULES] section of input. +//-------------------------------------------------------------- { - EN_Network *net = &pr->network; - time_options_t *time = &pr->time_options; - rules_t *rules = &pr->rules; + Network *net = &pr->network; + Parser *parser = &pr->parser; + Rules *rules = &pr->rules; - int i, - actionCount = 0; // Number of actions actually taken - - // Start of rule evaluation time interval - rules->Time1 = time->Htime - dt + 1; - - // Iterate through each rule - rules->ActionList = NULL; - for (i = 1; i <= net->Nrules; i++) - { - // If premises true, add THEN clauses to action list - if (evalpremises(pr, i) == TRUE) - { - updateactionlist(pr, i, net->Rule[i].ThenActions); - } - - // If premises false, add ELSE actions to list - else - { - if (net->Rule[i].ElseActions != NULL) - { - updateactionlist(pr, i, net->Rule[i].ElseActions); - } - } - } - - // Execute actions then clear action list - if (rules->ActionList != NULL) actionCount = takeactions(pr); - clearactionlist(rules); - return actionCount; -} - -int ruledata(EN_Project *pr) -/* -**-------------------------------------------------------------- -** Parses a line from [RULES] section of input. -** Called by newline() in INPUT2.C module. -** Tok[] is an array of tokens parsed from input line. -**-------------------------------------------------------------- -*/ -{ - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - rules_t *rules = &pr->rules; - char **Tok = par->Tok; - - int key, // Keyword code + int key, // Keyword code err; + char **Tok = parser->Tok; // Tokenized line of a rule statement - // Exit if current rule has an error */ + // Exit if current rule has an error if (rules->RuleState == r_ERROR) return 0; // Find the key word that begins the rule statement @@ -314,18 +271,66 @@ int ruledata(EN_Project *pr) return err; } - -void adjustrules(EN_Project *pr, int objtype, int index) -/* -**----------------------------------------------------------- -** Adjusts rules when a specific node or link is deleted. -** Called by EN_deletenode & EN_deletelink in EPANET.C. -**----------------------------------------------------------- -*/ +void ruleerrmsg(Project *pr) +//----------------------------------------------------------- +// Report a rule parsing error message +//----------------------------------------------------------- { + Network *net = &pr->network; + Parser *parser = &pr->parser; + Rules *rules = &pr->rules; + + int i; + char label[MAXMSG + 1]; + char msg[MAXLINE + 1]; + char **Tok = parser->Tok; + + // Get text of error message + switch (rules->Errcode) + { + case 201: strcpy(msg, R_ERR201); break; + case 202: strcpy(msg, R_ERR202); break; + case 203: strcpy(msg, R_ERR203); break; + case 204: strcpy(msg, R_ERR204); break; + case 207: strcpy(msg, R_ERR207); break; + case 221: strcpy(msg, R_ERR221); break; + default: return; + } + + // Get label of rule being parsed + if (net->Nrules > 0) + { + strcpy(label, t_RULE); + strcat(label, " "); + strcat(label, net->Rule[net->Nrules].label); + } + else strcpy(label, t_RULES_SECT); + + // Write rule label and error message to status report + sprintf(pr->Msg, "%s", msg); + strcat(pr->Msg, label); + strcat(pr->Msg, ":"); + writeline(pr, pr->Msg); + + // Write text of rule clause being parsed to status report + strcpy(msg, Tok[0]); + for (i = 1; i < parser->Ntokens; i++) + { + strcat(msg, " "); + strcat(msg, Tok[i]); + } + writeline(pr, msg); +} + +void adjustrules(Project *pr, int objtype, int index) +//----------------------------------------------------------- +// Adjusts rules when a specific node or link is deleted. +//----------------------------------------------------------- +{ + Network *net = &pr->network; + Rules *rules = &pr->rules; + int i, delete; - EN_Network *net = &pr->network; - rules_t *rules = &pr->rules; Spremise *p; Saction *a; @@ -384,46 +389,14 @@ void adjustrules(EN_Project *pr, int objtype, int index) } } -void deleterule(EN_Project *pr, int index) -/* -**----------------------------------------------------------- -** Deletes a specific rule -**----------------------------------------------------------- -*/ +void adjusttankrules(Project *pr) +//----------------------------------------------------------- +// Adjusts tank indices in rule premises. +//----------------------------------------------------------- { - int i; - EN_Network *net = &pr->network; - Srule *lastRule; + Network *net = &pr->network; - // Free memory allocated to rule's premises & actions - clearrule(pr, index); - - // Shift position of higher indexed rules down one - for (i = index; i <= net->Nrules - 1; i++) - { - net->Rule[i] = net->Rule[i + 1]; - } - - // Remove premises & actions from last (inactive) entry in Rule array - lastRule = &net->Rule[net->Nrules]; - lastRule->Premises = NULL; - lastRule->ThenActions = NULL; - lastRule->ElseActions = NULL; - - // Reduce active rule count by one - net->Nrules--; -} - -void adjusttankrules(EN_Project *pr) -/* -**----------------------------------------------------------- -** Adjusts tank indices in rule premises. -** Called by EN_addnode in EPANET.C. -**----------------------------------------------------------- -*/ -{ int i, njuncs; - EN_Network *net = &pr->network; Spremise *p; njuncs = net->Njuncs; @@ -438,68 +411,137 @@ void adjusttankrules(EN_Project *pr) } } -void clearactionlist(rules_t *rules) -/* -**---------------------------------------------------------- -** Clears memory used for action list -**---------------------------------------------------------- -*/ -{ - SactionList *nextItem; - SactionList *actionItem; - actionItem = rules->ActionList; - while (actionItem != NULL) - { - nextItem = actionItem->next; - free(actionItem); - actionItem = nextItem; - } -} - -void clearrule(EN_Project *pr, int i) -/* -**----------------------------------------------------------- -** Clears memory used by a rule for premises & actions -**----------------------------------------------------------- -*/ +Spremise *getpremise(Spremise *premises, int i) +//---------------------------------------------------------- +// Return the i-th premise in a rule +//---------------------------------------------------------- { - EN_Network *net = &pr->network; + int count = 0; Spremise *p; - Spremise *pnext; - Saction *a; - Saction *anext; - p = net->Rule[i].Premises; + p = premises; while (p != NULL) { - pnext = p->next; - free(p); - p = pnext; - } - a = net->Rule[i].ThenActions; - while (a != NULL) - { - anext = a->next; - free(a); - a = anext; - } - a = net->Rule[i].ElseActions; - while (a != NULL) - { - anext = a->next; - free(a); - a = anext; + count++; + if (count == i) break; + p = p->next; } + return p; } -void newrule(EN_Project *pr) -/* -**---------------------------------------------------------- -** Adds new rule to rule base -**---------------------------------------------------------- -*/ +Saction *getaction(Saction *actions, int i) +//---------------------------------------------------------- +// Return the i-th action from a rule's action list +//---------------------------------------------------------- { - EN_Network *net = &pr->network; + int count = 0; + Saction *a; + + a = actions; + while (a != NULL) + { + count++; + if (count == i) break; + a = a->next; + } + return a; +} + +int writerule(Project *pr, FILE *f, int ruleIndex) +//----------------------------------------------------------------------------- +// Write a rule to an INP file. +//----------------------------------------------------------------------------- +{ + Network *net = &pr->network; + Rules *rules = &pr->rules; + + Srule *rule = &net->Rule[ruleIndex]; + Spremise *p; + Saction *a; + + // Write each premise clause to the file + p = rule->Premises; + fprintf(f, "\nIF "); + while (p != NULL) + { + writepremise(p, f, net); + p = p->next; + if (p) fprintf(f, "\n%-5s", Ruleword[p->logop]); + } + + // Write each THEN action clause to the file + a = rule->ThenActions; + if (a) fprintf(f, "\nTHEN "); + while (a != NULL) + { + writeaction(a, f, net); + a = a->next; + if (a) fprintf(f, "\nAND "); + } + + // Write each ELSE action clause to the file + a = rule->ElseActions; + if (a) fprintf(f, "\nELSE "); + while (a != NULL) + { + writeaction(a, f, net); + a = a->next; + if (a) fprintf(f, "\nAND "); + } + + // Write the rule's priority to the file + if (rule->priority > 0) fprintf(f, "\nPRIORITY %f", rule->priority); + return 0; +} + +int checkrules(Project *pr, long dt) +//----------------------------------------------------- +// Checks which rules should fire at current time. +//----------------------------------------------------- +{ + Network *net = &pr->network; + Times *time = &pr->times; + Rules *rules = &pr->rules; + + int i; + int actionCount = 0; // Number of actions actually taken + + // Start of rule evaluation time interval + rules->Time1 = time->Htime - dt + 1; + + // Iterate through each rule + rules->ActionList = NULL; + for (i = 1; i <= net->Nrules; i++) + { + // If premises true, add THEN clauses to action list + if (evalpremises(pr, i) == TRUE) + { + updateactionlist(pr, i, net->Rule[i].ThenActions); + } + + // If premises false, add ELSE actions to list + else + { + if (net->Rule[i].ElseActions != NULL) + { + updateactionlist(pr, i, net->Rule[i].ElseActions); + } + } + } + + // Execute actions then clear action list + if (rules->ActionList != NULL) actionCount = takeactions(pr); + clearactionlist(rules); + return actionCount; +} + +void newrule(Project *pr) +//---------------------------------------------------------- +// Adds a new rule to the project +//---------------------------------------------------------- +{ + Network *net = &pr->network; + char **Tok = pr->parser.Tok; Srule *rule = &net->Rule[net->Nrules]; @@ -513,321 +555,251 @@ void newrule(EN_Project *pr) pr->rules.LastElseAction = NULL; } -int newpremise(EN_Project *pr, int logop) -/* -**-------------------------------------------------------------------- -** Adds new premise to current rule. -** Formats are: -** IF/AND/OR -** IF/AND/OR SYSTEM (units) -** -** Calls findmatch() and hour() in INPUT2.C. -** Calls findnode() and findlink() in EPANET.C. -**--------------------------------------------------------------------- -*/ +int newpremise(Project *pr, int logop) +//-------------------------------------------------------------------- +// Adds new premise to current rule. +// Formats are: +// IF/AND/OR +// IF/AND/OR SYSTEM (units) +//--------------------------------------------------------------------- { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; + Network *net = &pr->network; + Parser *parser = &pr->parser; + Rules *rules = &pr->rules; - rules_t *rules = &pr->rules; - char **Tok = par->Tok; - - int i, j, k, m, r, s, v; - double x; - Spremise *p; - - /* Check for correct number of tokens */ - if (par->Ntokens != 5 && par->Ntokens != 6) - return (201); - - /* Find network object & id if present */ - i = findmatch(Tok[1], Object); - if (i == r_SYSTEM) { - j = 0; - v = findmatch(Tok[2], Varword); - if (v != r_DEMAND && v != r_TIME && v != r_CLOCKTIME) { - return (201); - } - } else { - v = findmatch(Tok[3], Varword); - if (v < 0) { - return (201); - } - switch (i) { - case r_NODE: - case r_JUNC: - case r_RESERV: - case r_TANK: - k = r_NODE; - break; - case r_LINK: - case r_PIPE: - case r_PUMP: - case r_VALVE: - k = r_LINK; - break; - default: - return (201); - } - i = k; - if (i == r_NODE) { - j = findnode(net, Tok[2]); - if (j == 0) - return (203); - switch (v) { - case r_DEMAND: - case r_HEAD: - case r_GRADE: - case r_LEVEL: - case r_PRESSURE: - break; - case r_FILLTIME: - case r_DRAINTIME: - if (j <= net->Njuncs) { - return (201); - } - break; - - default: - return (201); - } - } else { - j = findlink(net, Tok[2]); - if (j == 0) { - return (204); - } - switch (v) { - case r_FLOW: - case r_STATUS: - case r_SETTING: - break; - default: - return (201); - } - } - } - - /* Parse relational operator (r) and check for synonyms */ - if (i == r_SYSTEM) { - m = 3; - } else { - m = 4; - } - k = findmatch(Tok[m], Operator); - if (k < 0) - return (201); - switch (k) { - case IS: - r = EQ; - break; - case NOT: - r = NE; - break; - case BELOW: - r = LT; - break; - case ABOVE: - r = GT; - break; - default: - r = k; - } - - /* Parse for status (s) or numerical value (x) */ - s = 0; - x = MISSING; - if (v == r_TIME || v == r_CLOCKTIME) { - if (par->Ntokens == 6) - x = hour(Tok[4], Tok[5]) * 3600.; - else - x = hour(Tok[4], "") * 3600.; - if (x < 0.0) - return (202); - } else if ((k = findmatch(Tok[par->Ntokens - 1], Value)) > IS_NUMBER) - s = k; - else { - if (!getfloat(Tok[par->Ntokens - 1], &x)) - return (202); - if (v == r_FILLTIME || v == r_DRAINTIME) - x = x * 3600.0; - } - - /* Create new premise structure */ - p = (Spremise *)malloc(sizeof(Spremise)); - if (p == NULL) - return (101); - p->object = i; - p->index = j; - p->variable = v; - p->relop = r; - p->logop = logop; - p->status = s; - p->value = x; - - /* Add premise to current rule's premise list */ - p->next = NULL; - if (rules->LastPremise == NULL) - net->Rule[net->Nrules].Premises = p; - else - rules->LastPremise->next = p; - rules->LastPremise = p; - return (0); -} - -int newaction(EN_Project *pr) -/* -**---------------------------------------------------------- -** Adds new action to current rule. -** Format is: -** THEN/ELSE/AND LINK IS -** -** Calls findlink() from EPANET.C. -** Calls getfloat() and findmatch() from INPUT2.C. -**---------------------------------------------------------- -*/ -{ - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - rules_t *rules = &pr->rules; - char **Tok = par->Tok; - - int j, k, s; - double x; - Saction *a; - - /* Check for correct number of tokens */ - if (par->Ntokens != 6) - return (201); - - /* Check that link exists */ - j = findlink(net, Tok[2]); - if (j == 0) - return (204); - - /*** Updated 9/7/00 ***/ - /* Cannot control a CV */ - if (net->Link[j].Type == CVPIPE) - return (207); - - /* Find value for status or setting */ - s = -1; - x = MISSING; - if ((k = findmatch(Tok[5], Value)) > IS_NUMBER) { - s = k; - } else { - if (!getfloat(Tok[5], &x)) { - return (202); - } - if (x < 0.0) { - return (202); - } - } - - /*** Updated 9/7/00 ***/ - /* Cannot change setting for a GPV ***/ - if (x != MISSING && net->Link[j].Type == GPV) - return (202); - - /*** Updated 3/1/01 ***/ - /* Set status for pipe in case setting was specified */ - if (x != MISSING && net->Link[j].Type == PIPE) { - if (x == 0.0) - s = IS_CLOSED; - else - s = IS_OPEN; - x = MISSING; - } - - /* Create a new action structure */ - a = (Saction *)malloc(sizeof(Saction)); - if (a == NULL) - return (101); - a->link = j; - a->status = s; - a->setting = x; - - /* Add action to current rule's action list */ - if (rules->RuleState == r_THEN) { - a->next = NULL; - if (rules->LastThenAction == NULL) - net->Rule[net->Nrules].ThenActions = a; - else - rules->LastThenAction->next = a; - rules->LastThenAction = a; - } else { - a->next = NULL; - if (rules->LastElseAction == NULL) - net->Rule[net->Nrules].ElseActions = a; - else - rules->LastElseAction->next = a; - rules->LastElseAction = a; - } - return (0); -} - -int newpriority(EN_Project *pr) -/* -**--------------------------------------------------- -** Adds priority rating to current rule -**--------------------------------------------------- -*/ -{ - EN_Network *net = &pr->network; - char **Tok = pr->parser.Tok; + int i, j, k, m, r, s, v; double x; + char **Tok = parser->Tok; + Spremise *p; - if (!getfloat(Tok[1], &x)) return (202); + // Check for correct number of tokens + if (parser->Ntokens != 5 && parser->Ntokens != 6) return 201; + + // Find network object & id if present + i = findmatch(Tok[1], Object); + if (i == r_SYSTEM) + { + j = 0; + v = findmatch(Tok[2], Varword); + if (v != r_DEMAND && v != r_TIME && v != r_CLOCKTIME) return 201; + } + else + { + v = findmatch(Tok[3], Varword); + if (v < 0) return (201); + switch (i) + { + case r_NODE: + case r_JUNC: + case r_RESERV: + case r_TANK: + k = r_NODE; + break; + case r_LINK: + case r_PIPE: + case r_PUMP: + case r_VALVE: + k = r_LINK; + break; + default: + return 201; + } + i = k; + if (i == r_NODE) + { + j = findnode(net, Tok[2]); + if (j == 0) return 203; + switch (v) + { + case r_DEMAND: + case r_HEAD: + case r_GRADE: + case r_LEVEL: + case r_PRESSURE: + break; + case r_FILLTIME: + case r_DRAINTIME: + if (j <= net->Njuncs) return 201; + break; + default: + return 201; + } + } + else + { + j = findlink(net, Tok[2]); + if (j == 0) return 204; + switch (v) + { + case r_FLOW: + case r_STATUS: + case r_SETTING: + break; + default: + return 201; + } + } + } + + // Parse relational operator (r) and check for synonyms + if (i == r_SYSTEM) m = 3; + else m = 4; + k = findmatch(Tok[m], Operator); + if (k < 0) return 201; + switch (k) + { + case IS: + r = EQ; + break; + case NOT: + r = NE; + break; + case BELOW: + r = LT; + break; + case ABOVE: + r = GT; + break; + default: + r = k; + } + + // Parse for status (s) or numerical value (x) + s = 0; + x = MISSING; + if (v == r_TIME || v == r_CLOCKTIME) + { + if (parser->Ntokens == 6) x = hour(Tok[4], Tok[5]) * 3600.; + else x = hour(Tok[4], "") * 3600.; + if (x < 0.0) return 202; + } + else if ((k = findmatch(Tok[parser->Ntokens - 1], Value)) > IS_NUMBER) s = k; + else + { + if (!getfloat(Tok[parser->Ntokens - 1], &x)) + return (202); + if (v == r_FILLTIME || v == r_DRAINTIME) x = x * 3600.0; + } + + // Create new premise structure + p = (Spremise *)malloc(sizeof(Spremise)); + if (p == NULL) return 101; + p->object = i; + p->index = j; + p->variable = v; + p->relop = r; + p->logop = logop; + p->status = s; + p->value = x; + + // Add premise to current rule's premise list + p->next = NULL; + if (rules->LastPremise == NULL) net->Rule[net->Nrules].Premises = p; + else rules->LastPremise->next = p; + rules->LastPremise = p; + return 0; +} + +int newaction(Project *pr) +//---------------------------------------------------------- +// Adds new action to current rule. +// Format is: +// THEN/ELSE/AND LINK IS +//---------------------------------------------------------- +{ + Network *net = &pr->network; + Parser *parser = &pr->parser; + Rules *rules = &pr->rules; + + int j, k, s; + double x; + Saction *a; + char **Tok = parser->Tok; + + // Check for correct number of tokens + if (parser->Ntokens != 6) return 201; + + // Check that link exists + j = findlink(net, Tok[2]); + if (j == 0) return 204; + + // Cannot control a CV + if (net->Link[j].Type == CVPIPE) return 207; + + // Find value for status or setting + s = -1; + x = MISSING; + if ((k = findmatch(Tok[5], Value)) > IS_NUMBER) s = k; + else + { + if (!getfloat(Tok[5], &x)) return 202; + if (x < 0.0) return 202; + } + + // Cannot change setting for a GPV + if (x != MISSING && net->Link[j].Type == GPV) return 202; + + // Set status for pipe in case setting was specified + if (x != MISSING && net->Link[j].Type == PIPE) + { + if (x == 0.0) s = IS_CLOSED; + else s = IS_OPEN; + x = MISSING; + } + + // Create a new action structure + a = (Saction *)malloc(sizeof(Saction)); + if (a == NULL) return 101; + a->link = j; + a->status = s; + a->setting = x; + + // Add action to current rule's action list + if (rules->RuleState == r_THEN) + { + a->next = NULL; + if (rules->LastThenAction == NULL) + { + net->Rule[net->Nrules].ThenActions = a; + } + else rules->LastThenAction->next = a; + rules->LastThenAction = a; + } + else + { + a->next = NULL; + if (rules->LastElseAction == NULL) + { + net->Rule[net->Nrules].ElseActions = a; + } + else rules->LastElseAction->next = a; + rules->LastElseAction = a; + } + return 0; +} + +int newpriority(Project *pr) +//--------------------------------------------------- +// Adds priority rating to current rule +//--------------------------------------------------- +{ + Network *net = &pr->network; + + double x; + char **Tok = pr->parser.Tok; + + if (!getfloat(Tok[1], &x)) return 202; net->Rule[net->Nrules].priority = x; return 0; } -Spremise *getpremise(Spremise *premises, int i) -/* -**---------------------------------------------------------- -** Return the i-th premise in a rule -**---------------------------------------------------------- -*/ +int evalpremises(Project *pr, int i) +//---------------------------------------------------------- +// Checks if premises to rule i are true +//---------------------------------------------------------- { - int count = 0; - Spremise *p; - p = premises; - while (p != NULL) - { - count++; - if (count == i) break; - p = p->next; - } - return p; -} + Network *net = &pr->network; -Saction *getaction(Saction *actions, int i) -/* -**---------------------------------------------------------- -** Return the i-th action from a rule's action list -**---------------------------------------------------------- -*/ -{ - int count = 0; - Saction *a; - a = actions; - while (a != NULL) - { - count++; - if (count == i) break; - a = a->next; - } - return a; -} - - -int evalpremises(EN_Project *pr, int i) -/* -**---------------------------------------------------------- -** Checks if premises to rule i are true -**---------------------------------------------------------- -*/ -{ - EN_Network *net = &pr->network; int result; Spremise *p; @@ -849,12 +821,10 @@ int evalpremises(EN_Project *pr, int i) return result; } -int checkpremise(EN_Project *pr, Spremise *p) -/* -**---------------------------------------------------------- -** Checks if a particular premise is true -**---------------------------------------------------------- -*/ +int checkpremise(Project *pr, Spremise *p) +//---------------------------------------------------------- +// Checks if a particular premise is true +//---------------------------------------------------------- { if (p->variable == r_TIME || p->variable == r_CLOCKTIME) return (checktime(pr,p)); @@ -862,15 +832,14 @@ int checkpremise(EN_Project *pr, Spremise *p) else return (checkvalue(pr,p)); } -int checktime(EN_Project *pr, Spremise *p) -/* -**------------------------------------------------------------ -** Checks if condition on system time holds -**------------------------------------------------------------ -*/ +int checktime(Project *pr, Spremise *p) +//------------------------------------------------------------ +// Checks if condition on system time holds +//------------------------------------------------------------ { - time_options_t *time = &pr->time_options; - rules_t *rules = &pr->rules; + Times *time = &pr->times; + Rules *rules = &pr->rules; + char flag; long t1, t2, x; @@ -928,160 +897,137 @@ int checktime(EN_Project *pr, Spremise *p) return 1; } -int checkstatus(EN_Project *pr, Spremise *p) -/* -**------------------------------------------------------------ -** Checks if condition on link status holds -**------------------------------------------------------------ -*/ +int checkstatus(Project *pr, Spremise *p) +//------------------------------------------------------------ +// Checks if condition on link status holds +//------------------------------------------------------------ { - hydraulics_t *hyd = &pr->hydraulics; + Hydraul *hyd = &pr->hydraul; + + char i; + int j; - char i; - int j; - switch (p->status) { - case IS_OPEN: - case IS_CLOSED: - case IS_ACTIVE: - i = hyd->LinkStatus[p->index]; - if (i <= CLOSED) - j = IS_CLOSED; - else if (i == ACTIVE) - j = IS_ACTIVE; - else - j = IS_OPEN; - if (j == p->status && p->relop == EQ) - return (1); - if (j != p->status && p->relop == NE) - return (1); - } - return (0); + switch (p->status) + { + case IS_OPEN: + case IS_CLOSED: + case IS_ACTIVE: + i = hyd->LinkStatus[p->index]; + if (i <= CLOSED) j = IS_CLOSED; + else if (i == ACTIVE) j = IS_ACTIVE; + else j = IS_OPEN; + if (j == p->status && p->relop == EQ) return 1; + if (j != p->status && p->relop == NE) return 1; + } + return 0; } -int checkvalue(EN_Project *pr, Spremise *p) -/* -**---------------------------------------------------------- -** Checks if numerical condition on a variable is true. -** Uses tolerance of 0.001 when testing conditions. -**---------------------------------------------------------- -*/ +int checkvalue(Project *pr, Spremise *p) +//---------------------------------------------------------- +// Checks if numerical condition on a variable is true. +// Uses tolerance of 0.001 when testing conditions. +//---------------------------------------------------------- { - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; - Snode *Node = net->Node; - Slink *Link = net->Link; - Stank *Tank = net->Tank; + int i, j, v; + double x, // A variable's value + tol = 1.e-3; // Equality tolerance + int Njuncs = net->Njuncs; + double *Ucf = pr->Ucf; + double *NodeDemand = hyd->NodeDemand; + double *LinkFlow = hyd->LinkFlow; + double *LinkSetting = hyd->LinkSetting; + Snode *Node = net->Node; + Slink *Link = net->Link; + Stank *Tank = net->Tank; - const int Njuncs = net->Njuncs; - double *Ucf = pr->Ucf; - double *NodeDemand = hyd->NodeDemand; - double *LinkFlows = hyd->LinkFlows; - double *LinkSetting = hyd->LinkSetting; - - int i, j, v; - double x, tol = 1.e-3; + // Find the value being checked + i = p->index; + v = p->variable; + switch (v) + { + case r_DEMAND: + if (p->object == r_SYSTEM) x = hyd->Dsystem * Ucf[DEMAND]; + else x = NodeDemand[i] * Ucf[DEMAND]; + break; - i = p->index; - v = p->variable; - switch (v) { - case r_DEMAND: - if (p->object == r_SYSTEM) - x = hyd->Dsystem * Ucf[DEMAND]; - else - x = NodeDemand[i] * Ucf[DEMAND]; - break; - - case r_HEAD: - case r_GRADE: - x = hyd->NodeHead[i] * Ucf[HEAD]; - break; - case r_PRESSURE: - x = (hyd->NodeHead[i] - Node[i].El) * Ucf[PRESSURE]; - break; - case r_LEVEL: - x = (hyd->NodeHead[i] - Node[i].El) * Ucf[HEAD]; - break; - case r_FLOW: - x = ABS(LinkFlows[i]) * Ucf[FLOW]; - break; - case r_SETTING: - if (LinkSetting[i] == MISSING) - return (0); - x = LinkSetting[i]; - switch (Link[i].Type) { - case PRV: - case PSV: - case PBV: - x = x * Ucf[PRESSURE]; - break; - case FCV: - x = x * Ucf[FLOW]; - break; - default: - break; - } - break; - case r_FILLTIME: - if (i <= Njuncs) - return (0); - j = i - Njuncs; - if (Tank[j].A == 0.0) - return (0); - if (NodeDemand[i] <= TINY) - return (0); - x = (Tank[j].Vmax - Tank[j].V) / NodeDemand[i]; - break; - case r_DRAINTIME: - if (i <= Njuncs) - return (0); - j = i - Njuncs; - if (Tank[j].A == 0.0) - return (0); - if (NodeDemand[i] >= -TINY) - return (0); - x = (Tank[j].Vmin - Tank[j].V) / NodeDemand[i]; - break; - default: - return (0); - } - switch (p->relop) { - case EQ: - if (ABS(x - p->value) > tol) - return (0); - break; - case NE: - if (ABS(x - p->value) < tol) - return (0); - break; - case LT: - if (x > p->value + tol) - return (0); - break; - case LE: - if (x > p->value - tol) - return (0); - break; - case GT: - if (x < p->value - tol) - return (0); - break; - case GE: - if (x < p->value + tol) - return (0); - break; - } - return (1); + case r_HEAD: + case r_GRADE: + x = hyd->NodeHead[i] * Ucf[HEAD]; + break; + + case r_PRESSURE: + x = (hyd->NodeHead[i] - Node[i].El) * Ucf[PRESSURE]; + break; + + case r_LEVEL: + x = (hyd->NodeHead[i] - Node[i].El) * Ucf[HEAD]; + break; + + case r_FLOW: + x = ABS(LinkFlow[i]) * Ucf[FLOW]; + break; + + case r_SETTING: + if (LinkSetting[i] == MISSING) return 0; + x = LinkSetting[i]; + switch (Link[i].Type) + { + case PRV: + case PSV: + case PBV: + x = x * Ucf[PRESSURE]; + break; + case FCV: + x = x * Ucf[FLOW]; + break; + default: + break; + } + break; + + case r_FILLTIME: + if (i <= Njuncs) return 0; + j = i - Njuncs; + if (Tank[j].A == 0.0) return 0; + if (NodeDemand[i] <= TINY) return 0; + x = (Tank[j].Vmax - Tank[j].V) / NodeDemand[i]; + break; + + case r_DRAINTIME: + if (i <= Njuncs) return 0; + j = i - Njuncs; + if (Tank[j].A == 0.0) return 0; + if (NodeDemand[i] >= -TINY) return 0; + x = (Tank[j].Vmin - Tank[j].V) / NodeDemand[i]; + break; + + default: + return 0; + } + + // Compare value x against the premise + switch (p->relop) + { + case EQ: if (ABS(x - p->value) > tol) return 0; break; + case NE: if (ABS(x - p->value) < tol) return 0; break; + case LT: if (x > p->value + tol) return 0; break; + case LE: if (x > p->value - tol) return 0; break; + case GT: if (x < p->value - tol) return 0; break; + case GE: if (x < p->value + tol) return 0; break; + } + return 1; } -void updateactionlist(EN_Project *pr, int i, Saction *actions) -/* -**--------------------------------------------------- -** Adds rule's actions to action list -**--------------------------------------------------- -*/ +void updateactionlist(Project *pr, int i, Saction *actions) +//--------------------------------------------------- +// Adds rule's actions to action list +//-------------------------------------------------- { - rules_t *rules = &pr->rules; + Rules *rules = &pr->rules; + SactionList *actionItem; Saction *a; @@ -1105,15 +1051,14 @@ void updateactionlist(EN_Project *pr, int i, Saction *actions) } } -int onactionlist(EN_Project *pr, int i, Saction *a) -/* -**----------------------------------------------------------------------------- -** Checks if action a from rule i can be added to the action list -**----------------------------------------------------------------------------- -*/ +int onactionlist(Project *pr, int i, Saction *a) +//----------------------------------------------------------------------------- +// Checks if action a from rule i can be added to the action list +//----------------------------------------------------------------------------- { + Network *net = &pr->network; + int link, i1; - EN_Network *net = &pr->network; SactionList *actionItem; Saction *a1; @@ -1145,194 +1090,140 @@ int onactionlist(EN_Project *pr, int i, Saction *a) return 0; } -int takeactions(EN_Project *pr) -/* -**----------------------------------------------------------- -** Implements actions on action list -**----------------------------------------------------------- -*/ +int takeactions(Project *pr) +//----------------------------------------------------------- +// Implements actions on action list +//----------------------------------------------------------- { + Network *net = &pr->network; + Hydraul *hyd = &pr->hydraul; + Report *rpt = &pr->report; + Rules *rules = &pr->rules; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - report_options_t *rep = &pr->report; - rules_t *rules = &pr->rules; + char flag; + int k, s, n; + double tol = 1.e-3, v, x; + Saction *a; + SactionList *actionItem; - Saction *a; - SactionList *actionItem; - char flag; - int k, s, n; - double tol = 1.e-3, v, x; - - n = 0; - actionItem = rules->ActionList; - while (actionItem != NULL) { - flag = FALSE; - a = actionItem->action; - k = a->link; - s = hyd->LinkStatus[k]; - v = hyd->LinkSetting[k]; - x = a->setting; + n = 0; + actionItem = rules->ActionList; + while (actionItem != NULL) + { + flag = FALSE; + a = actionItem->action; + k = a->link; + s = hyd->LinkStatus[k]; + v = hyd->LinkSetting[k]; + x = a->setting; - /* Switch link from closed to open */ - if (a->status == IS_OPEN && s <= CLOSED) { - setlinkstatus(pr, k, 1, &hyd->LinkStatus[k], &hyd->LinkSetting[k]); - flag = TRUE; + // Switch link from closed to open + if (a->status == IS_OPEN && s <= CLOSED) + { + setlinkstatus(pr, k, 1, &hyd->LinkStatus[k], &hyd->LinkSetting[k]); + flag = TRUE; + } + + // Switch link from not closed to closed + else if (a->status == IS_CLOSED && s > CLOSED) + { + setlinkstatus(pr, k, 0, &hyd->LinkStatus[k], &hyd->LinkSetting[k]); + flag = TRUE; + } + + // Change link's setting + else if (x != MISSING) + { + switch (net->Link[k].Type) + { + case PRV: + case PSV: + case PBV: + x = x / pr->Ucf[PRESSURE]; + break; + case FCV: + x = x / pr->Ucf[FLOW]; + break; + default: + break; + } + if (ABS(x - v) > tol) + { + setlinksetting(pr, k, x, &hyd->LinkStatus[k], + &hyd->LinkSetting[k]); + flag = TRUE; + } + } + + // Report rule action + if (flag == TRUE) + { + n++; + if (rpt->Statflag) + { + writeruleaction(pr, k, net->Rule[actionItem->ruleIndex].label); + } + } + + // Move to next action on list + actionItem = actionItem->next; } - - /* Switch link from not closed to closed */ - else if (a->status == IS_CLOSED && s > CLOSED) { - setlinkstatus(pr, k, 0, &hyd->LinkStatus[k], &hyd->LinkSetting[k]); - flag = TRUE; - } - - /* Change link's setting */ - else if (x != MISSING) { - switch (net->Link[k].Type) { - case PRV: - case PSV: - case PBV: - x = x / pr->Ucf[PRESSURE]; - break; - case FCV: - x = x / pr->Ucf[FLOW]; - break; - default: - break; - } - if (ABS(x - v) > tol) { - setlinksetting(pr, k, x, &hyd->LinkStatus[k], &hyd->LinkSetting[k]); - flag = TRUE; - } - } - - /* Report rule action */ - if (flag == TRUE) { - n++; - if (rep->Statflag) - writeruleaction(pr, k, net->Rule[actionItem->ruleIndex].label); - } - - /* Move to next action on list */ - actionItem = actionItem->next; - } - return (n); + return n; } -void ruleerrmsg(EN_Project *pr) -/* -**----------------------------------------------------------- -** Report a rule parsing error message -**----------------------------------------------------------- -*/ +void clearactionlist(Rules *rules) +//---------------------------------------------------------- +// Clears memory used for action list +//---------------------------------------------------------- { - EN_Network *net = &pr->network; - parser_data_t *par = &pr->parser; - rules_t *rules = &pr->rules; - char **Tok = par->Tok; - - int i; - char label[MAXMSG+1]; - char msg[MAXLINE+1]; - - // Get text of error message - switch (rules->Errcode) + SactionList *nextItem; + SactionList *actionItem; + actionItem = rules->ActionList; + while (actionItem != NULL) { - case 201: - strcpy(msg, R_ERR201); - break; - case 202: - strcpy(msg, R_ERR202); - break; - case 203: - strcpy(msg, R_ERR203); - break; - case 204: - strcpy(msg, R_ERR204); - break; - case 207: - strcpy(msg, R_ERR207); - break; - case 221: - strcpy(msg, R_ERR221); - break; - default: - return; + nextItem = actionItem->next; + free(actionItem); + actionItem = nextItem; } - - // Get label of rule being parsed - if (net->Nrules > 0) - { - strcpy(label, t_RULE); - strcat(label, " "); - strcat(label, net->Rule[net->Nrules].label); - } - else strcpy(label, t_RULES_SECT); - - // Write rule label and error message to status report - sprintf(pr->Msg, "%s", msg); - strcat(pr->Msg, label); - strcat(pr->Msg, ":"); - writeline(pr, pr->Msg); - - // Write text of rule clause being parsed to status report - strcpy(msg, Tok[0]); - for (i = 1; i < par->Ntokens; i++) - { - strcat(msg, " "); - strcat(msg, Tok[i]); - } - writeline(pr, msg); } - -int writerule(EN_Project *pr, FILE *f, int ruleIndex) -//----------------------------------------------------------------------------- -// Write a rule to an INP file. -//----------------------------------------------------------------------------- +void clearrule(Project *pr, int i) +//----------------------------------------------------------- +// Clears memory used by a rule for premises & actions +//----------------------------------------------------------- { - EN_Network *net = &pr->network; - rules_t *rules = &pr->rules; - Srule *rule = &net->Rule[ruleIndex]; - Spremise *p; - Saction *a; + Network *net = &pr->network; - // Write each premise clause to the file - p = rule->Premises; - fprintf(f, "\nIF "); + Spremise *p; + Spremise *pnext; + Saction *a; + Saction *anext; + + p = net->Rule[i].Premises; while (p != NULL) { - writepremise(p, f, net); - p = p->next; - if (p) fprintf(f, "\n%-5s", Ruleword[p->logop]); + pnext = p->next; + free(p); + p = pnext; } - - // Write each THEN action clause to the file - a = rule->ThenActions; - if (a) fprintf(f, "\nTHEN "); + a = net->Rule[i].ThenActions; while (a != NULL) { - writeaction(a, f, net); - a = a->next; - if (a) fprintf(f, "\nAND "); + anext = a->next; + free(a); + a = anext; } - - // Write each ELSE action clause to the file - a = rule->ElseActions; - if (a) fprintf(f, "\nELSE "); + a = net->Rule[i].ElseActions; while (a != NULL) { - writeaction(a, f, net); - a = a->next; - if (a) fprintf(f, "\nAND "); + anext = a->next; + free(a); + a = anext; } - - // Write the rule's priority to the file - if (rule->priority > 0) fprintf(f, "\nPRIORITY %f", rule->priority); - return 0; } -void writepremise(Spremise *p, FILE *f, EN_Network *net) + +void writepremise(Spremise *p, FILE *f, Network *net) //----------------------------------------------------------------------------- // Write a rule's premise clause to an INP file. //----------------------------------------------------------------------------- @@ -1385,8 +1276,7 @@ void writepremise(Spremise *p, FILE *f, EN_Network *net) Operator[p->relop], s_value); } - -void writeaction(Saction *a, FILE *f, EN_Network *net) +void writeaction(Saction *a, FILE *f, Network *net) //----------------------------------------------------------------------------- // Write a rule's action clause to an INP file. //----------------------------------------------------------------------------- @@ -1456,5 +1346,3 @@ void gettimetxt(double secs, char *timetxt) sprintf(timetxt, "%d:%02d:%02d", hours, minutes, seconds); } } - -/***************** END OF RULES.C ******************/ diff --git a/src/smatrix.c b/src/smatrix.c index 8518486..f81f3dc 100755 --- a/src/smatrix.c +++ b/src/smatrix.c @@ -1,37 +1,21 @@ /* -******************************************************************* - -SMATRIX.C -- Sparse matrix routines for EPANET program. - -VERSION: 2.00 -DATE: 5/8/00 -AUTHOR: L. Rossman - US EPA - NRMRL - -This module contains the sparse matrix routines used to solve -a network's hydraulic equations. The entry points into this -module are: - createsparse() -- called from openhyd() in HYDRAUL.C - freesparse() -- called from closehyd() in HYDRAUL.C - linsolve() -- called from netsolve() in HYDRAUL.C - -createsparse() does the following: - 1. for each node, builds an adjacency list that identifies - all links connected to the node (see buildlists()) - 2. re-orders the network's nodes to minimize the number - of non-zero entries in the hydraulic solution matrix - (see reorder()) - 3. symbolically factorizes the solution matrix - (see factorize()) - 4. converts the adjacency lists into a compact scheme - for storing the non-zero coeffs. in the lower diagonal - portion of the solution matrix (see storesparse()) - -freesparse() frees the memory used for the sparse matrix. - -linsolve() solves the linearized system of hydraulic equations. - -******************************************************************** + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: smatrix.c + Description: solves a sparse set of linear equations + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** +*/ +/* + This module contains the sparse matrix routines used to solve a network's + hydraulic equations. The functions exported by this module are: + createsparse() -- called from openhyd() in HYDRAUL.C + freesparse() -- called from closehyd() in HYDRAUL.C + linsolve() -- called from netsolve() in HYDRAUL.C */ #include @@ -44,7 +28,7 @@ linsolve() solves the linearized system of hydraulic equations. #include #include -#include +#include //For optional timer macros #include "text.h" #include "types.h" @@ -55,22 +39,25 @@ extern int genmmd(int *neqns, int *xadj, int *adjncy, int *invp, int *perm, int *delta, int *dhead, int *qsize, int *llist, int *marker, int *maxint, int *nofsub); +// Exported functions +int createsparse(Project *); +void freesparse(Project *); +int linsolve(Smatrix *, int); // Local functions -static int allocsparse(EN_Project *pr); -static int buildlists(EN_Project *pr, int); -static int paralink(EN_Project *pr, int, int, int); -static void xparalinks(EN_Project *pr); -static void freelists(EN_Project *pr); -static void countdegree(EN_Project *pr); -static int reordernodes(EN_Project *pr); -static int factorize(EN_Project *pr); -static int growlist(EN_Project *pr, int); -static int newlink(EN_Project *pr, Padjlist); -static int linked(EN_Network *net, int, int); -static int addlink(EN_Network *net, int, int, int); -static int storesparse(EN_Project *pr, int); -static int sortsparse(EN_Project *pr, int); +static int allocsmatrix(Smatrix *, int, int); +static int alloclinsolve(Smatrix *, int); +static int localadjlists(Network *, Smatrix *); +static int paralink(Network *, Smatrix *, int, int, int k); +static void xparalinks(Network *); +static int reordernodes(Project *); +static int factorize(Project *); +static int growlist(Project *, int); +static int newlink(Project *, Padjlist); +static int linked(Network *, int, int); +static int addlink(Network *, int, int, int); +static int storesparse(Project *, int); +static int sortsparse(Smatrix *, int); static void transpose(int, int *, int *, int *, int *, int *, int *, int *); @@ -91,7 +78,7 @@ static void transpose(int, int *, int *, int *, int *, // timer SmatrixTimer; -int createsparse(EN_Project *pr) +int createsparse(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -100,89 +87,109 @@ int createsparse(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Smatrix *sm = &pr->hydraul.smatrix; + int errcode = 0; - - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *solver = &pr->hydraulics.solver; // cleartimer(SmatrixTimer); // starttimer(SmatrixTimer); + // Allocate sparse matrix data structures + errcode = allocsmatrix(sm, net->Nnodes, net->Nlinks); + if (errcode) return errcode; - /* Allocate data structures */ - ERRCODE(allocsparse(pr)); - - if (errcode) { - return(errcode); - } - - /* Build node-link adjacency lists with parallel links removed. */ - solver->Degree = (int *) calloc(net->Nnodes+1, sizeof(int)); - ERRCODE(MEMCHECK(solver->Degree)); - ERRCODE(buildlists(pr, TRUE)); - if (!errcode) - { - xparalinks(pr); // Remove parallel links - countdegree(pr); // Find degree of each junction - } // (= # of adjacent links) + // Build a local version of node-link adjacency lists + // with parallel links removed + errcode = localadjlists(net, sm); + if (errcode) return errcode; // Re-order nodes to minimize number of non-zero coeffs. - // in factorized solution matrix. - hyd->Ncoeffs = net->Nlinks; + // in factorized solution matrix ERRCODE(reordernodes(pr)); // Factorize solution matrix by updating adjacency lists - // with non-zero connections due to fill-ins. + // with non-zero connections due to fill-ins + sm->Ncoeffs = net->Nlinks; ERRCODE(factorize(pr)); // Allocate memory for sparse storage of positions of non-zero - // coeffs. and store these positions in vector NZSUB. + // coeffs. and store these positions in vector NZSUB ERRCODE(storesparse(pr, net->Njuncs)); - // Free memory used for adjacency lists and sort - // row indexes in NZSUB to optimize linsolve(). - if (!errcode) { - freelists(pr); - } - ERRCODE(sortsparse(pr, net->Njuncs)); + // Free memory used for local adjacency lists and sort + // row indexes in NZSUB to optimize linsolve() + freeadjlists(net); + ERRCODE(sortsparse(sm, net->Njuncs)); - // Re-build adjacency lists without removing parallel - // links for use in future connectivity checking. - ERRCODE(buildlists(pr,FALSE)); + // Allocate memory used by linear eqn. solver + ERRCODE(alloclinsolve(sm, net->Nnodes)); - // Free allocated memory - free(solver->Degree); - return(errcode); -} /* End of createsparse */ + // Re-build adjacency lists for future use + ERRCODE(buildadjlists(net)); + return errcode; +} -int allocsparse(EN_Project *pr) +int allocsmatrix(Smatrix *sm, int Nnodes, int Nlinks) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code -** Purpose: allocates memory for indexing the solution matrix +** Purpose: allocates memory for representing a sparse matrix **-------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - solver_t *solver = &pr->hydraulics.solver; - int errcode = 0; - net->Adjlist = (Padjlist *) calloc(net->Nnodes+1, sizeof(Padjlist)); - solver->Order = (int *) calloc(net->Nnodes+1, sizeof(int)); - solver->Row = (int *) calloc(net->Nnodes+1, sizeof(int)); - solver->Ndx = (int *) calloc(net->Nlinks+1, sizeof(int)); - ERRCODE(MEMCHECK(net->Adjlist)); - ERRCODE(MEMCHECK(solver->Order)); - ERRCODE(MEMCHECK(solver->Row)); - ERRCODE(MEMCHECK(solver->Ndx)); - return(errcode); + + // Memory for linear eqn. solver allocated in alloclinsolve(). + sm->Aij = NULL; + sm->Aii = NULL; + sm->F = NULL; + sm->temp = NULL; + sm->link = NULL; + sm->first = NULL; + + // Memory for representing sparse matrix data structure + sm->Order = (int *) calloc(Nnodes+1, sizeof(int)); + sm->Row = (int *) calloc(Nnodes+1, sizeof(int)); + sm->Ndx = (int *) calloc(Nlinks+1, sizeof(int)); + ERRCODE(MEMCHECK(sm->Order)); + ERRCODE(MEMCHECK(sm->Row)); + ERRCODE(MEMCHECK(sm->Ndx)); + return errcode; } -void freesparse(EN_Project *pr) +int alloclinsolve(Smatrix *sm, int n) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: allocates memory used by linear eqn. solver. +**-------------------------------------------------------------- +*/ +{ + int errcode = 0; + n = n + 1; // All arrays are 1-based + + sm->Aij = (double *)calloc(sm->Ncoeffs + 1, sizeof(double)); + sm->Aii = (double *)calloc(n, sizeof(double)); + sm->F = (double *)calloc(n, sizeof(double)); + sm->temp = (double *)calloc(n, sizeof(double)); + sm->link = (int *)calloc(n, sizeof(int)); + sm->first = (int *)calloc(n, sizeof(int)); + ERRCODE(MEMCHECK(sm->Aij)); + ERRCODE(MEMCHECK(sm->Aii)); + ERRCODE(MEMCHECK(sm->F)); + ERRCODE(MEMCHECK(sm->temp)); + ERRCODE(MEMCHECK(sm->link)); + ERRCODE(MEMCHECK(sm->first)); + return errcode; +} + + +void freesparse(Project *pr) /* **---------------------------------------------------------------- ** Input: None @@ -191,50 +198,56 @@ void freesparse(EN_Project *pr) **---------------------------------------------------------------- */ { - EN_Network *net = &pr->network; - solver_t *solver = &pr->hydraulics.solver; + Network *net = &pr->network; + Smatrix *sm = &pr->hydraul.smatrix; // stoptimer(SmatrixTimer); // printf("\n"); // printf("\n Processing Time = %7.3f s", gettimer(SmatrixTimer)); // printf("\n"); - freelists(pr); - FREE(net->Adjlist); - FREE(solver->Order); - FREE(solver->Row); - FREE(solver->Ndx); - FREE(solver->XLNZ); - FREE(solver->NZSUB); - FREE(solver->LNZ); -} /* End of freesparse */ + FREE(sm->Order); + FREE(sm->Row); + FREE(sm->Ndx); + FREE(sm->XLNZ); + FREE(sm->NZSUB); + FREE(sm->LNZ); + + FREE(sm->Aij); + FREE(sm->Aii); + FREE(sm->F); + FREE(sm->temp); + FREE(sm->link); + FREE(sm->first); +} -int buildlists(EN_Project *pr, int paraflag) +int localadjlists(Network *net, Smatrix *sm) /* **-------------------------------------------------------------- -** Input: paraflag = TRUE if list marks parallel links -** Output: returns error code -** Purpose: builds linked list of links adjacent to each node +** Input: none +** Output: returns error code +** Purpose: builds linked list of non-parallel links adjacent to each node **-------------------------------------------------------------- */ { - int i,j,k; - int pmark = 0; + int i, j, k; + int pmark = 0; // parallel link marker int errcode = 0; Padjlist alink; - - EN_Network *net = &pr->network; + + // Create an array of adjacency lists + freeadjlists(net); + net->Adjlist = (Padjlist *)calloc(net->Nnodes + 1, sizeof(Padjlist)); + if (net->Adjlist == NULL) return 101; // For each link, update adjacency lists of its end nodes - for (k=1; k <= net->Nlinks; k++) + for (k = 1; k <= net->Nlinks; k++) { i = net->Link[k].N1; j = net->Link[k].N2; - if (paraflag) { - pmark = paralink(pr, i, j, k); // Parallel link check - } - + pmark = paralink(net, sm, i, j, k); // Parallel link check + // Include link in start node i's list alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); if (alink == NULL) return(101); @@ -253,55 +266,57 @@ int buildlists(EN_Project *pr, int paraflag) alink->next = net->Adjlist[j]; net->Adjlist[j] = alink; } - return(errcode); -} /* End of buildlists */ + + // Remove parallel links from adjacency lists + xparalinks(net); + return errcode; +} -int paralink(EN_Project *pr, int i, int j, int k) +int paralink(Network *net, Smatrix *sm, int i, int j, int k) /* **-------------------------------------------------------------- -** Input: i = index of start node of link -** j = index of end node of link -** k = link index +** Input: i = index of start node of link +** j = index of end node of link +** k = link index ** Output: returns 1 if link k parallels another link, else 0 -** Purpose: checks for parallel links between nodes i and j -** +** Purpose: checks for parallel links between nodes i and j +** **-------------------------------------------------------------- */ { Padjlist alink; - for (alink = pr->network.Adjlist[i]; alink != NULL; alink = alink->next) + for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) { // Link || to k (same end nodes) if (alink->node == j) { // Assign Ndx entry to this link - pr->hydraulics.solver.Ndx[k] = alink->link; + sm->Ndx[k] = alink->link; return(1); } } // Ndx entry if link not parallel - pr->hydraulics.solver.Ndx[k] = k; + sm->Ndx[k] = k; return(0); -} /* End of paralink */ +} -void xparalinks(EN_Project *pr) +void xparalinks(Network *net) /* **-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: removes parallel links from nodal adjacency lists +** Input: none +** Output: none +** Purpose: removes parallel links from nodal adjacency lists **-------------------------------------------------------------- */ { int i; Padjlist alink, // Current item in adjacency list blink; // Previous item in adjacency list - EN_Network *net = &pr->network; // Scan adjacency list of each node - for (i=1; i <= net->Nnodes; i++) + for (i = 1; i <= net->Nnodes; i++) { alink = net->Adjlist[i]; // First item in list blink = NULL; @@ -311,81 +326,28 @@ void xparalinks(EN_Project *pr) { if (blink == NULL) // This holds at start of list { - net->Adjlist[i] = alink->next; - free(alink); // Remove item from list - alink = net->Adjlist[i]; + net->Adjlist[i] = alink->next; + free(alink); // Remove item from list + alink = net->Adjlist[i]; } else // This holds for interior of list { - blink->next = alink->next; - free(alink); // Remove item from list - alink = blink->next; + blink->next = alink->next; + free(alink); // Remove item from list + alink = blink->next; } - } - else - { - blink = alink; // Move to next item in list - alink = alink->next; - } - } - } -} /* End of xparalinks */ - - -void freelists(EN_Project *pr) -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: frees memory used for nodal adjacency lists -**-------------------------------------------------------------- -*/ -{ - int i; - Padjlist alink; - EN_Network *net = &pr->network; - - - for (i=0; i <= net->Nnodes; i++) - { - for (alink = net->Adjlist[i]; alink != NULL; alink = net->Adjlist[i]) - { - net->Adjlist[i] = alink->next; - free(alink); - } - } -} /* End of freelists */ - - -void countdegree(EN_Project *pr) -/* -**---------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: counts number of nodes directly connected to each node -**---------------------------------------------------------------- -*/ -{ - int i; - Padjlist alink; - EN_Network *net = &pr->network; - - memset(pr->hydraulics.solver.Degree, 0, (net->Nnodes+1) * sizeof(int)); - - // NOTE: For purposes of node re-ordering, Tanks (nodes with - // indexes above Njuncs) have zero degree of adjacency. - - for (i=1; i <= net->Njuncs; i++) { - for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) { - if (alink->node > 0) { - pr->hydraulics.solver.Degree[i]++; + } + else + { + blink = alink; // Move to next item in list + alink = alink->next; } } } } -int reordernodes(EN_Project *pr) +int reordernodes(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -395,15 +357,15 @@ int reordernodes(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Smatrix *sm = &pr->hydraul.smatrix; + int k, knode, m, njuncs, nlinks; int delta = -1; int nofsub = 0; int maxint = INT_MAX; //defined in limits.h int errcode; - - EN_Network *net = &pr->network; - solver_t *solver = &pr->hydraulics.solver; - Padjlist alink; + Padjlist alink; // Local versions of node adjacency lists int *adjncy = NULL; @@ -416,10 +378,10 @@ int reordernodes(EN_Project *pr) int *marker = NULL; // Default ordering - for (k=1; k <= net->Nnodes; k++) + for (k = 1; k <= net->Nnodes; k++) { - solver->Row[k] = k; - solver->Order[k] = k; + sm->Row[k] = k; + sm->Order[k] = k; } njuncs = net->Njuncs; nlinks = net->Nlinks; @@ -441,7 +403,7 @@ int reordernodes(EN_Project *pr) for (alink = net->Adjlist[k]; alink != NULL; alink = alink->next) { knode = alink->node; - if (knode <= njuncs) + if (knode > 0 && knode <= njuncs) { adjncy[m] = knode; m++; @@ -451,7 +413,7 @@ int reordernodes(EN_Project *pr) } // Generate a multiple minimum degree node re-ordering - genmmd(&njuncs, xadj, adjncy, solver->Row, solver->Order, &delta, + genmmd(&njuncs, xadj, adjncy, sm->Row, sm->Order, &delta, dhead, qsize, llist, marker, &maxint, &nofsub); errcode = 0; } @@ -465,10 +427,10 @@ int reordernodes(EN_Project *pr) FREE(llist); FREE(marker); return errcode; -} /* End of reordernodes */ +} -int factorize(EN_Project *pr) +int factorize(Project *pr) /* **-------------------------------------------------------------- ** Input: none @@ -478,29 +440,47 @@ int factorize(EN_Project *pr) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Smatrix *sm = &pr->hydraul.smatrix; + int k, knode; int errcode = 0; - EN_Network *net = &pr->network; - solver_t *solver = &pr->hydraulics.solver; + Padjlist alink; + + // Find degree of each junction node + sm->Degree = (int *)calloc(net->Nnodes + 1, sizeof(int)); + if (sm->Degree == NULL) return 101; + + // NOTE: For purposes of node re-ordering, Tanks (nodes with + // indexes above Njuncs) have zero degree of adjacency. + + for (k = 1; k <= net->Njuncs; k++) + { + for (alink = net->Adjlist[k]; alink != NULL; alink = alink->next) + { + if (alink->node > 0) sm->Degree[k]++; + } + } // Augment each junction's adjacency list to account for // new connections created when solution matrix is solved. // NOTE: Only junctions (indexes <= Njuncs) appear in solution matrix. - for (k = 1; k <= net->Njuncs; k++) // Examine each junction + for (k = 1; k <= net->Njuncs; k++) // Examine each junction { - knode = solver->Order[k]; // Re-ordered index - if (!growlist(pr, knode)) // Augment adjacency list + knode = sm->Order[k]; // Re-ordered index + if (!growlist(pr, knode)) // Augment adjacency list { errcode = 101; break; } - solver->Degree[knode] = 0; // In-activate node + sm->Degree[knode] = 0; // In-activate node } - return(errcode); -} /* End of factorize */ + free(sm->Degree); + return errcode; +} -int growlist(EN_Project *pr, int knode) +int growlist(Project *pr, int knode) /* **-------------------------------------------------------------- ** Input: knode = node index @@ -511,28 +491,30 @@ int growlist(EN_Project *pr, int knode) **-------------------------------------------------------------- */ { - int node; - Padjlist alink; - EN_Network *net = &pr->network; - solver_t *solver = &pr->hydraulics.solver; + Network *net = &pr->network; + Smatrix *sm = &pr->hydraul.smatrix; + int node; + Padjlist alink; + // Iterate through all nodes connected to knode for (alink = net->Adjlist[knode]; alink != NULL; alink = alink -> next) { - node = alink->node; // End node of connecting link - if (solver->Degree[node] > 0) // End node is active + node = alink->node; // End node of connecting link + if (node > 0 && sm->Degree[node] > 0) // End node is active { - solver->Degree[node]--; // Reduce degree of adjacency - if (!newlink(pr, alink)) { // Add to adjacency list - return(0); + sm->Degree[node]--; // Reduce degree of adjacency + if (!newlink(pr, alink)) // Add to adjacency list + { + return 0; } } } - return(1); -} /* End of growlist */ + return 1; +} -int newlink(EN_Project *pr, Padjlist alink) +int newlink(Project *pr, Padjlist alink) /* **-------------------------------------------------------------- ** Input: alink = element of node's adjacency list @@ -542,12 +524,12 @@ int newlink(EN_Project *pr, Padjlist alink) **-------------------------------------------------------------- */ { - int inode, jnode; - Padjlist blink; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *solver = &pr->hydraulics.solver; + Network *net = &pr->network; + Smatrix *sm = &pr->hydraul.smatrix; + int inode, jnode; + Padjlist blink; + // Scan all entries in adjacency list that follow anode. inode = alink->node; // End node of connection to anode for (blink = alink->next; blink != NULL; blink = blink->next) @@ -556,28 +538,28 @@ int newlink(EN_Project *pr, Padjlist alink) // If jnode still active, and inode not connected to jnode, // then add a new connection between inode and jnode. - if (solver->Degree[jnode] > 0) // jnode still active + if (jnode > 0 && sm->Degree[jnode] > 0) // jnode still active { - if (!linked(net, inode, jnode)) // inode not linked to jnode + if (!linked(net, inode, jnode)) // inode not linked to jnode { // Since new connection represents a non-zero coeff. // in the solution matrix, update the coeff. count. - hyd->Ncoeffs++; + sm->Ncoeffs++; // Update adjacency lists for inode & jnode to // reflect the new connection. - if (!addlink(net, inode, jnode, hyd->Ncoeffs)) return(0); - if (!addlink(net, jnode, inode, hyd->Ncoeffs)) return(0); - solver->Degree[inode]++; - solver->Degree[jnode]++; + if (!addlink(net, inode, jnode, sm->Ncoeffs)) return 0; + if (!addlink(net, jnode, inode, sm->Ncoeffs)) return 0; + sm->Degree[inode]++; + sm->Degree[jnode]++; } } } - return(1); -} /* End of newlink */ + return 1; +} -int linked(EN_Network *n, int i, int j) +int linked(Network *net, int i, int j) /* **-------------------------------------------------------------- ** Input: i = node index @@ -588,15 +570,15 @@ int linked(EN_Network *n, int i, int j) */ { Padjlist alink; - for (alink = n->Adjlist[i]; alink != NULL; alink = alink->next) + for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) { - if (alink->node == j) return(1); + if (alink->node == j) return 1; } - return(0); -} /* End of linked */ + return 0; +} -int addlink(EN_Network *net, int i, int j, int n) +int addlink(Network *net, int i, int j, int n) /* **-------------------------------------------------------------- ** Input: i = node index @@ -609,16 +591,16 @@ int addlink(EN_Network *net, int i, int j, int n) { Padjlist alink; alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); - if (alink == NULL) return(0); + if (alink == NULL) return 0; alink->node = j; alink->link = n; alink->next = net->Adjlist[i]; net->Adjlist[i] = alink; - return(1); -} /* End of addlink */ + return 1; +} -int storesparse(EN_Project *pr, int n) +int storesparse(Project *pr, int n) /* **-------------------------------------------------------------- ** Input: n = number of rows in solution matrix @@ -628,49 +610,49 @@ int storesparse(EN_Project *pr, int n) **-------------------------------------------------------------- */ { + Network *net = &pr->network; + Smatrix *sm = &pr->hydraul.smatrix; + + int i, ii, j, k, l, m; + int errcode = 0; Padjlist alink; - int i, ii, j, k, l, m; - int errcode = 0; - EN_Network *net = &pr->network; - hydraulics_t *hyd = &pr->hydraulics; - solver_t *solver = &pr->hydraulics.solver; - - /* Allocate sparse matrix storage */ - solver->XLNZ = (int *) calloc(n+2, sizeof(int)); - solver->NZSUB = (int *) calloc(hyd->Ncoeffs+2, sizeof(int)); - solver->LNZ = (int *) calloc(hyd->Ncoeffs+2, sizeof(int)); - ERRCODE(MEMCHECK(solver->XLNZ)); - ERRCODE(MEMCHECK(solver->NZSUB)); - ERRCODE(MEMCHECK(solver->LNZ)); - if (errcode) return(errcode); + // Allocate sparse matrix storage + sm->XLNZ = (int *) calloc(n+2, sizeof(int)); + sm->NZSUB = (int *) calloc(sm->Ncoeffs+2, sizeof(int)); + sm->LNZ = (int *) calloc(sm->Ncoeffs+2, sizeof(int)); + ERRCODE(MEMCHECK(sm->XLNZ)); + ERRCODE(MEMCHECK(sm->NZSUB)); + ERRCODE(MEMCHECK(sm->LNZ)); + if (errcode) return errcode; // Generate row index pointers for each column of matrix k = 0; - solver->XLNZ[1] = 1; - for (i=1; i<=n; i++) // column + sm->XLNZ[1] = 1; + for (i = 1; i <= n; i++) // column { m = 0; - ii = solver->Order[i]; + ii = sm->Order[i]; for (alink = net->Adjlist[ii]; alink != NULL; alink = alink->next) { - j = solver->Row[alink->node]; // row + if (alink->node == 0) continue; + j = sm->Row[alink->node]; // row l = alink->link; if (j > i && j <= n) { m++; k++; - solver->NZSUB[k] = j; - solver->LNZ[k] = l; + sm->NZSUB[k] = j; + sm->LNZ[k] = l; } } - solver->XLNZ[i+1] = solver->XLNZ[i] + m; + sm->XLNZ[i+1] = sm->XLNZ[i] + m; } - return(errcode); -} /* End of storesparse */ + return errcode; +} -int sortsparse(EN_Project *pr, int n) +int sortsparse(Smatrix *sm, int n) /* **-------------------------------------------------------------- ** Input: n = number of rows in solution matrix @@ -682,17 +664,14 @@ int sortsparse(EN_Project *pr, int n) int i, k; int *xlnzt, *nzsubt, *lnzt, *nzt; int errcode = 0; - - hydraulics_t *hyd = &pr->hydraulics; - solver_t *solver = &pr->hydraulics.solver; - - int *LNZ = solver->LNZ; - int *XLNZ = solver->XLNZ; - int *NZSUB = solver->NZSUB; + + int *LNZ = sm->LNZ; + int *XLNZ = sm->XLNZ; + int *NZSUB = sm->NZSUB; xlnzt = (int *) calloc(n+2, sizeof(int)); - nzsubt = (int *) calloc(hyd->Ncoeffs+2, sizeof(int)); - lnzt = (int *) calloc(hyd->Ncoeffs+2, sizeof(int)); + nzsubt = (int *) calloc(sm->Ncoeffs+2, sizeof(int)); + lnzt = (int *) calloc(sm->Ncoeffs+2, sizeof(int)); nzt = (int *) calloc(n+2, sizeof(int)); ERRCODE(MEMCHECK(xlnzt)); ERRCODE(MEMCHECK(nzsubt)); @@ -701,13 +680,13 @@ int sortsparse(EN_Project *pr, int n) if (!errcode) { // Count # non-zeros in each row - for (i=1; i<=n; i++) nzt[i] = 0; - for (i=1; i<=n; i++) + for (i = 1; i <= n; i++) nzt[i] = 0; + for (i = 1; i <= n; i++) { for (k = XLNZ[i]; k < XLNZ[i+1]; k++) nzt[NZSUB[k]]++; } xlnzt[1] = 1; - for (i=1; i<=n; i++) xlnzt[i+1] = xlnzt[i] + nzt[i]; + for (i = 1; i <= n; i++) xlnzt[i+1] = xlnzt[i] + nzt[i]; // Transpose matrix twice to order column indexes transpose(n, XLNZ, NZSUB, LNZ, xlnzt, nzsubt, lnzt, nzt); @@ -715,12 +694,12 @@ int sortsparse(EN_Project *pr, int n) } // Reclaim memory - FREE(xlnzt); - FREE(nzsubt); - FREE(lnzt); - FREE(nzt); - return(errcode); -} /* End of sortsparse */ + free(xlnzt); + free(nzsubt); + free(lnzt); + free(nzt); + return errcode; +} void transpose(int n, int *il, int *jl, int *xl, int *ilt, int *jlt, @@ -737,10 +716,10 @@ void transpose(int n, int *il, int *jl, int *xl, int *ilt, int *jlt, { int i, j, k, kk; - for (i=1; i<=n; i++) nzt[i] = 0; - for (i=1; i<=n; i++) + for (i = 1; i <= n; i++) nzt[i] = 0; + for (i = 1; i <= n; i++) { - for (k=il[i]; kF = solution values +** Output: sm->F = solution values ** returns 0 if solution found, or index of ** equation causing system to be ill-conditioned ** Purpose: solves sparse symmetric system of linear @@ -778,47 +757,35 @@ int linsolve(EN_Project *pr, int n) **-------------------------------------------------------------- */ { - solver_t *solver = &pr->hydraulics.solver; - double *Aii = solver->Aii; - double *Aij = solver->Aij; - double *B = solver->F; - int *LNZ = solver->LNZ; - int *XLNZ = solver->XLNZ; - int *NZSUB = solver->NZSUB; + double *Aii = sm->Aii; + double *Aij = sm->Aij; + double *B = sm->F; + double *temp = sm->temp; + int *LNZ = sm->LNZ; + int *XLNZ = sm->XLNZ; + int *NZSUB = sm->NZSUB; + int *link = sm->link; + int *first = sm->first; - int *link, *first; - int i, istop, istrt, isub, j, k, kfirst, newk; - int errcode = 0; - double bj, diagj, ljk; - double *temp; + int i, istop, istrt, isub, j, k, kfirst, newk; + double bj, diagj, ljk; - temp = (double *) calloc(n+1, sizeof(double)); - link = (int *) calloc(n+1,sizeof(int)); - first = (int *) calloc(n+1,sizeof(int)); - ERRCODE(MEMCHECK(temp)); - ERRCODE(MEMCHECK(link)); - ERRCODE(MEMCHECK(first)); - if (errcode) - { - errcode = -errcode; - goto ENDLINSOLVE; - } - memset(temp,0,(n+1)*sizeof(double)); - memset(link,0,(n+1)*sizeof(int)); + memset(temp, 0, (n + 1) * sizeof(double)); + memset(link, 0, (n + 1) * sizeof(int)); + memset(first, 0, (n + 1) * sizeof(int)); - /* Begin numerical factorization of matrix A into L */ - /* Compute column L(*,j) for j = 1,...n */ - for (j=1; j<=n; j++) + // Begin numerical factorization of matrix A into L + // Compute column L(*,j) for j = 1,...n + for (j = 1; j <= n; j++) { - /* For each column L(*,k) that affects L(*,j): */ + // For each column L(*,k) that affects L(*,j): diagj = 0.0; newk = link[j]; k = newk; while (k != 0) { - - /* Outer product modification of L(*,j) by */ - /* L(*,k) starting at first[k] of L(*,k). */ + // Outer product modification of L(*,j) by + // L(*,k) starting at first[k] of L(*,k) newk = link[k]; kfirst = first[k]; ljk = Aij[LNZ[kfirst]]; @@ -828,15 +795,15 @@ int linsolve(EN_Project *pr, int n) if (istop >= istrt) { - /* Before modification, update vectors 'first' */ - /* and 'link' for future modification steps. */ + // Before modification, update vectors 'first' + // and 'link' for future modification steps first[k] = istrt; isub = NZSUB[istrt]; link[k] = link[isub]; link[isub] = k; - /* The actual mod is saved in vector 'temp'. */ - for (i=istrt; i<=istop; i++) + // The actual mod is saved in vector 'temp' + for (i = istrt; i <= istop; i++) { isub = NZSUB[i]; temp[isub] += Aij[LNZ[i]]*ljk; @@ -845,13 +812,12 @@ int linsolve(EN_Project *pr, int n) k = newk; } - /* Apply the modifications accumulated */ - /* in 'temp' to column L(*,j). */ + // Apply the modifications accumulated + // in 'temp' to column L(*,j) diagj = Aii[j] - diagj; - if (diagj <= 0.0) /* Check for ill-conditioning */ + if (diagj <= 0.0) // Check for ill-conditioning { - errcode = j; - goto ENDLINSOLVE; + return j; } diagj = sqrt(diagj); Aii[j] = diagj; @@ -863,7 +829,7 @@ int linsolve(EN_Project *pr, int n) isub = NZSUB[istrt]; link[j] = link[isub]; link[isub] = j; - for (i=istrt; i<=istop; i++) + for (i = istrt; i <= istop; i++) { isub = NZSUB[i]; bj = (Aij[LNZ[i]] - temp[isub])/diagj; @@ -871,10 +837,10 @@ int linsolve(EN_Project *pr, int n) temp[isub] = 0.0; } } - } /* next j */ + } // next j - /* Foward substitution */ - for (j=1; j<=n; j++) + // Foward substitution + for (j = 1; j <= n; j++) { bj = B[j]/Aii[j]; B[j] = bj; @@ -882,7 +848,7 @@ int linsolve(EN_Project *pr, int n) istop = XLNZ[j+1] - 1; if (istop >= istrt) { - for (i=istrt; i<=istop; i++) + for (i = istrt; i <= istop; i++) { isub = NZSUB[i]; B[isub] -= Aij[LNZ[i]]*bj; @@ -890,15 +856,15 @@ int linsolve(EN_Project *pr, int n) } } - /* Backward substitution */ - for (j=n; j>=1; j--) + // Backward substitution + for (j = n; j >= 1; j--) { bj = B[j]; istrt = XLNZ[j]; istop = XLNZ[j+1] - 1; if (istop >= istrt) { - for (i=istrt; i<=istop; i++) + for (i = istrt; i <= istop; i++) { isub = NZSUB[i]; bj -= Aij[LNZ[i]]*B[isub]; @@ -906,14 +872,5 @@ int linsolve(EN_Project *pr, int n) } B[j] = bj/Aii[j]; } - -ENDLINSOLVE: - free(temp); - free(link); - free(first); - return(errcode); -} /* End of linsolve */ - - -/************************ END OF SMATRIX.C ************************/ - + return 0; +} diff --git a/src/text.h b/src/text.h index b428be1..9d25813 100755 --- a/src/text.h +++ b/src/text.h @@ -1,22 +1,21 @@ /* -**************************************************** - - String Constants for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 10/25/00 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -**************************************************** + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: text.h + Description: string constants used throughout EPANET + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ -/* ------------ Keyword Dictionary ---------- */ + #ifndef TEXT_H #define TEXT_H +//------- Keyword Dictionary ------------------------------ + #define w_USE "USE" #define w_SAVE "SAVE" @@ -194,7 +193,8 @@ AUTHOR: L. Rossman #define w_ELSE "ELSE" #define w_PRIORITY "PRIO" -/* ---------Input Section Names ---------- */ +// ------ Input File Section Names ------------------------ + #define s_TITLE "[TITLE]" #define s_JUNCTIONS "[JUNCTIONS]" #define s_RESERVOIRS "[RESERVOIRS]" @@ -225,8 +225,8 @@ AUTHOR: L. Rossman #define s_TAGS "[TAGS]" #define s_END "[END]" -/* ---------------- Units ---------------- */ -/*** Limit units to MAXID or less characters ***/ +//------- Units ------------------------------------------- + #define u_CFS "cfs" #define u_GPM "gpm" #define u_AFD "a-f/d" @@ -257,13 +257,15 @@ AUTHOR: L. Rossman #define u_per1000FT "/1000ft" #define u_HP "hp" -/* -------------- Curve Types ----------------- */ +//------- Curve Types ------------------------------------- + #define c_HEADLOSS "HEADLOSS" #define c_PUMP "PUMP" #define c_EFFIC "EFFIC" #define c_VOLUME "VOLUME" -/* ------------------ Text Phrases ------------------- */ +//------- Text Phrases ------------------------------------ + #define t_ABOVE "above" #define t_BELOW "below" #define t_HW "Hazen-Williams" @@ -334,7 +336,8 @@ AUTHOR: L. Rossman #define t_ORIFICE "Orifice Flow" -/* ------------------ Format Messages ------------------*/ +//----- Summary Report Format Strings --------------------- + #define LOGO1 \ "******************************************************************" #define LOGO2 \ @@ -399,10 +402,9 @@ AUTHOR: L. Rossman #define FMT47 " with %s below %-.2f %s" #define FMT48 " with %s above %-.2f %s" -/* ---------- Status Report Format Strings ------------ */ -#define FMT49 "Hydraulic Status:" +//----- Status Report Format Strings ---------------------- -/*** Updated 6/24/02 ***/ +#define FMT49 "Hydraulic Status:" #define FMT50 "%10s: Tank %s is %s at %-.2f %s" #define FMT51 "%10s: Reservoir %s is %s" #define FMT52 "%10s: %s %s %s" @@ -422,13 +424,12 @@ AUTHOR: L. Rossman #define FMT63 "%10s: %s %s changed by rule %s" #define FMT64 "%10s: Balancing the network:\n" #define FMT65 " Trial %2d: relative flow change = %-.6f" -/*** End of update ***/ - #define FMT66 " maximum flow change = %.4f for Link %s" #define FMT67 " maximum flow change = %.4f for Node %s" #define FMT68 " maximum head error = %.4f for Link %s\n" -/* -------------------- Energy Report Table ------------------- */ +//----- Energy Report Table ------------------------------- + #define FMT71 "Energy Usage:" #define FMT72 \ " Usage Avg. Kw-hr Avg. Peak Cost" @@ -437,18 +438,21 @@ AUTHOR: L. Rossman #define FMT74 "%38s Demand Charge: %9.2f" #define FMT75 "%38s Total Cost: %9.2f" -/* -------------------- Node Report Table --------------------- */ +//----- Node Report Table --------------------------------- + #define FMT76 "%s Node Results:" #define FMT77 "Node Results:" #define FMT78 "Node Results at %s hrs:" -/* -------------------- Link Report Table --------------------- */ +//----- Link Report Table --------------------------------- + #define FMT79 "%s Link Results:" #define FMT80 "Link Results:" #define FMT81 "Link Results at %s hrs:" #define FMT82 "\n\f\n Page %-d %60.60s\n" -/* ------------------- Progress Messages ---------------------- */ +//----- Progress Messages --------------------------------- + #define FMT100 " Retrieving network data ... " #define FMT101 " Computing hydraulics at hour %-10s " #define FMT102 " Computing water quality at hour %-10s " @@ -457,16 +461,17 @@ AUTHOR: L. Rossman #define FMT104 "Analysis begun %s" #define FMT105 "Analysis ended %s" -/*------------------- Error Messages --------------------*/ +//----- Rule Error Messages ------------------------------- + #define R_ERR201 "Input Error 201: syntax error in following line of " #define R_ERR202 "Input Error 202: illegal numeric value in following line of " #define R_ERR203 "Input Error 203: undefined node in following line of " #define R_ERR204 "Input Error 204: undefined link in following line of " #define R_ERR207 "Input Error 207: attempt to control a CV in following line of " - #define R_ERR221 "Input Error 221: mis-placed clause in following line of " -/*-------------------- Specific Warning Messages -------------------------*/ +//----- Specific Warning Messages ------------------------- + #define WARN01 "WARNING: System unbalanced at %s hrs." #define WARN02 \ "WARNING: Maximum trials exceeded at %s hrs. System may be unstable." @@ -477,7 +482,8 @@ AUTHOR: L. Rossman #define WARN05 "WARNING: %s %s %s at %s hrs." #define WARN06 "WARNING: Negative pressures at %s hrs." -/*-------------------- General Warning Messages -------------------------*/ +//----- General Warning Messages -------------------------- + #define WARN1 "WARNING: System hydraulically unbalanced." #define WARN2 "WARNING: System may be hydraulically unstable." #define WARN3 "WARNING: System disconnected." diff --git a/src/types.h b/src/types.h index 2220c69..f27fd4e 100755 --- a/src/types.h +++ b/src/types.h @@ -1,78 +1,70 @@ /* -*********************************************************************** - -TYPES.H -- Global constants and data types for EPANET program - -VERSION: 2.00 -DATE: 5/8/00 - 9/7/00 - 10/25/00 - 3/1/01 - 12/6/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -********************************************************************** + ****************************************************************************** + Project: OWA EPANET + Version: 2.2 + Module: types.h + Description: symbolic constants and data types used throughout EPANET + Authors: see AUTHORS + Copyright: see AUTHORS + License: see LICENSE + Last Updated: 11/27/2018 + ****************************************************************************** */ + #ifndef TYPES_H #define TYPES_H -//#include "epanet2.h" #include "hash.h" #include - -/*********************************************************/ -/* All floats have been re-declared as doubles (7/3/07). */ -/*********************************************************/ /* ------------------------------------------- Definition of 4-byte integers & reals ------------------------------------------- */ -typedef float REAL4; +typedef float REAL4; typedef int INT4; /* ------------------------------ - Global Constants ------------------------------ +---------------------------------------------- + Various constants +---------------------------------------------- */ -/*** Updated ***/ #define CODEVERSION 20200 #define MAGICNUMBER 516114521 -#define ENGINE_VERSION 201 -#define EOFMARK 0x1A /* Use 0x04 for UNIX systems */ -#define MAXTITLE 3 /* Max. # title lines */ +#define ENGINE_VERSION 201 // Used for binary hydraulics file +#define EOFMARK 0x1A // Use 0x04 for UNIX systems +#define MAXTITLE 3 // Max. # title lines #define TITLELEN 79 // Max. # characters in a title line -#define MAXID 31 /* Max. # characters in ID name */ -#define MAXMSG 255 /* Max. # characters in message text */ -#define MAXLINE 1024 /* Max. # characters read from input line */ -#define MAXFNAME 259 /* Max. # characters in file name */ -#define MAXTOKS 40 /* Max. items per line of input */ -#define TZERO 1.E-4 /* Zero time tolerance */ +#define MAXID 31 // Max. # characters in ID name +#define MAXMSG 255 // Max. # characters in message text +#define MAXLINE 1024 // Max. # characters read from input line +#define MAXFNAME 259 // Max. # characters in file name +#define MAXTOKS 40 // Max. items per line of input +#define TZERO 1.E-4 // Zero time tolerance #define TRUE 1 #define FALSE 0 #define FULL 2 #define BIG 1.E10 #define TINY 1.E-6 -#define MISSING -1.E10 - -#define CBIG 1.e8 /* Big coefficient */ -#define CSMALL 1.e-6 /* Small coefficient */ - +#define MISSING -1.E10 // Missing value indicator +#define DIFFUS 1.3E-8 // Diffusivity of chlorine + // @ 20 deg C (sq ft/sec) +#define VISCOS 1.1E-5 // Kinematic viscosity of water + // @ 20 deg C (sq ft/sec) +#define SEPSTR " \t\n\r" // Token separator characters #ifdef M_PI #define PI M_PI #else #define PI 3.141592654 #endif -/*** Updated 9/7/00 ***/ -/* Various conversion factors */ -#define GPMperCFS 448.831 +/* +---------------------------------------------- + Flow units conversion factors +---------------------------------------------- +*/ +#define GPMperCFS 448.831 #define AFDperCFS 1.9837 #define MGDperCFS 0.64632 #define IMGDperCFS 0.5382 @@ -89,16 +81,9 @@ typedef int INT4; #define KWperHP 0.7457 #define SECperDAY 86400 -#define DIFFUS 1.3E-8 /* Diffusivity of chlorine */ - /* @ 20 deg C (sq ft/sec) */ -#define VISCOS 1.1E-5 /* Kinematic viscosity of water */ - /* @ 20 deg C (sq ft/sec) */ - -#define SEPSTR " \t\n\r" /* Token separator characters */ - /* --------------------------------------------------------------------- - Macro to test for successful allocation of memory + Macros to test for successful allocation of memory and to free it --------------------------------------------------------------------- */ #define MEMCHECK(x) (((x) == NULL) ? 101 : 0 ) @@ -106,826 +91,780 @@ typedef int INT4; /* --------------------------------------------------------------------- - Conversion macros to be used in place of functions + Conversion macros to be used in place of functions --------------------------------------------------------------------- -*/ -#define INT(x) ((int)(x)) /* integer portion of x */ -#define FRAC(x) ((x)-(int)(x)) /* fractional part of x */ -#define ABS(x) (((x)<0) ? -(x) : (x)) /* absolute value of x */ -#define MIN(x,y) (((x)<=(y)) ? (x) : (y)) /* minimum of x and y */ -#define MAX(x,y) (((x)>=(y)) ? (x) : (y)) /* maximum of x and y */ +*/ +#define INT(x) ((int)(x)) // integer portion of x +#define FRAC(x) ((x)-(int)(x)) // fractional part of x +#define ABS(x) (((x)<0) ? -(x) : (x)) // absolute value of x +#define MIN(x,y) (((x)<=(y)) ? (x) : (y)) // minimum of x and y +#define MAX(x,y) (((x)>=(y)) ? (x) : (y)) // maximum of x and y #define ROUND(x) (((x)>=0) ? (int)((x)+.5) : (int)((x)-.5)) - /* round-off of x */ -#define MOD(x,y) ((x)%(y)) /* x modulus y */ -#define SQR(x) ((x)*(x)) /* x-squared */ -#define SGN(x) (((x)<0) ? (-1) : (1)) /* sign of x */ + // round-off of x +#define MOD(x,y) ((x)%(y)) // x modulus y +#define SQR(x) ((x)*(x)) // x-squared +#define SGN(x) (((x)<0) ? (-1) : (1)) // sign of x #define UCHAR(x) (((x) >= 'a' && (x) <= 'z') ? ((x)&~32) : (x)) - /* uppercase char of x */ + // uppercase char of x /* ------------------------------------------------------ Macro to evaluate function x with error checking - (Fatal errors are numbered higher than 100) + (Fatal errors are numbered higher than 100) ------------------------------------------------------ */ -#define ERRCODE(x) (errcode = ((errcode>100) ? (errcode) : (x))) +#define ERRCODE(x) (errcode = ((errcode>100) ? (errcode) : (x))) /* - ---------------------------------------------- - Global Enumeration Types - ---------------------------------------------- +---------------------------------------------- + Enumerated Data Types +---------------------------------------------- */ typedef enum { - NODE = 0, - LINK = 1 + NODE, + LINK } ObjectType; typedef enum { - JUNCTION = 0, - RESERVOIR = 1, - TANK = 2 + JUNCTION, + RESERVOIR, + TANK } NodeType; typedef enum { - CVPIPE = 0, - PIPE = 1, - PUMP = 2, - PRV = 3, - PSV = 4, - PBV = 5, - FCV = 6, - TCV = 7, - GPV = 8 + CVPIPE, // pipe with check valve + PIPE, // pipe + PUMP, // pump + PRV, // pressure reducing valve + PSV, // pressure sustaining valve + PBV, // pressure breaker valve + FCV, // flow control valve + TCV, // throttle control valve + GPV // general purpose valve } LinkType; typedef enum { - USE, /* use from previous run */ - SAVE, /* save after current run */ - SCRATCH /* use temporary file */ + USE, // use hydraulics file from previous run + SAVE, // save hydraulics file after current run + SCRATCH // use temporary hydraulics file } HydFiletype; typedef enum { - NONE, /* no quality analysis */ - CHEM, /* analyze a chemical */ - AGE, /* analyze water age */ - TRACE /* trace % of flow from a source */ + NONE, // no quality analysis + CHEM, // analyze a chemical + AGE, // analyze water age + TRACE // trace % of flow from a source } QualType; typedef enum { - V_CURVE, /* volume curve */ - P_CURVE, /* pump curve */ - E_CURVE, /* efficiency curve */ - H_CURVE, /* head loss curve */ - G_CURVE /* General\default curve */ + V_CURVE, // volume curve + P_CURVE, // pump curve + E_CURVE, // efficiency curve + H_CURVE, // head loss curve + G_CURVE // general\default curve } CurveType; typedef enum { - CONST_HP, /* constant horsepower */ - POWER_FUNC, /* power function */ - CUSTOM, /* user-defined custom curve */ + CONST_HP, // constant horsepower + POWER_FUNC, // power function + CUSTOM, // user-defined custom curve NOCURVE } PumpType; typedef enum { - CONCEN, /* inflow concentration */ - MASS, /* mass inflow booster */ - SETPOINT, /* setpoint booster */ - FLOWPACED /* flow paced booster */ + CONCEN, // inflow concentration + MASS, // mass inflow booster + SETPOINT, // setpoint booster + FLOWPACED // flow paced booster } SourceType; typedef enum { - LOWLEVEL, /* act when grade below set level */ - HILEVEL, /* act when grade above set level */ - TIMER, /* act when set time reached */ - TIMEOFDAY /* act when time of day occurs */ -} ControlType; + LOWLEVEL, // act when grade below set level + HILEVEL, // act when grade above set level + TIMER, // act when set time reached + TIMEOFDAY // act when time of day occurs +} ControlType; typedef enum { - XHEAD, /* pump cannot deliver head (closed) */ - TEMPCLOSED, /* temporarily closed */ - CLOSED, /* closed */ - OPEN, /* open */ - ACTIVE, /* valve active (partially open) */ - XFLOW, /* pump exceeds maximum flow */ - XFCV, /* FCV cannot supply flow */ - XPRESSURE, /* valve cannot supply pressure */ - FILLING, /* tank filling */ - EMPTYING /* tank emptying */ -} StatType; + XHEAD, // pump cannot deliver head (closed) + TEMPCLOSED, // temporarily closed + CLOSED, // closed + OPEN, // open + ACTIVE, // valve active (partially open) + XFLOW, // pump exceeds maximum flow + XFCV, // FCV cannot supply flow + XPRESSURE, // valve cannot supply pressure + FILLING, // tank filling + EMPTYING // tank emptying +} StatusType; typedef enum { - HW, /* Hazen-Williams */ - DW, /* Darcy-Weisbach */ - CM /* Chezy-Manning */ -} HeadLossType; + HW, // Hazen-Williams + DW, // Darcy-Weisbach + CM // Chezy-Manning +} HeadLossType; typedef enum { - US, /* US */ - SI /* SI (metric) */ -} UnitsType; + US, // US + SI // SI (metric) +} UnitsType; typedef enum { - CFS, /* cubic feet per second */ - GPM, /* gallons per minute */ - MGD, /* million gallons per day */ - IMGD, /* imperial million gal. per day */ - AFD, /* acre-feet per day */ - LPS, /* liters per second */ - LPM, /* liters per minute */ - MLD, /* megaliters per day */ - CMH, /* cubic meters per hour */ - CMD /* cubic meters per day */ -} FlowUnitsType; + CFS, // cubic feet per second + GPM, // gallons per minute + MGD, // million gallons per day + IMGD, // imperial million gal. per day + AFD, // acre-feet per day + LPS, // liters per second + LPM, // liters per minute + MLD, // megaliters per day + CMH, // cubic meters per hour + CMD // cubic meters per day +} FlowUnitsType; typedef enum { - PSI, /* pounds per square inch */ - KPA, /* kiloPascals */ - METERS /* meters */ - -} PressUnitsType; -typedef enum { - LOW, /* lower limit */ - HI, /* upper limit */ - PREC /* precision */ - -} RangeType; -typedef enum { - MIX1, /* 1-compartment model */ - MIX2, /* 2-compartment model */ - FIFO, /* First in, first out model */ - LIFO /* Last in, first out model */ -} MixType; + PSI, // pounds per square inch + KPA, // kiloPascals + METERS // meters +} PressureUnitsType; typedef enum { - SERIES, /* none */ - AVG, /* time-averages */ - MIN, /* minimum values */ - MAX, /* maximum values */ - RANGE /* max - min values */ -} TstatType; + LOW, // lower limit + HI, // upper limit + PREC // precision +} RangeType; - -#define MAXVAR 21 /* Max. # types of network variables */ - /* (equals # items enumed below) */ typedef enum { - ELEV = 0, /* nodal elevation */ - DEMAND, /* nodal demand flow */ - HEAD, /* nodal hydraulic head */ - PRESSURE, /* nodal pressure */ - QUALITY, /* nodal water quality */ + MIX1, // complete mix model + MIX2, // 2-compartment model + FIFO, // first in, first out model + LIFO // last in, first out model +} MixType; - LENGTH, /* link length */ - DIAM, /* link diameter */ - FLOW, /* link flow rate */ - VELOCITY, /* link flow velocity */ - HEADLOSS, /* link head loss */ - LINKQUAL, /* avg. water quality in link */ - STATUS, /* link status */ - SETTING, /* pump/valve setting */ - REACTRATE, /* avg. reaction rate in link */ - FRICTION, /* link friction factor */ +typedef enum { + SERIES, // point time series + AVG, // time-averages + MIN, // minimum values + MAX, // maximum values + RANGE // max - min values +} StatisticType; - POWER, /* pump power output */ - TIME, /* simulation time */ - VOLUME, /* tank volume */ - CLOCKTIME, /* simulation time of day */ - FILLTIME, /* time to fill a tank */ - DRAINTIME /* time to drain a tank */ +typedef enum { + ELEV = 0, // nodal elevation + DEMAND, // nodal demand flow + HEAD, // nodal hydraulic head + PRESSURE, // nodal pressure + QUALITY, // nodal water quality + + LENGTH, // link length + DIAM, // link diameter + FLOW, // link flow rate + VELOCITY, // link flow velocity + HEADLOSS, // link head loss + LINKQUAL, // avg. water quality in link + STATUS, // link status + SETTING, // pump/valve setting + REACTRATE, // avg. reaction rate in link + FRICTION, // link friction factor + + POWER, // pump power output + TIME, // simulation time + VOLUME, // tank volume + CLOCKTIME, // simulation time of day + FILLTIME, // time to fill a tank + DRAINTIME, // time to drain a tank + MAXVAR // total number of variable fields } FieldType; typedef enum { - _TITLE,_JUNCTIONS,_RESERVOIRS,_TANKS,_PIPES,_PUMPS, - _VALVES,_CONTROLS,_RULES,_DEMANDS,_SOURCES,_EMITTERS, - _PATTERNS,_CURVES,_QUALITY,_STATUS,_ROUGHNESS,_ENERGY, - _REACTIONS,_MIXING,_REPORT,_TIMES,_OPTIONS, - _COORDS,_VERTICES,_LABELS,_BACKDROP,_TAGS,_END -} SectType; + _TITLE, _JUNCTIONS, _RESERVOIRS, _TANKS, _PIPES, _PUMPS, + _VALVES, _CONTROLS, _RULES, _DEMANDS, _SOURCES, _EMITTERS, + _PATTERNS, _CURVES, _QUALITY, _STATUS, _ROUGHNESS, _ENERGY, + _REACTIONS, _MIXING, _REPORT, _TIMES, _OPTIONS, + _COORDS, _VERTICES, _LABELS, _BACKDROP, _TAGS, _END +} SectionType; typedef enum { - STATHDR, /* Hydraulic Status header */ - ENERHDR, /* Energy Usage header */ - NODEHDR, /* Node Results header */ - LINKHDR /* Link Results header */ -} HdrType; + STATHDR, // hydraulic status header + ENERHDR, // energy usage header + NODEHDR, // node results header + LINKHDR // link results header +} HdrType; typedef enum { - NEGATIVE = -1, // Flow in reverse of pre-assigned direction - ZERO_FLOW = 0, // Zero flow - POSITIVE = 1 // Flow in pre-assigned direction + NEGATIVE = -1, // flow in reverse of pre-assigned direction + ZERO_FLOW = 0, // zero flow + POSITIVE = 1 // flow in pre-assigned direction } FlowDirection; typedef enum { - PCNT_ONLINE, - PCNT_EFFIC, - KWH_PER_FLOW, - TOTAL_KWH, - MAX_KW, - TOTAL_COST, - MAX_ENERGY_STATS -} EnergyStats; - -typedef enum { - DDA, // Demand Driven Analysis - PDA // Pressure Driven Analysis + DDA, // demand driven analysis + PDA // pressure driven analysis } DemandModelType; /* ------------------------------------------------------ - Global Data Structures + Fundamental Data Structures ------------------------------------------------------ */ -struct IDstring /* Holds component ID labels */ +struct IDstring // Holds component ID label { - char ID[MAXID+1]; + char ID[MAXID+1]; }; -struct Floatlist /* Element of list of floats */ +struct Floatlist // Element of List of Numbers { - double value; - struct Floatlist *next; + double value; // element's numerical value + struct Floatlist *next; // next element on the list }; typedef struct Floatlist SFloatlist; -struct Tmplist /* Element of temp list for Pattern & Curve data */ +struct Tmplist // Item of Temporary List of Objects { - int i; - char ID[MAXID+1]; - SFloatlist *x; - SFloatlist *y; - struct Tmplist *next; + 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; +typedef struct Tmplist STmplist; // Pointer to temporary list of objects -typedef struct /* TIME PATTERN OBJECT */ +typedef struct // Time Pattern Object { - char ID[MAXID+1]; /* Pattern ID */ - int Length; /* Pattern length */ - double *F; /* Pattern factors */ -} Spattern; + char ID[MAXID+1]; // pattern ID + int Length; // pattern length + double *F; // pattern factors +} Spattern; -typedef struct /* CURVE OBJECT */ +typedef struct // Curve Object { - char ID[MAXID+1]; /* Curve ID */ - CurveType Type; /* Curve type */ - int Npts; /* Number of points */ - double *X; /* X-values */ - double *Y; /* Y-values */ -} Scurve; + char ID[MAXID+1]; // curve ID + CurveType Type; // curve type + int Npts; // number of points + double *X; // x-values + double *Y; // y-values +} Scurve; -typedef struct /* Coord OBJECT */ +struct Sdemand // Demand List Item { - char ID[MAXID+1]; /* Coord ID */ - double X; /* X-value */ - double Y; /* Y-value */ - char HaveCoords; /* Coordinates flag */ -} Scoord; - -struct Sdemand /* DEMAND CATEGORY OBJECT */ -{ - double Base; /* Baseline demand */ - int Pat; /* Pattern index */ - char Name[MAXMSG+1]; /* Demand category name */ - struct Sdemand *next; /* Next record */ + double Base; // baseline demand + int Pat; // pattern index + char Name[MAXMSG+1]; // demand category name + struct Sdemand *next; // next demand list item }; -typedef struct Sdemand *Pdemand; /* Pointer to demand object */ +typedef struct Sdemand *Pdemand; // Pointer to demand list -typedef struct +typedef struct // Energy Usage Object { - double hrsOnLine; // hours pump is online - double efficiency; // total time wtd. efficiency - double kwHrsPerCFS; // total kw-hrs per cfs of flow - double kwHrs; // total kw-hrs consumed - double maxKwatts; // max. kw consumed - double totalCost; // total pumping cost + double TimeOnLine; // hours pump is online + double Efficiency; // total time wtd. efficiency + double KwHrsPerFlow; // total kw-hrs per unit of flow + double KwHrs; // total kw-hrs consumed + double MaxKwatts; // max. kw consumed + double TotalCost; // total pumping cost } Senergy; -struct Ssource /* WQ SOURCE OBJECT */ +struct Ssource // Water Quality Source Object { - /*int Node;*/ /* Node index of source */ - double C0; /* Base concentration/mass */ - int Pat; /* Pattern index */ - double Smass; /* Actual mass flow rate */ - SourceType Type; /* SourceType (see below) */ + double C0; // base concentration/mass + int Pat; // pattern index + double Smass; // actual mass flow rate + SourceType Type; // type of source }; -typedef struct Ssource *Psource; /* Pointer to WQ source object */ +typedef struct Ssource *Psource; // Pointer to source object -typedef struct /* NODE OBJECT */ +typedef struct // Node Object { - char ID[MAXID+1]; /* Node ID */ - double El; /* Elevation */ - Pdemand D; /* Demand pointer */ - Psource S; /* Source pointer */ - double C0; /* Initial quality */ - double Ke; /* Emitter coeff. */ - char Rpt; /* Reporting flag */ - NodeType Type; /* Node Type */ - char Comment[MAXMSG+1]; /* Node Comment */ -} Snode; + char ID[MAXID+1]; // node ID + double X; // x-coordinate + double Y; // y-coordinate + double El; // elevation + Pdemand D; // demand pointer + Psource S; // source pointer + double C0; // initial quality + double Ke; // emitter coeff. + char Rpt; // reporting flag + NodeType Type; // node type + char Comment[MAXMSG+1]; // node comment +} Snode; -typedef struct /* LINK OBJECT */ +typedef struct // Link Object { - char ID[MAXID+1]; /* Link ID */ - int N1; /* Start node index */ - int N2; /* End node index */ - double Diam; /* Diameter */ - double Len; /* Length */ - double Kc; /* Roughness */ - double Km; /* Minor loss coeff. */ - double Kb; /* Bulk react. coeff */ - double Kw; /* Wall react. coeff */ - double R; /* Flow resistance */ - double Rc; /* Reaction coeff. */ - double Qa; // Low flow limit - LinkType Type; // Link type */ - StatType Stat; /* Initial status */ - char Rpt; /* Reporting flag */ - char Comment[MAXMSG+1]; /* Link Comment */ -} Slink; + char ID[MAXID+1]; // link ID + int N1; // start node index + int N2; // end node index + double Diam; // diameter + double Len; // length + double Kc; // roughness + double Km; // minor loss coeff. + double Kb; // bulk react. coeff. + double Kw; // wall react. coef. + double R; // flow resistance + double Rc; // reaction coeff. + double Qa; // low flow limit + LinkType Type; // link type + StatusType Status; // initial status + char Rpt; // reporting flag + char Comment[MAXMSG+1]; // link Comment +} Slink; -typedef struct /* TANK OBJECT */ +typedef struct // Tank Object { - int Node; /* Node index of tank */ - double A; /* Tank area */ - double Hmin; /* Minimum water elev */ - double Hmax; /* Maximum water elev */ - double H0; /* Initial water elev */ - double Vmin; /* Minimum volume */ - double Vmax; /* Maximum volume */ - double V0; /* Initial volume */ - double Kb; /* Reaction coeff. (1/days) */ - double V; /* Tank volume */ - double C; /* Concentration */ - int Pat; /* Fixed grade time pattern */ - int Vcurve; /* Vol.- elev. curve index */ - MixType MixModel;/* Type of mixing model */ - /* (see MixType below) */ - double V1max; /* Mixing compartment size */ -} Stank; + int Node; // node index of tank + double A; // tank area + double Hmin; // minimum water elev + double Hmax; // maximum water elev + double H0; // initial water elev + double Vmin; // minimum volume + double Vmax; // maximum volume + double V0; // initial volume + double Kb; // bulk reaction coeff. + double V; // tank volume + double C; // concentration + int Pat; // fixed grade time pattern + int Vcurve; // volume v. elev. curve index + MixType MixModel; // type of mixing model + double V1max; // mixing compartment size +} Stank; -typedef struct /* PUMP OBJECT */ +typedef struct // Pump Object { - int Link; /* Link index of pump */ - int Ptype; /* Pump curve type */ - /* (see PumpType below) */ - double Q0; /* Initial flow */ - double Qmax; /* Maximum flow */ - double Hmax; /* Maximum head */ - double H0; /* Shutoff head */ - double R; /* Flow coeffic. */ - double N; /* Flow exponent */ - int Hcurve; /* Head v. flow curve index */ - int Ecurve; /* Effic. v. flow curve index */ - int Upat; /* Utilization pattern index */ - int Epat; /* Energy cost pattern index */ - double Ecost; /* Unit energy cost */ - double Energy[MAX_ENERGY_STATS]; /* Energy usage statistics */ -} Spump; + int Link; // link index of pump + int Ptype; // pump curve type + double Q0; // initial flow + double Qmax; // maximum flow + double Hmax; // maximum head + double H0; // shutoff head + double R; // flow coeffic. + double N; // flow exponent + int Hcurve; // head v. flow curve index + int Ecurve; // effic. v. flow curve index + int Upat; // utilization pattern index + int Epat; // energy cost pattern index + double Ecost; // unit energy cost + Senergy Energy; // energy usage statistics +} Spump; -typedef struct /* VALVE OBJECT */ +typedef struct // Valve Object { - int Link; /* Link index of valve */ -} Svalve; + int Link; // link index of valve +} Svalve; -typedef struct /* CONTROL STATEMENT */ +typedef struct // Control Statement { - int Link; /* Link index */ - int Node; /* Control node index */ - long Time; /* Control time */ - double Grade; /* Control grade */ - double Setting; /* New link setting */ - StatType Status; /* New link status */ - ControlType Type;/* Control type */ - /* (see ControlType below) */ -} Scontrol; + int Link; // link index + int Node; // control node index + long Time; // control time + double Grade; // control grade + double Setting; // new link setting + StatusType Status; // new link status + ControlType Type; // control type +} Scontrol; -struct Sadjlist /* NODE ADJACENCY LIST ITEM */ +typedef struct // Field Object of Report Table { - int node; /* Index of connecting node */ - int link; /* Index of connecting link */ - struct Sadjlist *next; /* Next item in list */ -}; -/* Pointer to adjacency list item */ -typedef struct Sadjlist *Padjlist; - -struct Sseg /* PIPE SEGMENT record used */ -{ /* for WQ routing */ - double v; /* Segment volume */ - double c; /* Water quality value */ - struct Sseg *prev; /* Record for previous segment */ -}; -typedef struct Sseg *Pseg; /* Pointer to pipe segment */ - -typedef struct /* FIELD OBJECT of report table */ -{ - char Name[MAXID+1]; /* Name of reported variable */ - char Units[MAXID+1]; /* Units of reported variable */ - char Enabled; /* Enabled if in table */ - int Precision; /* Number of decimal places */ - double RptLim[2]; /* Lower/upper report limits */ + char Name[MAXID+1]; // name of reported variable + char Units[MAXID+1]; // units of reported variable + char Enabled; // enabled if in table + int Precision; // number of decimal places + double RptLim[2]; // lower/upper report limits } SField; -typedef struct s_Premise /* Rule Premise Clause */ +struct Sadjlist // Node Adjacency List Item { - int logop; /* Logical operator */ - int object; /* Node or link */ - int index; /* Object's index */ - int variable; /* Pressure, flow, etc. */ - int relop; /* Relational operator */ - int status; /* Variable's status */ - double value; /* Variable's value */ - struct s_Premise *next; + int node; // index of connecting node + int link; // index of connecting link + struct Sadjlist *next; // next item in list +}; +typedef struct Sadjlist *Padjlist; // Pointer to adjacency list + +struct Sseg // Pipe Segment List Item +{ + double v; // segment volume + double c; // segment water quality + struct Sseg *prev; // previous segment in list +}; +typedef struct Sseg *Pseg; // Pointer to pipe segment list + +typedef struct s_Premise // Rule Premise Clause +{ + int logop; // logical operator (IF, AND, OR) + int object; // NODE or LINK + int index; // object's index + int variable; // pressure, flow, etc. + int relop; // relational operator (=, >, <, etc.) + int status; // variable's status (OPEN, CLOSED) + double value; // variable's value + struct s_Premise *next; // next premise clause } Spremise; -typedef struct s_Action /* Rule Action Clause */ +typedef struct s_Action // Rule Action Clause { - int link; /* Link index */ - int status; /* Link's status */ - double setting; /* Link's setting */ - struct s_Action *next; + int link; // link index + int status; // link's status + double setting; // link's setting + struct s_Action *next; } Saction; -typedef struct /* Control Rule Structure */ +typedef struct // Control Rule Structure { - char label[MAXID+1]; /* Rule label */ - double priority; /* Priority level */ - Spremise *Premises; /* Linked list of premises */ - Saction *ThenActions; /* Linked list of THEN actions */ - Saction *ElseActions; /* Linked list of ELSE actions */ + char label[MAXID+1]; // rule label + double priority; // priority level + Spremise *Premises; // list of premises + Saction *ThenActions; // list of THEN actions + Saction *ElseActions; // list of ELSE actions } Srule; -typedef struct s_ActionItem /* Action list item */ +typedef struct s_ActionItem // Action List Item { - int ruleIndex; /* Index of rule action belongs to */ - Saction *action; /* An action structure */ - struct s_ActionItem *next; + int ruleIndex; // index of rule action belongs to + Saction *action; // an action clause + struct s_ActionItem *next; // next action on the list } SactionList; -typedef struct +typedef struct // Mass Balance Components { - double initial; - double inflow; - double outflow; - double reacted; - double final; - double ratio; -} MassBalance; + double initial; // initial mass in system + double inflow; // mass inflow to system + double outflow; // mass outflow from system + double reacted; // mass reacted in system + double final; // final mass in system + double ratio; // ratio of mass added to mass lost +} SmassBalance; + +/* +------------------------------------------------------ + Wrapper Data Structures +------------------------------------------------------ +*/ + +// Input File Parser Wrapper +typedef struct { + FILE *InFile; // Input file handle + + char + Unitsflag, // Unit system flag + Flowflag, // Flow units flag + Pressflag, // Pressure units flag + DefPatID[MAXID+1], // Default demand pattern ID + InpFname[MAXFNAME+1], // Input file name + *Tok[MAXTOKS], // Array of token strings + Comment[MAXMSG+1]; // Comment text + + int + MaxNodes, // Node count from input file */ + MaxLinks, // Link count " " " + MaxJuncs, // Junction count " " " + MaxPipes, // Pipe count " " " + MaxTanks, // Tank count " " " + MaxPumps, // Pump count " " " + MaxValves, // Valve count " " " + MaxControls, // Control count " " " + MaxRules, // Rule count " " " + MaxPats, // Pattern count " " " + MaxCurves, // Curve count " " " + Ntokens, // Number of tokens in line of input + Ntitle; // Number of title lines + + STmplist + *Patlist, // Temporary time pattern list + *PrevPat, // Previous pattern list element + *Curvelist, // Temporary list of curves + *PrevCurve; // Previous curve list element + + double *X; // Temporary array for curve data + +} Parser; + +// Time Step Wrapper +typedef struct { + + long + Tstart, // Starting time of day + Hstep, // Nominal hyd. time step + Pstep, // Time pattern time step + Pstart, // Starting pattern time + Rstep, // Reporting time step + Rstart, // Time when reporting starts + Rtime, // Next reporting time + Htime, // Current hyd. time + Hydstep, // Actual hydraulic time step + Qstep, // Quality time step + Qtime, // Current quality time + Rulestep, // Rule evaluation time step + Dur; // Duration of simulation + +} Times; + +// Reporting Wrapper +typedef struct { + + FILE *RptFile; // Report file handle + + int + Nperiods, // Number of reporting periods + PageSize; // Lines/page in output report/ + + long + LineNum, // Current line number + PageNum; // Current page number + + char + Rptflag, // Report flag + Tstatflag, // Report time series statistic flag + Summaryflag, // Report summary flag + Messageflag, // Error/warning message flag + Statflag, // Status report flag + Energyflag, // Energy report flag + Nodeflag, // Node report flag + Linkflag, // Link report flag + Atime[13], // Clock time (hrs:min:sec) + Rpt1Fname[MAXFNAME+1], // Primary report file name + Rpt2Fname[MAXFNAME+1], // Secondary report file name + DateStamp[26], // Current date & time + Fprinterr; // File write error flag + + SField Field[MAXVAR]; // Output reporting fields + +} Report; + +// Output File Wrapper +typedef struct { + + char + HydFname[MAXFNAME+1], // Hydraulics file name + OutFname[MAXFNAME+1], // Binary output file name + Outflag, // Output file flag + Hydflag, // Hydraulics flag + SaveHflag, // Hydraulic results saved flag + SaveQflag, // Quality results saved flag + Saveflag; // General purpose save flag + + long + HydOffset, // Hydraulics file byte offset + OutOffset1, // 1st output file byte offset + OutOffset2; // 2nd output file byte offset + + FILE + *OutFile, // Output file handle + *HydFile, // Hydraulics file handle + *TmpOutFile; // Temporary file handle + +} Outfile; + +// Rule-Based Controls Wrapper +typedef struct { + + SactionList *ActionList; // Linked list of action items + int RuleState; // State of rule interpreter + int Errcode; // Rule parser error code + long Time1; // Start of rule evaluation time interval + Spremise *LastPremise; // Previous premise clause + Saction *LastThenAction; // Previous THEN action + Saction *LastElseAction; // Previous ELSE action + +} Rules; + +// Sparse Matrix Wrapper +typedef struct { + + double + *Aii, // Diagonal matrix coeffs. + *Aij, // Non-zero, off-diagonal matrix coeffs. + *F, // Right hand side vector + *temp; // Array used by linear eqn. solver + + int + Ncoeffs, // Number of non-zero matrix coeffs + *Order, // Node-to-row of re-ordered matrix + *Row, // Row-to-node of re-ordered matrix + *Ndx, // Index of link's coeff. in Aij + *XLNZ, // Start position of each column in NZSUB + *NZSUB, // Row index of each coeff. in each column + *LNZ, // Position of each coeff. in Aij array + *Degree, // Number of links adjacent to each node + *link, // Array used by linear eqn. solver + *first; // Array used by linear eqn. solver + +} Smatrix; + +// Hydraulics Solver Wrapper +typedef struct { + + double + *NodeHead, // Node hydraulic heads + *NodeDemand, // Node demand + emitter flows + *DemandFlow, // Demand outflows + *EmitterFlow, // Emitter outflows + *LinkFlow, // Link flows + *LinkSetting, // Link settings + Htol, // Hydraulic head tolerance + Qtol, // Flow rate tolerance + RQtol, // Flow resistance tolerance + Hexp, // Exponent in headloss formula + Qexp, // Exponent in emitter formula + Pexp, // Exponent in demand formula + Pmin, // Pressure needed for any demand + Preq, // Pressure needed for full demand + Dmult, // Demand multiplier + Hacc, // Relative flow change limit + FlowChangeLimit, // Absolute flow change limit + HeadErrorLimit, // Hydraulic head error limit + DampLimit, // Solution damping threshold + Viscos, // Kin. viscosity (sq ft/sec) + SpGrav, // Specific gravity + Epump, // Global pump efficiency + Dsystem, // Total system demand + Ecost, // Base energy cost per kwh + Dcost, // Energy demand charge/kw/day + Emax, // Peak energy usage + RelativeError, // Total flow change / total flow + MaxHeadError, // Max. error for link head loss + MaxFlowChange, // Max. change in link flow + RelaxFactor, // Relaxation factor for flow updating + *P, // Inverse of head loss derivatives + *Y, // Flow correction factors + *Xflow; // Inflow - outflow at each node + + int + DefPat, // Default demand pattern + Epat, // Energy cost time pattern + DemandModel, // Fixed or pressure dependent + Iterations, // Number of hydraulic trials taken + MaxIter, // Max. hydraulic trials allowed + ExtraIter, // Extra hydraulic trials + CheckFreq, // Hydraulic trials between status checks + MaxCheck, // Hydraulic trials limit on status checks + Haltflag; // Flag to halt simulation + + StatusType + *LinkStatus, // Link status + *OldStatus; // Previous link/tank status + + char + OpenHflag, // Hydraulic system opened flag + Formflag; // Head loss formula flag + + Smatrix smatrix; // Sparse matrix storage + +} Hydraul; // Forward declaration of the Mempool structure defined in mempool.h struct Mempool; +// Water Quality Solver Wrapper typedef struct { - char - Qualflag, // Water quality flag - OpenQflag, // Quality system opened flag - Reactflag, // Reaction indicator - OutOfMemory; // Out of memory indicator char - ChemName[MAXID+1], // Name of chemical - ChemUnits[MAXID+1]; // Units of chemical + Qualflag, // Water quality analysis flag + OpenQflag, // Quality system opened flag + Reactflag, // Reaction indicator + OutOfMemory; // Out of memory indicator + + char + ChemName[MAXID + 1], // Name of chemical + ChemUnits[MAXID + 1]; // Units of chemical int - TraceNode, // Source node for flow tracing - *SortedNodes, // Topologically sorted node indexes - *Ilist, // Link incidence lists for all nodes - *IlistPtr; // Start index of each node in Ilist + TraceNode, // Source node for flow tracing + *SortedNodes; // Topologically sorted node indexes double - Ctol, // Water quality tolerance - Diffus, // Diffusivity (sq ft/sec) - Wbulk, // Avg. bulk reaction rate - Wwall, // Avg. wall reaction rate - Wtank, // Avg. tank reaction rate - Wsource, // Avg. mass inflow - Rfactor, // Roughness-reaction factor - Sc, // Schmidt Number - Bucf, // Bulk reaction units conversion factor - Tucf, // Tank reaction units conversion factor - BulkOrder, // Bulk flow reaction order - WallOrder, // Pipe wall reaction order - TankOrder, // Tank reaction order - Kbulk, // Global bulk reaction coeff. - Kwall, // Global wall reaction coeff. - Climit, // Limiting potential quality - SourceQual, // External source quality - *NodeQual, // Reported node quality state - *PipeRateCoeff; // Pipe reaction rate coeffs. - - long - Qstep, // Quality time step (sec) - Qtime; // Current quality time (sec) + Ctol, // Water quality tolerance + Diffus, // Diffusivity (sq ft/sec) + Wbulk, // Avg. bulk reaction rate + Wwall, // Avg. wall reaction rate + Wtank, // Avg. tank reaction rate + Wsource, // Avg. mass inflow + Rfactor, // Roughness-reaction factor + Sc, // Schmidt Number + Bucf, // Bulk reaction units conversion factor + Tucf, // Tank reaction units conversion factor + BulkOrder, // Bulk flow reaction order + WallOrder, // Pipe wall reaction order + TankOrder, // Tank reaction order + Kbulk, // Global bulk reaction coeff. + Kwall, // Global wall reaction coeff. + Climit, // Limiting potential quality + SourceQual, // External source quality + *NodeQual, // Reported node quality state + *PipeRateCoeff; // Pipe reaction rate coeffs. struct Mempool - *SegPool; // Memory pool for water quality segments + *SegPool; // Memory pool for water quality segments Pseg - FreeSeg, // Pointer to unused segment - *FirstSeg, // First (downstream) segment in each pipe - *LastSeg; // Last (upstream) segment in each pipe + FreeSeg, // Pointer to unused segment + *FirstSeg, // First (downstream) segment in each pipe + *LastSeg; // Last (upstream) segment in each pipe FlowDirection - *FlowDir; // Flow direction for each pipe + *FlowDir; // Flow direction for each pipe - MassBalance - massbalance; // Mass balance components -} quality_t; + SmassBalance + MassBalance; // Mass balance components +} Quality; + +// Pipe Network Wrapper typedef struct { - long - Tstart, /* Starting time of day (sec) */ - Hstep, /* Nominal hyd. time step (sec) */ - Pstep, /* Time pattern time step (sec) */ - Pstart, /* Starting pattern time (sec) */ - Rstep, /* Reporting time step (sec) */ - Rstart, /* Time when reporting starts */ - Rtime, /* Next reporting time */ - Htime, /* Current hyd. time (sec) */ - Hydstep, /* Actual hydraulic time step */ - Rulestep, /* Rule evaluation time step */ - Dur; /* Duration of simulation (sec) */ - -} time_options_t; - - -typedef struct { - - FILE *InFile; /// Input file pointer - - char - Coordflag, /* Load coordinates flag */ - Unitsflag, /* Unit system flag */ - Flowflag, /* Flow units flag */ - Pressflag; /* Pressure units flag */ int - MaxNodes, /* Node count from input file */ - MaxLinks, /* Link count from input file */ - MaxJuncs, /* Junction count */ - MaxPipes, /* Pipe count */ - MaxTanks, /* Tank count */ - MaxPumps, /* Pump count */ - MaxValves, /* Valve count */ - MaxControls, /* Control count */ - MaxRules, /* Rule count */ - MaxPats, /* Pattern count */ - MaxCurves; /* Curve count */ - - char - DefPatID[MAXID+1], /* Default demand pattern ID */ - InpFname[MAXFNAME+1]; /* Input file name */ - - STmplist - *Patlist, /* Temporary time pattern list */ - *Curvelist; /* Temporary list of curves */ - - double *X; // temporary array for curve data - int - Ntokens, /* Number of tokens in input line */ - Ntitle; /* Number of title lines */ - - char *Tok[MAXTOKS]; /* Array of token strings */ - char Comment[MAXMSG+1]; - STmplist *PrevPat; /* Pointer to pattern list element */ - STmplist *PrevCurve; /* Pointer to curve list element */ - -} parser_data_t; - -typedef struct { - - FILE *RptFile; /* Report file pointer */ - - int - Nperiods, /* Number of reporting periods */ - PageSize; /* Lines/page in output report */ - - char - Rptflag, /* Report flag */ - Tstatflag, /* Time statistics flag */ - Summaryflag, /* Report summary flag */ - Messageflag, /* Error/warning message flag */ - Statflag, /* Status report flag */ - Energyflag, /* Energy report flag */ - Nodeflag, /* Node report flag */ - Linkflag, /* Link report flag */ - Atime[13], /* Clock time (hrs:min:sec) */ - Rpt1Fname[MAXFNAME+1], /* Primary report file name */ - Rpt2Fname[MAXFNAME+1]; /* Secondary report file name */ - - SField Field[MAXVAR]; /* Output reporting fields */ - - long LineNum; /* Current line number */ - long PageNum; /* Current page number */ - char DateStamp[26]; /* Current date & time */ - char Fprinterr; /* File write error flag */ - -} report_options_t; - - -typedef struct { - - char - HydFname[MAXFNAME+1], /* Hydraulics file name */ - OutFname[MAXFNAME+1], /* Binary output file name */ - Outflag, /* Output file flag */ - Hydflag; /* Hydraulics flag */ - - long - HydOffset, /* Hydraulics file byte offset */ - OutOffset1, /* 1st output file byte offset */ - OutOffset2; /* 2nd output file byte offset */ - - FILE - *OutFile, /* Output file pointer */ - *HydFile, /* Hydraulics file pointer */ - *TmpOutFile; /* Temporary file handle */ - -} out_file_t; - -typedef struct { - - char - SaveHflag, /* Hydraul. results saved flag */ - SaveQflag, /* Quality results saved flag */ - Saveflag; /* General purpose save flag */ - -} save_options_t; - -/* - ** NOTE: Hydraulic analysis of the pipe network at a given point in time - ** is done by repeatedly solving a linearized version of the - ** equations for conservation of flow & energy: - ** - ** A*H = F - ** - ** where H = vector of heads (unknowns) at each node, - ** F = vector of right-hand side coeffs. - ** A = square matrix of coeffs. - ** and both A and F are updated at each iteration until there is - ** negligible change in pipe flows. - ** - ** Each row (or column) of A corresponds to a junction in the pipe - ** network. Each link (pipe, pump or valve) in the network has a - ** non-zero entry in the row-column of A that corresponds to its - ** end points. This results in A being symmetric and very sparse. - ** The following arrays are used to efficiently manage this sparsity: - */ -typedef struct { - // hydraulic solution vars - double - *Aii, /* Diagonal coeffs. of A */ - *Aij, /* Non-zero, off-diagonal coeffs. of A */ - *F, /* Right hand side coeffs. */ - *P, /* Inverse headloss derivatives */ - *Y; /* Flow correction factors */ - - int - *Order, /* Node-to-row of A */ - *Row, /* Row-to-node of A */ - *Ndx, /* Index of link's coeff. in Aij */ - *XLNZ, /* Start position of each column in NZSUB */ - *NZSUB, /* Row index of each coeff. in each column */ - *LNZ, /* Position of each coeff. in Aij array */ - *Degree; /* Number of links adjacent to each node */ -} solver_t; - -typedef struct { - double - *NodeDemand, // Node actual total outflow - *DemandFlows, // Demand outflows - *EmitterFlows, /* Emitter flows */ - *LinkSetting, /* Link settings */ - *LinkFlows, /* Link flows */ - *NodeHead, - Htol, /* Hydraulic head tolerance */ - Qtol, /* Flow rate tolerance */ - RQtol, /* Flow resistance tolerance */ - Hexp, /* Exponent in headloss formula */ - Qexp, /* Exponent in emitter formula */ - Pexp, // Exponent in demand formula - Pmin, // Pressure needed for any demand - Preq, // Pressure needed for full demand - Dmult, /* Demand multiplier */ - - Hacc, /* Hydraulics solution accuracy */ - FlowChangeLimit, /* Hydraulics flow change limit */ - HeadErrorLimit, /* Hydraulics head error limit */ - - DampLimit, /* Solution damping threshold */ - Viscos, /* Kin. viscosity (sq ft/sec) */ - SpGrav, /* Specific gravity */ - Epump, /* Global pump efficiency */ - Dsystem, /* Total system demand */ - Ecost, /* Base energy cost per kwh */ - Dcost, /* Energy demand charge/kw/day */ - Emax, /* Peak energy usage */ - *X_tmp; - - int - DefPat, /* Default demand pattern */ - Epat, /* Energy cost time pattern */ - DemandModel; // Fixed or pressure dependent - - StatType - *LinkStatus, /* Link status */ - *OldStat; /* Previous link/tank status */ - - int - MaxIter, /* Max. hydraulic trials */ - ExtraIter, /* Extra hydraulic trials */ - Ncoeffs, /* Number of non-0 matrix coeffs*/ - CheckFreq, /* Hydraulics solver parameter */ - MaxCheck; /* Hydraulics solver parameter */ - - char - OpenHflag, /* Hydraul. system opened flag */ - Formflag; /* Hydraulic formula flag */ - - /* Info about hydraulic solution */ - double RelativeError; - double MaxHeadError; - double MaxFlowChange; - int Iterations; - - /* Flag used to halt taking further time steps */ - int Haltflag; - /* Relaxation factor used for updating flow changes */ - double RelaxFactor; - - solver_t solver; - -} hydraulics_t; - -typedef struct { - SactionList *ActionList; /* Linked list of action items */ - int RuleState; /* State of rule interpreter */ - int Errcode; // Rule parser error code - long Time1; /* Start of rule evaluation time interval (sec) */ - Spremise *LastPremise; /* Previous premise clause */ - Saction *LastThenAction; /* Previous THEN action */ - Saction *LastElseAction; /* Previous ELSE action */ -} rules_t; - -typedef struct { - int Nnodes, /* Number of network nodes */ - Ntanks, /* Number of tanks */ - Njuncs, /* Number of junction nodes */ - Nlinks, /* Number of network links */ - Npipes, /* Number of pipes */ - Npumps, /* Number of pumps */ - Nvalves, /* Number of valves */ - Ncontrols, /* Number of simple controls */ - Nrules, /* Number of control rules */ - Npats, /* Number of time patterns */ - Ncurves, /* Number of data curves */ - Ncoords; /* Number of node coordinates */ - - Snode *Node; /* Node array */ - Slink *Link; /* Link array */ - Stank *Tank; /* Tank array */ - Spump *Pump; /* Pump array */ - Svalve *Valve; /* Valve array */ - Spattern *Pattern; /* Time pattern array */ - Scurve *Curve; /* Data curve array */ - Scoord *Coord; /* Node coordinate array */ - Scontrol *Control; /* Simple controls array */ - Srule *Rule; /* Rule-based controls array */ + Nnodes, // Number of network nodes + Ntanks, // Number of tanks + Njuncs, // Number of junction nodes + Nlinks, // Number of network links + Npipes, // Number of pipes + Npumps, // Number of pumps + Nvalves, // Number of valves + Ncontrols, // Number of simple controls + Nrules, // Number of control rules + Npats, // Number of time patterns + Ncurves; // Number of data curves + + Snode *Node; // Node array + Slink *Link; // Link array + Stank *Tank; // Tank array + Spump *Pump; // Pump array + Svalve *Valve; // Valve array + Spattern *Pattern; // Time pattern array + Scurve *Curve; // Data curve array + Scontrol *Control; // Simple controls array + Srule *Rule; // Rule-based controls array HashTable - *NodeHashTable, - *LinkHashTable; /* Hash tables for ID labels */ - Padjlist *Adjlist; /* Node adjacency lists */ + *NodeHashTable, // Hash table for Node ID names + *LinkHashTable; // Hash table for Link ID names + Padjlist *Adjlist; // Node adjacency lists -} EN_Network; +} Network; +// Overall Project Wrapper +typedef struct Project { -/* project wrapper */ -typedef struct EN_Project { - - EN_Network network; /// the network description struct - hydraulics_t hydraulics; - rules_t rules; - quality_t quality; - time_options_t time_options; - - parser_data_t parser; - report_options_t report; - out_file_t out_files; - save_options_t save_options; - - double Ucf[MAXVAR]; // Unit conversion factors - + Network network; // Pipe network wrapper + Parser parser; // Input file parser wrapper + Times times; // Time step wrapper + Report report; // Reporting wrapper + Outfile outfile; // Output file wrapper + Rules rules; // Rule-based controls wrapper + Hydraul hydraul; // Hydraulics solver wrapper + Quality quality; // Water quality solver wrapper + + double Ucf[MAXVAR]; // Unit conversion factors + char - Openflag, // Toolkit open flag - Warnflag, // Warning flag - Msg[MAXMSG+1], // General-purpose string: errors, messages - Title[MAXTITLE][TITLELEN+1], // Project title - MapFname[MAXFNAME+1], // Map file name - TmpHydFname[MAXFNAME+1], // Temporary hydraulics file name - TmpOutFname[MAXFNAME+1], // Temporary output file name - TmpStatFname[MAXFNAME+1]; // Temporary statistic file name - - void (* viewprog) (char *); // Pointer to progress viewing function - -} EN_Project; + Openflag, // Project open flag + Warnflag, // Warning flag + Msg[MAXMSG+1], // General-purpose string: errors, messages + Title[MAXTITLE][TITLELEN+1], // Project title + MapFname[MAXFNAME+1], // Map file name + TmpHydFname[MAXFNAME+1], // Temporary hydraulics file name + TmpOutFname[MAXFNAME+1], // Temporary output file name + TmpStatFname[MAXFNAME+1]; // Temporary statistic file name + + void (* viewprog) (char *); // Pointer to progress viewing function + +} Project, *EN_Project; #endif diff --git a/tests/test_addrule.cpp b/tests/test_addrule.cpp index 6eeea93..851d821 100644 --- a/tests/test_addrule.cpp +++ b/tests/test_addrule.cpp @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) int link113, node23, link22, pump9_before, pump9_after; float priority; - EN_ProjectHandle ph = NULL; + EN_Project ph = NULL; EN_createproject(&ph); std::string path_inp = std::string(DATA_PATH_INP); diff --git a/tests/test_demand_categories.cpp b/tests/test_demand_categories.cpp index bbfc43f..efc8d8a 100644 --- a/tests/test_demand_categories.cpp +++ b/tests/test_demand_categories.cpp @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(test_demand_categories) int error = 0; int Nindex, ndem; - EN_ProjectHandle ph = NULL; + EN_Project ph = NULL; error = EN_createproject(&ph); error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); diff --git a/tests/test_net_builder.cpp b/tests/test_net_builder.cpp index f6f7bea..ee95341 100644 --- a/tests/test_net_builder.cpp +++ b/tests/test_net_builder.cpp @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(test_net_builder) float 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_ProjectHandle ph = NULL; + EN_Project ph = NULL; EN_createproject(&ph); std::string path_inp = std::string(DATA_PATH_INP); diff --git a/tests/test_reent.cpp b/tests/test_reent.cpp index 4ac1d3c..99f896f 100644 --- a/tests/test_reent.cpp +++ b/tests/test_reent.cpp @@ -23,7 +23,7 @@ using namespace std; void epanet_thread(long i) { int errorcode = 0; - EN_ProjectHandle ph; + EN_Project ph; string prefix = "example_"; string suffix = ".inp"; diff --git a/tests/test_setid.cpp b/tests/test_setid.cpp index 68fe5cc..70f0579 100644 --- a/tests/test_setid.cpp +++ b/tests/test_setid.cpp @@ -5,10 +5,14 @@ This is a test for the API functions that change a node or link ID name. A node and link name are changed, the network is saved, reopened and the new names are checked. */ +//#define NO_BOOST +#ifndef NO_BOOST #define BOOST_TEST_MODULE "toolkit" #include +#endif +#include #include #include "epanet2.h" @@ -16,20 +20,28 @@ A node and link name are changed, the network is saved, reopened and the new nam #define DATA_PATH_RPT "./test.rpt" #define DATA_PATH_OUT "./test.out" +#ifdef NO_BOOST +#define BOOST_REQUIRE(x) (((x)) ? cout << "\nPassed at line " << __LINE__ : cout << "\nFailed at line " << __LINE__ ) +#endif + using namespace std; +#ifndef NO_BOOST BOOST_AUTO_TEST_SUITE (test_toolkit) - BOOST_AUTO_TEST_CASE(test_setid) +#else +int main(int argc, char *argv[]) +#endif { string path_inp(DATA_PATH_INP); string path_rpt(DATA_PATH_RPT); string path_out(DATA_PATH_OUT); - + string inp_save("net1_setid.inp"); + int error = 0; int index; - EN_ProjectHandle ph = NULL; + EN_Project ph = NULL; EN_createproject(&ph); error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), ""); @@ -56,32 +68,33 @@ BOOST_AUTO_TEST_CASE(test_setid) BOOST_REQUIRE(error == 0); // Save the project - error = EN_saveinpfile(ph, "net1_setid.inp"); + error = EN_saveinpfile(ph, inp_save.c_str()); BOOST_REQUIRE(error == 0); error = EN_close(ph); BOOST_REQUIRE(error == 0); EN_deleteproject(&ph); - - // Re-open the saved project + // Re-open the saved project EN_createproject(&ph); - error = EN_open(ph, "net1_setid.inp", path_rpt.c_str(), ""); + error = EN_open(ph, inp_save.c_str(), path_rpt.c_str(), ""); BOOST_REQUIRE(error == 0); // Check that 3rd node has its new name error = EN_getnodeindex(ph, newid_2, &index); BOOST_REQUIRE(error == 0); - BOOST_CHECK(index == 3); + BOOST_REQUIRE(index == 3); // Check that 3rd link has its new name error = EN_getlinkindex(ph, newid_4, &index); BOOST_REQUIRE(error == 0); - BOOST_CHECK(index == 3); + BOOST_REQUIRE(index == 3); error = EN_close(ph); BOOST_REQUIRE(error == 0); EN_deleteproject(&ph); } +#ifndef NO_BOOST BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/test_setlinktype.cpp b/tests/test_setlinktype.cpp index 65293b0..b9efb6f 100644 --- a/tests/test_setlinktype.cpp +++ b/tests/test_setlinktype.cpp @@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(test_setlinktype) int p113, n31, p121, n113_1, n113_2; float q113 = 0.0f, p31 = 0.0f, diam; - EN_ProjectHandle ph = NULL; + EN_Project ph = NULL; EN_createproject(&ph); std::string path_inp = std::string(DATA_PATH_INP); diff --git a/tests/test_toolkit.cpp b/tests/test_toolkit.cpp index c09b158..618fc1c 100644 --- a/tests/test_toolkit.cpp +++ b/tests/test_toolkit.cpp @@ -36,7 +36,7 @@ BOOST_AUTO_TEST_SUITE (test_toolkit) BOOST_AUTO_TEST_CASE (test_alloc_free) { int error = 0; - EN_ProjectHandle ph = NULL; + EN_Project ph = NULL; error = EN_createproject(&ph); @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE (test_open_close) string path_rpt(DATA_PATH_RPT); string path_out(DATA_PATH_OUT); - EN_ProjectHandle ph = NULL; + EN_Project ph = NULL; EN_createproject(&ph); int error = EN_open(ph, path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); @@ -74,7 +74,8 @@ BOOST_AUTO_TEST_CASE(test_save_reopen) string path_rpt(DATA_PATH_RPT); string path_out(DATA_PATH_OUT); - EN_ProjectHandle ph_save, ph_reopen; + EN_Project ph_save; + EN_Project ph_reopen; EN_createproject(&ph_save); @@ -126,7 +127,7 @@ struct Fixture{ string path_out; int error; - EN_ProjectHandle ph; + EN_Project ph; }; BOOST_AUTO_TEST_SUITE(test_epanet_fixture) diff --git a/win_build/WinSDK/Makefile.bat b/win_build/WinSDK/Makefile.bat index a175d7e..4206ecb 100644 --- a/win_build/WinSDK/Makefile.bat +++ b/win_build/WinSDK/Makefile.bat @@ -21,9 +21,9 @@ Find /i "x86" < checkOS.tmp > StringCheck.tmp If %ERRORLEVEL% == 1 ( CALL "%SDK_PATH%bin\"SetEnv.cmd /x64 /release rem : create EPANET2.DLL - cl -o epanet2.dll epanet.c util\errormanager.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c qualroute.c qualreact.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /link /DLL + cl -o epanet2.dll epanet.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c project.c quality.c qualroute.c qualreact.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /link /DLL rem : create EPANET2.EXE - cl -o epanet2.exe epanet.c util\errormanager.c ..\run\main.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c qualroute.c qualreact.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /I ..\src /link + cl -o epanet2.exe epanet.c ..\run\main.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c project.c quality.c qualroute.c qualreact.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /I ..\src /link md "%Build_PATH%"\64bit move /y "%SRC_PATH%"\*.dll "%Build_PATH%"\64bit move /y "%SRC_PATH%"\*.exe "%Build_PATH%"\64bit @@ -35,9 +35,9 @@ rem : 32 bit with DEF CALL "%SDK_PATH%bin\"SetEnv.cmd /x86 /release echo "32 bit with epanet2.def mapping" rem : create EPANET2.DLL -cl -o epanet2.dll epanet.c util\errormanager.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c qualroute.c qualreact.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /link /DLL /def:..\win_build\WinSDK\epanet2.def /MAP +cl -o epanet2.dll epanet.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c project.c quality.c qualroute.c qualreact.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /link /DLL /def:..\win_build\WinSDK\epanet2.def /MAP rem : create EPANET2.EXE -cl -o epanet2.exe epanet.c util\errormanager.c ..\run\main.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c quality.c qualroute.c qualreact.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /I ..\src /link +cl -o epanet2.exe epanet.c ..\run\main.c hash.c hydraul.c hydcoeffs.c hydstatus.c hydsolver.c inpfile.c input1.c input2.c input3.c mempool.c output.c project.c quality.c qualroute.c qualreact.c report.c rules.c smatrix.c genmmd.c /I ..\include /I ..\run /I ..\src /link md "%Build_PATH%"\32bit move /y "%SRC_PATH%"\*.dll "%Build_PATH%"\32bit move /y "%SRC_PATH%"\*.exe "%Build_PATH%"\32bit