# Symbolic units: yt.units¶

Notebook

### Dimensional analysis¶

The fastest way to get into the unit system is to explore the quantities that live in the yt.units namespace:

In :
from yt.units import meter, gram, kilogram, second, joule
print (kilogram*meter**2/second**2 == joule)
print (kilogram*meter**2/second**2)

/usr/lib64/python3.6/site-packages/h5py/__init__.py:34: FutureWarning: Conversion of the second argument of issubdtype from float to np.floating is deprecated. In future, it will be treated as np.float64 == np.dtype(float).type.
from ._conv import register_converters as _register_converters

True
1.0 kg*m**2/s**2

In :
from yt.units import m, kg, s, W
kg*m**2/s**3 == W

Out:
array(True)
In :
from yt.units import kilometer
three_kilometers = 3*kilometer
print (three_kilometers)

3.0 km

In :
from yt.units import gram, kilogram
print (gram+kilogram)

print (kilogram+gram)

print (kilogram/gram)

1001.0 g
1.001 kg
1000.0 dimensionless


These unit symbols are all instances of a new class we've added to yt 3.0, YTQuantity. YTQuantity is useful for storing a single data point.

In :
type(kilogram)

Out:
yt.units.yt_array.YTQuantity

We also provide YTArray, which can store arrays of quantities:

In :
arr = [3,4,5]*kilogram

print (arr)

print (type(arr))

[3. 4. 5.] kg
<class 'yt.units.yt_array.YTArray'>


### Creating arrays and quantities¶

Most people will interact with the new unit system using YTArray and YTQuantity. These are both subclasses of numpy's fast array type, ndarray, and can be used interchangeably with other NumPy arrays. These new classes make use of the unit system to append unit metadata to the underlying ndarray. YTArray is intended to store array data, while YTQuantity is intended to store scalars in a particular unit system.

There are two ways to create arrays and quantities. The first is to explicitly create it by calling the class constructor and supplying a unit string:

In :
from yt.units.yt_array import YTArray

sample_array = YTArray([1,2,3], 'g/cm**3')

print (sample_array)

[1. 2. 3.] g/cm**3


The unit string can be an arbitrary combination of metric unit names. Just a few examples:

In :
from yt.units.yt_array import YTQuantity
from yt.units import kboltz
from numpy.random import random
import numpy as np

print ("Length:")
print (YTQuantity(random(), 'm'))
print (YTQuantity(random(), 'cm'))
print (YTQuantity(random(), 'Mpc'))
print (YTQuantity(random(), 'AU'))
print ('')

print ("Time:")
print (YTQuantity(random(), 's'))
print (YTQuantity(random(), 'min'))
print (YTQuantity(random(), 'hr'))
print (YTQuantity(random(), 'day'))
print (YTQuantity(random(), 'yr'))
print ('')

print ("Mass:")
print (YTQuantity(random(), 'g'))
print (YTQuantity(random(), 'kg'))
print (YTQuantity(random(), 'Msun'))
print ('')

print ("Energy:")
print (YTQuantity(random(), 'erg'))
print (YTQuantity(random(), 'g*cm**2/s**2'))
print (YTQuantity(random(), 'eV'))
print (YTQuantity(random(), 'J'))
print ('')

print ("Temperature:")
print (YTQuantity(random(), 'K'))
print ((YTQuantity(random(), 'eV')/kboltz).in_cgs())

Length:
0.9439741771584573 m
0.4153724745104015 cm
0.3562036659516079 Mpc
0.7948016121299761 AU

Time:
0.3668026069018807 s
0.20887817633379846 min
0.38983937256766366 hr
0.3664960539122286 day
0.17174120203582122 yr

Mass:
0.9616324784012525 g
0.42298557128446546 kg
0.6184495061171246 Msun

Energy:
0.8797309031963264 erg
0.6536412534165104 cm**2*g/s**2
0.14876480955082427 eV
0.817533226680098 J

Temperature:
0.9028965146205524 K
605.275225338459 K


Dimensional arrays and quantities can also be created by multiplication with another array or quantity:

In :
from yt.units import kilometer
print (kilometer)

1.0 km

In :
three_kilometers = 3*kilometer
print (three_kilometers)

3.0 km


When working with a YTArray with complicated units, you can use unit_array and unit_quantity to conveniently apply units to data:

In :
test_array = YTArray(np.random.random(20), 'erg/s')

print (test_array)

[0.94886769 0.69683711 0.4586724  0.09573412 0.07886342 0.71548608
0.16584565 0.72397415 0.34699726 0.21276056 0.55276801 0.21320142
0.10999254 0.0743557  0.10841152 0.37885772 0.40899366 0.63444685
0.14307456 0.69846425] erg/s


