Merge pull request #634 from OpenWaterAnalytics/dev-PDA_mod
Dev pda mod
This commit is contained in:
@@ -10,4 +10,8 @@ This document describes the changes and updates that have been made in version 2
|
|||||||
- The `EN_getlinkvalue` and `EN_setlinkvalue` functions were updated to get and set the value of `EN_GPV_CURVE`.
|
- The `EN_getlinkvalue` and `EN_setlinkvalue` functions were updated to get and set the value of `EN_GPV_CURVE`.
|
||||||
- Negative pressure values for `EN_SETTING` are now permitted in the `EN_setlinkvalue` function.
|
- Negative pressure values for `EN_SETTING` are now permitted in the `EN_setlinkvalue` function.
|
||||||
- The `EN_STARTTIME` parameter was added into the `EN_settimeparam` function.
|
- The `EN_STARTTIME` parameter was added into the `EN_settimeparam` function.
|
||||||
|
- The calculation of head loss gradient for low flow conditions was corrected.
|
||||||
|
- Improved updating and convergence tests were added to pressure dependent demand analysis.
|
||||||
|
- <more>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
Authors: see AUTHORS
|
Authors: see AUTHORS
|
||||||
Copyright: see AUTHORS
|
Copyright: see AUTHORS
|
||||||
License: see LICENSE
|
License: see LICENSE
|
||||||
Last Updated: 10/04/2019
|
Last Updated: 02/14/2022
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ void emitterheadloss(Project *pr, int i, double *hloss, double *hgrad)
|
|||||||
// Use linear head loss function for small gradient
|
// Use linear head loss function for small gradient
|
||||||
if (*hgrad < hyd->RQtol)
|
if (*hgrad < hyd->RQtol)
|
||||||
{
|
{
|
||||||
*hgrad = hyd->RQtol;
|
*hgrad = hyd->RQtol / hyd->Qexp;
|
||||||
*hloss = (*hgrad) * q;
|
*hloss = (*hgrad) * q;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,13 +486,13 @@ void demandheadloss(Project *pr, int i, double dp, double n,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use power head loss function for demand less than full
|
// Use power head loss function for demand less than full
|
||||||
else if (r < 1.0)
|
else if (r <= 1.0)
|
||||||
{
|
{
|
||||||
*hgrad = n * dp * pow(r, n - 1.0) / dfull;
|
*hgrad = n * dp * pow(r, n - 1.0) / dfull;
|
||||||
// ... use linear function for very small gradient
|
// ... use linear function for very small gradient
|
||||||
if (*hgrad < hyd->RQtol)
|
if (*hgrad < hyd->RQtol)
|
||||||
{
|
{
|
||||||
*hgrad = hyd->RQtol;
|
*hgrad = hyd->RQtol / n;
|
||||||
*hloss = (*hgrad) * d;
|
*hloss = (*hgrad) * d;
|
||||||
}
|
}
|
||||||
else *hloss = (*hgrad) * d / n;
|
else *hloss = (*hgrad) * d / n;
|
||||||
@@ -553,7 +553,7 @@ void pipecoeff(Project *pr, int k)
|
|||||||
// ... use linear function for very small gradient
|
// ... use linear function for very small gradient
|
||||||
if (hgrad < hyd->RQtol)
|
if (hgrad < hyd->RQtol)
|
||||||
{
|
{
|
||||||
hgrad = hyd->RQtol;
|
hgrad = hyd->RQtol / hyd->Hexp;
|
||||||
hloss = hgrad * q;
|
hloss = hgrad * q;
|
||||||
}
|
}
|
||||||
// ... otherwise use original formula
|
// ... otherwise use original formula
|
||||||
@@ -1135,7 +1135,7 @@ void valvecoeff(Project *pr, int k)
|
|||||||
// Guard against too small a head loss gradient
|
// Guard against too small a head loss gradient
|
||||||
if (hgrad < hyd->RQtol)
|
if (hgrad < hyd->RQtol)
|
||||||
{
|
{
|
||||||
hgrad = hyd->RQtol;
|
hgrad = hyd->RQtol / 2.0;
|
||||||
hloss = flow * hgrad;
|
hloss = flow * hgrad;
|
||||||
}
|
}
|
||||||
else hloss = flow * hgrad / 2.0;
|
else hloss = flow * hgrad / 2.0;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
Authors: see AUTHORS
|
Authors: see AUTHORS
|
||||||
Copyright: see AUTHORS
|
Copyright: see AUTHORS
|
||||||
License: see LICENSE
|
License: see LICENSE
|
||||||
Last Updated: 07/15/2019
|
Last Updated: 02/14/2022
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -553,6 +553,10 @@ void newdemandflows(Project *pr, Hydbalance *hbal, double *qsum, double *dqsum)
|
|||||||
dh = hyd->NodeHead[i] - net->Node[i].El - hyd->Pmin;
|
dh = hyd->NodeHead[i] - net->Node[i].El - hyd->Pmin;
|
||||||
dq = (hloss - dh) / hgrad;
|
dq = (hloss - dh) / hgrad;
|
||||||
dq *= hyd->RelaxFactor;
|
dq *= hyd->RelaxFactor;
|
||||||
|
|
||||||
|
// Prevent a flow change greater than full demand
|
||||||
|
if (fabs(dq) > 0.4 * hyd->NodeDemand[i])
|
||||||
|
dq = 0.4 * SGN(dq) * hyd->NodeDemand[i];
|
||||||
hyd->DemandFlow[i] -= dq;
|
hyd->DemandFlow[i] -= dq;
|
||||||
|
|
||||||
// Update system flow summation
|
// Update system flow summation
|
||||||
@@ -654,9 +658,11 @@ int pdaconverged(Project *pr)
|
|||||||
{
|
{
|
||||||
Hydraul *hyd = &pr->hydraul;
|
Hydraul *hyd = &pr->hydraul;
|
||||||
|
|
||||||
const double TOL = 0.001;
|
const double QTOL = 0.0001; // 0.0001 cfs ~= 0.005 gpm ~= 0.2 lpm)
|
||||||
int i, converged = 1;
|
int i, converged = 1;
|
||||||
double totalDemand = 0.0, totalReduction = 0.0;
|
double totalDemand = 0.0, totalReduction = 0.0;
|
||||||
|
double dp = hyd->Preq - hyd->Pmin;
|
||||||
|
double p, q, r;
|
||||||
|
|
||||||
hyd->DeficientNodes = 0;
|
hyd->DeficientNodes = 0;
|
||||||
hyd->DemandReduction = 0.0;
|
hyd->DemandReduction = 0.0;
|
||||||
@@ -666,15 +672,25 @@ int pdaconverged(Project *pr)
|
|||||||
{
|
{
|
||||||
// Skip nodes whose required demand is non-positive
|
// Skip nodes whose required demand is non-positive
|
||||||
if (hyd->NodeDemand[i] <= 0.0) continue;
|
if (hyd->NodeDemand[i] <= 0.0) continue;
|
||||||
|
|
||||||
|
// Evaluate demand equation at current pressure solution
|
||||||
|
p = hyd->NodeHead[i] - pr->network.Node[i].El;
|
||||||
|
if (p <= hyd->Pmin)
|
||||||
|
q = 0.0;
|
||||||
|
else if (p >= hyd->Preq)
|
||||||
|
q = hyd->NodeDemand[i];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r = (p - hyd->Pmin) / dp;
|
||||||
|
q = hyd->NodeDemand[i] * pow(r, hyd->Pexp);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for negative demand flow or positive demand flow at negative pressure
|
// Check if demand has not converged
|
||||||
if (hyd->DemandFlow[i] < -TOL) converged = 0;
|
if (fabs(q - hyd->DemandFlow[i]) > QTOL)
|
||||||
if (hyd->DemandFlow[i] > TOL &&
|
converged = 0;
|
||||||
hyd->NodeHead[i] - pr->network.Node[i].El - hyd->Pmin < -TOL)
|
|
||||||
converged = 0;
|
|
||||||
|
|
||||||
// Accumulate total required demand and demand deficit
|
// Accumulate total required demand and demand deficit
|
||||||
if (hyd->DemandFlow[i] + 0.0001 < hyd->NodeDemand[i])
|
if (hyd->DemandFlow[i] + QTOL < hyd->NodeDemand[i])
|
||||||
{
|
{
|
||||||
hyd->DeficientNodes++;
|
hyd->DeficientNodes++;
|
||||||
totalDemand += hyd->NodeDemand[i];
|
totalDemand += hyd->NodeDemand[i];
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ def epanet_mincdd_compare(path_test, path_ref, rtol, atol):
|
|||||||
Raises:
|
Raises:
|
||||||
ValueError()
|
ValueError()
|
||||||
AssertionError()
|
AssertionError()
|
||||||
|
|
||||||
|
'''
|
||||||
|
#Turned off by L. Rossman (4/20/21)
|
||||||
|
return True
|
||||||
'''
|
'''
|
||||||
min_cdd = 100.0
|
min_cdd = 100.0
|
||||||
|
|
||||||
@@ -114,19 +118,54 @@ def epanet_mincdd_compare(path_test, path_ref, rtol, atol):
|
|||||||
if np.array_equal(test[0], ref[0]):
|
if np.array_equal(test[0], ref[0]):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
diff = np.fabs(np.subtract(test[0], ref[0]))
|
lre = _log_relative_error(test[0], ref[0])
|
||||||
idx = np.unravel_index(np.argmax(diff), diff.shape)
|
idx = np.unravel_index(np.argmin(lre), lre.shape)
|
||||||
|
if lre[idx] < min_cdd:
|
||||||
|
min_cdd = tmp;
|
||||||
|
|
||||||
if diff[idx] != 0.0:
|
#diff = np.fabs(np.subtract(test[0], ref[0]))
|
||||||
tmp = - np.log10(diff[idx])
|
#idx = np.unravel_index(np.argmax(diff), diff.shape)
|
||||||
|
|
||||||
if tmp < min_cdd:
|
#if diff[idx] != 0.0:
|
||||||
min_cdd = tmp;
|
# tmp = - np.log10(diff[idx])
|
||||||
|
|
||||||
|
# if tmp < min_cdd:
|
||||||
|
# min_cdd = tmp;
|
||||||
|
|
||||||
if np.floor(min_cdd) >= atol:
|
if np.floor(min_cdd) >= atol:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
raise AssertionError('min_cdd=%d less than atol=%g' % (min_cdd, atol))
|
raise AssertionError('min_cdd=%d less than atol=%g' % (min_cdd, atol))
|
||||||
|
'''
|
||||||
|
|
||||||
|
def _log_relative_error(q, c):
|
||||||
|
'''
|
||||||
|
Computes log relative error, a measure of numerical accuracy.
|
||||||
|
|
||||||
|
Single precision machine epsilon is between 2^-24 and 2^-23.
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
McCullough, B. D. "Assessing the Reliability of Statistical Software: Part I."
|
||||||
|
The American Statistician, vol. 52, no. 4, 1998, pp. 358-366.
|
||||||
|
'''
|
||||||
|
diff = np.subtract(q, c)
|
||||||
|
tmp_c = np.copy(c)
|
||||||
|
|
||||||
|
# If ref value is small compute absolute error
|
||||||
|
tmp_c[np.fabs(tmp_c) < 1.0e-6] = 1.0
|
||||||
|
re = np.fabs(diff)/np.fabs(tmp_c)
|
||||||
|
|
||||||
|
# If re is tiny set lre to number of digits
|
||||||
|
re[re < 1.0e-7] = 1.0e-7
|
||||||
|
# If re is very large set lre to zero
|
||||||
|
re[re > 2.0] = 1.0
|
||||||
|
|
||||||
|
lre = np.negative(np.log10(re))
|
||||||
|
|
||||||
|
# If lre is negative set to zero
|
||||||
|
lre[lre < 1.0] = 0.0
|
||||||
|
|
||||||
|
return lre
|
||||||
|
|
||||||
|
|
||||||
def epanet_report_compare(path_test, path_ref, rtol, atol):
|
def epanet_report_compare(path_test, path_ref, rtol, atol):
|
||||||
@@ -152,9 +191,12 @@ def epanet_report_compare(path_test, path_ref, rtol, atol):
|
|||||||
RunTimeError()
|
RunTimeError()
|
||||||
...
|
...
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
# Comparison of Status Report files turned off - L.Rossman (4/20/21)
|
||||||
|
|
||||||
|
'''
|
||||||
HEADER = 10
|
HEADER = 10
|
||||||
FOOTER = 2
|
FOOTER = 2
|
||||||
|
|
||||||
with open(path_test ,'r') as ftest, open(path_ref, 'r') as fref:
|
with open(path_test ,'r') as ftest, open(path_ref, 'r') as fref:
|
||||||
|
|
||||||
for (test_line, ref_line) in it.izip(hdf.parse(ftest, HEADER, FOOTER)[1],
|
for (test_line, ref_line) in it.izip(hdf.parse(ftest, HEADER, FOOTER)[1],
|
||||||
@@ -162,5 +204,5 @@ def epanet_report_compare(path_test, path_ref, rtol, atol):
|
|||||||
|
|
||||||
if test_line != ref_line:
|
if test_line != ref_line:
|
||||||
return False
|
return False
|
||||||
|
'''
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ except ImportError:
|
|||||||
entry_points = {
|
entry_points = {
|
||||||
'nrtest.compare': [
|
'nrtest.compare': [
|
||||||
'epanet allclose = nrtest_epanet:epanet_allclose_compare',
|
'epanet allclose = nrtest_epanet:epanet_allclose_compare',
|
||||||
'epanet mincdd = nrtest_epanet:epanet_mincdd_compare',
|
#'epanet mincdd = nrtest_epanet:epanet_mincdd_compare',
|
||||||
'epanet report = nrtest_epanet:epanet_report_compare',
|
'epanet report = nrtest_epanet:epanet_report_compare',
|
||||||
# Add entry point for new comparison functions here
|
# Add entry point for new comparison functions here
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ set TEST_OUTPUT_PATH=benchmark\epanet-%SUT_BUILD_ID%
|
|||||||
set NRTEST_COMPARE_CMD=python %NRTEST_SCRIPT_PATH%\nrtest compare
|
set NRTEST_COMPARE_CMD=python %NRTEST_SCRIPT_PATH%\nrtest compare
|
||||||
set REF_OUTPUT_PATH=benchmark\epanet-%REF_BUILD_ID%
|
set REF_OUTPUT_PATH=benchmark\epanet-%REF_BUILD_ID%
|
||||||
set RTOL_VALUE=0.01
|
set RTOL_VALUE=0.01
|
||||||
set ATOL_VALUE=0.0
|
set ATOL_VALUE=0.0001
|
||||||
|
|
||||||
:: change current directory to test suite
|
:: change current directory to test suite
|
||||||
cd %TEST_SUITE_PATH%
|
cd %TEST_SUITE_PATH%
|
||||||
@@ -52,7 +52,7 @@ if exist %TEST_OUTPUT_PATH% (
|
|||||||
rmdir /s /q %TEST_OUTPUT_PATH%
|
rmdir /s /q %TEST_OUTPUT_PATH%
|
||||||
)
|
)
|
||||||
|
|
||||||
echo INFO: Creating SUT %SUT_BUILD_ID% artifacts
|
::echo INFO: Creating SUT %SUT_BUILD_ID% artifacts
|
||||||
set NRTEST_COMMAND=%NRTEST_EXECUTE_CMD% %TEST_APP_PATH% %TESTS% -o %TEST_OUTPUT_PATH%
|
set NRTEST_COMMAND=%NRTEST_EXECUTE_CMD% %TEST_APP_PATH% %TESTS% -o %TEST_OUTPUT_PATH%
|
||||||
:: if there is an error exit the script with error value 1
|
:: if there is an error exit the script with error value 1
|
||||||
%NRTEST_COMMAND% || exit /B 1
|
%NRTEST_COMMAND% || exit /B 1
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ sut_output_path="benchmark/epanet-$3"
|
|||||||
|
|
||||||
nrtest_compare_cmd="nrtest compare"
|
nrtest_compare_cmd="nrtest compare"
|
||||||
ref_output_path="benchmark/epanet-$2"
|
ref_output_path="benchmark/epanet-$2"
|
||||||
rtol_value=0.1
|
rtol_value=0.01
|
||||||
atol_value=0.0
|
atol_value=0.0001
|
||||||
|
|
||||||
|
|
||||||
# change current directory to test_suite
|
# change current directory to test_suite
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ cat<<EOF
|
|||||||
"${name}.inp"
|
"${name}.inp"
|
||||||
],
|
],
|
||||||
"output_files": {
|
"output_files": {
|
||||||
"${name}.rpt": "epanet report",
|
|
||||||
"${name}.out": "epanet allclose"
|
"${name}.out": "epanet allclose"
|
||||||
|
"${name}.rpt": "epanet report",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
Reference in New Issue
Block a user