diff --git a/Dockerfile b/Dockerfile
index 693a141c91644a9b7a214df832a47b9bc37975cc..173f1ece3038661b02ec39fc20e3898c37658e37 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -70,6 +70,7 @@ RUN cp dist/nomad-lab-*.tar.gz dist/nomad-lab.tar.gz
 RUN python -m nomad.cli dev metainfo > gui/src/metainfo.json
 RUN python -m nomad.cli dev search-quantities > gui/src/searchQuantities.json
 RUN python -m nomad.cli dev toolkit-metadata > gui/src/toolkitMetadata.json
+RUN python -m nomad.cli dev units > gui/src/units.js
 WORKDIR /install/docs
 RUN make html
 RUN \
@@ -90,6 +91,7 @@ COPY --from=build /install/gui/src/metainfo.json /app/src/metainfo.json
 COPY --from=build /install/gui/src/searchQuantities.json /app/src/searchQuantities.json
 COPY --from=build /install/gui/src/parserMetadata.json /app/src/parserMetadata.json
 COPY --from=build /install/gui/src/toolkitMetadata.json /app/src/toolkitMetadata.json
+COPY --from=build /install/gui/src/units.js /app/src/units.js
 RUN yarn run build
 
 # Build the Encyclopedia GUI in the gui build image
diff --git a/gui/src/components/archive/ArchiveBrowser.js b/gui/src/components/archive/ArchiveBrowser.js
index 28a46a5d24b5d33a26b7eca68d2331f6cd0c4eb5..6cc52403ba71f53291fe15197a79fa5684cceecc 100644
--- a/gui/src/components/archive/ArchiveBrowser.js
+++ b/gui/src/components/archive/ArchiveBrowser.js
@@ -1,4 +1,3 @@
-
 import React, { useMemo, useState, useCallback } from 'react'
 import PropTypes from 'prop-types'
 import { atom, useRecoilState, useRecoilValue } from 'recoil'
@@ -16,17 +15,36 @@ import { ErrorHandler, ErrorCard } from '../ErrorHandler'
 import DOS from '../visualization/DOS'
 import { StructureViewer, BrillouinZoneViewer } from '@lauri-codes/materia'
 import Markdown from '../Markdown'
-import { convert } from '../../utils'
+import { UnitSelector } from './UnitSelector'
+import { convertSI } from '../../utils'
+import { conversionMap } from '../../units'
 
 export const configState = atom({
   key: 'config',
   default: {
     'showMeta': false,
     'showCodeSpecific': false,
-    'showAllDefined': false
+    'showAllDefined': false,
+    'energyUnit': 'joule'
   }
 })
 
+let defaults = {}
+for (const dimension in conversionMap) {
+  const info = conversionMap[dimension]
+  defaults[dimension] = info.units[0]
+}
+const override = {
+  'length': 'angstrom',
+  'energy': 'electron_volt',
+  'system': 'custom'
+}
+defaults = {...defaults, ...override}
+export const unitsState = atom({
+  key: 'units',
+  default: defaults
+})
+
 // Shared instance of the StructureViewer
 const viewer = new StructureViewer()
 const bzViewer = new BrillouinZoneViewer()
