# Unit SystemsΒΆ

Notebook

By default, the results of most calculations in yt are expressed in a "centimeters-grams-seconds" (CGS) set of units. This includes the values of derived fields and aliased fields.

However, this system of units may not be the most natural for a given dataset or an entire class of datasets. For this reason, yt provides the ability to define new unit systems and use them in a way that is highly configurable by the end-user.

### Unit Systems Available in yt¶

Several unit systems are already supplied for use within yt. They are:

• "cgs": Centimeters-grams-seconds unit system, with base of (cm, g, s, K, radian). Uses the Gaussian normalization for electromagnetic units.
• "mks": Meters-kilograms-seconds unit system, with base of (m, kg, s, K, radian, A).
• "imperial": Imperial unit system, with base of (mile, lbm, s, R, radian).
• "galactic": "Galactic" unit system, with base of (kpc, Msun, Myr, K, radian).
• "solar": "Solar" unit system, with base of (AU, Mearth, yr, K, radian).
• "planck": Planck natural units $(\hbar = c = G = k_B = 1)$, with base of (l_pl, m_pl, t_pl, T_pl, radian).
• "geometrized": Geometrized natural units $(c = G = 1)$, with base of (l_geom, m_geom, t_geom, K, radian).

We can examine these unit systems by querying them from the unit_system_registry. For example, we can look at the default CGS system:

In [1]:
import yt
yt.unit_system_registry["cgs"]

/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

Out[1]:
cgs Unit System
Base Units:
length: cm
mass: g
time: s
temperature: K
Other Units:
energy: erg
specific_energy: erg/g
pressure: dyne/cm**2
force: dyne
magnetic_field_cgs: gauss
charge_cgs: esu
current_cgs: statA

We can see that we have two sets of units that this system defines: "base" and "other" units. The "base" units are the set of units from which all other units in the system are composed of, such as centimeters, grams, and seconds. The "other" units are compound units which fields with specific dimensionalities are converted to, such as ergs, dynes, gauss, and electrostatic units (esu).

We see a similar setup for the MKS system, except that in this case, there is a base unit of current, the Ampere:

In [2]:
yt.unit_system_registry["mks"]

Out[2]:
mks Unit System
Base Units:
length: m
mass: kg
time: s
temperature: K
current_mks: A
Other Units:
energy: J
specific_energy: J/kg
pressure: Pa
force: N
magnetic_field_mks: T
charge_mks: C

We can also look at the imperial system:

In [3]:
yt.unit_system_registry["imperial"]

Out[3]:
imperial Unit System
Base Units:
length: ft
mass: lbm
time: s
temperature: R
Other Units:
force: lbf
energy: ft*lbf
pressure: lbf/ft**2

and the "galactic" system as well:

In [4]:
yt.unit_system_registry["galactic"]

Out[4]:
galactic Unit System
Base Units:
length: kpc
mass: Msun
time: Myr
temperature: K
Other Units:
energy: keV
magnetic_field_cgs: uG

### Converting YTArrays to the Different Unit Systems¶

When a dataset is loaded, a unit system may be specified. When this happens, all aliased and derived fields will be converted to the units of the given system. The default is "cgs".

For example, we can specify that the fields from a FLASH dataset can be expressed in MKS units:

In [5]:
ds_flash = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0100", unit_system="mks")
sp = ds_flash.sphere("c", (100.,"kpc"))
print (sp["density"]) # This is an alias for ("flash","dens")
print (sp["pressure"]) # This is an alias for ("flash","pres")
print (sp["angular_momentum_x"]) # This is a derived field
print (sp["kinetic_energy"]) # This is also a derived field

