Repository: yt-project/unyt
Pull request #22 (2018-06-28 21:59:51)

Use dimensions of units to do CGS <-> MKS conversions.
This PR refactors the CGS <-> MKS conversions to be based around dimensions. This makes it possible to convert code magnetic units between unit systems (or any unit that has the same dimensions as gauss or tesla).

This fixes issues with converting code E&M magnetic units between unit systems and I've added a test to capture this use case.

This came up in the context of getting yt to use unyt under the hood.


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 76d8876..d0afbd4 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -6,12 +6,6 @@ unyt began life as a submodule of yt named yt.units.
 
 It was separated from yt.units as its own package in 2018.
 
-1.0.4 (2018-08-08)
-------------------
-
-* Expand installation instructions
-* Mention paper and arxiv submission in the readme.
-
 1.0.3 (2018-06-06)
 ------------------
 
diff --git a/README.rst b/README.rst
index 74a508e..c1c2a83 100644
--- a/README.rst
+++ b/README.rst
@@ -35,7 +35,7 @@ A package for handling numpy arrays with units.
 Often writing code that deals with data that has units can be confusing. A
 function might return an array but at least with plain NumPy arrays, there is no
 way to easily tell what the units of the data are without somehow knowing *a
-priori*.
+prioi*.
 
 The ``unyt`` package (pronounced like "unit") provides a subclass of NumPy's
 ``ndarray`` class that knows about units. For example, one could do:
diff --git a/unyt/_unit_lookup_table.py b/unyt/_unit_lookup_table.py
index 2289a40..fc7cfd8 100644
--- a/unyt/_unit_lookup_table.py
+++ b/unyt/_unit_lookup_table.py
@@ -60,164 +60,160 @@ import numpy as np
 
 default_unit_symbol_lut = {
     # base
-    "m": (1.0, dimensions.length, 0.0, r"\rm{m}", True),
-    "g":  (1.0e-3, dimensions.mass, 0.0, r"\rm{g}", True),
-    "s":  (1.0, dimensions.time, 0.0, r"\rm{s}", True),
-    "K":  (1.0, dimensions.temperature, 0.0, r"\rm{K}", True),
-    "radian": (1.0, dimensions.angle, 0.0, r"\rm{radian}", True),
-    "A": (1.0, dimensions.current_mks, 0.0, r"\rm{A}", True),
-    "cd": (1.0, dimensions.luminous_intensity, 0.0, r"\rm{cd}", True),
-    "mol": (1.0 / amu_grams, dimensions.dimensionless, 0.0, r"\rm{mol}", True),
+    "m": (1.0, dimensions.length, 0.0, r"\rm{m}"),
+    "g":  (1.0e-3, dimensions.mass, 0.0, r"\rm{g}"),
+    "s":  (1.0, dimensions.time, 0.0, r"\rm{s}"),
+    "K":  (1.0, dimensions.temperature, 0.0, r"\rm{K}"),
+    "radian": (1.0, dimensions.angle, 0.0, r"\rm{radian}"),
+    "A": (1.0, dimensions.current_mks, 0.0, r"\rm{A}"),
+    "cd": (1.0, dimensions.luminous_intensity, 0.0, r"\rm{cd}"),
+    "mol": (1.0 / amu_grams, dimensions.dimensionless, 0.0, r"\rm{mol}"),
 
     # some cgs
-    "dyne": (1.0e-5, dimensions.force, 0.0, r"\rm{dyn}", True),
-    "erg":  (1.0e-7, dimensions.energy, 0.0, r"\rm{erg}", True),
-    "Ba": (0.1, dimensions.pressure, 0.0, r"\rm{Ba}", True),
-    "esu":  (1.0e-3**1.5, dimensions.charge_cgs, 0.0, r"\rm{esu}", True),
-    "gauss": (0.1**0.5, dimensions.magnetic_field_cgs, 0.0, r"\rm{G}", True),
-    "degC": (1.0, dimensions.temperature, -273.15, r"^\circ\rm{C}", True),
-    "statA": (1.0e-3**1.5, dimensions.current_cgs, 0.0, r"\rm{statA}", True),
+    "dyne": (1.0e-5, dimensions.force, 0.0, r"\rm{dyn}"),
+    "erg":  (1.0e-7, dimensions.energy, 0.0, r"\rm{erg}"),
+    "Ba": (0.1, dimensions.pressure, 0.0, r"\rm{Ba}"),
+    "esu":  (1.0e-3**1.5, dimensions.charge_cgs, 0.0, r"\rm{esu}"),
+    "gauss": (0.1**0.5, dimensions.magnetic_field_cgs, 0.0, r"\rm{G}"),
+    "degC": (1.0, dimensions.temperature, -273.15, r"^\circ\rm{C}"),
+    "statA": (1.0e-3**1.5, dimensions.current_cgs, 0.0, r"\rm{statA}"),
     "statV": (0.1*1.0e-3**0.5, dimensions.electric_potential_cgs,
-              0.0, r"\rm{statV}", True),
-    "statohm": (100.0, dimensions.resistance_cgs, 0.0, r"\rm{statohm}", True),
-    "Mx": (1.0e-3**1.5, dimensions.magnetic_flux_cgs, 0.0, r"\rm{Mx}", True),
+              0.0, r"\rm{statV}"),
+    "statohm": (100.0, dimensions.resistance_cgs, 0.0, r"\rm{statohm}"),
+    "Mx": (1.0e-3**1.5, dimensions.magnetic_flux_cgs, 0.0, r"\rm{Mx}"),
 
     # some SI
