77import logging
88import re
99from collections .abc import Callable , Sequence
10+ from contextlib import suppress
1011from itertools import chain
1112from operator import itemgetter
1213from typing import TYPE_CHECKING
@@ -651,9 +652,12 @@ def num_events_solver(self) -> int:
651652 :return:
652653 number of event symbols (length of the root vector in AMICI)
653654 """
654- constant_syms = self ._static_symbols (["k" , "p" , "w" ])
655+ # TODO(performance): we could include constant `x` here as well
656+ # (dx/dt = 0 AND not target of event assignments)
657+ # this will require passing `x` to `fexplicit_roots`
658+ static_syms = self ._static_symbols (["k" , "p" , "w" ])
655659 return sum (
656- not event .has_explicit_trigger_times (constant_syms )
660+ not event .has_explicit_trigger_times (static_syms )
657661 for event in self .events ()
658662 )
659663
@@ -1137,6 +1141,7 @@ def generate_basic_variables(self) -> None:
11371141 Generates the symbolic identifiers for all variables in
11381142 ``DEModel._variable_prototype``
11391143 """
1144+ self .parse_events ()
11401145 self ._reorder_events ()
11411146
11421147 for var in self ._variable_prototype :
@@ -1163,11 +1168,6 @@ def parse_events(self) -> None:
11631168 for expr in self ._expressions :
11641169 expr .set_val (self ._process_heavisides (expr .get_val (), roots ))
11651170
1166- # remove all possible Heavisides from roots, which may arise from
1167- # the substitution of `'w'` in `_collect_heaviside_roots`
1168- for root in roots :
1169- root .set_val (self ._process_heavisides (root .get_val (), roots ))
1170-
11711171 # Now add the found roots to the model components
11721172 for root in roots :
11731173 # skip roots of SBML events, as these have already been added
@@ -1181,20 +1181,57 @@ def _reorder_events(self) -> None:
11811181 Re-order events - first those that require root tracking,
11821182 then the others.
11831183 """
1184- constant_syms = self ._static_symbols (["k" , "p" , "w" ])
1184+ # Currently, the C++ simulations relies on the order of events:
1185+ # those that require numerical root-finding must come first, then
1186+ # those with explicit trigger times that don't depend on dynamic
1187+ # variables.
1188+ # TODO: This re-ordering here is a bit ugly, because we already need
1189+ # to generate certain model equations to perform this ordering.
1190+ # Ideally, we'd split froot into explicit and implicit parts during
1191+ # code generation instead (as already done for jax models).
1192+ static_syms = self ._static_symbols (["k" , "p" , "w" ])
1193+
1194+ # ensure that we don't have computed any root-related symbols/equations
1195+ # yet, because the re-ordering might invalidate them
1196+ # check after `self._static_symbols` which itself generates certain
1197+ # equations
1198+ if (
1199+ generated := set (self ._syms )
1200+ | set (self ._eqs )
1201+ | set (self ._sparsesyms )
1202+ | set (self ._sparseeqs )
1203+ ) and (
1204+ "root" in generated
1205+ or any (
1206+ name .startswith ("droot" ) or name .endswith ("droot" )
1207+ for name in generated
1208+ )
1209+ ):
1210+ raise AssertionError (
1211+ "This function must be called before computing any "
1212+ "root-related symbols/equations. "
1213+ "The following symbols/equations are already "
1214+ f"generated: { generated } "
1215+ )
1216+
11851217 self ._events = list (
11861218 chain (
11871219 itertools .filterfalse (
1188- lambda e : e .has_explicit_trigger_times (constant_syms ),
1220+ lambda e : e .has_explicit_trigger_times (static_syms ),
11891221 self ._events ,
11901222 ),
11911223 filter (
1192- lambda e : e .has_explicit_trigger_times (constant_syms ),
1224+ lambda e : e .has_explicit_trigger_times (static_syms ),
11931225 self ._events ,
11941226 ),
11951227 )
11961228 )
11971229
1230+ # regenerate after re-ordering
1231+ with suppress (KeyError ):
1232+ del self ._syms ["h" ]
1233+ self .sym ("h" )
1234+
11981235 def get_appearance_counts (self , idxs : list [int ]) -> list [int ]:
11991236 """
12001237 Counts how often a state appears in the time derivative of
0 commit comments