Repository: pymoca/pymoca

Issue:
CasADi: a constant is allowed to be a canonical variable but it is not handled properly
The problem is well explained in the following example:

## Constant

If `c` is a constant then it may become a canonical variable with z as an alias but it will raise an error.

```modelica
model ConstantAlias

Real x;
Real z;
constant Real c=0;

equation

der(x) = c;
z = c;

end ConstantAlias;
```

```python
options = {'detect_aliases': True}
model = transfer_model(model_folder, 'ConstantAlias', options)
```

```
INFO:pymoca:Detecting aliases
Traceback (most recent call last):
  File "/home/pietro/anaconda3/envs/pymocaExp/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3331, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-2-d427935174b8>", line 32, in <module>
    model = transfer_model(model_folder, 'ConstantAlias', options)
  File "/home/pietro/Desktop/Thesis/git/pymoca/src/pymoca/backends/casadi/api.py", line 477, in transfer_model
    return _compile_model(model_folder, model_name, compiler_options)
  File "/home/pietro/Desktop/Thesis/git/pymoca/src/pymoca/backends/casadi/api.py", line 119, in _compile_model
    model.simplify(compiler_options)
  File "/home/pietro/Desktop/Thesis/git/pymoca/src/pymoca/backends/casadi/model.py", line 844, in simplify
    canonical_state = all_states[canonical]
KeyError: 'c'
```

## Parameter 

If `c` is a parameter then there are no problems:

```modelica
model ParameterAlias

Real x;
Real z;
parameter Real c=0;

equation

der(x) = c;
z = c;

end ParameterAlias;
```

```python
options = {'detect_aliases': True}
model = transfer_model(model_folder, 'ParameterAlias', options)
```

Discussion:

Patch:
diff --git a/src/pymoca/backends/casadi/model.py b/src/pymoca/backends/casadi/model.py
index 8b788f4..a668e6f 100644
--- a/src/pymoca/backends/casadi/model.py
+++ b/src/pymoca/backends/casadi/model.py
@@ -767,6 +767,7 @@ class Model:
             alg_states = OrderedDict([(s.symbol.name(), s) for s in self.alg_states])
             inputs = OrderedDict([(s.symbol.name(), s) for s in self.inputs])
             parameters = OrderedDict([(s.symbol.name(), s) for s in self.parameters])
+            constants = OrderedDict([(s.symbol.name(), s) for s in self.constants])
 
             all_states = OrderedDict()
             all_states.update(states)
@@ -774,9 +775,11 @@ class Model:
             all_states.update(alg_states)
             all_states.update(inputs)
             all_states.update(parameters)
+            all_states.update(constants)
 
             # For now, we only eliminate algebraic states.
-            do_not_eliminate = set(list(der_states) + list(states) + list(inputs) + list(parameters))
+            do_not_eliminate = set(list(der_states) + list(states) + list(inputs)
+                                   + list(parameters) + list(constants))
 
             # When doing a `detect_aliases` pass multiple times, we have to avoid
             # handling aliases we already handled before as their states no longer


Test patch:
diff --git a/test/gen_casadi_test.py b/test/gen_casadi_test.py
index 2adf5e5..28b7666 100644
--- a/test/gen_casadi_test.py
+++ b/test/gen_casadi_test.py
@@ -1388,6 +1388,54 @@ class GenCasadiTest(unittest.TestCase):
         self.assert_model_equivalent_numeric(casadi_model, ref_model)
         self.assertEqual(casadi_model.states[0].aliases, {'-alias_neg', 'alias_pos'})
 
+    def test_constant_aliases(self):
+        txt = """
+            model ConstantAlias
+
+              Real x;
+              Real z;
+              constant Real c = 0;
+
+              equation
+
+              der(x) = c;
+              z = c;
+
+            end ConstantAlias;
+        """
+
+        ast_tree = parser.parse(txt)
+        casadi_model = gen_casadi.generate(ast_tree, 'ConstantAlias')
+        casadi_model.simplify({'detect_aliases': True})
+
+        c = casadi_model.constants[0]
+        self.assertSetEqual(c.aliases, {'z'})
+        self.assertEqual(len(casadi_model.alg_states), 0)
+
+    def test_parameter_aliases(self):
+        txt = """
+            model ParameterAlias
+
+              Real x;
+              Real z;
+              parameter Real c = 0;
+
+              equation
+
+              der(x) = c;
+              z = c;
+
+            end ParameterAlias;
+        """
+
+        ast_tree = parser.parse(txt)
+        casadi_model = gen_casadi.generate(ast_tree, 'ParameterAlias')
+        casadi_model.simplify({'detect_aliases': True})
+
+        p = casadi_model.parameters[0]
+        self.assertSetEqual(p.aliases, {'z'})
+        self.assertEqual(len(casadi_model.alg_states), 0)
+
     def test_simplify_expand_vectors(self):
         # Create model, cache it, and load the cache
         compiler_options = \