Kuramoto Lab: Full Simulation

Module 2: Synchronization and Collective Behaviour

Optional extension

This page is optional. It combines the animation and the bifurcation diagram into one interactive window with sliders and live plots (Kuramoto 2003; Hunter 2007). If you skip it, continue with Finite-Size Effects.

Template

You can start from the template in amlab/odes_coupled/kuramoto_template.py and fill in the missing parts. The template includes the structure for sliders, animation, and order parameter computation, so you can focus on implementing the Kuramoto model dynamics and visualizations.

Alternatively, you can build the full simulation from scratch by following the steps outlined in the previous pages and combining them together.

Order Parameter

Exercise

Complete the function that computes the order parameter. It returns \(r\), \(\psi\) and the two coordinates \(r\cos\psi\) and \(r\sin\psi\) of the red dot.

def kuramoto_order_parameter(theta: np.ndarray) -> tuple:
    # TODO: compute r * exp(i * phi)
    order_param =  # replace this line
    r = np.abs(order_param)
    phi = np.angle(order_param)
    rcosphi = np.real(order_param)
    rsinphi = np.imag(order_param)
    return r, phi, rcosphi, rsinphi

axis=0 averages over oscillators, so the same function also works on the 2D array sol.y and returns \(r(t)\).

def kuramoto_order_parameter(theta: np.ndarray) -> tuple:
    order_param = np.mean(np.exp(1j * theta), axis=0)
    r = np.abs(order_param)
    phi = np.angle(order_param)
    rcosphi = np.real(order_param)
    rsinphi = np.imag(order_param)
    return r, phi, rcosphi, rsinphi

Sliders

Use sliders to update the coupling strength, number of oscillators, and the distribution scale. The template creates the three sliders, slider_coupling, slider_num_oscillators and slider_sigma, inside run_simulation().

Exercise

Write the callback update_sliders() that reads the slider values, updates the parameters and reinitializes the oscillators. Then attach it to the three sliders.

def update_sliders(_):
    # TODO: declare the outer variables you change as nonlocal

    # TODO: read the new values from the sliders

    # TODO: reinitialize theta and omega

# TODO: attach update_sliders to each slider with on_changed()

The callback is defined inside run_simulation(), so you need nonlocal (not global) to change its variables. The number of oscillators must be an integer: use int(slider_num_oscillators.val).

def update_sliders(_):
    nonlocal coupling_strength, num_oscillators, sigma, theta, omega
    coupling_strength = slider_coupling.val
    num_oscillators = int(slider_num_oscillators.val)
    sigma = slider_sigma.val
    theta, omega = initialize_oscillators(num_oscillators, sigma=sigma)

slider_coupling.on_changed(update_sliders)
slider_num_oscillators.on_changed(update_sliders)
slider_sigma.on_changed(update_sliders)

A complete version, which also pauses the animation while the parameters change and restarts the simulation when you press a key, is in amlab/odes_coupled/kuramoto.py. Run it with python amlab/odes_coupled/kuramoto.py.

When you finish, run the full simulation and explore how synchronization changes as you vary \(K\). A good test: set \(K\) just below and just above \(K_c\) and watch how long \(r(t)\) takes to settle.

References

Hunter, John D. 2007. “Matplotlib: A 2D Graphics Environment.” Computing in Science & Engineering 9 (3): 90–95. https://doi.org/10.1109/MCSE.2007.55.
Kuramoto, Yoshiki. 2003. Chemical Oscillations, Waves, and Turbulence. Dover Publications.