-    "J": (1.0, dimensions.energy, 0.0, r"\rm{J}", True),
-    "W": (1.0, dimensions.power, 0.0, r"\rm{W}", True),
-    "Hz": (1.0, dimensions.rate, 0.0, r"\rm{Hz}", True),
-    "N": (1.0, dimensions.force, 0.0, r"\rm{N}", True),
-    "C": (1.0, dimensions.charge_mks, 0.0, r"\rm{C}", True),
-    "T": (1.0, dimensions.magnetic_field_mks, 0.0, r"\rm{T}", True),
-    "Pa": (1.0, dimensions.pressure, 0.0, r"\rm{Pa}", True),
-    "V": (1.0, dimensions.electric_potential_mks, 0.0, r"\rm{V}", True),
-    "ohm": (1.0, dimensions.resistance_mks, 0.0, r"\Omega", True),
-    "Wb": (1.0, dimensions.magnetic_flux_mks, 0.0, r"\rm{Wb}", True),
-    "lm": (1.0, dimensions.luminous_flux, 0.0, r"\rm{lm}", True),
-    "lx": (1.0, dimensions.luminous_flux/dimensions.area, 0.0, r"\rm{lx}",
-           True),
+    "J": (1.0, dimensions.energy, 0.0, r"\rm{J}"),
+    "W": (1.0, dimensions.power, 0.0, r"\rm{W}"),
+    "Hz": (1.0, dimensions.rate, 0.0, r"\rm{Hz}"),
+    "N": (1.0, dimensions.force, 0.0, r"\rm{N}"),
+    "C": (1.0, dimensions.charge_mks, 0.0, r"\rm{C}"),
+    "T": (1.0, dimensions.magnetic_field_mks, 0.0, r"\rm{T}"),
+    "Pa": (1.0, dimensions.pressure, 0.0, r"\rm{Pa}"),
+    "V": (1.0, dimensions.electric_potential_mks, 0.0, r"\rm{V}"),
+    "ohm": (1.0, dimensions.resistance_mks, 0.0, r"\Omega"),
+    "Wb": (1.0, dimensions.magnetic_flux_mks, 0.0, r"\rm{Wb}"),
+    "lm": (1.0, dimensions.luminous_flux, 0.0, r"\rm{lm}"),
+    "lx": (1.0, dimensions.luminous_flux/dimensions.area, 0.0, r"\rm{lx}"),
 
     # Imperial and other non-metric units
-    "inch": (m_per_inch, dimensions.length, 0.0, r"\rm{in}", False),
-    "ft": (m_per_ft, dimensions.length, 0.0, r"\rm{ft}", False),
-    "yd": (0.9144, dimensions.length, 0.0, r"\rm{yd}", False),
-    "mile": (1609.344, dimensions.length, 0.0, r"\rm{mile}", False),
-    "fur": (m_per_ft*660.0, dimensions.length, 0.0, r"\rm{fur}", False),
+    "inch": (m_per_inch, dimensions.length, 0.0, r"\rm{in}"),
+    "ft": (m_per_ft, dimensions.length, 0.0, r"\rm{ft}"),
+    "yd": (0.9144, dimensions.length, 0.0, r"\rm{yd}"),
+    "mile": (1609.344, dimensions.length, 0.0, r"\rm{mile}"),
+    "fur": (m_per_ft*660.0, dimensions.length, 0.0, r"\rm{fur}"),
     "degF": (kelvin_per_rankine, dimensions.temperature, -459.67,
-             "^\circ\rm{F}", False),
-    "R": (kelvin_per_rankine, dimensions.temperature, 0.0, r"^\circ\rm{R}",
-          False),
+             "^\circ\rm{F}"),
+    "R": (kelvin_per_rankine, dimensions.temperature, 0.0, r"^\circ\rm{R}"),
     "lbf": (kg_per_pound*standard_gravity_m_per_s2,
-            dimensions.force, 0.0, r"\rm{lbf}", False),
-    "lb": (kg_per_pound, dimensions.mass, 0.0, r"\rm{lb}", False),
-    "lbm": (kg_per_pound, dimensions.mass, 0.0, r"\rm{lbm}", False),
-    "atm": (pascal_per_atm, dimensions.pressure, 0.0, r"\rm{atm}", False),
-    "hp": (watt_per_horsepower, dimensions.power, 0.0, r"\rm{hp}", False),
-    "oz": (kg_per_pound/16.0, dimensions.mass, 0.0, r"\rm{oz}", False),
-    "ton": (kg_per_pound*2000.0, dimensions.mass, 0.0, r"\rm{ton}", False),
+            dimensions.force, 0.0, r"\rm{lbf}"),
+    "lb": (kg_per_pound, dimensions.mass, 0.0, r"\rm{lb}"),
+    "lbm": (kg_per_pound, dimensions.mass, 0.0, r"\rm{lbm}"),
+    "atm": (pascal_per_atm, dimensions.pressure, 0.0, r"\rm{atm}"),
+    "hp": (watt_per_horsepower, dimensions.power, 0.0, r"\rm{hp}"),
+    "oz": (kg_per_pound/16.0, dimensions.mass, 0.0, r"\rm{oz}"),
+    "ton": (kg_per_pound*2000.0, dimensions.mass, 0.0, r"\rm{ton}"),
     "slug": (kg_per_pound*standard_gravity_m_per_s2/m_per_ft,
-             dimensions.mass, 0.0, r"\rm{slug}", False),
-    "cal": (4.184, dimensions.energy, 0.0, r"\rm{cal}", True),
-    "BTU": (1055.0559, dimensions.energy, 0.0, r"\rm{BTU}", False),
+             dimensions.mass, 0.0, r"\rm{slug}"),
+    "cal": (4.184, dimensions.energy, 0.0, r"\rm{cal}"),
+    "BTU": (1055.0559, dimensions.energy, 0.0, r"\rm{BTU}"),
     "psi": (kg_per_pound*standard_gravity_m_per_s2/m_per_inch**2,
-            dimensions.pressure, 0.0, r"\rm{psi}", False),
-    "smoot": (1.7018, dimensions.length, 0.0, r"\rm{smoot}", False),
+            dimensions.pressure, 0.0, r"\rm{psi}"),
+    "smoot": (1.7018, dimensions.length, 0.0, r"\rm{smoot}"),
 
     # dimensionless stuff
-    "h": (1.0, dimensions.dimensionless, 0.0, r"h", False),
-    "dimensionless": (1.0, dimensions.dimensionless, 0.0, r"", False),
+    "h": (1.0, dimensions.dimensionless, 0.0, r"h"),
+    "dimensionless": (1.0, dimensions.dimensionless, 0.0, r""),
 
     # times
