-/*******************************************************************************\r
- * Copyright (c) 2007, 2012, 2014 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.sysdyn.modelica;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-\r
-import org.simantics.sysdyn.representation.Book;\r
-import org.simantics.sysdyn.representation.Configuration;\r
-import org.simantics.sysdyn.representation.Dependency;\r
-import org.simantics.sysdyn.representation.Enumeration;\r
-import org.simantics.sysdyn.representation.IElement;\r
-import org.simantics.sysdyn.representation.IndependentVariable;\r
-import org.simantics.sysdyn.representation.Input;\r
-import org.simantics.sysdyn.representation.Module;\r
-import org.simantics.sysdyn.representation.ModuleType;\r
-import org.simantics.sysdyn.representation.Sheet;\r
-import org.simantics.sysdyn.representation.Stock;\r
-import org.simantics.sysdyn.representation.Variable;\r
-import org.simantics.sysdyn.representation.expressions.DelayExpression;\r
-import org.simantics.sysdyn.representation.expressions.IExpression;\r
-\r
-/**\r
- * ModelicaWriter writes Sysdyn model representations (objmap) into Modelica code.\r
- * \r
- * @author Teemu Lempinen\r
- * @author Tuomas Miettinen\r
- *\r
- */\r
-public class ModelicaWriter {\r
- \r
- public static final String VAR_TIME = "time";\r
- public static final String VAR_START = "startTime";\r
- public static final String VAR_STOP = "stopTime";\r
- public static final String VAR_STEP = "timeStep";\r
-\r
- /**\r
- * Write a collection of configurations into a single Modelica code\r
- * @param isGame \r
- * \r
- * @param Configurations Configurations, one main configuration and possible modules\r
- * @return Complete Modelica code of a model\r
- */\r
- public static String write(Collection<Configuration> _configurations, double startTime, double stopTime, double timeStep, boolean isGame, String omVersion) {\r
-\r
- ArrayList<Configuration> configurations = new ArrayList<Configuration>(_configurations);\r
- Collections.sort(configurations, new Comparator<Configuration>() {\r
-\r
- boolean uses(Configuration o1, Configuration o2) {\r
- ModuleType type = o2.getModuleType();\r
- if(type == null) return false;\r
- for(IElement e : o1.getElements()) {\r
- if(e instanceof Module) {\r
- Module m = (Module)e;\r
- if(m.getType().equals(type)) {\r
- return true;\r
- }\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- @Override\r
- public int compare(Configuration o1, Configuration o2) {\r
- if(uses(o1, o2)) return 1;\r
- else if(uses(o2, o1)) return -1;\r
- else return 0;\r
- }\r
-\r
- });\r
-\r
- Configuration modelConf = null;\r
- for(Configuration conf : configurations) {\r
- if(conf.getModel() != null) {\r
- modelConf = conf;\r
- }\r
- }\r
- StringBuilder b = new StringBuilder();\r
-\r
- int spreadsheetlocation = b.length();\r
-\r
- String modelName = modelConf.getLabel().replace(" ", "");\r
- b.append("model " + modelName + "\n");\r
-\r
- // Super class for enumerations\r
- b.append("partial class Enumeration_class\n");\r
- b.append(" parameter Integer size;\n");\r
- b.append(" parameter Integer elements[:];\n");\r
- b.append("end Enumeration_class;\n\n");\r
-\r
- // find out which delays are used in the model and create the\r
- // necessary classes\r
- List<String> generated = new ArrayList<String>();\r
- for (Configuration configuration : configurations) {\r
- for (IElement element : configuration.getElements()) {\r
- if (element instanceof IndependentVariable) {\r
- IndependentVariable variable = (IndependentVariable)element;\r
- for (IExpression expression : variable.getExpressions()) {\r
- if (expression instanceof DelayExpression) {\r
- DelayExpression delay = (DelayExpression)expression;\r
- \r
- int order = delay.getOrder(); \r
- int[] dimensions = null;\r
- \r
- // TODO: is it cool to assume an expression is in the array\r
- // shorthand form if the expression does not have an array range\r
- // even though the variable has array indices? (if this \r
- // assumption does not hold DelayExpression must be updated as well)\r
- \r
- if (expression.getArrayRange() == null) {\r
- dimensions = variable.getDimensionArray();\r
- }\r
- \r
- // create the appropriate delay class if it has not \r
- // been created already\r
- \r
- if (!generated.contains(getDelayName(order, dimensions))) {\r
- b.append(getDelayClass(order, dimensions));\r
- b.append("\n");\r
- generated.add(getDelayName(order, dimensions));\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- HashSet<String> sheetNames = new HashSet<String>();\r
- for(Sheet sheet : getSpreadSheets(configurations))\r
- sheetNames.add(sheet.getModelicaName());\r
- \r
- // write global time parameters (could be moved to writeConfiguration())\r
- b.append("// Simulation parameters\n");\r
- b.append("inner parameter Real ").append(VAR_START).append(" = ").append(startTime).append(";\n");\r
- b.append("inner parameter Real ").append(VAR_STOP).append(" = ").append(stopTime).append(";\n");\r
- b.append("inner parameter Real ").append(VAR_STEP).append(" = ").append(timeStep).append(";\n");\r
- b.append('\n');\r
-\r
- // Write all module configurations to the declarations part (first)\r
- for(Configuration conf : configurations) {\r
- conf.setIsGameConfiguration(isGame);\r
- if(!conf.equals(modelConf))\r
- writeConfiguration(conf, sheetNames, startTime, b);\r
- }\r
-\r
- // Write model configuration last, so that equations-part does not contain module definitions\r
- modelConf.setIsGameConfiguration(isGame);\r
- writeConfiguration(modelConf, sheetNames, startTime, b);\r
-\r
- b.append("end " + modelName + ";\n\n");\r
-\r
- // Insert spreadsheets\r
- if(omVersion != null && omVersion.startsWith("1.9")) {\r
- b.insert(spreadsheetlocation, getGlobalSpreadSheets(configurations));\r
- } else {\r
- b.append(getGlobalSpreadSheets(configurations));\r
- }\r
-\r
- return b.toString();\r
- }\r
-\r
- /**\r
- * Get all spreadsheets that are found in the model\r
- * @param configurations\r
- * @return\r
- */\r
- private static List<Sheet> getSpreadSheets(Collection<Configuration> configurations) {\r
- for(Configuration conf : configurations) {\r
- if(conf.getModel() != null) {\r
- for(IElement e : conf.getElements()) {\r
- if(e instanceof Book) {\r
- return ((Book)e).getSheets();\r
- }\r
- }\r
- }\r
- }\r
- return Collections.emptyList();\r
- }\r
-\r
- /**\r
- * \r
- */\r
- private static String getGlobalSpreadSheets(Collection<Configuration> configurations) {\r
- StringBuilder sheets = new StringBuilder();\r
- for(Configuration conf : configurations) {\r
- if(conf.getModel() != null) {\r
- for(IElement e : conf.getElements()) {\r
- if(e instanceof Book) {\r
- return ((Book)e).getBook();\r
- }\r
- }\r
- }\r
- }\r
-\r
- return sheets.toString();\r
- }\r
-\r
- /**\r
- * Write a single configuration to a given string builder\r
- * \r
- * @param configuration Model or module configuration\r
- * @param b String builder\r
- */\r
- private static void writeConfiguration(Configuration configuration, HashSet<String> sheetNames, double startTime, StringBuilder b) {\r
- boolean defTime = false;\r
- String app;\r
-\r
- // Lists for storing different configuration elements\r
- ArrayList<IndependentVariable> variables = new ArrayList<IndependentVariable>();\r
- ArrayList<Input> inputs = new ArrayList<Input>();\r
- ArrayList<Module> modules = new ArrayList<Module>();\r
- ArrayList<Stock> stocks = new ArrayList<Stock>();\r
- ArrayList<Enumeration> enumerations = new ArrayList<Enumeration>();\r
- ArrayList<Dependency> inputDependencies = new ArrayList<Dependency>();\r
- ArrayList<Dependency> outputDependencies = new ArrayList<Dependency>();\r
- HashMap<String, ArrayList<Input>> moduleInputs = new HashMap<String, ArrayList<Input>>();\r
-\r
- // Initialize lists\r
- for(IElement element : configuration.getElements()) {\r
- if(element instanceof IndependentVariable) {\r
- // Normal variable\r
- variables.add((IndependentVariable)element);\r
- if(element instanceof Stock)\r
- // Stock\r
- stocks.add((Stock)element);\r
- } else if (element instanceof Module) {\r
- // Module\r
- Module m = (Module)element; \r
- modules.add(m);\r
- moduleInputs.put(m.getModelicaName(), new ArrayList<Input>());\r
- for(IElement e : m.getType().getConfiguration().getElements())\r
- // Inputs inside the module\r
- if(e instanceof Input && !((Input)e).isHeadOfDependency()) {\r
- moduleInputs.get(m.getModelicaName()).add((Input)e);\r
- }\r
- } else if (element instanceof Input) {\r
- // Input variables\r
- inputs.add((Input)element);\r
- } else if (element instanceof Enumeration) {\r
- // Enumerations\r
- enumerations.add((Enumeration)element);\r
- } else if (element instanceof Dependency) {\r
- Dependency dependency = (Dependency)element;\r
- if(dependency.getHead() instanceof Module) {\r
- // References given to child modules\r
- outputDependencies.add(dependency);\r
- } else if(dependency.getTail() instanceof Module){\r
- // References from child modules\r
- inputDependencies.add(dependency);\r
- }\r
- }\r
- }\r
-\r
- // Setup input references. (Input, String reference to another variable)\r
- HashMap<Input, String> inputReferences = new HashMap<Input, String>();\r
- setupInputReferences(inputReferences, inputDependencies);\r
-\r
-\r
- // If the configuration is model configuration, use model name. Otherwise, use configuration name.\r
- ModuleType mt = configuration.getModuleType();\r
-\r
- // className == null, if this is a model configuration. model configuration start and end are written in ModelicaWriter.write\r
- String className = mt != null ? (mt.getModelicaName()) : null;\r
-\r
- if(className != null)\r
- b.append("class "+className+"\n");\r
-\r
- // Add spreadsheets to all modules and model. Model is "inner" and modules "outer"\r
- String globalStatus = mt != null ? "outer" : "inner";\r
- for(String sheetName : sheetNames)\r
- b.append(" " + globalStatus + " " + sheetName + "_class" + " "+ sheetName + ";\n");\r
-\r
- if(!enumerations.isEmpty()) {\r
- b.append("// Enumeration definitions\n");\r
- for(Enumeration e : enumerations) {\r
- b.append(e.getDeclaration());\r
- }\r
- }\r
- \r
- if (mt != null) {\r
- b.append("// References to simulation parameters\n");\r
- b.append(" outer Real ").append(VAR_START).append(";\n");\r
- b.append(" outer Real ").append(VAR_STOP).append(";\n");\r
- b.append(" outer Real ").append(VAR_STEP).append(";\n");\r
- }\r
-\r
- b.append("// Variable definitions\n");\r
- for(IndependentVariable variable : variables) {\r
- app = variable.getDeclaration();\r
- if (app != null) {\r
- b.append(app);\r
- }\r
- }\r
-\r
- if(defTime) {\r
- // Time variable for FMU (game) simulations\r
- if(configuration.isGameConfiguration()) {\r
- if(configuration.getModel() != null)\r
- // Parameter for model root. Values changed in FMU simulator\r
- b.append(" parameter Real time = " + startTime + ";\n");\r
- else\r
- // Continuous variable for module instances\r
- b.append(" Real time;\n");\r
-\r
- }\r
- }\r
-\r
- if(!modules.isEmpty()) {\r
- b.append("// Module definitions\n");\r
- for(Module m : modules) {\r
- b.append(m.getDeclaration());\r
- }\r
- }\r
-\r
-\r
- // Input definitions\r
- inputDefinitions(b, configuration, inputs, inputReferences);\r
-\r
- boolean initialEquations = false;\r
- for(Stock stock : stocks) {\r
- app = stock.getInitialEquation();\r
- if (app != null) {\r
- if(initialEquations == false) {\r
- initialEquations = true;\r
- b.append("// Initial Equations\n");\r
- b.append("initial equation\n");\r
- }\r
- b.append(app);\r
- }\r
- }\r
-\r
- boolean equation = false;\r
- b.append("// Equations\n");\r
- for(IndependentVariable variable : variables) {\r
- app = variable.getEquation();\r
- if (app != null) {\r
- if(!equation) {\r
- b.append("equation\n");\r
- equation = true;\r
- }\r
-\r
- b.append(app);\r
- }\r
- }\r
-\r
- // If "equation" has not been added but there are still equations to be defined, add "equation"\r
- if(!equation && (!inputReferences.isEmpty() || !outputDependencies.isEmpty() ||\r
- !moduleInputs.isEmpty() || !modules.isEmpty()))\r
- b.append("equation\n");\r
-\r
- // Continuous input references\r
- continuousInputReferences(b, inputReferences);\r
-\r
- b.append("// Outputs\n");\r
- for(Dependency dependency : outputDependencies) {\r
- Variable variable = (Variable)dependency.getTail();\r
- Module module = (Module)dependency.getHead();\r
- Input reference = (Input)dependency.refersTo();\r
- if(reference != null && reference.getName() != null && (reference.getVariability() == null || reference.getVariability().isEmpty())) {\r
- b.append(" " + module.getModelicaName() + "." + reference.getModelicaName() + " = " + variable.getModelicaName() + ";\n");\r
- moduleInputs.get(module.getModelicaName()).remove(reference);\r
- }\r
- }\r
-\r
- b.append("// Default values for inputs in modules\n");\r
- for(String moduleLabel : moduleInputs.keySet()) {\r
- for(Input input : moduleInputs.get(moduleLabel)) {\r
- if(input.getVariability() == null || input.getVariability().isEmpty())\r
- b.append(" " + moduleLabel + "." + input.getModelicaName() + " = " + input.getDefaultInputValue(moduleLabel) + ";\n");\r
- }\r
- }\r
-\r
- if(defTime) {\r
- if(configuration.isGameConfiguration() && !modules.isEmpty()) {\r
- b.append("// Time values for module\n");\r
- for(Module m : modules) {\r
- b.append(" " + m.getModelicaName() + ".time = time;\n");\r
- }\r
- }\r
- }\r
-\r
- if(className != null)\r
- b.append("end ").append(className).append(";\n\n");\r
-\r
- }\r
-\r
- /**\r
- * Define continuous input references\r
- * @param b String builder\r
- * @param inputReferences Input references\r
- */\r
- private static void continuousInputReferences(StringBuilder b, HashMap<Input, String> inputReferences) {\r
- b.append("// Inputs\n");\r
- for(Input i : inputReferences.keySet()) {\r
- if(i.getVariability() == null || i.getVariability().isEmpty()) {\r
- // Define only continuous variables here\r
- b.append(" " + i.getModelicaName() + " = " + inputReferences.get(i));\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Setup input references for all inputs that are defined in child modules\r
- * \r
- * @param inputReferences Map containing the references\r
- * @param inputDependencies List of input dependencies\r
- */\r
- private static void setupInputReferences(HashMap<Input, String> inputReferences, ArrayList<Dependency> inputDependencies) {\r
- for(Dependency dependency : inputDependencies) {\r
- Input input = (Input)dependency.getHead();\r
- Module module = (Module)dependency.getTail();\r
- Variable reference = (Variable)dependency.refersTo();\r
- String expression;\r
- // If reference exists, use reference name. Otherwise, use default value.\r
- if(reference != null && reference.getName() != null)\r
- expression = module.getModelicaName() + "." + reference.getModelicaName() + ";\n";\r
-\r
- else\r
- expression = input.getDefaultInputValue() + ";\n";\r
-\r
- inputReferences.put(input, expression);\r
- }\r
- }\r
-\r
- /**\r
- * Build input definitions\r
- * \r
- * @param b String builder\r
- * @param configuration Module configuration\r
- * @param inputs All inputs of this module\r
- * @param inputReferences \r
- */\r
- private static void inputDefinitions(StringBuilder b, Configuration configuration, ArrayList<Input> inputs, HashMap<Input, String> inputReferences) {\r
- if(inputs.isEmpty())\r
- return;\r
-\r
- b.append("// Input definitions\n");\r
- for(Input i : inputs) {\r
- if(i.getVariability() != null && !i.getVariability().isEmpty()) {\r
- // Input is NOT continuous\r
- if(inputReferences.containsKey(i)) {\r
- // Input is defined in a child module\r
- String declaration = i.getDeclaration();\r
- declaration = declaration.substring(0, declaration.length() - 2); // remove ";\n" from the end\r
- b.append(declaration + " = " + inputReferences.get(i));\r
- } else {\r
- // Input is not defined in a child module, use default value\r
- b.append(i.getDeclarationWithValue());\r
- }\r
- } else if(configuration.getModel() != null && !i.isHeadOfDependency()) {\r
- /*\r
- * Input is in the top of the hierarchy, \r
- * and it does not get value from anywhere else.\r
- * => Declare it wit its default value \r
- */\r
- b.append(i.getDeclarationWithValue());\r
- } else {\r
- // Continuous => Parent module takes care of declaring a value for the input\r
- b.append(i.getDeclaration());\r
- }\r
- }\r
- }\r
-\r
- public String escape(String name) {\r
- return name.replace(' ', '_');\r
- }\r
- \r
- public static final String DELAY_TIME = "delayTime";\r
- public static final String DELAY_INITIAL = "initialValue";\r
- \r
- private static String getDelayClass(int order, int...dimensions) {\r
- boolean array = dimensions != null && dimensions.length > 0;\r
- \r
- StringBuilder buffer = new StringBuilder();\r
-\r
- buffer.append("class ").append(getDelayName(order, dimensions)).append("\n");\r
-\r
- // variable block\r
-\r
- // (possibly) continuous auxiliary variable\r
- buffer.append('\t')\r
- .append(getReal("DL")).append(";\n");\r
- // (possibly) continuous delay time\r
- buffer.append('\t')\r
- .append(getReal(DELAY_TIME)).append(";\n");\r
- // (possibly) continuous initial value\r
- buffer.append('\t')\r
- .append(getReal(DELAY_INITIAL, dimensions)).append(";\n");\r
-\r
- // first valve\r
- buffer.append('\t')\r
- .append(getReal(getDelayValve(0), dimensions)).append(";\n");\r
-\r
- // stocks and valves, valves are delayed values of the variable\r
- for (int i = 1; i <= order; i++) {\r
- buffer.append('\t')\r
- .append(getReal("LV"+i, dimensions)).append(' ')\r
- .append("(" + (array ? "each " : "") + "fixed=false)").append(";\n");\r
- buffer.append('\t')\r
- .append(getReal(getDelayValve(i), dimensions)).append(";\n");\r
- }\r
-\r
- // initial equation block\r
- buffer.append("initial equation\n");\r
-\r
- // Each stock gets the same initial value\r
- for (int i = 1; i <= order; i++) {\r
- buffer.append('\t')\r
- .append("LV"+i)\r
- .append(" = ")\r
- .append("DL" + (array ? " .* " : " * ") + DELAY_INITIAL)\r
- .append(";\n");\r
- }\r
-\r
- // equation block\r
- buffer.append("equation\n");\r
- \r
- buffer.append('\t')\r
- .append("DL")\r
- .append(" = ")\r
- .append(DELAY_TIME +" / " + order)\r
- .append(";\n");\r
- \r
- // valves and stocks\r
- for (int i = 1; i <= order; i++) {\r
- buffer.append('\t')\r
- .append("der(LV"+i + ")")\r
- .append(" = ")\r
- .append("-" + getDelayValve(i) + (array ? " .+ " : " + ") + getDelayValve(i-1))\r
- .append(";\n");\r
- buffer.append('\t')\r
- .append(getDelayValve(i))\r
- .append(" = ")\r
- .append("LV"+i + (array ? " ./ " : " / ") + "DL")\r
- .append(";\n");\r
- }\r
-\r
- buffer.append("end ").append(getDelayName(order, dimensions)).append(";\n");\r
-\r
- return buffer.toString();\r
- }\r
- \r
- private static String getReal(String name, int...dims) {\r
- StringBuilder buffer = new StringBuilder();\r
- buffer.append("Real ").append(name);\r
- if (dims != null && dims.length > 0) {\r
- buffer.append('[');\r
- for (int i = 0; i < dims.length; i++) {\r
- if (i > 0) buffer.append(',');\r
- buffer.append(dims[i]);\r
- }\r
- buffer.append(']');\r
- }\r
- return buffer.toString();\r
- }\r
- \r
- public static String getDelayName(int order, int...dims) {\r
- StringBuilder buffer = new StringBuilder();\r
- buffer.append("o_").append(order).append('_');\r
- if (dims != null && dims.length > 0) {\r
- buffer.append("d_");\r
- for (int dim : dims) buffer.append(dim).append('_');\r
- }\r
- buffer.append("delay");\r
- return buffer.toString();\r
- }\r
- \r
- public static String getDelayValve(int order) {\r
- return "delay"+order;\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2012, 2014 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.sysdyn.modelica;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import org.simantics.sysdyn.representation.Book;
+import org.simantics.sysdyn.representation.Configuration;
+import org.simantics.sysdyn.representation.Dependency;
+import org.simantics.sysdyn.representation.Enumeration;
+import org.simantics.sysdyn.representation.IElement;
+import org.simantics.sysdyn.representation.IndependentVariable;
+import org.simantics.sysdyn.representation.Input;
+import org.simantics.sysdyn.representation.Module;
+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.
+ *
+ * @author Teemu Lempinen
+ * @author Tuomas Miettinen
+ *
+ */
+public class ModelicaWriter {
+
+ public static final String VAR_TIME = "time";
+ public static final String VAR_START = "startTime";
+ public static final String VAR_STOP = "stopTime";
+ public static final String VAR_STEP = "timeStep";
+
+ /**
+ * 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<Configuration> _configurations, double startTime, double stopTime, double timeStep, boolean isGame, String omVersion) {
+
+ ArrayList<Configuration> configurations = new ArrayList<Configuration>(_configurations);
+ Collections.sort(configurations, new Comparator<Configuration>() {
+
+ 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;
+ }
+
+ });
+
+ 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
+ List<String> generated = new ArrayList<String>();
+ for (Configuration configuration : configurations) {
+ for (IElement element : configuration.getElements()) {
+ if (element instanceof IndependentVariable) {
+ IndependentVariable variable = (IndependentVariable)element;
+ for (IExpression expression : variable.getExpressions()) {
+ if (expression instanceof DelayExpression) {
+ DelayExpression delay = (DelayExpression)expression;
+
+ int order = delay.getOrder();
+ int[] dimensions = null;
+
+ // TODO: is it cool to assume an expression is in the array
+ // shorthand form if the expression does not have an array range
+ // even though the variable has array indices? (if this
+ // assumption does not hold DelayExpression must be updated as well)
+
+ if (expression.getArrayRange() == null) {
+ dimensions = variable.getDimensionArray();
+ }
+
+ // create the appropriate delay class if it has not
+ // been created already
+
+ if (!generated.contains(getDelayName(order, dimensions))) {
+ b.append(getDelayClass(order, dimensions));
+ b.append("\n");
+ generated.add(getDelayName(order, dimensions));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ HashSet<String> sheetNames = new HashSet<String>();
+ for(Sheet sheet : getSpreadSheets(configurations))
+ sheetNames.add(sheet.getModelicaName());
+
+ // write global time parameters (could be moved to writeConfiguration())
+ b.append("// Simulation parameters\n");
+ b.append("inner parameter Real ").append(VAR_START).append(" = ").append(startTime).append(";\n");
+ b.append("inner parameter Real ").append(VAR_STOP).append(" = ").append(stopTime).append(";\n");
+ b.append("inner parameter Real ").append(VAR_STEP).append(" = ").append(timeStep).append(";\n");
+ b.append('\n');
+
+ // Write all module configurations to the declarations part (first)
+ for(Configuration conf : configurations) {
+ conf.setIsGameConfiguration(isGame);
+ if(!conf.equals(modelConf))
+ writeConfiguration(conf, sheetNames, startTime, b);
+ }
+
+ // Write model configuration last, so that equations-part does not contain module definitions
+ modelConf.setIsGameConfiguration(isGame);
+ writeConfiguration(modelConf, sheetNames, startTime, 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<Sheet> getSpreadSheets(Collection<Configuration> 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<Configuration> 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<String> sheetNames, double startTime, StringBuilder b) {
+ boolean defTime = true;
+ String app;
+
+ // Lists for storing different configuration elements
+ ArrayList<IndependentVariable> variables = new ArrayList<IndependentVariable>();
+ ArrayList<Input> inputs = new ArrayList<Input>();
+ ArrayList<Module> modules = new ArrayList<Module>();
+ ArrayList<Stock> stocks = new ArrayList<Stock>();
+ ArrayList<Enumeration> enumerations = new ArrayList<Enumeration>();
+ ArrayList<Dependency> inputDependencies = new ArrayList<Dependency>();
+ ArrayList<Dependency> outputDependencies = new ArrayList<Dependency>();
+ HashMap<String, ArrayList<Input>> moduleInputs = new HashMap<String, ArrayList<Input>>();
+
+ // 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.getModelicaName(), new ArrayList<Input>());
+ for(IElement e : m.getType().getConfiguration().getElements())
+ // Inputs inside the module
+ if(e instanceof Input && !((Input)e).isHeadOfDependency()) {
+ moduleInputs.get(m.getModelicaName()).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<Input, String> inputReferences = new HashMap<Input, String>();
+ 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.getModelicaName()) : null;
+
+ if(className != null)
+ b.append("class "+className+"\n");
+
+ // 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());
+ }
+ }
+
+ if (mt != null) {
+ b.append("// References to simulation parameters\n");
+ b.append(" outer Real ").append(VAR_START).append(";\n");
+ b.append(" outer Real ").append(VAR_STOP).append(";\n");
+ b.append(" outer Real ").append(VAR_STEP).append(";\n");
+ }
+
+ 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 = " + startTime + ";\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.getModelicaName() + "." + reference.getModelicaName() + " = " + variable.getModelicaName() + ";\n");
+ moduleInputs.get(module.getModelicaName()).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.getModelicaName() + ".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<Input, String> 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<Input, String> inputReferences, ArrayList<Dependency> 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.getModelicaName() + "." + 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<Input> inputs, HashMap<Input, String> 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, int...dimensions) {
+ boolean array = dimensions != null && dimensions.length > 0;
+
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append("class ").append(getDelayName(order, dimensions)).append("\n");
+
+ // variable block
+
+ // (possibly) continuous auxiliary variable
+ buffer.append('\t')
+ .append(getReal("DL")).append(";\n");
+ // (possibly) continuous delay time
+ buffer.append('\t')
+ .append(getReal(DELAY_TIME)).append(";\n");
+ // (possibly) continuous initial value
+ buffer.append('\t')
+ .append(getReal(DELAY_INITIAL, dimensions)).append(";\n");
+
+ // first valve
+ buffer.append('\t')
+ .append(getReal(getDelayValve(0), dimensions)).append(";\n");
+
+ // stocks and valves, valves are delayed values of the variable
+ for (int i = 1; i <= order; i++) {
+ buffer.append('\t')
+ .append(getReal("LV"+i, dimensions)).append(' ')
+ .append("(" + (array ? "each " : "") + "fixed=false)").append(";\n");
+ buffer.append('\t')
+ .append(getReal(getDelayValve(i), dimensions)).append(";\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('\t')
+ .append("LV"+i)
+ .append(" = ")
+ .append("DL" + (array ? " .* " : " * ") + DELAY_INITIAL)
+ .append(";\n");
+ }
+
+ // equation block
+ buffer.append("equation\n");
+
+ buffer.append('\t')
+ .append("DL")
+ .append(" = ")
+ .append(DELAY_TIME +" / " + order)
+ .append(";\n");
+
+ // valves and stocks
+ for (int i = 1; i <= order; i++) {
+ buffer.append('\t')
+ .append("der(LV"+i + ")")
+ .append(" = ")
+ .append("-" + getDelayValve(i) + (array ? " .+ " : " + ") + getDelayValve(i-1))
+ .append(";\n");
+ buffer.append('\t')
+ .append(getDelayValve(i))
+ .append(" = ")
+ .append("LV"+i + (array ? " ./ " : " / ") + "DL")
+ .append(";\n");
+ }
+
+ buffer.append("end ").append(getDelayName(order, dimensions)).append(";\n");
+
+ return buffer.toString();
+ }
+
+ private static String getReal(String name, int...dims) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("Real ").append(name);
+ if (dims != null && dims.length > 0) {
+ buffer.append('[');
+ for (int i = 0; i < dims.length; i++) {
+ if (i > 0) buffer.append(',');
+ buffer.append(dims[i]);
+ }
+ buffer.append(']');
+ }
+ return buffer.toString();
+ }
+
+ public static String getDelayName(int order, int...dims) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("o_").append(order).append('_');
+ if (dims != null && dims.length > 0) {
+ buffer.append("d_");
+ for (int dim : dims) buffer.append(dim).append('_');
+ }
+ buffer.append("delay");
+ return buffer.toString();
+ }
+
+ public static String getDelayValve(int order) {
+ return "delay"+order;
+ }
+
+}