/* * 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.PropertiesMap; import org.addition.epanet.network.io.Keywords; import org.addition.epanet.network.structures.Control; import org.addition.epanet.network.structures.Control.ControlType; 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.List; import java.util.logging.Logger; public class SimulationControl { private final Control control; private SimulationLink link; private SimulationNode node=null; public SimulationControl(List<SimulationNode> nodes, List<SimulationLink> links, Control ref) { if (ref.getNode() != null) { String nid = ref.getNode().getId(); for (SimulationNode simulationNode : nodes) { if (simulationNode.getId().equals(nid)) { node = simulationNode; break; } } } if (ref.getLink() != null) { String linkId = ref.getLink().getId(); for (SimulationLink simulationLink : links) { if(simulationLink.getLink().getId().equals(linkId)) { link = simulationLink;break; } } } control = ref; } public SimulationLink getLink() { return link; } public SimulationNode getNode() { return node; } public long getTime() { return control.getTime(); } public double getGrade() { return control.getGrade(); } public double getSetting() { return control.getSetting(); } public StatType getStatus() { return control.getStatus(); } public ControlType getType() { return control.getType(); } /** * Get the shortest time step to activate the control. * * @param fMap * @param pMap * @param htime * @param tstep * @return * @throws ENException */ private long getRequiredTimeStep(FieldsMap fMap, PropertiesMap pMap, long htime, long tstep) throws ENException { long t = 0; // Node control if (getNode() != null) { if (!(getNode() instanceof SimulationTank)) // Check if node is a tank return tstep; double h = node.getSimHead(); // Current tank grade double q = node.getSimDemand(); // Flow into tank if (Math.abs(q) <= Constants.QZERO) return tstep; if ((h < getGrade() && getType() == ControlType.HILEVEL && q > 0.0) // Tank below hi level & filling || (h > getGrade() && getType() == ControlType.LOWLEVEL && q < 0.0)) // Tank above low level & emptying { SimulationTank tank = ((SimulationTank) getNode()); double v = tank.findVolume(fMap, getGrade()) - tank.getSimVolume(); t = Math.round(v / q); // Time to reach level } } // Time control if (getType() == ControlType.TIMER) { if (getTime() > htime) t = getTime() - htime; } // Time-of-day control if (getType() == ControlType.TIMEOFDAY) { long t1 = (htime + pMap.getTstart()) % Constants.SECperDAY; long t2 = getTime(); if (t2 >= t1) t = t2 - t1; else t = Constants.SECperDAY - t1 + t2; } // Revise time step if (t > 0 && t < tstep) { SimulationLink link = getLink(); // Check if rule actually changes link status or setting if (link != null && (link.getType().id > LinkType.PIPE.id && link.getSimSetting() != getSetting()) || (link.getSimStatus() != getStatus())) tstep = t; } return tstep; } // Revises time step based on shortest time to fill or drain a tank public static long minimumTimeStep(FieldsMap fMap, PropertiesMap pMap, List<SimulationControl> controls, long htime, long tstep) throws ENException { long newTStep = tstep; for (SimulationControl control : controls) newTStep = control.getRequiredTimeStep(fMap, pMap, htime, newTStep); return newTStep; } // Implements simple controls based on time or tank levels public static int stepActions(Logger log, FieldsMap fMap, PropertiesMap pMap, List<SimulationControl> controls, long htime) throws ENException { int setsum = 0; // Examine each control statement for (SimulationControl control : controls) { boolean reset = false; // Make sure that link is defined if (control.getLink() == null) continue; // Link is controlled by tank level if (control.getNode() != null && control.getNode() instanceof SimulationTank) { double h = control.getNode().getSimHead(); double vplus = Math.abs(control.getNode().getSimDemand()); SimulationTank tank = (SimulationTank) control.getNode(); double v1 = tank.findVolume(fMap, h); double v2 = tank.findVolume(fMap, control.getGrade()); if (control.getType() == ControlType.LOWLEVEL && v1 <= v2 + vplus) reset = true; if (control.getType() == ControlType.HILEVEL && v1 >= v2 - vplus) reset = true; } // Link is time-controlled if (control.getType() == ControlType.TIMER) { if (control.getTime() == htime) reset = true; } // Link is time-of-day controlled if (control.getType() == ControlType.TIMEOFDAY) { if ((htime + pMap.getTstart()) % Constants.SECperDAY == control.getTime()) reset = true; } // Update link status & pump speed or valve setting if (reset) { StatType s1, s2; SimulationLink link = control.getLink(); if (link.getSimStatus().id <= StatType.CLOSED.id) s1 = StatType.CLOSED; else s1 = StatType.OPEN; s2 = control.getStatus(); double k1 = link.getSimSetting(); double k2 = k1; if (control.getLink().getType().id > LinkType.PIPE.id) k2 = control.getSetting(); if (s1 != s2 || k1 != k2) { link.setSimStatus(s2); link.setSimSetting(k2); if (pMap.getStatflag() != null) logControlAction(log, control, htime); setsum++; } } } return (setsum); } // Adjusts settings of links controlled by junction pressures after a hydraulic solution is found public static boolean pSwitch(Logger log, PropertiesMap pMap, FieldsMap fMap, List<SimulationControl> controls) throws ENException { boolean anychange = false; for (SimulationControl control : controls) { boolean reset = false; if (control.getLink() == null) continue; // Determine if control based on a junction, not a tank if (control.getNode() != null && !(control.getNode() instanceof SimulationTank)) { // Determine if control conditions are satisfied if (control.getType() == ControlType.LOWLEVEL && control.getNode().getSimHead() <= control.getGrade() + pMap.getHtol()) reset = true; if (control.getType() == ControlType.HILEVEL && control.getNode().getSimHead() >= control.getGrade() - pMap.getHtol()) reset = true; } SimulationLink link = control.getLink(); // Determine if control forces a status or setting change if (reset) { boolean change = false; StatType s = link.getSimStatus(); if (link.getType() == LinkType.PIPE) { if (s != control.getStatus()) change = true; } if (link.getType() == LinkType.PUMP) { if (link.getSimSetting() != control.getSetting()) change = true; } if (link.getType().id >= LinkType.PRV.id) { if (link.getSimSetting() != control.getSetting()) change = true; else if (link.getSimSetting() == Constants.MISSING && s != control.getStatus()) change = true; } // If a change occurs, update status & setting if (change) { link.setSimStatus(control.getStatus()); if (link.getType().id > LinkType.PIPE.id) link.setSimSetting(control.getSetting()); if (pMap.getStatflag() == PropertiesMap.StatFlag.FULL) logStatChange(log, fMap, link, s); anychange = true; } } } return (anychange); } private static void logControlAction(Logger log, SimulationControl control, long Htime) { SimulationNode n = control.getNode(); SimulationLink l = control.getLink(); String Msg = ""; switch (control.getType()) { case LOWLEVEL: case HILEVEL: { String type = Keywords.w_JUNC;// NodeType type= NodeType.JUNC; if (n instanceof SimulationTank) { if (((SimulationTank) n).isReservoir()) type = Keywords.w_RESERV; else type = Keywords.w_TANK; } Msg = String.format(Utilities.getText("FMT54"), Utilities.getClockTime(Htime), l.getType().parseStr, l.getLink().getId(), type, n.getId()); break; } case TIMER: case TIMEOFDAY: Msg = String.format(Utilities.getText("FMT55"), Utilities.getClockTime(Htime), l.getType().parseStr, l.getLink().getId()); break; default: return; } log.warning(Msg); } private static void logStatChange(Logger log, FieldsMap fMap, SimulationLink link, StatType oldstatus) { StatType s1 = oldstatus; StatType s2 = link.getSimStatus(); try { if (s2 == s1) { double setting = link.getSimSetting(); switch (link.getType()) { case PRV: case PSV: case PBV: setting *= fMap.getUnits(Type.PRESSURE); break; case FCV: setting *= fMap.getUnits(Type.FLOW); } log.warning(String.format(Utilities.getText("FMT56"), link.getType().parseStr, link.getLink().getId(), setting)); return; } StatType j1, j2; if (s1 == StatType.ACTIVE) j1 = StatType.ACTIVE; else if (s1.ordinal() <= StatType.CLOSED.ordinal()) j1 = StatType.CLOSED; else j1 = StatType.OPEN; if (s2 == StatType.ACTIVE) j2 = StatType.ACTIVE; else if (s2.ordinal() <= StatType.CLOSED.ordinal()) j2 = StatType.CLOSED; else j2 = StatType.OPEN; if (j1 != j2) { log.warning(String.format(Utilities.getText("FMT57"), link.getType().parseStr, link.getLink().getId(), j1.reportStr, j2.reportStr)); } } catch (ENException e) { } } }