-    "min": (sec_per_min, dimensions.time, 0.0, r"\rm{min}", False),
-    "hr":  (sec_per_hr, dimensions.time, 0.0, r"\rm{hr}", False),
-    "day": (sec_per_day, dimensions.time, 0.0, r"\rm{d}", False),
-    "d": (sec_per_day, dimensions.time, 0.0, r"\rm{d}", False),
-    "yr":  (sec_per_year, dimensions.time, 0.0, r"\rm{yr}", True),
+    "min": (sec_per_min, dimensions.time, 0.0, r"\rm{min}"),
+    "hr":  (sec_per_hr, dimensions.time, 0.0, r"\rm{hr}"),
+    "day": (sec_per_day, dimensions.time, 0.0, r"\rm{d}"),
+    "d": (sec_per_day, dimensions.time, 0.0, r"\rm{d}"),
+    "yr":  (sec_per_year, dimensions.time, 0.0, r"\rm{yr}"),
 
     # Velocities
-    "c": (speed_of_light_m_per_s, dimensions.velocity, 0.0, r"\rm{c}", False),
+    "c": (speed_of_light_m_per_s, dimensions.velocity, 0.0, r"\rm{c}"),
 
     # Solar units
-    "Msun": (mass_sun_kg, dimensions.mass, 0.0, r"M_\odot", False),
-    "msun": (mass_sun_kg, dimensions.mass, 0.0, r"M_\odot", False),
-    "Rsun": (m_per_rsun, dimensions.length, 0.0, r"R_\odot", False),
-    "rsun": (m_per_rsun, dimensions.length, 0.0, r"R_\odot", False),
-    "R_sun": (m_per_rsun, dimensions.length, 0.0, r"R_\odot", False),
-    "r_sun": (m_per_rsun, dimensions.length, 0.0, r"R_\odot", False),
-    "Lsun": (luminosity_sun_watts, dimensions.power, 0.0, r"L_\odot", False),
-    "Tsun": (temp_sun_kelvin, dimensions.temperature, 0.0, r"T_\odot", False),
-    "Zsun": (metallicity_sun, dimensions.dimensionless, 0.0, r"Z_\odot",
-             False),
-    "Mjup": (mass_jupiter_kg, dimensions.mass, 0.0, r"M_{\rm{Jup}}", False),
-    "Mearth": (mass_earth_kg, dimensions.mass, 0.0, r"M_\oplus", False),
-    "R_jup": (m_per_rjup, dimensions.length, 0.0, r"R_\mathrm{Jup}", False),
-    "r_jup": (m_per_rjup, dimensions.length, 0.0, r"R_\mathrm{Jup}", False),
-    "R_earth": (m_per_rearth, dimensions.length, 0.0, r"R_\oplus", False),
-    "r_earth": (m_per_rearth, dimensions.length, 0.0, r"R_\oplus", False),
+    "Msun": (mass_sun_kg, dimensions.mass, 0.0, r"M_\odot"),
+    "msun": (mass_sun_kg, dimensions.mass, 0.0, r"M_\odot"),
+    "Rsun": (m_per_rsun, dimensions.length, 0.0, r"R_\odot"),
+    "rsun": (m_per_rsun, dimensions.length, 0.0, r"R_\odot"),
+    "R_sun": (m_per_rsun, dimensions.length, 0.0, r"R_\odot"),
+    "r_sun": (m_per_rsun, dimensions.length, 0.0, r"R_\odot"),
+    "Lsun": (luminosity_sun_watts, dimensions.power, 0.0, r"L_\odot"),
+    "Tsun": (temp_sun_kelvin, dimensions.temperature, 0.0, r"T_\odot"),
+    "Zsun": (metallicity_sun, dimensions.dimensionless, 0.0, r"Z_\odot"),
+    "Mjup": (mass_jupiter_kg, dimensions.mass, 0.0, r"M_{\rm{Jup}}"),
+    "Mearth": (mass_earth_kg, dimensions.mass, 0.0, r"M_\oplus"),
 
     # astro distances
-    "AU": (m_per_au, dimensions.length, 0.0, r"\rm{AU}", False),
-    "au": (m_per_au, dimensions.length, 0.0, r"\rm{AU}", False),
-    "ly": (m_per_ly, dimensions.length, 0.0, r"\rm{ly}", False),
-    "pc": (m_per_pc, dimensions.length, 0.0, r"\rm{pc}", True),
+    "AU": (m_per_au, dimensions.length, 0.0, r"\rm{AU}"),
+    "au": (m_per_au, dimensions.length, 0.0, r"\rm{AU}"),
+    "ly": (m_per_ly, dimensions.length, 0.0, r"\rm{ly}"),
+    "pc": (m_per_pc, dimensions.length, 0.0, r"\rm{pc}"),
 
     # angles
-    "degree": (np.pi/180., dimensions.angle, 0.0, r"\rm{deg}", False),
+    "degree": (np.pi/180., dimensions.angle, 0.0, r"\rm{deg}"),  # degrees
     "arcmin": (np.pi/10800., dimensions.angle, 0.0,
-               r"\rm{arcmin}", False),  # arcminutes
+               r"\rm{arcmin}"),  # arcminutes
     "arcsec": (np.pi/648000., dimensions.angle, 0.0,
-               r"\rm{arcsec}", False),  # arcseconds
+               r"\rm{arcsec}"),  # arcseconds
     "mas": (np.pi/648000000., dimensions.angle, 0.0,
-            r"\rm{mas}", False),  # milliarcseconds
-    "hourangle": (np.pi/12., dimensions.angle, 0.0, r"\rm{HA}", False),
-    "steradian": (1.0, dimensions.solid_angle, 0.0, r"\rm{sr}", False),
-    "lat": (-np.pi/180.0, dimensions.angle, 90.0, r"\rm{Latitude}", False),
-    "lon": (np.pi/180.0, dimensions.angle, -180.0, r"\rm{Longitude}", False),
+            r"\rm{mas}"),  # milliarcseconds
+    "hourangle": (np.pi/12., dimensions.angle, 0.0, r"\rm{HA}"),  # hour angle
+    "steradian": (1.0, dimensions.solid_angle, 0.0, r"\rm{sr}"),
+    "lat": (-np.pi/180.0, dimensions.angle, 90.0, r"\rm{Latitude}"),
+    "lon": (np.pi/180.0, dimensions.angle, -180.0, r"\rm{Longitude}"),
 
     # misc
