Skip to content

Site percolation

Site percolation probed by a fire lit along the top row, which spans the lattice above p_c = 0.5927.

  • Name: site_percolation
  • Field: physics/statistical_mechanics
  • Formalism: lattice dynamics
  • References: Broadbent and Hammersley 1957; Stauffer and Aharony 1994; Charbonneau 2017

Factory

@register_model(
    field='physics/statistical_mechanics',
    tags=('lattice', 'percolation', 'phase transition', 'fire'),
    references=('Broadbent and Hammersley 1957', 'Stauffer and Aharony 1994', 'Charbonneau 2017'),
    defaults={'until': 200, 'dt': 1},
)
def site_percolation(
    shape: Sequence[int] = (64, 64),
    *,
    p: float = 0.6,
    random_state: int | np.random.Generator | None = 0,
) -> System:
    """Return site percolation probed by a fire lit along the top row, which spans the lattice above `p_c = 0.5927`.

    Sites are occupied with probability `p`, and the fire burns the clusters connected to the top row, one layer of
    sites per step.

    Args:
        shape:
            The number of rows and columns.

        p:
            The occupation probability.

        random_state:
            The seed or generator of the occupations.

    Returns:
        system:
            The model, with the covers `lattice.cover`.

    Examples:
        >>> from skcomplex.models.physics.statistical_mechanics import site_percolation
        >>> site_percolation((10, 10)).elements[0].check().prepare().size_
        100
    """
    shape = tuple(shape)
    cover = (np.random.default_rng(random_state).random(shape) < p).astype(float)
    cover[0] *= BURNING
    space = LatticeSpace(shape, boundary='fixed')
    return System(
        elements=[LatticeElement('lattice', element=PercolationSiteElement, space=space, values={'cover': cover})],
    )

Components

class PercolationSiteElement(Element):
    """Site of a percolation lattice: empty at 0, occupied at 1, burning at 2, or burnt at 3.

    Fire spreads from burning sites to their occupied neighbours and burns out in one step, so a fire lit along one
    edge burns exactly the clusters that touch that edge.

    Args:
        label:
            The label of the site or the lattice group.

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

        cover:
            The initial cover, 0, 1, 2, or 3.

    Examples:
        >>> from skcomplex.models.physics.statistical_mechanics import PercolationSiteElement
        >>> PercolationSiteElement('site').cover
        0.0
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        cover: Annotated[float, StateSpec(domain=NN)] = 0.0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.cover = cover

    def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
        """Return the spread and burning out of the fire.

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

        Returns:
            laws:
                The laws of the component.
        """
        return [LocalRuleLaw(s.cover, VonNeumannNeighborhood(center=True), PercolationSiteElement._burn)]

    @staticmethod
    def _burn(cover: Array, *around: Array) -> Array:
        """Return the next cover of a site, which burns out, catches fire from a burning neighbour, or stays."""
        catches = (cover == 1) & np.any(np.stack(around) == BURNING, axis=0)
        return np.where(cover == BURNING, BURNT, np.where(catches, BURNING, cover))