Repository navigation
feat: run non-spatial simulations on VCell's Sundials ODE solver (simulate_ode) - #65
Merged
Merged
Conversation
…ulate_ode) vc.simulate_ode(biomodel, simulation) runs a non-spatial simulation on CVODE, IDA or Combined IDA/CVODE: libvcell (>= 0.0.20, vcml_to_ode_input) writes the solver input and pyvcell-odesolver (the new `ode` extra) solves it; the .ida table comes back as an OdeResult (times, variable_names, values). vc.simulate_ode_vcml(vcml, name) does the same for a VCML document as-is, without reading it into a Biomodel, so documents pyvcell cannot represent (VCML 0.4, 2009) run too. Reading and writing non-spatial simulations, which this needs: - The reader skipped every simulation without a MeshSpecification, and every one without OutputTimeStep. Simulation.mesh_size is now optional (None: non-spatial), and the output is one of output_time_step, keep_every + keep_at_most (VCell's ODE default: 83% of the ODE simulations in a 2,730-model VCell corpus) or output_times; exactly one is set. - The writer hard-coded the tolerances, the maximum time step and the output step. Simulation gains absolute_tolerance, relative_tolerance and max_time_step, read and written; their defaults are the former literals, written with the same spelling, so existing output is unchanged. - MathOverrides were dropped (written empty): a re-saved or re-run model used its default parameters, not its simulation's. 53% of the corpus has them. Simulation.math_overrides keeps each <Constant> verbatim, parameter scans (ConstantArraySpec) included. - MassConservationModelReduction was dropped, so VCell's default (reduce) applied and conserved species left the results. Application gains mass_conservation_model_reduction (None: VCell's default, as before). Application.add_ode_sim(...) adds an ODE simulation to a non-spatial application. Tests: 12 in tests/vcml/test_ode_simulation.py, among them a fidelity test (the original document and pyvcell's rewrite give the same solver settings and the same answer), which found the two dropped settings. New fixture: public BioModel 31668439 (VCML 0.4, IDA), downloaded anonymously from the VCell API. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A Biomodel now keeps its <ModelUnitSystem> (Model.unit_system, the attributes verbatim) and writes it back; it used to be dropped, so every write put the model in VCell's default units and VCell derived different math. Public BioModel 266638414 (dimensionless units, lumped kinetics) gained a KMOLE factor and 102 output points became 24; 215 of 2,730 VCell corpus models have a non-default unit system. No unit system changes unless asked: - unit_system="vcell" (load_sbml_*, load_antimony_*) converts to VCell's units, as before. - A model whose SBML leaves its units undefined (SBML Level 3 has no defaults; Antimony writes Level 3) now fails to load unless assume_units says what its numbers are: "vcell" (read as uM, um, s; molecules/um2 on membranes: the old implicit behaviour, now stated) or "sbml_l2" (SBML Level 2's defaults: mol, litre, m2, m, s; only the undefined units are filled). "vcell" refuses a model that declares some units. The two combine: assume_units="sbml_l2", unit_system="vcell" converts mol/litre to uM. load_antimony_* gain both arguments, load_sbml_url too. Callers updated: the guides, their notebooks, the sysbio notebooks and the example script load their unitless Antimony with assume_units="vcell", so their results are unchanged; docs/guides/sbml-models.md describes units. Two tests asserted variable names from the dropped unit system (s0_init_uM); their fixture is in umol/l, so VCell now names them s0_init_umol_l_1 (the same values). Co-Authored-By: Claude Opus 5.5 <[email protected]>
Member
Author
|
Corpus sweep on this branch ( Before this PR, none of the 200 could run through pyvcell: the reader dropped every non-spatial simulation.
Where pyvcell's model still falls short: follow-ups, none of them regressions.
🤖 Generated with Claude Code |
…note libvcell appends "; stored math (...)" when it used a rule-based application's stored math (virtualcell/libvcell: rule-based stored math). The file name is the part before the ';'; 0.0.20 sends just the name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…xtra libvcell 0.0.20 is on PyPI with all of 0.0.19's wheels (macOS arm64, Linux 2_28/2_34 x86_64 and aarch64, Windows), so CI now runs the ODE solve tests instead of skipping them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…n the native extra Co-Authored-By: Claude Opus 5.5 <[email protected]>
…+ runtime) 0.9.6's Linux wheels failed to load on a stock Linux (libc++.so.1: cannot open shared object file). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
pyvcell can now run non-spatial simulations on VCell's Sundials ODE solvers: CVODE, IDA, and Combined IDA/CVODE.
vcml_to_ode_input, Add vcml_to_ode_input and sbml_to_ode_input entrypoints (0.0.20) libvcell#27). Thenativeextra now needs libvcell >= 0.0.23:vcml_to_ode_input;pyvcell-odesolver, built from virtualcell/vcell-ode, solves it. It's the newodeextra and needs >= 0.9.7: 0.9.6's Linux wheels didn't bundle libc++ and failed to load on a stock Linux (fix(ci): bundle the C++ runtime in the Linux wheels (auditwheel); version 0.9.7 vcell-ode#12).API
simulate_odechecks the model first: the simulation must be non-spatial and on an ODE solver. It then imports the optional parts lazily, with install or upgrade messages that name the extra or the libvcell version needed.Reading and writing non-spatial simulations
simulate_oderuns a model through pyvcell's VCML round trip, so these fixes were needed. The figures below come from a corpus of 2,730 VCell models (vcell-fenics/vcml_biomodels):Non-spatial simulations were dropped. The reader skipped every simulation without a
MeshSpecification, and every one withoutOutputTimeStep. Now:Simulation.mesh_sizeis optional, withNonemeaning non-spatial;output_time_step,keep_every+keep_at_most, oroutput_times.keep_every+keep_at_mostis VCell's ODE default, used by 83% of the corpus's ODE simulations.Solver settings were hard-coded.
absolute_tolerance,relative_toleranceandmax_time_stepare now read and written. Their defaults are the former literals, spelled the same way, so existing spatial output is byte-identical.MathOverrideswere dropped. They were written empty, so a re-saved or re-run model used its default parameters, not its simulation's. 53% of corpus models have overrides.Simulation.math_overrideskeeps each<Constant>verbatim, including parameter scans (ConstantArraySpec). In the test fixtureNonspatial_ODE.vcml,J_r0was 20 in the simulation and came back as 2.MassConservationModelReductionwas dropped. VCell's default (reduce) then applied, and conserved species disappeared from the results. 822 corpus applications set it tofalse.Application.mass_conservation_model_reductionkeeps it;Nonemeans VCell's default, as before.<ModelUnitSystem>was dropped. Every write put the model in VCell's default units, so VCell derived different math. Public BioModel 266638414, which uses dimensionless units and lumped kinetics, gained a KMOLE factor, and its 102 output points became 24. 8% of corpus models (215) use a non-default unit system.Model.unit_systemnow keeps it verbatim.Units: respected, and changed only when asked (behaviour change)
unit_system="vcell"onload_sbml_*andload_antimony_*converts to VCell's units, as before.assume_unitssays what its numbers are. SBML Level 3 has no default units, and that's what Antimony writes, so an Antimony model with no declared units falls in this case.assume_units="vcell"reads the numbers as µM, µm and s, with molecules/µm² on membranes. That's what pyvcell used to do silently. It refuses a model that declares some of its units.assume_units="sbml_l2"uses SBML Level 2's defaults (mol, litre, m², m, s), filling in only the units that are undefined.assume_units="sbml_l2", unit_system="vcell"reads mol/litre and converts to µM.assume_units="vcell", so their results are unchanged.docs/guides/sbml-models.mdgains a Units section.s0_init_uM). Their fixture is in µmol/l, so VCell now names its0_init_umol_l_1, with the same values.Tests
tests/vcml/test_ode_simulation.pyadds 17 tests:add_ode_simonly on a non-spatial application, with an ODE solver;.idareader;assume_units"vcell" and "sbml_l2", conversion afterwards, partial units);vcml_to_ode_inputand pyvcell-odesolver is installed. These include a fidelity test: the original document and pyvcell's rewrite must give the same solver settings and the same answer. That test is what found the two dropped settings.The new fixtures are public BioModels 31668439 (VCML 0.4, IDA) and 266638414 (dimensionless units), both downloaded anonymously from the VCell API.
CI: green on Python 3.12, 3.13 and 3.14 (Linux): 136 passed and 13 skipped, with all 17 ODE tests running against libvcell 0.0.23 and pyvcell-odesolver 0.9.7.
Corpus sweep: original vs pyvcell's round trip
The sweep took 200 random VCML 4 models with an ODE simulation from
vcell-fenics/vcml_biomodels. For each, it solved the original document (simulate_ode_vcml), then pyvcell's load-and-rewrite of it, and compared the results.failed to generate math: null(16) and a missingelectrical unit factorparameter in kinetics (14).🤖 Generated with Claude Code