public class Constants {


    public static double        PI = 3.141592654;

    /**
     * Max. # of disconnected nodes listed
      */
    public static int           MAXCOUNT = 10;
    /**
     * Max. # title lines
     */
    public static int           MAXTITLE = 3;
    /**
     * Max. input errors reported
     */
    public static final int     MAXERRS = 10;
    //public static final int     MAXMSG = 79;
    //public static final int     MAXID  = 31;
    //public static final int     MAXFNAME = 259;

    /**
     * Epanet binary files code version
     */
    public static final int CODEVERSION=20012;

     /**
     * Epanet binary files ID
     */
    public static final int MAGICNUMBER=516114521;

     /**
     * Epanet binary files version
     */
    public static final int VERSION=200;

    /**
     * Equivalent to zero flow
      */
    public static final double QZERO=1.e-6d;
    /**
     * Big coefficient
     */
    public static final double CBIG=1.e8d;
    /**
     * Small coefficient
     */
    public static final double CSMALL=1.e-6d;

    /**
     * Default max. # hydraulic iterations
     */
    public static final int MAXITER=200;
    /**
     * Default hydraulics convergence ratio
     */
    public static final double  HACC=0.001d;
    /**
     * Default hydraulic head tolerance (ft)
     */
    public static final double  HTOL     =0.0005d;
    /**
     * Default flow rate tolerance (cfs)
     */
    public static final double  QTOL     =0.0001d;

    /**
     * Default water age tolerance (hrs)
     */
    public static final double  AGETOL   =0.01d;
    /**
     * Default concentration tolerance
     */
    public static final double  CHEMTOL  =0.01d;
    /**
     * Default uses no page breaks
     */
    public static final int     PAGESIZE =0;
    /**
     * Default specific gravity
     */
    public static final double  SPGRAV   =1.0d;
    /**
     * Default pump efficiency
     */
    public static final double  EPUMP    =75d;
    /**
     * Default demand pattern ID
     */
    public static final String  DEFPATID ="1";

    /**
     * Default low flow resistance tolerance
     */
    public static final double  RQTOL     =1E-7d;

    /**
     * Default status check frequency
     */
    public static final int     CHECKFREQ =2;
    /**
     * Default # iterations for status checks
     */
    public static final int     MAXCHECK  =10;
    /**
     *  Default damping threshold
     */
    public static final double  DAMPLIMIT =0;

    ///**
    // * Max. # types of network variables
    // */
    //public static final int     MAXVAR = 21;

    public static final double  BIG  =     1.E10d;
    public static final double  TINY =     1.E-6d;

    public static final double  MISSING =  -1.E10d;

    public static final double  GPMperCFS = 448.831d;
    public static final double  AFDperCFS = 1.9837d;
    public static final double  MGDperCFS = 0.64632d;
    public static final double  IMGDperCFS= 0.5382d;
    public static final double  LPSperCFS = 28.317d;
    public static final double  LPMperCFS = 1699.0d;
    public static final double  CMHperCFS = 101.94d;
    public static final double  CMDperCFS = 2446.6d;
    public static final double  MLDperCFS = 2.4466d;
    public static final double  M3perFT3  = 0.028317d;
    public static final double  LperFT3   = 28.317d;
    public static final double  MperFT    = 0.3048d;
    public static final double  MMperFT    = 304.8d;
    public static final double  INperFT  = 12.0d;
    public static final double  PSIperFT  = 0.4333d;
    public static final double  KPAperPSI = 6.895d;
    public static final double  KWperHP   = 0.7457d;
    public static final int     SECperDAY = 86400;

