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).

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pymoca/pymoca/pull/258?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca) Report
Base: **80.38**% // Head: **80.39**% // Increases project coverage by **`+0.00%`** :tada:
> Coverage data is based on head [(`30ff746`)](https://codecov.io/gh/pymoca/pymoca/pull/258?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca) compared to base [(`0e5a471`)](https://codecov.io/gh/pymoca/pymoca/commit/0e5a471ff3506d8dc04c8d0774b36a19ea3b9861?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca).
> Patch coverage: 100.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master     #258   +/-   ##
=======================================
  Coverage   80.38%   80.39%           
=======================================
  Files          17       17           
  Lines        4079     4081    +2     
  Branches      949      950    +1     
=======================================
+ Hits         3279     3281    +2     
  Misses        647      647           
  Partials      153      153           
```


| [Impacted Files](https://codecov.io/gh/pymoca/pymoca/pull/258?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca) | Coverage Δ | |
|---|---|---|
| [src/pymoca/backends/sympy/generator.py](https://codecov.io/gh/pymoca/pymoca/pull/258/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca#diff-c3JjL3B5bW9jYS9iYWNrZW5kcy9zeW1weS9nZW5lcmF0b3IucHk=) | `96.77% <100.00%> (+0.07%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/pymoca/pymoca/pull/258?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca).


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()