Adds tank overflow feature
This commit is contained in:
@@ -61,7 +61,8 @@ typedef enum {
|
|||||||
EN_MIXFRACTION = 22, //!< Tank mixing fraction
|
EN_MIXFRACTION = 22, //!< Tank mixing fraction
|
||||||
EN_TANK_KBULK = 23, //!< Tank bulk decay coefficient
|
EN_TANK_KBULK = 23, //!< Tank bulk decay coefficient
|
||||||
EN_TANKVOLUME = 24, //!< Current computed tank volume (read only)
|
EN_TANKVOLUME = 24, //!< Current computed tank volume (read only)
|
||||||
EN_MAXVOLUME = 25 //!< Tank maximum volume (read only)
|
EN_MAXVOLUME = 25, //!< Tank maximum volume (read only)
|
||||||
|
EN_CANOVERFLOW = 26 //!< Tank can overflow (= 1) or not (= 0)
|
||||||
} EN_NodeProperty;
|
} EN_NodeProperty;
|
||||||
|
|
||||||
/// Link properties
|
/// Link properties
|
||||||
|
|||||||
11
src/epanet.c
11
src/epanet.c
@@ -1802,6 +1802,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
|
|||||||
tank->Vcurve = 0;
|
tank->Vcurve = 0;
|
||||||
tank->MixModel = 0;
|
tank->MixModel = 0;
|
||||||
tank->V1max = 10000;
|
tank->V1max = 10000;
|
||||||
|
tank->CanOverflow = FALSE;
|
||||||
}
|
}
|
||||||
net->Nnodes++;
|
net->Nnodes++;
|
||||||
p->parser.MaxNodes = net->Nnodes;
|
p->parser.MaxNodes = net->Nnodes;
|
||||||
@@ -2197,6 +2198,11 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
|
|||||||
v = tankvolume(p, index - nJuncs, NodeHead[index]) * Ucf[VOLUME];
|
v = tankvolume(p, index - nJuncs, NodeHead[index]) * Ucf[VOLUME];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EN_CANOVERFLOW:
|
||||||
|
if (Node[index].Type != TANK) return 0;
|
||||||
|
v = Tank[index - nJuncs].CanOverflow;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 251;
|
return 251;
|
||||||
}
|
}
|
||||||
@@ -2495,6 +2501,11 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EN_CANOVERFLOW:
|
||||||
|
if (Node[index].Type != TANK) return 0;
|
||||||
|
Tank[index - nJuncs].CanOverflow = (value != 0.0);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 251;
|
return 251;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -441,7 +441,7 @@ void tankstatus(Project *pr, int k, int n1, int n2)
|
|||||||
h = hyd->NodeHead[n1] - hyd->NodeHead[n2];
|
h = hyd->NodeHead[n1] - hyd->NodeHead[n2];
|
||||||
|
|
||||||
// If tank is full, then prevent flow into it
|
// If tank is full, then prevent flow into it
|
||||||
if (hyd->NodeHead[n1] >= tank->Hmax - hyd->Htol)
|
if (hyd->NodeHead[n1] >= tank->Hmax - hyd->Htol && !tank->CanOverflow)
|
||||||
{
|
{
|
||||||
// Case 1: Link is a pump discharging into tank
|
// Case 1: Link is a pump discharging into tank
|
||||||
if (link->Type == PUMP)
|
if (link->Type == PUMP)
|
||||||
|
|||||||
@@ -195,8 +195,10 @@ int saveinpfile(Project *pr, const char *fname)
|
|||||||
sqrt(4.0 * tank->A / PI) * pr->Ucf[ELEV],
|
sqrt(4.0 * tank->A / PI) * pr->Ucf[ELEV],
|
||||||
tank->Vmin * SQR(pr->Ucf[ELEV]) * pr->Ucf[ELEV]);
|
tank->Vmin * SQR(pr->Ucf[ELEV]) * pr->Ucf[ELEV]);
|
||||||
if ((j = tank->Vcurve) > 0) sprintf(s1, "%s", net->Curve[j].ID);
|
if ((j = tank->Vcurve) > 0) sprintf(s1, "%s", net->Curve[j].ID);
|
||||||
|
else if (tank->CanOverflow) strcpy(s1, "*");
|
||||||
else strcpy(s1, " ");
|
else strcpy(s1, " ");
|
||||||
fprintf(f, "\n%s %-31s", s, s1);
|
fprintf(f, "\n%s %-31s", s, s1);
|
||||||
|
if (tank->CanOverflow) fprintf(f, " YES ");
|
||||||
if (node->Comment) fprintf(f, " ;%s", node->Comment);
|
if (node->Comment) fprintf(f, " ;%s", node->Comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/input3.c
20
src/input3.c
@@ -138,7 +138,8 @@ int tankdata(Project *pr)
|
|||||||
int i, // Node index
|
int i, // Node index
|
||||||
n, // # data items
|
n, // # data items
|
||||||
pattern = 0, // Time pattern index
|
pattern = 0, // Time pattern index
|
||||||
curve = 0; // Curve index
|
curve = 0, // Curve index
|
||||||
|
overflow = FALSE;// Overflow indicator
|
||||||
double el = 0.0, // Elevation
|
double el = 0.0, // Elevation
|
||||||
initlevel = 0.0, // Initial level
|
initlevel = 0.0, // Initial level
|
||||||
minlevel = 0.0, // Minimum level
|
minlevel = 0.0, // Minimum level
|
||||||
@@ -185,12 +186,20 @@ int tankdata(Project *pr)
|
|||||||
if (n >= 7 && !getfloat(parser->Tok[6], &minvol)) return setError(parser, 6, 202);
|
if (n >= 7 && !getfloat(parser->Tok[6], &minvol)) return setError(parser, 6, 202);
|
||||||
|
|
||||||
// If volume curve supplied check it exists
|
// If volume curve supplied check it exists
|
||||||
if (n == 8)
|
if (n >= 8)
|
||||||
{
|
{
|
||||||
curve = findcurve(net, parser->Tok[7]);
|
if (strlen(parser->Tok[7]) > 0 && *(parser->Tok[7]) != '*')
|
||||||
if (curve == 0) return setError(parser, 7, 206);
|
{
|
||||||
net->Curve[curve].Type = VOLUME_CURVE;
|
curve = findcurve(net, parser->Tok[7]);
|
||||||
|
if (curve == 0) return setError(parser, 7, 206);
|
||||||
|
net->Curve[curve].Type = VOLUME_CURVE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse overflow indicator if present
|
||||||
|
if (n >= 9 && match(parser->Tok[8], w_YES))
|
||||||
|
overflow = TRUE;
|
||||||
|
|
||||||
if (initlevel < 0.0) return setError(parser, 2, 209);
|
if (initlevel < 0.0) return setError(parser, 2, 209);
|
||||||
if (minlevel < 0.0) return setError(parser, 3, 209);
|
if (minlevel < 0.0) return setError(parser, 3, 209);
|
||||||
if (maxlevel < 0.0) return setError(parser, 4, 209);
|
if (maxlevel < 0.0) return setError(parser, 4, 209);
|
||||||
@@ -216,6 +225,7 @@ int tankdata(Project *pr)
|
|||||||
tank->A = diam;
|
tank->A = diam;
|
||||||
tank->Pat = pattern;
|
tank->Pat = pattern;
|
||||||
tank->Kb = MISSING;
|
tank->Kb = MISSING;
|
||||||
|
tank->CanOverflow = overflow;
|
||||||
|
|
||||||
//*******************************************************************
|
//*******************************************************************
|
||||||
// NOTE: The min, max, & initial volumes set here are based on a
|
// NOTE: The min, max, & initial volumes set here are based on a
|
||||||
|
|||||||
@@ -417,6 +417,7 @@ typedef struct // Tank Object
|
|||||||
int Vcurve; // volume v. elev. curve index
|
int Vcurve; // volume v. elev. curve index
|
||||||
MixType MixModel; // type of mixing model
|
MixType MixModel; // type of mixing model
|
||||||
double V1max; // mixing compartment size
|
double V1max; // mixing compartment size
|
||||||
|
int CanOverflow; // tank can overflow or not
|
||||||
} Stank;
|
} Stank;
|
||||||
|
|
||||||
typedef struct // Pump Object
|
typedef struct // Pump Object
|
||||||
|
|||||||
119
tests/test_overflow.cpp
Normal file
119
tests/test_overflow.cpp
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
******************************************************************************
|
||||||
|
Project: OWA EPANET
|
||||||
|
Version: 2.2
|
||||||
|
Module: test_overflow.cpp
|
||||||
|
Description: Tests EPANET toolkit api functions
|
||||||
|
Authors: see AUTHORS
|
||||||
|
Copyright: see AUTHORS
|
||||||
|
License: see LICENSE
|
||||||
|
Last Updated: 06/16/2019
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tests the EN_CANOVERFLOW option for Tank nodes
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include "test_toolkit.hpp"
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE (test_overflow)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_tank_overflow)
|
||||||
|
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
int Nindex, Lindex;
|
||||||
|
double level, spillage, spillage2, inflow;
|
||||||
|
char testFile[] = "test_overflow.inp";
|
||||||
|
|
||||||
|
EN_Project ph = NULL;
|
||||||
|
|
||||||
|
error = EN_createproject(&ph);
|
||||||
|
error = EN_open(ph, DATA_PATH_NET1, DATA_PATH_RPT, "");
|
||||||
|
|
||||||
|
// Get index of the tank and its inlet/outlet pipe
|
||||||
|
error = EN_getnodeindex(ph, (char *)"2", &Nindex);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
error = EN_getlinkindex(ph, (char *)"110", &Lindex);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
// Set initial & maximum level to 130
|
||||||
|
error = EN_setnodevalue(ph, Nindex, EN_TANKLEVEL, 130);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
error = EN_setnodevalue(ph, Nindex, EN_MAXLEVEL, 130);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
// Set duration to 1 hr
|
||||||
|
error = EN_settimeparam(ph, EN_DURATION, 3600);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
// Solve hydraulics with default of no tank spillage allowed
|
||||||
|
error = EN_solveH(ph);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
// Check that tank remains full
|
||||||
|
error = EN_getnodevalue(ph, Nindex, EN_TANKLEVEL, &level);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
BOOST_REQUIRE(abs(level - 130.0) < 0.0001);
|
||||||
|
|
||||||
|
// Check that there is no spillage
|
||||||
|
error = EN_getnodevalue(ph, Nindex, EN_DEMAND, &spillage);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
BOOST_REQUIRE(abs(spillage) < 0.0001);
|
||||||
|
|
||||||
|
// Check that inflow link is closed
|
||||||
|
error = EN_getlinkvalue(ph, Lindex, EN_FLOW, &inflow);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
BOOST_REQUIRE(abs(inflow) < 0.0001);
|
||||||
|
|
||||||
|
// Turn tank overflow option on
|
||||||
|
error = EN_setnodevalue(ph, Nindex, EN_CANOVERFLOW, 1);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
// Solve hydraulics again
|
||||||
|
error = EN_solveH(ph);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
// Check that tank remains full
|
||||||
|
error = EN_getnodevalue(ph, Nindex, EN_TANKLEVEL, &level);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
BOOST_REQUIRE(abs(level - 130.0) < 0.0001);
|
||||||
|
|
||||||
|
// Check that there is spillage equal to tank inflow
|
||||||
|
// (inflow has neg. sign since tank is start node of inflow pipe)
|
||||||
|
error = EN_getnodevalue(ph, Nindex, EN_DEMAND, &spillage);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
BOOST_REQUIRE(spillage > 0.0001);
|
||||||
|
error = EN_getlinkvalue(ph, Lindex, EN_FLOW, &inflow);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
BOOST_REQUIRE(abs(-inflow - spillage) < 0.0001);
|
||||||
|
|
||||||
|
// Save project to file and then close it
|
||||||
|
error = EN_saveinpfile(ph, testFile);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
error = EN_close(ph);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
// Re-open saved file & run it
|
||||||
|
error = EN_open(ph, testFile, DATA_PATH_RPT, "");
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
error = EN_solveH(ph);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
// Check that tank spillage has same value as before
|
||||||
|
error = EN_getnodevalue(ph, Nindex, EN_DEMAND, &spillage2);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
BOOST_REQUIRE(abs(spillage - spillage2) < 0.0001);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
error = EN_close(ph);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
error = EN_deleteproject(&ph);
|
||||||
|
BOOST_REQUIRE(error == 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
Reference in New Issue
Block a user