""" Function that return dictionary with conversion factors from reduced units to real units """
from scipy.constants import Boltzmann
[docs]
def conversion_factors(**kwargs):
""" Return conversion factors from simulation units to common physical units.
The function defines a set of *base simulation units* by specifying what
one unit of length, energy, and mass correspond to in SI (meters, joules,
kilograms). From these three base units it derives such as time, pressure, and more.
These are returned as a dictionary of multiplicative conversion factors.
Interpretation of the returned dictionary
-----------------------------------------
Each entry `cf[key]` is a factor that converts a value expressed in
simulation units to the unit indicated by `key`:
Example: to give simulation time in ps then use `t_ps = t_sim * cf['ps']`.
Parameters
----------
**kwargs
Exactly one keyword must be provided for each of the three base units:
`unit_length*`, `unit_energy*`, and `unit_mass*`. The suffix determines
the input unit, e.g.:
- Length: `unit_length_in_Angstrom`, `unit_length_in_nm`, ...
- Energy: `unit_energy_in_kJ_per_mol`, `unit_energy_in_K`, `unit_energy_in_eV`, ...
- Mass: `unit_mass_in_u`, `unit_mass_in_g`, ...
If no keyword arguments are given, SI base units are assumed:
`unit_length = 1 m`, `unit_energy = 1 J`, `unit_mass = 1 kg`.
Special option:
`get_possible_inputs=True` returns a dictionary mapping all accepted
input keyword names to their conversion factors to SI.
Returns
-------
dict
Dictionary of conversion factors from simulation units to various units.
The dictionary includes (among others):
- length: m, cm, nm, Å, ...
- energy: J, kJ/mol, kcal/mol, K, eV, ...
- mass: kg, g, u, g/mol, ...
- time: s, ps, fs, ns, ...
- pressure: Pa, MPa, GPa, bar, atm, ...
- ...
Raises
------
KeyError
If more than one keyword is provided for length, energy, or mass.
KeyError
If any of length, energy, or mass is not specified (and kwargs are not empty).
Examples
--------
>>> from pprint import pprint
>>> from gamdpy.tools import conversion_factors
>>> cf = conversion_factors(unit_length=1.0, unit_energy=1.0, unit_mass=1.0) # Standard SI units
>>> cf = conversion_factors() # Standard SI units (same as above)
>>> cf = conversion_factors(unit_length_in_Angstrom=3.4, unit_energy_in_K=120.0, unit_mass_in_u = 39.948) # Argon units
>>> cf = conversion_factors(unit_length_in_Angstrom=1.0, unit_energy_in_kcal_per_mol=1.0, unit_mass_in_u=1.0) # Molar units (LAMMPS real units)
>>> cf = conversion_factors(unit_length_in_cm=1.0, unit_energy_in_erg=1.0, unit_mass_in_g=1.0) # centimetre–gram–second (CGS) system
>>> cf = conversion_factors(unit_length_in_nm=1.0, unit_energy_in_kJ_per_mol=1.0, unit_mass_in_u = 1.0) # Atomistic SI-like units
>>> cf = conversion_factors(unit_length_in_Angstrom=1.0, unit_energy_in_eV=1.0, unit_mass_in_u = 1.0) # Metallic units
"""
# Check that one, and only one energy, length and mass is given
for prefix in 'unit_length', 'unit_energy', 'unit_mass':
matches = [k for k in kwargs if k.startswith(prefix)]
if len(matches) > 1:
raise KeyError(f'Expected only one {prefix} key, but got {len(matches)}: {matches}')
# Fundamental constants (same format as scipy.constants)
Avogadro = 6.02214076e+23 # 1 / mol
atomic_mass = 1.66053906892e-27 # kg
Boltzmann = 1.380649e-23 # m² · kg / ( s² · K ) = J / K
calorie = 4.184 # J
electron_volt = 1.602176634e-19 # J
astronomical_unit = 149597870700.0 # m
parsec = 3.085677581491367e+16 # m
atomic_mass = 1.66053906892e-27 # kg
solar_masses = 1.988416e30 # kg
hartree = 4.3597447222060e-18 # J
bohr_radius = 5.29177210544e-11 # m
# What unit one in reduced units corresponds to in SI units
unit_length, unit_energy, unit_mass = None, None, None
if not kwargs:
unit_length, unit_energy, unit_mass = 1.0, 1.0, 1.0 # Assume SI units of no kwargs are given
possible_lengths = {
# Name # Conversion factor to SI
'unit_length': 1.0,
'unit_length_in_cm': 1e-2,
'unit_length_in_nm': 1e-9,
'unit_length_in_Å': 1e-10,
'unit_length_in_Angstrom': 1e-10,
'unit_length_in_Ångström': 1e-10,
'unit_length_in_bohr_radius': bohr_radius,
'unit_length_in_AU': astronomical_unit,
'unit_length_in_parsec': parsec
}
for key, val in possible_lengths.items():
if key in kwargs:
unit_length = kwargs[key]*val
possible_energies = {
# Name # Conversion factor to SI
'unit_energy': 1.0,
'unit_energy_in_K': Boltzmann,
'unit_energy_in_Kelvin': Boltzmann,
'unit_energy_in_kJ_per_mol': 1e3 / Avogadro,
'unit_energy_in_kcal_per_mol': 1e3 * calorie / Avogadro,
'unit_energy_in_erg': 1e-7,
'unit_energy_in_zeptojoule': 1e-21,
'unit_energy_in_hartree': hartree,
'unit_energy_in_eV': electron_volt,
}
for key, val in possible_energies.items():
if key in kwargs:
unit_energy = kwargs[key]*val
possible_masses = {
# Name # Conversion factor to SI
'unit_mass': 1.0,
'unit_mass_in_g': 1e-3,
'unit_mass_in_u': atomic_mass,
'unit_mass_in_amu': atomic_mass,
'unit_mass_in_gram_per_mol': 1e-3 / Avogadro,
'unit_mass_in_attograms': 1e-21
}
for key, val in possible_masses.items():
if key in kwargs:
unit_mass = kwargs[key]*val
# Return a dict with possible inputs and their
if 'get_possible_inputs' in kwargs:
if kwargs['get_possible_inputs']:
return possible_lengths | possible_energies | possible_masses
# Check that a length, energy and mass have been given in keyword arguments
if unit_length == None or unit_energy == None or unit_mass == None:
raise KeyError(f'Some unit_length, unit_energy and unit_mass needs to be specified. Got {kwargs}')
# Derived units. N.B. Below we assume D=3 spatial dimensions
unit_time = unit_length * (unit_mass / unit_energy) ** 0.5 # s
unit_force = unit_energy / unit_length
unit_pressure = unit_energy / unit_length ** 3 # Pa
unit_density = unit_mass / unit_length ** 3 # kg/m^3
unit_area = unit_length**2
unit_volume = unit_length**3
return {
# Length
"unit_length": unit_length, # m
"in_parsec": unit_length / parsec,
"AU": unit_length / astronomical_unit,
"km": unit_length * 1e-3,
"m": unit_length,
"cm": unit_length * 1e2,
"mm": unit_length * 1e3,
"micrometer": unit_length * 1e6,
"μm": unit_length * 1e6,
"micron": unit_length * 1e6,
"nm": unit_length * 1e9,
"Å": unit_length * 1e10,
"Angstrom": unit_length * 1e10,
"Ångstrom": unit_length * 1e10,
"Bohr_radius": unit_length / bohr_radius,
# Energy
"unit_energy": unit_energy, # J
"J": unit_energy,
"zeptojoule": unit_energy*1e21,
"zJ": unit_energy*1e21,
"kJ": unit_energy * 1e-3,
"kJ/mol": unit_energy * 1e-3 * Avogadro,
"cal": unit_energy / calorie,
"kcal": unit_energy * 1e-3 / calorie,
"kcal/mol": unit_energy * 1e-3 / calorie * Avogadro,
"K": unit_energy / Boltzmann, # K
"Kelvin": unit_energy / Boltzmann,
"electron_volt": unit_energy / electron_volt,
"eV": unit_energy / electron_volt,
"keV": unit_energy / electron_volt * 1e-3,
"MeV": unit_energy / electron_volt * 1e-6,
"GeV": unit_energy / electron_volt * 1e-9,
"erg": unit_energy * 1e7,
"hartree": unit_energy / hartree,
# Mass
"unit_mass": unit_mass, # kg
"solar_masses": unit_mass/solar_masses,
"M☉": unit_mass/solar_masses,
"kg": unit_mass,
"gram": unit_mass*1e3,
"g": unit_mass*1e3,
"mg": unit_mass*1e6,
"mikrogram": unit_mass*1e9,
"μg": unit_mass*1e9,
"nanogram": unit_mass*1e12,
"ng": unit_mass*1e12,
"picogram": unit_mass*1e15,
"pg": unit_mass*1e15,
"attogram": unit_mass*1e21,
"ag": unit_mass*1e21,
"yoctogram": unit_mass*1e27,
"yg": unit_mass*1e27,
"u": unit_mass/atomic_mass,
"dalton": unit_mass/atomic_mass,
"kg/mol": unit_mass * Avogadro,
"g/mol": unit_mass * 1e3 * Avogadro,
# Time
'unit_time': unit_time, # s
'years': unit_time / 31556926,
'weeks': unit_time / 604800,
'days': unit_time / 86400,
'hours': unit_time / 3600,
'minutes': unit_time / 60,
's': unit_time,
'seconds': unit_time,
'milliseconds': unit_time * 1e3,
'ms': unit_time * 1e3,
'microseconds': unit_time * 1e6,
'μs': unit_time * 1e6,
'nanoseconds': unit_time * 1e9,
'ns': unit_time * 1e9,
'picoseconds': unit_time * 1e12,
'ps': unit_time * 1e12,
'femtoseconds': unit_time * 1e15,
'fs': unit_time * 1e15,
# Force
'unit_force': unit_force, # N
'Newton': unit_force,
'N': unit_force,
'millinewton': unit_force*1e3,
'mN': unit_force*1e3,
'nanonewton': unit_force*1e9,
'nN': unit_force*1e9,
'piconewton': unit_force*1e12,
'pN': unit_force*1e12,
'femtonewton': unit_force*1e15,
'fN': unit_force*1e15,
'Dyne': unit_force * 1e5, # CGS system
'dyn': unit_force * 1e5,
'(kcal/mol)/Angstrom': unit_force * 1e-3 / calorie * Avogadro / 1e10, # from LAMMPS real units
'(kJ/mol)/Angstrom': unit_force * 1e-3 / Avogadro / 1e10,
'eV/Angstrom': unit_force / electron_volt / 1e10, # from LAMMPS metallic units
# Pressure
'unit_pressure': unit_pressure,
'Pascal': unit_pressure,
'pa': unit_pressure,
'hectopascal': unit_pressure * 1e-2,
'kilopascal': unit_pressure * 1e-3,
'kpa': unit_pressure * 1e-3,
'megapascal': unit_pressure * 1e-6,
'MPa': unit_pressure * 1e-6,
'gigapascal': unit_pressure * 1e-9,
'GPa': unit_pressure * 1e-9,
'bar': unit_pressure * 1e-5,
'mbar': unit_pressure * 1e-2,
'psi': unit_pressure / 6894.757293168361,
'torr': unit_pressure / 133.322368,
'atm': unit_pressure / 101325,
'atmospheres': unit_pressure / 101325,
'Barye': unit_pressure * 10,
'Ba': unit_pressure * 10,
# Density (3D)
'unit_density': unit_density, # kg/m³
'kg/m3': unit_density,
'g/ml': unit_density * 1e-3,
'g/cm3': unit_density * 1e-3,
'grams_per_cubic_centimeter': unit_density * 1e-3,
'u/nm3': unit_density / atomic_mass * 1e-27,
'u/Å3': unit_density / atomic_mass * 1e-30,
# Area
'unit_area': unit_area, # m²
'square_meter': unit_area,
'm2': unit_area,
'square_centimeter': unit_area*1e4,
'cm2': unit_area*1e4,
'square_millimeter': unit_area*1e6,
'mm2': unit_area*1e6,
'square_nanometer': unit_area*1e18,
'nm2': unit_area*1e18,
'square_Ångström': unit_area*1e20,
'square_Angstrom': unit_area*1e20,
'Å2': unit_area*1e20,
# Volume
'unit_volume': unit_volume, # m³
'cubic_meter': unit_volume,
'm3': unit_volume,
'cubic_centimeter': unit_volume*1e6,
'cc': unit_volume*1e6,
'cm3': unit_volume*1e6,
'cubic_millimeter': unit_volume*1e9,
'mm3': unit_volume*1e9,
'cubic_nanometer': unit_volume*1e27,
'nm3': unit_volume*1e27,
'cubic_Angstrom': unit_volume*1e30,
'cubic_Ångström': unit_volume*1e30,
'Å3': unit_volume*1e30,
}