[1.30865584e-23 1.28922012e-23 1.30364287e-23 ... 1.61943869e-23
1.61525279e-23 1.59566008e-23] kg/m**3
[1.62223415e-11 1.60880725e-11 1.62334618e-11 ... 1.54101079e-11
1.52756530e-11 1.53220436e-11] Pa
[-3.97578484e+61 -3.92971123e+61 -3.95375251e+61 ...  2.39040683e+61
2.39880446e+61  2.44245785e+61] kg*m**2/s
[6.37117204e-13 6.12785535e-13 6.20621019e-13 ... 3.12205509e-13
3.01537806e-13 3.39879277e-13] Pa


Aliased fields are converted to the requested unit system, but the on-disk fields that they correspond to remain in their original (code) units:

In [6]:
print (sp["flash","dens"]) # This is aliased to ("gas", "density")
print (sp["flash","pres"]) # This is aliased to ("gas", "pressure")

[1.30865584e-26 1.28922012e-26 1.30364287e-26 ... 1.61943869e-26
1.61525279e-26 1.59566008e-26] code_mass/code_length**3
[1.62223415e-10 1.60880725e-10 1.62334618e-10 ... 1.54101079e-10
1.52756530e-10 1.53220436e-10] code_mass/(code_length*code_time**2)


We can take an Enzo dataset and express it in "galactic" units:

In [7]:
ds_enzo = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030", unit_system="galactic")
sp = ds_enzo.sphere("c", (20.,"kpc"))
print (sp["density"])
print (sp["pressure"])

[   30391.31059996    29274.89731402    27863.44432451 ...
1667855.69170645  2357612.90571688 16227261.84385045] Msun/kpc**3
[   5.77978674    5.56891759    5.30229715 ...  204.34231598  264.93114662
1221.18769656] Msun/(Myr**2*kpc)


We can also express all of the fields associated with a dataset in that dataset's system of "code" units. Though the on-disk fields are already in these units, this means that we can express even derived fields in code units as well:

In [8]:
ds_chombo = yt.load("KelvinHelmholtz/data.0004.hdf5", unit_system="code")
dd = ds_chombo.all_data()
print (dd["density"])
print (dd["kinetic_energy"])

[0.99997527 0.99996094 0.99995164 ... 0.92014853 0.92240488 0.9246393 ] code_mass/code_length**3
[0.03130718 0.03132397 0.03133794 ... 0.06072832 0.06020938 0.05968097] code_pressure


### Defining Fields So That They Can Use the Different Unit Systems¶

If you define a new derived field for use in yt and wish to make the different unit systems available to it, you will need to specify this when calling add_field. Suppose I defined a new field called "momentum_x" and wanted it to have general units. I would have to set it up in this fashion, using the unit_system attribute of the dataset and querying it for the appropriate dimensions:

In [9]:
mom_units = ds_flash.unit_system["velocity"]*ds_flash.unit_system["density"]
def _momentum_x(field, data):
return data["density"]*data["velocity_x"]

/home/fido/.local/lib64/python3.6/site-packages/ipykernel_launcher.py:4: UserWarning: Because 'sampling_type' not specified, yt will assume a cell 'sampling_type'
after removing the cwd from sys.path.


Now, the field will automatically be expressed in whatever units the dataset was called with. In this case, it was MKS:

In [10]:
slc = yt.SlicePlot(ds_flash, "z", ["momentum_x"], width=(300.,"kpc"))
slc.show()


Note that the momentum density has been plotted with the correct MKS units of $\mathrm{kg/(m^2\cdot{s})}$.

If you don't create a derived field from a dataset but instead use yt.add_field, and still want to use the unit system of that dataset for the units, the only option at present is to set units="auto" in the call to yt.add_field and the dimensions keyword to the correct dimensions for the field:

In [11]:
from yt.units import clight

def _rest_energy(field, data):
return data["cell_mass"]*clight*clight

sp = ds_flash2.sphere("c", (100.,"kpc"))
sp["rest_energy"]

