/* * 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.msx.Structures; import org.addition.epanet.msx.VariableInterface; import org.addition.epanet.util.Utilities; import org.cheffo.jeplite.ASTVarNode; import org.cheffo.jeplite.JEP; import org.cheffo.jeplite.ParseException; import org.cheffo.jeplite.SimpleNode; import org.cheffo.jeplite.function.PostfixMathCommand; import java.util.*; import java.util.regex.*; import java.util.regex.Pattern; public class MathExpr { private JEP jeb; private Map<ASTVarNode,Integer> variables; private SimpleNode topNode; private static final Pattern PAT_NUMBER = Pattern.compile("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$"); static class MathExp_exp extends PostfixMathCommand{ public MathExp_exp(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ return Math.exp(params[0]); } } static class MathExp_sgn extends PostfixMathCommand{ public MathExp_sgn(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ return Utilities.getSignal(params[0]); } } static class MathExp_acot extends PostfixMathCommand{ public MathExp_acot(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ return 1.57079632679489661923 - Math.atan(params[0]); } } static class MathExp_sinh extends PostfixMathCommand{ public MathExp_sinh(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ return Math.sinh(params[0]); } } static class MathExp_cosh extends PostfixMathCommand{ public MathExp_cosh(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ return Math.cosh(params[0]); } } static class MathExp_tanh extends PostfixMathCommand{ public MathExp_tanh(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ return Math.tanh(params[0]); } } static class MathExp_coth extends PostfixMathCommand{ public MathExp_coth(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ return (Math.exp(params[0])+Math.exp(-params[0]))/(Math.exp(params[0])-Math.exp(-params[0])); } } static class MathExp_log10 extends PostfixMathCommand{ public MathExp_log10(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ return Math.log10(params[0]); } } static class MathExp_step extends PostfixMathCommand{ public MathExp_step(){ numberOfParameters = 1; } public final double operation(double[] params) throws ParseException{ if (params[0] <= 0.0) return 0.0; else return 1.0; } } public MathExpr(){ jeb = new JEP(); jeb.addFunction("exp",new MathExp_exp()); jeb.addFunction("sgn",new MathExp_sgn()); jeb.addFunction("acot",new MathExp_acot()); jeb.addFunction("sinh",new MathExp_sinh()); jeb.addFunction("cosh",new MathExp_cosh()); jeb.addFunction("tanh",new MathExp_tanh()); jeb.addFunction("coth",new MathExp_coth()); jeb.addFunction("log10",new MathExp_log10()); jeb.addFunction("step",new MathExp_step()); jeb.addStandardConstants(); jeb.addStandardFunctions(); variables = new Hashtable<ASTVarNode,Integer>(); } //public double evaluate(Chemical chem, boolean pipe){ // double res = 0; // // for( Map.Entry<ASTVarNode,Integer> entry : variables.entrySet()){ // if(pipe) // entry.getKey().setValue(chem.getPipeVariableValue(entry.getValue()) ); // else // entry.getKey().setValue(chem.getTankVariableValue(entry.getValue()) ); // } // // try { // return topNode.getValue(); // } catch (org.cheffo.jeplite.ParseException e) { // return 0; // } //} public double evaluatePipeExp(ExprVariable var){ for( Map.Entry<ASTVarNode,Integer> entry : variables.entrySet()){ entry.getKey().setValue(var.getPipeVariableValue(entry.getValue()) ); } try { return topNode.getValue(); } catch (org.cheffo.jeplite.ParseException e) { return 0; } } public double evaluateTankExp(ExprVariable var){ for( Map.Entry<ASTVarNode,Integer> entry : variables.entrySet()){ entry.getKey().setValue(var.getTankVariableValue(entry.getValue()) ); } try { return topNode.getValue(); } catch (org.cheffo.jeplite.ParseException e) { return 0; } } public static MathExpr create(String formula, VariableInterface var){ MathExpr expr = new MathExpr(); String [] colWords = formula.split("[\\W]"); final List<String> mathFuncs = Arrays.asList(new String[]{"cos", "sin", "tan", "cot", "abs", "sgn", "sqrt", "log", "exp", "asin", "acos", "atan", "acot", "sinh", "cosh", "tanh", "coth", "log10", "step"}); for(String word : colWords) { if(word.trim().length()!=0) { if(!PAT_NUMBER.matcher(word).matches()){ // if it isn't a number // its a word if(!mathFuncs.contains(word.toLowerCase())) { expr.jeb.addVariable(word,0.0d); ASTVarNode node = expr.jeb.getVarNode(word); expr.variables.put(node,var.getIndex(word)); } else // it's a function { // it's an upper case function, convert to lower case if(!word.equals(word.toLowerCase())) formula = formula.replaceAll(word,word.toLowerCase()); } } } } expr.jeb.parseExpression(formula); expr.topNode = expr.jeb.getTopNode(); return expr; } }