Repository: yt-project/unyt
Pull request #93 (2019-06-25 16:02:43)

Support loading json unit registries produced by yt.units
This should hopefully unblock https://github.com/yt-project/yt/pull/2219 once this is merged and we cut a new release yt can depend on. Ping @brittonsmith.

While working on this I found a bug in our definition of light year, it looks like when we went from using CGS base values to MKS the correction to the value of light year didn't go in the correct direction, leaving us off by a factor of 10,000 (100 squared).

Note that we need some minor changes on the yt side (which I have staged already but this needs to be merged first) to fully support loading old ytdata outputs.


Patch:
diff --git a/unyt/_physical_ratios.py b/unyt/_physical_ratios.py
index e9eaaba..03dc502 100644
--- a/unyt/_physical_ratios.py
+++ b/unyt/_physical_ratios.py
@@ -46,7 +46,7 @@ cm_per_mpc = 3.08567758e21
 km_per_m = 1e-3
 km_per_cm = 1e-5
 m_per_cm = 1e-2
-ly_per_m = 1.05702341e-20
+ly_per_m = 1.05702341e-16
 rsun_per_m = 1.4378145e-9
 rearth_per_m = 1.56961033e-7  # Mean (volumetric) radius
 rjup_per_m = 1.43039006737e-8  # Mean (volumetric) radius
diff --git a/unyt/unit_registry.py b/unyt/unit_registry.py
index 5c3d4d9..660bef5 100644
--- a/unyt/unit_registry.py
+++ b/unyt/unit_registry.py
@@ -251,8 +251,24 @@ class UnitRegistry:
         for k, v in data.items():
             unsan_v = list(v)
             unsan_v[1] = sympify(v[1], locals=vars(unyt_dims))
+            if len(unsan_v) == 4:
+                # old unit registry so we need to add SI-prefixability to the registry
+                # entry and correct the base_value to be in MKS units
+                if k in default_unit_symbol_lut:
+                    unsan_v.append(default_unit_symbol_lut[k][4])
+                else:
+                    unsan_v.append(False)
+                dims = unsan_v[1]
+                for dim_factor in dims.as_ordered_factors():
+                    dim, power = dim_factor.as_base_exp()
+                    if dim == unyt_dims.mass:
+                        unsan_v[0] /= 1000 ** float(power)
+                    if dim == unyt_dims.length:
+                        unsan_v[0] /= 100 ** float(power)
             lut[k] = tuple(unsan_v)
-
+        for k in default_unit_symbol_lut:
+            if k not in lut:
+                lut[k] = default_unit_symbol_lut[k]
         return cls(lut=lut, add_default_symbols=False)
 
     def list_same_dimensions(self, unit_object):


