Multi-block and multi-species systems¶
A system in pops is a set of blocks, one per model or species, that all live on a
single mesh and couple through one shared elliptic solve. This page explains what a
block is, why several blocks share the same hierarchy and auxiliary channel, and how
the coupling between species reduces to a summed right-hand side.
One block per model or species¶
A block is a named carrier for one model on the mesh. On the public path you declare it with
case.block(name, physics=...), once per species, and pops.bind installs it through the
low-level runtime (add_block for native bricks, add_equation for a compiled DSL,
domain-specific language, model). The same install seam exists on pops.System (single level) and
on pops.AmrSystem (refined); the multi-block runtime is identical on both.
Each block keeps its own numerics. A block fixes its spatial scheme (limiter, flux,
reconstruction), its time treatment (explicit or imex), and its multirate
(substeps / stride). Two species can therefore advance with different schemes
inside one run. For what a model is and how you write one, see
the physical model.
The block name is the index for state access: set_density(name), mass(name),
and density(name) all take the name, so blocks never collide.
Coupled sources between species¶
Beyond the field coupling, you can wire an explicit inter-species source with
add_coupled_source for terms such as ionization or collisions. These read cell by
cell on the shared layout, so no interpolation is needed between species. When you
build the two contributions as exactly opposite terms, the pair mass is conserved to
machine precision.
A coupling resolves the component it targets by role, not by a literal index. It can
use a user-defined role label, resolved via index_of(name), in addition to the
canonical VariableRole enum, so a block can expose a component whose role sits
outside the enum. Resolution is strict and fail-loud: if a block declares roles but
not the one a named coupling (collision, thermal exchange, ionization) requires, the
lookup raises rather than silently falling back to a default component. Declaring the
roles in the model is what makes this work; see
the symbolic DSL reference.
Conservation across blocks¶
Conservation is checked per block, not only globally. On AMR each block owns its own flux registers, so reflux and average-down keep every species conservative on its own, and the mass of each block survives a regrid. The shared hierarchy is regridded once from the union of all blocks’ refinement tags, then transfers run per block on the new common layout. For how the refinement and conservation operators work, see adaptive mesh refinement and the ALGORITHMS guide.
What to keep in mind¶
The shared Poisson is the reason multi-block stays cheap: one elliptic solve serves every species, and the summed right-hand side is the whole coupling. The price is the identical-layout requirement, which the construction guard enforces up front. Distinct hierarchies per species, with conservative projections between them, are a design frontier and not available today; see the ARCHITECTURE guide and current limitations.