unit_quantity returns a YTQuantity with a value of 1.0 and the same units as the array it is a attached to.

In :
print (test_array.unit_quantity)

1.0 erg/s


unit_array returns a YTArray with the same units and shape as the array it is a attached to and with all values set to 1.0.

In :
print (test_array.unit_array)

[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] erg/s


These are useful when doing arithmetic:

In :
print (test_array + 1.0*test_array.unit_quantity)

[1.94886769 1.69683711 1.4586724  1.09573412 1.07886342 1.71548608
1.16584565 1.72397415 1.34699726 1.21276056 1.55276801 1.21320142
1.10999254 1.0743557  1.10841152 1.37885772 1.40899366 1.63444685
1.14307456 1.69846425] erg/s

In :
print (test_array + np.arange(20)*test_array.unit_array)

[ 0.94886769  1.69683711  2.4586724   3.09573412  4.07886342  5.71548608
6.16584565  7.72397415  8.34699726  9.21276056 10.55276801 11.21320142
12.10999254 13.0743557  14.10841152 15.37885772 16.40899366 17.63444685
18.14307456 19.69846425] erg/s


For convenience, unit_quantity is also available via uq and unit_array is available via ua. You can use these arrays to create dummy arrays with the same units as another array - this is sometimes easier than manually creating a new array or quantity.

In :
print (test_array.uq)

print (test_array.unit_quantity == test_array.uq)

1.0 erg/s
True

In :
from numpy import array_equal

print (test_array.ua)

print (array_equal(test_array.ua, test_array.unit_array))

[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] erg/s
True


Unit metadata is encoded in the units attribute that hangs off of YTArray or YTQuantity instances:

In :
from yt.units import kilometer, erg

print ("kilometer's units:", kilometer.units)
print ("kilometer's dimensions:", kilometer.units.dimensions)

print ('')

print ("erg's units:", erg.units)
print ("erg's dimensions: ", erg.units.dimensions)

kilometer's units: km
kilometer's dimensions: (length)

erg's units: erg
erg's dimensions:  (length)**2*(mass)/(time)**2


### Arithmetic with YTQuantity and YTArray¶

Of course it wouldn't be very useful if all we could do is create data with units. The real power of the new unit system is that we can add, subtract, mutliply, and divide using quantities and dimensional arrays:

In :
a = YTQuantity(3, 'cm')
b = YTQuantity(3, 'm')

print (a+b)
print (b+a)
print ('')

print ((a+b).in_units('ft'))

303.0 cm
3.03 m

9.940944881889763 ft

In :
a = YTQuantity(42, 'mm')
b = YTQuantity(1, 's')

print (a/b)
print ((a/b).in_cgs())
print ((a/b).in_mks())
print ((a/b).in_units('km/s'))
print ('')

print (a*b)
print ((a*b).in_cgs())
print ((a*b).in_mks())

42.0 mm/s
4.2 cm/s
0.042 m/s
4.2e-05 km/s

42.0 mm*s
4.2 cm*s
0.042 m*s

In :
m = YTQuantity(35, 'g')
a = YTQuantity(9.8, 'm/s**2')

print (m*a)
print ((m*a).in_units('dyne'))

343.0 g*m/s**2
34300.0 dyne

In :
from yt.units import G, kboltz

print ("Newton's constant: ", G)
print ("Newton's constant in MKS: ", G.in_mks(), "\n")

print ("Boltzmann constant: ", kboltz)
print ("Boltzmann constant in MKS: ", kboltz.in_mks())

Newton's constant:  6.67384e-08 cm**3/(g*s**2)
Newton's constant in MKS:  6.67384e-11 m**3/(kg*s**2)

Boltzmann constant:  1.3806488e-16 erg/K
Boltzmann constant in MKS:  1.3806488e-23 kg*m**2/(K*s**2)

In :
rho = YTQuantity(1, 'g/cm**3')
t_ff = (G*rho)**(-0.5)

print (t_ff)

3870.901361178502 s


An exception is raised if we try to do a unit operation that doesn't make any sense:

In :
from yt.utilities.exceptions import YTUnitOperationError

a = YTQuantity(3, 'm')
b = YTQuantity(5, 'erg')

try:
print (a+b)
except YTUnitOperationError as e:
print (e)

The <ufunc 'add'> operator for YTArrays with units (m) and (erg) is not well defined.


A plain ndarray or a YTArray created with empty units is treated as a dimensionless quantity and can be used in situations where unit consistency allows it to be used:

In :
a = YTArray([1.,2.,3.], 'm')
b = np.array([2.,2.,2.])

print ("a:   ", a)
print ("b:   ", b)
print ("a*b: ", a*b)

a:    [1. 2. 3.] m
b:    [2. 2. 2.]
a*b:  [2. 4. 6.] m

