Repository: pymoca/pymoca

Issue:
Modelica time is not supported by the SymPy backend
As discussed in #255, the Modelica Reference to the simulation time is not supported by the SymPy backend. In a flat Modelica model, the time variable may be used in generators, such as line 12 from the attached example:
`sine__y = sine__offset + sine__amplitude * sin(6.283185307179586 * sine__f * time + sine__phase);`
The time variable will not be defined in the declaration section of the model/class when compiling the MO file to an MOF file via omc and is thus not part of the symbols in the ast. The pymoca.sympy.generator creates a time variable "t", but does not link the "time" variable, that is present in the ast only in the equations but not as a symbol, to this variable "t". Possible solutions are:
1. The inclusion of "time" in the list of symbols of the ast, in every case or only when it is used in an equation, or
2. The declaration of a time variable in pymoca.sympy.generator, which would then be somehow linked to the variable "t", created in the pymoca.sympy.runtime.OdeModel class (an easy but inelegant way could be to create a state or variable for "time" in exitClass() in pymoca.sympy.generator and then linking it to "t" within SymPy by symbolic equivalence).

Comments:

Patch:
diff --git a/src/pymoca/backends/sympy/generator.py b/src/pymoca/backends/sympy/generator.py
index f8692b9..b19ea87 100644
--- a/src/pymoca/backends/sympy/generator.py
+++ b/src/pymoca/backends/sympy/generator.py
@@ -179,11 +179,12 @@ class {{tree.name}}(OdeModel):
         self.src[tree] = "{:s}".format(val)
 
     def exitComponentRef(self, tree: ast.ComponentRef):
-
         # prevent name clash with builtins
         name = tree.name.replace('.', '__')
         while name in BUILTINS:
             name = name + '_'
+        if name == 'time':
+            name = 'self.t'
         self.src[tree] = name
 
     def exitSymbol(self, tree: ast.Symbol):
@@ -191,7 +192,6 @@ class {{tree.name}}(OdeModel):
         name = tree.name.replace('.', '__')
         while name in BUILTINS:
             name = name + '_'
-
         self.src[tree] = name
 
     def exitEquation(self, tree: ast.Equation):


Test patch:
diff --git a/test/gen_sympy_test.py b/test/gen_sympy_test.py
index 25bad9e..55b38c2 100644
--- a/test/gen_sympy_test.py
+++ b/test/gen_sympy_test.py
@@ -142,6 +142,32 @@ class GenSympyTest(unittest.TestCase):
         # res = e.simulate()
         self.flush()
 
+    def test_time_builtin(self):
+        """Tests Modelica `time` used in a model"""
+        with open(os.path.join(MODEL_DIR, 'SpringSystem.mo'), 'r') as f:
+            txt = f.read()
+        ast_tree = parser.parse(txt)
+        forced_spring_model = '''
+        model ForcedSpringSystem "SpringSystem with time-varying input force"
+            SpringSystem sys;
+        equation
+            sys.u = 100.0*sin(2*time);
+        end ForcedSpringSystem;
+        '''
+        system_ast = parser.parse(forced_spring_model)
+        ast_tree.extend(system_ast)
+        flat_tree = tree.flatten(ast_tree, ast.ComponentRef(name='ForcedSpringSystem'))
+        print(flat_tree)
+        text = gen_sympy.generate(ast_tree, 'ForcedSpringSystem')
+        with open(os.path.join(GENERATED_DIR, 'ForcedSpringSystem.py'), 'w') as f:
+            f.write(text)
+        from test.generated.ForcedSpringSystem import ForcedSpringSystem as ForcedSpringSystem
+        e = ForcedSpringSystem()
+        e.linearize_symbolic()
+        e.linearize()
+        # noinspection PyUnusedLocal
+        res = e.simulate(x0=[1.0, 1.0])
+        self.flush()
 
 if __name__ == "__main__":
     unittest.main()