/* * 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; import org.addition.epanet.Constants; import org.addition.epanet.util.ENException; import org.addition.epanet.network.io.Keywords; import org.addition.epanet.network.structures.Field; import org.addition.epanet.util.Utilities; import java.util.LinkedHashMap; import java.util.Map; /** * * Units report properties & conversion support class */ public class FieldsMap { /** * Network variables */ static public enum Type { ELEV (0,Keywords.t_ELEV ), // nodal water quality DEMAND (1,Keywords.t_DEMAND ), // nodal demand flow HEAD (2,Keywords.t_HEAD ), // link flow velocity PRESSURE (3,Keywords.t_PRESSURE ), // avg. reaction rate in link QUALITY (4,Keywords.t_QUALITY ), // link friction factor LENGTH (5,Keywords.t_LENGTH ), // avg. water quality in link DIAM (6,Keywords.t_DIAM ), // nodal hydraulic head FLOW (7,Keywords.t_FLOW ), // link diameter VELOCITY (8,Keywords.t_VELOCITY ), // time to fill a tank HEADLOSS (9,Keywords.t_HEADLOSS ), // link head loss LINKQUAL (10,Keywords.t_LINKQUAL ), // link status STATUS (11,Keywords.t_STATUS ), // tank volume SETTING (12,Keywords.t_SETTING ), // simulation time REACTRATE (13,Keywords.t_REACTRATE), // pump power output FRICTION (14,Keywords.t_FRICTION ), // link flow rate POWER (15), // pump/valve setting TIME (16), // simulation time of day VOLUME (17), // time to drain a tank CLOCKTIME (18), // nodal elevation FILLTIME (19), // link length DRAINTIME (20); // nodal pressure /** * Get field type from string. * @param text String to be parsed. * @return Parsed Field, null the string doesn't match any of the possible types. */ public static Type parse(String text){ for (Type type : Type.values()) if (Utilities.match(text, type.parseStr)) return type; return null; } /** * Field sequencial id. */ public final int id; /** * Field string. */ public final String parseStr; private Type(int id){this.id = id;this.parseStr = "";} private Type(int id, String pStr){this.id = id;this.parseStr = pStr;} } /** * Report fields properties. */ private Map<Type,Field> fields; /** * Fields units values. */ private Map<Type,Double> units; /** * Init fields default configuration */ public FieldsMap() { try{ fields = new LinkedHashMap<Type,Field>(); units = new LinkedHashMap<Type,Double>(); for(Type type: Type.values()) setField(type,new Field(type.parseStr)); getField(Type.FRICTION).setPrecision(3); for (int i= Type.DEMAND.id;i<= Type.QUALITY.id; i++) getField(Type.values()[i]).setEnabled(true); for (int i= Type.FLOW.id;i<= Type.HEADLOSS.id; i++) getField(Type.values()[i]).setEnabled(true); } catch (ENException e){ e.printStackTrace(); } } /** * Get report field properties from type. * @param type Field type. * @return Report field. * @throws org.addition.epanet.util.ENException If specified type not found. */ public Field getField(Type type) throws ENException { Object obj = fields.get(type); if(obj==null) throw new ENException(201,type.parseStr); else return (Field)obj; } /** * Get conversion value from field type. * @param type Field type. * @return Conversion units value (from user units to system units) * @throws ENException If specified type not found. */ public Double getUnits(Type type) throws ENException { Object obj = units.get(type); if(obj==null) throw new ENException(201,type.parseStr); else return (Double)obj; } /** * Update fields and units, after loading the INP. * @param targetUnits * @param flowFlag * @param pressFlag * @param qualFlag * @param ChemUnits * @param SpGrav * @param Hstep */ public void prepare(PropertiesMap.UnitsType targetUnits, PropertiesMap.FlowUnitsType flowFlag, PropertiesMap.PressUnitsType pressFlag, PropertiesMap.QualType qualFlag, String ChemUnits, Double SpGrav, Long Hstep) throws ENException { double dcf, ccf, qcf, hcf, pcf, wcf; if (targetUnits == PropertiesMap.UnitsType.SI) { getField(Type.DEMAND).setUnits(flowFlag.parseStr); getField(Type.ELEV).setUnits(Keywords.u_METERS); getField(Type.HEAD).setUnits(Keywords.u_METERS); if (pressFlag == PropertiesMap.PressUnitsType.METERS) getField(Type.PRESSURE).setUnits(Keywords.u_METERS); else getField(Type.PRESSURE).setUnits(Keywords.u_KPA); getField(Type.LENGTH).setUnits(Keywords.u_METERS); getField(Type.DIAM).setUnits(Keywords.u_MMETERS); getField(Type.FLOW).setUnits(flowFlag.parseStr); getField(Type.VELOCITY).setUnits(Keywords.u_MperSEC); getField(Type.HEADLOSS).setUnits("m"+Keywords.u_per1000M); getField(Type.FRICTION).setUnits(""); getField(Type.POWER).setUnits(Keywords.u_KW); dcf = 1000.0* Constants.MperFT; qcf = Constants.LPSperCFS; if (flowFlag == PropertiesMap.FlowUnitsType.LPM) qcf = Constants.LPMperCFS; if (flowFlag == PropertiesMap.FlowUnitsType.MLD) qcf = Constants.MLDperCFS; if (flowFlag == PropertiesMap.FlowUnitsType.CMH) qcf = Constants.CMHperCFS; if (flowFlag == PropertiesMap.FlowUnitsType.CMD) qcf = Constants.CMDperCFS; hcf = Constants.MperFT; if (pressFlag == PropertiesMap.PressUnitsType.METERS) pcf = Constants.MperFT*SpGrav; else pcf = Constants.KPAperPSI*Constants.PSIperFT*SpGrav; wcf = Constants.KWperHP; } else { getField(Type.DEMAND).setUnits(flowFlag.parseStr); getField(Type.ELEV).setUnits(Keywords.u_FEET); getField(Type.HEAD).setUnits(Keywords.u_FEET); getField(Type.PRESSURE).setUnits(Keywords.u_PSI); getField(Type.LENGTH).setUnits(Keywords.u_FEET); getField(Type.DIAM).setUnits(Keywords.u_INCHES); getField(Type.FLOW).setUnits(flowFlag.parseStr); getField(Type.VELOCITY).setUnits(Keywords.u_FTperSEC); getField(Type.HEADLOSS).setUnits("ft"+Keywords.u_per1000FT); getField(Type.FRICTION).setUnits(""); getField(Type.POWER).setUnits(Keywords.u_HP); dcf = 12.0; qcf = 1.0; if (flowFlag == PropertiesMap.FlowUnitsType.GPM) qcf = Constants.GPMperCFS; if (flowFlag == PropertiesMap.FlowUnitsType.MGD) qcf = Constants.MGDperCFS; if (flowFlag == PropertiesMap.FlowUnitsType.IMGD)qcf = Constants.IMGDperCFS; if (flowFlag == PropertiesMap.FlowUnitsType.AFD) qcf = Constants.AFDperCFS; hcf = 1.0; pcf = Constants.PSIperFT*SpGrav; wcf = 1.0; } getField(Type.QUALITY).setUnits(""); ccf = 1.0; if (qualFlag == PropertiesMap.QualType.CHEM) { ccf = 1.0/Constants.LperFT3; getField(Type.QUALITY).setUnits(ChemUnits); getField(Type.REACTRATE).setUnits(ChemUnits + Keywords.t_PERDAY); } else if (qualFlag == PropertiesMap.QualType.AGE) getField(Type.QUALITY).setUnits(Keywords.u_HOURS); else if (qualFlag == PropertiesMap.QualType.TRACE) getField(Type.QUALITY).setUnits(Keywords.u_PERCENT); setUnits(Type.DEMAND,qcf); setUnits(Type.ELEV,hcf); setUnits(Type.HEAD,hcf); setUnits(Type.PRESSURE,pcf); setUnits(Type.QUALITY,ccf); setUnits(Type.LENGTH,hcf); setUnits(Type.DIAM,dcf); setUnits(Type.FLOW,qcf); setUnits(Type.VELOCITY,hcf); setUnits(Type.HEADLOSS,hcf); setUnits(Type.LINKQUAL,ccf); setUnits(Type.REACTRATE,ccf); setUnits(Type.FRICTION,1.0); setUnits(Type.POWER,wcf); setUnits(Type.VOLUME,hcf*hcf*hcf); if (Hstep < 1800) { setUnits(Type.TIME,1.0/60.0); getField(Type.TIME).setUnits(Keywords.u_MINUTES); } else { setUnits(Type.TIME,1.0/3600.0); getField(Type.TIME).setUnits(Keywords.u_HOURS); } } /** * Revert system units to user units. * @param type Field type. * @param value Value to be converted. * @return Value in user units. * @throws ENException */ public double revertUnit(Type type,double value) throws ENException { return type!=null?value*getUnits(type):value; } public double convertUnitToSystem(Type type,double value) throws ENException { return value/getUnits(type); } /** * Set field properties. * @param type Field type. * @param value Report field reference. */ private void setField(Type type,Field value) { fields.put(type,value); } /** * Set conversion value from field type. * @param type Field type. * @param value Field value. */ private void setUnits(Type type,Double value) { units.put(type,value); } }