-    "eV": (J_per_eV, dimensions.energy, 0.0, r"\rm{eV}", True),
-    "amu": (amu_kg, dimensions.mass, 0.0, r"\rm{amu}", False),
-    "angstrom": (m_per_ang, dimensions.length, 0.0, r"\AA", False),
-    "Jy": (jansky_mks, dimensions.specific_flux, 0.0, r"\rm{Jy}", True),
-    "counts": (1.0, dimensions.dimensionless, 0.0, r"\rm{counts}", False),
-    "photons": (1.0, dimensions.dimensionless, 0.0, r"\rm{photons}", False),
-    "me": (mass_electron_kg, dimensions.mass, 0.0, r"m_e", False),
-    "mp": (mass_hydrogen_kg, dimensions.mass, 0.0, r"m_p", False),
-    'Sv': (1.0, dimensions.specific_energy, 0.0, r"\rm{Sv}", True),
-    "rayleigh": (2.5e9/np.pi, dimensions.count_intensity, 0.0, r"\rm{R}",
-                 False),
-    "lambert": (1.0e4/np.pi, dimensions.luminance, 0.0, r"\rm{L}", False),
-    "nt": (1.0, dimensions.luminance, 0.0, r"\rm{nt}", False),
+    "eV": (J_per_eV, dimensions.energy, 0.0, r"\rm{eV}"),
+    "amu": (amu_kg, dimensions.mass, 0.0, r"\rm{amu}"),
+    "angstrom": (m_per_ang, dimensions.length, 0.0, r"\AA"),
+    "Jy": (jansky_mks, dimensions.specific_flux, 0.0, r"\rm{Jy}"),
+    "counts": (1.0, dimensions.dimensionless, 0.0, r"\rm{counts}"),
+    "photons": (1.0, dimensions.dimensionless, 0.0, r"\rm{photons}"),
+    "me": (mass_electron_kg, dimensions.mass, 0.0, r"m_e"),
+    "mp": (mass_hydrogen_kg, dimensions.mass, 0.0, r"m_p"),
+    'Sv': (1.0, dimensions.specific_energy, 0.0, r"\rm{Sv}"),
+    "rayleigh": (2.5e9/np.pi, dimensions.count_intensity, 0.0, r"\rm{R}"),
+    "lambert": (1.0e4/np.pi, dimensions.luminance, 0.0, r"\rm{L}"),
+    "nt": (1.0, dimensions.luminance, 0.0, r"\rm{nt}"),
 
     # for AstroPy compatibility
-    "solMass": (mass_sun_kg, dimensions.mass, 0.0, r"M_\odot", False),
-    "solRad": (m_per_rsun, dimensions.length, 0.0, r"R_\odot", False),
-    "solLum": (luminosity_sun_watts, dimensions.power, 0.0, r"L_\odot", False),
-    "dyn": (1.0e-5, dimensions.force, 0.0, r"\rm{dyn}", False),
-    "sr": (1.0, dimensions.solid_angle, 0.0, r"\rm{sr}", False),
-    "rad": (1.0, dimensions.angle, 0.0, r"\rm{rad}", False),
-    "deg": (np.pi/180., dimensions.angle, 0.0, r"\rm{deg}", False),
-    "Fr":  (1.0e-3**1.5, dimensions.charge_cgs, 0.0, r"\rm{Fr}", False),
-    "G": (0.1**0.5, dimensions.magnetic_field_cgs, 0.0, r"\rm{G}", True),
-    "Angstrom": (m_per_ang, dimensions.length, 0.0, r"\AA", False),
-    "statC": (1.0e-3**1.5, dimensions.charge_cgs, 0.0, r"\rm{statC}", True),
+    "solMass": (mass_sun_kg, dimensions.mass, 0.0, r"M_\odot"),
+    "solRad": (m_per_rsun, dimensions.length, 0.0, r"R_\odot"),
+    "solLum": (luminosity_sun_watts, dimensions.power, 0.0, r"L_\odot"),
+    "dyn": (1.0e-5, dimensions.force, 0.0, r"\rm{dyn}"),
+    "sr": (1.0, dimensions.solid_angle, 0.0, r"\rm{sr}"),
+    "rad": (1.0, dimensions.angle, 0.0, r"\rm{rad}"),
+    "deg": (np.pi/180., dimensions.angle, 0.0, r"\rm{deg}"),
+    "Fr":  (1.0e-3**1.5, dimensions.charge_cgs, 0.0, r"\rm{Fr}"),
+    "G": (0.1**0.5, dimensions.magnetic_field_cgs, 0.0, r"\rm{G}"),
+    "Angstrom": (m_per_ang, dimensions.length, 0.0, r"\AA"),
+    "statC": (1.0e-3**1.5, dimensions.charge_cgs, 0.0, r"\rm{statC}"),
 
     # Planck units
