Wrong conversion for General Purpose Valve Curves
Summary I believe that General Purpose Valves curves are being converted using the wrong unit: 'headloss' rather than 'hydraulichead'.
(Headloss is always 1/1000 wheras hydraulic head is metres/feet).
https://github.com/USEPA/WNTR/blob/c41b5e42a59e8ab291a0734f0934a7c52a3c65da/wntr/epanet/io.py#L848C21-L848C76
EPANET documentation defines GPV curves as "flow - head loss relationship", https://epanet22.readthedocs.io/en/latest/3_network_model.html. Can you provide an example using WNTR of the incorrect conversion?
Sorry I didn't see your reply !
This is correct, but the WNTR HydParam.HeadLoss is actually 'unit headloss ' measured in metres/1000metres or feet/1000feet. The headloss referred to in the docs is units of head (i.e. metres / feet). This is represented in wntr as HydParam.HydraulicHead
See also the UI in EPANET which specifies the units of headloss as feet.
There is actually a related problem with the link results, where pipes headloss is in units of metres/1000 metres, whilst within the same dataframe valves and pumps are in units of metres. Whilst this could be considered correct behaviour it is extremely confusing. (I could open an new issue for this if appropriate - the solution isn't obvious !).
As an example:
The below .inp file will have a leadloss curve of flow: 1 lps headloss: 1 metres when opened in epanet.
When opened with wntr it will give the following
wn.curves['1']
<Curve: '1', curve_type='HEADLOSS', points=[(0.001, 0.001)]>
flow is correctly converted to 0.001 m3/s, but headloss is not 1 metre
[TITLE]
[JUNCTIONS]
;ID Elev Demand Pattern
2 0 0 ;
3 0 0 ;
[RESERVOIRS]
;ID Head Pattern
[TANKS]
;ID Elevation InitLevel MinLevel MaxLevel Diameter MinVol VolCurve Overflow
[PIPES]
;ID Node1 Node2 Length Diameter Roughness MinorLoss Status
[PUMPS]
;ID Node1 Node2 Parameters
[VALVES]
;ID Node1 Node2 Diameter Type Setting MinorLoss
1 2 3 12 GPV 1 0 ;
[TAGS]
[DEMANDS]
;Junction Demand Pattern Category
[STATUS]
;ID Status/Setting
[PATTERNS]
;ID Multipliers
;
[CURVES]
;ID X-Value Y-Value
;HEADLOSS:
1 1 1
[CONTROLS]
[RULES]
[ENERGY]
Global Efficiency 75
Global Price 0
Demand Charge 0
[EMITTERS]
;Junction Coefficient
[QUALITY]
;Node InitQual
[SOURCES]
;Node Type Quality Pattern
[REACTIONS]
;Type Pipe/Tank Coefficient
[REACTIONS]
Order Bulk 1
Order Tank 1
Order Wall 1
Global Bulk 0
Global Wall 0
Limiting Potential 0
Roughness Correlation 0
[MIXING]
;Tank Model
[TIMES]
Duration 0
Hydraulic Timestep 1:00
Quality Timestep 0:05
Pattern Timestep 1:00
Pattern Start 0:00
Report Timestep 1:00
Report Start 0:00
Start ClockTime 12 am
Statistic None
[REPORT]
Status No
Summary No
Page 0
[OPTIONS]
Units LPS
Headloss H-W
Specific Gravity 1
Viscosity 1
Trials 40
Accuracy 0.001
CHECKFREQ 2
MAXCHECK 10
DAMPLIMIT 0
Unbalanced Continue 10
Pattern 1
Demand Multiplier 1.0
Emitter Exponent 0.5
Quality None mg/L
Diffusivity 1
Tolerance 0.01
[COORDINATES]
;Node X-Coord Y-Coord
2 1645.710 8875.740
3 6216.716 8816.568
[VERTICES]
;Link X-Coord Y-Coord
[LABELS]
;X-Coord Y-Coord Label & Anchor Node
[BACKDROP]
DIMENSIONS 0.000 0.000 10000.000 10000.000
UNITS None
FILE
OFFSET 0.00 0.00
[END]
Headloss in WNTR is unitless (meters/meter) in SI units. The documentation needs to be updated in https://usepa.github.io/WNTR/units.html.
Headloss first gets converted using HydParam.HydraulicHead (length unit), then using HydParam.HeadLoss (to divide out or multiply by 1000). See the following:
https://github.com/USEPA/WNTR/blob/8a61184a69efbbdc07756bcc083f10b4255bdaf3/wntr/epanet/util.py#L582C1-L591C67
https://github.com/USEPA/WNTR/blob/8a61184a69efbbdc07756bcc083f10b4255bdaf3/wntr/epanet/util.py#L679C4-L689C1
You can also use the following code to test this out.
from wntr.epanet.util import to_si, from_si, HydParam
flow_units = 'LPS'
inp_file_value = 1 # m/1000m
SI_value = to_si(flow_units, inp_file_value, HydParam.HeadLoss) # unitless
convert_back = from_si(flow_units, SI_value, HydParam.HeadLoss) # m/1000m
print(inp_file_value, SI_value, convert_back)
Does that resolve the issue?
I think the HydParam.HeadLoss is only for simulation results for pipes. I would refer to this as 'unit headloss'
For valves and pumps it doesn't make sense as they don't have any length. I think this is why the documentation says that metres is used (HydParam.HydraulicHead). This is what epanet does.