/* * 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.hydraulic.SparseMatrix; import org.addition.epanet.network.FieldsMap; import org.addition.epanet.network.PropertiesMap; import org.addition.epanet.network.structures.Curve; import org.addition.epanet.network.structures.Link; import org.addition.epanet.network.structures.Link.LinkType; import org.addition.epanet.network.structures.Link.StatType; import org.addition.epanet.util.ENException; import org.addition.epanet.util.Utilities; import java.util.Collection; import java.util.List; import java.util.logging.Logger; /** * */ public class SimulationValve extends SimulationLink { public SimulationValve(Collection<SimulationNode> indexedNodes, Link ref, int idx) { super(indexedNodes, ref, idx); } // Computes solution matrix coeffs. for a completely open, closed, or throttled control valve. protected void valveCoeff(PropertiesMap pMap) throws ENException { double p; // Valve is closed. Use a very small matrix coeff. if (status.id <= StatType.CLOSED.id) { invHeadLoss = 1.0 / Constants.CBIG; flowCorrection = flow; return; } // Account for any minor headloss through the valve if (getKm() > 0.0) { p = 2.0 * getKm() * Math.abs(flow); if (p < pMap.getRQtol()) p = pMap.getRQtol(); invHeadLoss = 1.0 / p; flowCorrection = flow / 2.0; } else { invHeadLoss = 1.0 / pMap.getRQtol(); flowCorrection = flow; } } // Computes solution matrix coeffs. for a completely open, closed, or throttled control valve. private void valveCoeff(PropertiesMap pMap, double km) throws ENException { double p; // Valve is closed. Use a very small matrix coeff. if (status.id <= StatType.CLOSED.id) { invHeadLoss = 1.0 / Constants.CBIG; flowCorrection = flow; return; } // Account for any minor headloss through the valve if (km > 0.0) { p = 2.0 * km * Math.abs(flow); if (p < pMap.getRQtol()) p = pMap.getRQtol(); invHeadLoss = 1.0 / p; flowCorrection = flow / 2.0; } else { invHeadLoss = 1.0 / pMap.getRQtol(); flowCorrection = flow; } } // Computes P & Y coeffs. for pressure breaker valve void pbvCoeff(PropertiesMap pMap) throws ENException { if (setting == Constants.MISSING || setting == 0.0) valveCoeff(pMap); else if (getKm() * (flow * flow) > setting) valveCoeff(pMap); else { invHeadLoss = Constants.CBIG; flowCorrection = setting * Constants.CBIG; } } // Computes P & Y coeffs. for throttle control valve void tcvCoeff(PropertiesMap pMap) throws ENException { double km = getKm(); if (setting != Constants.MISSING) km = (0.02517 * setting / Math.pow(getDiameter(), 4)); valveCoeff(pMap, km); } // Computes P & Y coeffs. for general purpose valve void gpvCoeff(FieldsMap fMap, PropertiesMap pMap, Curve[] curves) throws ENException { if (status == StatType.CLOSED) valveCoeff(pMap); else { double q = Math.max(Math.abs(flow), Constants.TINY); Curve.Coeffs coeffs = curves[(int) Math.round(setting)].getCoeff(fMap, q); invHeadLoss = 1.0 / Math.max(coeffs.r, pMap.getRQtol()); flowCorrection = invHeadLoss * (coeffs.h0 + coeffs.r * q) * Utilities.getSignal(flow); } } // Updates status of a flow control valve. StatType fcvStatus(PropertiesMap pMap, StatType s) throws ENException { StatType status; status = s; if (getFirst().getSimHead() - getSecond().getSimHead() < -pMap.getHtol()) status = StatType.XFCV; else if (flow < -pMap.getQtol()) status = StatType.XFCV; else if (s == StatType.XFCV && flow >= setting) status = StatType.ACTIVE; return (status); } // Computes solution matrix coeffs. for pressure reducing valves void prvCoeff(PropertiesMap pMap, LSVariables ls, SparseMatrix smat) throws ENException { int k = getIndex(); int i = smat.getRow(first.getIndex()); int j = smat.getRow(second.getIndex()); double hset = second.getElevation() + setting; if (status == StatType.ACTIVE) { invHeadLoss = 0.0; flowCorrection = flow + ls.getNodalInFlow(second); ls.addRHSCoeff(j, +(hset * Constants.CBIG)); ls.addAii(j, +Constants.CBIG); if (ls.getNodalInFlow(second) < 0.0) ls.addRHSCoeff(i, +ls.getNodalInFlow(second)); return; } valveCoeff(pMap); ls.addAij(smat.getNdx(k), -invHeadLoss); ls.addAii(i, +invHeadLoss); ls.addAii(j, +invHeadLoss); ls.addRHSCoeff(i, +(flowCorrection - flow)); ls.addRHSCoeff(j, -(flowCorrection - flow)); } // Computes solution matrix coeffs. for pressure sustaining valve void psvCoeff(PropertiesMap pMap, LSVariables ls, SparseMatrix smat) throws ENException { int k = getIndex(); int i = smat.getRow(first.getIndex()); int j = smat.getRow(second.getIndex()); double hset = first.getElevation() + setting; if (status == StatType.ACTIVE) { invHeadLoss = 0.0; flowCorrection = flow - ls.getNodalInFlow(first); ls.addRHSCoeff(i, +(hset * Constants.CBIG)); ls.addAii(i, +Constants.CBIG); if (ls.getNodalInFlow(first) > 0.0) ls.addRHSCoeff(j, +ls.getNodalInFlow(first)); return; } valveCoeff(pMap); ls.addAij(smat.getNdx(k), -invHeadLoss); ls.addAii(i, +invHeadLoss); ls.addAii(j, +invHeadLoss); ls.addRHSCoeff(i, +(flowCorrection - flow)); ls.addRHSCoeff(j, -(flowCorrection - flow)); } // computes solution matrix coeffs. for flow control valve void fcvCoeff(PropertiesMap pMap, LSVariables ls, SparseMatrix smat) throws ENException { int k = getIndex(); double q = setting; int i = smat.getRow(first.getIndex()); int j = smat.getRow(second.getIndex()); // If valve active, break network at valve and treat // flow setting as external demand at upstream node // and external supply at downstream node. if (status == StatType.ACTIVE) { ls.addNodalInFlow(first.getIndex(), -q); ls.addRHSCoeff(i, -q); ls.addNodalInFlow(second.getIndex(), +q); ls.addRHSCoeff(j, +q); invHeadLoss = 1.0 / Constants.CBIG; ls.addAij(smat.getNdx(k), -invHeadLoss); ls.addAii(i, +invHeadLoss); ls.addAii(j, +invHeadLoss); flowCorrection = flow - q; } else { // Otherwise treat valve as an open pipe valveCoeff(pMap); ls.addAij(smat.getNdx(k), -invHeadLoss); ls.addAii(i, +invHeadLoss); ls.addAii(j, +invHeadLoss); ls.addRHSCoeff(i, +(flowCorrection - flow)); ls.addRHSCoeff(j, -(flowCorrection - flow)); } } // Determines if a node belongs to an active control valve // whose setting causes an inconsistent set of eqns. If so, // the valve status is fixed open and a warning condition // is generated. public static boolean checkBadValve(PropertiesMap pMap, Logger log, List<SimulationValve> valves, long Htime, int n) throws ENException { for (SimulationValve link : valves) { SimulationNode n1 = link.getFirst(); SimulationNode n2 = link.getSecond(); if (n == n1.getIndex() || n == n2.getIndex()) { if (link.getType() == LinkType.PRV || link.getType() == LinkType.PSV || link.getType() == LinkType.FCV) { if (link.status == StatType.ACTIVE) { if (pMap.getStatflag() == PropertiesMap.StatFlag.FULL) { logBadValve(log, link, Htime); } if (link.getType() == LinkType.FCV) link.status = StatType.XFCV; else link.status = StatType.XPRESSURE; return true; } } return false; } } return false; } private static void logBadValve(Logger log, SimulationLink link, long Htime) { log.warning(String.format(Utilities.getText("FMT61"), Utilities.getClockTime(Htime), link.getLink().getId())); } // Updates status of a pressure reducing valve. private StatType prvStatus(PropertiesMap pMap, double hset) throws ENException { if (setting == Constants.MISSING) return (status); double htol = pMap.getHtol(); double hml = getKm() * (flow * flow); double h1 = first.getSimHead(); double h2 = second.getSimHead(); StatType tStatus = status; switch (status) { case ACTIVE: if (flow < -pMap.getQtol()) tStatus = StatType.CLOSED; else if (h1 - hml < hset - htol) tStatus = StatType.OPEN; else tStatus = StatType.ACTIVE; break; case OPEN: if (flow < -pMap.getQtol()) tStatus = StatType.CLOSED; else if (h2 >= hset + htol) tStatus = StatType.ACTIVE; else tStatus = StatType.OPEN; break; case CLOSED: if (h1 >= hset + htol && h2 < hset - htol) tStatus = StatType.ACTIVE; else if (h1 < hset - htol && h1 > h2 + htol) tStatus = StatType.OPEN; else tStatus = StatType.CLOSED; break; case XPRESSURE: if (flow < -pMap.getQtol()) tStatus = StatType.CLOSED; break; } return (tStatus); } // Updates status of a pressure sustaining valve. private StatType psvStatus(PropertiesMap pMap, double hset) throws ENException { if (setting == Constants.MISSING) return (status); double h1 = first.getSimHead(); double h2 = second.getSimHead(); double htol = pMap.getHtol(); double hml = getKm() * (flow * flow); StatType tStatus = status; switch (status) { case ACTIVE: if (flow < -pMap.getQtol()) tStatus = StatType.CLOSED; else if (h2 + hml > hset + htol) tStatus = StatType.OPEN; else tStatus = StatType.ACTIVE; break; case OPEN: if (flow < -pMap.getQtol()) tStatus = StatType.CLOSED; else if (h1 < hset - htol) tStatus = StatType.ACTIVE; else tStatus = StatType.OPEN; break; case CLOSED: if (h2 > hset + htol && h1 > h2 + htol) tStatus = StatType.OPEN; else if (h1 >= hset + htol && h1 > h2 + htol) tStatus = StatType.ACTIVE; else tStatus = StatType.CLOSED; break; case XPRESSURE: if (flow < -pMap.getQtol()) tStatus = StatType.CLOSED; break; } return (tStatus); } // Compute P & Y coefficients for PBV,TCV,GPV valves public boolean computeValveCoeff(FieldsMap fMap, PropertiesMap pMap, Curve[] curves) throws ENException { switch (getType()) { case PBV: pbvCoeff(pMap); break; case TCV: tcvCoeff(pMap); break; case GPV: gpvCoeff(fMap, pMap, curves); break; case FCV: case PRV: case PSV: if (getSimSetting() == Constants.MISSING) valveCoeff(pMap); else return false; break; } return true; } // Updates status for PRVs & PSVs whose status is not fixed to OPEN/CLOSED public static boolean valveStatus(FieldsMap fMap, PropertiesMap pMap, Logger log, List<SimulationValve> valves) throws ENException { boolean change = false; for (SimulationValve v : valves) { if (v.setting == Constants.MISSING) continue; StatType s = v.status; switch (v.getType()) { case PRV: { double hset = v.second.getElevation() + v.setting; v.status = v.prvStatus(pMap, hset); break; } case PSV: { double hset = v.first.getElevation() + v.setting; v.status = v.psvStatus(pMap, hset); break; } default: continue; } if (s != v.status) { if (pMap.getStatflag() == PropertiesMap.StatFlag.FULL) logStatChange(fMap, log, v, s, v.status); change = true; } } return (change); } // Computes solution matrix coeffs. for PRVs, PSVs & FCVs whose status is not fixed to OPEN/CLOSED public static void computeMatrixCoeffs(PropertiesMap pMap, LSVariables ls, SparseMatrix smat, List<SimulationValve> valves) throws ENException { for (SimulationValve valve : valves) { if (valve.getSimSetting() == Constants.MISSING) continue; switch (valve.getType()) { case PRV: valve.prvCoeff(pMap, ls, smat); break; case PSV: valve.psvCoeff(pMap, ls, smat); break; case FCV: valve.fcvCoeff(pMap, ls, smat); break; } } } }