-    "m_pl": (planck_mass_kg, dimensions.mass, 0.0, r"m_{\rm{P}}", False),
-    "l_pl": (planck_length_m, dimensions.length, 0.0, r"\ell_\rm{P}", False),
-    "t_pl": (planck_time_s, dimensions.time, 0.0, r"t_{\rm{P}}", False),
-    "T_pl": (planck_temperature_K, dimensions.temperature, 0.0, r"T_{\rm{P}}",
-             False),
-    "q_pl": (planck_charge_C, dimensions.charge_mks, 0.0, r"q_{\rm{P}}",
-             False),
-    "E_pl": (planck_energy_J, dimensions.energy, 0.0, r"E_{\rm{P}}", False),
+    "m_pl": (planck_mass_kg, dimensions.mass, 0.0, r"m_{\rm{P}}"),
+    "l_pl": (planck_length_m, dimensions.length, 0.0, r"\ell_\rm{P}"),
+    "t_pl": (planck_time_s, dimensions.time, 0.0, r"t_{\rm{P}}"),
+    "T_pl": (planck_temperature_K, dimensions.temperature, 0.0, r"T_{\rm{P}}"),
+    "q_pl": (planck_charge_C, dimensions.charge_mks, 0.0, r"q_{\rm{P}}"),
+    "E_pl": (planck_energy_J, dimensions.energy, 0.0, r"E_{\rm{P}}"),
 
     # Geometrized units
-    "m_geom": (mass_sun_kg, dimensions.mass, 0.0, r"M_\odot", False),
+    "m_geom": (mass_sun_kg, dimensions.mass, 0.0, r"M_\odot"),
     "l_geom": (newton_mks*mass_sun_kg/speed_of_light_m_per_s**2,
-               dimensions.length, 0.0, r"M_\odot", False),
+               dimensions.length, 0.0, r"M_\odot"),
     "t_geom": (newton_mks*mass_sun_kg/speed_of_light_m_per_s**3,
-               dimensions.time, 0.0, r"M_\odot", False),
+               dimensions.time, 0.0, r"M_\odot"),
+
+    # Some Solar System units
+    "R_earth": (m_per_rearth, dimensions.length, 0.0, r"R_\oplus"),
+    "r_earth": (m_per_rearth, dimensions.length, 0.0, r"R_\oplus"),
+    "R_jup": (m_per_rjup, dimensions.length, 0.0, r"R_\mathrm{Jup}"),
+    "r_jup": (m_per_rjup, dimensions.length, 0.0, r"R_\mathrm{Jup}"),
 }
 
 # This dictionary formatting from magnitude package, credit to Juan Reyero.
@@ -248,6 +244,44 @@ latex_prefixes = {
     "u": r"\mu",
     }
 