/tmp/yt/yt/fields/local_fields.py:46: UserWarning: Because 'sampling_type' not specified, yt will assume a cell 'sampling_type'
warnings.warn("Because 'sampling_type' not specified, yt will "

Out[11]:
YTArray([2.14072573e+11, 2.05195508e+11, 2.12049357e+11, ...,
1.83742921e+11, 1.82751487e+11, 1.83874598e+11]) Msun*kpc**2/Myr**2

### Obtaining Physical Constants in a Specific Unit System¶

Each unit system provides the ability to obtain any physical constant in yt's physical constants database in the base units of that system via the constants attribute of the unit system. For example, to obtain the value of Newton's universal constant of gravitation in different base units:

In [12]:
for name in ["cgs", "mks", "imperial", "planck", "geometrized"]:
unit_system = yt.unit_system_registry[name]
print (name, unit_system.constants.G)

cgs 6.67384e-08 cm**3/(g*s**2)
mks 6.67384e-11 m**3/(kg*s**2)
imperial 1.0690466160367012e-09 ft**3/(lbm*s**2)
planck 0.9999999999999999 l_pl**3/(m_pl*t_pl**2)
geometrized 1.0 l_geom**3/(m_geom*t_geom**2)


Equivalently, one could import a physical constant from the main database and convert it using in_base:

In [13]:
from yt.units import G
print (G.in_base("mks"))

6.67384e-11 m**3/(kg*s**2)


### Defining Your Own Unit System¶

You are not limited to using the unit systems already defined by yt. A new unit system can be defined by creating a new UnitSystem instance. For example, to create a unit system where the default units are in millimeters, centigrams, and microseconds:

In [14]:
small_unit_system = yt.UnitSystem("small", "mm", "cg", "us")


where the required arguments are a name for the unit system, and the length_unit, mass_unit, and time_unit for the unit system, which serve as the "base" units to convert everything else to. Once a unit system instance is created, it is automatically added to the unit_system_registry so that it may be used throughout yt:

In [15]:
yt.unit_system_registry["small"]

Out[15]:
small Unit System
Base Units:
length: mm
mass: cg
time: us
temperature: K
Other Units:

Note that the base units for the dimensions of angle and temperature have been automatically set to radians and Kelvin, respectively. If desired, these can be specified using optional arguments when creating the UnitSystem object:

In [16]:
wacky_unit_system = yt.UnitSystem("wacky", "mile", "kg", "day", temperature_unit="R", angle_unit="deg")
wacky_unit_system

Out[16]:
wacky Unit System
Base Units:
length: mile
mass: kg
time: day
temperature: R
angle: deg
Other Units:

Though it will rarely be necessary, an MKS-style system of units where a unit of current can be specified as a base unit can also be created using the current_mks optional argument:

In [17]:
mksish_unit_system = yt.UnitSystem("mksish", "dm", "ug", "ks", current_mks_unit="mA")
mksish_unit_system

Out[17]:
mksish Unit System
Base Units:
length: dm
mass: ug
time: ks
temperature: K
current_mks: mA
Other Units:

Initializing a UnitSystem object only sets up the base units. In this case, all fields will be converted to combinations of these base units based on their dimensionality. However, you may want to specify that fields of a given dimensionality use a compound unit by default instead. For example, you might prefer that in the "small" unit system that pressures be represented in microdynes per millimeter squared. To do this, set these to be the units of the "pressure" dimension explicitly:

In [18]:
small_unit_system["pressure"] = "udyne/mm**2"


We can now look at the small_unit_system object and see that these units are now defined for pressure in the "Other Units" category:

In [19]:
small_unit_system

Out[19]:
small Unit System
Base Units:
length: mm
mass: cg
time: us
temperature: K
Other Units:
pressure: udyne/mm**2

We can do the same for a few other dimensionalities:

In [20]:
small_unit_system["magnetic_field_cgs"] = "mG"
small_unit_system["specific_energy"] = "cerg/ug"
small_unit_system["velocity"] = "cm/s"
small_unit_system

Out[20]:
small Unit System
Base Units:
length: mm
mass: cg
time: us
temperature: K
velocity: cm/s