Skip to content

Region variables: VolumeRegion/MembraneRegion equations in the math model, VCML, and results - #60

Merged
jcschaff merged 1 commit into
mainfrom
feat/region-equations
Sep 27, 2026
Merged

jcschaff merged 1 commit into
mainfrom
feat/region-equations

Conversation

@jcschaff

Copy link
Copy Markdown
Member

Why

VCell writes a well-mixed species in a spatial application, and a whole-membrane quantity such as the membrane potential, as a region variable. A region variable has one value per connected region and is governed by a VolumeRegionEquation or MembraneRegionEquation. pyvcell read the variables but dropped their equations. Everything downstream saw undeclared variables: in the vcell-fenics coverage survey, 29 of 600 sampled public applications failed this way (virtualcell/vcell-fenics#188).

Result processing also crashed on any such model. A volume function that uses the region variable had no numexpr binding, so write_zarr raised KeyError: 's1'.

What

  • Math model (models_math.py): VolumeRegionEquation (uniform_rate, volume_rate, initial, solution_type, solution) on CompartmentSubDomain, and MembraneRegionEquation (with membrane_rate) on MembraneSubDomain. Both are exported from pyvcell.vcml.
  • VCML read/write, matching VCell's XmlReader / Xmlproducer: UniformRate and the region rate (written as 0.0 when absent), Initial, and <SolutionType Type="unknown|exact">, whose text is an exact solution.
  • SpeciesMapping flags: force_constant, well_mixed and force_continuous (the LocalizedCompoundSpec attributes). The writer used to drop them, so any round trip, including vc.simulate, turned a well-mixed species spatial and unclamped a clamped species.
  • Results (zarr_writer.py), per the "push it down to each element" convention used for x/y/z/t: each domain-qualified VOLUME_REGION state variable is replicated onto every voxel of its region (values[volume_region_map]). It becomes its own channel, placed after the volume variables, and a binding for the volume functions. The solver's built-in region quantities (vcRegionVolume, no domain) aren't state, so they stay out. Membrane-region variables stay out of the zarr for now, like membrane variables, since it's a volume grid.

Tests

  • New fixture SmallSpatialProject_3D_well_mixed.vcml (20 KB): the existing 3D fixture with s1 (volume) and s2 (membrane) marked well-mixed, and its math regenerated by libvcell. s1 becomes a VolumeRegionVariable and s2 a MembraneRegionVariable.
  • Reading: the region equations come back as expected.
  • Round trips: the math round trip is extended to this fixture, and the species-mapping flags round-trip (including a clamped species).
  • fvsolver run: the s1 channel exists; J_r0 = f(s0, s1) evaluates; and at every output time s1 is constant across subdomain0 and equal to the solver's per-region value.
  • Suite: pytest gives 115 passed and 11 skipped, and mypy and pre-commit are clean.

🤖 Generated with Claude Code

…write, and results

VCell writes a well-mixed species in a spatial application, and a
whole-membrane quantity such as the membrane potential, as a region
variable: one value per connected region, with a VolumeRegionEquation or
MembraneRegionEquation. pyvcell dropped those equations on read, so
consumers saw undeclared variables. Result processing also crashed on any
model with one, because a volume function referencing it had no numexpr
binding (KeyError).

- models_math: VolumeRegionEquation (uniform_rate, volume_rate, initial,
  solution_type, solution) on CompartmentSubDomain, and
  MembraneRegionEquation (with membrane_rate) on MembraneSubDomain. Both
  are exported from pyvcell.vcml.
- vcml_reader / vcml_writer: read and write them as VCell's XmlReader and
  Xmlproducer do: UniformRate and the region rate (default 0.0), Initial,
  and <SolutionType Type="unknown|exact">, whose text is an exact solution.
- SpeciesMapping: force_constant, well_mixed and force_continuous, the
  LocalizedCompoundSpec flags. The writer used to drop them, so a round
  trip (or vc.simulate) turned a well-mixed species spatial and unclamped a
  clamped species.
- zarr_writer: each domain-qualified VOLUME_REGION state variable is
  replicated onto every voxel of its region, values[volume_region_map].
  It becomes a channel after the volume variables and a binding for the
  functions, as x/y/z/t already are. Membrane-region variables stay out of
  the zarr, like membrane variables (it is a volume grid).

Tests use a new 20 KB fixture: SmallSpatialProject_3D with s1 (volume) and
s2 (membrane) well-mixed, math regenerated by libvcell. They cover the
region equations as read, the math and flag round trips, and an fvsolver
run whose s1 channel is constant across subdomain0 and equals the solver's
per-region value at every output time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant