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 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
@@ -0,0 +1,4 @@
1
+ from .cli import app
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(app())
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()