# Structured Units#

Numpy arrays can be structured arrays, where each element consists of multiple fields. These can be used with `Quantity` using a `StructuredUnit`, which provides a `UnitBase` for each field. For example, this allows constructing a single `Quantity` object with position and velocity fields that have different units, but are contained within the same object (as is needed to support units in the PyERFA wrappers around the ERFA routines that use position-velocity arrays).

## Creating Structured Quantities#

You can create structured quantities either directly or by multiplication with a `StructuredUnit`, with the latter in turn either created directly, or through `Unit`.

### Example#

To create a structured quantity containing a position and velocity:

```>>> import astropy.units as u, numpy as np
>>> pv_values = np.array([([1., 0., 0.], [0., 0.125, 0.]),
...                       ([0., 1., 0.], [-0.125, 0., 0.])],
...                      dtype=[('p', '(3,)f8'), ('v', '(3,)f8')])
>>> pv = u.Quantity(pv_values, u.StructuredUnit((u.km, u.km/u.s)))
>>> pv
<Quantity [([1., 0., 0.], [ 0.   ,  0.125,  0.   ]),
([0., 1., 0.], [-0.125,  0.   ,  0.   ])] (km, km / s)>
>>> pv_values * u.Unit('AU, AU/day')
<Quantity [([1., 0., 0.], [ 0.   ,  0.125,  0.   ]),
([0., 1., 0.], [-0.125,  0.   ,  0.   ])] (AU, AU / d)>
```

As for normal `Quantity` objects, you can access the value and the unit with the `value` and `unit` attribute, respectively. In addition, you can index any given field using its name:

```>>> pv = pv_values * u.Unit('km, km/s')
>>> pv.value
array([([1., 0., 0.], [ 0.   ,  0.125,  0.   ]),
([0., 1., 0.], [-0.125,  0.   ,  0.   ])],
dtype=[('p', '<f8', (3,)), ('v', '<f8', (3,))])
>>> pv.unit
Unit("(km, km / s)")
>>> pv['v']
<Quantity [[ 0.   ,  0.125,  0.   ],
[-0.125,  0.   ,  0.   ]] km / s>
```

Structures can be nested, as in this example taken from an PyERFA test case for `erfa.ldn()`:

```>>> ldbody = [
...     (0.00028574, 3e-10, ([-7.81014427, -5.60956681, -1.98079819],
...                          [0.0030723249, -0.00406995477, -0.00181335842])),
...     (0.00095435, 3e-9, ([0.738098796, 4.63658692, 1.9693136],
...                         [-0.00755816922, 0.00126913722, 0.000727999001])),
...     (1.0, 6e-6, ([-0.000712174377, -0.00230478303, -0.00105865966],
...                  [6.29235213e-6, -3.30888387e-7, -2.96486623e-7]))
>>> ldbody
<Quantity [(2.8574e-04, 3.e-10, ([-7.81014427e+00, -5.60956681e+00, -1.98079819e+00], [ 3.07232490e-03, -4.06995477e-03, -1.81335842e-03])),
(9.5435e-04, 3.e-09, ([ 7.38098796e-01,  4.63658692e+00,  1.96931360e+00], [-7.55816922e-03,  1.26913722e-03,  7.27999001e-04])),
(1.0000e+00, 6.e-06, ([-7.12174377e-04, -2.30478303e-03, -1.05865966e-03], [ 6.29235213e-06, -3.30888387e-07, -2.96486623e-07]))] (solMass, rad, (AU, AU / d))>
```

## Converting to Different Units#

Like regular `Quantity` objects, structured quantities can be converted to different units, as long as they have the same structure and each unit is equivalent.

### Example#

To convert a structured quantity to a different unit:

```>>> pv.to((u.m, u.m / u.s))
<Quantity [([1000.,    0.,    0.], [   0.,  125.,    0.]),
([   0., 1000.,    0.], [-125.,    0.,    0.])] (m, m / s)>
>>> pv.cgs
<Quantity [([100000.,      0.,      0.], [     0.,  12500.,      0.]),
([     0., 100000.,      0.], [-12500.,      0.,      0.])] (cm, cm / s)>
```

## Use with ERFA#

The ERFA C routines make use of structured types, and these are exposed in the PyERFA interface.

Warning

Not all PyERFA routines are wrapped yet. Help with adding wrappers will be appreciated.

### Example#

To use a position-velocity structured array with PyERFA:

```>>> import erfa
>>> pv_values = np.array([([1., 0., 0.], [0., 0.125, 0.]),
...                       ([0., 1., 0.], [-0.125, 0., 0.])],
...                      dtype=erfa.dt_pv)
>>> pv = pv_values << u.Unit('AU,AU/day')
>>> erfa.pvu(86400*u.s, pv)
<Quantity [([ 1.   ,  0.125,  0.   ], [ 0.   ,  0.125,  0.   ]),
([-0.125,  1.   ,  0.   ], [-0.125,  0.   ,  0.   ])] (AU, AU / d)>
>>> erfa.pv2s(pv)