/*
* Copyright (C) 2012 Addition, Lda. (addition at addition dot pt)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package org.addition.epanet.network.io.input;
import org.addition.epanet.Constants;
import org.addition.epanet.util.ENException;
import org.addition.epanet.network.FieldsMap;
import org.addition.epanet.network.FieldsMap.*;
import org.addition.epanet.network.Network;
import org.addition.epanet.network.PropertiesMap;
import org.addition.epanet.util.ENLevels;
import org.addition.epanet.util.Utilities;
import org.addition.epanet.network.structures.Link.*;
import java.util.logging.Logger;
import org.addition.epanet.network.structures.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Abstract input file parser.
*/
public abstract class InputParser {
/**
* Reference to the error logger.
*/
protected Logger log;
protected InputParser(Logger log) {
this.log = log;
}
public static InputParser create(Network.FileType type, Logger log) {
switch (type) {
case INP_FILE:
return new InpParser(log);
case EXCEL_FILE:
return new ExcelParser(log);
case XML_FILE:
return new XMLParser(log,false);
case XML_GZ_FILE:
return new XMLParser(log,true);
case NULL_FILE:
return new NullParser(log);
}
return null;
}
public abstract Network parse(Network net, File f) throws ENException;
/**
* Prepare the hydraulic network for simulation.
* @param net
* @throws ENException
*/
protected void convert(Network net) throws ENException {
initTanks(net);
initPumps(net);
initPatterns(net);
checkUnlinked(net);
convertUnits(net);
}
/**
* Adjust simulation configurations.
* @param net
* @throws ENException
*/
public static void adjust(Network net) throws ENException {
adjustData(net);
}
/**
* Adjust simulation configurations.
* @param net
* @throws ENException
*/
private static void adjustData(Network net) throws ENException {
PropertiesMap m = net.getPropertiesMap();
int i;
double ucf;
if (m.getPstep() <= 0) m.setPstep(3600);
if (m.getRstep() == 0) m.setRstep(m.getPstep());
if (m.getHstep() <= 0) m.setHstep(3600);
if (m.getHstep() > m.getPstep()) m.setHstep(m.getPstep());
if (m.getHstep() > m.getRstep()) m.setHstep(m.getRstep());
if (m.getRstart() > m.getDuration()) m.setRstart(0);
if (m.getDuration() == 0) m.setQualflag(PropertiesMap.QualType.NONE);
if (m.getQstep() == 0) m.setQstep(m.getHstep() / 10);
if (m.getRulestep() == 0) m.setRulestep(m.getHstep() / 10);
m.setRulestep(Math.min(m.getRulestep(), m.getHstep()));
m.setQstep(Math.min(m.getQstep(), m.getHstep()));
if (m.getCtol() == Constants.MISSING) {
if (m.getQualflag() == PropertiesMap.QualType.AGE)
m.setCtol(Constants.AGETOL);
else
m.setCtol(Constants.CHEMTOL);
}
switch (m.getFlowflag()) {
case LPS:
case LPM:
case MLD:
case CMH:
case CMD:
m.setUnitsflag(PropertiesMap.UnitsType.SI);
break;
default:
m.setUnitsflag(PropertiesMap.UnitsType.US);
}
if (m.getUnitsflag() != PropertiesMap.UnitsType.SI)
m.setPressflag(PropertiesMap.PressUnitsType.PSI);
else if (m.getPressflag() == PropertiesMap.PressUnitsType.PSI)
m.setPressflag(PropertiesMap.PressUnitsType.METERS);
ucf = 1.0;
if (m.getUnitsflag() == PropertiesMap.UnitsType.SI)
ucf = Math.pow(Constants.MperFT, 2);
if (m.getViscos() == Constants.MISSING)
m.setViscos(Constants.VISCOS);
else if (m.getViscos() > 1.e-3)
m.setViscos(m.getViscos() * Constants.VISCOS);
else
m.setViscos(m.getViscos() / ucf);
if (m.getDiffus() == Constants.MISSING)
m.setDiffus(Constants.DIFFUS);
else if (m.getDiffus() > 1.e-4)
m.setDiffus(m.getDiffus() * Constants.DIFFUS);
else
m.setDiffus(m.getDiffus() / ucf);
if (m.getFormflag() == PropertiesMap.FormType.HW)
m.setHexp(1.852);
else
m.setHexp(2.0);
double Rfactor = m.getRfactor();
PropertiesMap.FormType formFlag = m.getFormflag();
double Kbulk = m.getKbulk();
for (Link link : net.getLinks()) {
if (link.getType().id > LinkType.PIPE.id)
continue;
if (link.getKb() == Constants.MISSING)
link.setKb(Kbulk);
if (link.getKw() == Constants.MISSING)
{
if (Rfactor == 0.0)
link.setKw(m.getKwall());
else if ((link.getRoughness() > 0.0) && (link.getDiameter() > 0.0)) {
if (formFlag == PropertiesMap.FormType.HW)
link.setKw(Rfactor / link.getRoughness());
if (formFlag == PropertiesMap.FormType.DW)
link.setKw(Rfactor / Math.abs(Math.log(link.getRoughness() / link.getDiameter())));
if (formFlag == PropertiesMap.FormType.CM)
link.setKw(Rfactor * link.getRoughness());
}
else
link.setKw(0.0);
}
}
for (Tank tank : net.getTanks())
if (tank.getKb() == Constants.MISSING)
tank.setKb(Kbulk);
Pattern defpat = net.getPattern(m.getDefPatId());
if (defpat == null)
defpat = net.getPattern("");
if (defpat == null)
defpat = net.getPattern("");
for (Node node : net.getNodes()) {
for (Demand d : node.getDemand()) {
if (d.getPattern() == null)
d.setPattern(defpat);
}
}
if (m.getQualflag() == PropertiesMap.QualType.NONE)
net.getFieldsMap().getField(Type.QUALITY).setEnabled(false);
}
/**
* Initialize tank properties.
* @param net Hydraulic network reference.
* @throws ENException
*/
private void initTanks(Network net) throws ENException {
int n = 0;
double a;
for (Tank tank : net.getTanks()) {
if (tank.getArea() == 0.0)
continue;
int levelerr = 0;
if (tank.getH0() > tank.getHmax() ||
tank.getHmin() > tank.getHmax() ||
tank.getH0() < tank.getHmin()
) levelerr = 1;
Curve curv = tank.getVcurve();
if (curv != null) {
n = curv.getNpts() - 1;
if (tank.getHmin() < curv.getX().get(0) ||
tank.getHmax() > curv.getX().get(n))
levelerr = 1;
}
if (levelerr != 0) {
throw new ENException(225, tank.getId());
} else if (curv != null) {
tank.setVmin(Utilities.linearInterpolator(curv.getNpts(), curv.getX(),
curv.getY(), tank.getHmin()));
tank.setVmax(Utilities.linearInterpolator(curv.getNpts(), curv.getX(),
curv.getY(), tank.getHmax()));
tank.setV0(Utilities.linearInterpolator(curv.getNpts(), curv.getX(),
curv.getY(), tank.getH0()));
a = (curv.getY().get(n) - curv.getY().get(0)) /
(curv.getX().get(n) - curv.getX().get(0));
tank.setArea(Math.sqrt(4.0 * a / Constants.PI));
}
}
}
/**
* Convert hydraulic structures values from user units to simulation system units.
* @param net Hydraulic network reference.
* @throws ENException
*/
private void convertUnits(Network net) throws ENException {
int i, j, k;
double ucf;
FieldsMap fMap = net.getFieldsMap();
PropertiesMap pMap = net.getPropertiesMap();
for (Node node : net.getNodes()) {
node.setElevation(node.getElevation() / fMap.getUnits(Type.ELEV));
node.setC0(new double[]{node.getC0()[0] / fMap.getUnits(Type.QUALITY)});
}
for (Node node : net.getNodes()) {
if (node instanceof Tank)
continue;
for (Demand d : node.getDemand()) {
d.setBase(d.getBase() / fMap.getUnits(Type.DEMAND));
}
}
ucf = Math.pow(fMap.getUnits(Type.FLOW), pMap.getQexp()) / fMap.getUnits(Type.PRESSURE);
for (Node node : net.getNodes()) {
if (node instanceof Tank)
continue;
if (node.getKe() > 0.0)
node.setKe(ucf / Math.pow(node.getKe(), pMap.getQexp()));
}
for (Tank tk : net.getTanks()) {
tk.setH0(tk.getElevation() + tk.getH0() / fMap.getUnits(Type.ELEV));
tk.setHmin(tk.getElevation() + tk.getHmin() / fMap.getUnits(Type.ELEV));
tk.setHmax(tk.getElevation() + tk.getHmax() / fMap.getUnits(Type.ELEV));
tk.setArea(Constants.PI * Math.pow(tk.getArea() / fMap.getUnits(Type.ELEV), 2) / 4.0);
tk.setV0(tk.getV0() / fMap.getUnits(Type.VOLUME));
tk.setVmin(tk.getVmin() / fMap.getUnits(Type.VOLUME));
tk.setVmax(tk.getVmax() / fMap.getUnits(Type.VOLUME));
tk.setKb(tk.getKb() / Constants.SECperDAY);
//tk.setVolume(tk.getV0());
tk.setConcentration(tk.getC0());
tk.setV1max(tk.getV1max() * tk.getVmax());
}
pMap.setClimit(pMap.getClimit() / fMap.getUnits(Type.QUALITY));
pMap.setCtol(pMap.getCtol() / fMap.getUnits(Type.QUALITY));
pMap.setKbulk(pMap.getKbulk() / Constants.SECperDAY);
pMap.setKwall(pMap.getKwall() / Constants.SECperDAY);
for (Link lk : net.getLinks()) {
if (lk.getType().id <= LinkType.PIPE.id) {
if (pMap.getFormflag() == PropertiesMap.FormType.DW)
lk.setRoughness(lk.getRoughness() / (1000.0 * fMap.getUnits(Type.ELEV)));
lk.setDiameter(lk.getDiameter() / fMap.getUnits(Type.DIAM));
lk.setLenght(lk.getLenght() / fMap.getUnits(Type.LENGTH));
lk.setKm(0.02517 * lk.getKm() / Math.pow(lk.getDiameter(), 2) / Math.pow(lk.getDiameter(), 2));
lk.setKb(lk.getKb() / Constants.SECperDAY);
lk.setKw(lk.getKw() / Constants.SECperDAY);
} else if (lk instanceof Pump) {
Pump pump = (Pump) lk;
if (pump.getPtype().equals(Pump.Type.CONST_HP)) {
if (pMap.getUnitsflag() == PropertiesMap.UnitsType.SI)
pump.setFlowCoefficient(pump.getFlowCoefficient() / fMap.getUnits(Type.POWER));
} else {
if (pump.getPtype().equals(Pump.Type.POWER_FUNC)) {
pump.setH0(pump.getH0() / fMap.getUnits(Type.HEAD));
pump.setFlowCoefficient(pump.getFlowCoefficient() * (Math.pow(fMap.getUnits(Type.FLOW), pump.getN())) / fMap.getUnits(Type.HEAD));
}
pump.setQ0(pump.getQ0() / fMap.getUnits(Type.FLOW));
pump.setQmax(pump.getQmax() / fMap.getUnits(Type.FLOW));
pump.setHmax(pump.getHmax() / fMap.getUnits(Type.HEAD));
}
} else {
lk.setDiameter(lk.getDiameter() / fMap.getUnits(Type.DIAM));
lk.setKm(0.02517 * lk.getKm() / Math.pow(lk.getDiameter(), 2) / Math.pow(lk.getDiameter(), 2));
if (lk.getRoughness() != Constants.MISSING)
switch (lk.getType()) {
case FCV:
lk.setRoughness(lk.getRoughness() / fMap.getUnits(Type.FLOW));
break;
case PRV:
case PSV:
case PBV:
lk.setRoughness(lk.getRoughness() / fMap.getUnits(Type.PRESSURE));
break;
}
}
lk.initResistance(net.getPropertiesMap().getFormflag(),net.getPropertiesMap().getHexp());
}
for (Control c_i : net.getControls()) {
if (c_i.getLink() == null) continue;
if (c_i.getNode() != null) {
Node node = c_i.getNode();
if (node instanceof Tank)
c_i.setGrade(node.getElevation() +
c_i.getGrade() / fMap.getUnits(Type.ELEV));
else
c_i.setGrade(node.getElevation() + c_i.getGrade() / fMap.getUnits(Type.PRESSURE));
}
if (c_i.getSetting() != Constants.MISSING)
switch (c_i.getLink().getType()) {
case PRV:
case PSV:
case PBV:
c_i.setSetting(c_i.getSetting() / fMap.getUnits(Type.PRESSURE));
break;
case FCV:
c_i.setSetting(c_i.getSetting() / fMap.getUnits(Type.FLOW));
}
}
}
/**
* Initialize pump properties.
* @param net Hydraulic network reference.
* @throws ENException
*/
private void initPumps(Network net) throws ENException {
double h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0;
for (Pump pump : net.getPumps()) {
// Constant Hp pump
if (pump.getPtype() == Pump.Type.CONST_HP) {
pump.setH0(0.0);
pump.setFlowCoefficient(-8.814 * pump.getKm());
pump.setN(-1.0);
pump.setHmax(Constants.BIG);
pump.setQmax(Constants.BIG);
pump.setQ0(1.0);
continue;
}
// Set parameters for pump curves
else if (pump.getPtype() == Pump.Type.NOCURVE) {
Curve curve = pump.getHcurve();
if (curve == null) {
throw new ENException(226, pump.getId());
}
int n = curve.getNpts();
if (n == 1) {
pump.setPtype(Pump.Type.POWER_FUNC);
q1 = curve.getX().get(0);
h1 = curve.getY().get(0);
h0 = 1.33334 * h1;
q2 = 2.0 * q1;
h2 = 0.0;
} else if (n == 3 && curve.getX().get(0) == 0.0) {
pump.setPtype(Pump.Type.POWER_FUNC);
h0 = curve.getY().get(0);
q1 = curve.getX().get(1);
h1 = curve.getY().get(1);
q2 = curve.getX().get(2);
h2 = curve.getY().get(2);
} else
pump.setPtype(Pump.Type.CUSTOM);
// Compute shape factors & limits of power function pump curves
if (pump.getPtype() == Pump.Type.POWER_FUNC) {
double[] a = new double[1], b = new double[1], c = new double[1];
if (!Utilities.getPowerCurve(h0, h1, h2, q1, q2, a, b, c))
throw new ENException(227, pump.getId());
pump.setH0(-a[0]);
pump.setFlowCoefficient(-b[0]);
pump.setN(c[0]);
pump.setQ0(q1);
pump.setQmax(Math.pow(-a[0] / b[0], (1.0 / c[0])));
pump.setHmax(h0);
}
}
// Assign limits to custom pump curves
if (pump.getPtype() == Pump.Type.CUSTOM) {
Curve curve = pump.getHcurve();
for (int m = 1; m < curve.getNpts(); m++) {
if (curve.getY().get(m) >= curve.getY().get(m - 1)) // Check for invalid curve
{
throw new ENException(227, pump.getId());
}
}
pump.setQmax(curve.getX().get(curve.getNpts() - 1));
pump.setQ0((curve.getX().get(0) + pump.getQmax()) / 2.0);
pump.setHmax(curve.getY().get(0));
}
}
}
/**
* Initialize patterns.
* @param net Hydraulic network reference.
* @throws ENException
*/
private void initPatterns(Network net) throws ENException {
for (Pattern par : net.getPatterns()) {
if (par.getFactorsList().size() == 0) {
par.getFactorsList().add(1.0);
}
}
}
/**
* Check for unlinked nodes.
* @param net Hydraulic network reference.
* @throws ENException
*/
private void checkUnlinked(Network net) throws ENException {
int[] marked = new int[net.getNodes().size() + 1];
List<Link> links = new ArrayList<Link>(net.getLinks());
List<Node> nodes = new ArrayList<Node>(net.getNodes());
int err = 0;
for (Link link : links) {
marked[nodes.indexOf(link.getFirst())]++;
marked[nodes.indexOf(link.getSecond())]++;
}
int i = 0;
for (Node node : nodes) {
if (marked[i] == 0) {
err++;
log.log(ENLevels.ERROR, "checkUnlinked", new ENException(233, node.getId()));
}
if (err >= Constants.MAXERRS)
break;
i++;
}
// if (err > 0)
// throw new ENException(200);
}
}