/*
* 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.io;
import org.addition.epanet.Constants;
import org.addition.epanet.hydraulic.HydraulicSim;
import org.addition.epanet.hydraulic.structures.SimulationLink;
import org.addition.epanet.hydraulic.structures.SimulationNode;
import org.addition.epanet.network.FieldsMap;
import org.addition.epanet.network.PropertiesMap;
import org.addition.epanet.network.structures.Field;
import org.addition.epanet.network.structures.Link;
import org.addition.epanet.network.structures.Node;
import org.addition.epanet.network.structures.Pump;
import org.addition.epanet.quality.QualitySim;
import org.addition.epanet.quality.structures.QualityLink;
import org.addition.epanet.quality.structures.QualityNode;
import org.addition.epanet.util.ENException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.util.List;
/**
* Aware compatible hydraulic step snapshot
*/
public class AwareStep {
private double[] QN;
private double[] QL;
private double[] D;
private double[] H;
private double[] Q;
private double[] DH;
private long hydTime;
private long hydStep;
public static final int FORMAT_VERSION = 1;
public static class HeaderInfo {
public int version;
public int nodes;
public int links;
public long rstart;
public long rstep;
public long duration;
}
public static void writeHeader(DataOutput outStream, HydraulicSim hydraulicSim, long rstart, long rstep, long duration) throws IOException, ENException {
outStream.writeInt(FORMAT_VERSION);
outStream.writeInt(hydraulicSim.getnNodes().size());
outStream.writeInt(hydraulicSim.getnLinks().size());
outStream.writeLong(rstart);
outStream.writeLong(rstep);
outStream.writeLong(duration);
}
public static HeaderInfo readHeader(DataInput in) throws IOException, ENException {
HeaderInfo headerInfo = new HeaderInfo();
headerInfo.version = in.readInt();
headerInfo.nodes = in.readInt();
headerInfo.links = in.readInt();
headerInfo.rstart = in.readLong();
headerInfo.rstep = in.readLong();
headerInfo.duration = in.readLong();
return headerInfo;
}
public static void write(DataOutput outStream, HydraulicSim hydraulicSim, long hydStep) throws IOException, ENException {
List<SimulationNode> nodes = hydraulicSim.getnNodes();
List<SimulationLink> links = hydraulicSim.getnLinks();
long hydTime = hydraulicSim.getHtime();
int nNodes = nodes.size();
int nLinks = links.size();
int baSize = (nNodes * 3 + nLinks * 3) * Double.SIZE / 8 +
Long.SIZE * 2 / 8;
ByteBuffer buf = ByteBuffer.allocate(baSize);
for (SimulationNode node : nodes) {
buf.putDouble(node.getSimDemand());
buf.putDouble(node.getSimHead());
buf.putDouble(0.0);
}
for (SimulationLink link : links) {
buf.putDouble(link.getSimStatus().id <= Link.StatType.CLOSED.id ? 0d : link.getSimFlow());
buf.putDouble((link.getFirst().getSimHead() - link.getSecond().getSimHead()));
buf.putDouble(0.0);
}
buf.putLong(hydStep);
buf.putLong(hydTime);
buf.flip();
outStream.write(buf.array());
}
public static void writeHydAndQual(DataOutput outStream, HydraulicSim hydraulicSim, QualitySim qualitySim, long step, long time) throws IOException, ENException {
List<QualityNode> qNodes = qualitySim != null ? qualitySim.getnNodes() : null;
List<QualityLink> qLinks = qualitySim != null ? qualitySim.getnLinks() : null;
List<SimulationNode> nodes = hydraulicSim.getnNodes();
List<SimulationLink> links = hydraulicSim.getnLinks();
int nNodes = nodes.size();
int nLinks = links.size();
int baSize = (nNodes * 3 + nLinks * 3) * Double.SIZE / 8 + Long.SIZE * 2 / 8;
ByteBuffer buf = ByteBuffer.allocate(baSize);
int count = 0;
for (SimulationNode node : nodes) {
buf.putDouble(node.getSimDemand());
buf.putDouble(node.getSimHead());
buf.putDouble(qualitySim != null ? qNodes.get(count++).getQuality() : 0.0);
}
count = 0;
for (SimulationLink link : links) {
buf.putDouble(link.getSimStatus().id <= Link.StatType.CLOSED.id ? 0d : link.getSimFlow());
buf.putDouble((link.getFirst().getSimHead() - link.getSecond().getSimHead()));
buf.putDouble(qualitySim != null ? qLinks.get(count++).getAverageQuality(null) : 0);
}
buf.putLong(step);
buf.putLong(time);
buf.flip();
outStream.write(buf.array());
}
/*public static void writeHybrid(DataOutput outStream,HydraulicSim hydraulicSim, double [] qN, double [] qL , long step, long time) throws IOException, ENException {
List<SimulationNode> nodes = hydraulicSim.getnNodes();
List<SimulationLink> links = hydraulicSim.getnLinks();
int nNodes = nodes.size();
int nLinks = links.size();
int baSize = (nNodes * 3 + nLinks * 3) * Double.SIZE / 8 +
Long.SIZE * 2 / 8;
ByteBuffer buf = ByteBuffer.allocate(baSize);
int count= 0;
for (SimulationNode node : nodes) {
buf.putDouble(node.getSimDemand());
buf.putDouble(node.getSimHead());
buf.putDouble(qN[count++]);
}
count = 0;
for (SimulationLink link : links) {
buf.putDouble(link.getSimStatus().id <= Link.StatType.CLOSED.id ? 0d : link.getSimFlow());
buf.putDouble((link.getFirst().getSimHead() - link.getSecond().getSimHead()));
buf.putDouble(qL[count++]);
}
buf.putLong(step);
buf.putLong(time);
buf.flip();
outStream.write(buf.array());
} */
public AwareStep(DataInput inStream, HeaderInfo headerInfo) throws IOException {
int nNodes = headerInfo.nodes;
int nLinks = headerInfo.links;
D = new double[nNodes];
H = new double[nNodes];
Q = new double[nLinks];
DH = new double[nLinks];
QN = new double[nNodes];
QL = new double[nLinks];
int baSize = (nNodes * 3 + nLinks * 3) * Double.SIZE / 8 +
Long.SIZE * 2 / 8;
byte[] ba = new byte[baSize];
inStream.readFully(ba);
ByteBuffer buf = ByteBuffer.wrap(ba);
for (int i = 0; i < nNodes; i++) {
D[i] = buf.getDouble();
H[i] = buf.getDouble();
QN[i] = buf.getDouble();
}
for (int i = 0; i < nLinks; i++) {
Q[i] = buf.getDouble();
DH[i] = buf.getDouble();
QL[i] = buf.getDouble();
}
hydStep = buf.getLong();
hydTime = buf.getLong();
}
public double getNodeDemand(int id, Node node, FieldsMap fMap) {
try {
return fMap != null ? fMap.revertUnit(FieldsMap.Type.DEMAND, D[id]) : D[id];
} catch (ENException e) {
return 0;
}
}
public double getNodeHead(int id, Node node, FieldsMap fMap) {
try {
return fMap != null ? fMap.revertUnit(FieldsMap.Type.HEAD, H[id]) : H[id];
} catch (ENException e) {
return 0;
}
}
public double getNodePressure(int id, Node node, FieldsMap fMap) {
try {
double P = (getNodeHead(id, node, null) - node.getElevation());
return fMap != null ? fMap.revertUnit(FieldsMap.Type.PRESSURE, P) : P;
} catch (ENException e) {
return 0;
}
}
public double getLinkFlow(int id, Link link, FieldsMap fMap) {
try {
return fMap != null ? fMap.revertUnit(FieldsMap.Type.FLOW, Q[id]) : Q[id];
} catch (ENException e) {
return 0;
}
}
public double getLinkVelocity(int id, Link link, FieldsMap fMap) {
try {
double V;
double flow = getLinkFlow(id, link, null);
if (link instanceof Pump)
V = 0;
else
V = (Math.abs(flow) / (Constants.PI * Math.pow(link.getDiameter(), 2) / 4.0));
return fMap != null ? fMap.revertUnit(FieldsMap.Type.VELOCITY, V) : V;
} catch (ENException e) {
return 0;
}
}
public double getLinkHeadLoss(int id, Link link, FieldsMap fMap) {
try {
if (getLinkFlow(id, link, null) == 0) {
return 0.0;
} else {
double h = DH[id];
if (!(link instanceof Pump))
h = Math.abs(h);
if (link.getType().id <= Link.LinkType.PIPE.id)
return (1000 * h / link.getLenght());
else
return fMap != null ? fMap.revertUnit(FieldsMap.Type.HEADLOSS, h) : h;
}
} catch (ENException e) {
return 0;
}
}
public double getLinkFriction(int id, Link link, FieldsMap fMap) {
try {
double F;
double flow = getLinkFlow(id, link, null);
if (link.getType().id <= Link.LinkType.PIPE.id && Math.abs(flow) > Constants.TINY) {
double h = Math.abs(DH[id]);
F = 39.725 * h * Math.pow(link.getDiameter(), 5) / link.getLenght() /
(flow * flow);
} else
F = 0;
return fMap != null ? fMap.revertUnit(FieldsMap.Type.FRICTION, F) : F;
} catch (ENException e) {
return 0;
}
}
public double getLinkAvrQuality(int id) {
return QL[id];
}
public double getNodeQuality(int id) {
return QN[id];
}
public long getStep() {
return hydStep;
}
public long getTime() {
return hydTime;
}
}