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 |Unit| 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)>