# Introduction

In this page we illustrate how to introduce a new beam element compatible with Xtrack. We will use for illustration the “SRotation” element which performs the following transformation of the particle coordinates:

x = cos(theta) * x + sin(theta) * y

y = -sin(theta) * x + cos(theta) * y

px = cos(theta) * px + sin(theta) * py

py = -sin(theta) * px + cos(theta) * py

The element is fully described by the rotation angle theta.

# Definition and management of the data structure

New beam elements are defined as python classes inheriting from the class `BeamElement`

of xtrack.
In each element class we define a dictionary called `_xofields`

, which specifies names and types of the data to be made accessible to the C tracking code.

Although our beam element is defined by the single parameter (theta), it is convenient to store the quantities sin(theta) and cos(theta) to avoid recalculating them multiple times:

```
import xobjects as xo
import xtrack as xt
class SRotation(xt.BeamElement):
_xofields={
'cos_z': xo.Float64,
'sin_z': xo.Float64,
}
```

## Allocation of beam elements on CPU or GPU

Objects of the defined class can be allocated as follows:

```
srot = SRotation(sin_z=1., cos_z=0)
```

By default the objects are allocated in the CPU memory. They can be allocated in the memory of a GPU by providing an xobject context or buffer. For example:

```
ctx = xo.ContextCupy()
# Object allocated on the GPU
srot = SRotation(sin_z=1., cos_z=0, _context=ctx)
```

## Python access to beam-element data

The fields specified in `_xofields`

are automatically exposed as attributes of the objects that can be read and set with the standard python syntax, also if the object is allocated on the GPU:

```
print(srot.sin_z)
# returns 1.0
srot.sin_z = 0.9
print(srot.sin_z)
# returns 0.9
```

## Underlying xobject

The data specified by the `_xofields`

dictionary is stored in a contiguous memory
block (an `xobjects.Struct`

instance) which is managed by the Xobjects library
and is made accessible to the C tracking code.

The xobject can be accessed with the `_xobject`

attribute of the beam element.
For example, in the case of our SRotation element:

```
srot._xobject
```

All attributes of the xobject are automatically exposed as attributes of the beam element.
For example, in the case of our SRotation element `srot._xobject.sin_z`

is the
same as `srot.sin_z`

.

Arrays are exposed as native Xobjects arrays in the `_xobject`

attribute, and
as numpy or numpy-like arrays as attributes of the beam element. For example, in
the case of a `xtrack.Multipole`

element we find:

```
mp = xtrack.Multipole(knl=[1,2,3])
mp._xobject.knl
# is an xobjects array
mp.knl
# is a numpy array
```

It should be noted that the the two are different views of the same memory area, hence any modification can be made indifferently on any of them.

The numpy view (or np-like on GPU contexts) gives the possibility of using
numpy features on the array (e.g. `np.sum`

, `np.mean`

, slicing, masking, etc.).

## Custom `__init__`

method

Additional attributes and methods can be added to the class. If the `__init__`

method is defined, the `__init__`

of the parent class needs to be called to
initialize the `xobject`

, i.e. the data structure accessible from the C code.

In our example we want to initialize the object providing the rotation angle and
not its sine and cosine and we introduce a property called `angle`

that allows
setting or getting the angle from the stored sine and cosine. This can be done
as follows:

```
import numpy as np
import xobjects as xo
import xtrack as xt
class SRotation(BeamElement):
def __init__(self, angle=0, **kwargs):
anglerad = angle / 180 * np.pi
kwargs['cos_z']=np.cos(anglerad)
kwargs['sin_z']=np.sin(anglerad)
super().__init__(**kwargs)
@property
def angle(self):
return np.arctan2(self.sin_z, self.cos_z) * (180.0 / np.pi)
@angle.setter
def angle(self, value):
anglerad = value / 180 * np.pi
self.cos_z = np.cos(anglerad)
self.sin_z = np.sin(anglerad)
```