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]))
...     ] * u.Unit('Msun,radian,(AU,AU/day)')
>>> 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)  
(<Quantity [0.        , 1.57079633] rad>,
 <Quantity [0., 0.] rad>,
 <Quantity [1., 1.] AU>,
 <Quantity [0.125, 0.125] rad / d>,
 <Quantity [0., 0.] rad / d>,
 <Quantity [0., 0.] AU / d>)
>>> z_axis = np.array(([0, 0, 1], [0, 0, 0]), erfa.dt_pv) * u.Unit('1,1/s')
>>> erfa.pvxpv(pv, z_axis)
<Quantity [([ 0., -1.,  0.], [0.125, 0.   , 0.   ]),
           ([ 1.,  0.,  0.], [0.   , 0.125, 0.   ])] (AU, AU / d)>