Unit Systems


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
cgs Unit System
 Base Units:
  length: cm
  mass: g
  time: s
  temperature: K
  angle: rad
 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]:
mks Unit System
 Base Units:
  length: m
  mass: kg
  time: s
  temperature: K
  angle: rad
  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]:
imperial Unit System
 Base Units:
  length: ft
  mass: lbm
  time: s
  temperature: R
  angle: rad
 Other Units:
  force: lbf
  energy: ft*lbf
  pressure: lbf/ft**2

and the "galactic" system as well:

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

Converting YTArrays to the Different Unit Systems

Choosing a Unit System When Loading a Dataset

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+63  3.92971123e+63  3.95375251e+63 ... -2.39040683e+63
 -2.39880446e+63 -2.44245785e+63] 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"]
ds_flash.add_field(("gas","momentum_x"), function=_momentum_x, units=mom_units)
<ipython-input-9-4bc86cd77896>:4: UserWarning: Because 'sampling_type' not specified, yt will assume a cell 'sampling_type'
  ds_flash.add_field(("gas","momentum_x"), function=_momentum_x, units=mom_units)

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

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
yt.add_field(("gas","rest_energy"), function=_rest_energy, units="auto", dimensions="energy")

ds_flash2 = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150", unit_system="galactic")

sp = ds_flash2.sphere("c", (100.,"kpc"))
/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 "
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]:
small Unit System
 Base Units:
  length: mm
  mass: cg
  time: us
  temperature: K
  angle: rad
 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
 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
 Base Units:
  length: dm
  mass: ug
  time: ks
  temperature: K
  angle: rad
  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
 Base Units:
  length: mm
  mass: cg
  time: us
  temperature: K
  angle: rad
 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
 Base Units:
  length: mm
  mass: cg
  time: us
  temperature: K
  angle: rad
 Other Units:
  pressure: udyne/mm**2
  magnetic_field_cgs: mG
  specific_energy: cerg/ug
  velocity: cm/s

(7)_Unit_Systems.ipynb; 7)_Unit_Systems_evaluated.ipynb; 7)_Unit_Systems.py)