    public static final double  DIFFUS    = 1.3E-8d;
    public static final double  VISCOS    = 1.1E-5 ;
} 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);
    }

} 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;}
} public class PropertiesMap {

    /**
     * Flow units.
     */
    static public enum FlowUnitsType {
        AFD(Keywords.w_AFD),         //   acre-feet per day
        CFS(Keywords.w_CFS),         //   cubic feet per second
        CMD(Keywords.w_CMD),         //   cubic meters per day
        CMH(Keywords.w_CMH),         //   cubic meters per hour
        GPM(Keywords.w_GPM),         //   gallons per minute
        IMGD(Keywords.w_IMGD),       //   imperial million gal. per day
        LPM(Keywords.w_LPM),         //   liters per minute
        LPS(Keywords.w_LPS),         //   liters per second
        MGD(Keywords.w_MGD),         //   million gallons per day
        MLD(Keywords.w_MLD);         //   megaliters per day

        public static FlowUnitsType parse(String text) {
            for (FlowUnitsType type : FlowUnitsType.values())
                if (Utilities.match(text, type.parseStr)) return type;
            return null;
        }

        public final String parseStr;

        private FlowUnitsType(String parseStr) {
            this.parseStr = parseStr;
        }
    }

    /**
     * Head loss formula.
     */
    static public enum FormType {
        /**
         * Chezy-Manning
         */
        CM(Keywords.w_CM),
        /**
         * Darcy-Weisbach
         */
        DW(Keywords.w_DW),
        /**
         * Hazen-Williams
         */
        HW(Keywords.w_HW);
        /**
         * Parse string id.
         */
        public final String parseStr;

        FormType(String parseStr) {
            this.parseStr = parseStr;
        }
    }

    /**
     * Hydraulics solution option.
     */
    static public enum Hydtype {
        /**
         * Save after current run.
         */
        SAVE,
        /**
         * Use temporary file.
         */
        SCRATCH,
        /**
         * Use from previous run.
         */
        USE
    }

    /**
     * Pressure units.
     */
    static public enum PressUnitsType {
        KPA(Keywords.w_KPA),        // pounds per square inch
        METERS(Keywords.w_METERS),        // kiloPascals
        PSI(Keywords.w_PSI);  // meters

        public final String parseStr;

        PressUnitsType(String parseStr) {
            this.parseStr = parseStr;
        }
    }

    /**
     * Water quality analysis option.
     */
    static public enum QualType {
        /**
         * Analyze water age.
         */
        AGE(2, Keywords.w_AGE),
        /**
         * Analyze a chemical.
         */
        CHEM(1, Keywords.w_CHEM),
        /**
         * No quality analysis.
         */
        NONE(0, Keywords.w_NONE),
        /**
         * Trace % of flow from a source
         */
        TRACE(3, Keywords.w_TRACE);

        /**
         * Parse quality type from string.
         */
        public static QualType parse(String text) {
            for (QualType type : QualType.values())
                if (Utilities.match(text, type.parseStr)) return type;
            return null;
        }

        /**
         * Sequencial id.
         */
        public final int id;

        /**
         * Parse string id.
         */
        public final String parseStr;

        private QualType(int id, String pStr) {
            this.id = id;
            this.parseStr = pStr;
        }
    }

    /**
     * Reporting flag.
     */
    static public enum ReportFlag {
        FALSE, //0
        SOME, //2,
        // //1
        TRUE
    }

    /**
     * Status report options.
     */
    static public enum StatFlag {
        FALSE(Keywords.w_NO),
        FULL(Keywords.w_FULL),
        TRUE(Keywords.w_YES);

        public static StatFlag parse(String text) {
            for (StatFlag type : StatFlag.values())
                if (Utilities.match(text, type.parseStr)) return type;
            return null;
        }

        public final String parseStr;

        private StatFlag(String parseStr) {
            this.parseStr = parseStr;
        }

    }

    /**
     * Time series statistics.
     */
    static public enum TstatType {
        AVG(Keywords.w_AVG),      // none
        MAX(Keywords.w_MAX),       // time-averages
        MIN(Keywords.w_MIN),       // minimum values
        RANGE(Keywords.w_RANGE),       // maximum values
        SERIES(Keywords.w_NONE);     // max - min values

        public final String parseStr;

        private TstatType(String parseStr) {
            this.parseStr = parseStr;
        }
    }


    /**
     * Unit system.
     */
    static public enum UnitsType {
        /**
         * SI (metric)
         */
        SI,
        /**
         * US
         */
        US
    }

    public static final String ALTREPORT = "AltReport";
    public static final String BULKORDER = "BulkOrder";
    public static final String CHECK_FREQ = "CheckFreq";
    public static final String CHEM_NAME = "ChemName";
    public static final String CHEM_UNITS = "ChemUnits";
    public static final String CLIMIT = "Climit";
    public static final String CTOL = "Ctol";
    public static final String DAMP_LIMIT = "DampLimit";
    public static final String DCOST = "Dcost";
    public static final String DEF_PAT_ID = "DefPatID";
    public static final String DIFFUS = "Diffus";
    public static final String DMULT = "Dmult";
    public static final String DUR = "Dur";
    public static final String ECOST = "Ecost";
    public static final String EMAX = "Emax";
    public static final String ENERGYFLAG = "Energyflag";
    public static final String EPAT_ID = "EpatID";
    public static final String EPUMP = "Epump";
    public static final String EXTRA_ITER = "ExtraIter";
    public static final String FLOWFLAG = "Flowflag";
    public static final String FORMFLAG = "Formflag";
    public static final String HACC = "Hacc";
    public static final String HEXP = "Hexp";
    public static final String HSTEP = "Hstep";
    public static final String HTOL = "Htol";
    public static final String HYD_FNAME = "HydFname";
    public static final String HYDFLAG = "Hydflag";
    public static final String KBULK = "Kbulk";
    public static final String KWALL = "Kwall";
    public static final String LINKFLAG = "Linkflag";
    public static final String MAP_FNAME = "MapFname";
    public static final String MAXCHECK = "MaxCheck";
    public static final String MAXITER = "MaxIter";
    public static final String MESSAGEFLAG = "Messageflag";
    public static final String NODEFLAG = "Nodeflag";
    public static final String PAGE_SIZE = "PageSize";
    public static final String PRESSFLAG = "Pressflag";
    public static final String PSTART = "Pstart";
    public static final String PSTEP = "Pstep";
    public static final String QEXP = "Qexp";
    public static final String QSTEP = "Qstep";
    public static final String QTOL = "Qtol";
    public static final String QUALFLAG = "Qualflag";
    public static final String RFACTOR = "Rfactor";
    public static final String RQTOL = "RQtol";
    public static final String RSTART = "Rstart";
    public static final String RSTEP = "Rstep";
    public static final String RULESTEP = "Rulestep";
    public static final String SPGRAV = "SpGrav";
    public static final String STATFLAG = "Statflag";
    public static final String SUMMARYFLAG = "Summaryflag";
    public static final String TANKORDER = "TankOrder";
    public static final String TRACE_NODE = "TraceNode";
    public static final String TSTART = "Tstart";
    public static final String TSTATFLAG = "Tstatflag";
    public static final String UNITSFLAG = "Unitsflag";
    public static final String VISCOS = "Viscos";

    public static final String WALLORDER = "WallOrder";
    public static final String[] EpanetObjectsNames = {TSTATFLAG, HSTEP, DUR, QSTEP, CHECK_FREQ,
            MAXCHECK, DMULT, ALTREPORT, QEXP, HEXP, RQTOL, QTOL, BULKORDER, TANKORDER, WALLORDER,
            RFACTOR, CLIMIT, KBULK, KWALL, DCOST, ECOST, EPAT_ID, EPUMP, PAGE_SIZE, STATFLAG, SUMMARYFLAG,
            MESSAGEFLAG, ENERGYFLAG, NODEFLAG, LINKFLAG, RULESTEP, PSTEP, PSTART, RSTEP, RSTART, TSTART,
            FLOWFLAG, PRESSFLAG, FORMFLAG, HYDFLAG, QUALFLAG, UNITSFLAG, HYD_FNAME, CHEM_NAME, CHEM_UNITS,
            DEF_PAT_ID, MAP_FNAME, TRACE_NODE, EXTRA_ITER, CTOL, DIFFUS, DAMP_LIMIT, VISCOS, SPGRAV, MAXITER,
            HACC, HTOL, EMAX
    };

    private Map<String, Object> values;



    public PropertiesMap() {
        values = new HashMap<String, Object>();
        loadDefaults();
    }

    /**
     * Get an object from the map.
     *
     * @param name Object name.
     * @return Object refernce.
     * @throws ENException If object name not found.
     */
    public Object get(String name) throws ENException {
        return values.get(name);
    }

    public String getAltReport() throws ENException {
        return (String) get(ALTREPORT);
    }

    public Double getBulkOrder() throws ENException {
        return (Double) get(BULKORDER);
    }

    public Integer getCheckFreq() throws ENException {
        return (Integer) get(CHECK_FREQ);
    }

    public String getChemName() throws ENException {
        return (String) get(CHEM_NAME);
    }

    public String getChemUnits() throws ENException {
        return (String) get(CHEM_UNITS);
    }

    public Double getClimit() throws ENException {
        return (Double) get(CLIMIT);
    }

    public Double getCtol() throws ENException {
        return (Double) get(CTOL);
    }

    public Double getDampLimit() throws ENException {
        return (Double) get(DAMP_LIMIT);
    }

    public Double getDcost() throws ENException {
        return (Double) get(DCOST);
    }

    public String getDefPatId() throws ENException {
        return (String) get(DEF_PAT_ID);
    }

    public Double getDiffus() throws ENException {
        return (Double) get(DIFFUS);
    }

    public Double getDmult() throws ENException {
        return (Double) get(DMULT);
    }

    public Long getDuration() throws ENException {
        return (Long) get(DUR);
    }

    public Double getEcost() throws ENException {
        return (Double) get(ECOST);
    }

    public Double getEmax() throws ENException {
        return (Double) get(EMAX);
    }

    public Boolean getEnergyflag() throws ENException {
        return (Boolean) get(ENERGYFLAG);
    }

    public String getEpatId() throws ENException {
        return (String) get(EPAT_ID);
    }

    public Double getEpump() throws ENException {
        return (Double) get(EPUMP);
    }

    public Integer getExtraIter() throws ENException {
        return (Integer) get(EXTRA_ITER);
    }

    public FlowUnitsType getFlowflag() throws ENException {
        return (FlowUnitsType) get(FLOWFLAG);
    }

    public FormType getFormflag() throws ENException {
        return (FormType) get(FORMFLAG);
    }

    public Double getHacc() throws ENException {
        return (Double) get(HACC);
    }

    public Double getHexp() throws ENException {
        return (Double) get(HEXP);
    }

    public Long getHstep() throws ENException {
        return (Long) get(HSTEP);
    }

    public Double getHtol() throws ENException {
        return (Double) get(HTOL);
    }

    public Hydtype getHydflag() throws ENException {
        return (Hydtype) get(HYDFLAG);
    }

    public String getHydFname() throws ENException {
        return (String) get(HYD_FNAME);
    }

    public Double getKbulk() throws ENException {
        return (Double) get(KBULK);
    }

    public Double getKwall() throws ENException {
        return (Double) get(KWALL);
    }

    public ReportFlag getLinkflag() throws ENException {
        return (ReportFlag) get(LINKFLAG);
    }

    public String getMapFname() throws ENException {
        return (String) get(MAP_FNAME);
    }

    public Integer getMaxCheck() throws ENException {
        return (Integer) get(MAXCHECK);
    }

    public Integer getMaxIter() throws ENException {
        return (Integer) get(MAXITER);
    }

    public Boolean getMessageflag() throws ENException {
        return (Boolean) get(MESSAGEFLAG);
    }

    public ReportFlag getNodeflag() throws ENException {
        return (ReportFlag) get(NODEFLAG);
    }

    /**
     * Get objects names in this map.
     *
     * @param exclude_epanet exclude Epanet objects.
     * @return List of objects names.
     */
    public List<String> getObjectsNames(boolean exclude_epanet) {
        List<String> allObjs = new ArrayList<String>(values.keySet());
        if (exclude_epanet)
            allObjs.removeAll(Arrays.asList(EpanetObjectsNames));
        return allObjs;
    }

    public Integer getPageSize() throws ENException {
        return (Integer) get(PAGE_SIZE);
    }

    public PressUnitsType getPressflag() throws ENException {
        return (PressUnitsType) get(PRESSFLAG);
    }

    public Long getPstart() throws ENException {
        return (Long) get(PSTART);
    }

    public Long getPstep() throws ENException {
        return (Long) get(PSTEP);
    }

    public Double getQexp() throws ENException {
        return (Double) get(QEXP);
    }

    public Long getQstep() throws ENException {
        return (Long) get(QSTEP);
    }

    public Double getQtol() throws ENException {
        return (Double) get(QTOL);
    }

    public QualType getQualflag() throws ENException {
        return (QualType) get(QUALFLAG);
    }

    public Double getRfactor() throws ENException {
        return (Double) get(RFACTOR);
    }

    public Double getRQtol() throws ENException {
        return (Double) get(RQTOL);
    }

    public Long getRstart() throws ENException {
        return (Long) get(RSTART);
    }

    public Long getRstep() throws ENException {
        return (Long) get(RSTEP);
    }

    public Long getRulestep() throws ENException {
        return (Long) get(RULESTEP);
    }

    public Double getSpGrav() throws ENException {
        return (Double) get(SPGRAV);
    }

    public StatFlag getStatflag() throws ENException {
        return (StatFlag) get(STATFLAG);
    }

    public Boolean getSummaryflag() throws ENException {
        return (Boolean) get(SUMMARYFLAG);
    }

    public Double getTankOrder() throws ENException {
        return (Double) get(TANKORDER);
    }

    public String getTraceNode() throws ENException {
        return (String) get(TRACE_NODE);
    }

    public Long getTstart() throws ENException {
        return (Long) get(TSTART);
    }

    public TstatType getTstatflag() throws ENException {
        return (TstatType) get(TSTATFLAG);
    }

    public UnitsType getUnitsflag() throws ENException {
        return (UnitsType) get(UNITSFLAG);
    }

    public Double getViscos() throws ENException {
        return (Double) get(VISCOS);
    }

    public Double getWallOrder() throws ENException {
        return (Double) get(WALLORDER);
    }

    /**
     * Init properties with default value.
     */
    private void loadDefaults() {
        put(BULKORDER, new Double(1.0d));     // 1st-order bulk reaction rate
        put(TANKORDER, new Double(1.0d));     // 1st-order tank reaction rate
        put(WALLORDER, new Double(1.0d));     // 1st-order wall reaction rate
        put(RFACTOR, new Double(1.0d));     // No roughness-reaction factor
        put(CLIMIT, new Double(0.0d));     // No limiting potential quality
        put(KBULK, new Double(0.0d));     // No global bulk reaction
        put(KWALL, new Double(0.0d));     // No global wall reaction
        put(DCOST, new Double(0.0d));     // Zero energy demand charge
        put(ECOST, new Double(0.0d));     // Zero unit energy cost
        put(EPAT_ID, "");                   // No energy price pattern
        put(EPUMP, Constants.EPUMP);      // Default pump efficiency
        put(PAGE_SIZE, Constants.PAGESIZE);
        put(STATFLAG, StatFlag.FALSE);
        put(SUMMARYFLAG, true);
        put(MESSAGEFLAG, true);
        put(ENERGYFLAG, false);
        put(NODEFLAG, ReportFlag.FALSE);
        put(LINKFLAG, ReportFlag.FALSE);
        put(TSTATFLAG, TstatType.SERIES);     // Generate time series output
        put(HSTEP, new Long(3600));       // 1 hr hydraulic time step
        put(DUR, new Long(0));          // 0 sec duration (steady state)
        put(QSTEP, new Long(0));          // No pre-set quality time step
        put(RULESTEP, new Long(0));          // No pre-set rule time step
        put(PSTEP, new Long(3600));       // 1 hr time pattern period
        put(PSTART, new Long(0));          // Starting pattern period
        put(RSTEP, new Long(3600));       // 1 hr reporting period
        put(RSTART, new Long(0));          // Start reporting at time 0
        put(TSTART, new Long(0));          // Starting time of day
        put(FLOWFLAG, FlowUnitsType.GPM);    // Flow units are gpm
        put(PRESSFLAG, PressUnitsType.PSI);   // Pressure units are psi
        put(FORMFLAG, FormType.HW);          // Use Hazen-Williams formula
        put(HYDFLAG, Hydtype.SCRATCH);      // No external hydraulics file
        put(QUALFLAG, QualType.NONE);        // No quality simulation
        put(UNITSFLAG, UnitsType.US);         // US unit system
        put(HYD_FNAME, "");
        put(CHEM_NAME, Keywords.t_CHEMICAL);
        put(CHEM_UNITS, Keywords.u_MGperL);    // mg/L
        put(DEF_PAT_ID, Constants.DEFPATID);   // Default demand pattern index
        put(MAP_FNAME, "");
        put(ALTREPORT, "");
        put(TRACE_NODE, "");                   // No source tracing
        put(EXTRA_ITER, new Integer(-1));      // Stop if network unbalanced
        put(CTOL, Constants.MISSING);    // No pre-set quality tolerance
        put(DIFFUS, Constants.MISSING);    // Temporary diffusivity
        put(DAMP_LIMIT, Constants.DAMPLIMIT);
        put(VISCOS, Constants.MISSING);    // Temporary viscosity
        put(SPGRAV, Constants.SPGRAV);     // Default specific gravity
        put(MAXITER, Constants.MAXITER);    // Default max. hydraulic trials
        put(HACC, Constants.HACC);       // Default hydraulic accuracy
        put(HTOL, Constants.HTOL);       // Default head tolerance
        put(QTOL, Constants.QTOL);       // Default flow tolerance
        put(RQTOL, Constants.RQTOL);      // Default hydraulics parameters
        put(HEXP, new Double(0.0d));
        put(QEXP, new Double(2.0d));     // Flow exponent for emitters
        put(CHECK_FREQ, Constants.CHECKFREQ);
        put(MAXCHECK, Constants.MAXCHECK);
        put(DMULT, new Double(1.0d));     // Demand multiplier
        put(EMAX, new Double(0.0d));     // Zero peak energy usage


    }


    /**
     * Insert an object into the map.
     *
     * @param name Object name.
     * @param obj  Object reference.
     */
    public void put(String name, Object obj) {
        values.put(name, obj);
    }

    public void setAltReport(String str) throws ENException {
        put(ALTREPORT, str);
    }

    public void setBulkOrder(Double bulkOrder) throws ENException {
        put(BULKORDER, bulkOrder);
    }

    public void setCheckFreq(int checkFreq) throws ENException {
        put(CHECK_FREQ, checkFreq);
    }

    public void setChemName(String chemName) throws ENException {
        put(CHEM_NAME, chemName);
    }

    public void setChemUnits(String chemUnits) throws ENException {
        put(CHEM_UNITS, chemUnits);
    }

    public void setClimit(Double climit) throws ENException {
        put(CLIMIT, climit);
    }

    public void setCtol(Double ctol) throws ENException {
        put(CTOL, ctol);
    }

    public void setDampLimit(Double dampLimit) throws ENException {
        put(DAMP_LIMIT, dampLimit);
    }

    public void setDcost(Double dcost) throws ENException {
        put(DCOST, dcost);
    }

    public void setDefPatId(String defPatID) throws ENException {
        put(DEF_PAT_ID, defPatID);
    }

    public void setDiffus(Double diffus) throws ENException {
        put(DIFFUS, diffus);
    }

    public void setDmult(Double dmult) throws ENException {
        put(DMULT, dmult);
    }

    public void setDuration(long dur) throws ENException {
        put(DUR, dur);
    }

    public void setEcost(Double ecost) throws ENException {
        put(ECOST, ecost);
    }

    public void setEmax(Double emax) throws ENException {
        put(EMAX, emax);
    }

    public void setEnergyflag(boolean energyflag) throws ENException {
        put(ENERGYFLAG, energyflag);
    }

    public void setEpatId(String epat) throws ENException {
        put(EPAT_ID, epat);
    }

    public void setEpump(Double epump) throws ENException {
        put(EPUMP, epump);
    }

    public void setExtraIter(int extraIter) throws ENException {
        put(EXTRA_ITER, extraIter);
    }

    public void setFlowflag(FlowUnitsType flowflag) throws ENException {
        put(FLOWFLAG, flowflag);
    }

    public void setFormflag(FormType formflag) throws ENException {
        put(FORMFLAG, formflag);
    }

    public void setHacc(Double hacc) throws ENException {
        put(HACC, hacc);
    }

    public void setHexp(Double hexp) throws ENException {
        put(HEXP, hexp);
    }

    public void setHstep(long hstep) throws ENException {
        put(HSTEP, hstep);
    }

    public void setHtol(Double htol) throws ENException {
        put(HTOL, htol);
    }

    public void setHydflag(Hydtype hydflag) throws ENException {
        put(HYDFLAG, hydflag);
    }

    public void setHydFname(String hydFname) throws ENException {
        put(HYD_FNAME, hydFname);
    }

    public void setKbulk(Double kbulk) throws ENException {
        put(KBULK, kbulk);
    }

    public void setKwall(Double kwall) throws ENException {
        put(KWALL, kwall);
    }

    public void setLinkflag(ReportFlag linkflag) throws ENException {
        put(LINKFLAG, linkflag);
    }

    public void setMapFname(String mapFname) throws ENException {
        put(MAP_FNAME, mapFname);
    }

    public void setMaxCheck(int maxCheck) throws ENException {
        put(MAXCHECK, maxCheck);
    }

    public void setMaxIter(int maxIter) throws ENException {
        put(MAXITER, maxIter);
    }

    public void setMessageflag(boolean messageflag) throws ENException {
        put(MESSAGEFLAG, messageflag);
    }

    public void setNodeflag(ReportFlag nodeflag) throws ENException {
        put(NODEFLAG, nodeflag);
    }

    public void setPageSize(int pageSize) throws ENException {
        put(PAGE_SIZE, pageSize);
    }

    public void setPressflag(PressUnitsType pressflag) throws ENException {
        put(PRESSFLAG, pressflag);
    }

    public void setPstart(long pstart) throws ENException {
        put(PSTART, pstart);
    }

    public void setPstep(long pstep) throws ENException {
        put(PSTEP, pstep);
    }

    public void setQexp(Double qexp) throws ENException {
        put(QEXP, qexp);
    }

    public void setQstep(long qstep) throws ENException {
        put(QSTEP, qstep);
    }

    public void setQtol(Double qtol) throws ENException {
        put(QTOL, qtol);
    }

    public void setQualflag(QualType qualflag) throws ENException {
        put(QUALFLAG, qualflag);
    }

    public void setRfactor(Double rfactor) throws ENException {
        put(RFACTOR, rfactor);
    }

    public void setRQtol(Double RQtol) throws ENException {
        put(RQTOL, RQtol);
    }

    public void setRstart(long rstart) throws ENException {
        put(RSTART, rstart);
    }

    public void setRstep(long rstep) throws ENException {
        put(RSTEP, rstep);
    }

    public void setRulestep(long rulestep) throws ENException {
        put(RULESTEP, rulestep);
    }

    public void setSpGrav(Double spGrav) throws ENException {
        put(SPGRAV, spGrav);
    }

    public void setStatflag(StatFlag statflag) throws ENException {
        put(STATFLAG, statflag);
    }

    public void setSummaryflag(boolean summaryflag) throws ENException {
        put(SUMMARYFLAG, summaryflag);
    }

    public void setTankOrder(Double tankOrder) throws ENException {
        put(TANKORDER, tankOrder);
    }

    public void setTraceNode(String traceNode) throws ENException {
        put(TRACE_NODE, traceNode);
    }

    public void setTstart(long tstart) throws ENException {
        put(TSTART, tstart);
    }

    public void setTstatflag(TstatType tstatflag) throws ENException {
        put(TSTATFLAG, tstatflag);
    }

    public void setUnitsflag(UnitsType unitsflag) throws ENException {
        put(UNITSFLAG, unitsflag);
    }

    public void setViscos(Double viscos) throws ENException {
        put(VISCOS, viscos);
    }

    public void setWallOrder(Double wallOrder) throws ENException {
        put(WALLORDER, wallOrder);
    }


} public class Curve {

    /**
     * Computed curve coefficients.
     */
    public static class Coeffs{
        public double h0;       // head at zero flow (y-intercept)

        public double r;        // dHead/dFlow (slope)
        public Coeffs(double h0, double r) {
            this.h0 = h0;
            this.r = r;
        }
    }

    // Type of curve
    static public enum CurveType{
        E_CURVE     (2),    // volume curve
        H_CURVE     (3),    // pump curve
        P_CURVE     (1),    // efficiency curve
        V_CURVE     (0);    // head loss curve

        public final int id;

        private CurveType(int val){id = val;}
    }
    /**
     * Curve name.
     */
    private String      id;
    /**
     * Curve type.
     */
    private CurveType   type;
    /**
     * Curve abscissa values.
     */
    private DblList     x;


    /**
     * Curve ordinate values.
     */
    private DblList     y;

    public Curve() {
        this.id = "";
        x = new DblList();
        y = new DblList();
    }

    /**
     * Computes intercept and slope of head v. flow curve at current flow.
     * @param q Flow value.
     * @return
     */
    public Coeffs getCoeff(FieldsMap fMap,double q) throws ENException {
        double h0;
        double r;
        int k1, k2, npts;

        q *= fMap.getUnits(Type.FLOW);

        npts = getNpts();

        k2 = 0;
        while (k2 < npts && x.get(k2) < q) k2++;
        if (k2 == 0) k2++;
        else if (k2 == npts) k2--;
        k1 = k2 - 1;

        r = (y.get(k2) - y.get(k1)) / (x.get(k2) - x.get(k1));
        h0 = y.get(k1) - (r) * x.get(k1);

        h0 = (h0) / fMap.getUnits(Type.HEAD);
        r = (r) * fMap.getUnits(Type.FLOW) / fMap.getUnits(Type.HEAD);

        return new Coeffs(h0, r);
    }

    public String getId() {
        return id;
    }

    /**
     * Get the number of points.
     * @return If the abscissa points count differ from the ordinate it returns -1, otherwise,
     * it returns the abscissa point count.
     */
    public int getNpts() {
        if(x.size()!=y.size()){
            return -1;
        }
        return x.size();
    }

    public CurveType getType() {
        return type;
    }

    public List<Double> getX(){
        return x;
    }

    public List<Double> getY(){
        return y;
    }

    public void setId(String Id) {
        this.id = Id;
    }

    public void setType(CurveType Type) {
        this.type = Type;
    }
} public class Link implements Comparable<Link> {
    /**
     * Init links flow resistance values.
     *
     * @param formflag
     * @param hexp
     * @throws org.addition.epanet.util.ENException
     *
     */
    public void initResistance(PropertiesMap.FormType formflag, Double hexp) {
        double e, d, L;
        this.setFlowResistance(Constants.CSMALL);
        switch (this.getType()) {
            case CV:
            case PIPE:
                e = this.getRoughness();
                d = this.getDiameter();
                L = this.getLenght();
                switch (formflag) {
                    case HW:
                        this.setFlowResistance(4.727 * L / Math.pow(e, hexp) / Math.pow(d, 4.871));
                        break;
                    case DW:
                        this.setFlowResistance(L / 2.0 / 32.2 / d / Math.pow(Constants.PI * Math.pow(d, 2) / 4.0, 2));
                        break;
                    case CM:
                        this.setFlowResistance(Math.pow(4.0 * e / (1.49 * Constants.PI * d * d), 2) *
                                Math.pow((d / 4.0), -1.333) * L);
                }
                break;

            case PUMP:
                this.setFlowResistance(Constants.CBIG);
                break;
        }
    }

    /**
     * Type of link
     */
    static public enum LinkType {
        /**
         * Pipe with check valve.
         */
        CV(0, Keywords.w_CV),
        /**
         * Flow control valve.
         */
        FCV(6, Keywords.w_FCV),
        /**
         * General purpose valve.
         */
        GPV(8, Keywords.w_GPV),
        /**
         * Pressure breaker valve.
         */
        PBV(5, Keywords.w_PBV),
        /**
         * Regular pipe.
         */
        PIPE(1, Keywords.w_PIPE),
        /**
         * Pressure reducing valve.
         */
        PRV(3, Keywords.w_PRV),
        /**
         * Pressure sustaining valve.
         */
        PSV(4, Keywords.w_PSV),
        /**
         * Pump.
         */
        PUMP(2, Keywords.w_PUMP),
        /**
         * Throttle control valve.
         */
        TCV(7, Keywords.w_TCV);

        public static LinkType parse(String text) {
            for (LinkType type : LinkType.values())
                if (Utilities.match(text, type.parseStr)) return type;
            return null;
        }

        /**
         * Sequencial ID.
         */
        public final int id;

        /**
         * Valve type string.
         */
        public final String parseStr;
        ;

        private LinkType(int val, String str) {
            id = val;
            parseStr = str;
        }
    }

    /**
     * Link/Tank/Pump status
     */
    static public enum StatType {
        /**
         * Valve active (partially open).
         */
        ACTIVE(4, Keywords.w_ACTIVE, Keywords.t_ACTIVE),
        /**
         * Closed.
         */
        CLOSED(2, Keywords.w_CLOSED, Keywords.t_CLOSED),
        /**
         * Tank emptying.
         */
        EMPTYING(9, "", Keywords.t_EMPTYING),
        /**
         * Tank filling.
         */
        FILLING(8, "", Keywords.t_FILLING),
        /**
         * Open.
         */
        OPEN(3, Keywords.w_OPEN, Keywords.t_OPEN),
        /**
         * Temporarily closed.
         */
        TEMPCLOSED(1, "", Keywords.t_TEMPCLOSED),
        /**
         * FCV cannot supply flow.
         */
        XFCV(6, "", Keywords.t_XFCV),
        /**
         * Pump exceeds maximum flow.
         */
        XFLOW(5, "", Keywords.t_XFLOW),
        /**
         * Pump cannot deliver head (closed).
         */
        XHEAD(0, "", Keywords.t_XHEAD),
        /**
         * Valve cannot supply pressure.
         */
        XPRESSURE(7, "", Keywords.t_XPRESSURE);

        /**
         * Sequencial id.
         */
        public final int id;

        /**
         * Parse string.
         */
        public final String parseStr;

        /**
         * Report string.
         */
        public final String reportStr;


        private StatType(int val, String str, String rep) {
            id = val;
            parseStr = str;
            reportStr = rep;
        }
    }

    /**
     * Initial species concentrations.
     */
    private double[] c0;
    /**
     * Link comment (parsed from INP or excel file)
     */
    private String comment;
    /**
     * Link diameter (feet).
     */
    private double diameter;
    /**
     * First node.
     */
    private Node first;
    /**
     * Link name.
     */
    private String ID;
    /**
     * Bulk react. coeff.
     */
    private double kb;
    /**
     * Minor loss coeff.
     */
    private double km;
    /**
     * Wall react. coeff.
     */
    private double kw;
    /**
     * Link length (feet).
     */
    private double lenght;
    /**
     * Kinetic parameter values.
     */
    private double[] param;

    /**
     * Flow resistance.
     */
    private double resistance;
    /**
     * Roughness factor.
     */
    private double roughness;
    /**
     * Link report flag.
     */
    private boolean rptFlag;
    /**
     * Second node.
     */
    private Node second;
    /**
     * Link status.
     */
    private StatType status;
    /**
     * Link subtype.
     */
    private LinkType type;
    /**
     * List of points for link path rendering.
     */
    private List<Point> vertices;

    public Link() {
        comment = "";
        vertices = new ArrayList<Point>();
        type = LinkType.CV;
        status = StatType.XHEAD;
    }

    public double[] getC0() {
        return c0;
    }

    public String getComment() {
        return comment;
    }

    public double getDiameter() {
        return diameter;
    }

    public Node getFirst() {
        return first;
    }

    public double getFlowResistance() {
        return resistance;
    }

    public String getId() {
        return ID;
    }

    public double getKb() {
        return kb;
    }

    public double getKm() {
        return km;
    }

    public double getKw() {
        return kw;
    }

    public double getLenght() {
        return lenght;
    }

    public double getNUDiameter(PropertiesMap.UnitsType type) {
        return NUConvert.revertDiameter(type, diameter);
    }

    public double getNULength(PropertiesMap.UnitsType type) {
        return NUConvert.revertDistance(type, lenght);
    }

    public double getNURoughness(PropertiesMap.FlowUnitsType fType, PropertiesMap.PressUnitsType pType, double SpGrav) {
        switch (getType()) {
            case FCV:
                return NUConvert.revertFlow(fType, roughness);
            case PRV:
            case PSV:
            case PBV:
                return NUConvert.revertPressure(pType, SpGrav, roughness);
        }
        return roughness;
    }

    public double[] getParam() {
        return param;
    }

    public double getRoughness() {
        return roughness;
    }

    public Node getSecond() {
        return second;
    }

    public StatType getStat() {
        return status;
    }

    public LinkType getType() {
        return type;
    }

    public List<Point> getVertices() {
        return vertices;
    }

    public boolean isRptFlag() {
        return rptFlag;
    }

    public void setC0(double[] c0) {
        this.c0 = c0;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public void setDiameter(double diameter) {
        this.diameter = diameter;
    }

    public void setDiameterAndUpdate(double diameter, org.addition.epanet.network.Network net) throws ENException {
        double realkm = km * Math.pow(this.diameter, 4.0) / 0.02517;
        this.diameter = diameter;
        km = 0.02517 * realkm / Math.pow(diameter, 4);
        initResistance(net.getPropertiesMap().getFormflag(), net.getPropertiesMap().getHexp());
    }


    public void setFirst(Node n1) {
        first = n1;
    }

    public void setFlowResistance(double r) {
        this.resistance = r;
    }

    public void setId(String id) {
        ID = id;
    }

    public void setKb(double kb) {
        this.kb = kb;
    }

    public void setKm(double km) {
        this.km = km;
    }

    public void setKw(double kw) {
        this.kw = kw;
    }

    public void setLenght(double len) {
        this.lenght = len;
    }

    public void setNUDiameter(PropertiesMap.UnitsType type, double value) {
        diameter = NUConvert.convertDistance(type, value);
    }

    public void setNULenght(PropertiesMap.UnitsType type, double value) {
        lenght = NUConvert.convertDistance(type, value);
    }

    public void setParam(double[] param) {
        this.param = param;
    }

    public void setReportFlag(boolean rptFlag) {
        this.rptFlag = rptFlag;
    }

    public void setRoughness(double kc) {
        this.roughness = kc;
    }

    public void setSecond(Node n2) {
        second = n2;
    }

    public void setStatus(StatType stat) {
        this.status = stat;
    }

    public void setType(LinkType type) {
        this.type = type;
    }

//    @Override
//    public boolean equals(Object o) {
//        if (this == o) return true;
//        if (o == null || getClass() != o.getClass()) return false;
//
//        Link link = (Link) o;
//
//        if (ID != null ? !ID.equals(link.ID) : link.ID != null) return false;
//
//        return true;
//    }

    @Override
    public int hashCode() {
        return ID != null ? ID.hashCode() : 0;
    }

    public int compareTo(Link o) {
        return ID.compareTo(o.ID);
    }
} static public enum StatType {
    /**
     * Valve active (partially open).
     */
    ACTIVE(4, Keywords.w_ACTIVE, Keywords.t_ACTIVE),
    /**
     * Closed.
     */
    CLOSED(2, Keywords.w_CLOSED, Keywords.t_CLOSED),
    /**
     * Tank emptying.
     */
    EMPTYING(9, "", Keywords.t_EMPTYING),
    /**
     * Tank filling.
     */
    FILLING(8, "", Keywords.t_FILLING),
    /**
     * Open.
     */
    OPEN(3, Keywords.w_OPEN, Keywords.t_OPEN),
    /**
     * Temporarily closed.
     */
    TEMPCLOSED(1, "", Keywords.t_TEMPCLOSED),
    /**
     * FCV cannot supply flow.
     */
    XFCV(6, "", Keywords.t_XFCV),
    /**
     * Pump exceeds maximum flow.
     */
    XFLOW(5, "", Keywords.t_XFLOW),
    /**
     * Pump cannot deliver head (closed).
     */
    XHEAD(0, "", Keywords.t_XHEAD),
    /**
     * Valve cannot supply pressure.
     */
    XPRESSURE(7, "", Keywords.t_XPRESSURE);

    /**
     * Sequencial id.
     */
    public final int id;

    /**
     * Parse string.
     */
    public final String parseStr;

    /**
     * Report string.
     */
    public final String reportStr;


    private StatType(int val, String str, String rep) {
        id = val;
        parseStr = str;
        reportStr = rep;
    }
} public class Pattern {
    /**
     * Pattern factors list.
     */
    private DblList factors;
    /**
     * Pattern name.
     */
    private String  id;

    public Pattern() {
        this.id = "";
        factors = new DblList();
    }

    public void add(Double factor){
        factors.add(factor);
    }


    public List<Double> getFactorsList(){
        return factors;
    }

    public String getId() {
        return id;
    }

    public int getLength(){
        return factors.size();
    }

    public void setId(String text){
        id = text;
    }
} public class Pump extends Link
{
    /**
     * Type of pump curve.
      */
    static public enum Type{
        /**
         * Constant horsepower.
         */
        CONST_HP(0),
        /**
         * User-defined custom curve.
         */
        CUSTOM(2),
        NOCURVE(3),

        /**
         * Power function.
         */
        POWER_FUNC(1);

        /**
         * Sequencial id.
         */
        public final int id;

        private Type(int val){id = val;}
    }

    /**
     * Unit energy cost.
     */
    private double eCost;
    /**
     * Effic. v. flow curve reference.
     */
    private Curve eCurve;
    /**
     * Energy usage statistics.
     */
    private double  energy[] = {0,0,0,0,0,0};
    /**
     * Energy cost pattern.
     */
    private Pattern ePat;
    /**
     * Shutoff head (feet)
     */
    private double h0;
    /**
     * Head v. flow curve reference.
     */
    private Curve hCurve;
    /**
     * Maximum head (feet)
     */
    private double hMax;
    /**
     * Flow exponent.
     */
    private double n;
    /**
     * Pump curve type.
     */
    private Type ptype;
    /**
     * Initial flow (feet^3/s).
     */
    private double q0;
    /**
     * Maximum flow (feet^3/s).
     */
    private double qMax;
    /**
     * Flow coefficient.
     */
    private double r;
    /**
     * Utilization pattern reference.
     */
    private Pattern uPat;

    public Pump() {
        super();
    }


    public double getEcost() {
        return eCost;
    }

    public Curve getEcurve() {
        return eCurve;
    }

    public double getEnergy(int id) {
        return energy[id];
    }

    public Pattern getEpat() {
        return ePat;
    }

    public double getFlowCoefficient() {
        return r;
    }

    public double getH0() {
        return h0;
    }

    public Curve getHcurve() {
        return hCurve;
    }

    public double getHmax() {
        return hMax;
    }

    public double getN() {
        return n;
    }

    public double getNUFlowCoefficient(PropertiesMap.UnitsType type){
        return NUConvert.revertPower(type,r);
    }


    public double getNUInitialFlow(PropertiesMap.FlowUnitsType type){
        return NUConvert.revertFlow(type,q0);
    }

    public double getNUMaxFlow(PropertiesMap.FlowUnitsType type){
        return NUConvert.revertFlow(type,qMax);
    }

    public double getNUMaxHead(PropertiesMap.UnitsType type){
        return NUConvert.revertDistance(type,hMax);
    }

    public double getNUShutoffHead(PropertiesMap.UnitsType type){
        return NUConvert.revertDistance(type,hMax);
    }

    public Type getPtype() {
        return ptype;
    }

    public double getQ0() {
        return q0;
    }

    public double getQmax() {
        return qMax;
    }

    public Pattern getUpat() {
        return uPat;
    }

    public void setEcost(double ecost) {
        eCost = ecost;
    }

    public void setEcurve(Curve ecurve) {
        this.eCurve = ecurve;
    }

    public void setEnergy(int id, double energy) {
        this.energy[id]=energy;
    }

    public void setEpat(Pattern epat) {
        ePat = epat;
    }

    public void setFlowCoefficient(double r) {
        this.r = r;
    }

    public void setH0(double h0) {
        this.h0 = h0;
    }

    public void setHcurve(Curve hcurve) {
        this.hCurve = hcurve;
    }

    public void setHmax(double hmax) {
        this.hMax = hmax;
    }

    public void setN(double n) {
        this.n = n;
    }

    public void setNUFlowCoefficient(PropertiesMap.UnitsType type, double value){
        r = NUConvert.convertPower(type,value);
    }

    public void setNUInitialFlow(PropertiesMap.FlowUnitsType type, double value){
        q0 = NUConvert.convertFlow(type,value);
    }

    public void setNUMaxFlow(PropertiesMap.FlowUnitsType type, double value){
        qMax = NUConvert.convertFlow(type,value);
    }

    public void setNUMaxHead(PropertiesMap.UnitsType type, double value){
        hMax = NUConvert.convertDistance(type,value);
    }

    public void setNUShutoffHead(PropertiesMap.UnitsType type, double value){
        h0 = NUConvert.convertDistance(type,value);
    }

    public void setPtype(Type ptype) {
        this.ptype = ptype;
    }

    public void setQ0(double q0) {
        this.q0 = q0;
    }

    public void setQmax(double qMax) {
        this.qMax = qMax;
    }

    public void setUpat(Pattern upat) {
        uPat = upat;
    }
} public class ENException extends Exception{

    /**
     * Error text bundle.
     */
    private static final ResourceBundle errorBundle = PropertyResourceBundle.getBundle("Error");

    /**
     * Array of arguments to be used in the error string creation.
     */
    private Object [] arguments;

    /**
     * Epanet error code.
     */
    private int codeID;

    /**
     * Get error code.
     * @return Code id.
     */
    public int getCodeID(){
        return codeID;
    }

    /**
     * Contructor from error code id.
     * @param id Error code id.
     */
    public ENException(int id){
        arguments = null;
        codeID = id;
    }

    /**
     * Contructor from error code id and multiple arguments.
     * @param id Error code id.
     * @param arg Extra arguments.
     */
    public ENException(int id, Object ... arg){
        codeID = id;
        arguments = arg;
    }

    /**
     * Contructor from other exception and multiple arguments.
     * @param e
     * @param arg
     */
    public ENException(ENException e, Object ... arg){
        arguments = arg;
        codeID = e.getCodeID();
    }

    /**
     * Get arguments array.
     */
    public Object [] getArguments(){
        return arguments;
    }

    /**
     * Handles the exception string conversion.
     * @return Final error string.
     */
    public String toString(){
        String str = errorBundle.getString("ERR"+codeID);
        if(str==null)
            return String.format("Unknown error message (%d)",codeID);
        else if(arguments!=null)
            return String.format(str,arguments);
        return str;
    }

}
 public class Utilities {

    /**
     * Convert string to double.
     * @param str Number string.
     * @return Reference to the converted Double number, null if the conversion was unsuccessful.
     */
    public static Double getDouble(String str){
        Double val;
        try{
            val = Double.parseDouble(str);
            return val;
        }
        catch(NumberFormatException ex)
        {
            return null;
        }
    }

    /**
     * Check if two strings match (case independent), based on the shortest string length.
     * @param a String A.
     * @param b String B.
     * @return Boolean is the two strings are similar.
     */
    public static boolean match(String a, String b){
        if(a.length()==0||b.length()==0)
            return false;

        if(a.length()>b.length())
        {
            String tmp = a.substring(0,b.length());
            if(tmp.equalsIgnoreCase(b))
                return true;
        }
        else
        {
            String tmp = b.substring(0,a.length());
            if(a.equalsIgnoreCase(tmp))
                return true;
        }
        return false;
    }

    /**
     * Parse time string to a double number.
     * @param time  Time string.
     * @param units Units format (PM or AM)
     * @return Time in hours (1.0 is 3600 seconds)
     */
    public static double getHour(String time, String units)
    {
        int    n=0;
        double  [] y = {0.0d,0.0d,0.0d};
        String [] s = time.split(":");


        for(int i = 0;i<s.length && i<=3;i++)
        {
            try{y[i] = Double.parseDouble(s[i]);}catch(NumberFormatException ex){return(-1.0d);}
            n++;
        }

        if (n == 1)
        {
            if (units.length() == 0)
                return(y[0]);

            if (Utilities.match(units, Keywords.w_SECONDS)) return(y[0]/3600.0);
            if (Utilities.match(units,Keywords.w_MINUTES)) return(y[0]/60.0);
            if (Utilities.match(units,Keywords.w_HOURS))   return(y[0]);
            if (Utilities.match(units,Keywords.w_DAYS))    return(y[0]*24.0);
        }

        if (n > 1) y[0] = y[0] + y[1]/60.0 + y[2]/3600.0;

        if (units.length() == 0)
            return(y[0]);

        if (units.equalsIgnoreCase(Keywords.w_AM))
        {
            if (y[0] >= 13.0) return(-1.0);
            if (y[0] >= 12.0) return(y[0]-12.0);
            else return(y[0]);
        }
        if (units.equalsIgnoreCase(Keywords.w_PM))
        {
            if (y[0] >= 13.0) return(y[0]-12.0);
            if (y[0] >= 12.0) return(y[0]);
            else return(y[0]+12.0);
        }
        return(-1.0);
    }

    /**
     * Compute the linear interpolation of a 2d cartesian graph.
     * @param n Number of points.
     * @param x X axis values array (abscissa).
     * @param y Y axis values array (ordinate).
     * @param xx The abscissa value.
     * @return The interpolated value.
     */
    public static double linearInterpolator(int n, List<Double> x, List<Double> y, double xx)
    {
        int    k,m;
        double  dx,dy;

        m = n - 1;
        if (xx <= x.get(0)) return(y.get(0));
        for (k=1; k<=m; k++)
        {
            if (x.get(k) >= xx)
            {
                dx = x.get(k)-x.get(k-1);
                dy = y.get(k)-y.get(k-1);
                if (Math.abs(dx) < Constants.TINY) return(y.get(k));
                else return(y.get(k) - (x.get(k)-xx)*dy/dx);
            }
        }
        return(y.get(m));
    }

    /**
     * Convert time to a string.
     * @param seconds Time to convert, in seconds.
     * @return Time string in epanet format.
     */
    public static String getClockTime(long seconds){
        long h,m,s;
        h = seconds/3600;
        m = (seconds % 3600) / 60;
        s = seconds - 3600*h - 60*m;
        return String.format("%02d:%02d:%02d",h,m,s);
    }

    /**
     * Computes coeffs. for pump curve.
     * @param h0 shutoff head
     * @param h1 design head
     * @param h2 head at max. flow
     * @param q1 design flow
     * @param q2 max. flow
     * @param a pump curve coeffs. (H = a-bQ^c),
     * @param b pump curve coeffs. (H = a-bQ^c),
     * @param c pump curve coeffs. (H = a-bQ^c),
     * @return Returns true if sucessful, false otherwise.
     */
    public static boolean  getPowerCurve(double h0, double h1, double h2, double q1,
                                         double q2, double [] a, double [] b, double [] c)
    {
        double h4,h5;
        if (
                h0      < Constants.TINY ||
                        h0 - h1 < Constants.TINY ||
                        h1 - h2 < Constants.TINY ||
                        q1      < Constants.TINY ||
                        q2 - q1 < Constants.TINY
                ) return false;
        a[0] = h0;
        h4 = h0 - h1;
        h5 = h0 - h2;
        c[0] = Math.log(h5/h4)/Math.log(q2/q1);
        if (c[0] <= 0.0 || c[0] > 20.0) return false;
        b[0] = -h4/Math.pow(q1,c[0]);

        if (b[0] >= 0.0)
            return(false);

        return(true);
    }

    /**
     * Get value signal, if bigger than 0 returns 1, -1 otherwise.
     * @param val Any real number.
     * @return -1 or 1
     */
    public static double getSignal(double val) {
        return val < 0 ? -1d : 1d;
    }

    /**
     * Text messages bundle.
     */
    public static final ResourceBundle textBundle = PropertyResourceBundle.getBundle("Text");

    /**
     * Error messages bundle.
     */
    public static final ResourceBundle errorBundle = PropertyResourceBundle.getBundle("Error");

    /**
     * Get epanet text info string from bundle.
     * @param text String id.
     * @return Info string.
     */
    public static String getText(String text) {
        return textBundle.getString(text);
    }

    /**
     * Get epanet error string from bundle.
     * @param text String id.
     * @return Error string.
     */
    public static String getError(String text) {
        return errorBundle.getString(text);
    }

    /**
     * Get float from byte array.
     * @param array Byte array.
     * @param id Float start byte.
     * @return Parsed float.
     */
    public static float getFloatFromBytes(byte [] array, int id){
        return Float.intBitsToFloat((array[id] << 24) |
                ((array[id+1] & 0xff) << 16) |
                ((array[id+2] & 0xff) << 8 |
                        (array[id+3] & 0xff)));
    }
    /**
     * Get integer from byte array.
     * @param array Byte array.
     * @param id Integer start byte.
     * @return Parsed Integer.
     */
    public static int getIntegerFromBytes(byte [] array, int id){
        return (array[id] << 24) |
                ((array[id+1] & 0xff) << 16) |
                ((array[id+2] & 0xff) << 8 |
                        (array[id+3] & 0xff));
    }

    /**
     * Get file extension string.
     * @param filename
     * @return
     */
    public static String getFileExtension(String filename){
        int lastDot = filename.lastIndexOf(".");
        if(lastDot>0 && lastDot+1< filename.length()){
            return filename.substring(lastDot+1).toLowerCase();
        }

        return "";
    }
    /**
     * Check if the applicationn is running on MacOS
     * @return
     */
    public static boolean isMac(){
        String os = System.getProperty("os.name").toLowerCase();
        return (os.indexOf( "mac" ) >= 0);
    }


}
import java.util.List;
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.structures.Curve;
import org.addition.epanet.network.structures.Link;
import org.addition.epanet.network.structures.Link.StatType;
import org.addition.epanet.network.structures.Pattern;
import org.addition.epanet.network.structures.Pump;
import org.addition.epanet.util.ENException;
import org.addition.epanet.util.Utilities;
import java.util.Collection;
/*
 * 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;



public class SimulationPump extends SimulationLink {

    private double h0;                  // Simulated shutoff head
    private double flowCoefficient;     // Simulated Flow coefficent
    private double n;                   // Simulated flow expoent

    public static class Energy {
        public Energy(double power, double efficiency) {
            this.power = power;
            this.efficiency = efficiency;
        }

        public double power;        // Pump used power (KW)
        public double efficiency;   // Pump effiency
    }

    public SimulationPump(Collection<SimulationNode> indexedNodes, Link ref, int idx) {
        super(indexedNodes, ref, idx);
        for (int i = 0; i < 6; i++)
            energy[i] = ((Pump) ref).getEnergy(0);

        h0 = ((Pump) ref).getH0();
        flowCoefficient = ((Pump) ref).getFlowCoefficient();
        n = ((Pump) ref).getN();
    }

    private double energy[] = {0, 0, 0, 0, 0, 0};


    public Pump.Type getPtype() {
        return ((Pump) link).getPtype();
    }

    public double getQ0() {
        return ((Pump) link).getQ0();
    }

    public double getQmax() {
        return ((Pump) link).getQmax();
    }

    public double getHmax() {
        return ((Pump) link).getHmax();
    }


    public Curve getHcurve() {
        return ((Pump) link).getHcurve();
    }

    public Curve getEcurve() {
        return ((Pump) link).getEcurve();
    }

    public Pattern getUpat() {
        return ((Pump) link).getUpat();
    }

    public Pattern getEpat() {
        return ((Pump) link).getEpat();
    }

    public double getEcost() {
        return ((Pump) link).getEcost();
    }


    // Simulation getters and setters


    public double getEnergy(int id) {
        return energy[id];//((Pump)node).getEnergy(id);
    }

    public void setEnergy(int id, double value) {
        energy[id] = value;
    }


    private void setH0(double h0) {
        this.h0 = h0;
    }

    public double getH0() {
        return h0;
    }

    public double getFlowCoefficient() {
        return flowCoefficient;
    }

    private void setFlowCoefficient(double flowCoefficient) {
        this.flowCoefficient = flowCoefficient;
    }

    private void setN(double n) {
        this.n = n;
    }

    public double getN() {
        return n;
    }

    // Computes flow energy associated with this link pump.
    private Energy getFlowEnergy(PropertiesMap pMap, FieldsMap fMap) throws ENException {