Zero-range process
The zero-range process on a ring, which condenses above the critical density 1 / (b - 2) for b > 2.
- Name:
zero_range_process - Field:
physics/statistical_mechanics - Formalism: jump process
- References: Evans and Hanney 2005; Bertin 2021
Factory
@register_model(
field='physics/statistical_mechanics',
tags=('zero-range process', 'condensation', 'nonequilibrium', 'jump process'),
references=('Evans and Hanney 2005', 'Bertin 2021'),
defaults={'until': 2000.0, 'dt': 10.0, 'backend': NumbaBackend()},
)
def zero_range_process(
n_sites: int = 50,
*,
density: float = 3.0,
b: float = 4.0,
p: float = 1.0,
q: float = 0.0,
random_state: int | np.random.Generator | None = 0,
) -> System:
"""Return the zero-range process on a ring, which condenses above the critical density `1 / (b - 2)` for `b > 2`.
A particle leaves a site with `n` particles at rate `u(n) = 1 + b / n`, to the right with weight `p` and to the
left with weight `q`. The stationary distribution factorizes with the weights `f(n) = prod_k 1 / u(k)`, which
decay as `n^-b`. Above the critical density a fluid at density `1 / (b - 2)` coexists with one site that holds the
excess particles.
Args:
n_sites:
The number of sites.
density:
The number of particles per site.
b:
The strength of the decrease of the departure rate with the occupation.
p:
The rate scale of hops to the right.
q:
The rate scale of hops to the left.
random_state:
The seed or generator of the initial sites of the particles.
Returns:
system:
The model, with the occupations `sites.n`.
Examples:
>>> from skcomplex.models.physics.statistical_mechanics import zero_range_process
>>> system = zero_range_process(10, density=2.0)
>>> float(system.elements[0].values['n'].sum()), len(system.interactions)
(20.0, 1)
"""
occupations = _scatter(round(density * n_sites), n_sites, random_state)
sites = LatticeElement('sites', element=ParticleSiteElement, space=LatticeSpace(n_sites), values={'n': occupations})
return System(elements=[sites], interactions=[ZeroRangeHopsInteraction('hops', sites='sites', p=p, q=q, b=b)])
Components
class ParticleSiteElement(Element):
"""Site of a lattice of particles, holding the number `n` of particles or quanta on it.
Args:
label:
The label of the site or the group.
visual:
How the element is drawn, or `None` for `default_visual_`.
n:
The initial number of particles.
Examples:
>>> from skcomplex.models.physics.statistical_mechanics import ParticleSiteElement
>>> ParticleSiteElement('site', n=3.0).n
3.0
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
n: Annotated[float, StateSpec(domain=NN)] = 0.0,
) -> None:
super().__init__(label, visual=visual)
self.n = n
class ZeroRangeHopsInteraction(TransferInteraction):
"""Hops of one particle from every site of a ring, to the right at rate `p u(n)` and to the left at rate `q u(n)`.
The rate depends only on the occupation `n` of the departure site, through `u(n) = 1 + b / n`. It is a transfer of
one particle along every edge of the ring, to the right and back.
Args:
label:
The label of the hops.
sites:
The group of sites, in ring order.
p:
The rate scale of hops to the right.
q:
The rate scale of hops to the left.
b:
The strength of the decrease of `u(n)` with the occupation, 0 for a constant rate.
Examples:
>>> from skcomplex.models.physics.statistical_mechanics import ZeroRangeHopsInteraction
>>> ZeroRangeHopsInteraction('hops', sites='sites').b
4.0
"""
def __init__(
self,
label: str,
*,
sites: Annotated[str, RoleSpec()],
p: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
q: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
b: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 4.0,
) -> None:
super().__init__(
label,
source=sites,
target=sites,
state='n',
amount=1.0,
rate=self._get_right_rate,
reverse_rate=self._get_left_rate,
offset=1,
)
self.sites = sites
self.p = p
self.q = q
self.b = b
def _get_holders(self, namespace: SimpleNamespace) -> list[tuple[SimpleNamespace, SimpleNamespace]]:
"""Return the sites, whose every member hops to the next one and back."""
return [(namespace.sites, namespace.sites)]
@staticmethod
def _get_right_rate(s: SimpleNamespace) -> Expression:
"""Return the rate `p u(n)` of a hop to the right."""
return s.p * ZeroRangeHopInteraction._departure_rate(s.source.n, s.b)
@staticmethod
def _get_left_rate(s: SimpleNamespace) -> Expression:
"""Return the rate `q u(n)` of a hop to the left, from the site on the right."""
return s.q * ZeroRangeHopInteraction._departure_rate(s.target.n, s.b)