Units

In Euphonic, units are handled by Pint. Pint wraps Numpy arrays as a Quantity object, so you can easily see which units values are in.

Euphonic’s ureg

Units in euphonic are accessed through euphonic.ureg, which is a Pint.UnitRegistry object that contains all required units. For example, to create temperature as a Quantity with units in Kelvin rather than a plain float:

from euphonic import ureg
temperature = 5.0*ureg('K')

This can also be done with Numpy arrays, to create an array with units. For example, to create an array of energy bins in meV:

from euphonic import ureg
import numpy as np
arr = np.arange(0, 100, 1)
energy_bins = arr*ureg('meV')

Quantities as Function Arguments

Many Euphonic functions require Quantity wrapped values as arguments. Simply import the unit registry and create a Quantity, then pass it to a function:

from euphonic import ureg, QpointPhononModes

phonons = QpointPhononModes.from_castep('quartz.phonon')

fm = ureg('fm')
scattering_lengths = {'Si': 4.1491*fm, 'O': 5.803*fm}
sf = phonons.calculate_structure_factor(scattering_lengths)

Object Attributes

Any dimensioned attributes (attributes with units) on a Euphonic object, for example cell_vectors or frequencies, are actually properties. They are stored internally in atomic units, and only wrapped as a Quantity in user-friendly units once they are accessed.

Changing attribute units

When a Euphonic object is created, its dimensioned attributes will be in default units (e.g. meV for frequencies, angstrom for cell vectors). Each Quantity attribute has an associated string attribute that can be used to change the units. See the following example to change the units of frequency in QpointPhononModes:

>>> from euphonic import QpointPhononModes
>>> phonons = QpointPhononModes.from_castep('quartz.phonon')
>>> phonons.frequencies[0]
<Quantity([  0.38193331   0.61106305   0.69303768  15.86537413  15.88218266
  27.64203018  31.98753254  32.21911407  42.20189648  42.90662972
  46.91907755  48.26610037  54.57118794  56.89834674  60.53664449
  61.56378648  86.09430023  86.53104436  95.78403943  98.8705196
 100.38739904 132.68487558 133.97647412 134.71659207 142.55121327
 142.97971084 152.80832126], 'millielectron_volt')>
>>> phonons.frequencies_unit = '1/cm'
>>> phonons.frequencies[0]
<Quantity([   3.0805      4.928556    5.589726  127.962875  128.098445  222.948014
  257.996855  259.864686  340.381258  346.065315  378.42789   389.292362
  440.146324  458.916126  488.260977  496.545436  694.397377  697.919956
  772.550396  797.444538  809.678996 1070.175718 1080.593163 1086.562617
 1149.7531   1153.209166 1232.482257], '1 / centimeter')>

The pattern is the same for any Quantity attribute e.g. ForceConstants.force_constants has ForceConstants.force_constants_unit, Crystal.cell_vectors has Crystal.cell_vectors_unit

Changing attribute values

Each dimensioned property also has a setter which allows it to be set. For example, to set new Crystal.cell_vectors:

>>> from euphonic import ForceConstants
>>> fc = ForceConstants.from_castep('quartz.castep_bin')
>>> fc.crystal.cell_vectors
<Quantity([[ 2.42617588 -4.20225989  0.        ]
 [ 2.42617588  4.20225989  0.        ]
 [ 0.          0.          5.35030451]], 'angstrom')>
>>> fc.crystal.cell_vectors = np.array(
...     [[ 4.85235176, -8.40451979, 0.],
...      [ 4.85235176,  8.40451979, 0.],
...      [ 0., 0., 10.70060903]])*ureg('angstrom')
>>> fc.crystal.cell_vectors
<Quantity([[ 4.85235176 -8.40451979  0.        ]
 [ 4.85235176  8.40451979  0.        ]
 [ 0.          0.         10.70060903]], 'angstrom')>

However as dimensioned attributes are properties, individual elements can’t be set by indexing, for example the following to set a single element of Crystal.atom_mass does not work:

>>> from euphonic import ForceConstants
>>> fc = ForceConstants.from_castep('quartz.castep_bin')
>>> fc.crystal.atom_mass
<Quantity([15.99939997 15.99939997 15.99939997 15.99939997 15.99939997 15.99939997
 28.08549995 28.08549995 28.08549995], 'unified_atomic_mass_unit')>
>>> fc.crystal.atom_mass[0] = 17.999*ureg('amu')
>>> fc.crystal.atom_mass
<Quantity([15.99939997 15.99939997 15.99939997 15.99939997 15.99939997 15.99939997
 28.08549995 28.08549995 28.08549995], 'unified_atomic_mass_unit')>

Nothing has changed! Instead, get the entire array, change any desired entries and then set the whole attribute as follows:

>>> from euphonic import ForceConstants
>>> fc = ForceConstants.from_castep('quartz.castep_bin')
>>> atom_mass = fc.crystal.atom_mass
>>> atom_mass[0] = 17.999*ureg('amu')
>>> fc.crystal.atom_mass = atom_mass
>>> fc.crystal.atom_mass
<Quantity([17.999      15.99939997 15.99939997 15.99939997 15.99939997 15.99939997
 28.08549995 28.08549995 28.08549995], 'unified_atomic_mass_unit')>