<jupyter_start><jupyter_text>In yt 3.0, we want to make it easier to access "raw" simulation data that a code writes directly to disk. The new unit system makes it much simpler to convert back and forth between physical coordinates and the unscaled "raw" coordinate system used internally in the simulation code.  In some cases, this conversion involves transforming to comoving coordinates, so that is also covered here.  Code units Let's take a look at a cosmological enzo dataset to play with converting between physical units and code units:<jupyter_code>import yt
ds = yt.load('Enzo_64/DD0043/data0043')<jupyter_output><empty_output><jupyter_text>The conversion factors between Enzo's internal unit system and the physical CGS system are stored in the dataset's `unit_registry` object.  Code units have names like `code_length` and `code_time`. Let's take a look at the names of all of the code units, along with their CGS conversion factors for this cosmological enzo dataset:<jupyter_code>reg = ds.unit_registry

for un in reg.keys():
    if un.startswith('code_'):
        fmt_tup = (un, reg.lut[un][0], str(reg.lut[un][1]))
        print ("Unit name:      {:<15}\nCGS conversion: {:<15}\nDimensions:     {:<15}\n".format(*fmt_tup))
fmt_tup<jupyter_output><empty_output><jupyter_text>Most of the time you will not have to deal with the unit registry.  For example, the conversion factors to code units are stored as attributes of the dataset object:<jupyter_code>print ("Length unit: ", ds.length_unit)
print ("Time unit: ", ds.time_unit)
print ("Mass unit: ", ds.mass_unit)
print ("Velocity unit: ", ds.velocity_unit)<jupyter_output><empty_output><jupyter_text>Conversion factors will be supplied in CGS by default.  We can also ask what the conversion factors are in code units.<jupyter_code>print ("Length unit: ", ds.length_unit.in_units('code_length'))
print ("Time unit: ", ds.time_unit.in_units('code_time'))
print ("Mass unit: ", ds.mass_unit.in_units('code_mass'))
print ("Velocity unit: ", ds.velocity_unit.in_units('code_velocity'))<jupyter_output><empty_output><jupyter_text>as expected, all the conversion factors are unity in code units. We can also play with unit conversions on `ds.domain_width`.  First, we see for enzo how code length units are defined relative to the domain width:<jupyter_code>ds.domain_width
ds.domain_width.in_cgs()
ds.domain_width.in_units('Mpccm/h')<jupyter_output><empty_output><jupyter_text>Comoving units This last example uses a cosmological unit.  In english, I asked for the domain width in comoving megaparsecs, scaled as if the hubble constant were 100 km/s/Mpc.  Although $h$ isn't really a unit, yt treats it as one for the purposes of the unit system.  As an aside, <NAME>'s [research note](http://arxiv.org/abs/1308.4150) on the history, use, and interpretation of $h$ as it appears in the astronomical literature is pretty much required reading for anyone who has to deal with factors of $h$ every now and then.In yt, comoving length unit symbols are named following the pattern `(length symbol)cm`, i.e. `pccm` for comoving parsec or `mcm` for a comoving meter.  A comoving length unit is different from the normal length unit by a factor of $(1+z)$:<jupyter_code>z = ds.current_redshift
 
print (ds.quan(1, 'Mpc')/ds.quan(1, 'Mpccm'))
print (1+z)<jupyter_output><empty_output><jupyter_text>As we saw before, $h$ is treated like any other unit symbol. It has `dimensionless` units, just like a scalar:<jupyter_code>print (ds.quan(1, 'Mpc')/ds.quan(1, 'Mpc/h'))
print (ds.hubble_constant)<jupyter_output><empty_output><jupyter_text>These units can be used in readily used in plots and anywhere a length unit is appropriate in yt.<jupyter_code>slc = yt.SlicePlot(ds, 0, 'density', width=(128, 'Mpccm/h'))
slc.set_figure_size(6)<jupyter_output><empty_output><jupyter_text>The unit registry When you create a `YTArray` without referring to a unit registry, yt uses the default unit registry, which does not include code units or comoving units.<jupyter_code>from yt import YTQuantity

a = YTQuantity(3, 'cm')

print (a.units.registry.keys())<jupyter_output><empty_output><jupyter_text>When a dataset is loaded, yt infers conversion factors from the internal simulation unit system to the CGS unit system.  These conversion factors are stored in a `unit_registry` along with conversion factors to the other known unit symbols.  For the cosmological Enzo dataset we loaded earlier, we can see there are a number of additional unit symbols not defined in the default unit lookup table:<jupyter_code>print (sorted([k for k in ds.unit_registry.keys() if k not in a.units.registry.keys()]))<jupyter_output><empty_output><jupyter_text>Since code units do not appear in the default unit symbol lookup table, one must explicitly refer to a unit registry when creating a `YTArray` to be able to convert to the unit system of a simulation. To make this as clean as possible, there are array and quantity-creating convenience functions attached to the `Dataset` object:* `ds.arr()`* `ds.quan()`These functions make it straightforward to create arrays and quantities that can be converted to code units or comoving units.  For example:<jupyter_code>a = ds.quan(3, 'code_length')

print (a)
print (a.in_cgs())
print (a.in_units('Mpccm/h'))
b = ds.arr([3, 4, 5], 'Mpccm/h')
print (b)
print (b.in_cgs())<jupyter_output><empty_output><jupyter_text>Overriding Code Unit Definitions On occasion, you might have a dataset for a supported frontend that does not have the conversions to code units accessible (for example, Athena data) or you may want to change them outright. `yt` provides a mechanism so that one may provide their own code unit definitions to `load`, which override the default rules for a given frontend for defining code units. This is provided through the `units_override` dictionary. We'll use an example of an Athena dataset. First, a call to `load` without `units_override`:<jupyter_code>ds1 = yt.load("MHDSloshing/virgo_low_res.0054.vtk")
print (ds1.length_unit)
print (ds1.mass_unit)
print (ds1.time_unit)
sp1 = ds1.sphere("c",(0.1,"unitary"))
print (sp1["density"])<jupyter_output><empty_output><jupyter_text>This is a galaxy cluster dataset, so it is not likely that the units of density are correct. We happen to know that the unit definitions are different, so we can override the units:<jupyter_code>units_override = {"length_unit":(1.0,"Mpc"),
                  "time_unit":(1.0,"Myr"),
                  "mass_unit":(1.0e14,"Msun")}<jupyter_output><empty_output><jupyter_text>`units_override` can take the following keys:* `length_unit`* `time_unit`* `mass_unit`* `magnetic_unit`* `temperature_unit`and the associated values can be (value, unit) tuples, `YTQuantities`, or floats (in the latter case they are assumed to have the corresponding cgs unit).<jupyter_code>ds2 = yt.load("MHDSloshing/virgo_low_res.0054.vtk", units_override=units_override)
print (ds2.length_unit)
print (ds2.mass_unit)
print (ds2.time_unit)
sp2 = ds2.sphere("c",(0.1,"unitary"))
print (sp2["density"])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09733013137448793, 'fraction_numerical': 0.011442294109337477, 'mean_word_length': 5.641651031894934, 'pattern_counts': {'":': 3, '<': 61, '<?xml version=': 0, '>': 58, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '428421', 'license': 'BSD-3-Clause-Clear', 'n_tokens_mistral': 2251, 'n_tokens_neox': 2173, 'n_words': 840, 'path': 'doc/source/analyzing/units/3)_Comoving_units_and_code_units.ipynb', 'repo_name': 'ninaMc/yt', 'size': '7086'}