@@ -50,6 +68,7 @@ ArchiveBrowser.propTypes = ({
 
 function ArchiveConfigForm({searchOptions}) {
   const [config, setConfig] = useRecoilState(configState)
+
   const handleConfigChange = event => {
     const changes = {[event.target.name]: event.target.checked}
     if (changes.showCodeSpecific) {
@@ -64,21 +83,23 @@ function ArchiveConfigForm({searchOptions}) {
   const { url } = useRouteMatch()
 
   return (
-    <Box marginTop={-6}>
-      <FormGroup row style={{alignItems: 'flex-end'}}>
-        <Autocomplete
-          options={searchOptions}
-          getOptionLabel={(option) => option.name}
-          style={{ width: 350 }}
-          onChange={(_, value) => {
-            if (value) {
-              history.push(url + value.path)
-            }
-          }}
-          renderInput={(params) => <TextField {...params} label="search" margin="normal" />}
-        />
+    <Box marginTop={-3} padding={0}>
+      <FormGroup row style={{alignItems: 'center'}}>
+        <Box style={{width: 350, height: 60}}>
+          <Autocomplete
+            options={searchOptions}
+            getOptionLabel={(option) => option.name}
+            style={{ width: 350, marginTop: -20 }}
+            onChange={(_, value) => {
+              if (value) {
+                history.push(url + value.path)
+              }
+            }}
+            renderInput={(params) => <TextField {...params} label="search" margin="normal" />}
+          />
+        </Box>
         <Box flexGrow={1} />
-        <Tooltip title="Enable to also show all code specfic data">
+        <Tooltip title="Enable to also show all code specific data">
           <FormControlLabel
             control={
               <Checkbox
@@ -113,6 +134,7 @@ function ArchiveConfigForm({searchOptions}) {
             label="definitions"
           />
         </Tooltip>
+        <UnitSelector unitsState={unitsState}></UnitSelector>
       </FormGroup>
     </Box>
   )
@@ -246,6 +268,7 @@ class QuantityAdaptor extends ArchiveAdaptor {
 }
 
 function QuantityItemPreview({value, def}) {
+  const units = useRecoilState(unitsState)[0]
   if (def.type.type_kind === 'reference') {
     return <Box component="span" fontStyle="italic">
       <Typography component="span">reference ...</Typography>
@@ -280,9 +303,14 @@ function QuantityItemPreview({value, def}) {
       </Typography>
     </Box>
   } else {
+    let finalValue = value
+    let finalUnit = def.unit
+    if (def.unit) {
+      [finalValue, finalUnit] = convertSI(value, def.unit, units)
+    }
     return <Box component="span" whiteSpace="nowarp">
-      <Number component="span" variant="body1" value={value} exp={8} />
-      {def.unit && <Typography component="span">&nbsp;{def.unit}</Typography>}
+      <Number component="span" variant="body1" value={finalValue} exp={8} />
+      {finalUnit && <Typography component="span">&nbsp;{finalUnit}</Typography>}
     </Box>
   }
 }
@@ -292,10 +320,18 @@ QuantityItemPreview.propTypes = ({
 })
 
 function QuantityValue({value, def}) {
+  // Figure out the units
+  const units = useRecoilState(unitsState)[0]
+  let finalValue = value
+  let finalUnit = def.unit
+  if (def.unit) {
+    [finalValue, finalUnit] = convertSI(value, def.unit, units)
+  }
+
   return <Box
     marginTop={2} marginBottom={2} textAlign="center" fontWeight="bold"
   >
-    {def.shape.length > 0 ? <Matrix values={value} shape={def.shape} invert={def.shape.length === 1} /> : <Number value={value} exp={16} variant="body2" />}
+    {def.shape.length > 0 ? <Matrix values={finalValue} shape={def.shape} invert={def.shape.length === 1} /> : <Number value={finalValue} exp={16} variant="body2" />}
     {def.shape.length > 0 &&
       <Typography noWrap variant="caption">
         ({def.shape.map((dimension, index) => <span key={index}>
@@ -303,7 +339,7 @@ function QuantityValue({value, def}) {
         </span>)}&nbsp;)
       </Typography>
     }
-    {def.unit && <Typography noWrap>{def.unit}</Typography>}
+    {def.unit && <Typography noWrap>{finalUnit}</Typography>}
   </Box>
 }
 QuantityValue.propTypes = ({
@@ -371,14 +407,14 @@ function Overview({section, def}) {
     } else if (sectionPath === visualizedSystem.sectionPath && nAtoms === visualizedSystem.nAtoms) {
       positionsOnly = true
       system = {
-        positions: convert(section.atom_positions, 'm', 'angstrom')
+        positions: convertSI(section.atom_positions, 'meter', {length: 'angstrom'}, false)
       }
     // Completely new system
     } else {
       system = {
         'species': section.atom_species,
-        'cell': convert(section.lattice_vectors, 'm', 'angstrom'),
-        'positions': convert(section.atom_positions, 'm', 'angstrom'),
+        'cell': convertSI(section.lattice_vectors, 'meter', {length: 'angstrom'}, false),
+        'positions': convertSI(section.atom_positions, 'meter', {length: 'angstrom'}, false),
         'pbc': section.configuration_periodic_dimensions
       }
     }
@@ -409,6 +445,7 @@ function Overview({section, def}) {
               className={style.bands}
               data={section}
               aspectRatio={1}
+              unitsState={unitsState}
             ></BandStructure>
           </ErrorHandler>
         </Box>
@@ -453,6 +490,7 @@ function Overview({section, def}) {
         className={style.dos}
         data={section}
         aspectRatio={1 / 2}
+        unitsState={unitsState}
       ></DOS>
     </ErrorHandler>
   }
diff --git a/gui/src/components/archive/UnitSelector.js b/gui/src/components/archive/UnitSelector.js
new file mode 100644
index 0000000000000000000000000000000000000000..28a31f424ce1ed0c802068888719a6812363ca1e
--- /dev/null
+++ b/gui/src/components/archive/UnitSelector.js
@@ -0,0 +1,177 @@
+import React, { useCallback, useState } from 'react'
+import { useRecoilState } from 'recoil'
+import { makeStyles } from '@material-ui/core/styles'
+import {
+  Box,
+  Button,
+  Menu,
+  MenuItem,
+  FormControl,
+  InputLabel,
+  Select,
+  FormLabel,
+  FormControlLabel,
+  RadioGroup,
+  Radio,
+  Tooltip
+} from '@material-ui/core'
+import PropTypes from 'prop-types'
+import clsx from 'clsx'
+import { conversionMap, unitMap, unitSystems } from '../../units'
+
+/**
+ * Component that wraps it's children in a container that can be 'floated',
+ * i.e. displayed on an html element that is positioned relative to the
+ * viewport and is above all other elements.
+ */
+export function UnitSelector({className, classes, unitsState, onUnitChange, onSystemChange}) {
+  // States
+  const [canSelect, setCanSelect] = useState(true)
+  const [anchorEl, setAnchorEl] = React.useState(null)
+  const open = Boolean(anchorEl)
+  const [units, setUnits] = useRecoilState(unitsState)
+
+  // Styles
+  const useStyles = makeStyles((theme) => {
+    return {
+      menuItem: {
+        width: '10rem'
+      },
+      systems: {
+        margin: theme.spacing(2),
+        marginTop: theme.spacing(1)
+      }
+    }
+  })
+  const style = useStyles(classes)
+
+  // Callbacks
+  const openMenu = useCallback((event) => {
+    setAnchorEl(event.currentTarget)
+  }, [])
+  const closeMenu = useCallback(() => {
+    setAnchorEl(null)
+  }, [])
+  const handleSystemChange = useCallback((event) => {
+    const systemName = event.target.value
+    let changes = {system: systemName}
+    if (systemName === 'custom') {
+      setCanSelect(true)
+    } else {
+      setCanSelect(false)
+      const system = unitSystems[systemName]
+      changes = {...changes, ...system.units}
+    }
+    setUnits({...units, ...changes})
+    if (onSystemChange) {
+      onSystemChange(event)
+    }
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [])
+  const handleUnitChange = useCallback(event => {
+    const changes = {[event.target.name]: event.target.value}
+    if (onUnitChange) {
+      onUnitChange(event)
+    }
+    console.log(changes)
+    setUnits({...units, ...changes})
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [])
+
+  // Ordered list of controllable units. It may be smaller than the full list of
+  // units.
+  const unitNames = ['energy', 'length', 'force', 'mass', 'time', 'temperature']
+  const systemNames = ['SI', 'AU']
+
+  return (
+    <Box className={clsx(style.root, className)}>
+      <Button
+        aria-controls="customized-menu"
+        aria-haspopup="true"
+        variant="outlined"
+        color="primary"
+        onClick={openMenu}
+      >
+        Select units
+      </Button>
+      <Menu
+        id="select-unit"
+        anchorEl={anchorEl}
+        getContentAnchorEl={null}
+        anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
+        transformOrigin={{ vertical: 'top', horizontal: 'right' }}
+        keepMounted
+        open={open}
+        onClose={closeMenu}
+      >
+        <FormControl
+          component="fieldset"
+          classes={{root: style.systems}}
+        >
+          <FormLabel component="legend">Unit system</FormLabel>
+          <RadioGroup aria-label="gender" name="gender1" value={units.system} onChange={handleSystemChange}>
+            <Tooltip title="Custom units">
+              <FormControlLabel value="custom" control={<Radio />} label="Custom" />
+            </Tooltip>
+            {systemNames.map((systemName) => {
+              const system = unitSystems[systemName]
+              return <Tooltip key={systemName} title={system.description}>
+                <FormControlLabel value={systemName} control={<Radio />} label={system.label} />
+              </Tooltip>
+            })}
+          </RadioGroup>
+        </FormControl>
+        {unitNames.map((dimension) => {
+          const unitList = conversionMap[dimension].units
+          return <MenuItem
+            key={dimension}
+          >
+            <FormControl disabled={!canSelect}>
+              <InputLabel id="demo-simple-select-label">{dimension}</InputLabel>
+              <Select
+                classes={{root: style.menuItem}}
+                labelId="demo-simple-select-label"
+                id="demo-simple-select"
+                name={dimension}
+                value={units[dimension]}
+                onChange={handleUnitChange}
+              >
+                {unitList.map((unit) => {
+                  const unitLabel = unitMap[unit].label
+                  return <MenuItem key={unit} value={unit}>{unitLabel}</MenuItem>
+                })}
+              </Select>
+            </FormControl>
+          </MenuItem>
+        })}
+      </Menu>
+    </Box>
+  )
+}
+
+UnitSelector.propTypes = {
+  /**
+   * CSS class for the root element.
+   */
+  className: PropTypes.string,
+  /**
+   * CSS classes for this component.
+   */
+  classes: PropTypes.object,
+  /**
+   * Recoil atom containing the unit configuration that this component will
+   * attach to.
+   */
+  unitsState: PropTypes.object,
+  /**
+   * Callback for unit selection.
+   */
+  onUnitChange: PropTypes.func,
+  /**
+   * Callback for unit system selection.
+   */
+  onSystemChange: PropTypes.func
+}
+UnitSelector.defaultProps = {
+  float: false
+}
diff --git a/gui/src/components/visualization/BandStructure.js b/gui/src/components/visualization/BandStructure.js
index d18dde174c59c8e5ba652108d61ac40d9ff65982..f689ad22afc31b22853e873020e2516d7698692b 100644
--- a/gui/src/components/visualization/BandStructure.js
+++ b/gui/src/components/visualization/BandStructure.js
@@ -1,4 +1,5 @@
 import React, {useState, useEffect, useMemo} from 'react'
+import { useRecoilValue } from 'recoil'
 import PropTypes from 'prop-types'
 import { makeStyles, useTheme } from '@material-ui/core/styles'
 import clsx from 'clsx'
@@ -6,11 +7,12 @@ import {
   Box
 } from '@material-ui/core'
 import Plot from '../visualization/Plot'
-import { convert, distance, mergeObjects } from '../../utils'
+import { convertSI, distance, mergeObjects } from '../../utils'
 
-export default function BandStructure({data, layout, aspectRatio, className, classes, onRelayout, onAfterPlot, onRedraw, onRelayouting}) {
+export default function BandStructure({data, layout, aspectRatio, className, classes, onRelayout, onAfterPlot, onRedraw, onRelayouting, unitsState}) {
   const [finalData, setFinalData] = useState(undefined)
   const [pathSegments, setPathSegments] = useState(undefined)
+  const units = useRecoilValue(unitsState)
 
   // Styles
   const useStyles = makeStyles(
@@ -89,7 +91,7 @@ export default function BandStructure({data, layout, aspectRatio, className, cla
 
       // Create plot data entry for each band
       for (let band of bands) {
-        band = convert(band, 'joule', 'eV')
+        band = convertSI(band, 'joule', units, false)
         plotData.push(
           {
             x: path,
@@ -122,7 +124,7 @@ export default function BandStructure({data, layout, aspectRatio, className, cla
 
     // Create plot data entry for each band
     for (let band of bands) {
-      band = convert(band, 'joule', 'eV')
+      band = convertSI(band, 'joule', units, false)
       plotData.push(
         {
           x: path,
@@ -138,7 +140,7 @@ export default function BandStructure({data, layout, aspectRatio, className, cla
     }
 
     setFinalData(plotData)
-  }, [data, theme.palette.primary.main, theme.palette.secondary.main])
+  }, [data, theme.palette.primary.main, theme.palette.secondary.main, units])
 
   // Merge custom layout with default layout
   const tmpLayout = useMemo(() => {
@@ -241,5 +243,6 @@ BandStructure.propTypes = {
   onAfterPlot: PropTypes.func,
   onRedraw: PropTypes.func,
   onRelayout: PropTypes.func,
-  onRelayouting: PropTypes.func
+  onRelayouting: PropTypes.func,
+  unitsState: PropTypes.object // Recoil atom containing the unit configuration
 }
diff --git a/gui/src/components/visualization/DOS.js b/gui/src/components/visualization/DOS.js
index ae8a2aaaeb8d4f069eeb902811f7d94e1595313a..aba50c9e085648e3d5d1178a5423a4096ffe045e 100644
--- a/gui/src/components/visualization/DOS.js
+++ b/gui/src/components/visualization/DOS.js
@@ -1,4 +1,5 @@
 import React, {useState, useEffect, useMemo} from 'react'
+import { useRecoilValue } from 'recoil'
 import PropTypes from 'prop-types'
 import { makeStyles, useTheme } from '@material-ui/core/styles'
 import clsx from 'clsx'
@@ -6,22 +7,23 @@ import {
   Box
 } from '@material-ui/core'
 import Plot from '../visualization/Plot'
-import { convert, mergeObjects } from '../../utils'
+import { convertSI, convertSILabel, mergeObjects } from '../../utils'
 
-export default function DOS({data, layout, aspectRatio, className, classes, onRelayout, onAfterPlot, onRedraw, onRelayouting}) {
+export default function DOS({data, layout, aspectRatio, className, classes, onRelayout, onAfterPlot, onRedraw, onRelayouting, unitsState}) {
   const [finalData, setFinalData] = useState(undefined)
+  const units = useRecoilValue(unitsState)
 
   // Merge custom layout with default layout
   const tmpLayout = useMemo(() => {
     let defaultLayout = {
       yaxis: {
         title: {
-          text: 'Energy (eV)'
+          text: `Energy (${convertSILabel('joule', units)})`
         }
       }
     }
     return mergeObjects(layout, defaultLayout)
-  }, [layout])
+  }, [layout, units])
 
   // Styles
   const useStyles = makeStyles(
@@ -45,7 +47,7 @@ export default function DOS({data, layout, aspectRatio, className, classes, onRe
     const plotData = []
     if (data !== undefined) {
       let nChannels = data[valueName].length
-      let energies = convert(data[energyName], 'joule', 'eV')
+      let energies = convertSI(data[energyName], 'joule', units, false)
       if (nChannels === 2) {
         plotData.push(
           {
@@ -74,7 +76,7 @@ export default function DOS({data, layout, aspectRatio, className, classes, onRe
       )
     }
     setFinalData(plotData)
-  }, [data, theme.palette.primary.main, theme.palette.secondary.main])
+  }, [data, theme.palette.primary.main, theme.palette.secondary.main, units])
 
   // Compute layout that depends on data.
   const computedLayout = useMemo(() => {
@@ -85,12 +87,12 @@ export default function DOS({data, layout, aspectRatio, className, classes, onRe
     let defaultLayout = {
       xaxis: {
         title: {
-          text: norm ? 'states/eV/m<sup>3</sup>/atom' : 'states/eV/cell'
+          text: norm ? convertSILabel('states/joule/m^3/atom', units) : convertSILabel('states/joule/cell', units)
         }
       }
     }
     return defaultLayout
-  }, [data])
+  }, [data, units])
 
   // Merge the given layout and layout computed from data
   const finalLayout = useMemo(() => {
@@ -123,5 +125,6 @@ DOS.propTypes = {
   onAfterPlot: PropTypes.func,
   onRedraw: PropTypes.func,
   onRelayout: PropTypes.func,
-  onRelayouting: PropTypes.func
+  onRelayouting: PropTypes.func,
+  unitsState: PropTypes.object // Recoil atom containing the unit configuration
 }
diff --git a/gui/src/units.js b/gui/src/units.js
new file mode 100644
index 0000000000000000000000000000000000000000..73e2eee67eabe8637591c7f8caf7d7526edcf67a
--- /dev/null
+++ b/gui/src/units.js
@@ -0,0 +1,500 @@
+// Generated by NOMAD CLI. Do not edit manually.
+export const unitMap = {
+  second: {
+    dimension: 'time',
+    label: 'Second',
+    abbreviation: 's'
+  },
+  atomic_unit_of_time: {
+    dimension: 'time',
+    label: 'Atomic unit of time',
+    abbreviation: 'atomic_unit_of_time'
+  },
+  meter: {
+    dimension: 'length',
+    label: 'Meter',
+    abbreviation: 'm'
+  },
+  bohr: {
+    dimension: 'length',
+    label: 'Bohr',
+    abbreviation: 'bohr'
+  },
+  angstrom: {
+    dimension: 'length',
+    label: '\u00c5ngstrom',
+    abbreviation: '\u00c5'
+  },
+  kilogram: {
+    dimension: 'mass',
+    label: 'Kilogram',
+    abbreviation: 'kg'
+  },
+  electron_mass: {
+    dimension: 'mass',
+    label: 'Electron mass',
+    abbreviation: 'm\u2091'
+  },
+  unified_atomic_mass_unit: {
+    dimension: 'mass',
+    label: 'Unified atomic mass unit',
+    abbreviation: 'u'
+  },
+  ampere: {
+    dimension: 'current',
+    label: 'Ampere',
+    abbreviation: 'A'
+  },
+  atomic_unit_of_current: {
+    dimension: 'current',
+    label: 'Atomic unit of current',
+    abbreviation: 'atomic_unit_of_current'
+  },
+  mole: {
+    dimension: 'substance',
+    label: 'Mole',
+    abbreviation: 'mole'
+  },
+  candela: {
+    dimension: 'luminosity',
+    label: 'Candela',
+    abbreviation: 'cd'
+  },
+  kelvin: {
+    dimension: 'temperature',
+    label: 'Kelvin',
+    abbreviation: 'K'
+  },
+  celsius: {
+    dimension: 'temperature',
+    label: 'Celsius',
+    abbreviation: '\u00b0C'
+  },
+  fahrenheit: {
+    dimension: 'temperature',
+    label: 'Fahrenheit',
+    abbreviation: '\u00b0F'
+  },
+  atomic_unit_of_temperature: {
+    dimension: 'temperature',
+    label: 'Atomic unit of temperature',
+    abbreviation: 'atomic_unit_of_temperature'
+  },
+  newton: {
+    dimension: 'force',
+    label: 'Newton',
+    abbreviation: 'N'
+  },
+  atomic_unit_of_force: {
+    dimension: 'force',
+    label: 'Atomic unit of force',
+    abbreviation: 'atomic_unit_of_force'
+  },
+  pascal: {
+    dimension: 'pressure',
+    label: 'Pascal',
+    abbreviation: 'Pa'
+  },
+  joule: {
+    dimension: 'energy',
+    label: 'Joule',
+    abbreviation: 'J'
+  },
+  electron_volt: {
+    dimension: 'energy',
+    label: 'Electron volt',
+    abbreviation: 'eV'
+  },
+  hartree: {
+    dimension: 'energy',
+    label: 'Hartree',
+    abbreviation: 'Ha'
+  },
+  watt: {
+    dimension: 'power',
+    label: 'Watt',
+    abbreviation: 'W'
+  },
+  hertz: {
+    dimension: 'frequency',
+    label: 'Hertz',
+    abbreviation: 'Hz'
+  },
+  volt: {
+    dimension: 'electric_potential',
+    label: 'Volt',
+    abbreviation: 'V'
+  },
+  farad: {
+    dimension: 'capacitance',
+    label: 'Farad',
+    abbreviation: 'F'
+  },
+  coulomb: {
+    dimension: 'charge',
+    label: 'Coulomb',
+    abbreviation: 'C'
+  },
+  elementary_charge: {
+    dimension: 'charge',
+    label: 'Elementary charge',
+    abbreviation: 'e'
+  },
+  tesla: {
+    dimension: 'magnetic_field',
+    label: 'Tesla',
+    abbreviation: 'T'
+  },
+  weber: {
+    dimension: 'magnetic_flux',
+    label: 'Weber',
+    abbreviation: 'Wb'
+  },
+  henry: {
+    dimension: 'inductance',
+    label: 'Henry',
+    abbreviation: 'H'
+  },
+  dimensionless: {
+    dimension: 'dimensionless',
+    label: 'Dimensionless',
+    abbreviation: ''
+  }
+}
+export const conversionMap = {
+  time: {
+    units: [
+      'second',
+      'atomic_unit_of_time'
+    ],
+    multipliers: {
+      second: {
+        second: 1,
+        atomic_unit_of_time: 4.134137333518244e+16
+      },
+      atomic_unit_of_time: {
+        second: 2.4188843265856806e-17,
+        atomic_unit_of_time: 1
+      }
+    }
+  },
+  length: {
+    units: [
+      'meter',
+      'bohr',
+      'angstrom'
+    ],
+    multipliers: {
+      meter: {
+        meter: 1,
+        bohr: 18897261246.22279,
+        angstrom: 10000000000.0
+      },
+      bohr: {
+        meter: 5.2917721090397754e-11,
+        bohr: 1,
+        angstrom: 0.5291772109039775
+      },
+      angstrom: {
+        meter: 1e-10,
+        bohr: 1.8897261246222794,
+        angstrom: 1
+      }
+    }
+  },
+  mass: {
+    units: [
+      'kilogram',
+      'electron_mass',
+      'unified_atomic_mass_unit'
+    ],
+    multipliers: {
+      kilogram: {
+        kilogram: 1,
+        electron_mass: 1.0977691057577633e+30,
+        unified_atomic_mass_unit: 6.022140762081123e+26
+      },
+      electron_mass: {
+        kilogram: 9.1093837015e-31,
+        electron_mass: 1,
+        unified_atomic_mass_unit: 0.0005485799090624057
+      },
+      unified_atomic_mass_unit: {
+        kilogram: 1.6605390666e-27,
+        electron_mass: 1822.8884862173131,
+        unified_atomic_mass_unit: 1
+      }
+    }
+  },
+  current: {
+    units: [
+      'ampere',
+      'atomic_unit_of_current'
+    ],
+    multipliers: {
+      ampere: {
+        ampere: 1,
+        atomic_unit_of_current: 150.97488474455437
+      },
+      atomic_unit_of_current: {
+        ampere: 0.006623618237509995,
+        atomic_unit_of_current: 1
+      }
+    }
+  },
+  substance: {
+    units: [
+      'mole'
+    ],
+    multipliers: {
+      mole: {
+        mole: 1
+      }
+    }
+  },
+  luminosity: {
+    units: [
+      'candela'
+    ],
+    multipliers: {
+      candela: {
+        candela: 1
+      }
+    }
+  },
+  temperature: {
+    units: [
+      'kelvin',
+      'celsius',
+      'fahrenheit',
+      'atomic_unit_of_temperature'
+    ],
+    multipliers: {
+      kelvin: {
+        kelvin: 1,
+        celsius: 1.0,
+        fahrenheit: 1.7999999999999998,
+        atomic_unit_of_temperature: 3.1668115634555572e-06
+      },
+      celsius: {
+        kelvin: 1,
+        celsius: 1,
+        fahrenheit: 1.7999999999999998,
+        atomic_unit_of_temperature: 3.1668115634555572e-06
+      },
+      fahrenheit: {
+        kelvin: 0.5555555555555556,
+        celsius: 0.5555555555555556,
+        fahrenheit: 1,
+        atomic_unit_of_temperature: 1.7593397574753097e-06
+      },
+      atomic_unit_of_temperature: {
+        kelvin: 315775.0248040719,
+        celsius: 315775.0248040719,
+        fahrenheit: 568395.0446473294,
+        atomic_unit_of_temperature: 1
+      }
+    },
+    constants: {
+      kelvin: {
+        celsius: -273.15,
+        fahrenheit: -459.67
+      },
+      celsius: {
+        celsius: -273.15,
+        fahrenheit: -459.67
+      },
+      fahrenheit: {
+        celsius: -273.15,
+        fahrenheit: -459.67
+      },
+      atomic_unit_of_temperature: {
+        celsius: -273.15,
+        fahrenheit: -459.67
+      }
+    }
+  },
+  force: {
+    units: [
+      'newton',
+      'atomic_unit_of_force'
+    ],
+    multipliers: {
+      newton: {
+        newton: 1,
+        atomic_unit_of_force: 12137802.66097955
+      },
+      atomic_unit_of_force: {
+        newton: 8.238723498238991e-08,
+        atomic_unit_of_force: 1
+      }
+    }
+  },
+  pressure: {
+    units: [
+      'pascal'
+    ],
+    multipliers: {
+      pascal: {
+        pascal: 1
+      }
+    }
+  },
+  energy: {
+    units: [
+      'joule',
+      'electron_volt',
+      'hartree'
+    ],
+    multipliers: {
+      joule: {
+        joule: 1,
+        electron_volt: 6.241509074460763e+18,
+        hartree: 2.2937122783962883e+17
+      },
+      electron_volt: {
+        joule: 1.602176634e-19,
+        electron_volt: 1,
+        hartree: 0.03674932217565436
+      },
+      hartree: {
+        joule: 4.35974472220717e-18,
+        electron_volt: 27.21138624598847,
+        hartree: 1
+      }
+    }
+  },
+  power: {
+    units: [
+      'watt'
+    ],
+    multipliers: {
+      watt: {
+        watt: 1
+      }
+    }
+  },
+  frequency: {
+    units: [
+      'hertz'
+    ],
+    multipliers: {
+      hertz: {
+        hertz: 1
+      }
+    }
+  },
+  electric_potential: {
+    units: [
+      'volt'
+    ],
+    multipliers: {
+      volt: {
+        volt: 1
+      }
+    }
+  },
+  capacitance: {
+    units: [
+      'farad'
+    ],
+    multipliers: {
+      farad: {
+        farad: 1
+      }
+    }
+  },
+  charge: {
+    units: [
+      'coulomb',
+      'elementary_charge'
+    ],
+    multipliers: {
+      coulomb: {
+        coulomb: 1,
+        elementary_charge: 6.241509074460763e+18
+      },
+      elementary_charge: {
+        coulomb: 1.602176634e-19,
+        elementary_charge: 1
+      }
+    }
+  },
+  magnetic_field: {
+    units: [
+      'tesla'
+    ],
+    multipliers: {
+      tesla: {
+        tesla: 1
+      }
+    }
+  },
+  magnetic_flux: {
+    units: [
+      'weber'
+    ],
+    multipliers: {
+      weber: {
+        weber: 1
+      }
+    }
+  },
+  inductance: {
+    dimension: 'inductance',
+    units: [
+      'henry'
+    ],
+    multipliers: {
+      henry: {
+        henry: 1
+      }
+    }
+  },
+  dimensionless: {
+    dimension: 'dimensionless',
+    units: [
+      'dimensionless'
+    ],
+    multipliers: {
+      dimensionless: {
+        dimensionless: 1
+      }
+    }
+  }
+}
+export const unitSystems = {
+  SI: {
+    label: 'SI',
+    description: 'International System of Units (SI)',
+    units: {
+      time: 'second',
+      length: 'meter',
+      mass: 'kilogram',
+      current: 'ampere',
+      substance: 'mole',
+      luminosity: 'candela',
+      temperature: 'kelvin',
+      force: 'newton',
+      pressure: 'pascal',
+      energy: 'joule',
+      power: 'watt',
+      frequency: 'hertz',
+      electric_potential: 'volt',
+      charge: 'coulomb'
+    }
+  },
+  AU: {
+    label: 'Atomic units',
+    description: 'Hartree atomic units',
+    units: {
+      time: 'atomic_unit_of_time',
+      length: 'bohr',
+      mass: 'electron_mass',
+      current: 'atomic_unit_of_current',
+      temperature: 'atomic_unit_of_temperature',
+      force: 'atomic_unit_of_force',
+      energy: 'hartree'
+    }
+  }
+}
diff --git a/gui/src/utils.js b/gui/src/utils.js
index 6e34e4dc8a9980866c905702e979101b98fdca20..f39bc8038f5cf70c620594092875e9f2c13f659b 100644
--- a/gui/src/utils.js
+++ b/gui/src/utils.js
@@ -1,4 +1,5 @@
-import { unit } from 'mathjs'
+import { parse } from 'mathjs'
+import { conversionMap, unitMap, unitSystems } from './units'
 import { cloneDeep, merge } from 'lodash'
 
 export const isEquivalent = (a, b) => {
@@ -35,42 +36,57 @@ export const capitalize = (s) => {
 }
 
 /**
- * Used to convert numeric values from one unit to another. Works on
- * n-dimensional arrays and implemented as a relatively simple for loop for
- * performance. If conversion times become an issue, it might be worthwhile to
- * look at vectorization with WebAssembly.
+ * Used to scale numeric values. Works on n-dimensional arrays and implemented
+ * as a relatively simple for loop for performance. If conversion times become
+ * an issue, it might be worthwhile to look at vectorization with WebAssembly.
  *
  * @param {*} value The values to convert
- * @param {*} from Original unit.
- * @param {*} to Target unit.
+ * @param {number} factor Scaling factor to apply.
  *
- * @return {*} A copy of the original data with units converted.
+ * @return {*} A copy of the original data with numbers scaled.
  */
-export function convert(value, from, to) {
-  // Determine the scaling factor
-  let factor = unit(1, from).toNumber(to)
-
+export function scale(value, factor) {
   // Convert arrays
-  return scale(value, factor)
+  function scaleRecursive(list, newList) {
+    let isScalarArray = !Array.isArray(list[0])
+    if (isScalarArray) {
+      for (let i = 0, size = list.length; i < size; ++i) {
+        newList.push(list[i] * factor)
+      }
+    } else {
+      for (let i = 0, size = list.length; i < size; ++i) {
+        let iList = []
+        newList.push(iList)
+        scaleRecursive(list[i], iList)
+      }
+    }
+  }
+  let isArray = Array.isArray(value)
+  let newValue
+  if (!isArray) {
+    newValue = value * factor
+  } else {
+    newValue = []
+    scaleRecursive(value, newValue)
+  }
+  return newValue
 }
 
 /**
- * Used to scale numeric values. Works on n-dimensional arrays and implemented
- * as a relatively simple for loop for performance. If conversion times become
- * an issue, it might be worthwhile to look at vectorization with WebAssembly.
+ * Used to add a single scalar value to an n-dimensional array.
  *
  * @param {*} value The values to convert
- * @param {number} factor Scaling factor to apply.
+ * @param {number} addition Value to add.
  *
  * @return {*} A copy of the original data with numbers scaled.
  */
-export function scale(value, factor) {
+export function add(value, addition) {
   // Convert arrays
   function scaleRecursive(list, newList) {
     let isScalarArray = !Array.isArray(list[0])
     if (isScalarArray) {
       for (let i = 0, size = list.length; i < size; ++i) {
-        newList.push(list[i] * factor)
+        newList.push(list[i] + addition)
       }
     } else {
       for (let i = 0, size = list.length; i < size; ++i) {
@@ -83,7 +99,7 @@ export function scale(value, factor) {
   let isArray = Array.isArray(value)
   let newValue
   if (!isArray) {
-    newValue = value * factor
+    newValue = value + addition
   } else {
     newValue = []
     scaleRecursive(value, newValue)
@@ -91,6 +107,120 @@ export function scale(value, factor) {
   return newValue
 }
 
+/**
+ * Used to convert numeric values from SI units to the given unit system. Works
+ * on n-dimensional arrays.
+ *
+ * @param {*} value The values to convert. Can be a scalar or an n-dimensional
+ * array.
+ * @param {string} from Original SI unit definition. Can be any algebraic
+ * combination of SI units, e.g. "1 / meter^2". The unit names should follow
+ * the definitions provided in the file units.js that is generated by the NOMAD
+ * CLI.
+ * @param {*} system Target unit system. A Javascript object where each
+ * physical quantity (e.g. "length") acts as a key that corresponds to a target
+ * unit (e.g. "angstrom"). The unit names should follow the definitions
+ * provided in the file units.js that is generated by the NOMAD CLI.
+ *
+ * @return {*} A copy of the original data with units converted.
+ */
+export function convertSI(value, unit, system, units = true) {
+  // Modify syntax to comply with math.js evaluation
+  const from = unit.replace('**', '^')
+
+  // Temperatures require special handling due to the fact that Celsius and
+  // Fahrenheit are not absolute units and are non-multiplicative. Two kinds of
+  // temperature conversions are supported: ones with a single temperature unit
+  // and ones where temperature is used as a part of an expression. If a single
+  // temperature unit is specified, they are converted normally taking the
+  // offset into account. If they are used as a part of an expression, they are
+  // interpreted as ranges and the offset is ignored.
+  if (from === 'kelvin') {
+    const unitTo = system['temperature']
+    const multiplier = conversionMap['temperature'].multipliers['kelvin'][unitTo]
+    const constant = conversionMap['temperature'].constants['kelvin'][unitTo]
+    const label = unitMap['kelvin'].label
+    let newValues = value
+    if (multiplier !== 1) {
+      newValues = scale(newValues, multiplier)
+    }
+    if (constant !== undefined) {
+      newValues = add(newValues, constant)
+    }
+    return [newValues, label]
+  }
+
+  // Gather all units present
+  const variables = new Set()
+  const rootNode = parse(from)
+  rootNode.traverse((node, path, parent) => {
+    if (node.isSymbolNode) {
+      variables.add(node.name)
+    }
+  })
+
+  // Check if conversion is required. The unit definition string is standardized
+  // even if no conversion took place.
+  let isSI = true
+  for (const unit of variables) {
+    const dimension = unitMap[unit].dimension
+    const unitSI = unitSystems['SI'][dimension]
+    isSI = unit === unitSI
+    if (isSI) {
+      break
+    }
+  }
+  if (isSI) {
+    if (units) {
+      const newUnit = convertSILabel(from, system)
+      return [value, newUnit]
+    }
+    return value
+  }
+
+  // Gather conversion values for each present SI unit
+  const scope = {}
+  for (const unitFrom of variables) {
+    const dimension = unitMap[unitFrom].dimension
+    const unitTo = system[dimension]
+    scope[unitFrom] = conversionMap[dimension].multipliers[unitFrom][unitTo]
+  }
+
+  // Compute the scaling factor by evaluating the unit definition with the
+  // SI units converted to target system
+  const code = rootNode.compile()
+  const factor = code.evaluate(scope)
+
+  // Scale values to new units
+  let newValues = scale(value, factor)
+
+  // Form new unit definition string by replacing the SI units with the target
+  if (units) {
+    const newUnit = convertSILabel(from, system)
+    return [newValues, newUnit]
+  }
+  return newValues
+}
+
+export function convertSILabel(label, system) {
+  // Form new unit definition string by replacing the SI units with the target
+  const rootNode = parse(label)
+  const newRoot = rootNode.transform((node, path, parent) => {
+    if (node.isSymbolNode) {
+      const unitFromInfo = unitMap[node.name]
+      if (unitFromInfo !== undefined) {
+        const dimension = unitFromInfo.dimension
+        const unitTo = system[dimension]
+        const unitToInfo = unitMap[unitTo]
+        const label = unitToInfo.abbreviation
+        node.name = label
+      }
+    }
+    return node
+  })
+  return newRoot.toString()
+}
+
 /**
  * Used to calculate the distance between two n-dimensional points,
  *
diff --git a/nomad/cli/dev.py b/nomad/cli/dev.py
index e395d67852878ecf2d361866f52448d7525c6907..af3a82cd34410cf085e1b329b3d9fd12a980c595 100644
--- a/nomad/cli/dev.py
+++ b/nomad/cli/dev.py
@@ -55,6 +55,10 @@ def gui_qa():
 @dev.command(help='Generates a JSON with all metainfo.')
 def metainfo():
     import json
+    print(json.dumps(metainfo_undecorated(), indent=2))
+
+
+def metainfo_undecorated():
     from nomad.metainfo import Package, Environment
 
     # TODO the __init_metainfo__() should not be necessary and automatically performed
@@ -81,7 +85,7 @@ def metainfo():
     for package in Package.registry.values():
         export.m_add_sub_section(Environment.packages, package)
 
-    print(json.dumps(export.m_to_dict(with_meta=True), indent=2))
+    return export.m_to_dict(with_meta=True)
 
 
 @dev.command(help='Generates a JSON with all search quantities.')
@@ -345,3 +349,458 @@ def update_parsers_metadata():
             local.seek(0)  # go to the top
             local.write(body)
             local.truncate()
+
+
+@dev.command(help='Creates a Javascript source file containing the required unit conversion factors.')
+@click.pass_context
+def units(ctx):
+    import re
+    import json
+    from nomad.units import ureg
+
+    # Mapping from unit name to dimension
+    unit_map = {
+        # Time
+        "second": {
+            "dimension": "time",
+            "label": "Second",
+            "abbreviation": "s",
+        },
+        "atomic_unit_of_time": {
+            "dimension": "time",
+            "label": "Atomic unit of time",
+            "abbreviation": "atomic_unit_of_time",
+        },
+        # Length
+        "meter": {
+            "dimension": "length",
+            "label": "Meter",
+            "abbreviation": "m",
+        },
+        "bohr": {
+            "dimension": "length",
+            "label": "Bohr",
+            "abbreviation": "bohr",
+        },
+        "angstrom": {
+            "dimension": "length",
+            "label": "Ångstrom",
+            "abbreviation": "Å",
+        },
+        # Mass
+        "kilogram": {
+            "dimension": "mass",
+            "label": "Kilogram",
+            "abbreviation": "kg",
+        },
+        "electron_mass": {
+            "dimension": "mass",
+            "label": "Electron mass",
+            "abbreviation": "mₑ",
+        },
+        "unified_atomic_mass_unit": {
+            "dimension": "mass",
+            "label": "Unified atomic mass unit",
+            "abbreviation": "u",
+        },
+        # Current
+        "ampere": {
+            "dimension": "current",
+            "label": "Ampere",
+            "abbreviation": "A",
+        },
+        "atomic_unit_of_current": {
+            "dimension": "current",
+            "label": "Atomic unit of current",
+            "abbreviation": "atomic_unit_of_current",
+        },
+        # Substance
+        "mole": {
+            "dimension": "substance",
+            "label": "Mole",
+            "abbreviation": "mole",
+        },
+        # Luminosity
+        "candela": {
+            "dimension": "luminosity",
+            "label": "Candela",
+            "abbreviation": "cd",
+        },
+        # Temperature
+        "kelvin": {
+            "dimension": "temperature",
+            "label": "Kelvin",
+            "abbreviation": "K",
+        },
+        "celsius": {
+            "dimension": "temperature",
+            "label": "Celsius",
+            "abbreviation": "°C",
+        },
+        "fahrenheit": {
+            "dimension": "temperature",
+            "label": "Fahrenheit",
+            "abbreviation": "°F",
+        },
+        "atomic_unit_of_temperature": {
+            "dimension": "temperature",
+            "label": "Atomic unit of temperature",
+            "abbreviation": "atomic_unit_of_temperature",
+        },
+        # Force
+        "newton": {
+            "dimension": "force",
+            "label": "Newton",
+            "abbreviation": "N",
+        },
+        "atomic_unit_of_force": {
+            "dimension": "force",
+            "label": "Atomic unit of force",
+            "abbreviation": "atomic_unit_of_force",
+        },
+        # Pressure
+        "pascal": {
+            "dimension": "pressure",
+            "label": "Pascal",
+            "abbreviation": "Pa"
+        },
+        # Energy
+        "joule": {
+            "dimension": "energy",
+            "label": "Joule",
+            "abbreviation": "J",
+        },
+        "electron_volt": {
+            "dimension": "energy",
+            "label": "Electron volt",
+            "abbreviation": "eV",
+        },
+        "hartree": {
+            "dimension": "energy",
+            "label": "Hartree",
+            "abbreviation": "Ha",
+        },
+        # Power
+        "watt": {
+            "dimension": "power",
+            "label": "Watt",
+            "abbreviation": "W",
+        },
+        # Frequency
+        "hertz": {
+            "dimension": "frequency",
+            "label": "Hertz",
+            "abbreviation": "Hz",
+        },
+        # Electric potential
+        "volt": {
+            "dimension": "electric_potential",
+            "label": "Volt",
+            "abbreviation": "V",
+        },
+        # Capacitance
+        "farad": {
+            "dimension": "capacitance",
+            "label": "Farad",
+            "abbreviation": "F",
+        },
+        # Charge
+        "coulomb": {
+            "dimension": "charge",
+            "label": "Coulomb",
+            "abbreviation": "C",
+        },
+        "elementary_charge": {
+            "dimension": "charge",
+            "label": "Elementary charge",
+            "abbreviation": "e",
+        },
+        # Magnetic field
+        "tesla": {
+            "dimension": "magnetic_field",
+            "label": "Tesla",
+            "abbreviation": "T",
+        },
+        # Magnetic flux
+        "weber": {
+            "dimension": "magnetic_flux",
+            "label": "Weber",
+            "abbreviation": "Wb",
+        },
+        # Inductance
+        "henry": {
+            "dimension": "inductance",
+            "label": "Henry",
+            "abbreviation": "H",
+        },
+        # dimensionless
+        "dimensionless": {
+            "dimension": "dimensionless",
+            "label": "Dimensionless",
+            "abbreviation": "",
+        },
+    }
+
+    # Units that are supported
+    unit_table = {
+        # Base units
+        "time": {
+            "units": [
+                "second",
+                "atomic_unit_of_time",
+            ],
+            "multipliers": {},
+        },
+        "length": {
+            "units": [
+                "meter",
+                "bohr",
+                "angstrom",
+            ],
+            "multipliers": {},
+        },
+        "mass": {
+            "units": [
+                "kilogram",
+                "electron_mass",
+                "unified_atomic_mass_unit",
+            ],
+            "multipliers": {},
+        },
+        "current": {
+            "units": [
+                "ampere",
+                "atomic_unit_of_current",
+            ],
+            "multipliers": {},
+        },
+        "substance": {
+            "units": [
+                "mole",
+            ],
+            "multipliers": {},
+        },
+        "luminosity": {
+            "units": [
+                "candela",
+            ],
+            "multipliers": {},
+        },
+        "temperature": {
+            "units": [
+                "kelvin",
+                "celsius",
+                "fahrenheit",
+                "atomic_unit_of_temperature",
+            ],
+            "multipliers": {},
+            "constants": {},
+        },
+        # Derived units
+        "force": {
+            "units": [
+                "newton",
+                "atomic_unit_of_force",
+            ],
+            "multipliers": {},
+        },
+        "pressure": {
+            "units": [
+                "pascal",
+            ],
+            "multipliers": {},
+        },
+        "energy": {
+            "units": [
+                "joule",
+                "electron_volt",
+                "hartree",
+            ],
+            "multipliers": {},
+        },
+        "power": {
+            "units": [
+                "watt",
+            ],
+            "multipliers": {},
+        },
+        "frequency": {
+            "units": [
+                "hertz",
+            ],
+            "multipliers": {},
+        },
+        "electric_potential": {
+            "units": [
+                "volt",
+            ],
+            "multipliers": {},
+        },
+        "capacitance": {
+            "units": [
+                "farad",
+            ],
+            "multipliers": {},
+        },
+        "charge": {
+            "units": [
+                "coulomb",
+                "elementary_charge",
+            ],
+            "multipliers": {},
+        },
+        "magnetic_field": {
+            "units": [
+                "tesla",
+            ],
+            "multipliers": {},
+        },
+        "magnetic_flux": {
+            "units": [
+                "weber",
+            ],
+            "multipliers": {},
+        },
+        "inductance": {
+            "dimension": "inductance",
+            "units": [
+                "henry",
+            ],
+            "multipliers": {},
+        },
+        "dimensionless": {
+            "dimension": "dimensionless",
+            "units": [
+                "dimensionless",
+            ],
+            "multipliers": {},
+        },
+    }
+
+    # Unit systems
+    unit_systems = {
+        "SI": {
+            "label": "SI",
+            "description": "International System of Units (SI)",
+            "units": {
+                "time": "second",
+                "length": "meter",
+                "mass": "kilogram",
+                "current": "ampere",
+                "substance": "mole",
+                "luminosity": "candela",
+                "temperature": "kelvin",
+                "force": "newton",
+                "pressure": "pascal",
+                "energy": "joule",
+                "power": "watt",
+                "frequency": "hertz",
+                "electric_potential": "volt",
+                "charge": "coulomb",
+            },
+        },
+        "AU": {
+            "label": "Atomic units",
+            "description": "Hartree atomic units",
+            "units": {
+                "time": "atomic_unit_of_time",
+                "length": "bohr",
+                "mass": "electron_mass",
+                "current": "atomic_unit_of_current",
+                "temperature": "atomic_unit_of_temperature",
+                "force": "atomic_unit_of_force",
+                "energy": "hartree",
+            }
+        }
+    }
+
+    # Precompute conversion factors and possible shifts
+    for value in unit_table.values():
+        units = value["units"]
+        for i_unit in units:
+            for j_unit in units:
+                # Create dictionaries if not present
+                multipliers = value["multipliers"]
+                if i_unit not in multipliers:
+                    multipliers[i_unit] = {}
+                if j_unit not in multipliers[i_unit]:
+                    multipliers[i_unit][j_unit] = {}
+
+                # Check if there is a constant shift: y = ax + b -> y(0) = b.
+                # Uses delta units for temperatures since only they are
+                # multiplicative.
+                y_0 = 0 * getattr(ureg, "delta_" + i_unit, getattr(ureg, i_unit))
+                b = y_0.to(getattr(ureg, j_unit)).magnitude
+
+                # Solving the multiplication factor with:
+                # y(1) = a + b -> a = y(1) - b
+                # Uses delta units for temperatures since only they are
+                # multiplicative. Causes minor numerical accuracy issues in the
+                # factor due to floating point precision
+                y_1 = 1 * getattr(ureg, "delta_" + i_unit, getattr(ureg, i_unit))
+                a = y_1.to(getattr(ureg, "delta_" + j_unit, getattr(ureg, j_unit))).magnitude
+                multipliers[i_unit][j_unit] = a
+
+                # Create dictionaries if not present
+                if b != 0:
+                    constants = value["constants"]
+                    if i_unit not in constants:
+                        constants[i_unit] = {}
+                    if j_unit not in constants[i_unit]:
+                        constants[i_unit][j_unit] = {}
+                    constants[i_unit][j_unit] = b
+
+    # Check that all defined units are present in the conversion table
+    for unit, info in unit_map.items():
+        dimension = info["dimension"]
+        unit_list = unit_table[dimension]["units"]
+        assert unit in unit_list, "Could not find '{}' in the unit table under the dimension '{}'.".format(unit, dimension)
+
+    # Check unit system correctness
+    for system in unit_systems.values():
+        for dimension, unit in system["units"].items():
+            info = unit_map[unit]
+            assert dimension == info["dimension"]
+
+    # Go through the metainfo and check that all units are defined
+    all_metainfo = metainfo_undecorated()
+    units = set()
+    packages = all_metainfo["packages"]
+    for package in packages:
+        sections = package["section_definitions"]
+        for section in sections:
+            quantities = section.get("quantities", [])
+            for quantity in quantities:
+                unit = quantity.get("unit")
+                if unit is not None:
+                    parts = unit.split()
+                    for part in parts:
+                        is_operator = part in {"/", "**", "*"}
+                        is_number = True
+                        try:
+                            int(part)
+                        except Exception:
+                            is_number = False
+                        if not is_operator and not is_number:
+                            units.add(part)
+    for unit in units:
+        assert unit in unit_map, "The unit '{}' is not defined in the unit definitions.".format(unit)
+
+    # Print unit conversion table and unit systems as a Javascript source file
+    output = "// Generated by NOMAD CLI. Do not edit manually.\n"
+    output += "export const unitMap = "
+    json_string = json.dumps(unit_map, indent=2)
+    json_string = re.sub(r'(?<!: )"(\S*?)":', '\\1:', json_string)
+    json_string = json_string.replace("\"", "'")
+    output += json_string
+    output += "\nexport const conversionMap = "
+    json_string = json.dumps(unit_table, indent=2)
+    json_string = re.sub(r'(?<!: )"(\S*?)":', '\\1:', json_string)
+    json_string = json_string.replace("\"", "'")
+    output += json_string
+    output += "\nexport const unitSystems = "
+    json_string = json.dumps(unit_systems, indent=2)
+    json_string = re.sub(r'(?<!: )"(\S*?)":', '\\1:', json_string)
+    json_string = json_string.replace("\"", "'")
+    output += json_string
+    print(output)
diff --git a/nomad/units.py b/nomad/units/__init__.py
similarity index 78%
rename from nomad/units.py
rename to nomad/units/__init__.py
index 5fbcc0ffe1e9f5f2baf1cc05ddca39823317b64c..780aaf37b0c0fb6d59744613e4fa22e255ef4149 100644
--- a/nomad/units.py
+++ b/nomad/units/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2018 Markus Scheidgen, empty_task
+# Copyright 2018 Markus Scheidgen
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,10 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""
-This module contains a global unit registry used thoughout the nomad-FAIR
-package.
-"""
+import os
 from pint import UnitRegistry
 
-ureg = UnitRegistry()
+ureg = UnitRegistry(os.path.join(os.path.dirname(__file__), "default_en.txt"))
diff --git a/nomad/units/constants_en.txt b/nomad/units/constants_en.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c3ecbf1c20fcb0399ada0eb29576566ad8f7e04f
--- /dev/null
+++ b/nomad/units/constants_en.txt
@@ -0,0 +1,73 @@
+# Default Pint constants definition file
+# Based on the International System of Units
+# Language: english
+# Source: https://physics.nist.gov/cuu/Constants/
+#         https://physics.nist.gov/PhysRefData/XrayTrans/Html/search.html
+# :copyright: 2013,2019 by Pint Authors, see AUTHORS for more details.
+
+#### MATHEMATICAL CONSTANTS ####
+# As computed by Maxima with fpprec:50
+
+pi     = 3.1415926535897932384626433832795028841971693993751 = π  # pi
+tansec = 4.8481368111333441675396429478852851658848753880815e-6   # tangent of 1 arc-second ~ arc_second/radian
+ln10   = 2.3025850929940456840179914546843642076011014886288      # natural logarithm of 10
+wien_x = 4.9651142317442763036987591313228939440555849867973      # solution to (x-5)*exp(x)+5 = 0 => x = W(5/exp(5))+5
+wien_u = 2.8214393721220788934031913302944851953458817440731      # solution to (u-3)*exp(u)+3 = 0 => u = W(3/exp(3))+3
+
+#### DEFINED EXACT CONSTANTS ####
+
+speed_of_light = 299792458 m/s = c = c_0                      # since 1983
+planck_constant = 6.62607015e-34 J s = h                      # since May 2019
+elementary_charge = 1.602176634e-19 C = e                     # since May 2019
+avogadro_number = 6.02214076e23                               # since May 2019
+boltzmann_constant = 1.380649e-23 J K^-1 = k = k_B            # since May 2019
+standard_gravity = 9.80665 m/s^2 = g_0 = g0 = g_n = gravity   # since 1901
+standard_atmosphere = 1.01325e5 Pa = atm = atmosphere         # since 1954
+conventional_josephson_constant = 4.835979e14 Hz / V = K_J90  # since Jan 1990
+conventional_von_klitzing_constant = 2.5812807e4 ohm = R_K90  # since Jan 1990
+
+#### DERIVED EXACT CONSTANTS ####
+# Floating-point conversion may introduce inaccuracies
+
+zeta = c / (cm/s) = ζ
+dirac_constant = h / (2 * π) = ħ = hbar = atomic_unit_of_action = a_u_action
+avogadro_constant = avogadro_number * mol^-1 = N_A
+molar_gas_constant = k * N_A = R
+faraday_constant = e * N_A
+conductance_quantum = 2 * e ** 2 / h = G_0
+magnetic_flux_quantum = h / (2 * e) = Φ_0 = Phi_0
+josephson_constant = 2 * e / h = K_J
+von_klitzing_constant = h / e ** 2 = R_K
+stefan_boltzmann_constant = 2 / 15 * π ** 5 * k ** 4 / (h ** 3 * c ** 2) = σ = sigma
+first_radiation_constant = 2 * π * h * c ** 2 = c_1
+second_radiation_constant = h * c / k = c_2
+wien_wavelength_displacement_law_constant = h * c / (k * wien_x)
+wien_frequency_displacement_law_constant = wien_u * k / h
+
+#### MEASURED CONSTANTS ####
+# Recommended CODATA-2018 values
+# To some extent, what is measured and what is derived is a bit arbitrary.
+# The choice of measured constants is based on convenience and on available uncertainty.
+# The uncertainty in the last significant digits is given in parentheses as a comment.
+
+newtonian_constant_of_gravitation = 6.67430e-11 m^3/(kg s^2) = _ = gravitational_constant  # (15)
+rydberg_constant = 1.0973731568160e7 * m^-1 = R_∞ = R_inf                                  # (21)
+electron_g_factor = -2.00231930436256 = g_e                                                # (35)
+atomic_mass_constant = 1.66053906660e-27 kg = m_u                                          # (50)
+electron_mass = 9.1093837015e-31 kg = m_e = atomic_unit_of_mass = a_u_mass                 # (28)
+proton_mass = 1.67262192369e-27 kg = m_p                                                   # (51)
+neutron_mass = 1.67492749804e-27 kg = m_n                                                  # (95)
+lattice_spacing_of_Si = 1.920155716e-10 m = d_220                                          # (32)
+K_alpha_Cu_d_220 = 0.80232719                                                              # (22)
+K_alpha_Mo_d_220 = 0.36940604                                                              # (19)
+K_alpha_W_d_220 = 0.108852175                                                              # (98)
+
+#### DERIVED CONSTANTS ####
+
+fine_structure_constant = (2 * h * R_inf / (m_e * c)) ** 0.5 = α = alpha
+vacuum_permeability = 2 * α * h / (e ** 2 * c) = µ_0 = mu_0 = mu0 = magnetic_constant
+vacuum_permittivity = e ** 2 / (2 * α * h * c) = ε_0 = epsilon_0 = eps_0 = eps0 = electric_constant
+impedance_of_free_space = 2 * α * h / e ** 2 = Z_0 = characteristic_impedance_of_vacuum
+coulomb_constant = α * hbar * c / e ** 2 = k_C
+classical_electron_radius = α * hbar / (m_e * c) = r_e
+thomson_cross_section = 8 / 3 * π * r_e ** 2 = σ_e = sigma_e
diff --git a/nomad/units/default_en.txt b/nomad/units/default_en.txt
new file mode 100644
index 0000000000000000000000000000000000000000..916e924c74a4e60ba39360c0d97d152d2e588bb9
--- /dev/null
+++ b/nomad/units/default_en.txt
@@ -0,0 +1,848 @@
+# Default Pint units definition file
+# Based on the International System of Units
+# Language: english
+# :copyright: 2013,2019 by Pint Authors, see AUTHORS for more details.
+
+# Syntax
+# ======
+# Units
+# -----
+# <canonical name> = <relation to another unit or dimension> [= <symbol>] [= <alias>] [ = <alias> ] [...]
+#
+# The canonical name and aliases should be expressed in singular form.
+# Pint automatically deals with plurals built by adding 's' to the singular form; plural
+# forms that don't follow this rule should be instead explicitly listed as aliases.
+#
+# If a unit has no symbol and one wants to define aliases, then the symbol should be
+# conventionally set to _.
+#
+# Example:
+#     millennium = 1e3 * year = _ = millennia
+#
+#
+# Prefixes
+# --------
+# <prefix>- = <amount> [= <symbol>] [= <alias>] [ = <alias> ] [...]
+#
+# Example:
+#     deca- =  1e+1  = da- = deka-
+#
+#
+# Derived dimensions
+# ------------------
+# [dimension name] = <relation to other dimensions>
+#
+# Example:
+#     [density] = [mass] / [volume]
+#
+# Note that primary dimensions don't need to be declared; they can be
+# defined for the first time in a unit definition.
+# E.g. see below `meter = [length]`
+#
+#
+# Additional aliases
+# ------------------
+# @alias <canonical name or previous alias> = <alias> [ = <alias> ] [...]
+#
+# Used to add aliases to already existing unit definitions.
+# Particularly useful when one wants to enrich definitions
+# from defaults_en.txt with custom aliases.
+#
+# Example:
+#     @alias meter = my_meter
+
+# See also: https://pint.readthedocs.io/en/latest/defining.html
+
+@defaults
+    group = international
+    system = mks
+@end
+
+
+#### PREFIXES ####
+
+# decimal prefixes
+yocto- = 1e-24 = y-
+zepto- = 1e-21 = z-
+atto- =  1e-18 = a-
+femto- = 1e-15 = f-
+pico- =  1e-12 = p-
+nano- =  1e-9  = n-
+micro- = 1e-6  = µ- = u-
+milli- = 1e-3  = m-
+centi- = 1e-2  = c-
+deci- =  1e-1  = d-
+deca- =  1e+1  = da- = deka-
+hecto- = 1e2   = h-
+kilo- =  1e3   = k-
+mega- =  1e6   = M-
+giga- =  1e9   = G-
+tera- =  1e12  = T-
+peta- =  1e15  = P-
+exa- =   1e18  = E-
+zetta- = 1e21  = Z-
+yotta- = 1e24  = Y-
+
+# binary_prefixes
+kibi- = 2**10 = Ki-
+mebi- = 2**20 = Mi-
+gibi- = 2**30 = Gi-
+tebi- = 2**40 = Ti-
+pebi- = 2**50 = Pi-
+exbi- = 2**60 = Ei-
+zebi- = 2**70 = Zi-
+yobi- = 2**80 = Yi-
+
+# extra_prefixes
+semi- = 0.5 = _ = demi-
+sesqui- = 1.5
+
+
+#### BASE UNITS ####
+
+meter = [length] = m = metre
+second = [time] = s = sec
+ampere = [current] = A = amp
+candela = [luminosity] = cd = candle
+gram = [mass] = g
+mole = [substance] = mol
+kelvin = [temperature]; offset: 0 = K = degK = °K = degree_Kelvin = degreeK  # older names supported for compatibility
+radian = [] = rad
+neper = [] = Np
+bit = []
+count = []
+
+
+#### CONSTANTS ####
+
+@import constants_en.txt
+
+
+#### UNITS ####
+# Common and less common, grouped by quantity.
+# Conversion factors are exact (except when noted),
+# although floating-point conversion may introduce inaccuracies
+
+# Angle
+turn = 2 * π * radian = _ = revolution = cycle = circle
+degree = π / 180 * radian = deg = arcdeg = arcdegree = angular_degree
+arcminute = degree / 60 = arcmin = arc_minute = angular_minute
+arcsecond = arcminute / 60 = arcsec = arc_second = angular_second
+milliarcsecond = 1e-3 * arcsecond = mas
+grade = π / 200 * radian = grad = gon
+mil = π / 32000 * radian
+
+# Solid angle
+steradian = radian ** 2 = sr
+square_degree = (π / 180) ** 2 * sr = sq_deg = sqdeg
+
+# Logarithmic ratio
+bel = 0.5 * ln10 * neper
+
+# Information
+byte = 8 * bit = B = octet
+baud = bit / second = Bd = bps
+
+# Length
+angstrom = 1e-10 * meter = Å = ångström = Å
+micron = micrometer = µ
+fermi = femtometer = fm
+light_year = speed_of_light * julian_year = ly = lightyear
+astronomical_unit = 149597870700 * meter = au  # since Aug 2012
+parsec = 1 / tansec * astronomical_unit = pc
+nautical_mile = 1852 * meter = nmi
+bohr = hbar / (alpha * m_e * c) = a_0 = a0 = bohr_radius = atomic_unit_of_length = a_u_length
+x_unit_Cu = K_alpha_Cu_d_220 * d_220 / 1537.4 = Xu_Cu
+x_unit_Mo = K_alpha_Mo_d_220 * d_220 / 707.831 = Xu_Mo
+angstrom_star = K_alpha_W_d_220 * d_220 / 0.2090100 = Å_star
+planck_length = (hbar * gravitational_constant / c ** 3) ** 0.5
+
+# Mass
+metric_ton = 1e3 * kilogram = t = tonne
+unified_atomic_mass_unit = atomic_mass_constant = u = amu
+dalton = atomic_mass_constant = Da
+grain = 64.79891 * milligram = gr
+gamma_mass = microgram
+carat = 200 * milligram = ct = karat
+planck_mass = (hbar * c / gravitational_constant) ** 0.5
+
+# Time
+minute = 60 * second = min
+hour = 60 * minute = hr
+day = 24 * hour = d
+week = 7 * day
+fortnight = 2 * week
+year = 365.25 * day = a = yr = julian_year
+month = year / 12
+decade = 10 * year
+century = 100 * year = _ = centuries
+millennium = 1e3 * year = _ = millennia
+eon = 1e9 * year
+shake = 1e-8 * second
+svedberg = 1e-13 * second
+atomic_unit_of_time = hbar / E_h = a_u_time
+gregorian_year = 365.2425 * day
+sidereal_year = 365.256363004 * day                # approximate, as of J2000 epoch
+tropical_year = 365.242190402 * day                # approximate, as of J2000 epoch
+common_year = 365 * day
+leap_year = 366 * day
+sidereal_day = day / 1.00273790935079524           # approximate
+sidereal_month = 27.32166155 * day                 # approximate
+tropical_month = 27.321582 * day                   # approximate
+synodic_month = 29.530589 * day = _ = lunar_month  # approximate
+planck_time = (hbar * gravitational_constant / c ** 5) ** 0.5
+
+# Temperature
+degree_Celsius = kelvin; offset: 273.15 = °C = celsius = degC = degreeC
+degree_Rankine = 5 / 9 * kelvin; offset: 0 = °R = rankine = degR = degreeR
+degree_Fahrenheit = 5 / 9 * kelvin; offset: 233.15 + 200 / 9 = °F = fahrenheit = degF = degreeF
+degree_Reaumur = 4 / 5 * kelvin; offset: 273.15 = °Re = reaumur = degRe = degreeRe = degree_Réaumur = réaumur
+atomic_unit_of_temperature = E_h / k = a_u_temp
+planck_temperature = (hbar * c ** 5 / gravitational_constant / k ** 2) ** 0.5
+
+# Area
+[area] = [length] ** 2
+are = 100 * meter ** 2
+barn = 1e-28 * meter ** 2 = b
+darcy = centipoise * centimeter ** 2 / (second * atmosphere)
+hectare = 100 * are = ha
+
+# Volume
+[volume] = [length] ** 3
+liter = decimeter ** 3 = l = L = litre
+cubic_centimeter = centimeter ** 3 = cc
+lambda = microliter = λ
+stere = meter ** 3
+
+# Frequency
+[frequency] = 1 / [time]
+hertz = 1 / second = Hz
+revolutions_per_minute = revolution / minute = rpm
+revolutions_per_second = revolution / second = rps
+counts_per_second = count / second = cps
+
+# Wavenumber
+[wavenumber] = 1 / [length]
+reciprocal_centimeter = 1 / cm = cm_1 = kayser
+
+# Velocity
+[velocity] = [length] / [time] = [speed]
+knot = nautical_mile / hour = kt = knot_international = international_knot
+mile_per_hour = mile / hour = mph = MPH
+kilometer_per_hour = kilometer / hour = kph = KPH
+kilometer_per_second = kilometer / second = kps
+meter_per_second = meter / second = mps
+foot_per_second = foot / second = fps
+
+# Acceleration
+[acceleration] = [velocity] / [time]
+galileo = centimeter / second ** 2 = Gal
+
+# Force
+[force] = [mass] * [acceleration]
+newton = kilogram * meter / second ** 2 = N
+dyne = gram * centimeter / second ** 2 = dyn
+force_kilogram = g_0 * kilogram = kgf = kilogram_force = pond
+force_gram = g_0 * gram = gf = gram_force
+force_metric_ton = g_0 * metric_ton = tf = metric_ton_force = force_t = t_force
+atomic_unit_of_force = E_h / a_0 = a_u_force
+
+# Energy
+[energy] = [force] * [length]
+joule = newton * meter = J
+erg = dyne * centimeter
+watt_hour = watt * hour = Wh = watthour
+electron_volt = e * volt = eV
+rydberg = h * c * R_inf = Ry
+hartree = 2 * rydberg = E_h = Eh = hartree_energy = atomic_unit_of_energy = a_u_energy
+calorie = 4.184 * joule = cal = thermochemical_calorie = cal_th
+international_calorie = 4.1868 * joule = cal_it = international_steam_table_calorie
+fifteen_degree_calorie = 4.1855 * joule = cal_15
+british_thermal_unit = 1055.056 * joule = Btu = BTU = Btu_iso
+international_british_thermal_unit = 1e3 * pound / kilogram * degR / kelvin * international_calorie = Btu_it
+thermochemical_british_thermal_unit = 1e3 * pound / kilogram * degR / kelvin * calorie = Btu_th
+quadrillion_Btu = 1e15 * Btu = quad
+therm = 1e5 * Btu = thm = EC_therm
+US_therm = 1.054804e8 * joule  # approximate, no exact definition
+ton_TNT = 1e9 * calorie = tTNT
+tonne_of_oil_equivalent = 1e10 * international_calorie = toe
+atmosphere_liter = atmosphere * liter = atm_l
+
+# Power
+[power] = [energy] / [time]
+watt = joule / second = W
+volt_ampere = volt * ampere = VA
+horsepower = 550 * foot * force_pound / second = hp = UK_horsepower = hydraulic_horsepower
+boiler_horsepower = 33475 * Btu / hour                            # unclear which Btu
+metric_horsepower = 75 * force_kilogram * meter / second
+electrical_horsepower = 746 * watt
+refrigeration_ton = 12e3 * Btu / hour = _ = ton_of_refrigeration  # approximate, no exact definition
+standard_liter_per_minute = atmosphere * liter / minute = slpm = slm
+conventional_watt_90 = K_J90 ** 2 * R_K90 / (K_J ** 2 * R_K) * watt = W_90
+
+# Momentum
+[momentum] = [length] * [mass] / [time]
+
+# Density (as auxiliary for pressure)
+[density] = [mass] / [volume]
+mercury = 13.5951 * kilogram / liter = Hg = Hg_0C = Hg_32F = conventional_mercury
+water = 1.0 * kilogram / liter = H2O = conventional_water
+mercury_60F = 13.5568 * kilogram / liter = Hg_60F   # approximate
+water_39F = 0.999972 * kilogram / liter = water_4C  # approximate
+water_60F = 0.999001 * kilogram / liter             # approximate
+
+# Pressure
+[pressure] = [force] / [area]
+pascal = newton / meter ** 2 = Pa
+barye = dyne / centimeter ** 2 = Ba = barie = barad = barrie = baryd
+bar = 1e5 * pascal
+technical_atmosphere = kilogram * g_0 / centimeter ** 2 = at
+torr = atm / 760
+pound_force_per_square_inch = force_pound / inch ** 2 = psi
+kip_per_square_inch = kip / inch ** 2 = ksi
+millimeter_Hg = millimeter * Hg * g_0 = mmHg = mm_Hg = millimeter_Hg_0C
+centimeter_Hg = centimeter * Hg * g_0 = cmHg = cm_Hg = centimeter_Hg_0C
+inch_Hg = inch * Hg * g_0 = inHg = in_Hg = inch_Hg_32F
+inch_Hg_60F = inch * Hg_60F * g_0
+inch_H2O_39F = inch * water_39F * g_0
+inch_H2O_60F = inch * water_60F * g_0
+foot_H2O = foot * water * g_0 = ftH2O = feet_H2O
+centimeter_H2O = centimeter * water * g_0 = cmH2O = cm_H2O
+
+# Torque
+[torque] = [force] * [length]
+foot_pound = foot * force_pound = ft_lb = footpound
+
+# Viscosity
+[viscosity] = [pressure] * [time]
+poise = 0.1 * Pa * second = P
+reyn = psi * second
+
+# Kinematic viscosity
+[kinematic_viscosity] = [area] / [time]
+stokes = centimeter ** 2 / second = St
+
+# Fluidity
+[fluidity] = 1 / [viscosity]
+rhe = 1 / poise
+
+# Amount of substance
+particle = 1 / N_A = _ = molec = molecule
+
+# Concentration
+[concentration] = [substance] / [volume]
+molar = mole / liter = M
+
+# Catalytic activity
+[activity] = [substance] / [time]
+katal = mole / second = kat
+enzyme_unit = micromole / minute = U = enzymeunit
+
+# Entropy
+[entropy] = [energy] / [temperature]
+clausius = calorie / kelvin = Cl
+
+# Molar entropy
+[molar_entropy] = [entropy] / [substance]
+entropy_unit = calorie / kelvin / mole = eu
+
+# Radiation
+becquerel = counts_per_second = Bq
+curie = 3.7e10 * becquerel = Ci
+rutherford = 1e6 * becquerel = Rd
+gray = joule / kilogram = Gy
+sievert = joule / kilogram = Sv
+rads = 0.01 * gray
+rem = 0.01 * sievert
+roentgen = 2.58e-4 * coulomb / kilogram = _ = röntgen  # approximate, depends on medium
+
+# Heat transimission
+[heat_transmission] = [energy] / [area]
+peak_sun_hour = 1e3 * watt_hour / meter ** 2 = PSH
+langley = thermochemical_calorie / centimeter ** 2 = Ly
+
+# Luminance
+[luminance] = [luminosity] / [area]
+nit = candela / meter ** 2
+stilb = candela / centimeter ** 2
+lambert = 1 / π * candela / centimeter ** 2
+
+# Luminous flux
+[luminous_flux] = [luminosity]
+lumen = candela * steradian = lm
+
+# Illuminance
+[illuminance] = [luminous_flux] / [area]
+lux = lumen / meter ** 2 = lx
+
+# Intensity
+[intensity] = [power] / [area]
+atomic_unit_of_intensity = 0.5 * ε_0 * c * atomic_unit_of_electric_field ** 2 = a_u_intensity
+
+# Current
+biot = 10 * ampere = Bi
+abampere = biot = abA
+atomic_unit_of_current = e / atomic_unit_of_time = a_u_current
+mean_international_ampere = mean_international_volt / mean_international_ohm = A_it
+US_international_ampere = US_international_volt / US_international_ohm = A_US
+conventional_ampere_90 = K_J90 * R_K90 / (K_J * R_K) * ampere = A_90
+planck_current = (c ** 6 / gravitational_constant / k_C) ** 0.5
+
+# Charge
+[charge] = [current] * [time]
+coulomb = ampere * second = C
+abcoulomb = 10 * C = abC
+faraday = e * N_A * mole
+conventional_coulomb_90 = K_J90 * R_K90 / (K_J * R_K) * coulomb = C_90
+
+# Electric potential
+[electric_potential] = [energy] / [charge]
+volt = joule / coulomb = V
+abvolt = 1e-8 * volt = abV
+mean_international_volt = 1.00034 * volt = V_it  # approximate
+US_international_volt = 1.00033 * volt = V_US    # approximate
+conventional_volt_90 = K_J90 / K_J * volt = V_90
+
+# Electric field
+[electric_field] = [electric_potential] / [length]
+atomic_unit_of_electric_field = e * k_C / a_0 ** 2 = a_u_electric_field
+
+# Electric displacement field
+[electric_displacement_field] = [charge] / [area]
+
+# Resistance
+[resistance] = [electric_potential] / [current]
+ohm = volt / ampere = Ω
+abohm = 1e-9 * ohm = abΩ
+mean_international_ohm = 1.00049 * ohm = Ω_it = ohm_it  # approximate
+US_international_ohm = 1.000495 * ohm = Ω_US = ohm_US   # approximate
+conventional_ohm_90 = R_K / R_K90 * ohm = Ω_90 = ohm_90
+
+# Resistivity
+[resistivity] = [resistance] * [length]
+
+# Conductance
+[conductance] = [current] / [electric_potential]
+siemens = ampere / volt = S = mho
+absiemens = 1e9 * siemens = abS = abmho
+
+# Capacitance
+[capacitance] = [charge] / [electric_potential]
+farad = coulomb / volt = F
+abfarad = 1e9 * farad = abF
+conventional_farad_90 = R_K90 / R_K * farad = F_90
+
+# Inductance
+[inductance] = [magnetic_flux] / [current]
+henry = weber / ampere = H
+abhenry = 1e-9 * henry = abH
+conventional_henry_90 = R_K / R_K90 * henry = H_90
+
+# Magnetic flux
+[magnetic_flux] = [electric_potential] * [time]
+weber = volt * second = Wb
+unit_pole = µ_0 * biot * centimeter
+
+# Magnetic field
+[magnetic_field] = [magnetic_flux] / [area]
+tesla = weber / meter ** 2 = T
+gamma = 1e-9 * tesla = γ
+
+# Magnetomotive force
+[magnetomotive_force] = [current]
+ampere_turn = ampere = At
+biot_turn = biot
+gilbert = 1 / (4 * π) * biot_turn = Gb
+
+# Magnetic field strength
+[magnetic_field_strength] = [current] / [length]
+
+# Electric dipole moment
+[electric_dipole] = [charge] * [length]
+debye = 1e-9 / ζ * coulomb * angstrom = D  # formally 1 D = 1e-10 Fr*Å, but we generally want to use it outside the Gaussian context
+
+# Electric quadrupole moment
+[electric_quadrupole] = [charge] * [area]
+buckingham = debye * angstrom
+
+# Magnetic dipole moment
+[magnetic_dipole] = [current] * [area]
+bohr_magneton = e * hbar / (2 * m_e) = µ_B = mu_B
+nuclear_magneton = e * hbar / (2 * m_p) = µ_N = mu_N
+
+
+#### UNIT GROUPS ####
+# Mostly for length, area, volume, mass, force
+# (customary or specialized units)
+
+@group USCSLengthInternational
+    thou = 1e-3 * inch = th = mil_length
+    inch = yard / 36 = in = international_inch = inches = international_inches
+    hand = 4 * inch
+    foot = yard / 3 = ft = international_foot = feet = international_feet
+    yard = 0.9144 * meter = yd = international_yard  # since Jul 1959
+    mile = 1760 * yard = mi = international_mile
+
+    circular_mil = π / 4 * mil_length ** 2 = cmil
+    square_inch = inch ** 2 = sq_in = square_inches
+    square_foot = foot ** 2 = sq_ft = square_feet
+    square_yard = yard ** 2 = sq_yd
+    square_mile = mile ** 2 = sq_mi
+
+    cubic_inch = in ** 3 = cu_in
+    cubic_foot = ft ** 3 = cu_ft = cubic_feet
+    cubic_yard = yd ** 3 = cu_yd
+@end
+
+@group USCSLengthSurvey
+    link = 1e-2 * chain = li = survey_link
+    survey_foot = 1200 / 3937 * meter = sft
+    fathom = 6 * survey_foot
+    rod = 16.5 * survey_foot = rd = pole = perch
+    chain = 4 * rod
+    furlong = 40 * rod = fur
+    cables_length = 120 * fathom
+    survey_mile = 5280 * survey_foot = smi = us_statute_mile
+    league = 3 * survey_mile
+
+    square_rod = rod ** 2 = sq_rod = sq_pole = sq_perch
+    acre = 10 * chain ** 2
+    square_survey_mile = survey_mile ** 2 = _ = section
+    square_league = league ** 2
+
+    acre_foot = acre * survey_foot = _ = acre_feet
+@end
+
+@group USCSDryVolume
+    dry_pint = bushel / 64 = dpi = US_dry_pint
+    dry_quart = bushel / 32 = dqt = US_dry_quart
+    dry_gallon = bushel / 8 = dgal = US_dry_gallon
+    peck = bushel / 4 = pk
+    bushel = 2150.42 cubic_inch = bu
+    dry_barrel = 7056 cubic_inch = _ = US_dry_barrel
+    board_foot = ft * ft * in = FBM = board_feet = BF = BDFT = super_foot = superficial_foot = super_feet = superficial_feet
+@end
+
+@group USCSLiquidVolume
+    minim = pint / 7680
+    fluid_dram = pint / 128 = fldr = fluidram = US_fluid_dram = US_liquid_dram
+    fluid_ounce = pint / 16 = floz = US_fluid_ounce = US_liquid_ounce
+    gill = pint / 4 = gi = liquid_gill = US_liquid_gill
+    pint = quart / 2 = pt = liquid_pint = US_pint
+    fifth = gallon / 5 = _ = US_liquid_fifth
+    quart = gallon / 4 = qt = liquid_quart = US_liquid_quart
+    gallon = 231 * cubic_inch = gal = liquid_gallon = US_liquid_gallon
+@end
+
+@group USCSVolumeOther
+    teaspoon = fluid_ounce / 6 = tsp
+    tablespoon = fluid_ounce / 2 = tbsp
+    shot = 3 * tablespoon = jig = US_shot
+    cup = pint / 2 = cp = liquid_cup = US_liquid_cup
+    barrel = 31.5 * gallon = bbl
+    oil_barrel = 42 * gallon = oil_bbl
+    beer_barrel = 31 * gallon = beer_bbl
+    hogshead = 63 * gallon
+@end
+
+@group Avoirdupois
+    dram = pound / 256 = dr = avoirdupois_dram = avdp_dram = drachm
+    ounce = pound / 16 = oz = avoirdupois_ounce = avdp_ounce
+    pound = 7e3 * grain = lb = avoirdupois_pound = avdp_pound
+    stone = 14 * pound
+    quarter = 28 * stone
+    bag = 94 * pound
+    hundredweight = 100 * pound = cwt = short_hundredweight
+    long_hundredweight = 112 * pound
+    ton = 2e3 * pound = _ = short_ton
+    long_ton = 2240 * pound
+    slug = g_0 * pound * second ** 2 / foot
+    slinch = g_0 * pound * second ** 2 / inch = blob = slugette
+
+    force_ounce = g_0 * ounce = ozf = ounce_force
+    force_pound = g_0 * pound = lbf = pound_force
+    force_ton = g_0 * ton = _ = ton_force = force_short_ton = short_ton_force
+    force_long_ton = g_0 * long_ton = _ = long_ton_force
+    kip = 1e3 * force_pound
+    poundal = pound * foot / second ** 2 = pdl
+@end
+
+@group AvoirdupoisUK using Avoirdupois
+    UK_hundredweight = long_hundredweight = UK_cwt
+    UK_ton = long_ton
+    UK_force_ton = force_long_ton = _ = UK_ton_force
+@end
+
+@group AvoirdupoisUS using Avoirdupois
+    US_hundredweight = hundredweight = US_cwt
+    US_ton = ton
+    US_force_ton = force_ton = _ = US_ton_force
+@end
+
+@group Troy
+    pennyweight = 24 * grain = dwt
+    troy_ounce = 480 * grain = toz = ozt
+    troy_pound = 12 * troy_ounce = tlb = lbt
+@end
+
+@group Apothecary
+    scruple = 20 * grain
+    apothecary_dram = 3 * scruple = ap_dr
+    apothecary_ounce = 8 * apothecary_dram = ap_oz
+    apothecary_pound = 12 * apothecary_ounce = ap_lb
+@end
+
+@group ImperialVolume
+    imperial_minim = imperial_fluid_ounce / 480
+    imperial_fluid_scruple = imperial_fluid_ounce / 24
+    imperial_fluid_drachm = imperial_fluid_ounce / 8 = imperial_fldr = imperial_fluid_dram
+    imperial_fluid_ounce = imperial_pint / 20 = imperial_floz = UK_fluid_ounce
+    imperial_gill = imperial_pint / 4 = imperial_gi = UK_gill
+    imperial_cup = imperial_pint / 2 = imperial_cp = UK_cup
+    imperial_pint = imperial_gallon / 8 = imperial_pt = UK_pint
+    imperial_quart = imperial_gallon / 4 = imperial_qt = UK_quart
+    imperial_gallon = 4.54609 * liter = imperial_gal = UK_gallon
+    imperial_peck = 2 * imperial_gallon = imperial_pk = UK_pk
+    imperial_bushel = 8 * imperial_gallon = imperial_bu = UK_bushel
+    imperial_barrel = 36 * imperial_gallon = imperial_bbl = UK_bbl
+@end
+
+@group Printer
+    pica = inch / 6 = _ = printers_pica
+    point = pica / 12 = pp = printers_point = big_point = bp
+    didot = 1 / 2660 * m
+    cicero = 12 * didot
+    tex_point = inch / 72.27
+    tex_pica = 12 * tex_point
+    tex_didot = 1238 / 1157 * tex_point
+    tex_cicero = 12 * tex_didot
+    scaled_point = tex_point / 65536
+    css_pixel = inch / 96 = px
+
+    pixel = [printing_unit] = _ = dot = pel = picture_element
+    pixels_per_centimeter = pixel / cm = PPCM
+    pixels_per_inch = pixel / inch = dots_per_inch = PPI = ppi = DPI = printers_dpi
+    bits_per_pixel = bit / pixel = bpp
+@end
+
+@group Textile
+    tex = gram / kilometer = Tt
+    dtex = decitex
+    denier = gram / (9 * kilometer) = den = Td
+    jute = pound / (14400 * yard) = Tj
+    aberdeen = jute = Ta
+    RKM  = gf / tex
+
+    number_english = 840 * yard / pound = Ne = NeC = ECC
+    number_meter = kilometer / kilogram = Nm
+@end
+
+
+#### CGS ELECTROMAGNETIC UNITS ####
+
+# === Gaussian system of units ===
+@group Gaussian
+    franklin = erg ** 0.5 * centimeter ** 0.5 = Fr = statcoulomb = statC = esu
+    statvolt = erg / franklin = statV
+    statampere = franklin / second = statA
+    gauss = dyne / franklin = G
+    maxwell = gauss * centimeter ** 2 = Mx
+    oersted = dyne / maxwell = Oe = ørsted
+    statohm = statvolt / statampere = statΩ
+    statfarad = franklin / statvolt = statF
+    statmho = statampere / statvolt
+@end
+# Note this system is not commensurate with SI, as ε_0 and µ_0 disappear;
+# some quantities with different dimensions in SI have the same
+# dimensions in the Gaussian system (e.g. [Mx] = [Fr], but [Wb] != [C]),
+# and therefore the conversion factors depend on the context (not in pint sense)
+[gaussian_charge] = [length] ** 1.5 * [mass] ** 0.5 / [time]
+[gaussian_current] = [gaussian_charge] / [time]
+[gaussian_electric_potential] = [gaussian_charge] / [length]
+[gaussian_electric_field] = [gaussian_electric_potential] / [length]
+[gaussian_electric_displacement_field] = [gaussian_charge] / [area]
+[gaussian_electric_flux] = [gaussian_charge]
+[gaussian_electric_dipole] = [gaussian_charge] * [length]
+[gaussian_electric_quadrupole] = [gaussian_charge] * [area]
+[gaussian_magnetic_field] = [force] / [gaussian_charge]
+[gaussian_magnetic_field_strength] = [gaussian_magnetic_field]
+[gaussian_magnetic_flux] = [gaussian_magnetic_field] * [area]
+[gaussian_magnetic_dipole] = [energy] / [gaussian_magnetic_field]
+[gaussian_resistance] = [gaussian_electric_potential] / [gaussian_current]
+[gaussian_resistivity] = [gaussian_resistance] * [length]
+[gaussian_capacitance] = [gaussian_charge] / [gaussian_electric_potential]
+[gaussian_inductance] = [gaussian_electric_potential] * [time] / [gaussian_current]
+[gaussian_conductance] = [gaussian_current] / [gaussian_electric_potential]
+@context Gaussian = Gau
+    [gaussian_charge] -> [charge]: value / k_C ** 0.5
+    [charge] -> [gaussian_charge]: value * k_C ** 0.5
+    [gaussian_current] -> [current]: value / k_C ** 0.5
+    [current] -> [gaussian_current]: value * k_C ** 0.5
+    [gaussian_electric_potential] -> [electric_potential]: value * k_C ** 0.5
+    [electric_potential] -> [gaussian_electric_potential]: value / k_C ** 0.5
+    [gaussian_electric_field] -> [electric_field]: value * k_C ** 0.5
+    [electric_field] -> [gaussian_electric_field]: value / k_C ** 0.5
+    [gaussian_electric_displacement_field] -> [electric_displacement_field]: value / (4 * π / ε_0) ** 0.5
+    [electric_displacement_field] -> [gaussian_electric_displacement_field]: value * (4 * π / ε_0) ** 0.5
+    [gaussian_electric_dipole] -> [electric_dipole]: value / k_C ** 0.5
+    [electric_dipole] -> [gaussian_electric_dipole]: value * k_C ** 0.5
+    [gaussian_electric_quadrupole] -> [electric_quadrupole]: value / k_C ** 0.5
+    [electric_quadrupole] -> [gaussian_electric_quadrupole]: value * k_C ** 0.5
+    [gaussian_magnetic_field] -> [magnetic_field]: value / (4 * π / µ_0) ** 0.5
+    [magnetic_field] -> [gaussian_magnetic_field]: value * (4 * π / µ_0) ** 0.5
+    [gaussian_magnetic_flux] -> [magnetic_flux]: value / (4 * π / µ_0) ** 0.5
+    [magnetic_flux] -> [gaussian_magnetic_flux]: value * (4 * π / µ_0) ** 0.5
+    [gaussian_magnetic_field_strength] -> [magnetic_field_strength]: value / (4 * π * µ_0) ** 0.5
+    [magnetic_field_strength] -> [gaussian_magnetic_field_strength]: value * (4 * π * µ_0) ** 0.5
+    [gaussian_magnetic_dipole] -> [magnetic_dipole]: value * (4 * π / µ_0) ** 0.5
+    [magnetic_dipole] -> [gaussian_magnetic_dipole]: value / (4 * π / µ_0) ** 0.5
+    [gaussian_resistance] -> [resistance]: value * k_C
+    [resistance] -> [gaussian_resistance]: value / k_C
+    [gaussian_resistivity] -> [resistivity]: value * k_C
+    [resistivity] -> [gaussian_resistivity]: value / k_C
+    [gaussian_capacitance] -> [capacitance]: value / k_C
+    [capacitance] -> [gaussian_capacitance]: value * k_C
+    [gaussian_inductance] -> [inductance]: value * k_C
+    [inductance] -> [gaussian_inductance]: value / k_C
+    [gaussian_conductance] -> [conductance]: value / k_C
+    [conductance] -> [gaussian_conductance]: value * k_C
+@end
+
+# === ESU system of units ===
+#   (where different from Gaussian)
+#   See note for Gaussian system too
+@group ESU using Gaussian
+    statweber = statvolt * second = statWb
+    stattesla = statweber / centimeter ** 2 = statT
+    stathenry = statweber / statampere = statH
+@end
+[esu_charge] = [length] ** 1.5 * [mass] ** 0.5 / [time]
+[esu_current] = [esu_charge] / [time]
+[esu_electric_potential] = [esu_charge] / [length]
+[esu_magnetic_flux] = [esu_electric_potential] * [time]
+[esu_magnetic_field] = [esu_magnetic_flux] / [area]
+[esu_magnetic_field_strength] = [esu_current] / [length]
+[esu_magnetic_dipole] = [esu_current] * [area]
+@context ESU = esu
+    [esu_magnetic_field] -> [magnetic_field]: value * k_C ** 0.5
+    [magnetic_field] -> [esu_magnetic_field]: value / k_C ** 0.5
+    [esu_magnetic_flux] -> [magnetic_flux]: value * k_C ** 0.5
+    [magnetic_flux] -> [esu_magnetic_flux]: value / k_C ** 0.5
+    [esu_magnetic_field_strength] -> [magnetic_field_strength]: value / (4 * π / ε_0) ** 0.5
+    [magnetic_field_strength] -> [esu_magnetic_field_strength]: value * (4 * π / ε_0) ** 0.5
+    [esu_magnetic_dipole] -> [magnetic_dipole]: value / k_C ** 0.5
+    [magnetic_dipole] -> [esu_magnetic_dipole]: value * k_C ** 0.5
+@end
+
+
+#### CONVERSION CONTEXTS ####
+
+@context(n=1) spectroscopy = sp
+    # n index of refraction of the medium.
+    [length] <-> [frequency]: speed_of_light / n / value
+    [frequency] -> [energy]: planck_constant * value
+    [energy] -> [frequency]: value / planck_constant
+    # allow wavenumber / kayser
+    [wavenumber] <-> [length]: 1 / value
+@end
+
+@context boltzmann
+    [temperature] -> [energy]: boltzmann_constant * value
+    [energy] -> [temperature]: value / boltzmann_constant
+@end
+
+@context energy
+    [energy] -> [energy] / [substance]: value * N_A
+    [energy] / [substance] -> [energy]: value / N_A
+    [energy] -> [mass]: value / c ** 2
+    [mass] -> [energy]: value * c ** 2
+@end
+
+@context(mw=0,volume=0,solvent_mass=0) chemistry = chem
+    # mw is the molecular weight of the species
+    # volume is the volume of the solution
+    # solvent_mass is the mass of solvent in the solution
+
+    # moles -> mass require the molecular weight
+    [substance] -> [mass]: value * mw
+    [mass] -> [substance]: value / mw
+
+    # moles/volume -> mass/volume and moles/mass -> mass/mass
+    # require the  molecular weight
+    [substance] / [volume] -> [mass] / [volume]: value * mw
+    [mass] / [volume] -> [substance] / [volume]: value / mw
+    [substance] / [mass] -> [mass] / [mass]: value * mw
+    [mass] / [mass] -> [substance] / [mass]: value / mw
+
+    # moles/volume -> moles requires the solution volume
+    [substance] / [volume] -> [substance]: value * volume
+    [substance] -> [substance] / [volume]: value / volume
+
+    # moles/mass -> moles requires the solvent (usually water) mass
+    [substance] / [mass] -> [substance]: value * solvent_mass
+    [substance] -> [substance] / [mass]: value / solvent_mass
+
+    # moles/mass -> moles/volume require the solvent mass and the volume
+    [substance] / [mass] -> [substance]/[volume]: value * solvent_mass / volume
+    [substance] / [volume] -> [substance] / [mass]: value / solvent_mass * volume
+
+@end
+
+@context textile
+    # Allow switching between Direct count system (i.e. tex) and
+    # Indirect count system (i.e. Ne, Nm)
+    [mass] / [length] <-> [length] / [mass]: 1 / value
+@end
+
+
+#### SYSTEMS OF UNITS ####
+
+@system SI
+    second
+    meter
+    kilogram
+    ampere
+    kelvin
+    mole
+    candela
+@end
+
+@system mks using international
+    meter
+    kilogram
+    second
+@end
+
+@system cgs using international, Gaussian, ESU
+    centimeter
+    gram
+    second
+@end
+
+@system atomic using international
+    # based on unit m_e, e, hbar, k_C, k
+    bohr: meter
+    electron_mass: gram
+    atomic_unit_of_time: second
+    atomic_unit_of_current: ampere
+    atomic_unit_of_temperature: kelvin
+@end
+
+@system Planck using international
+    # based on unit c, gravitational_constant, hbar, k_C, k
+    planck_length: meter
+    planck_mass: gram
+    planck_time: second
+    planck_current: ampere
+    planck_temperature: kelvin
+@end
+
+@system imperial using ImperialVolume, USCSLengthInternational, AvoirdupoisUK
+    yard
+    pound
+@end
+
+@system US using USCSLiquidVolume, USCSDryVolume, USCSVolumeOther, USCSLengthInternational, USCSLengthSurvey, AvoirdupoisUS
+    yard
+    pound
+@end
diff --git a/requirements.txt b/requirements.txt
index 148a49b8b3d35668a4707eee3bd32ba3d3fc3b50..73357bffa65c2b422ecd2723ed6abad8f55ea35a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -42,7 +42,7 @@ optimade==0.8.1
 structlog
 elasticsearch==6.4.0
 msgpack<0.6.0
-celery[redis]
+celery[redis]==4.4.7
 mongoengine==0.19.1
 pymongo==3.10.1
 Werkzeug==0.16.1