Complex Ginzburg-Landau
The complex Ginzburg-Landau equation A_t = A + (1 + i b) laplacian(A) - (1 + i c) |A|^2 A.
- Name:
complex_ginzburg_landau - Field:
physics/pattern_formation - Formalism: ordinary differential equations
- References: Aranson and Kramer 2002; Gros 2015
Factory
@register_model(
field='physics/pattern_formation',
tags=('field', 'chaos', 'spirals', 'spectral', 'pde'),
references=('Aranson and Kramer 2002', 'Gros 2015'),
defaults={'until': 50.0, 'dt': 1.0, 'solver': ETDRK4Solver(dt=0.1)},
)
def complex_ginzburg_landau(
shape: Sequence[int] = (64, 64),
*,
length: float = 64.0,
b: float = 1.0,
c: float = -1.5,
random_state: int | np.random.Generator | None = 0,
) -> System:
"""Return the complex Ginzburg-Landau equation `A_t = A + (1 + i b) laplacian(A) - (1 + i c) |A|^2 A`.
The amplitude `A = u + i v` is held as its real and imaginary parts, and the linear part couples them per Fourier
mode. With `1 + b c < 0` the uniform oscillation is unstable and spiral defects turn into turbulence.
Args:
shape:
The number of sites along both axes of the periodic lattice.
length:
The side of the square.
b:
The linear dispersion.
c:
The nonlinear dispersion.
random_state:
The seed or generator of the small random initial amplitude.
Returns:
system:
The model, with the real part `medium.u` and the imaginary part `medium.v`.
Examples:
>>> from skcomplex.models.physics.pattern_formation import complex_ginzburg_landau
>>> complex_ginzburg_landau((8, 8)).elements[0].values['c']
-1.5
"""
shape = tuple(shape)
rng = np.random.default_rng(random_state)
values = {'u': _noise(shape, 0.01, rng), 'v': _noise(shape, 0.01, rng), 'b': b, 'c': c}
space = LatticeSpace(shape, spacing=length / shape[0])
return System(
elements=[
FieldElement(
'medium',
element=GinzburgLandauElement,
space=space,
values=values,
discretization='spectral',
),
],
)
Components
class GinzburgLandauElement(Element):
"""Site of the complex Ginzburg-Landau equation `A_t = A + (1 + i b) laplacian(A) - (1 - i c) |A|^2 A`.
The amplitude `A = u + i v` is held as its real and imaginary parts. Without the Laplacian every site is a
Stuart-Landau oscillator, and the dispersion `b` and the nonlinear frequency shift `c` set whether plane waves are
stable or break into spiral defect chaos.
Args:
label:
The label of the site or the field.
visual:
How the site is drawn, or `None` for `default_visual_`.
u:
The initial real part of the amplitude.
v:
The initial imaginary part of the amplitude.
b:
The linear dispersion.
c:
The nonlinear frequency shift.
Attributes:
default_visual_:
The visual of the site when it sets none.
Examples:
>>> from skcomplex.models.physics.pattern_formation import GinzburgLandauElement
>>> GinzburgLandauElement('site').c
-1.5
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
u: Annotated[float, StateSpec()] = 0.0,
v: Annotated[float, StateSpec()] = 0.0,
b: Annotated[float, ParameterSpec()] = 1.0,
c: Annotated[float, ParameterSpec()] = -1.5,
) -> None:
super().__init__(label, visual=visual)
self.u = u
self.v = v
self.b = b
self.c = c
def _get_laws(self, namespace: SimpleNamespace) -> list[BaseLaw]:
"""Return the rates of the real and imaginary parts of the amplitude.
Args:
namespace:
The namespace of the symbols the laws can use.
Returns:
laws:
The rates of `u` and `v`.
"""
s = namespace
square = s.u**2 + s.v**2
return [
RateLaw(s.u, s.u + laplacian(s.u) - s.b * laplacian(s.v) - square * (s.u - s.c * s.v)),
RateLaw(s.v, s.v + s.b * laplacian(s.u) + laplacian(s.v) - square * (s.c * s.u + s.v)),
]
Equations
medium.u[i]: diff(u_medium(t), t) == (u_medium(t)^2 + v_medium(t)^2)*(c_medium*v_medium(t) - u_medium(t)) - b_medium*laplacian(v_medium(t)) + laplacian(u_medium(t)) + u_medium(t)
medium.v[i]: diff(v_medium(t), t) == -(c_medium*u_medium(t) + v_medium(t))*(u_medium(t)^2 + v_medium(t)^2) + b_medium*laplacian(u_medium(t)) + laplacian(v_medium(t)) + v_medium(t)