From 676b7bd6aded05f051a4ed85c35cd00eb2e46f68 Mon Sep 17 00:00:00 2001 From: jkauttio Date: Fri, 25 Apr 2014 12:55:30 +0000 Subject: [PATCH] Add missing features to vensim import, it is now feature complete but still needs cleanup refs #2924 git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/branches/dev-jkauttio@29353 ac1ea38d-2e2b-0410-8846-a27921b304fc --- .../sysdyn/modelImport/MdlParser.java | 19 +- .../simantics/sysdyn/modelImport/MdlUtil.java | 75 +- .../sysdyn/modelImport/mdl/Lookup.java | 27 +- .../sysdyn/modelImport/mdl/SketchComment.java | 4 +- .../sysdyn/modelImport/mdl/SketchValve.java | 2 +- .../modelImport/mdl/SubscriptVariable.java | 111 ++- .../sysdyn/modelImport/mdl/Variable.java | 16 +- .../sysdyn/modelImport/model/Model.java | 2 +- .../model/expression/LookupExpression.java | 3 +- .../modelImport/model/support/Function.java | 38 +- .../sysdyn/modelica/ModelicaWriter.java | 831 ++++++++++-------- 11 files changed, 700 insertions(+), 428 deletions(-) diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/MdlParser.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/MdlParser.java index 2d51eeef..b60deac5 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/MdlParser.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/MdlParser.java @@ -14,12 +14,7 @@ package org.simantics.sysdyn.modelImport; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.simantics.sysdyn.modelImport.mdl.Declaration; import org.simantics.sysdyn.modelImport.mdl.Lookup; @@ -27,7 +22,6 @@ import org.simantics.sysdyn.modelImport.mdl.MdlModel; import org.simantics.sysdyn.modelImport.mdl.Sketch; import org.simantics.sysdyn.modelImport.mdl.SketchComment; import org.simantics.sysdyn.modelImport.mdl.SketchConnection; -import org.simantics.sysdyn.modelImport.mdl.SketchElement; import org.simantics.sysdyn.modelImport.mdl.SketchObject; import org.simantics.sysdyn.modelImport.mdl.SketchValve; import org.simantics.sysdyn.modelImport.mdl.SketchVariable; @@ -35,18 +29,11 @@ import org.simantics.sysdyn.modelImport.mdl.Subscript; import org.simantics.sysdyn.modelImport.mdl.SubscriptVariable; import org.simantics.sysdyn.modelImport.mdl.Variable; import org.simantics.sysdyn.modelImport.model.Model; -import org.simantics.sysdyn.modelImport.model.element.Auxiliary; -import org.simantics.sysdyn.modelImport.model.element.Cloud; -import org.simantics.sysdyn.modelImport.model.element.Comment; import org.simantics.sysdyn.modelImport.model.element.Connection; import org.simantics.sysdyn.modelImport.model.element.ModelVariable; import org.simantics.sysdyn.modelImport.model.element.Symbol; import org.simantics.sysdyn.modelImport.model.element.Shadow; -import org.simantics.sysdyn.modelImport.model.element.Stock; import org.simantics.sysdyn.modelImport.model.element.Valve; -import org.simantics.sysdyn.modelImport.model.expression.IntegralExpression; -import org.simantics.sysdyn.modelImport.model.expression.NormalExpression; -import org.simantics.sysdyn.modelImport.model.support.Enumeration; public class MdlParser { @@ -70,12 +57,12 @@ public class MdlParser { double offset = 0; for (Subscript subscript : mdl.getAllSubscripts()) { - System.err.println("added subscript "+subscript.getName()+" "+subscript.isEquivalent()); - model.addEnumeration(subscript.getEnumeration(mdl)); + if (!subscript.isEquivalent()) + model.addEnumeration(subscript.getEnumeration(mdl)); } for (Lookup lookup : mdl.getAllLookups()) { - //model.addFunction(lookup.getFunction()); + model.addFunction(lookup.getFunction()); } try { diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/MdlUtil.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/MdlUtil.java index cdf019de..b3546407 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/MdlUtil.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/MdlUtil.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.simantics.sysdyn.modelImport.mdl.Lookup; import org.simantics.sysdyn.modelImport.mdl.MdlModel; public class MdlUtil { @@ -39,25 +40,25 @@ public class MdlUtil { "("+BASIC_NAME+")\\[("+BASIC_NAME+"\\!?(?:,"+BASIC_NAME+"\\!?)*)\\]"; // matches a vensim function (basic name followed by an open parenthesis) public static final String FUNCTION = - "([A-Za-z]\\w*(?:\\s+\\w+)*)\\s*\\("; + "("+BASIC_NAME+")\\s*\\("; public static String normalize(String str) { // start by removing all tabs from the string, not really necessary // but does make the equation cleaner str = str.replaceAll("\t", ""); - // inline operations could be removed here if it can be done reliablty - // normalize functions str = normalizeFunctions(str); // normalize variables str = normalizeVariables(str); + + // replace inline operations + str = str.replaceAll(":AND:", " and "); + str = str.replaceAll(":OR:", " or "); return str; } - // construct a replacement string from expression where each variable - // name is normalized to a sysdyn-friendly format private static String normalizeVariables(String expression) { StringBuilder result = new StringBuilder(); int offset = 0; @@ -99,8 +100,6 @@ public class MdlUtil { return result.toString(); } - // construct a replacement string from expression where each variable - // name is normalized to a sysdyn-friendly format private static String normalizeFunctions(String expression) { StringBuilder result = new StringBuilder(); int offset = 0; @@ -111,16 +110,26 @@ public class MdlUtil { String function = matcher.group(1); - if (function.equalsIgnoreCase("IF THEN ELSE")) { - result.append("IFTHENELSE"); + if (function.equalsIgnoreCase("sum")) { + // vensim "sum" is similar to modelica "sum" if operations + // are replaced with dot-operations (e.g. * with .*) inside + // the parameters + result.append("sum("); + int closing = expression.indexOf(')', matcher.end()); + String parameters = expression.substring(matcher.end(), closing); + parameters = parameters.replaceAll("\\*", ".*"); + result.append(parameters); + offset = closing; + } + else if (function.equalsIgnoreCase("if then else")) { + result.append("IFTHENELSE("); + offset = matcher.end(); } else { // this will also capitalize lookups, is this ok? - result.append(function.toUpperCase()); + result.append(function.toUpperCase()+"("); + offset = matcher.end(); } - result.append('('); - - offset = matcher.end(); } if (offset < expression.length()) { result.append(expression.substring(offset)); @@ -129,11 +138,14 @@ public class MdlUtil { return result.toString(); } - public static String expandIterations(String expression, MdlModel mdl) { - if (!expression.contains("[")) { - return expression; - } + public static String finalize(String expression, MdlModel mdl) { + expression = expandIterations(expression, mdl); + expression = renameLookups(expression, mdl); + return expression; + } + + private static String expandIterations(String expression, MdlModel mdl) { StringBuilder result = new StringBuilder(); int offset = 0; @@ -177,12 +189,37 @@ public class MdlUtil { return result.toString(); } + private static String renameLookups(String expression, MdlModel mdl) { + StringBuilder result = new StringBuilder(); + int offset = 0; + + Matcher matcher = Pattern.compile(FUNCTION).matcher(expression); + while (matcher.find()) { + result.append(expression.substring(offset, matcher.start())); + + String name = matcher.group(1); + + Lookup potential = mdl.getLookup(name); + + if (potential != null) { + System.err.println("found lookup "+name); + name = name.replaceAll("\\s+", ""); + } + + result.append(name+"("); + offset = matcher.end(); + } + if (offset < expression.length()) { + result.append(expression.substring(offset)); + } + + return result.toString(); + } + public static String replaceSubscripts(String expression, String subscript, String replacement) { StringBuilder result = new StringBuilder(); int offset = 0; - System.err.println("replace subscripts "+subscript+" with "+replacement+" in "+expression); - Matcher matcher = Pattern.compile(SUBSCRIPT).matcher(expression); while (matcher.find()) { result.append(expression.substring(offset, matcher.start())); diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/Lookup.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/Lookup.java index ae22514f..b166a32d 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/Lookup.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/Lookup.java @@ -52,8 +52,7 @@ public class Lookup extends Declaration { return null; } - // TODO: capitalize this? - String name = MdlUtil.normalize(matcher.group(lookupName)); + String name = matcher.group(lookupName).toUpperCase(); double xMin = Double.parseDouble(matcher.group(lookupRangeXMin)); double yMin = Double.parseDouble(matcher.group(lookupRangeYMin)); double xMax = Double.parseDouble(matcher.group(lookupRangeXMax)); @@ -97,10 +96,30 @@ public class Lookup extends Declaration { } public Function getFunction() { - if (function == null) { - function = null; + if (function != null) { + return function; } + StringBuilder body = new StringBuilder(); + + // one input, one output + body.append("input Real i;\n"); + body.append("output Real o;\n"); + + // interpolate over the set of points + body.append("algorithm\n"); + + body.append("o := interpolate(i, {"); + for (int i = 0; i < points.length/2; i++) { + if (i > 0) { + body.append(','); + } + body.append('{').append(points[2*i]).append(',').append(points[2*i+1]).append('}'); + } + body.append("});\n"); + + function = new Function(getName().replaceAll("\\s+", ""), body.toString()); + return function; } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SketchComment.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SketchComment.java index 0fc0d91c..8cae21d9 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SketchComment.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SketchComment.java @@ -65,8 +65,8 @@ public class SketchComment extends SketchElement { return new Comment(getDimensions(), "I/O objects are not supported"); switch(icon) { - case CLOUD: return new Cloud(getDimensions()); - case OTHER: return new Comment(getDimensions(), text); + case CLOUD: return new Cloud(getDimensions(sketch)); + case OTHER: return new Comment(getDimensions(sketch), text); default: return null; } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SketchValve.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SketchValve.java index 1a27e873..2d0c88ce 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SketchValve.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SketchValve.java @@ -50,7 +50,7 @@ public class SketchValve extends SketchElement { Valve valve = new Valve(Orientation.HORIZONTAL, getTextPosition()); - return variable.setUpModelVariable(valve, getDimensions(), mdl); + return variable.setUpModelVariable(valve, getDimensions(sketch), mdl); } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SubscriptVariable.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SubscriptVariable.java index bed7ca72..23bf744e 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SubscriptVariable.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/SubscriptVariable.java @@ -12,6 +12,7 @@ import java.util.regex.Pattern; import org.simantics.sysdyn.modelImport.MdlUtil; import org.simantics.sysdyn.modelImport.model.expression.EnumerationExpression; +import org.simantics.sysdyn.modelImport.model.expression.NormalExpression; import org.simantics.sysdyn.modelImport.model.support.Enumeration; public class SubscriptVariable extends Variable { @@ -113,9 +114,50 @@ public class SubscriptVariable extends Variable { EnumerationExpression expr = new EnumerationExpression(enumerations); - // populate the created expression (this is very complicated) + // populate the created expression (TODO: comment) - System.err.println("populate subscripts for variable "+getName()); + // TODO: is this check correct, also does not work correctly yet + if (next == null && enumerations.size() == 2) { + // option a: +// StringBuilder buffer = new StringBuilder(); +// buffer.append('{'); +// for (int i = 0; i < values.length; i++) { +// if (i > 0) { +// buffer.append(','); +// } +// buffer.append('{'); +// for (int j = 0; j < values[i].length; j++) { +// if (j > 0) { +// buffer.append(','); +// } +// buffer.append(values[i][j]); +// } +// buffer.append('}'); +// } +// buffer.append('}'); +// +// String[] exprindices = new String[enumerations.size()]; +// for (int i = 0; i < exprindices.length; i++) { +// exprindices[i] = enumerations.get(i).getName(); +// } +// +// expr.addExpression(new NormalExpression(buffer.toString()), exprindices); +// return expr; + + // option b: (probably more sensible) + double[][] values = getPossibleValueArray(getExpressionString()); + if (values != null) { + for (int i = 0; i < values.length; i++) { + for (int j = 0; j < values[i].length; j++) { + expr.addExpression( + new NormalExpression(Double.toString(values[i][j])), + enumerations.get(0).getValues().get(i), + enumerations.get(1).getValues().get(j)); + } + } + return expr; + } + } var = this; while (var != null) { @@ -144,7 +186,12 @@ public class SubscriptVariable extends Variable { } for (WorkExpression we : workqueue) { - expr.addExpression(parseExpression(MdlUtil.expandIterations(we.expression, mdl)), we.indices); + // TODO: is this check correct + String expression = MdlUtil.finalize(we.expression, mdl); + if (next == null) { + expression = removeComparisons(expression, indices, we.indices); + } + expr.addExpression(parseExpression(expression), we.indices); } var = var.getNext(); @@ -162,5 +209,63 @@ public class SubscriptVariable extends Variable { this.expression = expression; } } + + private static double[][] getPossibleValueArray(String expression) { + // (number(,number)*;)* + Matcher matcher = Pattern.compile( + "("+MdlUtil.DBL+"(,"+MdlUtil.DBL+")*;)*" + ).matcher(expression); + + if (!matcher.matches()) { + return null; + } + + String[] rows = expression.split(";"); + double[][] result = new double[rows.length][]; + for (int i = 0; i < rows.length; i++) { + String[] columns = rows[i].split(","); + result[i] = new double[columns.length]; + for (int j = 0; j < columns.length; j++) { + result[i][j] = Double.parseDouble(columns[j]); + } + } + + return result; + } + + private static String removeComparisons(String expression, String[] subscripts, String[] values) { + + if (!expression.contains("=")) { + return expression; + } + + for (int i = 0; i < subscripts.length; i++) { + for (int j = 0; j < subscripts.length; j++) { + StringBuilder result = new StringBuilder(); + int offset = 0; + + Matcher matcher = Pattern.compile(subscripts[i]+"\\s*=\\s*"+subscripts[j]).matcher(expression); + while (matcher.find()) { + result.append(expression.substring(offset, matcher.start())); + + if (values[i].equals(values[j])) { + result.append("true"); + } + else { + result.append("false"); + } + + offset = matcher.end(); + } + if (offset < expression.length()) { + result.append(expression.substring(offset)); + } + + expression = result.toString(); + } + } + + return expression; + } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/Variable.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/Variable.java index f93e7075..7dceeaef 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/Variable.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/mdl/Variable.java @@ -73,7 +73,7 @@ public class Variable extends Declaration { } public Expression getExpression(MdlModel mdl) { - return parseExpression(MdlUtil.expandIterations(expression, mdl)); + return parseExpression(MdlUtil.finalize(expression, mdl)); } protected static Expression parseExpression(String expression) { @@ -104,6 +104,20 @@ public class Variable extends Declaration { } return new DelayExpression(parameters[0], parameters[1], parameters[2], Integer.parseInt(parameters[3])); } + else if (function.startsWith("SMOOTHI")) { + if (parameters.length != 3) { + System.err.println("malformed smoothi expression: "+expression); + } + // what is the correct degree for smooth? + return new DelayExpression(parameters[0], parameters[1], parameters[2], 1); + } + else if (function.startsWith("SMOOTH")) { + if (parameters.length != 2) { + System.err.println("malformed smooth expression: "+expression); + } + // what is the correct degree and initial value for smooth? + return new DelayExpression(parameters[0], parameters[1], parameters[0], 1); + } else if (function.startsWith("GAME")) { // a game expression, currently treated as a normal expression if (parameters.length != 1) { diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/Model.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/Model.java index 38bde81b..0b56e6e6 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/Model.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/Model.java @@ -165,7 +165,7 @@ public class Model implements IWriteableObject { } for (Function f : getFunctions()) { - f.write(graph, configuration, context); + f.write(graph, model, context); } for (Symbol e : symbols) { diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/expression/LookupExpression.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/expression/LookupExpression.java index f2f84eba..8f68915d 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/expression/LookupExpression.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/expression/LookupExpression.java @@ -33,8 +33,9 @@ public class LookupExpression extends Expression { StringBuilder lookup = new StringBuilder(); lookup.append('{'); for (int i = 0; i < points.length / 2; i++) { - if (i > 0) + if (i > 0) { lookup.append(','); + } lookup.append('{').append(points[2*i]).append(',').append(points[2*i+1]).append('}'); } lookup.append('}'); diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/support/Function.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/support/Function.java index 7f086e92..faa5f5a6 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/support/Function.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelImport/model/support/Function.java @@ -2,34 +2,66 @@ package org.simantics.sysdyn.modelImport.model.support; import java.util.List; +import org.simantics.databoard.Bindings; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.Layer0; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.modelImport.model.IWriteableObject; import org.simantics.sysdyn.modelImport.model.WriteContext; public class Function implements IWriteableObject { private String name; + private String body; + private String description; - public Function(String name, List inputs, List outputs, String code) { + private Resource function; + + public Function(String name, String body) { + this(name, body, null); + } + + public Function(String name, String body, String description) { this.name = name; + this.body = body; + this.description = description; } public String getName() { return name; } + public String getBody() { + return body; + } + @Override public Resource write(WriteGraph graph, Resource parent, WriteContext context) throws DatabaseException { System.err.println("write function"); - return null; + + Layer0 l0 = Layer0.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + + function = GraphUtils.create2(graph, sr.SysdynModelicaFunction, + l0.HasName, name, + l0.PartOf, parent); + + graph.claimLiteral(function, sr.SysdynModelicaFunction_modelicaFunctionCode, body, Bindings.STRING); + + if (description != null) { + graph.claimLiteral(function, l0.HasDescription, body, Bindings.STRING); + } + + return function; } @Override public Resource getResource() { - return null; + return function; } } diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java index fc9415c7..1b191676 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java @@ -31,6 +31,8 @@ import org.simantics.sysdyn.representation.ModuleType; import org.simantics.sysdyn.representation.Sheet; import org.simantics.sysdyn.representation.Stock; import org.simantics.sysdyn.representation.Variable; +import org.simantics.sysdyn.representation.expressions.DelayExpression; +import org.simantics.sysdyn.representation.expressions.IExpression; /** * ModelicaWriter writes Sysdyn model representations (objmap) into Modelica code. @@ -41,387 +43,462 @@ import org.simantics.sysdyn.representation.Variable; */ public class ModelicaWriter { - /** - * Write a collection of configurations into a single Modelica code - * @param isGame - * - * @param Configurations Configurations, one main configuration and possible modules - * @return Complete Modelica code of a model - */ - public static String write(Collection _configurations, boolean isGame, String omVersion) { - - ArrayList configurations = new ArrayList(_configurations); - Collections.sort(configurations, new Comparator() { - - boolean uses(Configuration o1, Configuration o2) { - ModuleType type = o2.getModuleType(); - if(type == null) return false; - for(IElement e : o1.getElements()) { - if(e instanceof Module) { - Module m = (Module)e; - if(m.getType().equals(type)) { - return true; - } - } - } - return false; - } - + /** + * Write a collection of configurations into a single Modelica code + * @param isGame + * + * @param Configurations Configurations, one main configuration and possible modules + * @return Complete Modelica code of a model + */ + public static String write(Collection _configurations, boolean isGame, String omVersion) { + + ArrayList configurations = new ArrayList(_configurations); + Collections.sort(configurations, new Comparator() { + + boolean uses(Configuration o1, Configuration o2) { + ModuleType type = o2.getModuleType(); + if(type == null) return false; + for(IElement e : o1.getElements()) { + if(e instanceof Module) { + Module m = (Module)e; + if(m.getType().equals(type)) { + return true; + } + } + } + return false; + } + @Override public int compare(Configuration o1, Configuration o2) { - if(uses(o1, o2)) return 1; - else if(uses(o2, o1)) return -1; - else return 0; + if(uses(o1, o2)) return 1; + else if(uses(o2, o1)) return -1; + else return 0; } - + }); - - Configuration modelConf = null; - for(Configuration conf : configurations) { - if(conf.getModel() != null) { - modelConf = conf; - } - } - StringBuilder b = new StringBuilder(); - - int spreadsheetlocation = b.length(); - - String modelName = modelConf.getLabel().replace(" ", ""); - b.append("model " + modelName + "\n"); - - // Super class for enumerations - b.append("partial class Enumeration_class\n"); - b.append(" parameter Integer size;\n"); - b.append(" parameter Integer elements[:];\n"); - b.append("end Enumeration_class;\n\n"); - - - HashSet sheetNames = new HashSet(); - for(Sheet sheet : getSpreadSheets(configurations)) - sheetNames.add(sheet.getModelicaName()); - - // Write all module configurations to the declarations part (first) - for(Configuration conf : configurations) { - conf.setIsGameConfiguration(isGame); - if(!conf.equals(modelConf)) - writeConfiguration(conf, sheetNames, b); - } - - // Write model configuration last, so that equations-part does not contain module definitions - modelConf.setIsGameConfiguration(isGame); - writeConfiguration(modelConf, sheetNames, b); - - b.append("end " + modelName + ";\n\n"); - - // Insert spreadsheets - if(omVersion != null && omVersion.startsWith("1.9")) { - b.insert(spreadsheetlocation, getGlobalSpreadSheets(configurations)); - } else { - b.append(getGlobalSpreadSheets(configurations)); - } - - - return b.toString(); - } - - /** - * Get all spreadsheets that are found in the model - * @param configurations - * @return - */ - private static List getSpreadSheets(Collection configurations) { - for(Configuration conf : configurations) { - if(conf.getModel() != null) { - for(IElement e : conf.getElements()) { - if(e instanceof Book) { - return ((Book)e).getSheets(); - } - } - } - } - return Collections.emptyList(); - } - - /** - * - */ - private static String getGlobalSpreadSheets(Collection configurations) { - StringBuilder sheets = new StringBuilder(); - for(Configuration conf : configurations) { - if(conf.getModel() != null) { - for(IElement e : conf.getElements()) { - if(e instanceof Book) { - return ((Book)e).getBook(); - } - } - } - } - - return sheets.toString(); - } - - /** - * Write a single configuration to a given string builder - * - * @param configuration Model or module configuration - * @param b String builder - */ - private static void writeConfiguration(Configuration configuration, HashSet sheetNames, StringBuilder b) { - boolean defTime = true; - String app; - - // Lists for storing different configuration elements - ArrayList variables = new ArrayList(); - ArrayList inputs = new ArrayList(); - ArrayList modules = new ArrayList(); - ArrayList stocks = new ArrayList(); - ArrayList enumerations = new ArrayList(); - ArrayList inputDependencies = new ArrayList(); - ArrayList outputDependencies = new ArrayList(); - HashMap> moduleInputs = new HashMap>(); - - // Initialize lists - for(IElement element : configuration.getElements()) { - if(element instanceof IndependentVariable) { - // Normal variable - variables.add((IndependentVariable)element); - if(element instanceof Stock) - // Stock - stocks.add((Stock)element); - } else if (element instanceof Module) { - // Module - Module m = (Module)element; - modules.add(m); - moduleInputs.put(m.getName(), new ArrayList()); - for(IElement e : m.getType().getConfiguration().getElements()) - // Inputs inside the module - if(e instanceof Input && !((Input)e).isHeadOfDependency()) { - moduleInputs.get(m.getName()).add((Input)e); - } - } else if (element instanceof Input) { - // Input variables - inputs.add((Input)element); - } else if (element instanceof Enumeration) { - // Enumerations - enumerations.add((Enumeration)element); - } else if (element instanceof Dependency) { - Dependency dependency = (Dependency)element; - if(dependency.getHead() instanceof Module) { - // References given to child modules - outputDependencies.add(dependency); - } else if(dependency.getTail() instanceof Module){ - // References from child modules - inputDependencies.add(dependency); - } - } - } - - // Setup input references. (Input, String reference to another variable) - HashMap inputReferences = new HashMap(); - setupInputReferences(inputReferences, inputDependencies); - - - // If the configuration is model configuration, use model name. Otherwise, use configuration name. - ModuleType mt = configuration.getModuleType(); - - // className == null, if this is a model configuration. model configuration start and end are written in ModelicaWriter.write - String className = mt != null ? (mt.getName().replace(" ", "")) : null; - - if(className != null) - b.append("\nclass ").append(className); - - // Add spreadsheets to all modules and model. Model is "inner" and modules "outer" - String globalStatus = mt != null ? "outer" : "inner"; - for(String sheetName : sheetNames) - b.append("\n " + globalStatus + " " + sheetName + "_class" + " "+ sheetName + ";"); - - b.append("\n"); - - - if(!enumerations.isEmpty()) { - b.append("// Enumeration definitions\n"); - for(Enumeration e : enumerations) { - b.append(e.getDeclaration()); - } - } - - b.append("// Variable definitions\n"); - for(IndependentVariable variable : variables) { - app = variable.getDeclaration(); - if (app != null) b.append(app); - } - - if(defTime) { - // Time variable for FMU (game) simulations - if(configuration.isGameConfiguration()) { - if(configuration.getModel() != null) - // Parameter for model root. Values changed in FMU simulator - b.append(" parameter Real time = 0;\n"); - else - // Continuous variable for module instances - b.append(" Real time;\n"); - - } - } - - if(!modules.isEmpty()) { - b.append("// Module definitions\n"); - for(Module m : modules) { - b.append(m.getDeclaration()); - } - } - - - // Input definitions - inputDefinitions(b, configuration, inputs, inputReferences); - - boolean initialEquations = false; - for(Stock stock : stocks) { - app = stock.getInitialEquation(); - if (app != null) { - if(initialEquations == false) { - initialEquations = true; - b.append("// Initial Equations\n"); - b.append("initial equation\n"); - } - b.append(app); - } - } - - boolean equation = false; - b.append("// Equations\n"); - for(IndependentVariable variable : variables) { - app = variable.getEquation(); - if (app != null) { - if(!equation) { - b.append("equation\n"); - equation = true; - } - - b.append(app); - } - } - - // If "equation" has not been added but there are still equations to be defined, add "equation" - if(!equation && (!inputReferences.isEmpty() || !outputDependencies.isEmpty() || - !moduleInputs.isEmpty() || !modules.isEmpty())) - b.append("equation\n"); - - // Continous input references - continuousInputReferences(b, inputReferences); - - b.append("// Outputs\n"); - for(Dependency dependency : outputDependencies) { - Variable variable = (Variable)dependency.getTail(); - Module module = (Module)dependency.getHead(); - Input reference = (Input)dependency.refersTo(); - if(reference != null && reference.getName() != null && (reference.getVariability() == null || reference.getVariability().isEmpty())) { - b.append(" " + module.getName() + "." + reference.getModelicaName() + " = " + variable.getModelicaName() + ";\n"); - moduleInputs.get(module.getName()).remove(reference); - } - } - - b.append("// Default values for inputs in modules\n"); - for(String moduleLabel : moduleInputs.keySet()) { - for(Input input : moduleInputs.get(moduleLabel)) { - if(input.getVariability() == null || input.getVariability().isEmpty()) - b.append(" " + moduleLabel + "." + input.getModelicaName() + " = " + input.getDefaultInputValue(moduleLabel) + ";\n"); - } - } - - if(defTime) { - if(configuration.isGameConfiguration() && !modules.isEmpty()) { - b.append("// Time values for module\n"); - for(Module m : modules) { - b.append(" " + m.getName() + ".time = time;\n"); - } - } - } - - if(className != null) - b.append("end ").append(className).append(";\n\n"); - - } - + + Configuration modelConf = null; + for(Configuration conf : configurations) { + if(conf.getModel() != null) { + modelConf = conf; + } + } + StringBuilder b = new StringBuilder(); + + int spreadsheetlocation = b.length(); + + String modelName = modelConf.getLabel().replace(" ", ""); + b.append("model " + modelName + "\n"); + + // Super class for enumerations + b.append("partial class Enumeration_class\n"); + b.append(" parameter Integer size;\n"); + b.append(" parameter Integer elements[:];\n"); + b.append("end Enumeration_class;\n\n"); + + // find out which delays are used in the model and create the + // necessary classes + HashSet delays = new HashSet(); + for (Configuration configuration : configurations) { + for (IElement element : configuration.getElements()) { + if (element instanceof IndependentVariable) { + for (IExpression expression : ((IndependentVariable)element).getExpressions()) { + if (expression instanceof DelayExpression) { + delays.add(((DelayExpression)expression).getOrder()); + } + } + } + } + } + + for (Integer i : delays) { + b.append(getDelayClass(i)); + b.append("\n"); + } + + HashSet sheetNames = new HashSet(); + for(Sheet sheet : getSpreadSheets(configurations)) + sheetNames.add(sheet.getModelicaName()); + + // Write all module configurations to the declarations part (first) + for(Configuration conf : configurations) { + conf.setIsGameConfiguration(isGame); + if(!conf.equals(modelConf)) + writeConfiguration(conf, sheetNames, b); + } + + // Write model configuration last, so that equations-part does not contain module definitions + modelConf.setIsGameConfiguration(isGame); + writeConfiguration(modelConf, sheetNames, b); + + b.append("end " + modelName + ";\n\n"); + + // Insert spreadsheets + if(omVersion != null && omVersion.startsWith("1.9")) { + b.insert(spreadsheetlocation, getGlobalSpreadSheets(configurations)); + } else { + b.append(getGlobalSpreadSheets(configurations)); + } + + return b.toString(); + } + + /** + * Get all spreadsheets that are found in the model + * @param configurations + * @return + */ + private static List getSpreadSheets(Collection configurations) { + for(Configuration conf : configurations) { + if(conf.getModel() != null) { + for(IElement e : conf.getElements()) { + if(e instanceof Book) { + return ((Book)e).getSheets(); + } + } + } + } + return Collections.emptyList(); + } + + /** + * + */ + private static String getGlobalSpreadSheets(Collection configurations) { + StringBuilder sheets = new StringBuilder(); + for(Configuration conf : configurations) { + if(conf.getModel() != null) { + for(IElement e : conf.getElements()) { + if(e instanceof Book) { + return ((Book)e).getBook(); + } + } + } + } + + return sheets.toString(); + } + /** - * Define continuous input references - * @param b String builder - * @param inputReferences Input references - */ - private static void continuousInputReferences(StringBuilder b, HashMap inputReferences) { - b.append("// Inputs\n"); - for(Input i : inputReferences.keySet()) { - if(i.getVariability() == null || i.getVariability().isEmpty()) { - // Define only continuous variables here - b.append(" " + i.getModelicaName() + " = " + inputReferences.get(i)); - } - } - } - - /** - * Setup input references for all inputs that are defined in child modules - * - * @param inputReferences Map containing the references - * @param inputDependencies List of input dependencies - */ - private static void setupInputReferences(HashMap inputReferences, ArrayList inputDependencies) { - for(Dependency dependency : inputDependencies) { - Input input = (Input)dependency.getHead(); - Module module = (Module)dependency.getTail(); - Variable reference = (Variable)dependency.refersTo(); - String expression; - // If reference exists, use reference name. Otherwise, use default value. - if(reference != null && reference.getName() != null) - expression = module.getName() + "." + reference.getModelicaName() + ";\n"; - - else - expression = input.getDefaultInputValue() + ";\n"; - - inputReferences.put(input, expression); - } - } - - /** - * Build input definitions - * - * @param b String builder - * @param configuration Module configuration - * @param inputs All inputs of this module - * @param inputReferences - */ - private static void inputDefinitions(StringBuilder b, Configuration configuration, ArrayList inputs, HashMap inputReferences) { - if(inputs.isEmpty()) - return; - - b.append("// Input definitions\n"); - for(Input i : inputs) { - if(i.getVariability() != null && !i.getVariability().isEmpty()) { - // Input is NOT continuous - if(inputReferences.containsKey(i)) { - // Input is defined in a child module - String declaration = i.getDeclaration(); - declaration = declaration.substring(0, declaration.length() - 2); // remove ";\n" from the end - b.append(declaration + " = " + inputReferences.get(i)); - } else { - // Input is not defined in a child module, use default value - b.append(i.getDeclarationWithValue()); - } - } else if(configuration.getModel() != null && !i.isHeadOfDependency()) { - /* - * Input is in the top of the hierarchy, - * and it does not get value from anywhere else. - * => Declare it wit its default value - */ - b.append(i.getDeclarationWithValue()); - } else { - // Continuous => Parent module takes care of declaring a value for the input - b.append(i.getDeclaration()); - } - } - } - - public String escape(String name) { - return name.replace(' ', '_'); - } + * Write a single configuration to a given string builder + * + * @param configuration Model or module configuration + * @param b String builder + */ + private static void writeConfiguration(Configuration configuration, HashSet sheetNames, StringBuilder b) { + boolean defTime = true; + String app; + + // Lists for storing different configuration elements + ArrayList variables = new ArrayList(); + ArrayList inputs = new ArrayList(); + ArrayList modules = new ArrayList(); + ArrayList stocks = new ArrayList(); + ArrayList enumerations = new ArrayList(); + ArrayList inputDependencies = new ArrayList(); + ArrayList outputDependencies = new ArrayList(); + HashMap> moduleInputs = new HashMap>(); + + // Initialize lists + for(IElement element : configuration.getElements()) { + if(element instanceof IndependentVariable) { + // Normal variable + variables.add((IndependentVariable)element); + if(element instanceof Stock) + // Stock + stocks.add((Stock)element); + } else if (element instanceof Module) { + // Module + Module m = (Module)element; + modules.add(m); + moduleInputs.put(m.getName(), new ArrayList()); + for(IElement e : m.getType().getConfiguration().getElements()) + // Inputs inside the module + if(e instanceof Input && !((Input)e).isHeadOfDependency()) { + moduleInputs.get(m.getName()).add((Input)e); + } + } else if (element instanceof Input) { + // Input variables + inputs.add((Input)element); + } else if (element instanceof Enumeration) { + // Enumerations + enumerations.add((Enumeration)element); + } else if (element instanceof Dependency) { + Dependency dependency = (Dependency)element; + if(dependency.getHead() instanceof Module) { + // References given to child modules + outputDependencies.add(dependency); + } else if(dependency.getTail() instanceof Module){ + // References from child modules + inputDependencies.add(dependency); + } + } + } + + // Setup input references. (Input, String reference to another variable) + HashMap inputReferences = new HashMap(); + setupInputReferences(inputReferences, inputDependencies); + + + // If the configuration is model configuration, use model name. Otherwise, use configuration name. + ModuleType mt = configuration.getModuleType(); + + // className == null, if this is a model configuration. model configuration start and end are written in ModelicaWriter.write + String className = mt != null ? (mt.getName().replace(" ", "")) : null; + + if(className != null) + b.append("class "+className+"\n").append(className); + + // Add spreadsheets to all modules and model. Model is "inner" and modules "outer" + String globalStatus = mt != null ? "outer" : "inner"; + for(String sheetName : sheetNames) + b.append(" " + globalStatus + " " + sheetName + "_class" + " "+ sheetName + ";\n"); + + if(!enumerations.isEmpty()) { + b.append("// Enumeration definitions\n"); + for(Enumeration e : enumerations) { + b.append(e.getDeclaration()); + } + } + + b.append("// Variable definitions\n"); + for(IndependentVariable variable : variables) { + app = variable.getDeclaration(); + if (app != null) b.append(app); + } + + if(defTime) { + // Time variable for FMU (game) simulations + if(configuration.isGameConfiguration()) { + if(configuration.getModel() != null) + // Parameter for model root. Values changed in FMU simulator + b.append(" parameter Real time = 0;\n"); + else + // Continuous variable for module instances + b.append(" Real time;\n"); + + } + } + + if(!modules.isEmpty()) { + b.append("// Module definitions\n"); + for(Module m : modules) { + b.append(m.getDeclaration()); + } + } + + + // Input definitions + inputDefinitions(b, configuration, inputs, inputReferences); + + boolean initialEquations = false; + for(Stock stock : stocks) { + app = stock.getInitialEquation(); + if (app != null) { + if(initialEquations == false) { + initialEquations = true; + b.append("// Initial Equations\n"); + b.append("initial equation\n"); + } + b.append(app); + } + } + + boolean equation = false; + b.append("// Equations\n"); + for(IndependentVariable variable : variables) { + app = variable.getEquation(); + if (app != null) { + if(!equation) { + b.append("equation\n"); + equation = true; + } + + b.append(app); + } + } + + // If "equation" has not been added but there are still equations to be defined, add "equation" + if(!equation && (!inputReferences.isEmpty() || !outputDependencies.isEmpty() || + !moduleInputs.isEmpty() || !modules.isEmpty())) + b.append("equation\n"); + + // Continuous input references + continuousInputReferences(b, inputReferences); + + b.append("// Outputs\n"); + for(Dependency dependency : outputDependencies) { + Variable variable = (Variable)dependency.getTail(); + Module module = (Module)dependency.getHead(); + Input reference = (Input)dependency.refersTo(); + if(reference != null && reference.getName() != null && (reference.getVariability() == null || reference.getVariability().isEmpty())) { + b.append(" " + module.getName() + "." + reference.getModelicaName() + " = " + variable.getModelicaName() + ";\n"); + moduleInputs.get(module.getName()).remove(reference); + } + } + + b.append("// Default values for inputs in modules\n"); + for(String moduleLabel : moduleInputs.keySet()) { + for(Input input : moduleInputs.get(moduleLabel)) { + if(input.getVariability() == null || input.getVariability().isEmpty()) + b.append(" " + moduleLabel + "." + input.getModelicaName() + " = " + input.getDefaultInputValue(moduleLabel) + ";\n"); + } + } + + if(defTime) { + if(configuration.isGameConfiguration() && !modules.isEmpty()) { + b.append("// Time values for module\n"); + for(Module m : modules) { + b.append(" " + m.getName() + ".time = time;\n"); + } + } + } + + if(className != null) + b.append("end ").append(className).append(";\n\n"); + + } + + /** + * Define continuous input references + * @param b String builder + * @param inputReferences Input references + */ + private static void continuousInputReferences(StringBuilder b, HashMap inputReferences) { + b.append("// Inputs\n"); + for(Input i : inputReferences.keySet()) { + if(i.getVariability() == null || i.getVariability().isEmpty()) { + // Define only continuous variables here + b.append(" " + i.getModelicaName() + " = " + inputReferences.get(i)); + } + } + } + + /** + * Setup input references for all inputs that are defined in child modules + * + * @param inputReferences Map containing the references + * @param inputDependencies List of input dependencies + */ + private static void setupInputReferences(HashMap inputReferences, ArrayList inputDependencies) { + for(Dependency dependency : inputDependencies) { + Input input = (Input)dependency.getHead(); + Module module = (Module)dependency.getTail(); + Variable reference = (Variable)dependency.refersTo(); + String expression; + // If reference exists, use reference name. Otherwise, use default value. + if(reference != null && reference.getName() != null) + expression = module.getName() + "." + reference.getModelicaName() + ";\n"; + + else + expression = input.getDefaultInputValue() + ";\n"; + + inputReferences.put(input, expression); + } + } + + /** + * Build input definitions + * + * @param b String builder + * @param configuration Module configuration + * @param inputs All inputs of this module + * @param inputReferences + */ + private static void inputDefinitions(StringBuilder b, Configuration configuration, ArrayList inputs, HashMap inputReferences) { + if(inputs.isEmpty()) + return; + + b.append("// Input definitions\n"); + for(Input i : inputs) { + if(i.getVariability() != null && !i.getVariability().isEmpty()) { + // Input is NOT continuous + if(inputReferences.containsKey(i)) { + // Input is defined in a child module + String declaration = i.getDeclaration(); + declaration = declaration.substring(0, declaration.length() - 2); // remove ";\n" from the end + b.append(declaration + " = " + inputReferences.get(i)); + } else { + // Input is not defined in a child module, use default value + b.append(i.getDeclarationWithValue()); + } + } else if(configuration.getModel() != null && !i.isHeadOfDependency()) { + /* + * Input is in the top of the hierarchy, + * and it does not get value from anywhere else. + * => Declare it wit its default value + */ + b.append(i.getDeclarationWithValue()); + } else { + // Continuous => Parent module takes care of declaring a value for the input + b.append(i.getDeclaration()); + } + } + } + + public String escape(String name) { + return name.replace(' ', '_'); + } + + public static final String DELAY_TIME = "delayTime"; + public static final String DELAY_INITIAL = "initialValue"; + + private static String getDelayClass(int order) { + StringBuilder buffer = new StringBuilder(); + + buffer.append("class " + getDelayName(order) + "\n"); + + // variable block + + // (possibly) continuous auxiliary variable + buffer.append("\tReal DL;\n"); + // (possibly) continuous delay time + buffer.append("\tReal " + DELAY_TIME + ";\n"); + // (possibly) continuous initial value + buffer.append("\tReal " + DELAY_INITIAL + ";\n"); + + // first valve + buffer.append("\tReal " + getDelayValve(0) + ";\n"); + + // stocks and valves, valves are delayed values of the variable + for (int i = 1; i <= order; i++) { + buffer.append("\tReal LV"+i + "(fixed=false);\n"); + buffer.append("\tReal "+ getDelayValve(i) + ";\n"); + } + + // initial equation block + buffer.append("initial equation\n"); + + // Each stock gets the same initial value + for (int i = 1; i <= order; i++) { + buffer.append("\tLV"+i + " = DL * " + DELAY_INITIAL + ";\n"); + } + + // equation block + buffer.append("equation\n"); + + buffer.append("\tDL = " + DELAY_TIME + " / "+order+";\n"); + + // valves and stocks + for (int i = 1; i <= order; i++) { + buffer.append("\tder(LV"+i + ") = - " + getDelayValve(i) + " + " + getDelayValve(i-1) + ";\n"); + buffer.append("\t"+ getDelayValve(i) + " = LV"+i + " / DL;\n"); + } + + buffer.append("end "); + buffer.append(getDelayName(order)); + buffer.append(";\n"); + + return buffer.toString(); + } + + public static String getDelayName(int order) { + return "order_"+order+"_delay"; + } + + public static String getDelayValve(int order) { + return "delay"+order; + } } -- 2.47.1