]> gerrit.simantics Code Review - simantics/sysdyn.git/commitdiff
Re-enable adding time variable to configuration 02/302/2
authorJussi Koskela <jussi.koskela@semantum.fi>
Wed, 1 Feb 2017 08:33:03 +0000 (10:33 +0200)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Wed, 1 Feb 2017 08:46:59 +0000 (10:46 +0200)
Missing time variable caused failure to run game experiments. Removal of
time variable was committed accidentally.

refs #7006

Change-Id: Ie927f63b016e8cc31708eba1a6305cce90dfc04b

bundles/org.simantics.sysdyn/src/org/simantics/sysdyn/modelica/ModelicaWriter.java

index 31e679addfda925a941cf1712d853e62900c5a4f..97d8494397443b7735ff236d0f74c10d8db8c779 100644 (file)
-/*******************************************************************************\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;
+       }
+
+}