mxlpy 0.21.0__py3-none-any.whl → 0.23.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.
mxlpy/plot.py CHANGED
@@ -28,6 +28,7 @@ import numpy as np
28
28
  import pandas as pd
29
29
  import seaborn as sns
30
30
  from cycler import cycler
31
+ from matplotlib import colormaps
31
32
  from matplotlib import pyplot as plt
32
33
  from matplotlib.axes import Axes
33
34
  from matplotlib.colors import (
@@ -37,6 +38,8 @@ from matplotlib.colors import (
37
38
  colorConverter, # type: ignore
38
39
  )
39
40
  from matplotlib.figure import Figure
41
+ from matplotlib.legend import Legend
42
+ from matplotlib.patches import Patch
40
43
  from mpl_toolkits.mplot3d import Axes3D
41
44
 
42
45
  from mxlpy.label_map import LabelMapper
@@ -541,7 +544,7 @@ def bars(
541
544
  sns.barplot(data=cast(pd.DataFrame, x), ax=ax)
542
545
 
543
546
  if xlabel is None:
544
- xlabel = x.index.name if x.index.name is not None else ""
547
+ xlabel = x.index.name if x.index.name is not None else "" # type: ignore
545
548
  _default_labels(ax, xlabel=xlabel, ylabel=ylabel)
546
549
  if isinstance(x, pd.DataFrame):
547
550
  ax.legend(x.columns)
@@ -651,7 +654,7 @@ def lines(
651
654
  )
652
655
  _default_labels(
653
656
  ax,
654
- xlabel=x.index.name if xlabel is None else xlabel,
657
+ xlabel=x.index.name if xlabel is None else xlabel, # type: ignore
655
658
  ylabel=ylabel,
656
659
  )
657
660
  if legend:
@@ -792,7 +795,11 @@ def line_mean_std(
792
795
  color=color,
793
796
  alpha=alpha,
794
797
  )
795
- _default_labels(ax, xlabel=df.index.name, ylabel=None)
798
+ _default_labels(
799
+ ax,
800
+ xlabel=df.index.name, # type: ignore
801
+ ylabel=None,
802
+ )
796
803
  return fig, ax
797
804
 
798
805
 
@@ -865,11 +872,11 @@ def _create_heatmap(
865
872
  if title is not None:
866
873
  ax.set_title(title)
867
874
  ax.set_xticks(
868
- np.arange(0, len(df.columns), 1, dtype=float) + 0.5,
875
+ np.arange(0, len(df.columns), 1, dtype=float) + 0.5, # type: ignore
869
876
  labels=xticklabels,
870
877
  )
871
878
  ax.set_yticks(
872
- np.arange(0, len(df.index), 1, dtype=float) + 0.5,
879
+ np.arange(0, len(df.index), 1, dtype=float) + 0.5, # type: ignore
873
880
  labels=yticklabels,
874
881
  )
875
882
 
@@ -906,8 +913,8 @@ def heatmap(
906
913
  ax=ax,
907
914
  df=df,
908
915
  title=title,
909
- xlabel=df.index.name,
910
- ylabel=df.columns.name,
916
+ xlabel=df.index.name, # type: ignore
917
+ ylabel=df.columns.name, # type: ignore
911
918
  xticklabels=cast(list, df.columns),
912
919
  yticklabels=cast(list, df.index),
913
920
  annotate=annotate,
@@ -943,8 +950,8 @@ def heatmap_from_2d_idx(
943
950
 
944
951
  return _create_heatmap(
945
952
  df=df2d,
946
- xlabel=df2d.index.name,
947
- ylabel=df2d.columns.name,
953
+ xlabel=df2d.index.name, # type: ignore
954
+ ylabel=df2d.columns.name, # type: ignore
948
955
  xticklabels=[f"{i:.2f}" for i in df2d.columns],
949
956
  yticklabels=[f"{i:.2f}" for i in df2d.index],
950
957
  ax=ax,
@@ -1064,11 +1071,6 @@ def shade_protocol(
1064
1071
  add_legend: bool = True,
1065
1072
  ) -> None:
1066
1073
  """Shade the given protocol on the given axis."""
1067
- from matplotlib import colormaps
1068
- from matplotlib.colors import Normalize
1069
- from matplotlib.legend import Legend
1070
- from matplotlib.patches import Patch
1071
-
1072
1074
  cmap = colormaps[cmap_name]
1073
1075
  norm = Normalize(
1074
1076
  vmin=protocol.min() if vmin is None else vmin,
mxlpy/report.py CHANGED
@@ -3,25 +3,25 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from collections.abc import Callable
6
+ from dataclasses import dataclass
6
7
  from datetime import UTC, datetime
7
8
  from pathlib import Path
8
9
  from typing import cast
9
10
 
10
11
  import sympy
11
12
 
12
- from mxlpy.meta.source_tools import fn_to_sympy
13
+ from mxlpy.meta.sympy_tools import fn_to_sympy, list_of_symbols
13
14
  from mxlpy.model import Model
14
15
 
15
- __all__ = [
16
- "AnalysisFn",
17
- "markdown",
18
- ]
16
+ __all__ = ["AnalysisFn", "MarkdownReport", "markdown"]
19
17
 
20
18
  type AnalysisFn = Callable[[Model, Model, Path], tuple[str, Path]]
21
19
 
22
20
 
23
- def _list_of_symbols(args: list[str]) -> list[sympy.Symbol | sympy.Expr]:
24
- return [sympy.Symbol(arg) for arg in args]
21
+ def _latex_view(expr: sympy.Expr | None) -> str:
22
+ if expr is None:
23
+ return "<span style='color:red'>PARSE ERROR<span>"
24
+ return f"${sympy.latex(expr)}$"
25
25
 
26
26
 
27
27
  def _new_removed_changed[T](
@@ -44,48 +44,71 @@ def _table_header(items: list[str]) -> str:
44
44
  return f"{_table_row(items)}\n{_table_row(['---'] * len(items))}"
45
45
 
46
46
 
47
+ @dataclass
48
+ class MarkdownReport:
49
+ """Report of model comparison."""
50
+
51
+ data: str
52
+
53
+ def __str__(self) -> str:
54
+ """Markdown string representation."""
55
+ return self.data
56
+
57
+ def __repr__(self) -> str:
58
+ """Markdown string representation."""
59
+ return self.data
60
+
61
+ def _repr_markdown_(self) -> str:
62
+ return self.data
63
+
64
+ def write(self, path: Path) -> None:
65
+ """Write report to file."""
66
+ path.parent.mkdir(parents=True, exist_ok=True)
67
+
68
+ with path.open("w+") as fp:
69
+ fp.write(self.data)
70
+
71
+
47
72
  def markdown(
48
73
  m1: Model,
49
74
  m2: Model,
75
+ *,
50
76
  analyses: list[AnalysisFn] | None = None,
51
77
  rel_change: float = 1e-2,
52
78
  img_path: Path = Path(),
53
- ) -> str:
79
+ m1_name: str = "model 1",
80
+ m2_name: str = "model 2",
81
+ include_rhs: bool = True,
82
+ ) -> MarkdownReport:
54
83
  """Generate a markdown report comparing two models.
55
84
 
56
- Parameters
57
- ----------
58
- m1
59
- The first model to compare
60
- m2
61
- The second model to compare
62
- analyses
63
- A list of functions that analyze both models and return a report section with image
64
- rel_change
65
- The relative change threshold for numerical differences
66
- img_path
67
- The path to save images
68
-
69
- Returns
70
- -------
71
- str
72
- Markdown formatted report comparing the two models
73
-
74
- Examples
75
- --------
76
- >>> from mxlpy import Model
77
- >>> m1 = Model().add_parameter("k1", 0.1).add_variable("S", 1.0)
78
- >>> m2 = Model().add_parameter("k1", 0.2).add_variable("S", 1.0)
79
- >>> report = markdown(m1, m2)
80
- >>> "Parameters" in report and "k1" in report
81
- True
82
-
83
- >>> # With custom analysis function
84
- >>> def custom_analysis(m1, m2, path):
85
- ... return "## Custom analysis", path / "image.png"
86
- >>> report = markdown(m1, m2, analyses=[custom_analysis])
87
- >>> "Custom analysis" in report
88
- True
85
+ Args:
86
+ m1: The first model to compare
87
+ m2: The second model to compare
88
+ analyses: A list of functions that analyze both models and return a report section with image
89
+ rel_change: The relative change threshold for numerical differences
90
+ img_path: The path to save images
91
+ m1_name: Name of the first model
92
+ m2_name: Name of the second model
93
+ include_rhs: Whether to include numerical differences in the right hand side
94
+
95
+ Returns:
96
+ str: Markdown formatted report comparing the two models
97
+
98
+ Examples:
99
+ >>> from mxlpy import Model
100
+ >>> m1 = Model().add_parameter("k1", 0.1).add_variable("S", 1.0)
101
+ >>> m2 = Model().add_parameter("k1", 0.2).add_variable("S", 1.0)
102
+ >>> report = markdown(m1, m2)
103
+ >>> "Parameters" in report and "k1" in report
104
+ True
105
+
106
+ >>> # With custom analysis function
107
+ >>> def custom_analysis(m1, m2, path):
108
+ ... return "## Custom analysis", path / "image.png"
109
+ >>> report = markdown(m1, m2, analyses=[custom_analysis])
110
+ >>> "Custom analysis" in report
111
+ True
89
112
 
90
113
  """
91
114
  content: list[str] = [
@@ -101,20 +124,20 @@ def markdown(
101
124
  # Model stats
102
125
  content.extend(
103
126
  [
104
- "| Model component | Old | New |",
127
+ f"| Model component | {m1_name} | {m2_name} |",
105
128
  "| --- | --- | --- |",
106
- f"| variables | {len(m1.variables)} | {len(m2.variables)}|",
107
- f"| parameters | {len(m1.parameters)} | {len(m2.parameters)}|",
108
- f"| derived parameters | {len(m1.derived_parameters)} | {len(m2.derived_parameters)}|",
109
- f"| derived variables | {len(m1.derived_variables)} | {len(m2.derived_variables)}|",
110
- f"| reactions | {len(m1.reactions)} | {len(m2.reactions)}|",
129
+ f"| variables | {len(m1.get_raw_parameters())} | {len(m2.get_raw_parameters())}|",
130
+ f"| parameters | {len(m1.get_parameter_values())} | {len(m2.get_parameter_values())}|",
131
+ f"| derived parameters | {len(m1.get_derived_parameters())} | {len(m2.get_derived_parameters())}|",
132
+ f"| derived variables | {len(m1.get_derived_variables())} | {len(m2.get_derived_variables())}|",
133
+ f"| reactions | {len(m1.get_raw_reactions())} | {len(m2.get_raw_reactions())}|",
111
134
  f"| surrogates | {len(m1._surrogates)} | {len(m2._surrogates)}|", # noqa: SLF001
112
135
  ]
113
136
  )
114
137
 
115
138
  # Variables
116
139
  new_variables, removed_variables, changed_variables = _new_removed_changed(
117
- m1.variables, m2.variables
140
+ m1.get_initial_conditions(), m2.get_initial_conditions()
118
141
  )
119
142
  variables = []
120
143
  variables.extend(
@@ -131,8 +154,8 @@ def markdown(
131
154
  if len(variables) >= 1:
132
155
  content.extend(
133
156
  (
134
- "## Variables\n\n",
135
- "| Name | Old Value | New Value |",
157
+ "\n## Variables\n",
158
+ f"| Name | {m1_name} | {m2_name} |",
136
159
  "| ---- | --------- | --------- |",
137
160
  )
138
161
  )
@@ -140,7 +163,7 @@ def markdown(
140
163
 
141
164
  # Parameters
142
165
  new_parameters, removed_parameters, changed_parameters = _new_removed_changed(
143
- m1.parameters, m2.parameters
166
+ m1.get_parameter_values(), m2.get_parameter_values()
144
167
  )
145
168
  pars = []
146
169
  pars.extend(
@@ -157,8 +180,8 @@ def markdown(
157
180
  if len(pars) >= 1:
158
181
  content.extend(
159
182
  (
160
- "## Parameters\n\n",
161
- "| Name | Old Value | New Value |",
183
+ "\n## Parameters\n",
184
+ f"| Name | {m1_name} | {m2_name} |",
162
185
  "| ---- | --------- | --------- |",
163
186
  )
164
187
  )
@@ -166,17 +189,37 @@ def markdown(
166
189
 
167
190
  # Derived
168
191
  new_derived, removed_derived, changed_derived = _new_removed_changed(
169
- m1.derived, m2.derived
192
+ m1.get_raw_derived(),
193
+ m2.get_raw_derived(),
170
194
  )
171
195
  derived = []
172
196
  for k, v in new_derived.items():
173
- expr = sympy.latex(fn_to_sympy(v.fn, _list_of_symbols(v.args)))
174
- derived.append(f"| <span style='color:green'>{k}<span> | - | ${expr}$ |")
197
+ expr = _latex_view(
198
+ fn_to_sympy(
199
+ v.fn,
200
+ origin=k,
201
+ model_args=list_of_symbols(v.args),
202
+ )
203
+ )
204
+ derived.append(f"| <span style='color:green'>{k}<span> | - | {expr} |")
205
+
175
206
  for k, (v1, v2) in changed_derived.items():
176
- expr1 = sympy.latex(fn_to_sympy(v1.fn, _list_of_symbols(v1.args)))
177
- expr2 = sympy.latex(fn_to_sympy(v2.fn, _list_of_symbols(v2.args)))
207
+ expr1 = _latex_view(
208
+ fn_to_sympy(
209
+ v1.fn,
210
+ origin=k,
211
+ model_args=list_of_symbols(v1.args),
212
+ )
213
+ )
214
+ expr2 = _latex_view(
215
+ fn_to_sympy(
216
+ v2.fn,
217
+ origin=k,
218
+ model_args=list_of_symbols(v2.args),
219
+ )
220
+ )
178
221
  derived.append(
179
- f"| <span style='color: orange'>{k}</span> | ${expr1}$ | ${expr2}$ |"
222
+ f"| <span style='color: orange'>{k}</span> | {expr1} | {expr2} |"
180
223
  )
181
224
  derived.extend(
182
225
  f"| <span style='color:red'>{k}</span> | - | - |" for k in removed_derived
@@ -184,8 +227,8 @@ def markdown(
184
227
  if len(derived) >= 1:
185
228
  content.extend(
186
229
  (
187
- "## Derived\n\n",
188
- "| Name | Old Value | New Value |",
230
+ "\n## Derived\n",
231
+ f"| Name | {m1_name} | {m2_name} |",
189
232
  "| ---- | --------- | --------- |",
190
233
  )
191
234
  )
@@ -193,17 +236,36 @@ def markdown(
193
236
 
194
237
  # Reactions
195
238
  new_reactions, removed_reactions, changed_reactions = _new_removed_changed(
196
- m1.reactions, m2.reactions
239
+ m1.get_raw_reactions(), m2.get_raw_reactions()
197
240
  )
198
241
  reactions = []
199
242
  for k, v in new_reactions.items():
200
- expr = sympy.latex(fn_to_sympy(v.fn, _list_of_symbols(v.args)))
201
- reactions.append(f"| <span style='color:green'>{k}<span> | - | ${expr}$ |")
243
+ expr = _latex_view(
244
+ fn_to_sympy(
245
+ v.fn,
246
+ origin=k,
247
+ model_args=list_of_symbols(v.args),
248
+ )
249
+ )
250
+ reactions.append(f"| <span style='color:green'>{k}<span> | - | {expr} |")
251
+
202
252
  for k, (v1, v2) in changed_reactions.items():
203
- expr1 = sympy.latex(fn_to_sympy(v1.fn, _list_of_symbols(v1.args)))
204
- expr2 = sympy.latex(fn_to_sympy(v2.fn, _list_of_symbols(v2.args)))
253
+ expr1 = _latex_view(
254
+ fn_to_sympy(
255
+ v1.fn,
256
+ origin=k,
257
+ model_args=list_of_symbols(v1.args),
258
+ )
259
+ )
260
+ expr2 = _latex_view(
261
+ fn_to_sympy(
262
+ v2.fn,
263
+ origin=k,
264
+ model_args=list_of_symbols(v2.args),
265
+ )
266
+ )
205
267
  reactions.append(
206
- f"| <span style='color: orange'>{k}</span> | ${expr1}$ | ${expr2}$ |"
268
+ f"| <span style='color: orange'>{k}</span> | {expr1} | {expr2} |"
207
269
  )
208
270
  reactions.extend(
209
271
  f"| <span style='color:red'>{k}</span> | - | - |" for k in removed_reactions
@@ -212,8 +274,8 @@ def markdown(
212
274
  if len(reactions) >= 1:
213
275
  content.extend(
214
276
  (
215
- "## Reactions\n\n",
216
- "| Name | Old Value | New Value |",
277
+ "\n## Reactions\n",
278
+ f"| Name | {m1_name} | {m2_name} |",
217
279
  "| ---- | --------- | --------- |",
218
280
  )
219
281
  )
@@ -221,8 +283,8 @@ def markdown(
221
283
 
222
284
  # Now check for any numerical differences
223
285
  dependent = []
224
- d1 = m1.get_dependent()
225
- d2 = m2.get_dependent()
286
+ d1 = m1.get_args()
287
+ d2 = m2.get_args()
226
288
  rel_diff = ((d1 - d2) / d1).dropna()
227
289
  for k, v in rel_diff.loc[rel_diff.abs() >= rel_change].items():
228
290
  k = cast(str, k)
@@ -233,30 +295,31 @@ def markdown(
233
295
  content.extend(
234
296
  (
235
297
  "## Numerical differences of dependent values\n\n",
236
- "| Name | Old Value | New Value | Relative Change | ",
298
+ f"| Name | {m1_name} | {m2_name} | Relative Change | ",
237
299
  "| ---- | --------- | --------- | --------------- | ",
238
300
  )
239
301
  )
240
302
  content.append("\n".join(dependent))
241
303
 
242
- rhs = []
243
- r1 = m1.get_right_hand_side()
244
- r2 = m2.get_right_hand_side()
245
- rel_diff = ((r1 - r2) / r1).dropna()
246
- for k, v in rel_diff.loc[rel_diff.abs() >= rel_change].items():
247
- k = cast(str, k)
248
- rhs.append(
249
- f"| <span style='color:orange'>{k}</span> | {r1[k]:.2f} | {r2[k]:.2f} | {v:.1%} |"
250
- )
251
- if len(rhs) >= 1:
252
- content.extend(
253
- (
254
- "## Numerical differences of right hand side values\n\n",
255
- "| Name | Old Value | New Value | Relative Change | ",
256
- "| ---- | --------- | --------- | --------------- | ",
304
+ if include_rhs:
305
+ rhs = []
306
+ r1 = m1.get_right_hand_side()
307
+ r2 = m2.get_right_hand_side()
308
+ rel_diff = ((r1 - r2) / r1).dropna()
309
+ for k, v in rel_diff.loc[rel_diff.abs() >= rel_change].items():
310
+ k = cast(str, k)
311
+ rhs.append(
312
+ f"| <span style='color:orange'>{k}</span> | {r1[k]:.2f} | {r2[k]:.2f} | {v:.1%} |"
257
313
  )
258
- )
259
- content.append("\n".join(rhs))
314
+ if len(rhs) >= 1:
315
+ content.extend(
316
+ (
317
+ "\n## Numerical differences of right hand side values\n",
318
+ f"| Name | {m1_name} | {m2_name} | Relative Change | ",
319
+ "| ---- | --------- | --------- | --------------- | ",
320
+ )
321
+ )
322
+ content.append("\n".join(rhs))
260
323
 
261
324
  # Comparison functions
262
325
  if analyses is not None:
@@ -266,4 +329,4 @@ def markdown(
266
329
  # content.append(f"![{name}]({img_path})")
267
330
  content.append(f"<img src='{img_path}' alt='{name}' width='500'/>")
268
331
 
269
- return "\n".join(content)
332
+ return MarkdownReport(data="\n".join(content))
mxlpy/sbml/_export.py CHANGED
@@ -10,7 +10,7 @@ import numpy as np
10
10
 
11
11
  from mxlpy.meta.source_tools import get_fn_ast
12
12
  from mxlpy.sbml._data import AtomicUnit, Compartment
13
- from mxlpy.types import Derived
13
+ from mxlpy.types import Derived, InitialAssignment
14
14
 
15
15
  if TYPE_CHECKING:
16
16
  from collections.abc import Callable
@@ -440,30 +440,32 @@ def _create_sbml_variables(
440
440
  sbml_model : libsbml.Model
441
441
 
442
442
  """
443
- for name, value in model.variables.items():
443
+ for name, variable in model.get_raw_variables().items():
444
444
  cpd = sbml_model.createSpecies()
445
445
  cpd.setId(_convert_id_to_sbml(id_=name, prefix="CPD"))
446
446
 
447
447
  cpd.setConstant(False)
448
448
  cpd.setBoundaryCondition(False)
449
449
  cpd.setHasOnlySubstanceUnits(False)
450
- if isinstance(value, Derived):
450
+ # cpd.setUnit() # FIXME: implement
451
+ if isinstance((init := variable.initial_value), InitialAssignment):
451
452
  ar = sbml_model.createInitialAssignment()
452
453
  ar.setId(_convert_id_to_sbml(id_=name, prefix="IA"))
453
454
  ar.setName(_convert_id_to_sbml(id_=name, prefix="IA"))
454
455
  ar.setVariable(_convert_id_to_sbml(id_=name, prefix="IA"))
455
- ar.setMath(_sbmlify_fn(value.fn, value.args))
456
+ ar.setMath(_sbmlify_fn(init.fn, init.args))
456
457
  else:
457
- cpd.setInitialAmount(float(value))
458
+ cpd.setInitialAmount(float(init))
458
459
 
459
460
 
460
461
  def _create_sbml_derived_variables(*, model: Model, sbml_model: libsbml.Model) -> None:
461
- for name, dv in model.derived_variables.items():
462
+ for name, dv in model.get_derived_variables().items():
462
463
  sbml_ar = sbml_model.createAssignmentRule()
463
464
  sbml_ar.setId(_convert_id_to_sbml(id_=name, prefix="AR"))
464
465
  sbml_ar.setName(_convert_id_to_sbml(id_=name, prefix="AR"))
465
466
  sbml_ar.setVariable(_convert_id_to_sbml(id_=name, prefix="AR"))
466
467
  sbml_ar.setMath(_sbmlify_fn(dv.fn, dv.args))
468
+ # cpd.setUnit() # FIXME: implement
467
469
 
468
470
 
469
471
  def _create_derived_parameter(
@@ -477,6 +479,7 @@ def _create_derived_parameter(
477
479
  ar.setName(_convert_id_to_sbml(id_=name, prefix="AR"))
478
480
  ar.setVariable(_convert_id_to_sbml(id_=name, prefix="AR"))
479
481
  ar.setMath(_sbmlify_fn(dp.fn, dp.args))
482
+ # cpd.setUnit() # FIXME: implement
480
483
 
481
484
 
482
485
  def _create_sbml_parameters(
@@ -491,15 +494,23 @@ def _create_sbml_parameters(
491
494
  sbml_model : libsbml.Model
492
495
 
493
496
  """
494
- for parameter_id, value in model.parameters.items():
497
+ for name, value in model.get_raw_parameters().items():
495
498
  k = sbml_model.createParameter()
496
- k.setId(_convert_id_to_sbml(id_=parameter_id, prefix="PAR"))
499
+ k.setId(_convert_id_to_sbml(id_=name, prefix="PAR"))
497
500
  k.setConstant(True)
498
- k.setValue(float(value))
501
+
502
+ if isinstance((init := value.value), InitialAssignment):
503
+ ar = sbml_model.createInitialAssignment()
504
+ ar.setId(_convert_id_to_sbml(id_=name, prefix="IA"))
505
+ ar.setName(_convert_id_to_sbml(id_=name, prefix="IA"))
506
+ ar.setVariable(_convert_id_to_sbml(id_=name, prefix="IA"))
507
+ ar.setMath(_sbmlify_fn(init.fn, init.args))
508
+ else:
509
+ k.setValue(float(init))
499
510
 
500
511
 
501
512
  def _create_sbml_derived_parameters(*, model: Model, sbml_model: libsbml.Model) -> None:
502
- for name, dp in model.derived_parameters.items():
513
+ for name, dp in model.get_derived_parameters().items():
503
514
  _create_derived_parameter(sbml_model, name, dp)
504
515
 
505
516
 
@@ -509,7 +520,7 @@ def _create_sbml_reactions(
509
520
  sbml_model: libsbml.Model,
510
521
  ) -> None:
511
522
  """Create the reactions for the sbml model."""
512
- for name, rxn in model.reactions.items():
523
+ for name, rxn in model.get_raw_reactions().items():
513
524
  sbml_rxn = sbml_model.createReaction()
514
525
  sbml_rxn.setId(_convert_id_to_sbml(id_=name, prefix="RXN"))
515
526
  sbml_rxn.setName(name)