Fix problems with setting tank parameters (issue #464 )
This commit is contained in:
165
src/epanet.c
165
src/epanet.c
@@ -7,7 +7,7 @@
|
|||||||
Authors: see AUTHORS
|
Authors: see AUTHORS
|
||||||
Copyright: see AUTHORS
|
Copyright: see AUTHORS
|
||||||
License: see LICENSE
|
License: see LICENSE
|
||||||
Last Updated: 04/18/2019
|
Last Updated: 04/25/2019
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -2257,6 +2257,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
|
|||||||
int i, j, n;
|
int i, j, n;
|
||||||
Psource source;
|
Psource source;
|
||||||
double hTmp;
|
double hTmp;
|
||||||
|
double vTmp;
|
||||||
|
|
||||||
if (!p->Openflag) return 102;
|
if (!p->Openflag) return 102;
|
||||||
if (index <= 0 || index > nNodes) return 203;
|
if (index <= 0 || index > nNodes) return 203;
|
||||||
@@ -2365,77 +2366,132 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case EN_TANKDIAM:
|
case EN_TANKDIAM:
|
||||||
if (value <= 0.0) return 209;
|
if (value <= 0.0) return 209; // invalid diameter
|
||||||
if (index <= nJuncs) return 0;
|
if (index <= nJuncs) return 0; // node is not a tank
|
||||||
j = index - nJuncs;
|
j = index - nJuncs; // tank index
|
||||||
if (Tank[j].A > 0.0)
|
if (Tank[j].A == 0.0) return 0; // tank is a reservoir
|
||||||
|
value /= Ucf[ELEV]; // diameter in feet
|
||||||
|
Tank[j].A = PI * SQR(value) / 4.0; // new tank area
|
||||||
|
if (Tank[j].Vcurve > 0) // tank has a volume curve
|
||||||
{
|
{
|
||||||
value /= Ucf[ELEV];
|
Tank[j].Vcurve = 0; // remove volume curve
|
||||||
Tank[j].A = PI * SQR(value) / 4.0;
|
|
||||||
Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin);
|
// Since the volume curve no longer applies we assume that the tank's
|
||||||
Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
|
// shape below Hmin is cylindrical and Vmin equals area times Hmin
|
||||||
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
|
Tank[j].Vmin = Tank[j].A * Tank[j].Hmin;
|
||||||
Tank[j].Vcurve = 0;
|
|
||||||
}
|
}
|
||||||
|
// Since tank's area has changed its volumes must be updated
|
||||||
|
// NOTE: For a non-volume curve tank we can't change the Vmin
|
||||||
|
// associated with a Hmin since we don't know the tank's
|
||||||
|
// shape below Hmin. Vmin can always be changed by setting
|
||||||
|
// EN_MINVOLUME in a subsequent function call.
|
||||||
|
Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // new init. volume
|
||||||
|
vTmp = Tank[j].Vmax; // old max. volume
|
||||||
|
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); // new max. volume
|
||||||
|
Tank[j].V1max *= Tank[j].Vmax / vTmp; // new mix zone volume
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EN_MINVOLUME:
|
case EN_MINVOLUME:
|
||||||
if (value < 0.0) return 209;
|
if (value < 0.0) return 209; // invalid volume
|
||||||
if (index <= nJuncs) return 0;
|
if (index <= nJuncs) return 0; // node is not a tank
|
||||||
j = index - nJuncs;
|
j = index - nJuncs; // tank index
|
||||||
if (Tank[j].A > 0.0)
|
if (Tank[j].A == 0.0) return 0; // tank is a reservoir
|
||||||
|
i = Tank[j].Vcurve; // volume curve index
|
||||||
|
if (i > 0) // tank has a volume curve
|
||||||
{
|
{
|
||||||
Tank[j].Vmin = value / Ucf[VOLUME];
|
curve = &net->Curve[i]; // curve object
|
||||||
Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
|
if (value < curve->Y[0]) return 225; // volume off of curve
|
||||||
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
|
value /= Ucf[VOLUME]; // volume in ft3
|
||||||
|
hTmp = tankgrade(p, j, value); // head at given volume
|
||||||
|
if (hTmp > Tank[j].H0 ||
|
||||||
|
hTmp > Tank[j].Hmax) return 225; // invalid water levels
|
||||||
|
Tank[j].Hmin = hTmp; // new min. head
|
||||||
|
Tank[j].Vmin = value; // new min. volume
|
||||||
|
}
|
||||||
|
else // tank has no volume curve
|
||||||
|
{
|
||||||
|
// If the volume supplied by the function is 0 then the tank shape
|
||||||
|
// below Hmin is assumed to be cylindrical and a new Vmin value is
|
||||||
|
// computed. Otherwise Vmin is set to the supplied value.
|
||||||
|
if (value == 0.0) Tank[j].Vmin = Tank[j].A * Tank[j].Hmin;
|
||||||
|
else Tank[j].Vmin = value / Ucf[VOLUME];
|
||||||
|
|
||||||
|
// Since Vmin changes the other volumes need updating
|
||||||
|
Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // new init. volume
|
||||||
|
vTmp = Tank[j].Vmax; // old max. volume
|
||||||
|
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); // new max. volume
|
||||||
|
Tank[j].V1max *= Tank[j].Vmax / vTmp; // new mix zone volume
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EN_VOLCURVE:
|
case EN_VOLCURVE:
|
||||||
if (index < nJuncs) return 0;
|
// NOTE: Setting EN_VOLCURVE to 0 to remove a volume curve is not valid.
|
||||||
i = ROUND(value);
|
// One should instead set a value for EN_TANKDIAM.
|
||||||
if (i < 0 || i > net->Ncurves) return 205;
|
i = ROUND(value); // curve index
|
||||||
curve = &net->Curve[i];
|
if (i <= 0 ||
|
||||||
j = index - nJuncs;
|
i > net->Ncurves) return 205; // invalid curve index
|
||||||
if (Tank[j].A == 0.0) return 0;
|
if (index <= nJuncs) return 0; // node not a tank
|
||||||
|
j = index - nJuncs; // tank index
|
||||||
|
if (Tank[j].A == 0.0) return 0; // tank is a reservoir
|
||||||
|
curve = &net->Curve[i]; // curve object
|
||||||
|
|
||||||
|
// Check that tank's min/max levels lie within curve
|
||||||
|
value = (Tank[j].Hmin - Node[index].El) * Ucf[ELEV];
|
||||||
|
if (value < curve->X[0]) return 225;
|
||||||
|
value = (Tank[j].Hmax - Node[index].El) * Ucf[ELEV];
|
||||||
n = curve->Npts - 1;
|
n = curve->Npts - 1;
|
||||||
if (Tank[j].Vmin * Ucf[VOLUME] < curve->Y[0] ||
|
if (value > curve->X[n]) return 225;
|
||||||
Tank[j].Vmax * Ucf[VOLUME] > curve->Y[n]) return 225;
|
|
||||||
Tank[j].Vcurve = i;
|
Tank[j].Vcurve = i; // assign curve to tank
|
||||||
Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin);
|
Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); // new min. volume
|
||||||
Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
|
Tank[j].V0 = tankvolume(p, j, Tank[j].H0); // new init. volume
|
||||||
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
|
vTmp = Tank[j].Vmax; // old max. volume
|
||||||
Tank[j].A = (curve->Y[n] - curve->Y[0]) / (curve->X[n] - curve->X[0]);
|
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); // new max. volume
|
||||||
|
Tank[j].V1max *= Tank[j].Vmax / vTmp; // new mix zone volume
|
||||||
|
Tank[j].A = (curve->Y[n] - curve->Y[0]) / // nominal area
|
||||||
|
(curve->X[n] - curve->X[0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EN_MINLEVEL:
|
case EN_MINLEVEL:
|
||||||
if (value < 0.0) return 209;
|
if (value < 0.0) return 209; // invalid water level
|
||||||
if (index <= nJuncs) return 0; // not a tank or reservoir
|
if (index <= nJuncs) return 0; // node not a tank
|
||||||
j = index - nJuncs;
|
j = index - nJuncs; // tank index
|
||||||
if (Tank[j].A == 0.0) return 0; // node is a reservoir
|
if (Tank[j].A == 0.0) return 0; // tank is a reservoir
|
||||||
hTmp = value / Ucf[ELEV] + Node[index].El;
|
hTmp = value / Ucf[ELEV] + Node[index].El; // convert level to head
|
||||||
if (hTmp < Tank[j].Hmax && hTmp <= Tank[j].H0)
|
if (hTmp >= Tank[j].Hmax ||
|
||||||
|
hTmp > Tank[j].H0) return 225; // invalid water levels
|
||||||
|
i = Tank[j].Vcurve; // volume curve index
|
||||||
|
if (i > 0) // tank has a volume curve
|
||||||
{
|
{
|
||||||
if (Tank[j].Vcurve > 0) return 0;
|
curve = &net->Curve[i];
|
||||||
Tank[j].Hmin = hTmp;
|
if (value < curve->X[0]) return 225; // new level is off curve
|
||||||
Tank[j].Vmin = (Tank[j].Hmin - Node[index].El) * Tank[j].A;
|
Tank[j].Vmin = tankvolume(p, j, hTmp); // new min. volume
|
||||||
}
|
}
|
||||||
else return 225;
|
Tank[j].Hmin = hTmp;
|
||||||
|
// NOTE: We assume that for non-volume curve tanks Vmin doesn't change
|
||||||
|
// with Hmin. If not the case then a subsequent call setting
|
||||||
|
// EN_MINVOLUME must be made.
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EN_MAXLEVEL:
|
case EN_MAXLEVEL:
|
||||||
if (value < 0.0) return 209;
|
if (value <= 0.0) return 209; // invalid water level
|
||||||
if (index <= nJuncs) return 0; // not a tank or reservoir
|
if (index <= nJuncs) return 0; // node not a tank
|
||||||
j = index - nJuncs;
|
j = index - nJuncs; // tank index
|
||||||
if (Tank[j].A == 0.0) return 0; // node is a reservoir
|
if (Tank[j].A == 0.0) return 0; // tank is a reservoir
|
||||||
hTmp = value / Ucf[ELEV] + Node[index].El;
|
hTmp = value / Ucf[ELEV] + Node[index].El; // convert level to head
|
||||||
if (hTmp > Tank[j].Hmin && hTmp >= Tank[j].H0)
|
if (hTmp < Tank[j].Hmin ||
|
||||||
|
hTmp < Tank[j].H0) return 225; // invalid water levels
|
||||||
|
i = Tank[j].Vcurve; // volume curve index
|
||||||
|
if (i > 0) // tank has a volume curve
|
||||||
{
|
{
|
||||||
if (Tank[j].Vcurve > 0) return 0;
|
curve = &net->Curve[i];
|
||||||
Tank[j].Hmax = hTmp;
|
n = curve->Npts - 1; // last point on curve
|
||||||
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
|
if (value > curve->X[n]) return 225; // new level is off curve
|
||||||
}
|
}
|
||||||
else return 225;
|
Tank[j].Hmax = hTmp; // new max. head
|
||||||
|
vTmp = Tank[j].Vmax; // old max. volume
|
||||||
|
Tank[j].Vmax = tankvolume(p, j, hTmp); // new max. volume
|
||||||
|
Tank[j].V1max *= Tank[j].Vmax / vTmp; // new mix zone volume
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EN_MIXMODEL:
|
case EN_MIXMODEL:
|
||||||
@@ -2631,7 +2687,12 @@ int DLLEXPORT EN_settankdata(EN_Project p, int index, double elev,
|
|||||||
Tank[j].Hmin = elevation + minlvl / Ucf[ELEV];
|
Tank[j].Hmin = elevation + minlvl / Ucf[ELEV];
|
||||||
Tank[j].Hmax = elevation + maxlvl / Ucf[ELEV];
|
Tank[j].Hmax = elevation + maxlvl / Ucf[ELEV];
|
||||||
Tank[j].Vcurve = curveIndex;
|
Tank[j].Vcurve = curveIndex;
|
||||||
Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin);
|
if (curveIndex == 0)
|
||||||
|
{
|
||||||
|
if (minvol > 0.0) Tank[j].Vmin = minvol / Ucf[VOLUME];
|
||||||
|
else Tank[j].Vmin = Tank[j].A * Tank[j].Hmin;
|
||||||
|
}
|
||||||
|
else Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin);
|
||||||
Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
|
Tank[j].V0 = tankvolume(p, j, Tank[j].H0);
|
||||||
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
|
Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user