Ott-Antonsen reduction
The Ott-Antonsen equation for the order parameter of infinitely many Kuramoto oscillators.
- Name:
ott_antonsen - Field:
physics/synchronization - Formalism: ordinary differential equations
- References: Ott and Antonsen 2008; Bick et al. 2020
Factory
@register_model(
field='physics/synchronization',
tags=('synchronization', 'ode', 'mean field', 'dimension reduction'),
references=('Ott and Antonsen 2008', 'Bick et al. 2020'),
defaults={'until': 30.0, 'dt': 0.1},
)
def ott_antonsen(
r: float = 0.1,
*,
K: float = 2.0,
Delta: float = 0.5,
omega: float = 0.0,
) -> System:
"""Return the Ott-Antonsen equation for the order parameter of infinitely many Kuramoto oscillators.
For natural frequencies with a Lorentzian distribution of center `omega` and half width `Delta`, the complex order
parameter `z = x + i y` follows exactly `z' = (i omega - Delta) z + (K / 2)(1 - |z|²) z`, a Stuart-Landau
oscillator with the growth rate `K / 2 - Delta` and the saturation `K / 2`. Its modulus settles at
`sqrt(1 - 2 Delta / K)` above the critical coupling `K_c = 2 Delta`, and at 0 below it.
Args:
r:
The initial modulus of the order parameter.
K:
The coupling strength.
Delta:
The half width of the distribution of natural frequencies.
omega:
The center of the distribution of natural frequencies.
Returns:
system:
The model, with the real part `field.x` and the imaginary part `field.y` of the order parameter.
Examples:
>>> from skcomplex.models.physics.synchronization import ott_antonsen
>>> ott_antonsen(K=3.0).elements[0].kappa
1.5
"""
return System(elements=[StuartLandauElement('field', x=r, y=0.0, mu=K / 2 - Delta, omega=omega, kappa=K / 2)])
Components
class StuartLandauElement(Element):
"""Stuart-Landau oscillator `z' = tau ((mu + i omega) z - kappa (1 + i beta) |z|^2 z)` of `z = x + i y`.
It is the normal form of a supercritical Hopf bifurcation. For `mu > 0` the oscillator settles on the limit cycle
of radius `sqrt(mu / kappa)`, which it turns at the frequency `tau (omega - beta mu)`, and for `mu < 0` it decays to
the fixed point 0. The twist `beta` makes the frequency depend on the amplitude, and the time scale `tau` speeds
up or slows down the whole flow.
Args:
label:
The label of the oscillator or the group.
visual:
How the oscillator is drawn, or `None` for `default_visual_`.
x:
The initial real part of `z`.
y:
The initial imaginary part of `z`.
mu:
The growth rate of the fixed point 0, the bifurcation parameter.
omega:
The frequency of the linear rotation.
beta:
The twist, the shear of the frequency with the amplitude.
kappa:
The strength of the cubic saturation.
tau:
The time scale.
Attributes:
default_visual_:
The visual of the oscillator when it sets none.
Examples:
>>> import math
>>> from skcomplex.core.system import System
>>> from skcomplex.engine.runtime import Simulation
>>> from skcomplex.models.physics.common.oscillators import StuartLandauElement
>>> system = System(elements=[StuartLandauElement('oscillator', x=0.1, mu=4.0)])
>>> trajectory = Simulation(system, dt=0.5).check().prepare().run(until=10.0).trajectory_
>>> round(math.hypot(trajectory.values('oscillator.x')[-1], trajectory.values('oscillator.y')[-1]), 4)
2.0
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
x: Annotated[float, StateSpec()] = 1.0,
y: Annotated[float, StateSpec()] = 0.0,
mu: Annotated[float, ParameterSpec()] = 1.0,
omega: Annotated[float, ParameterSpec()] = 1.0,
beta: Annotated[float, ParameterSpec()] = 0.0,
kappa: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
tau: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
) -> None:
super().__init__(label, visual=visual)
self.x = x
self.y = y
self.mu = mu
self.omega = omega
self.beta = beta
self.kappa = kappa
self.tau = tau
def _get_laws(self, namespace: SimpleNamespace) -> list[BaseLaw]:
"""Return the rates of the real and imaginary parts of `z`.
Args:
namespace:
The namespace of the symbols the laws can use.
Returns:
laws:
The rates of `x` and `y`.
"""
s = namespace
saturation = s.kappa * (s.x**2 + s.y**2)
return [
RateLaw(s.x, s.tau * (s.mu * s.x - s.omega * s.y - saturation * (s.x - s.beta * s.y))),
RateLaw(s.y, s.tau * (s.mu * s.y + s.omega * s.x - saturation * (s.y + s.beta * s.x))),
]
Equations
field.x: diff(x_field(t), t) == ((x_field(t)^2 + y_field(t)^2)*(beta_field*y_field(t) - x_field(t))*kappa_field + mu_field*x_field(t) - omega_field*y_field(t))*tau_field
field.y: diff(y_field(t), t) == -((beta_field*x_field(t) + y_field(t))*(x_field(t)^2 + y_field(t)^2)*kappa_field - omega_field*x_field(t) - mu_field*y_field(t))*tau_field