Skip to content

Two-phase zone exchange

Independent zones of a two-phase apparatus, each exchanging particles with the rest of the apparatus.

  • Name: two_phase_zone_exchange
  • Field: physics/statistical_mechanics
  • Formalism: jump process
  • References: Barsky 2020

Factory

@register_model(
    field='physics/statistical_mechanics',
    tags=('jump process', 'binomial', 'fluctuations', 'two-phase flow'),
    references=('Barsky 2020',),
    defaults={'until': 20.0, 'dt': 0.5},
)
def two_phase_zone_exchange(
    n_zones: int = 100,
    *,
    Na: float = 100.0,
    p: float = 0.1,
    k: float = 1.0,
) -> System:
    """Return independent zones of a two-phase apparatus, each exchanging particles with the rest of the apparatus.

    The zones start empty and their numbers of particles settle on the binomial distribution of mean `Na p` and
    variance `Na p (1 - p)`.

    Args:
        n_zones:
            The number of independent zones.

        Na:
            The number of particles in the apparatus and a zone.

        p:
            The volume fraction of a zone.

        k:
            The rate of exchange.

    Returns:
        system:
            The model, with the numbers `zones.N`.

    Examples:
        >>> from skcomplex.models.physics.statistical_mechanics import two_phase_zone_exchange
        >>> two_phase_zone_exchange(5).elements[0].check().prepare().size_
        5
    """
    return System(
        elements=[GroupElement('zones', element=ZoneElement, size=n_zones, values={'Na': Na, 'p': p, 'k': k})],
    )

Components

class ZoneElement(Element):
    """Small zone of a two-phase apparatus that exchanges particles with the rest of it.

    Every one of the `Na` particles enters the zone at the rate `k p` and leaves it at the rate `k (1 - p)`, where
    `p = dV / V` is the volume fraction of the zone, so the number in the zone is binomial with mean `Na p`.

    Args:
        label:
            The label of the zone or the group.

        visual:
            How the element is drawn, or `None` for `default_visual_`.

        N:
            The initial number of particles in the zone.

        Na:
            The number of particles in the apparatus and the zone.

        p:
            The volume fraction of the zone.

        k:
            The rate of exchange.

    Examples:
        >>> from skcomplex.models.physics.statistical_mechanics import ZoneElement
        >>> ZoneElement('zone').Na
        100.0
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        N: Annotated[float, StateSpec(domain=NN)] = 0.0,
        Na: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 100.0,
        p: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.1,
        k: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.N = N
        self.Na = Na
        self.p = p
        self.k = k

    def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
        """Return the entries into and exits from the zone.

        Args:
            s:
                The namespace of the symbols the laws can use.

        Returns:
            laws:
                The laws of the component.
        """
        return [
            JumpLaw(s.k * s.p * (s.Na - s.N), {s.N: 1}),
            JumpLaw(s.k * (1 - s.p) * s.N, {s.N: -1}),
        ]