In :
c = YTArray([2,2,2])

print ("c:    ", c)
print ("a*c:  ", a*c)

c:     [2. 2. 2.] dimensionless
a*c:   [2. 4. 6.] m


### Saving and Loading YTArrays to/from disk¶

YTArrays can be written to disk, to be loaded again to be used in yt or in a different context later. There are two formats that can be written to/read from: HDF5 and ASCII.

#### HDF5¶

To write to HDF5, use write_hdf5:

In :
my_dens = YTArray(np.random.random(10), 'Msun/kpc**3')
my_temp = YTArray(np.random.random(10), 'K')
my_dens.write_hdf5("my_data.h5", dataset_name="density")
my_temp.write_hdf5("my_data.h5", dataset_name="temperature")


Where we used the dataset_name keyword argument to create a separate dataset for each array in the same file.

We can use the from_hdf5 classmethod to read the data back in:

In :
read_dens = YTArray.from_hdf5("my_data.h5", dataset_name="density")
print (my_dens)

[0.37423241 0.30678831 0.73266216 0.80790818 0.24426128 0.96044056
0.99461103 0.2778578  0.7609226  0.59798787] Msun/kpc**3
[0.37423241 0.30678831 0.73266216 0.80790818 0.24426128 0.96044056
0.99461103 0.2778578  0.7609226  0.59798787] Msun/kpc**3


We can use the info keyword argument to write_hdf5 to write some additional data to the file, which will be stored as attributes of the dataset:

In :
my_vels = YTArray(np.random.normal(10), 'km/s')
info = {"source":"galaxy cluster","user":"jzuhone"}
my_vels.write_hdf5("my_data.h5", dataset_name="velocity", info=info)


If you want to read/write a dataset from/to a specific group within the HDF5 file, use the group_name keyword:

In :
my_vels.write_hdf5("data_in_group.h5", dataset_name="velocity", info=info, group_name="/data/fields")


where we have used the standard HDF5 slash notation for writing a group hierarchy (e.g., group within a group):

#### ASCII¶

To write one or more YTArrays to an ASCII text file, use yt.savetxt, which works a lot like NumPy's savetxt, except with units:

In :
import yt
a = YTArray(np.random.random(size=10), "cm")
b = YTArray(np.random.random(size=10), "g")
c = YTArray(np.random.random(size=10), "s")
yt.savetxt("my_data.dat", [a,b,c], header='My cool data', footer='Data is over', delimiter="\t")


The file we wrote can then be easily used in other contexts, such as plotting in Gnuplot, or loading into a spreadsheet, or just for causal examination. We can quickly check it here:

In :
%%bash
more my_data.dat

::::::::::::::
my_data.dat
::::::::::::::
#My cool data
# Units
# cm	g	s
9.910352437040234053e-01	6.548734835411753608e-01	1.809853558103093896e-01
6.770653756705903303e-01	8.184171558689747883e-01	1.857878264696442017e-01
9.368934066587422116e-01	5.272603406748470611e-01	9.812958213660516460e-01
1.267347214747294171e-02	5.319747626019584175e-01	8.423723712725330603e-01
3.761581407859222459e-01	2.990607760364510925e-01	9.188235784586640253e-02
5.191197295658032118e-01	9.739912623345758913e-01	1.667234482788625405e-01
2.277457483998961418e-01	7.914232191013187423e-01	3.288337425687991589e-01
5.422964073951220820e-01	2.509347029759604331e-01	3.714901671977713926e-01
9.276558672640764502e-01	4.118828309134029420e-01	7.039936785971436795e-01
9.093917535500162241e-01	9.451925760042244606e-01	5.906303570946551673e-01
#Data is over


You can see that the header comes first, and then right before the data we have a subheader marking the units of each column. The footer comes after the data.

yt.loadtxt can be used to read the same data with units back in, or read data that has been generated from some other source. Just make sure it's in the format above. loadtxt can also selectively read from particular columns in the file with the usecols keyword argument:

In :
bb, cc = yt.loadtxt("my_data.dat", usecols=(1,2), delimiter="\t")
print (bb)
print (b)
print ('')
print (cc)
print (c)

[0.65487348 0.81841716 0.52726034 0.53197476 0.29906078 0.97399126
0.79142322 0.2509347  0.41188283 0.94519258] g
[0.65487348 0.81841716 0.52726034 0.53197476 0.29906078 0.97399126
0.79142322 0.2509347  0.41188283 0.94519258] g

[0.18098536 0.18578783 0.98129582 0.84237237 0.09188236 0.16672345
0.32883374 0.37149017 0.70399368 0.59063036] s
[0.18098536 0.18578783 0.98129582 0.84237237 0.09188236 0.16672345
0.32883374 0.37149017 0.70399368 0.59063036] s