Test patch:
diff --git a/unyt/tests/old_json_registry.txt b/unyt/tests/old_json_registry.txt
new file mode 100644
index 0000000..ba797e6
--- /dev/null
+++ b/unyt/tests/old_json_registry.txt
@@ -0,0 +1,1 @@
+{"g": [1.0, "Symbol('(mass)', positive=True)", 0.0, "\\rm{g}"], "s": [1.0, "Symbol('(time)', positive=True)", 0.0, "\\rm{s}"], "K": [1.0, "Symbol('(temperature)', positive=True)", 0.0, "\\rm{K}"], "radian": [1.0, "Symbol('(angle)', positive=True)", 0.0, "\\rm{radian}"], "dyne": [1.0, "Mul(Symbol('(length)', positive=True), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{dyn}"], "erg": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{erg}"], "esu": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Rational(3, 2)), Pow(Symbol('(mass)', positive=True), Rational(1, 2)), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{esu}"], "gauss": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Rational(-1, 2)), Pow(Symbol('(mass)', positive=True), Rational(1, 2)), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{G}"], "degC": [1.0, "Symbol('(temperature)', positive=True)", -273.15, "^\\circ\\rm{C}"], "statA": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Rational(3, 2)), Pow(Symbol('(mass)', positive=True), Rational(1, 2)), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{statA}"], "statV": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Rational(1, 2)), Pow(Symbol('(mass)', positive=True), Rational(1, 2)), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{statV}"], "statohm": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Integer(-1)), Symbol('(time)', positive=True))", 0.0, "\\rm{statohm}"], "m": [100.0, "Symbol('(length)', positive=True)", 0.0, "\\rm{m}"], "J": [10000000.0, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{J}"], "W": [10000000.0, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-3)))", 0.0, "\\rm{W}"], "Hz": [1.0, "Pow(Symbol('(time)', positive=True), Integer(-1))", 0.0, "\\rm{Hz}"], "N": [100000.0, "Mul(Symbol('(length)', positive=True), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{N}"], "C": [1.0, "Mul(Symbol('(current_mks)', positive=True), Symbol('(time)', positive=True))", 0.0, "\\rm{C}"], "A": [1.0, "Symbol('(current_mks)', positive=True)", 0.0, "\\rm{A}"], "T": [1000.0, "Mul(Pow(Symbol('(current_mks)', positive=True), Integer(-1)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{T}"], "Pa": [10.0, "Mul(Pow(Symbol('(length)', positive=True), Integer(-1)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{Pa}"], "V": [10000000.0, "Mul(Pow(Symbol('(current_mks)', positive=True), Integer(-1)), Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-3)))", 0.0, "\\rm{V}"], "ohm": [10000000.0, "Mul(Pow(Symbol('(current_mks)', positive=True), Integer(-2)), Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-3)))", 0.0, "\\Omega"], "ft": [30.48, "Symbol('(length)', positive=True)", 0.0, "\\rm{ft}"], "mile": [160934.4, "Symbol('(length)', positive=True)", 0.0, "\\rm{mile}"], "degF": [0.5555555555555556, "Symbol('(temperature)', positive=True)", -459.67, "^\\circ\\rm{F}"], "R": [0.5555555555555556, "Symbol('(temperature)', positive=True)", 0.0, "^\\circ\\rm{R}"], "lbf": [444822.16152605, "Mul(Symbol('(length)', positive=True), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{lbf}"], "lbm": [453.59237, "Symbol('(mass)', positive=True)", 0.0, "\\rm{lbm}"], "atm": [1013250.0, "Mul(Pow(Symbol('(length)', positive=True), Integer(-1)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{atm}"], "h": [0.702, "Integer(1)", 0.0, "h"], "dimensionless": [1.0, "Integer(1)", 0.0, ""], "min": [60.0, "Symbol('(time)', positive=True)", 0.0, "\\rm{min}"], "hr": [3600.0, "Symbol('(time)', positive=True)", 0.0, "\\rm{hr}"], "day": [86400.0, "Symbol('(time)', positive=True)", 0.0, "\\rm{d}"], "d": [86400.0, "Symbol('(time)', positive=True)", 0.0, "\\rm{d}"], "yr": [31557600.0, "Symbol('(time)', positive=True)", 0.0, "\\rm{yr}"], "c": [29979245800.0, "Mul(Symbol('(length)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{c}"], "Msun": [1.98841586e+33, "Symbol('(mass)', positive=True)", 0.0, "M_\\odot"], "msun": [1.98841586e+33, "Symbol('(mass)', positive=True)", 0.0, "M_\\odot"], "Rsun": [69550001060.63751, "Symbol('(length)', positive=True)", 0.0, "R_\\odot"], "rsun": [69550001060.63751, "Symbol('(length)', positive=True)", 0.0, "R_\\odot"], "R_sun": [69550001060.63751, "Symbol('(length)', positive=True)", 0.0, "R_\\odot"], "r_sun": [69550001060.63751, "Symbol('(length)', positive=True)", 0.0, "R_\\odot"], "Lsun": [3.827e+33, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-3)))", 0.0, "L_\\odot"], "Tsun": [5870.0, "Symbol('(temperature)', positive=True)", 0.0, "T_\\odot"], "Zsun": [0.01295, "Integer(1)", 0.0, "Z_\\odot"], "Mjup": [1.8985234333630655e+30, "Symbol('(mass)', positive=True)", 0.0, "M_{\\rm{Jup}}"], "Mearth": [6.045644495102106e+27, "Symbol('(mass)', positive=True)", 0.0, "M_\\oplus"], "AU": [14959787075076.674, "Symbol('(length)', positive=True)", 0.0, "\\rm{AU}"], "au": [14959787075076.674, "Symbol('(length)', positive=True)", 0.0, "\\rm{AU}"], "ly": [9.460528409678268e+17, "Symbol('(length)', positive=True)", 0.0, "\\rm{ly}"], "pc": [3.0856775809623245e+18, "Symbol('(length)', positive=True)", 0.0, "\\rm{pc}"], "degree": [0.017453292519943295, "Symbol('(angle)', positive=True)", 0.0, "^\\circ"], "arcmin": [0.0002908882086657216, "Symbol('(angle)', positive=True)", 0.0, "\\rm{arcmin}"], "arcsec": [4.84813681109536e-06, "Symbol('(angle)', positive=True)", 0.0, "\\rm{arcsec}"], "mas": [4.8481368110953594e-09, "Symbol('(angle)', positive=True)", 0.0, "\\rm{mas}"], "hourangle": [0.2617993877991494, "Symbol('(angle)', positive=True)", 0.0, "\\rm{HA}"], "steradian": [1.0, "Pow(Symbol('(angle)', positive=True), Integer(2))", 0.0, "\\rm{sr}"], "lat": [-0.017453292519943295, "Symbol('(angle)', positive=True)", 90.0, "\\rm{Latitude}"], "lon": [0.017453292519943295, "Symbol('(angle)', positive=True)", -180.0, "\\rm{Longitude}"], "eV": [1.602176562e-12, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{eV}"], "amu": [1.660538921e-24, "Symbol('(mass)', positive=True)", 0.0, "\\rm{amu}"], "angstrom": [1e-08, "Symbol('(length)', positive=True)", 0.0, "\\AA"], "Jy": [1e-23, "Mul(Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{Jy}"], "counts": [1.0, "Integer(1)", 0.0, "\\rm{counts}"], "photons": [1.0, "Integer(1)", 0.0, "\\rm{photons}"], "me": [9.10938291e-28, "Symbol('(mass)', positive=True)", 0.0, "m_e"], "mp": [1.6737352238051868e-24, "Symbol('(mass)', positive=True)", 0.0, "m_p"], "mol": [6.02214129011674e+23, "Integer(1)", 0.0, "\\rm{mol}"], "Sv": [10000.0, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{Sv}"], "rayleigh": [79577.47154594767, "Mul(Pow(Symbol('(angle)', positive=True), Integer(-2)), Pow(Symbol('(length)', positive=True), Integer(-2)), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{R}"], "solMass": [1.98841586e+33, "Symbol('(mass)', positive=True)", 0.0, "M_\\odot"], "solRad": [69550001060.63751, "Symbol('(length)', positive=True)", 0.0, "R_\\odot"], "solLum": [3.827e+33, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-3)))", 0.0, "L_\\odot"], "dyn": [1.0, "Mul(Symbol('(length)', positive=True), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{dyn}"], "sr": [1.0, "Pow(Symbol('(angle)', positive=True), Integer(2))", 0.0, "\\rm{sr}"], "rad": [1.0, "Symbol('(angle)', positive=True)", 0.0, "\\rm{rad}"], "deg": [0.017453292519943295, "Symbol('(angle)', positive=True)", 0.0, "^\\circ"], "Fr": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Rational(3, 2)), Pow(Symbol('(mass)', positive=True), Rational(1, 2)), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{Fr}"], "G": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Rational(-1, 2)), Pow(Symbol('(mass)', positive=True), Rational(1, 2)), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{G}"], "Angstrom": [1e-08, "Symbol('(length)', positive=True)", 0.0, "\\AA"], "statC": [1.0, "Mul(Pow(Symbol('(length)', positive=True), Rational(3, 2)), Pow(Symbol('(mass)', positive=True), Rational(1, 2)), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{statC}"], "m_pl": [2.1764701184435688e-05, "Symbol('(mass)', positive=True)", 0.0, "m_{\\rm{P}}"], "l_pl": [1.6162283157597786e-33, "Symbol('(length)', positive=True)", 0.0, "\\ell_\\rm{P}"], "t_pl": [5.391157357800438e-44, "Symbol('(time)', positive=True)", 0.0, "t_{\\rm{P}}"], "T_pl": [1.4168076561665009e+32, "Symbol('(temperature)', positive=True)", 0.0, "T_{\\rm{P}}"], "q_pl": [1.8755459562040954e-18, "Mul(Symbol('(current_mks)', positive=True), Symbol('(time)', positive=True))", 0.0, "q_{\\rm{P}}"], "E_pl": [1.9561137903170922e+16, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "E_{\\rm{P}}"], "m_geom": [1.98841586e+33, "Symbol('(mass)', positive=True)", 0.0, "M_\\odot"], "l_geom": [147658.08127593446, "Symbol('(length)', positive=True)", 0.0, "M_\\odot"], "t_geom": [4.925343427950227e-06, "Symbol('(time)', positive=True)", 0.0, "M_\\odot"], "R_earth": [637100801.9550942, "Symbol('(length)', positive=True)", 0.0, "R_\\oplus"], "r_earth": [637100801.9550942, "Symbol('(length)', positive=True)", 0.0, "R_\\oplus"], "R_jup": [6991100000.006706, "Symbol('(length)', positive=True)", 0.0, "R_\\mathrm{Jup}"], "r_jup": [6991100000.006706, "Symbol('(length)', positive=True)", 0.0, "R_\\mathrm{Jup}"], "code_length": [1.4065766789943524e+26, "Symbol('(length)', positive=True)", 0.0, "\\rm{code\\ length}"], "code_mass": [7.032854288130045e+48, "Symbol('(mass)', positive=True)", 0.0, "\\rm{code\\ mass}"], "code_density": [2.527210293640965e-30, "Mul(Pow(Symbol('(length)', positive=True), Integer(-3)), Symbol('(mass)', positive=True))", 0.0, "\\rm{code\\ density}"], "code_specific_energy": [2.1385728000000008e+18, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{code\\ specific\\ energy}"], "code_time": [1885954521538155.5, "Symbol('(time)', positive=True)", 0.0, "\\rm{code\\ time}"], "code_magnetic": [8.24114664867782e-06, "Mul(Pow(Symbol('(length)', positive=True), Rational(-1, 2)), Pow(Symbol('(mass)', positive=True), Rational(1, 2)), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{code\\ magnetic}"], "code_temperature": [1.0, "Symbol('(temperature)', positive=True)", 0.0, "\\rm{code\\ temperature}"], "code_pressure": [1.4057425005421192e-08, "Mul(Pow(Symbol('(length)', positive=True), Integer(-1)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{code\\ pressure}"], "code_velocity": [1462385995.556577, "Mul(Symbol('(length)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-1)))", 0.0, "\\rm{code\\ velocity}"], "code_metallicity": [1.0, "Integer(1)", 0.0, "\\rm{code\\ metallicity}"], "a": [1.0000000027810865, "Integer(1)", 0.0, "\\rm{a}"], "mcm": [100.00000027810864, "Symbol('(length)', positive=True)", 0.0, "\\rm{m}/(1+z)"], "pccm": [3.08567758954386e+18, "Symbol('(length)', positive=True)", 0.0, "\\rm{pc}/(1+z)"], "AUcm": [14959787116681.133, "Symbol('(length)', positive=True)", 0.0, "\\rm{AU}/(1+z)"], "aucm": [14959787116681.133, "Symbol('(length)', positive=True)", 0.0, "\\rm{au}/(1+z)"], "Mpccm": [3.08567758954386e+24, "Symbol('(length)', positive=True)", 0.0, "\\rm{Mpc}/(1+z)"], "cm": [1.0, "Symbol('(length)', positive=True)", 0.0, "\\rm{cm}"], "unitary": [1.4065766789943524e+26, "Symbol('(length)', positive=True)", 0.0, "\\rm{unitary}"], "keV": [1.602176562e-09, "Mul(Pow(Symbol('(length)', positive=True), Integer(2)), Symbol('(mass)', positive=True), Pow(Symbol('(time)', positive=True), Integer(-2)))", 0.0, "\\rm{keV}"]}
\ No newline at end of file
diff --git a/unyt/tests/test_unit_registry.py b/unyt/tests/test_unit_registry.py
index 9f6cbb0..74fcc87 100644
--- a/unyt/tests/test_unit_registry.py
+++ b/unyt/tests/test_unit_registry.py
@@ -14,8 +14,11 @@ Test unit lookup tables and registry
 # The full license is in the LICENSE file, distributed with this software.
 # -----------------------------------------------------------------------------
 
+import os
 import pytest
 
+from numpy.testing import assert_allclose
+
 from unyt.dimensions import length, energy
 from unyt.exceptions import SymbolNotFoundError, UnitParseError
 from unyt.unit_object import Unit
@@ -85,3 +88,20 @@ def test_registry_json():
 
     assert reg.lut["m"][1] is length
     assert reg.lut["erg"][1] is energy
+
+
+def test_old_registry_json():
+    path = os.sep.join([os.path.dirname(__file__), "old_json_registry.txt"])
+    with open(path) as f:
+        json_text = f.read()
+    reg = UnitRegistry.from_json(json_text)
+    default_reg = UnitRegistry()
+
+    loaded_keys = reg.keys()
+
+    for k in default_reg.keys():
+        assert k in loaded_keys
+        loaded_val = reg[k]
+        val = default_reg[k]
+        assert_allclose(loaded_val[0], val[0])
+        assert loaded_val[1:] == val[1:]