gotranx 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gotranx/__init__.py +51 -0
- gotranx/__main__.py +4 -0
- gotranx/atoms.py +215 -0
- gotranx/cellml/__init__.py +23 -0
- gotranx/cellml/cellml.py +1709 -0
- gotranx/cellml/mathml.py +341 -0
- gotranx/cli/__init__.py +94 -0
- gotranx/cli/cellml2ode.py +20 -0
- gotranx/cli/gotran2c.py +34 -0
- gotranx/cli/gotran2py.py +39 -0
- gotranx/codecomponent.py +44 -0
- gotranx/codegen/__init__.py +18 -0
- gotranx/codegen/base.py +289 -0
- gotranx/codegen/c.py +102 -0
- gotranx/codegen/python.py +130 -0
- gotranx/exceptions.py +80 -0
- gotranx/expressions.py +137 -0
- gotranx/load.py +36 -0
- gotranx/ode.lark +122 -0
- gotranx/ode.py +300 -0
- gotranx/ode_component.py +160 -0
- gotranx/parser.py +17 -0
- gotranx/save.py +138 -0
- gotranx/schemes.py +175 -0
- gotranx/sympytools.py +98 -0
- gotranx/templates/__init__.py +42 -0
- gotranx/templates/c.py +62 -0
- gotranx/templates/python.py +139 -0
- gotranx/transformer.py +212 -0
- gotranx/units.py +3 -0
- gotranx-0.1.0.dist-info/METADATA +276 -0
- gotranx-0.1.0.dist-info/RECORD +35 -0
- gotranx-0.1.0.dist-info/WHEEL +5 -0
- gotranx-0.1.0.dist-info/entry_points.txt +2 -0
- gotranx-0.1.0.dist-info/top_level.txt +1 -0
gotranx/__init__.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from importlib.metadata import metadata
|
|
2
|
+
|
|
3
|
+
from . import cli
|
|
4
|
+
from . import codecomponent
|
|
5
|
+
from . import exceptions
|
|
6
|
+
from . import load
|
|
7
|
+
from . import ode
|
|
8
|
+
from . import ode_component
|
|
9
|
+
from . import parser
|
|
10
|
+
from . import codegen
|
|
11
|
+
from . import transformer
|
|
12
|
+
from . import units
|
|
13
|
+
from . import sympytools
|
|
14
|
+
from . import schemes
|
|
15
|
+
from . import templates
|
|
16
|
+
from . import cellml
|
|
17
|
+
from .load import load_ode
|
|
18
|
+
from .ode import ODE
|
|
19
|
+
from .ode_component import Component
|
|
20
|
+
from .parser import Parser
|
|
21
|
+
from .transformer import TreeToODE
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
meta = metadata("gotranx")
|
|
25
|
+
__version__ = meta["Version"]
|
|
26
|
+
__author__ = meta["Author"]
|
|
27
|
+
__license__ = meta["License"]
|
|
28
|
+
__email__ = meta["Author-email"]
|
|
29
|
+
__program_name__ = meta["Name"]
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"cli",
|
|
33
|
+
"parser",
|
|
34
|
+
"Parser",
|
|
35
|
+
"codegen",
|
|
36
|
+
"transformer",
|
|
37
|
+
"TreeToODE",
|
|
38
|
+
"exceptions",
|
|
39
|
+
"load",
|
|
40
|
+
"load_ode",
|
|
41
|
+
"ode_component",
|
|
42
|
+
"Component",
|
|
43
|
+
"ode",
|
|
44
|
+
"ODE",
|
|
45
|
+
"units",
|
|
46
|
+
"codecomponent",
|
|
47
|
+
"sympytools",
|
|
48
|
+
"schemes",
|
|
49
|
+
"templates",
|
|
50
|
+
"cellml",
|
|
51
|
+
]
|
gotranx/__main__.py
ADDED
gotranx/atoms.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import attr
|
|
4
|
+
import lark
|
|
5
|
+
import pint
|
|
6
|
+
import sympy as sp
|
|
7
|
+
from structlog import get_logger
|
|
8
|
+
|
|
9
|
+
from .expressions import build_expression
|
|
10
|
+
from .units import ureg
|
|
11
|
+
|
|
12
|
+
logger = get_logger()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _set_symbol(instance, name: str) -> None:
|
|
16
|
+
object.__setattr__(
|
|
17
|
+
instance,
|
|
18
|
+
"symbol",
|
|
19
|
+
sp.Symbol(
|
|
20
|
+
name=name,
|
|
21
|
+
real=True,
|
|
22
|
+
imaginary=False,
|
|
23
|
+
commutative=True,
|
|
24
|
+
finite=True,
|
|
25
|
+
),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def unit_from_string(unit_str: str | None) -> pint.Unit | None:
|
|
30
|
+
if unit_str is not None:
|
|
31
|
+
try:
|
|
32
|
+
unit = ureg.Unit(unit_str)
|
|
33
|
+
except pint.UndefinedUnitError:
|
|
34
|
+
logger.warning(f"Undefined unit {unit_str!r}")
|
|
35
|
+
unit = None
|
|
36
|
+
else:
|
|
37
|
+
unit = None
|
|
38
|
+
return unit
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _set_unit(instance, unit_str: str) -> None:
|
|
42
|
+
object.__setattr__(instance, "unit", unit_from_string(unit_str))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@attr.s(frozen=True, slots=True)
|
|
46
|
+
class Comment:
|
|
47
|
+
text: str = attr.ib()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@attr.s(frozen=True, kw_only=True, slots=True)
|
|
51
|
+
class Atom:
|
|
52
|
+
"""Base class for atoms"""
|
|
53
|
+
|
|
54
|
+
name: str = attr.ib()
|
|
55
|
+
value: float | Expression | sp.core.Number = attr.ib()
|
|
56
|
+
components: tuple[str, ...] = attr.ib(default=("",))
|
|
57
|
+
description: str | None = attr.ib(None)
|
|
58
|
+
symbol: sp.Symbol = attr.ib(None)
|
|
59
|
+
unit_str: str | None = attr.ib(None, repr=False)
|
|
60
|
+
unit: pint.Unit | None = attr.ib(None)
|
|
61
|
+
|
|
62
|
+
def __attrs_post_init__(self):
|
|
63
|
+
if self.unit is None:
|
|
64
|
+
_set_unit(self, unit_str=self.unit_str)
|
|
65
|
+
if self.symbol is None:
|
|
66
|
+
_set_symbol(self, name=self.name)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@attr.s(frozen=True, kw_only=True, slots=True)
|
|
70
|
+
class Parameter(Atom):
|
|
71
|
+
"""A Parameter is a constant scalar value"""
|
|
72
|
+
|
|
73
|
+
value: float | sp.core.Number = attr.ib()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@attr.s(frozen=True, kw_only=True, slots=True)
|
|
77
|
+
class State(Atom):
|
|
78
|
+
"""A State is a variable that also has a
|
|
79
|
+
corresponding state derivative.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
value: float | sp.core.Number = attr.ib()
|
|
83
|
+
|
|
84
|
+
def to_TimeDependentState(self, t: sp.Symbol) -> "TimeDependentState":
|
|
85
|
+
return TimeDependentState(
|
|
86
|
+
name=self.name,
|
|
87
|
+
value=self.value,
|
|
88
|
+
symbol=sp.Function(self.symbol)(t),
|
|
89
|
+
components=self.components,
|
|
90
|
+
description=self.description,
|
|
91
|
+
unit_str=self.unit_str,
|
|
92
|
+
unit=self.unit,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@attr.s(frozen=True, kw_only=True, slots=True)
|
|
97
|
+
class TimeDependentState(State):
|
|
98
|
+
"""A TimeDependentState is a State, where the symbol
|
|
99
|
+
is a sympy Function instead of a pure Symbol.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
symbol: sp.Function = attr.ib()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@attr.s(frozen=True, kw_only=True, slots=True)
|
|
106
|
+
class Expression:
|
|
107
|
+
"""An Expression is a group of variables (i.e
|
|
108
|
+
states, parameters or other expressions) combined
|
|
109
|
+
with binary operations (i.e +, -, * etc)
|
|
110
|
+
An Expression is typically a right hand side of
|
|
111
|
+
an assignment."""
|
|
112
|
+
|
|
113
|
+
tree: lark.Tree = attr.ib(cmp=False) # Different trees can give same expression
|
|
114
|
+
dependencies: frozenset[str] = attr.ib(init=False)
|
|
115
|
+
|
|
116
|
+
def __attrs_post_init__(self):
|
|
117
|
+
object.__setattr__(self, "dependencies", self._find_dependencies())
|
|
118
|
+
|
|
119
|
+
def _find_dependencies(self) -> frozenset[str]:
|
|
120
|
+
deps = set()
|
|
121
|
+
|
|
122
|
+
for tree in self.tree.iter_subtrees():
|
|
123
|
+
if tree.data == "variable":
|
|
124
|
+
deps.add(str(tree.children[0]))
|
|
125
|
+
return frozenset(deps)
|
|
126
|
+
|
|
127
|
+
def resolve(self, symbols: dict[str, sp.Symbol]):
|
|
128
|
+
return build_expression(self.tree, symbols=symbols)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@attr.s(frozen=True, kw_only=True, slots=True)
|
|
132
|
+
class Assignment(Atom):
|
|
133
|
+
"""Assignments are object of the form `name = value`."""
|
|
134
|
+
|
|
135
|
+
value: Expression = attr.ib()
|
|
136
|
+
expr: sp.Expr = attr.ib(sp.S.Zero)
|
|
137
|
+
|
|
138
|
+
def resolve_expression(self, symbols: dict[str, sp.Symbol]) -> Assignment:
|
|
139
|
+
expr = self.value.resolve(symbols)
|
|
140
|
+
return type(self)(
|
|
141
|
+
name=self.name,
|
|
142
|
+
value=self.value,
|
|
143
|
+
components=self.components,
|
|
144
|
+
unit_str=self.unit_str,
|
|
145
|
+
unit=self.unit,
|
|
146
|
+
expr=expr,
|
|
147
|
+
symbol=self.symbol,
|
|
148
|
+
description=self.description,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def to_intermediate(self) -> "Intermediate":
|
|
152
|
+
return Intermediate(
|
|
153
|
+
name=self.name,
|
|
154
|
+
value=self.value,
|
|
155
|
+
components=self.components,
|
|
156
|
+
unit_str=self.unit_str,
|
|
157
|
+
unit=self.unit,
|
|
158
|
+
expr=self.expr,
|
|
159
|
+
description=self.description,
|
|
160
|
+
symbol=self.symbol,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def to_state_derivative(self, state: State) -> "StateDerivative":
|
|
164
|
+
return StateDerivative(
|
|
165
|
+
name=self.name,
|
|
166
|
+
value=self.value,
|
|
167
|
+
components=self.components,
|
|
168
|
+
unit_str=self.unit_str,
|
|
169
|
+
unit=self.unit,
|
|
170
|
+
state=state,
|
|
171
|
+
expr=self.expr,
|
|
172
|
+
description=self.description,
|
|
173
|
+
symbol=self.symbol,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def simplify(self) -> "Assignment":
|
|
177
|
+
return type(self)(
|
|
178
|
+
name=self.name,
|
|
179
|
+
value=self.value,
|
|
180
|
+
components=self.components,
|
|
181
|
+
unit_str=self.unit_str,
|
|
182
|
+
unit=self.unit,
|
|
183
|
+
expr=self.expr.simplify(),
|
|
184
|
+
description=self.description,
|
|
185
|
+
symbol=self.symbol,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@attr.s(frozen=True, kw_only=True, slots=True)
|
|
190
|
+
class Intermediate(Assignment):
|
|
191
|
+
"""Intermediate is a type of Assignment that is not
|
|
192
|
+
a StateDerivative"""
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@attr.s(frozen=True, kw_only=True, slots=True)
|
|
196
|
+
class StateDerivative(Assignment):
|
|
197
|
+
"""A StateDerivative is an Assignment of the form
|
|
198
|
+
`dX_dt = value` where X is a state. A StatedDerivative
|
|
199
|
+
also holds a pointer to the State"""
|
|
200
|
+
|
|
201
|
+
state: State = attr.ib()
|
|
202
|
+
|
|
203
|
+
def resolve_expression(self, symbols: dict[str, sp.Symbol]) -> Assignment:
|
|
204
|
+
expr = self.value.resolve(symbols)
|
|
205
|
+
return StateDerivative(
|
|
206
|
+
name=self.name,
|
|
207
|
+
value=self.value,
|
|
208
|
+
components=self.components,
|
|
209
|
+
unit_str=self.unit_str,
|
|
210
|
+
unit=self.unit,
|
|
211
|
+
symbol=self.symbol,
|
|
212
|
+
expr=expr,
|
|
213
|
+
state=self.state,
|
|
214
|
+
description=self.description,
|
|
215
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
from .cellml import CellMLParser
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def cellml_to_gotran(filename: Path | str, params: dict[str, Any] | None = None) -> str:
|
|
8
|
+
"""Convert a cellml file to gotran code
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
input_filename : Path or str
|
|
13
|
+
The path to the cellml file
|
|
14
|
+
params : dict[str, Any], optional
|
|
15
|
+
Parameters to pass to the parser, by default None
|
|
16
|
+
|
|
17
|
+
Returns
|
|
18
|
+
-------
|
|
19
|
+
str
|
|
20
|
+
The gotran code
|
|
21
|
+
"""
|
|
22
|
+
parser = CellMLParser(filename, params=params)
|
|
23
|
+
return parser.to_gotran()
|