+prefixable_units = [
+    "m",
+    "pc",
+    "mcm",
+    "pccm",
+    "g",
+    "eV",
+    "s",
+    "yr",
+    "K",
+    "dyne",
+    "erg",
+    "esu",
+    "J",
+    "Hz",
+    "W",
+    "gauss",
+    "G",
+    "Jy",
+    "N",
+    "T",
+    "A",
+    "C",
+    "statA",
+    "Pa",
+    "V",
+    "statV",
+    "ohm",
+    "statohm",
+    "Sv",
+    "mol",
+    "cd",
+    "lm",
+    "lx",
+    "Wb",
+    "Mx",
+]
+
 default_base_units = {
     dimensions.mass: 'kg',
     dimensions.length: 'm',
diff --git a/unyt/array.py b/unyt/array.py
index 6fa0716..e1458ff 100644
--- a/unyt/array.py
+++ b/unyt/array.py
@@ -132,6 +132,7 @@ from unyt._pint_conversions import convert_pint_units
 from unyt.unit_object import (
     _check_em_conversion,
     _em_conversion,
+    _sanitize_unit_system,
     Unit,
 )
 from unyt.unit_registry import UnitRegistry
@@ -600,7 +601,8 @@ class unyt_array(np.ndarray):
         units = _sanitize_units_convert(units, self.units.registry)
         if equivalence is None:
             try:
-                conv_data = _check_em_conversion(self.units.expr, units.expr)
+                conv_data = _check_em_conversion(
+                    self.units, units, registry=self.units.registry)
             except MKSCGSConversionError:
                 raise UnitConversionError(self.units, self.units.dimensions,
                                           units, units.dimensions)
@@ -762,7 +764,8 @@ class unyt_array(np.ndarray):
         units = _sanitize_units_convert(units, self.units.registry)
         if equivalence is None:
             try:
-                conv_data = _check_em_conversion(self.units.expr, units.expr)
+                conv_data = _check_em_conversion(
+                    self.units, units, registry=self.units.registry)
             except MKSCGSConversionError:
                 raise UnitConversionError(self.units, self.units.dimensions,
                                           units, units.dimensions)
@@ -890,13 +893,15 @@ class unyt_array(np.ndarray):
         >>> print(E.in_base("mks"))
         2.5e-07 W
         """
+        us = _sanitize_unit_system(unit_system, self)
         try:
-            conv_data = _check_em_conversion(self.units.expr)
+            conv_data = _check_em_conversion(
+                self.units, unit_system=us, registry=self.units.registry)
         except MKSCGSConversionError:
             raise UnitsNotReducible(self.units, unit_system)
         if any(conv_data):
             to_units, (conv, offset) = _em_conversion(
-                self.units, conv_data, unit_system=unit_system)
+                self.units, conv_data, unit_system=us)
         else:
             to_units = self.units.get_base_equivalent(unit_system)
             conv, offset = self.units.get_conversion_factor(to_units)
@@ -1843,9 +1848,6 @@ class unyt_quantity(unyt_array):
             raise RuntimeError("unyt_quantity instances must be scalars")
         return ret
 
-    def __round__(self):
-        return type(self)(round(float(self)), self.units)
-
 
 def _validate_numpy_wrapper_units(v, arrs):
     if not any(isinstance(a, unyt_array) for a in arrs):
diff --git a/unyt/unit_object.py b/unyt/unit_object.py
index 4220f31..50d9afb 100644
--- a/unyt/unit_object.py
+++ b/unyt/unit_object.py
@@ -53,17 +53,20 @@ from unyt.dimensions import (
     base_dimensions,
     dimensionless,
     temperature,
+    current_mks,
 )
+import unyt.dimensions as dims
 from unyt.equivalencies import equivalence_registry
 from unyt.exceptions import (
     InvalidUnitOperation,
     MKSCGSConversionError,
     UnitConversionError,
-    UnitsNotReducible
+    UnitsNotReducible,
 )
 from unyt._physical_ratios import speed_of_light_cm_per_s
 from unyt._unit_lookup_table import (
     unit_prefixes,
+    prefixable_units,
     latex_prefixes,
 )
 from unyt.unit_registry import (
@@ -83,6 +86,15 @@ global_dict = {
 }
 
 
+def _sanitize_unit_system(unit_system, obj):
+    from unyt.unit_systems import unit_system_registry
+    if hasattr(unit_system, "unit_registry"):
+        unit_system = unit_system.unit_registry.unit_system_id
+    elif unit_system == "code":
+        unit_system = obj.registry.unit_system_id
+    return unit_system_registry[str(unit_system)]
+
+
 def _auto_positive_symbol(tokens, local_dict, global_dict):
     """
     Inserts calls to ``Symbol`` for undefined variables.
@@ -629,14 +641,10 @@ class Unit(object):
         >>> (g/cm**3).get_base_equivalent('solar')
         Mearth/AU**3
         """
-        from unyt.unit_systems import unit_system_registry
-        if hasattr(unit_system, "unit_registry"):
-            unit_system = unit_system.unit_registry.unit_system_id
-        elif unit_system == "code":
-            unit_system = self.registry.unit_system_id
-        unit_system = unit_system_registry[str(unit_system)]
+        unit_system = _sanitize_unit_system(unit_system, self)
         try:
-            conv_data = _check_em_conversion(self.units.expr)
+            conv_data = _check_em_conversion(
+                self.units, registry=self.registry, unit_system=unit_system)
         except MKSCGSConversionError:
             raise UnitsNotReducible(self.units, unit_system)
         if any(conv_data):
@@ -710,22 +718,26 @@ class Unit(object):
 #
 
 
+# map from dimensions in one unit system to dimensions in other system,
+# canonical unit to convert to in that system, and floating point
+# conversion factor
 em_conversions = {
-    "C": ("esu", 0.1*speed_of_light_cm_per_s, "cgs"),
-    "T": ("gauss", 1.0e4, "cgs"),
-    "Wb": ("Mx", 1.0e8, "cgs"),
-    "A": ("statA", 0.1*speed_of_light_cm_per_s, "cgs"),
-    "V": ("statV", 1.0e-8*speed_of_light_cm_per_s, "cgs"),
-    "ohm": ("statohm", 1.0e9/(speed_of_light_cm_per_s**2), "cgs"),
-    "esu": ("C", 10.0/speed_of_light_cm_per_s, "mks"),
-    "Fr": ("C", 10.0/speed_of_light_cm_per_s, "mks"),
-    "statC": ("C", 10.0/speed_of_light_cm_per_s, "mks"),
-    "gauss": ("T", 1.0e-4, "mks"),
-    "G": ("T", 1.0e-4, "mks"),
-    "Mx": ("Wb", 1.0e-8, "mks"),
-    "statA": ("A", 10.0/speed_of_light_cm_per_s, "mks"),
-    "statV": ("V", 1.0e8/speed_of_light_cm_per_s, "mks"),
-    "statohm": ("ohm", speed_of_light_cm_per_s**2*1.0e-9, "mks"),
+    dims.charge_mks: (dims.charge_cgs, "esu", 0.1*speed_of_light_cm_per_s),
+    dims.charge_cgs: (dims.charge_mks, "C", 10.0/speed_of_light_cm_per_s),
+    dims.magnetic_field_mks: (dims.magnetic_field_cgs, "gauss", 1.0e4),
+    dims.magnetic_field_cgs: (dims.magnetic_field_mks, "T", 1.0e-4),
+    dims.current_mks: (
+        dims.current_cgs, "statA", 0.1*speed_of_light_cm_per_s),
+    dims.current_cgs: (dims.current_mks, "A", 10.0/speed_of_light_cm_per_s),
+    dims.electric_potential_mks: (
+        dims.electric_potential_cgs, "statV",
+        1.0e-8*speed_of_light_cm_per_s),
+    dims.electric_potential_cgs: (
+        dims.electric_potential_mks, "V", 1.0e8/speed_of_light_cm_per_s),
+    dims.resistance_mks: (
+        dims.resistance_cgs, "statohm", 1.0e9/(speed_of_light_cm_per_s**2)),
+    dims.resistance_cgs: (
+        dims.resistance_mks, "ohm", 1.0e-9*speed_of_light_cm_per_s**2)
 }
 
 
@@ -738,9 +750,11 @@ def _em_conversion(orig_units, conv_data, to_units=None, unit_system=None):
     with the new one in the unit expression and multiplying by the scale
     factor.
     """
-    conv_unit, scale = conv_data
+    conv_unit, canonical_unit, scale = conv_data
+    if conv_unit is None:
+        conv_unit = canonical_unit
     new_expr = orig_units.copy().expr.replace(
-        orig_units.expr, scale*conv_unit.expr)
+        orig_units.expr, scale*canonical_unit.expr)
     if unit_system is not None:
         # we don't know the to_units, so we get it directly from the
         # conv_data
@@ -752,24 +766,9 @@ def _em_conversion(orig_units, conv_data, to_units=None, unit_system=None):
     return to_units, conv
 
 
-def _get_em_base_unit(units):
-    unit_str = str(units)
-    if len(unit_str) == 1:
-        return unit_str
-    possible_prefix = unit_str[0]
-    prefix_len = 1
-    if unit_str[:2] == 'da':
-        possible_prefix = 'da'
-        prefix_len += 1
-    if possible_prefix in unit_prefixes:
-        base_unit = unit_str[prefix_len:]
-    else:
-        base_unit = unit_str
-    return base_unit
-
-
 @lru_cache(maxsize=128, typed=False)
-def _check_em_conversion(unit_expr, to_unit_expr=None):
+def _check_em_conversion(unit, to_unit=None, unit_system=None,
+                         registry=None):
     """Check to see if the units contain E&M units
 
     This function supports unyt's ability to convert data to and from E&M
@@ -783,31 +782,37 @@ def _check_em_conversion(unit_expr, to_unit_expr=None):
     trying to convert between CGS & MKS E&M units, it raises an error.
     """
     em_map = ()
