---
jupytext:
  formats: ipynb,md:myst
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
    jupytext_version: 1.17.2
kernelspec:
  name: python3
  display_name: Python 3 (ipykernel)
  language: python
---

```{code-cell} ipython3
:tags: [remove-input]

%config InlineBackend.figure_formats = ['svg']
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
```

# Astroid clocking

This example shows how to simulate astroid clocking in flatspin, as described in {cite}`Jensen2024`:

[“Clocked Dynamics in Artificial Spin Ice.” Nature Communications 15, no. 1 (2024): 1.](https://doi.org/10.1038/s41467-024-45319-7)

Astroid clocking exploits the shape of the [switching astroids](theory-switching) in an ASI to selectively switch a subset of the magnets.
{numref}`astroid-clocking`a shows pinwheel ASI, with its two interleaved sublattices $L_a$ (solid outline) and $L_b$ (dashed outline).
There are four possible magnet states, indicated by their color: orange/blue for $L_a$ and pink/green for $L_b$.

{numref}`astroid-clocking`b shows the two switching astroids for the magnets in sublattice $L_a$ (solid lines) and $L_b$ (dashed lines).
Four *clock fields* are defined in the figure: $H_A$, $H_B$, $H_a$ and $H_b$.
We refer to fields in uppercase as the positive fields, and lowercase as the negative fields.
Applying $H_A$ will only switch magnets within $L_a$, since it only crosses the astroid boundary of $L_a$ (and not $L_b$).
Furthermore, since $H_A$ is aligned with the orange state, it will only switch blue magnets to the orange state.
The same goes for the other fields.

```{figure} ../images/astroid_clocking.svg
:name: astroid-clocking

Astroid clocking
```

A key principle of astroid clocking is to tune the strengths of the clock fields to be *close*, but still inside the astroid boundary.
Then it is up to the dipolar fields from neighbor magnets to push a magnet past the astroid boundary, and cause preferential switching.
It is also possible to use field strengths that are just outside the boundary, in which case the dipolar fields may pull magnets back into the astroid, and preferentially prevent switching.
In pinwheel ASI, astroid clocking can be used to preferentially switch magnets along domain walls.

Here we demonstrate how to use flatspin for astroid clocking.
First we create a model of a 30x30 pinwheel ASI.

```{code-cell} ipython3
# Create the model
from flatspin.model import PinwheelSpinIceDiamond

params = dict(
    size=(30, 30),
    use_opencl=1,
    spin_angle=-45,
    spin_axis=0,
    alpha=0.0013,
    hc=0.2,
    sw_b=0.38,
    sw_c=1,
    sw_beta=1.3,
    sw_gamma=3.6,
)
model = PinwheelSpinIceDiamond(**params)
```

Then we set up an initial octagonal domain in the center of the ASI:

```{code-cell} ipython3
from skimage import morphology

# Utility function to initialize an octagon-shaped domain
def init_octagon(model, m, n, center, polarize=-1):
    G = model.grid()
    init = np.ones(G.size)
    oct = morphology.octagon(m, n)
    oct = 2*oct.astype(int)-1
    oct *= -1
    rows, cols = init.shape
    cy, cx = center
    oy, ox = oct.shape
    y0, x0 = cy - oy//2, cx - ox//2
    init[y0:y0+oy, x0:x0+ox] = oct
    model.set_grid('spin', init * polarize)
```

```{code-cell} ipython3
# Initial state: octagon
cx, cy = model.size
init_octagon(model, 9, 5, (cy, cx-1), -1)
init = model.spin.copy()

snapshot = dict(style="stadium", width=.25, scale=1.3)
model.plot(**snapshot);
```

Next we define the four clock fields, and create a {class}`PulseTrain <flatspin.encoder.PulseTrain>` encoder which will allow us to map lists of field names (`"ABABAB"`) to sequences of external fields:

```{code-cell} ipython3
# Set up clock fields and encoder
from flatspin.encoder import PulseTrain

H = 0.0765
pulses={
    "A": (H,  22),    # (field strength, angle)
    "a": (H, 180+22),
    "B": (H, -22),
    "b": (H, 180-22)
}

encoder = PulseTrain(pulses=pulses)
```

## Unipolar clocking

A clock protocol is a sequence of clock fields applied in a repeated fashion.
*Unipolar clocking* only employs the positive (or negative) clock fields.

```{figure} ../images/astroid_clocking_unipolar.svg
:name: astroid-clocking-unipolar

Unipolar clocking
```

This example shows *AB* clocking, where the field $H_A$ and $H_B$ are applied in an alternate fashion to gradually grow the center domain.
After four clock cycles of *AB*, we switch to *ab* clocking and use the negative fields $H_a$ and $H_b$ to gradually reverse the domain.

```{code-cell} ipython3
from flatspin.plotting import animate_spins

sequence = "AB"*4 + "ab"*4
h_ext = encoder(list(sequence))
model.set_spin(init)
spins = [model.spin.copy()]
for h_ext_i in h_ext:
    model.set_h_ext(h_ext_i)
    model.relax()
    spins.append(model.spin.copy())

labels = ["init"] + list(sequence)
anim = animate_spins(model, spins, labels, highlight_flips=True, fps=5, **snapshot)
plt.close() # Only show the animation
HTML(anim.to_jshtml())
```

## Bipolar clocking

*Bipolar clocking* uses both the positive and negative fields in each clock cycle.

```{figure} ../images/astroid_clocking_bipolar.svg
:name: astroid-clocking-unipolar

Bipolar clocking
```

Below we demonstrate *aAbB* clocking, which results in more complex dynamics as domains can both grow and shrink within each clock cycle.

```{code-cell} ipython3
sequence = "aAbB"*10
h_ext = encoder(list(sequence))
model.set_spin(init)
spins = [model.spin.copy()]
for h_ext_i in h_ext:
    model.set_h_ext(h_ext_i)
    model.relax()
    spins.append(model.spin.copy())

labels = ["init"] + list(sequence)
anim = animate_spins(model, spins, labels, highlight_flips=True, **snapshot)
plt.close() # Only show the animation
HTML(anim.to_jshtml())
```
