Repository: ModellingWebLab/cellmlmanip

Issue:
Determine derived quantities
I need to be able to determine derived quantities for chaste codegen.

@maurice I was replying to your last comment when it disappeared!
Anyway, you can use `get_symbols_by_rdf` to find things annotated explicitly as d-q and also everything with an oxmeta name. Then use something like:
[v for v, node in self.graph.nodes.items() if node.get('variable_type', '') not in ('state', 'free', 'parameter') and v in oxmeta_symbols]

jonathan  27 minutes ago
You could write that in -cg, or could add a method to cellmlmanip. Although the v in oxmeta_symbols bit probably wouldn't be appropriate for cellmlmanip

jonathan  27 minutes ago
But getting all the derived quantities could be a method, like for state vars etc.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186?src=pr&el=h1) Report
> Merging [#186](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186?src=pr&el=desc) into [develop](https://codecov.io/gh/ModellingWebLab/cellmlmanip/commit/dadacbbae97fe5180f5843333ff7feb752618338?src=pr&el=desc) will **increase** coverage by `0.12%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186/graphs/tree.svg?width=650&token=qJVaJx2tOK&height=150&src=pr)](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           develop    #186      +/-   ##
==========================================
+ Coverage    96.18%   96.3%   +0.12%     
==========================================
  Files            6       6              
  Lines          891     893       +2     
  Branches       186     187       +1     
==========================================
+ Hits           857     860       +3     
  Misses          17      17              
+ Partials        17      16       -1
```


| [Impacted Files](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [cellmlmanip/model.py](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186/diff?src=pr&el=tree#diff-Y2VsbG1sbWFuaXAvbW9kZWwucHk=) | `100% <100%> (+0.37%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186?src=pr&el=footer). Last update [dadacbb...e7a3600](https://codecov.io/gh/ModellingWebLab/cellmlmanip/pull/186?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

MauriceHendrix: > Are you sure that a derivative counts as a derived quantity? I thought those were only things like INa etc. ?

apparently you're right so I'll fix that.

Patch:
diff --git a/cellmlmanip/model.py b/cellmlmanip/model.py
index 98057c1..55d6d09 100644
--- a/cellmlmanip/model.py
+++ b/cellmlmanip/model.py
@@ -267,6 +267,14 @@ class Model(object):
         derivative_symbols = [v for v in self.graph if isinstance(v, sympy.Derivative)]
         return sorted(derivative_symbols, key=lambda state_var: state_var.args[0].order_added)
 
+    def get_derived_quantities(self):
+        """Returns a list of derived quantities found in the given model graph.
+        A derived quantity is any variable that is not a state variable or parameter/constant.
+        """
+        return [v for v, node in self.graph.nodes.items()
+                if not isinstance(v, sympy.Derivative)
+                and node.get('variable_type', '') not in ('state', 'free', 'parameter')]
+
     def get_state_symbols(self):
         """Returns a list of state variables found in the given model graph.
         The list is ordered by appearance in the cellml document.


Test patch:
diff --git a/tests/test_model_functions.py b/tests/test_model_functions.py
index 698f7aa..7a6c78e 100644
--- a/tests/test_model_functions.py
+++ b/tests/test_model_functions.py
@@ -62,6 +62,19 @@ class TestModelFunctions():
     #######################################################################
     # this section contains tests for each get_XXX function on Model
 
+    def test_get_derived_quantities(self, basic_model, simple_ode_model):
+        """ Tests Model.get_state_symbols() works correctly. """
+
+        derived_quantities = basic_model.get_derived_quantities()
+        assert len(derived_quantities) == 0
+
+        derived_quantities = simple_ode_model.get_derived_quantities()
+        assert str(sorted(derived_quantities, key=str)) == \
+            '[_circle_sibling$local_complex_maths, _circle_x_sibling$x2, _circle_y_implementation$rhs, '\
+            '_deriv_on_rhs$sv1_rate, _deriv_on_rhs1a$sv1_rate, _deriv_on_rhs1b$sv1_rate, _deriv_on_rhs2a$sv1_rate, '\
+            '_deriv_on_rhs2b$sv1_rate, _derived_from_state_var$dbl_sv1, _single_ode_rhs_computed_var$a, '\
+            '_time_units_conversion1$time, _time_units_conversion2$time]'
+
     def test_get_state_symbols(self, basic_model):
         """ Tests Model.get_state_symbols() works correctly. """