-    base_unit = _get_em_base_unit(str(unit_expr))
-    base_to_unit = _get_em_base_unit(str(to_unit_expr))
-    if base_unit == base_to_unit:
+    if unit == to_unit:
         return em_map
-    if base_unit in em_conversions:
-        em_info = em_conversions.get(base_unit, (None,)*2)
-        to_unit = Unit(em_info[0])
-        if ((to_unit_expr is None or
-             Unit(to_unit_expr).dimensions == to_unit.dimensions)):
-            em_map = (to_unit, em_info[1])
+    if unit.dimensions in em_conversions:
+        em_info = em_conversions[unit.dimensions]
+        em_unit = Unit(em_info[1], registry=registry)
+        if to_unit is None:
+            cmks_in_unit = current_mks in unit.dimensions.atoms()
+            cmks_in_unit_system = unit_system.units_map[current_mks]
+            cmks_in_unit_system = cmks_in_unit_system is not None
+            if cmks_in_unit and cmks_in_unit_system:
+                em_map = (unit_system[unit.dimensions], unit, 1.0)
+            else:
+                em_map = (None, em_unit, em_info[2])
+        elif to_unit.dimensions == em_unit.dimensions:
+            em_map = (to_unit, em_unit, em_info[2])
     if em_map:
         return em_map
-    for unit in unit_expr.atoms():
-        if unit.is_Number:
+    for unit_atom in unit.expr.atoms():
+        if unit_atom.is_Number:
             pass
-        bu = _get_em_base_unit(str(unit))
-        if bu in em_conversions:
-            conv_unit = em_conversions[bu][0]
-            if to_unit_expr is not None:
-                for ounit in to_unit_expr.atoms():
-                    bou = _get_em_base_unit(str(ounit))
+        bu = str(unit_atom)
+        budims = Unit(bu, registry=registry).dimensions
+        if budims in em_conversions:
+            conv_unit = em_conversions[budims][1]
+            if to_unit is not None:
+                for to_unit_atom in to_unit.expr.atoms():
+                    bou = str(to_unit_atom)
                     if bou == conv_unit:
-                        raise MKSCGSConversionError(unit_expr)
+                        raise MKSCGSConversionError(unit)
             else:
-                raise MKSCGSConversionError(unit_expr)
+                raise MKSCGSConversionError(unit)
     return em_map
 
 
@@ -925,9 +930,6 @@ def _lookup_unit_symbol(symbol_str, unit_symbol_lut):
             symbol_wo_pref = symbol_str[2:]
             possible_prefix = 'da'
 
-        prefixable_units = [u for u in unit_symbol_lut
-                            if unit_symbol_lut[u][4]]
-
         unit_is_si_prefixable = (symbol_wo_pref in unit_symbol_lut and
                                  symbol_wo_pref in prefixable_units)
 
@@ -956,7 +958,7 @@ def _lookup_unit_symbol(symbol_str, unit_symbol_lut):
             # Leave offset and dimensions the same, but adjust scale factor and
             # LaTeX representation
             ret = (unit_data[0] * prefix_value, unit_data[1], unit_data[2],
-                   latex_repr, False)
+                   latex_repr)
 
             unit_symbol_lut[symbol_str] = ret
 
@@ -1066,8 +1068,10 @@ def define_unit(symbol, value, tex_repr=None, offset=None, prefixable=False,
                                "(value, unit) tuple!")
     base_value = float(value.in_base(unit_system='mks'))
     dimensions = value.units.dimensions
-    registry.add(symbol, base_value, dimensions, prefixable=prefixable,
-                 tex_repr=tex_repr, offset=offset)
+    registry.add(symbol, base_value, dimensions, tex_repr=tex_repr,
+                 offset=offset)
+    if prefixable:
+        prefixable_units.append(symbol)
     if registry is default_unit_registry:
         u = Unit(symbol, registry=registry)
         setattr(unyt, symbol, u)
diff --git a/unyt/unit_registry.py b/unyt/unit_registry.py
index 5afeb13..5d81ed2 100644
--- a/unyt/unit_registry.py
+++ b/unyt/unit_registry.py
@@ -69,37 +69,10 @@ class UnitRegistry:
             self._unit_system_id = str(m.hexdigest())
         return self._unit_system_id
 
-    @property
-    def prefixable_units(self):
-        return [u for u in self.lut if self.lut[u][4]]
-
-    def add(self, symbol, base_value, dimensions, tex_repr=None, offset=None,
-            prefixable=False):
+    def add(self, symbol, base_value, dimensions, tex_repr=None, offset=None):
         """
         Add a symbol to this registry.
 
-        Parameters
-        ----------
-
-        symbol : str
-           The name of the unit
-        base_value : float
-           The scaling from the units value to the equivalent SI unit
-           with the same dimensions
-        dimensions : expr
-           The dimensions of the unit
-        tex_repr : str, optional
-           The LaTeX representation of the unit. If not provided a LaTeX
-           representation is automatically generated from the name of
-           the unit.
-        offset : float, optional
-           If set, the zero-point offset to apply to the unit to convert
-           to SI. This is mostly used for units like Farhenheit and
-           Celcius that are not defined on an absolute scale.
-        prefixable : bool
-           If True, then SI-prefix versions of the unit will be created
-           along with the unit itself.
-
         """
         from unyt.unit_object import _validate_dimensions
 
@@ -125,19 +98,12 @@ class UnitRegistry:
             tex_repr = r"\rm{" + symbol.replace('_', '\ ') + "}"
 
         # Add to lut
-        self.lut[symbol] = (
-            base_value, dimensions, offset, tex_repr, prefixable)
+        self.lut.update({symbol: (base_value, dimensions, offset, tex_repr)})
 
     def remove(self, symbol):
         """
         Remove the entry for the unit matching `symbol`.
 
-        Parameters
-        ----------
-
-        symbol : str
-           The name of the unit symbol to remove from the registry.
-
         """
         self._unit_system_id = None
 
