From: villberg Date: Mon, 17 Mar 2014 14:00:56 +0000 (+0000) Subject: Initial implementation. Manages to simulate all test models in rootFiles. X-Git-Tag: 1.8.1~119 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=c7192f0c8e801d71c0b7ff26418f18068779fe4a;p=simantics%2Fsysdyn.git Initial implementation. Manages to simulate all test models in rootFiles. refs #4765 git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@29140 ac1ea38d-2e2b-0410-8846-a27921b304fc --- diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Addition.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Addition.java index abf31cb4..5e303397 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Addition.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Addition.java @@ -31,5 +31,10 @@ public class Addition implements IExpression { Double d2 = (Double)exp2.evaluate(environment); return d1 + d2; } + + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new Addition(exp1.withBase(frame, prefix), exp2.withBase(frame, prefix)); + } } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/And.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/And.java index 0ed114b9..18f3f158 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/And.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/And.java @@ -36,5 +36,14 @@ public class And implements IExpression { for(IExpression e : exps) if(!(Boolean)e.evaluate(environment)) return false; return true; - } + } + + @Override + public IExpression withBase(IFrame frame, String prefix) { + ArrayList ne = new ArrayList(); + for(IExpression e : exps) ne.add(e.withBase(frame, prefix)); + return new And(ne); + } + + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Application.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Application.java index 4160663c..a8089c41 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Application.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Application.java @@ -30,4 +30,9 @@ public class Application implements IExpression { return environment.getSystem().evaluateFunction(environment, name, args); } + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new Application(name, args.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Argument.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Argument.java index e5258945..cbe534d0 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Argument.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Argument.java @@ -30,4 +30,9 @@ public class Argument implements IExpression { throw new UnsupportedOperationException(); } + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new Argument(name, modification.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ArgumentList.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ArgumentList.java index 9766f084..f2e637ad 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ArgumentList.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ArgumentList.java @@ -15,13 +15,21 @@ import java.util.ArrayList; public class ArgumentList { public ArrayList args; + public String op; public ArgumentList(ArrayList args) { this.args = args; + this.op = ""; } - public ArgumentList() { + public ArgumentList(ArrayList args, String op) { + this.args = args; + this.op = op; + } + + public ArgumentList(String op) { this.args = new ArrayList(); + this.op = op; } @Override @@ -37,4 +45,10 @@ public class ArgumentList { return b.toString(); } + public ArgumentList withBase(IFrame frame, String prefix) { + ArrayList a2 = new ArrayList(); + for(Argument a : args) a2.add((Argument)a.withBase(frame, prefix)); + return new ArgumentList(a2, op); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Array.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Array.java index eeb952b8..e6b9a455 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Array.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Array.java @@ -74,4 +74,22 @@ public class Array implements IExpression { for(int i=0;i lae = la.elements(); + Collection rae = ra.elements(); + if(lae.size() != rae.size()) throw new UnsupportedOperationException(); + Iterator li = lae.iterator(); + Iterator ri = rae.iterator(); + Array result = new Array(); + for(int i=0;i named = new HashMap(); + public Model model; public final double step; public double time; public boolean initial = true; + public int size; public Object[] valueTable; @@ -64,7 +73,7 @@ public class Environment implements IEnvironment, ISystem { this.step = step; this.time = start; - model.functions.put("size", new Fn1() { + model.functions.put("size", new Fn1(2) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -74,7 +83,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("zidz", new Fn1() { + model.functions.put("zidz", new Fn1(2) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -85,7 +94,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("xidz", new Fn1() { + model.functions.put("xidz", new Fn1(3) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -97,7 +106,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("availabilityExternal", new Fn1() { + model.functions.put("availabilityExternal", new Fn1(4) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -113,7 +122,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("pre", new Fn1() { + model.functions.put("pre", new Fn1(1) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -123,7 +132,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("fill", new Fn1() { + model.functions.put("fill", new Fn1(3) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -150,7 +159,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("randomNumber", new Fn1() { + model.functions.put("randomNumber", new Fn1(2) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -160,7 +169,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("initial", new Fn1() { + model.functions.put("initial", new Fn1(0) { @Override public Object evaluate(IEnvironment _environment, int argc) { @@ -168,7 +177,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("noEvent", new Fn1() { + model.functions.put("noEvent", new Fn1(1) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -178,7 +187,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("min", new Fn1() { + model.functions.put("min", new Fn1(5) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -191,7 +200,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("max", new Fn1() { + model.functions.put("max", new Fn1(5) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -204,7 +213,7 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("integer", new Fn1() { + model.functions.put("integer", new Fn1(1) { @Override public Object evaluate(IEnvironment environment, int argc) { @@ -215,7 +224,20 @@ public class Environment implements IEnvironment, ISystem { } }); - model.functions.put("delay", new Fn1() { + model.functions.put("sum", new Fn1(1) { + + @Override + public Object evaluate(IEnvironment environment, int argc) { + Object value = environment.getValue(0); + Array arr = (Array)value; + double res = 0; + for(Object o : arr.elements()) + res += (Double)o; + return res; + } + + }); + model.functions.put("delay", new Fn1(4) { @Override public Object evaluate(IEnvironment _environment, int argc) { @@ -260,9 +282,17 @@ public class Environment implements IEnvironment, ISystem { valueTable[key] = value; } + public void put(String key, Object value) { + named.put(key, value); + } + + public void setSize(int size) { + this.size = size; + } + @Override public int offset() { - return model.names.size(); + return size; } @Override @@ -283,30 +313,67 @@ public class Environment implements IEnvironment, ISystem { public double time() { return time; } + + double[] valueArray; + public String[] getValueKeyArray() { + + ArrayList keys = new ArrayList(); + + for (int i = 0; i < model.assignmentArray.length; i++) { + Variable v = model.assignmentArray[i].target; + keys.add(v.toString()); + } + + for (int i = 0; i < model.derivativeArray.length; i++) { + Variable v = model.derivativeArray[i].target; + keys.add(v.toString()); + } + + // NOTE: there is room for optimization as parameter values that do not + // change should only be obtained once (and parameter values that do + // change are (possibly) included in assignments or derivatives) + for(int i = 0; i < model.parameterArray.length; i++) { + Variable v = model.parameterArray[i].variable; + keys.add(v.toString()); + } + + valueArray = new double[keys.size()]; + + return keys.toArray(new String[keys.size()]); + + } + // TODO: this is probably not smart at all, figure out a better way to obtain results - public HashMap getValueMap() { - HashMap values = new HashMap(); + public double[] getValueArray() { + + int offset = 0; for (int i = 0; i < model.assignmentArray.length; i++) { Variable v = model.assignmentArray[i].target; - values.put(v.toString(), (Double)getValue(v.index(this))); + valueArray[offset++] = (Double)getValue(v.index(this, null)); } for (int i = 0; i < model.derivativeArray.length; i++) { Variable v = model.derivativeArray[i].target; - values.put(v.toString(), (Double)getValue(v.index(this))); + valueArray[offset++] = (Double)getValue(v.index(this, null)); } // NOTE: there is room for optimization as parameter values that do not // change should only be obtained once (and parameter values that do // change are (possibly) included in assignments or derivatives) - for (ParameterDeclaration p : model.parameters) { - Variable v = p.variable; - values.put(v.toString(), (Double)getValue(v.index(this))); + for(int i = 0; i < model.parameterArray.length; i++) { + Variable v = model.parameterArray[i].variable; + valueArray[offset++] = (Double)getValue(v.index(this, null)); } - return values; + return valueArray; + + } + + @Override + public Object getNamedValue(String key) { + return named.get(key); } } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Equals.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Equals.java index a27a8136..33e5d461 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Equals.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Equals.java @@ -30,5 +30,9 @@ public class Equals implements IExpression { return ((Double)exp1.evaluate(environment)) == ((Double)exp2.evaluate(environment)); } - + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new Equals(exp1.withBase(frame, prefix), exp2.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ForArray.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ForArray.java new file mode 100644 index 00000000..550d7ce9 --- /dev/null +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ForArray.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2013 Semantum Oy. + * 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package fi.semantum.sysdyn.solver; + +import java.util.ArrayList; +import java.util.Collection; + +public class ForArray implements IExpression { + + private ArrayList elements = new ArrayList(); + + public ForArray() { + + } + + public void addElement(Object element) { + if(element instanceof Constant) addElement(((Constant)element).value); + else elements.add(element); + } + + public void setElement(int index, Object element) { + elements.set(index, element); + } + + @Override + public Object evaluate(IEnvironment environment) { + return evaluated(environment); + } + + public Array evaluated(IEnvironment environment) { + + Array result = new Array(); + + IExpression exp = (IExpression)elements.get(0); + Argument arg = (Argument)elements.get(1); + + Array indices = (Array)arg.modification.evaluate(environment); + for(Object o : indices.elements()) { + environment.put(arg.name, o); +// Frame f = new Frame(environment, 1); +// f.put(arg.name, o); + result.addElement(exp.evaluate(environment)); + } + + return result; + + } + + @Override + public String toString() { + return elements.toString(); + } + + public int size(int col) { + return elements.size(); + } + + public Object element(int index) { + return elements.get(index); + } + + public Collection elements() { + return elements; + } + + public void ensureIndex(int index, boolean subArray) { + int needed = (index+1-elements.size()); + for(int i=0;i named; public Frame(IEnvironment parent, int offset) { this.parent = parent; @@ -29,11 +33,22 @@ public class Frame implements IEnvironment { public void put(int index, Object value) { parent.put(parent.offset() + index, value); } + + public void put(String key, Object value) { + if(named == null) named = new HashMap(); + named.put(key, value); + } @Override public Object getValue(int index) { return parent.getValue(parent.offset() + index); } + + @Override + public Object getNamedValue(String key) { + if(named == null) return null; + return named.get(key); + } @Override public ISystem getSystem() { diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Function.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Function.java index 925a192f..6b76f5b2 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Function.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Function.java @@ -15,7 +15,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -public class Function implements Fn, IFrame { +final public class Function implements Fn, IFrame { public static final boolean PRINT = false; @@ -106,4 +106,10 @@ public class Function implements Fn, IFrame { return parameters; } + @Override + public Object evaluateInput(IEnvironment environment, int argPosition) { + VariableDeclaration decl = inputs.get(argPosition); + return decl.modification.args.get(0).modification.evaluate(environment); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/GreaterOrEqualThan.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/GreaterOrEqualThan.java index 3f9fcf56..7b4d396d 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/GreaterOrEqualThan.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/GreaterOrEqualThan.java @@ -30,5 +30,9 @@ public class GreaterOrEqualThan implements IExpression { return ((Double)exp1.evaluate(environment)) >= ((Double)exp2.evaluate(environment)); } + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new GreaterOrEqualThan(exp1.withBase(frame, prefix), exp2.withBase(frame, prefix)); + } } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/GreaterThan.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/GreaterThan.java index 19554716..9f3244ea 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/GreaterThan.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/GreaterThan.java @@ -30,5 +30,9 @@ public class GreaterThan implements IExpression { return ((Double)exp1.evaluate(environment)) > ((Double)exp2.evaluate(environment)); } - + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new GreaterThan(exp1.withBase(frame, prefix), exp2.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IEnvironment.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IEnvironment.java index 71b6b738..036c1e9f 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IEnvironment.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IEnvironment.java @@ -13,8 +13,9 @@ package fi.semantum.sysdyn.solver; public interface IEnvironment { // Object getValue(String key); + Object getNamedValue(String key); Object getValue(int index); -// void put(String key, Object value); + void put(String key, Object value); void put(int index, Object value); // Object evaluateFunction(IEnvironment parent, String name, ArgumentList args); diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IExpression.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IExpression.java index e03d07ca..942b7a3a 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IExpression.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IExpression.java @@ -13,5 +13,6 @@ package fi.semantum.sysdyn.solver; public interface IExpression { public Object evaluate(IEnvironment environment); + public IExpression withBase(IFrame frame, String prefix); } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IfThenElse.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IfThenElse.java index 72aafc65..ba4bd801 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IfThenElse.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/IfThenElse.java @@ -36,4 +36,9 @@ public class IfThenElse implements IExpression { } } + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new IfThenElse(exp.withBase(frame, prefix), t.withBase(frame, prefix), e.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LessOrEqualThan.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LessOrEqualThan.java index 33295cd1..a1fbc884 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LessOrEqualThan.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LessOrEqualThan.java @@ -32,5 +32,9 @@ public class LessOrEqualThan implements IExpression { return d1 <= d2; } + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new LessOrEqualThan(exp1.withBase(frame, prefix), exp2.withBase(frame, prefix)); + } } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LessThan.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LessThan.java index 6a3aea88..4684ab52 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LessThan.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LessThan.java @@ -32,4 +32,9 @@ public class LessThan implements IExpression { return ((Double)left) < ((Double)right); } + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new LessThan(exp1.withBase(frame, prefix), exp2.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LineReader.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LineReader.java index 1dc522fd..81ecabc4 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LineReader.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/LineReader.java @@ -12,6 +12,7 @@ package fi.semantum.sysdyn.solver; import java.io.StringReader; +import fi.semantum.sysdyn.solver.Model.Globals; import fi.semantum.sysdyn.solver.parser.ModelParser; import fi.semantum.sysdyn.solver.parser.Node; import fi.semantum.sysdyn.solver.parser.SimpleNode; @@ -38,7 +39,7 @@ public class LineReader { public LineReader(String input, NodeCache cache) { chars = input.toCharArray(); this.cache = cache; - model = new Model(); + model = new Model(new Globals()); parser = new Parser(); } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Model.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Model.java index 54580e00..11dd7247 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Model.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Model.java @@ -17,6 +17,10 @@ import java.util.Map; class Model implements IFrame { + static class Globals { + public Map classes = new HashMap(); + } + final public static boolean PRINT = false; public boolean initial = false; @@ -29,6 +33,13 @@ class Model implements IFrame { public Assignment[] assignmentArray; public Assignment[] derivativeArray; + public ParameterDeclaration[] parameterArray; + + public final Globals globals; + + public Model(Globals globals) { + this.globals = globals; + } public Fn getFunction(String name) { return functions.get(name); @@ -86,10 +97,15 @@ class Model implements IFrame { Variable[] parameters = fn.parameters(); - for(int i=0;i a : functions.entrySet()) { + System.err.println("-"+a.getKey() + " " + a.getValue()); + } + + } + } \ No newline at end of file diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Multiplication.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Multiplication.java index 8721c27d..cb41f9dd 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Multiplication.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Multiplication.java @@ -26,7 +26,15 @@ public class Multiplication implements IExpression { } private Array arrayMul(Array a, Double d) { - return a; + Array result = new Array(); + for(Object o : a.elements()) { + if(o instanceof Double) { + result.addElement((Double)o*d); + } else { + throw new IllegalStateException(); + } + } + return result; } @Override @@ -38,4 +46,10 @@ public class Multiplication implements IExpression { else if (left instanceof Double && right instanceof Array) return arrayMul((Array)right, (Double)left); else throw new UnsupportedOperationException(); } + + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new Multiplication(exp1.withBase(frame, prefix), exp2.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Negation.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Negation.java index d20354dd..e810c278 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Negation.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Negation.java @@ -28,4 +28,9 @@ public class Negation implements IExpression { return -((Double)exp.evaluate(environment)); } + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new Negation(exp.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/NodeClass.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/NodeClass.java index 2d8c909a..9777c7d8 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/NodeClass.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/NodeClass.java @@ -22,6 +22,7 @@ public enum NodeClass { while_statement, statement, name, + element, element_list, element_modification, function_arguments, diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/NotEquals.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/NotEquals.java index e5bee880..f6056732 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/NotEquals.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/NotEquals.java @@ -30,5 +30,9 @@ public class NotEquals implements IExpression { return ((Double)exp1.evaluate(environment)) != ((Double)exp2.evaluate(environment)); } - + @Override + public IExpression withBase(IFrame frame, String prefix) { + return new NotEquals(exp1.withBase(frame, prefix), exp2.withBase(frame, prefix)); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Or.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Or.java index 1e72912a..cdc907e0 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Or.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Or.java @@ -36,5 +36,13 @@ public class Or implements IExpression { for(IExpression e : exps) if((Boolean)e.evaluate(environment)) return true; return false; - } + } + + @Override + public IExpression withBase(IFrame frame, String prefix) { + ArrayList ne = new ArrayList(); + for(IExpression e : exps) ne.add(e.withBase(frame, prefix)); + return new Or(ne); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ParameterDeclaration.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ParameterDeclaration.java index 1c3b0457..b3eca902 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ParameterDeclaration.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/ParameterDeclaration.java @@ -32,4 +32,9 @@ public class ParameterDeclaration implements IExpression { throw new UnsupportedOperationException(); } + @Override + public IExpression withBase(IFrame frame, String prefix) { + throw new UnsupportedOperationException(); + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Parser.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Parser.java index 0b1f5aad..195e19d6 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Parser.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Parser.java @@ -11,10 +11,16 @@ *******************************************************************************/ package fi.semantum.sysdyn.solver; +import java.io.File; +import java.io.StringReader; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import org.simantics.utils.FileUtils; + +import fi.semantum.sysdyn.solver.Model.Globals; +import fi.semantum.sysdyn.solver.parser.ModelParser; import fi.semantum.sysdyn.solver.parser.SimpleNode; public class Parser { @@ -55,11 +61,12 @@ public class Parser { IExpression exp = (IExpression)walk((SimpleNode)n.jjtGetChild(1), indent+2, model); if(v instanceof Derivative) { Derivative der = (Derivative)v; - Assignment eq = new Assignment(der.variable,new Derivate(der.variable, exp)); + Assignment eq = new Assignment(der.variable, der.variable.subscripts, new Derivate(der.variable, der.variable.subscripts, exp)); model.derivatives.add(eq); return eq; } else { - Assignment eq = new Assignment((Variable)v,exp); + Variable var = (Variable)v; + Assignment eq = new Assignment(var, var.subscripts, exp); if(model.initial) model.initials.add(eq); else @@ -69,10 +76,16 @@ public class Parser { case equation_section: if("initial".equals(n.op)) { model.initial = true; + for(int i=0;i comps = new ArrayList(); @@ -81,7 +94,10 @@ public class Parser { } return comps; case for_index: - return new ForIndex((Function)currentFrame, n.op, (IExpression)walk((SimpleNode)n.jjtGetChild(0),indent+2, model)); + if(currentFrame instanceof Function) + return new ForIndex((Function)currentFrame, n.op, (IExpression)walk((SimpleNode)n.jjtGetChild(0),indent+2, model)); + else + return new ForIndex(null, n.op, (IExpression)walk((SimpleNode)n.jjtGetChild(0),indent+2, model)); case for_indices: ArrayList indices = new ArrayList(); for(int i=0;i())); - if(currentFrame == model) model.variables.add(vd); + } else if (decl.modification instanceof IExpression) { + ArrayList as = new ArrayList(); + as.add(new Argument("", (IExpression)decl.modification)); + ArgumentList al = new ArgumentList(as); + VariableDeclaration vd = new VariableDeclaration(decl.variable, type_prefix, type_specifier, al); + model.variables.add(vd); clauses.add(vd); + } else { + if("Real".equals(type_specifier)) { + VariableDeclaration vd = new VariableDeclaration(decl.variable, type_prefix, type_specifier, new ArgumentList(new ArrayList())); + model.variables.add(vd); + clauses.add(vd); + } else if("Integer".equals(type_specifier)) { + VariableDeclaration vd = new VariableDeclaration(decl.variable, type_prefix, type_specifier, new ArgumentList(new ArrayList())); + model.variables.add(vd); + clauses.add(vd); + } else if("Boolean".equals(type_specifier)) { + VariableDeclaration vd = new VariableDeclaration(decl.variable, type_prefix, type_specifier, new ArgumentList(new ArrayList())); + model.variables.add(vd); + clauses.add(vd); + } else { + Model clazz = model.globals.classes.get(type_specifier); + for(VariableDeclaration vd : clazz.variables) { + String base = decl.variable.base.name + "."; + Variable var2 = vd.variable.withBase(currentFrame, base); + VariableDeclaration vd2 = new VariableDeclaration(var2, vd.direction, vd.type, vd.modification.withBase(currentFrame, base)); + model.variables.add(vd2); + clauses.add(vd2); + } + for(ParameterDeclaration pd : clazz.parameters) { + String base = decl.variable.base.name + "."; + Variable var2 = pd.variable.withBase(currentFrame, base); + ParameterDeclaration pd2 = new ParameterDeclaration(var2, pd.modification.withBase(currentFrame, base)); + model.parameters.add(pd2); + } + for(Assignment ass : clazz.assignments) { + Assignment ass2 = ass.withBase(currentFrame, decl.variable.base.name + "."); + model.assignments.add(ass2); + } + for(Assignment ass : clazz.initials) { + Assignment ass2 = ass.withBase(currentFrame, decl.variable.base.name + "."); + model.initials.add(ass2); + } + for(Assignment ass : clazz.derivatives) { + Assignment ass2 = ass.withBase(currentFrame, decl.variable.base.name + "."); + model.derivatives.add(ass2); + } + + } } } } @@ -261,15 +338,46 @@ public class Parser { model.functions.put(functionName, function); currentFrame = model; return function; + + } else if("model".equals(n.op)) { + + currentFrame = model; + + if(n.jjtGetNumChildren() == 1) { + return walk((SimpleNode)n.jjtGetChild(0), indent+2, model); + } else { + System.err.println("undefined children for class_definition model"); + } + return null; + + } else if("class".equals(n.op)) { + + Model clazz = new Model(model.globals); + SimpleNode specifier = (SimpleNode)n.jjtGetChild(0); + model.globals.classes.put(specifier.op, clazz); + currentFrame = clazz; + walk(specifier, indent+2, clazz); + currentFrame = model; + return null; + + } else { + System.err.println("class_definition " + n.op); } break; case array: - Array array = new Array(); if(n.jjtGetNumChildren() == 1) { ArgumentList al = (ArgumentList)walk((SimpleNode)n.jjtGetChild(0), indent+2, model); - for(Argument arg : al.args) array.addElement(arg.modification); + if("for".equals(al.op)) { + ForArray array = new ForArray(); + array.addElement(al.args.get(0).modification); + array.addElement(al.args.get(1)); + return array; + } else { + Array array = new Array(); + for(Argument arg : al.args) array.addElement(arg.modification); + return array; + } } - return array; case primary: if(n.op != null) { return Utils.parsePrimitive(n.op); @@ -338,22 +446,31 @@ public class Parser { return walk((SimpleNode)n.jjtGetChild(0), indent+2, model); } case arithmetic_expression: - if(n.jjtGetNumChildren() == 2) { + { + int i=0; + IExpression left; + if(n.jjtGetNumChildren() % 2 == 0) { String op = ((String)walk((SimpleNode)n.jjtGetChild(0), indent+2, model)).trim(); - if("-".equals(op)) return new Negation((IExpression)walk((SimpleNode)n.jjtGetChild(1), indent+2, model)); - } - if(n.jjtGetNumChildren() > 1) { - IExpression left = (IExpression)walk((SimpleNode)n.jjtGetChild(0), indent+2, model); - for(int i=1;i 1) { IExpression term = (IExpression)walk((SimpleNode)n.jjtGetChild(0), indent+2, model); @@ -361,6 +478,7 @@ public class Parser { String op = ((String)walk((SimpleNode)n.jjtGetChild(i), indent+2, model)).trim(); IExpression exp2 = (IExpression)walk((SimpleNode)n.jjtGetChild(i+1), indent+2, model); if("*".equals(op)) term = new Multiplication(term, exp2); + else if(".*".equals(op)) term = new ElementwiseProduct(term, exp2); else if("/".equals(op)) term = new Division(term, exp2); } return term; @@ -387,7 +505,7 @@ public class Parser { return new Derivative((Variable)args2.args.get(0).modification); } } else if("initial".equals(n.op)) { - return new Application("initial", new ArgumentList()); + return new Application("initial", new ArgumentList("")); } else if("application".equals(n.op)) { String name = (String)walk((SimpleNode)n.jjtGetChild(0), indent+2, model); ArgumentList args2 = (ArgumentList)walk((SimpleNode)n.jjtGetChild(1), indent+2, model); @@ -395,8 +513,98 @@ public class Parser { } } + System.err.println("fall-through " + n); + // should not get this far return null; } + public static final boolean WAIT = false; + + public static void main(String[] args) throws Exception { + + if(WAIT) { + for(int i=0;i<1e12;i++) { + int k = System.in.read(); + if(k==13) break; + Thread.sleep(10); + } + } + +// NodeCache cache = new NodeCache(); +// +//// long start = System.nanoTime(); +// +// File f = new File("C:/Users/Antti Villberg/Downloads/isomalli.txt"); +// String contents = FileUtils.getContents(f); +// LineReader reader = new LineReader(contents, cache); +// reader.parse(); +// +//// long duration = System.nanoTime()-start; +//// System.err.println("complete parse file in " + 1e-9*duration + "s."); +// +//// start = System.nanoTime(); +// +// if(WAIT) { +// for(int i=0;i<1e12;i++) { +// int k = System.in.read(); +// if(k==13) break; +// Thread.sleep(10); +// } +// } + + + + + File f1 = new File("d:/sysdynfn.txt"); + String contents1 = FileUtils.getContents(f1); + File f2 = new File("d:/sysdyntest.txt"); + String contents2 = contents1 + FileUtils.getContents(f2); + + StringReader reader = new StringReader(contents2); + ModelParser modelParser = new ModelParser(reader); + SimpleNode n = (SimpleNode)modelParser.stored_definition(); + System.err.println("n: " +n ); + Parser parser = new Parser(); + Model m = new Model(new Globals()); + parser.walk(n, 0, m); + + m.prettyPrint(); + +// ModelicaCompilationUnit unit = ModelicaAnalyzer.analyze(contents2); +// System.err.println("unit: " + unit); + + +// reader = new LineReader(contents2, cache); +// reader.parse(); +// Model model = reader.model; + +// String code = FileUtils.getContents(f); +// Model model = new Parser().parseModelica(code); + +// duration = System.nanoTime()-start; +// System.err.println("parsed file in " + 1e-9*duration + "s."); + + Solver solver = new Solver(); + solver.prepare(contents2); + +// +// if(WAIT) { +// for(int i=0;i<1e12;i++) { +// int k = System.in.read(); +// if(k==13) break; +// Thread.sleep(10); +// } +// } +// +// long start = System.nanoTime(); + for(int i=0;i<50;i++) { + solver.step(); + solver.printEnvironment(); + } +// long duration = System.nanoTime()-start; +// System.err.println("stepped simulation in " + 1e-9*duration + "s."); + + } + } diff --git a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Solver.java b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Solver.java index 283db970..5db6b334 100644 --- a/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Solver.java +++ b/fi.semantum.sysdyn.solver/src/fi/semantum/sysdyn/solver/Solver.java @@ -11,8 +11,12 @@ *******************************************************************************/ package fi.semantum.sysdyn.solver; +import java.io.StringReader; import java.util.ArrayList; -import java.util.HashMap; + +import fi.semantum.sysdyn.solver.Model.Globals; +import fi.semantum.sysdyn.solver.parser.ModelParser; +import fi.semantum.sysdyn.solver.parser.SimpleNode; public class Solver { @@ -48,13 +52,25 @@ public class Solver { } public void prepare(String input) throws Exception { - LineReader reader = new LineReader(input, cache); - reader.parse(); - model = reader.model; + long startNanos = System.nanoTime(); + +// LineReader reader = new LineReader(input, cache); +// reader.parse(); +// +// model = reader.model; + + StringReader reader = new StringReader(input); + ModelParser modelParser = new ModelParser(reader); + SimpleNode n = (SimpleNode)modelParser.stored_definition(); + Parser parser = new Parser(); + model = new Model(new Globals()); + parser.walk(n, 0, model); + env = new Environment(model, step, start); int size = model.prepare(); + env.setSize(size); for(Fn fn : model.functions.values()) { if(fn instanceof Function) @@ -65,6 +81,7 @@ public class Solver { model.assignmentArray = model.assignments.toArray(new Assignment[model.assignments.size()]); model.derivativeArray = model.derivatives.toArray(new Assignment[model.derivatives.size()]); + model.parameterArray = model.parameters.toArray(new ParameterDeclaration[model.parameters.size()]); newValues = new Object[Math.max(model.assignments.size(),model.derivatives.size())]; @@ -77,7 +94,7 @@ public class Solver { for(ParameterDeclaration pd : model.parameters) { try { if(!pd.assigned) { - pd.variable.assign(env, pd.modification.evaluate(env)); + pd.variable.assign(env, null, pd.modification.evaluate(env)); pd.assigned = true; } } catch (Exception e) { @@ -99,7 +116,7 @@ public class Solver { for(Argument arg : vd.modification.args) { if("start".equals(arg.name)) { Object value = arg.modification.evaluate(env); - vd.variable.assign(env, value); + vd.variable.assign(env, null, value); // make sure the variable is not initialized // twice, this is probably not the most // efficient way @@ -125,7 +142,7 @@ public class Solver { try { if(!ass.assigned) { Object value = ass.expression.evaluate(env); - ass.target.assign(env, value); + ass.target.assign(env, ass.subscripts, value); ass.assigned = true; } } catch (Exception e) { @@ -141,10 +158,19 @@ public class Solver { env.initial = false; ready = true; + + long endNanos = System.nanoTime(); + + System.err.println("Prepared model in " + 1e-6*(endNanos-startNanos) + "ms."); + + } + + public String[] keys() { + return env.getValueKeyArray(); } - public HashMap values() { - return env.getValueMap(); + public double[] values() { + return env.getValueArray(); } public void step() { @@ -162,7 +188,7 @@ public class Solver { newValues[i] = assignments[i].expression.evaluate(env); } for(int i=0;i 1) { + Array array = new Array(); + intoArray(environment, base.index, 0, array); + return array; + } + + Object result = environment.getNamedValue(base.name); + if(result == null) result = environment.getValue(index(environment, subscripts)); if(result == null) throw new UnassignedVariableException("No value for " + base.name); + return result; } } - private boolean hasScalarSubscript() { + private boolean hasScalarSubscript(IExpression[] subscripts) { if(subscripts == null) return false; else return true; } + public Variable withBase(IFrame frame, String prefix) { + if(subscripts != null) { + IExpression[] subscripts2 = new IExpression[subscripts.length]; + for(int i=0;i 0) + workPerExperiment = 10000 / experiments.length; + else + workPerExperiment = 10000; + for(IExperiment e : experiments) + if(e.getState() != ExperimentState.DISPOSED) + e.shutdown(shutdownMon.newChild(workPerExperiment)); + mon.setWorkRemaining(90000); + + final Semaphore activated = new Semaphore(0); + final DataContainer problem = new DataContainer(); + final DataContainer run = new DataContainer(); + manager.startExperiment(experimentResource, new IExperimentActivationListener() { + + @Override + public void onExperimentActivated(final IExperiment experiment) { +// MessageService.defaultLog(new org.eclipse.core.runtime.Status(IStatus.INFO, "org.simantics.simulation.ui", 0, "Activated experiment " + experiment.getIdentifier() , null)); + activated.release(); + run.set(experiment); + } + @Override + public void onFailure(Throwable e) { + problem.set(e); + activated.release(); + } + @Override + public void onMessage(IStatus message) { +// MessageService.getDefault().log(message); + } + @Override + public IProgressMonitor getProgressMonitor() { + return mon; + } + }, true); + try { + activated.acquire(); + Throwable t = problem.get(); + if (t != null) { + if (t instanceof ExperimentLoadingFailed) { +// ErrorLogger.defaultLogError(t); +// ShowMessage.showError("Experiment Activation Failed", t.getMessage()); + } else { +// ExceptionUtils.logAndShowError(t); + } + } + + return run.get().getIdentifier(); + //return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Experiment activation failed, see exception for details.", problem.get()); + } catch (InterruptedException e) { + return null; + } + } + + + public static String activateExperiment(Resource experiment) throws DatabaseException { + +// Resource experiment = Layer0Utils.getPossibleChild(graph, model, name); +// if( experiment == null) return false; + +// SimulationResource SIMU = SimulationResource.getInstance(graph); +// if (!graph.isInstanceOf(experiment, SIMU.Experiment)) return false; + + final IProject project = Simantics.getProject(); + if (project == null) return null; + + final IExperimentManager experimentManager = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); + if(experimentManager == null) return null; + + return SysdynExperiments.activateExperiment(null, project, experimentManager, experiment); + + } + + public static void run(String experimentId) throws DatabaseException { + + final IProject project = Simantics.getProject(); + if (project == null) return; + + final IExperimentManager experimentManager = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); + if(experimentManager == null) return; + + IExperiment experiment = experimentManager.getExperiment(experimentId); + if(experiment instanceof IDynamicExperiment) + ((IDynamicExperiment)experiment).simulate(true); + + } + + + + +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/solver/InternalSolver.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/solver/InternalSolver.java index 4c00a72e..7144a00c 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/solver/InternalSolver.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/solver/InternalSolver.java @@ -1,5 +1,6 @@ package org.simantics.sysdyn.solver; +import java.io.File; import java.util.HashMap; import org.simantics.modelica.ModelicaManager; @@ -11,11 +12,14 @@ import org.simantics.sysdyn.manager.SysdynModel; import org.simantics.sysdyn.modelica.ModelicaWriter; import org.simantics.sysdyn.representation.Model; import org.simantics.sysdyn.solver.SolverSettings.SolverType; +import org.simantics.utils.FileUtils; import fi.semantum.sysdyn.solver.Solver; public class InternalSolver implements ISolver { + public static final boolean PRINT_CODE = false; + private SysdynExperiment experiment; private SysdynModel model; private ISolverMonitor monitor; @@ -46,14 +50,14 @@ public class InternalSolver implements ISolver { @Override public void initialize() throws Exception { - String omVersion = ModelicaManager.getDefaultOMVersion(); - String modelContent = ModelicaWriter.write(model.getModules(), false, omVersion); +// String omVersion = ModelicaManager.getDefaultOMVersion(); + String modelContent = ModelicaWriter.write(model.getModules(), false, "1.9"); // update some stuff FunctionUtils.updateFunctionFilesForExperiment(experiment); location = ModelicaManager.createSimulationLocation(experiment.getExperimentDir(), - model.getConfiguration().getLabel(), modelContent); + model.getConfiguration().getLabel(), modelContent, null, false); // set solver parameters Model representation = model.getConfiguration().getModel(); @@ -80,12 +84,34 @@ public class InternalSolver implements ISolver { @Override public void buildModel() throws Exception { - String flat = ModelicaManager.getFlatModelText(location, monitor, FunctionUtils.getLibraryPathsForModelica(experiment)); - solver.prepare(flat); + +// String flat = ModelicaManager.getFlatModelText(location, monitor, FunctionUtils.getLibraryPathsForModelica(experiment)); +// System.err.println("=== FLAT ==="); +// System.err.println(flat); +// System.err.println("=== FLAT ENDS ==="); + + StringBuilder code = new StringBuilder(); + for(String path : FunctionUtils.getLibraryPathsForModelica(experiment)) { + File f = new File(location.modelFile.getParentFile(), path); + code.append(FileUtils.getContents(f)); + } + code.append(FileUtils.getContents(location.modelFile)); + + if(PRINT_CODE) { + System.err.println("=== CODE === "); + System.err.println(code.toString()); + System.err.println("=== CODE ENDS ==="); + } + + solver.prepare(code.toString()); + } @Override public void runSolver() throws Exception { + + long startTime = System.nanoTime(); + // the number of result intervals in the simulation (account for initial values) int count = (int)((stop - start) / interval) + 1; // the number of steps in one result interval @@ -94,13 +120,22 @@ public class InternalSolver implements ISolver { // an array containing an accurate time stamp for each interval double[] times = new double[count]; // a map containing values of all variables for each interval - HashMap values = new HashMap(); + + String[] keys = solver.keys(); + + double[][] values = new double[keys.length][]; + +// HashMap values = new HashMap(); // initialize the temporary data structures times[0] = start; - HashMap tmp = solver.values(); - for (String key : tmp.keySet()) { - values.put(key, new double[count]); - values.get(key)[0] = tmp.get(key); + + double[] valueArray = solver.values(); +// HashMap tmp = solver.values(); +// String[] keys = new ArrayList(tmp.keySet()).toArray(new String[tmp.size()]); + + for(int i=0;i(); - for (String name : values.keySet()) { - results.put(name, new SysdynDataSet(name, null, times, values.get(name))); + for(int i=0;i