/*
* 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.hydraulic.structures;
import org.addition.epanet.Constants;
import org.addition.epanet.network.FieldsMap;
import org.addition.epanet.network.FieldsMap.Type;
import org.addition.epanet.network.structures.Curve;
import org.addition.epanet.network.structures.Link.StatType;
import org.addition.epanet.network.structures.Node;
import org.addition.epanet.network.structures.Pattern;
import org.addition.epanet.network.structures.Tank;
import org.addition.epanet.util.ENException;
import org.addition.epanet.util.Utilities;
import java.util.List;
/**
*
*/
public class SimulationTank extends SimulationNode {
private StatType oldStat;
public SimulationTank(Node ref, int idx) {
super(ref, idx);
volume = ((Tank) node).getV0();
// Init
head = ((Tank) node).getH0();
demand = (0.0);
oldStat = StatType.TEMPCLOSED;
}
public Tank getNode() {
return (Tank) node;
}
private double volume;
public double getArea() {
return ((Tank) node).getArea();
}
public double getHmin() {
return ((Tank) node).getHmin();
}
public double getHmax() {
return ((Tank) node).getHmax();
}
//public double getH0() {
// return ((Tank)node).getH0();
//}
public double getVmin() {
return ((Tank) node).getVmin();
}
public double getVmax() {
return ((Tank) node).getVmax();
}
public double getV0() {
return ((Tank) node).getV0();
}
//public double getKb() {
// return ((Tank)node).getKb();
//}
//
//public double [] getConcentration() {
// return ((Tank)node).getConcentration();
//}
public Pattern getPattern() {
return ((Tank) node).getPattern();
}
public Curve getVcurve() {
return ((Tank) node).getVcurve();
}
//public MixType getMixModel() {
// return ((Tank)node).getMixModel();
//}
//
//public double getV1max() {
// return ((Tank)node).getV1max();
//}
/// Simulation getters & setters.
public double getSimVolume() {
return volume;//((Tank)node).getSimVolume();
}
public boolean isReservoir() {
return ((Tank) node).getArea() == 0;
}
public StatType getOldStat() {
return oldStat;
}
public void setOldStat(StatType oldStat) {
this.oldStat = oldStat;
}
/// Simulation methods
// Finds water volume in tank corresponding to elevation 'h'
double findVolume(FieldsMap fMap, double h) throws ENException {
Curve curve = getVcurve();
if (curve == null)
return (getVmin() + (h - getHmin()) * getArea());
else {
return (Utilities.linearInterpolator(curve.getNpts(), curve.getX(), curve.getY(),
(h - getElevation()) * fMap.getUnits(Type.HEAD) / fMap.getUnits(Type.VOLUME)));
}
}
// Computes new water levels in tank after current time step, with Euler integrator.
private void updateLevel(FieldsMap fMap, long tstep) throws ENException {
if (getArea() == 0.0) // Reservoir
return;
// Euler
double dv = demand * tstep;
volume += dv;
if (volume + demand >= getVmax())
volume = getVmax();
if (volume - demand <= getVmin())
volume = getVmin();
head = findGrade(fMap);
}
// Finds water level in tank corresponding to current volume
private double findGrade(FieldsMap fMap) throws ENException {
Curve curve = getVcurve();
if (curve == null)
return (getHmin() + (volume - getVmin()) / getArea());
else
return (getElevation() + Utilities.linearInterpolator(curve.getNpts(), curve.getY(), curve.getX(),
volume * fMap.getUnits(Type.VOLUME)) / fMap.getUnits(Type.HEAD));
}
// Get the required time step based to fill or drain a tank
private long getRequiredTimeStep(long tstep) {
if (isReservoir()) return tstep; // Skip reservoirs
double h = head; // Current tank grade
double q = demand; // Flow into tank
double v = 0.0;
if (Math.abs(q) <= Constants.QZERO)
return tstep;
if (q > 0.0 && h < getHmax())
v = getVmax() - getSimVolume(); // Volume to fill
else if (q < 0.0 && h > getHmin())
v = getVmin() - getSimVolume(); // Volume to drain
else
return tstep;
// Compute time to fill/drain
long t = Math.round(v / q);
// Revise time step
if (t > 0 && t < tstep)
tstep = t;
return tstep;
}
// Revises time step based on shortest time to fill or drain a tank
public static long minimumTimeStep(List<SimulationTank> tanks, long tstep) {
long newTStep = tstep;
for (SimulationTank tank : tanks)
newTStep = tank.getRequiredTimeStep(newTStep);
return newTStep;
}
// Computes new water levels in tanks after current time step.
public static void stepWaterLevels(List<SimulationTank> tanks, FieldsMap fMap, long tstep) throws ENException {
for (SimulationTank tank : tanks)
tank.updateLevel(fMap, tstep);
}
}