]> gerrit.simantics Code Review - simantics/sysdyn.git/blob
32608c3b7c9c276508efb5c1ed87425fbcccf183
[simantics/sysdyn.git] /
1 /*******************************************************************************\r
2  * Copyright (c) 2010 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.sysdyn.representation.expressions;\r
13 \r
14 import java.io.StringReader;\r
15 import java.util.ArrayList;\r
16 import java.util.Iterator;\r
17 import java.util.Set;\r
18 \r
19 import org.simantics.objmap.annotations.GraphType;\r
20 import org.simantics.objmap.annotations.RelatedValue;\r
21 import org.simantics.sysdyn.expressionParser.ExpressionParser;\r
22 import org.simantics.sysdyn.expressionParser.ParseException;\r
23 import org.simantics.sysdyn.representation.ArrayIndexes;\r
24 import org.simantics.sysdyn.representation.Book;\r
25 import org.simantics.sysdyn.representation.Enumeration;\r
26 import org.simantics.sysdyn.representation.IElement;\r
27 import org.simantics.sysdyn.representation.IndependentVariable;\r
28 import org.simantics.sysdyn.representation.Sheet;\r
29 import org.simantics.sysdyn.representation.Stock;\r
30 import org.simantics.sysdyn.representation.Valve;\r
31 import org.simantics.sysdyn.representation.utils.FormatUtils;\r
32 import org.simantics.sysdyn.representation.utils.IndexUtils;\r
33 \r
34 /**\r
35  * Class representing a stock expression in a variable\r
36  * \r
37  * @author Teemu Lempinen\r
38  *\r
39  */\r
40 @GraphType("http://www.simantics.org/Sysdyn-1.1/StockExpression")\r
41 public class StockExpression extends Expression {\r
42 \r
43     @RelatedValue("http://www.simantics.org/Sysdyn-1.1/HasInitialEquation")\r
44     private String initialEquation;\r
45 \r
46     @Override\r
47     public String getDeclaration(IndependentVariable variable) {\r
48         \r
49         // See if the start parameter is used Stock(start = x, fixed = y) \r
50         String value = null;\r
51         if(useStartValue(variable))\r
52             value = FormatUtils.formatExpressionForModelica(variable, initialEquation);\r
53         \r
54         \r
55         // Build the enumeration indexes, if there are enumerations. Stock[enumIndexes, enum2Indexes, ...]\r
56         ArrayIndexes ai = variable.getArrayIndexes();\r
57         ArrayList<Enumeration> enumerations = null;\r
58         if(ai != null) \r
59                 enumerations = ai.getEnumerations();\r
60         \r
61         String range = "";\r
62         if(enumerations != null && enumerations.size() > 0) {\r
63                 StringBuilder sb = new StringBuilder();\r
64                 sb.append("[");\r
65                 Iterator<Enumeration> iterator = enumerations.iterator();\r
66                 while(iterator.hasNext()) {\r
67                         sb.append(iterator.next().getName() + ".size");\r
68                         if(iterator.hasNext()) {\r
69                                 sb.append(", ");\r
70                         }\r
71                 }\r
72                 sb.append("]");\r
73                 range = sb.toString();\r
74         }\r
75         \r
76         String each = "";\r
77         // each is required when a single value is used for all dimensions e.g. Stock[30](each start = 0)  \r
78         if (value == null) {\r
79             // start parameter is not used, everything needs to be fixed=false\r
80             if(ai != null && !ai.getEnumerations().isEmpty())\r
81                 each = "each";\r
82             return "    " + variable.getType() + " " + variable.getName() + range + "(" + each + " fixed=false);\n";\r
83         } else {\r
84             // start parameter is used\r
85             \r
86             if(getStartValue(variable) != null) {\r
87                 // a single number is used as the initial equation -> (each start, each fixed) if there are dimensions\r
88                 if(ai != null && !ai.getEnumerations().isEmpty())\r
89                     each = "each";\r
90                 return "    " + variable.getType() + " " + variable.getName() + range + "(" + each+ " start=" + value + ", " + each + " fixed=true);\n";\r
91             } else {\r
92                 // a function or array constructor {.., .., ..} is used as the initial equation -> (start, each fixed) if there are dimensions\r
93                 if(ai != null && !ai.getEnumerations().isEmpty())\r
94                     each = "each";\r
95                 return "    " + variable.getType() + " " + variable.getName() + range + "(start=" + value + ", " + each + " fixed=true);\n";\r
96             }\r
97         }\r
98     }\r
99 \r
100     @Override\r
101     public String getEquation(IndependentVariable variable) {\r
102         \r
103         // Build range e.g. Stock[2,3]\r
104         String range = IndexUtils.rangeToIndexes(variable, this.getArrayRange());\r
105         \r
106         // Stock equation is always der(Stock)\r
107         StringBuilder b = new StringBuilder();\r
108         b.append("    der(")\r
109         .append(variable.getName() + range)\r
110         .append(") =");\r
111         \r
112         // Stock equation is formed automatically using incoming and outgoing flows (actually the nearest valves in those flows)\r
113         ArrayList<Valve> incoming = ((Stock)variable).getIncomingValves();\r
114         ArrayList<Valve> outgoing = ((Stock)variable).getOutgoingValves();\r
115         if(incoming.isEmpty() && outgoing.isEmpty()) {\r
116             // No connections, add 0 for each array index if any.\r
117             ArrayIndexes ai = variable.getArrayIndexes();\r
118             ArrayList<Enumeration> enumerations = null;\r
119             if(ai != null) \r
120                 enumerations = ai.getEnumerations();\r
121             \r
122             if(ai == null || enumerations == null || enumerations.isEmpty()) {\r
123                 b.append(" 0.0");\r
124             } else {\r
125                 b.append(" zeros(");\r
126                 for(int i = 0; i < enumerations.size(); i++) {\r
127                     b.append(enumerations.get(i).getEnumerationIndexes().size());\r
128                     if(i != enumerations.size() - 1)\r
129                         b.append(", ");\r
130                 }\r
131                 b.append(")");\r
132             }\r
133 \r
134         } else {\r
135             // incoming valves add and outgoing valves reduce the stock\r
136             for(Valve valve : outgoing)\r
137                 b.append("\n        - ").append(valve.getName() + range);\r
138             for(Valve valve : incoming)\r
139                 b.append("\n        + ").append(valve.getName() + range);\r
140         }\r
141         b.append(";\n");\r
142         return b.toString();\r
143     }\r
144     \r
145     /** \r
146      * Check whether to use fixed=true and start=... in Modelica code\r
147      * @return\r
148      */\r
149     private boolean useStartValue(IndependentVariable variable) {\r
150         // If no variables are used in the equation, start value is used\r
151         \r
152         // First the equation is formatted and parsed\r
153         String equation = FormatUtils.formatExpressionForModelica(variable, initialEquation);\r
154         ExpressionParser parser = new ExpressionParser(new StringReader(equation));\r
155         try {\r
156             parser.expr();\r
157             if(parser.getReferences().isEmpty()) {\r
158                 // if equation did not contain any references, start value is used\r
159                 return true;\r
160             } else {\r
161                 // Check if references are references to sheets. \r
162                 // Sheet references are allowed, since sheets contain only constants\r
163                 boolean found = false;\r
164                 Set<String> references = parser.getReferences().keySet();\r
165                 \r
166                 // Go through each reference\r
167                 for(String reference : references) {\r
168                     // We only need the first element to know that it is a Sheet (SheetName.CellOrRange)\r
169                     reference = reference.split("\\.")[0]; \r
170                     found = false;\r
171                     for(IElement element : variable.getParentConfiguration().getElements()) {\r
172                         if(element instanceof Book) {\r
173                             for(Sheet sheet : ((Book)element).getSheets()) {\r
174                                 if(reference.equals(sheet.getName())) {\r
175                                     found = true;\r
176                                     break;\r
177                                 }\r
178                             }\r
179                         }\r
180                         if(found)\r
181                             break;\r
182                     }\r
183                     \r
184                     // If there was no sheet for this reference name, return false\r
185                     if(!found)\r
186                         return false;\r
187                 }\r
188             }\r
189         } catch (ParseException e) {\r
190         }\r
191         return true;\r
192     }\r
193 \r
194     @Override\r
195     public String getInitialEquation(IndependentVariable variable) {\r
196         // if start value is used, no initial equation is returned\r
197         if(useStartValue(variable))\r
198             return null;\r
199         // format the initial equation for modelica execution \r
200         String equation = FormatUtils.formatExpressionForModelica(variable, initialEquation);\r
201         String range = IndexUtils.rangeToIndexes(variable, this.getArrayRange());\r
202         if(range == null)\r
203                 range = "";\r
204         return "    " + variable.getName() + range + " = " + equation + ";\n";\r
205     }\r
206     \r
207     \r
208     /**\r
209      * Return Double representation of the initial equation for given variable\r
210      * \r
211      * @param variable\r
212      * @return Double representing the initial equation or null if initial equation is not a double\r
213      */\r
214     private Double getStartValue(IndependentVariable variable) {\r
215         Double value = null;\r
216         ArrayList<IExpression> expressions = variable.getExpressions().getExpressions();\r
217         if(expressions.size() == 1) {\r
218                 IExpression e = expressions.get(0);\r
219             if(e.getInitialEquation(variable) == null) {\r
220                 try {\r
221                     value = Double.parseDouble(initialEquation);\r
222                 } catch(NumberFormatException e1) {\r
223                     \r
224                 }\r
225             }\r
226         }\r
227         return value;\r
228     }\r
229 \r
230 \r
231 }\r