Fisher-KPP
The Fisher-KPP equation, whose step invades the empty line as a front of speed 2 sqrt(r D).
- Name:
fisher_kpp - Field:
biology/ecology - Formalism: ordinary differential equations
- References: Fisher 1937; Kolmogorov, Petrovsky, and Piskunov 1937
Factory
@register_model(
field='biology/ecology',
tags=('field', 'fronts', 'reaction-diffusion', 'pde'),
references=('Fisher 1937', 'Kolmogorov, Petrovsky, and Piskunov 1937'),
defaults={'until': 40.0, 'dt': 1.0},
)
def fisher_kpp(
length: int = 800,
*,
D: float = 1.0,
r: float = 1.0,
spacing: float = 0.25,
front: float = 0.05,
) -> System:
"""Return the Fisher-KPP equation, whose step invades the empty line as a front of speed `2 sqrt(r D)`.
Args:
length:
The number of sites of the line.
D:
The diffusion coefficient.
r:
The growth rate.
spacing:
The distance between sites.
front:
The fraction of the line that starts occupied, on its left.
Returns:
system:
The model, with the density `line.u`.
Examples:
>>> from skcomplex.models.biology.ecology import fisher_kpp
>>> int(fisher_kpp(100, front=0.1).elements[0].values['u'].sum())
10
"""
u = (np.arange(length) < front * length).astype(float)
space = LatticeSpace(length, spacing=spacing)
return System(
elements=[
FieldElement(
'line',
element=FisherKPPElement,
space=space,
boundary={'u': 'zero-flux'},
values={'u': u, 'D': D, 'r': r},
),
],
)
Components
class FisherKPPElement(Element):
"""Site of a line whose density `u` diffuses and grows logistically, `du/dt = D laplacian(u) + r u (1 - u)`.
Args:
label:
The label of the site.
visual:
How the element is drawn, or `None` for `default_visual_`.
u:
The initial density, as a fraction of the carrying capacity.
D:
The diffusion coefficient.
r:
The growth rate.
Examples:
>>> from skcomplex.models.biology.ecology import FisherKPPElement
>>> FisherKPPElement('site', u=0.5).u
0.5
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
u: Annotated[object, StateSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
D: Annotated[object, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
r: Annotated[object, ParameterSpec()] = 1.0,
) -> None:
super().__init__(label, visual=visual)
self.u = u
self.D = D
self.r = r
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the diffusion and the logistic growth of the density.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The rate law of the density.
"""
return [RateLaw(s.u, s.D * laplacian(s.u) + s.r * s.u * (1 - s.u))]
Equations
line.u[i]: diff(u_line(t), t) == -r_line*(u_line(t) - 1)*u_line(t) + D_line*laplacian(u_line(t))