@@ -153,14 +119,6 @@ class UnitRegistry:
         Change the base value of a unit symbol.  Useful for adjusting code
         units after parsing parameters.
 
-        Parameters
-        ----------
-
-        symbol : str
-           The name of the symbol to modify
-        base_value : float
-           The new base_value for the symbol.
-
         """
         self._unit_system_id = None
 
@@ -203,12 +161,6 @@ class UnitRegistry:
     def from_json(cls, json_text):
         """
         Returns a UnitRegistry object from a json-serialized unit registry
-
-        Parameters
-        ----------
-
-        json_text : str
-           A string containing a json represention of a UnitRegistry
         """
         data = json.loads(json_text)
         lut = {}


Test patch:
diff --git a/unyt/tests/test_unit_registry.py b/unyt/tests/test_unit_registry.py
index 3066a42..c3d4c60 100644
--- a/unyt/tests/test_unit_registry.py
+++ b/unyt/tests/test_unit_registry.py
@@ -21,7 +21,6 @@ from unyt.exceptions import (
     SymbolNotFoundError,
     UnitParseError,
 )
-from unyt.unit_object import Unit
 from unyt.unit_registry import UnitRegistry
 
 
@@ -53,16 +52,3 @@ def test_add_modify_error():
 def test_keys():
     ureg = UnitRegistry()
     assert sorted(ureg.keys()) == sorted(ureg.lut.keys())
-
-
-def test_prefixable_units():
-    ureg = UnitRegistry()
-    pu = ureg.prefixable_units
-    assert 'm' in pu
-    assert 'pc' in pu
-    assert 'mol' in pu
-    ureg.add('foobar', 1.0, length, prefixable=True)
-    assert 'foobar' in ureg.prefixable_units
-    mfoobar = Unit('mfoobar', registry=ureg)
-    foobar = Unit('foobar', registry=ureg)
-    assert (1*foobar)/(1*mfoobar) == 1000
diff --git a/unyt/tests/test_unit_systems.py b/unyt/tests/test_unit_systems.py
index 7e64b92..5f2e30c 100644
--- a/unyt/tests/test_unit_systems.py
+++ b/unyt/tests/test_unit_systems.py
@@ -53,7 +53,7 @@ def test_unit_system_id():
     reg1 = UnitRegistry()
     reg1.remove('g')
     assert reg1.unit_system_id != reg2.unit_system_id
-    reg1.add('g', 1.0e-3, dimensions.mass, prefixable=True)
+    reg1.add('g', 1.0e-3, dimensions.mass)
     assert reg1.unit_system_id == reg2.unit_system_id
 
 
diff --git a/unyt/tests/test_units.py b/unyt/tests/test_units.py
index 6d863fe..59dfa17 100644
--- a/unyt/tests/test_units.py
+++ b/unyt/tests/test_units.py
@@ -36,6 +36,7 @@ from unyt.dimensions import (
     temperature,
     energy,
     magnetic_field_cgs,
+    magnetic_field_mks,
     power,
     rate
 )
@@ -52,7 +53,8 @@ from unyt.unit_object import (
 from unyt.unit_systems import UnitSystem
 from unyt._unit_lookup_table import (
     default_unit_symbol_lut,
-    unit_prefixes
+    unit_prefixes,
+    prefixable_units
 )
 import unyt.unit_symbols as unit_symbols
 from unyt._physical_ratios import (
@@ -73,7 +75,7 @@ def test_no_conflicting_symbols():
 
     # go through all possible prefix combos
     for symbol in default_unit_symbol_lut.keys():
-        if default_unit_symbol_lut[symbol][4]:
+        if symbol in prefixable_units:
             keys = unit_prefixes.keys()
         else:
             keys = [symbol]
@@ -492,7 +494,7 @@ def test_latex_repr():
 
     # create a fake comoving unit
     registry.add('pccm', registry.lut['pc'][0]/(1+2), length,
-                 "\\rm{pc}/(1+z)", prefixable=True)
+                 "\\rm{pc}/(1+z)")
 
     test_unit = Unit('Mpccm', registry=registry)
     assert_almost_equal(test_unit.base_value, m_per_mpc/3)
@@ -601,12 +603,17 @@ def test_code_unit():
 
     ureg = UnitRegistry()
     ureg.add('code_length', 10., length)
+    ureg.add('code_magnetic_field', 2.0, magnetic_field_mks)
     u = Unit('code_length', registry=ureg)
     assert u.is_code_unit is True
     assert u.get_base_equivalent() == Unit('m')
     u = Unit('cm')
     assert u.is_code_unit is False
 
+    u = Unit('code_magnetic_field', registry=ureg)
+    assert u.get_base_equivalent('mks') == Unit('T')
+    assert u.get_base_equivalent('cgs') == Unit('gauss')
+
     UnitSystem(ureg.unit_system_id, 'code_length', 'kg', 's', registry=ureg)
 
     u = Unit('cm', registry=ureg)
@@ -655,8 +662,3 @@ def test_define_unit_error():
         define_unit('foobar', 'baz')
     with pytest.raises(RuntimeError):
         define_unit('foobar', 12)
-
-
-def test_symbol_lut_length():
-    for v in default_unit_symbol_lut.values():
-        assert len(v) == 5
diff --git a/unyt/tests/test_unyt_array.py b/unyt/tests/test_unyt_array.py
index 73e0f1c..314c3e6 100644
--- a/unyt/tests/test_unyt_array.py
+++ b/unyt/tests/test_unyt_array.py
@@ -1906,15 +1906,3 @@ def test_creation():
         unyt_quantity('hello', 'cm')
     with pytest.raises(RuntimeError):
         unyt_quantity(np.array([1, 2, 3]), 'cm')
-
-
-def test_round():
-    from unyt import km
-
-    assert_equal(round(3.3*km), 3.)
-    assert_equal(round(3.5*km), 4.)
-    assert_equal(round(3*km), 3)
-    assert_equal(round(3.7*km), 4)
-
-    with pytest.raises(TypeError):
-        round([1, 2, 3]*km)