# The model object

## Contents

# The model object#

In this chapter we will get familiar with the flatspin model object, which implements the theoretical model. We will cover how to create a model object, setting parameters, and different model attributes.

This guide is written as a Jupyter notebook, which can be downloaded by clicking the download link from the top of the page.

The main object in flatspin is the *model* (`flatspin.model`

). Each model class defines a spin ice *geometry*, which specifies the positions and angles of the spins. The `SquareSpinIceClosed`

class, for instance, creates a square spin ice geometry (with “closed” edges):

```
from flatspin.model import SquareSpinIceClosed
model = SquareSpinIceClosed()
model.plot();
```

flatspin comes with model classes for some common geometries, shown in the gallery below:

```
import flatspin.model
class_names = ['SquareSpinIceClosed', 'SquareSpinIceOpen',
'KagomeSpinIce', 'KagomeSpinIceRotated',
'PinwheelSpinIceDiamond', 'PinwheelSpinIceLuckyKnot',
]
n_cols = 3
n_rows = int(np.ceil(len(class_names) / n_cols))
plt.figure(figsize=(2*n_cols, 2*n_rows))
for i, name in enumerate(class_names):
cls = getattr(flatspin.model, name)
plt.subplot(n_rows, n_cols, i+1)
plt.title(name)
model = cls()
model.plot()
plt.axis(False)
plt.tight_layout(w_pad=8)
```

If you would like to add your own geometries, see Extending flatspin.

## Model parameters#

Properties of the model can be changed through *parameters*, which are passed as keyword arguments to the model class. Note that the model *only* accepts keyword arguments (and no positional arguments).

For example, we may change the `size`

parameter to create a larger spin ice:

```
model = SquareSpinIceClosed(size=(10,5))
print(f"10x5 square ASI has", model.spin_count, "spins")
model.plot();
```

```
10x5 square ASI has 115 spins
```

Note that `size`

is geometry-specific: for `SquareSpinIceClosed`

, the size specifies the number of columns (rows) of horizontal (vertical) magnets.
For `KagomeSpinIce`

, the size denotes the number of hexagonal units:

```
from flatspin.model import KagomeSpinIce
model = KagomeSpinIce(size=(10,5))
print(f"10x5 kagome ASI has", model.spin_count, "spins")
model.plot();
```

```
10x5 kagome ASI has 179 spins
```

Other important parameters include `alpha`

(the coupling strength), `hc`

(the coercive field), `disorder`

(random variations in the coercive fields) and `temperature`

(absolute temperature in Kelvin). For a list of available parameters, see `SpinIce`

.

## Dealing with spins#

The state of all the spins is stored in the `spin`

array. Spin values are either `1`

or `-1`

, and are all initialized to `1`

at model instantiation.

```
model = SquareSpinIceClosed()
model.spin
```

```
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int8)
```

You may access elements of this array directly to read or modify the state of spins.
Alternatively, use `flip()`

to flip a single spin, `set_spin()`

to set the state of all spins, or `polarize()`

to reset all spins to `1`

or `-1`

.

```
model.spin[0] = -1
model.spin[-1] = -1
model.flip(4)
model.plot();
```

The `spin`

array has a flat index, where spins are ordered sequentially. Sometimes it can be easier to work in a different coordinate system, which is where spin labels come in. The left plot below shows the index of each spin. The right plot shows the corresponding labels, for `SquareSpinIceClosed`

, which uses a `(row, col)`

labeling scheme.

```
plt.figure(figsize=(8,4))
plt.subplot(121)
plt.title("Spin indices")
model.plot()
for i in model.indices():
plt.text(model.pos[i,0], model.pos[i,1], str(i), ha='center', va='center')
plt.subplot(122)
plt.title("Spin labels")
model.plot()
for i, l in enumerate(model.labels):
plt.text(model.pos[i,0], model.pos[i,1], tuple(l), ha='center', va='center')
```

You can use the `L`

object to look up the spin index of a label, or a label range:

```
L = model.L
print("(4,2):", L[4,2])
print("Row 3:", L[3])
print("Column 4:", L[:,4])
print("Rows 1-3:", L[1:3])
print("Odd rows:", L[1::2])
```

```
(4,2): 20
Row 3: [13 14 15 16 17]
Column 4: [ 8 17 26 35]
Rows 1-3: [ 4 5 6 7 8 9 10 11 12 13 14 15 16 17]
Odd rows: [ 4 5 6 7 8 13 14 15 16 17 22 23 24 25 26 31 32 33 34 35]
```

## Geometry#

Spin ice geometry is defined by the positions and angles of all the spins, and are stored in the `pos`

and `angle`

attributes. Positions are in reduced units, defined by `alpha`

(see Theory). The angles are in radians and define the rotation for the spins *assuming a positive spin value of 1*.

Please note that these attributes are considered **read-only** and cannot be changed after model initialization.

```
print(f"Spin 0 has position {model.pos[0]} and angle {np.rad2deg(model.angle[0])}")
print(f"Spin 4 has position {model.pos[4]} and angle {np.rad2deg(model.angle[4])}")
```

```
Spin 0 has position [0.5 0. ] and angle 0.0
Spin 4 has position [0. 0.5] and angle 90.0
```

As an alternative to tuning the coupling strength `alpha`

, the distance between spins may be adjusted by the `lattice_spacing`

parameter. This affects the positions of the spins directly, as opposed to indirectly through `alpha`

(see Theory).

```
model2 = SquareSpinIceClosed(lattice_spacing=10)
# Notice change in x/y labels when we plot()
model2.plot();
```

## Magnetization vectors#

As mentioned above, `angle`

is independent of the current spin state. To obtain the magnetization direction, use `vectors`

.

```
print(f"Spin 0 has magnetization {model.vectors[0]}")
print(f"Spin 1 has magnetization {model.vectors[1]}")
print(f"Spin 4 has magnetization {model.vectors[4]}")
```

```
Spin 0 has magnetization [-1. -0.]
Spin 1 has magnetization [1. 0.]
Spin 4 has magnetization [-6.123234e-17 -1.000000e+00]
```

## Coercive fields and disorder#

The coercive field defines the critical field strength required to flip a spin (see also Switching).
The coercive fields for all spins are stored in the `threshold`

array of the model object.
By default, the coercive fields are uniformly set to the parameter `hc`

.
We can introduce small variations in the coercive fields by setting the `disorder`

parameter, in which case the thresholds are sampled from a normal distribution with mean `hc`

and standard deviation `disorder * hc`

.

Below we plot a histogram of the coercive fields with 1% and 5% disorder.

```
model1 = SquareSpinIceClosed(size=(25,25), hc=0.1, disorder=0.01)
model5 = SquareSpinIceClosed(size=(25,25), hc=0.1, disorder=0.05)
bins = np.linspace(0.08, 0.12, 21) - 0.001
plt.hist(model1.threshold, bins=bins, label='1% disorder', alpha=0.5)
plt.hist(model5.threshold, bins=bins, label='5% disorder', alpha=0.5)
plt.legend()
plt.xlabel("hc");
```

## GPU acceleration#

flatspin provides GPU acceleration to speed up calculations on the GPU, but note that this must be explicitly enabled by setting the parameter `use_opencl=True`

. It is primarily the calculations of the magnetic fields that benefit from running on the GPU.