/* ----------------------------------------------------------------------
   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
   https://www.lammps.org/, Sandia National Laboratories
   LAMMPS development team: developers@lammps.org

   Copyright (2003) Sandia Corporation.  Under the terms of Contract
   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
   certain rights in this software.  This software is distributed under
   the GNU General Public License.

   See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
   Contributing authors: Ellad B. Tadmor (UMN)
------------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 2 of the License, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
   more details.

   You should have received a copy of the GNU General Public License along with
   this program; if not, see <https://www.gnu.org/licenses>.

   Linking LAMMPS statically or dynamically with other modules is making a
   combined work based on LAMMPS. Thus, the terms and conditions of the GNU
   General Public License cover the whole combination.

   In addition, as a special exception, the copyright holders of LAMMPS give
   you permission to combine LAMMPS with free software programs or libraries
   that are released under the GNU LGPL and with code included in the standard
   release of the "kim-api" under the CDDL (or modified versions of such code,
   with unchanged license). You may copy and distribute such a system following
   the terms of the GNU GPL for LAMMPS and the licenses of the other code
   concerned, provided that you include the source code of that other code
   when and as the GNU GPL requires distribution of source code.

   Note that people who make modified versions of LAMMPS are not obligated to
   grant this special exception for their modified versions; it is their choice
   whether to do so. The GNU General Public License gives permission to release
   a modified version without this exception; this exception also makes it
   possible to release a modified version which carries forward this exception.
------------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
   Designed for use with the kim-api-2.0.2 (and newer) package
------------------------------------------------------------------------- */

#include "kim_units.h"

#include "error.h"

#include <cmath>
#include <map>
#include <string>
#include <utility>

using namespace std;
using namespace LAMMPS_NS;
using namespace kim_units;

// Constants of nature and basic conversion factors
// Source: https://physics.nist.gov/cuu/Constants/Table/allascii.txt
//         Working with NIST values even when there are newer values for
//         compatibility with LAMMPS

// clang-format off

/*----------------------
   Fundamental constants
------------------------ */
static double constexpr boltz_si = 1.38064852e-23;   // [J K^-1] Boltzmann's factor
                                                     // (NIST value)
static double constexpr Nav = 6.022140857e23;        // [unitless] Avogadro's number (NIST value)
// static double constexp Nav = 6.02214076e23;       // [unitless] Avogadro's number
                                                     // (official value May 2019)
static double constexpr me_si = 9.10938356e-31;      // [kg] electron rest mass (NIST value)
// static double constexpr me_si = 9.10938291e-31;   // [kg] electron rest mass
static double constexpr e_si = 1.6021766208e-19;     // [C] elementary charge
                                                     // (charge of an electron/proton)
                                                     // (NIST value)

/*----------------------
   Distance units
------------------------ */
static double constexpr bohr_si = 5.2917721067e-11;  // [m] Bohr unit (distance between
                                                     //   nucleus and electron in H) (NIST value)
static double constexpr angstrom_si = 1e-10;         // [m] Angstrom
static double constexpr centimeter_si = 1e-2;        // [m] centimeter
static double constexpr micrometer_si = 1e-6;        // [m] micrometer (micron)
static double constexpr nanometer_si = 1e-9;         // [m] nanometer

/*----------------------
   Mass units
------------------------ */
static double constexpr gram_per_mole_si = 1e-3/Nav;  // [kg] gram per mole
static double constexpr amu_si = 1e-3/Nav;            // [kg] atomic mass unit (molecular
                                                      //   weight) For example, the mean
                                                      //   molecular weight of water
                                                      //   is 18.015 atomic mass units
                                                      //   (amu), so one mole of water
                                                      //   weight 18.015 grams.
static double constexpr gram_si = 1e-3;               // [kg] gram
static double constexpr picogram_si = 1e-15;          // [kg] picogram
static double constexpr attogram_si = 1e-21;          // [kg[ attogram

/*----------------------
   Time units
------------------------ */
static double constexpr atu_si = 2.418884326509e-17;  // [s] atomic time unit
                                                      //   ( = hbar/E_h where E_h is the
                                                      //   Hartree energy) (NIST value)
static double constexpr atu_electron_si = 5153034.567408186;  // must not use sqrt() in constexpr
// static double constexpr atu_electron_si = atu_si*sqrt(amu_si/me_si);
                                                      // [s] atomic time unit
                                                      //   used in electron system (see https://sourceforge.net/p/lammps/mailman/lammps-users/thread/BCA2BDB2-BA03-4280-896F-1E6120EF47B2%40caltech.edu/)
static double constexpr microsecond_si = 1e-6;        // [s] microsecond
static double constexpr nanosecond_si = 1e-9;         // [s] nanosecond
static double constexpr picosecond_si = 1e-12;        // [s] picosecond
static double constexpr femtosecond_si = 1e-15;       // [s] femtosecond

/*----------------------
   Density units
------------------------ */
static double constexpr gram_per_centimetercu_si =
             gram_si/centimeter_si/centimeter_si/centimeter_si;  // [kg/m^3] gram/centimeter^3
static double constexpr amu_per_bohrcu_si = amu_si/bohr_si/bohr_si/bohr_si;  // [kg/m^3] amu/bohr^3
static double constexpr picogram_per_micrometercu_si =
             picogram_si/micrometer_si/micrometer_si/micrometer_si; // [kg/m^3] picogram/micrometer^3
static double constexpr attogram_per_nanometercu_si =
             attogram_si/nanometer_si/nanometer_si/nanometer_si;  // [kg/m^3] attogram/nanometer^3

/*----------------------
   Energy/torque units
------------------------ */
static double constexpr kcal_si = 4184.0;              // [J] kilocalorie (heat energy
                                                       //   involved in warming up one
                                                       //   kilogram of water by one
                                                       //   degree Kelvin)
static double constexpr ev_si = 1.6021766208e-19;      // [J] electron volt (amount of
                                                       //   energy gained or lost by the
                                                       //   charge of a single electron
                                                       //   moving across an electric
                                                       //   potential difference of one
                                                       //   volt.) (NIST value)
static double constexpr hartree_si = 4.359744650e-18;  // [J] Hartree (approximately the
                                                       //   electric potential energy of
                                                       //   the hydrogen atom in its
                                                       //   ground state) (NIST value)
static double constexpr kcal_per_mole_si = kcal_si/Nav;// [J] kcal/mole
static double constexpr erg_si = 1e-7;                 // [J] erg
static double constexpr dyne_centimeter_si = 1e-7;     // [J[ dyne*centimeter
static double constexpr picogram_micrometersq_per_microsecondsq_si =
             picogram_si*micrometer_si/microsecond_si*micrometer_si/microsecond_si;
                                                       // [J] picogram*micrometer^2/
                                                       //   microsecond^2
static double constexpr attogram_nanometersq_per_nanosecondsq_si =
             attogram_si*nanometer_si/nanosecond_si*nanometer_si/nanosecond_si;
                                                       // [J] attogram*nanometer^2/
                                                       //   nanosecond^2

/*----------------------
   Velocity units
------------------------ */
static double constexpr angstrom_per_femtosecond_si =
             angstrom_si/femtosecond_si;      // [m/s] Angstrom/femtosecond
static double constexpr angstrom_per_picosecond_si =
             angstrom_si/picosecond_si;       // [m/s] Angstrom/picosecond
static double constexpr micrometer_per_microsecond_si =
             micrometer_si/microsecond_si;    // [m/s] micrometer/microsecond
static double constexpr nanometer_per_nanosecond_si =
             nanometer_si/nanosecond_si;      // [m/s] nanometer/nanosecond
static double constexpr centimeter_per_second_si =
             centimeter_si;                  // [m/s] centimeter/second
static double constexpr bohr_per_atu_electron_si =
             bohr_si/atu_electron_si;        // [m/s] bohr/atu

/*----------------------
   Force units
------------------------ */
static double constexpr kcal_per_mole_angstrom_si =
             kcal_per_mole_si/angstrom_si;       // [N] kcal/(mole*Angstrom)
static double constexpr ev_per_angstrom_si =
             ev_si/angstrom_si;                  // [N] eV/Angstrom
static double constexpr dyne_si =
             dyne_centimeter_si/centimeter_si;   // [N] dyne
static double constexpr hartree_per_bohr_si =
             hartree_si/bohr_si;                 // [N] hartree/bohr
static double constexpr picogram_micrometer_per_microsecondsq_si =
             picogram_si*micrometer_si/microsecond_si/microsecond_si;
                                                 // [N] picogram*micrometer/
                                                 //   microsecond^2
static double constexpr attogram_nanometer_per_nanosecondsq_si =
             attogram_si*nanometer_si/nanosecond_si/nanosecond_si;
                                                 // [N] attogram*nanometer/
                                                 //   nanosecond^2

/*----------------------
   Pressure units
------------------------ */
static double constexpr atmosphere_si = 101325.0; // [Pa] standard atmosphere (NIST value)
static double constexpr bar_si = 1e5;             // [Pa] bar
static double constexpr dyne_per_centimetersq_si =
             dyne_centimeter_si/centimeter_si/centimeter_si/centimeter_si;
                                       // [Pa] dyne/centimeter^2
static double constexpr picogram_per_micrometer_microsecondsq_si =
             picogram_si/(micrometer_si*microsecond_si*microsecond_si);
                                       // [Pa] picogram/(micrometer*
                                       //   microsecond^2)
static double constexpr attogram_per_nanometer_nanosecondsq_si =
             attogram_si/(nanometer_si*nanosecond_si*nanosecond_si);
                                       // [Pa] attogram/(nanometer*nanosecond^2)

/*----------------------
   Viscosity units
------------------------ */
static double constexpr poise_si = 0.1;                  // [Pa*s] Poise
static double constexpr amu_per_bohr_femtosecond_si =
             amu_si/(bohr_si*femtosecond_si); // [Pa*s] amu/(bohr*femtosecond)
static double constexpr picogram_per_micrometer_microsecond_si =
             picogram_si/(micrometer_si*microsecond_si);
                                              // [Pa*s] picogram/(micrometer*
                                              //   microsecond)
static double constexpr attogram_per_nanometer_nanosecond_si =
             attogram_si/(nanometer_si*nanosecond_si);
                                              // [Pa*s] attogram/(nanometer*
                                              //   nanosecond)

/*----------------------
   Charge units
------------------------ */
static double constexpr echarge_si = e_si;                   // [C] electron charge unit
static double constexpr statcoulomb_si = e_si/4.8032044e-10; // [C] Statcoulomb or esu
                                                  //   (value from LAMMPS units
                                                  //   documentation)
static double constexpr picocoulomb_si = 1e-12;              // [C] picocoulomb

/*----------------------
   Dipole units
------------------------ */
static double constexpr electron_angstrom_si = echarge_si*angstrom_si;
                                                 // [C*m] electron*angstrom
static double constexpr statcoulomb_centimeter_si = statcoulomb_si*centimeter_si;
                                                 // [C*m] statcoulomb*centimeter
static double constexpr debye_si = 1e-18*statcoulomb_centimeter_si;
                                                 // [C*m] Debye
static double constexpr picocoulomb_micrometer_si = picocoulomb_si*micrometer_si;
                                                 // [C*m] picocoulomb*micrometer
static double constexpr electron_nanometer_si = echarge_si*nanometer_si;
                                                 // [C*m] electron*nanometer

/*----------------------
   Electric field units
------------------------ */
static double constexpr volt_per_angstrom_si = 1.0/angstrom_si;// [V/m] volt/angstrom
static double constexpr statvolt_per_centimeter_si =
             erg_si/(statcoulomb_si*centimeter_si); // [V/m] statvolt/centimeter
static double constexpr volt_per_centimeter_si =
             1.0/centimeter_si;                     // [V/m] volt/centimeter
static double constexpr volt_per_micrometer_si =
             1.0/micrometer_si;                     // [V/m] volt/micrometer
static double constexpr volt_per_nanometer_si =
             1.0/nanometer_si;                      // [V/m] volt/nanometer

namespace LAMMPS_NS {
namespace kim_units {
// Define enumerations
enum sys_type
{
   real      = 1,
   metal     = 2,
   si        = 3,
   cgs       = 4,
   electron  = 5,
   micro     = 6,
   nano      = 7
};

enum unit_type
{
    mass         = 1,
    distance     = 2,
    time         = 3,
    energy       = 4,
    velocity     = 5,
    force        = 6,
    torque       = 7,
    temperature  = 8,
    pressure     = 9,
    viscosity    = 10,
    charge       = 11,
    dipole       = 12,
    efield       = 13,
    density      = 14
};

enum units
{
    // mass
    gram_per_mole = 101,
    kilogram      = 102,
    gram          = 103,
    amu           = 104,
    picogram      = 105,
    attogram      = 106,
    // distance
    angstrom    = 201,
    meter       = 202,
    centimeter  = 203,
    bohr        = 204,
    micrometer  = 205,
    nanometer   = 206,
    // time
    femtosecond  = 301,
    picosecond   = 302,
    second       = 303,
    microsecond  = 304,
    nanosecond   = 305,
    // energy
    kcal_per_mole = 401,
    ev            = 402,
    joule         = 403,
    erg           = 404,
    hartree       = 405,
    picogram_micrometersq_per_microsecondsq = 406,
    attogram_nanometersq_per_nanosecondsq   = 407,
    // velocity
    angstrom_per_femtosecond   = 501,
    angstrom_per_picosecond    = 502,
    meter_per_second           = 503,
    centimeter_per_second      = 504,
    bohr_per_atu               = 505,
    micrometer_per_microsecond = 506,
    nanometer_per_nanosecond   = 507,
    // force
    kcal_per_mole_angstrom = 601,
    ev_per_angstrom        = 602,
    newton                 = 603,
    dyne                   = 604,
    hartree_per_bohr       = 605,
    picogram_micrometer_per_microsecondsq = 606,
    attogram_nanometer_per_nanosecondsq   = 607,
    // torque
    newton_meter    = 701,
    dyne_centimeter = 702,
    // temperature
    kelvin         = 801,
    // pressure
    atmosphere = 901,
    bar        = 902,
    pascal     = 903,
    dyne_per_centimetersq                 = 904,
    picogram_per_micrometer_microsecondsq = 905,
    attogram_per_nanometer_nanosecondsq   = 906,
    // viscosity
    poise                    = 1001,
    pascal_second            = 1002,
    amu_per_bohr_femtosecond = 1003, // electron system, not in docs, GUESSED
    picogram_per_micrometer_microsecond = 1004,
    attogram_per_nanometer_nanosecond   = 1005,
    // charge
    echarge     = 1101,
    coulomb     = 1102,
    statcoulomb = 1103,
    picocoulomb = 1104,
    // dipole
    electron_angstrom      = 1201,
    coulomb_meter          = 1202,
    statcoulomb_centimeter = 1203,
    debye                  = 1204,
    picocoulomb_micrometer = 1205,
    electron_nanometer     = 1206,
    // electric field
    volt_per_angstrom       = 1301,
    volt_per_meter          = 1302,
    statvolt_per_centimeter = 1303,
    volt_per_centimeter     = 1304,
    volt_per_micrometer     = 1305,
    volt_per_nanometer      = 1306,
    // density
    gram_per_centimetercu     = 1401,
    kilogram_per_metercu      = 1402,
    amu_per_bohrcu            = 1403, // electron system, not in docs, GUESSED
    picogram_per_micrometercu = 1404,
    attogram_per_nanometercu  = 1405
};

void initialize_dictionaries();
units get_lammps_system_unit(sys_type system_enum, unit_type unit_type_enum);
double get_mass_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_distance_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_time_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_energy_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_velocity_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_force_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_torque_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_temperature_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_pressure_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_viscosity_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_charge_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_dipole_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_efield_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_density_conversion_factor(units from_unit_enum, units to_unit_enum);
double get_unit_conversion_factor(unit_type &unit_type_enum, sys_type from_system_enum,
                                  sys_type to_system_enum);

}
}

// Define dictionaries
static map<string, sys_type> system_dic;
static map<string, unit_type> unit_dic;
static map<unit_type, units> units_real_dic;
static map<unit_type, units> units_metal_dic;
static map<unit_type, units> units_si_dic;
static map<unit_type, units> units_cgs_dic;
static map<unit_type, units> units_electron_dic;
static map<unit_type, units> units_micro_dic;
static map<unit_type, units> units_nano_dic;

/* ---------------------------------------------------------------------- */

void kim_units::initialize_dictionaries()
{
  system_dic["real"]     = real;
  system_dic["metal"]    = metal;
  system_dic["si"]       = si;
  system_dic["cgs"]      = cgs;
  system_dic["electron"] = electron;
  system_dic["micro"]    = micro;
  system_dic["nano"]     = nano;

  unit_dic["mass"]       = mass;
  unit_dic["distance"]   = distance;
  unit_dic["time"]       = time;
  unit_dic["energy"]     = energy;
  unit_dic["velocity"]   = velocity;
  unit_dic["force"]      = force;
  unit_dic["torque"]     = torque;
  unit_dic["temperature"]= temperature;
  unit_dic["pressure"]   = pressure;
  unit_dic["viscosity"]  = viscosity;
  unit_dic["charge"]     = charge;
  unit_dic["dipole"]     = dipole;
  unit_dic["efield"]     = efield;
  unit_dic["density"]    = density;

  units_real_dic[mass]       = gram_per_mole;
  units_real_dic[distance]   = angstrom;
  units_real_dic[time]       = femtosecond;
  units_real_dic[energy]     = kcal_per_mole;
  units_real_dic[velocity]   = angstrom_per_femtosecond;
  units_real_dic[force]      = kcal_per_mole_angstrom;
  units_real_dic[torque]     = kcal_per_mole;
  units_real_dic[temperature]= kelvin;
  units_real_dic[pressure]   = atmosphere;
  units_real_dic[viscosity]  = poise;
  units_real_dic[charge]     = echarge;
  units_real_dic[dipole]     = electron_angstrom;
  units_real_dic[efield]     = volt_per_angstrom;
  units_real_dic[density]    = gram_per_centimetercu;

  units_metal_dic[mass]       = gram_per_mole;
  units_metal_dic[distance]   = angstrom;
  units_metal_dic[time]       = picosecond;
  units_metal_dic[energy]     = ev;
  units_metal_dic[velocity]   = angstrom_per_picosecond;
  units_metal_dic[force]      = ev_per_angstrom;
  units_metal_dic[torque]     = ev;
  units_metal_dic[temperature]= kelvin;
  units_metal_dic[pressure]   = bar;
  units_metal_dic[viscosity]  = poise;
  units_metal_dic[charge]     = echarge;
  units_metal_dic[dipole]     = electron_angstrom;
  units_metal_dic[efield]     = volt_per_angstrom;
  units_metal_dic[density]    = gram_per_centimetercu;

  units_si_dic[mass]       = kilogram;
  units_si_dic[distance]   = meter;
  units_si_dic[time]       = second;
  units_si_dic[energy]     = joule;
  units_si_dic[velocity]   = meter_per_second;
  units_si_dic[force]      = newton;
  units_si_dic[torque]     = newton_meter;
  units_si_dic[temperature]= kelvin;
  units_si_dic[pressure]   = pascal;
  units_si_dic[viscosity]  = pascal_second;
  units_si_dic[charge]     = coulomb;
  units_si_dic[dipole]     = coulomb_meter;
  units_si_dic[efield]     = volt_per_meter;
  units_si_dic[density]    = kilogram_per_metercu;

  units_cgs_dic[mass]       = gram;
  units_cgs_dic[distance]   = centimeter;
  units_cgs_dic[time]       = second;
  units_cgs_dic[energy]     = erg;
  units_cgs_dic[velocity]   = centimeter_per_second;
  units_cgs_dic[force]      = dyne;
  units_cgs_dic[torque]     = dyne_centimeter;
  units_cgs_dic[temperature]= kelvin;
  units_cgs_dic[pressure]   = dyne_per_centimetersq;
  units_cgs_dic[viscosity]  = poise;
  units_cgs_dic[charge]     = statcoulomb;
  units_cgs_dic[dipole]     = statcoulomb_centimeter;
  units_cgs_dic[efield]     = statvolt_per_centimeter;
  units_cgs_dic[density]    = gram_per_centimetercu;

  units_electron_dic[mass]       = amu;
  units_electron_dic[distance]   = bohr;
  units_electron_dic[time]       = femtosecond;
  units_electron_dic[energy]     = hartree;
  units_electron_dic[velocity]   = bohr_per_atu;
  units_electron_dic[force]      = hartree_per_bohr;
  units_electron_dic[torque]     = hartree;              // unknown, GUESSED
  units_electron_dic[temperature]= kelvin;
  units_electron_dic[pressure]   = pascal;
  units_electron_dic[viscosity]  = pascal_second;        // unknown, GUESSED
  units_electron_dic[charge]     = echarge;
  units_electron_dic[dipole]     = debye;
  units_electron_dic[efield]     = volt_per_centimeter;
  units_electron_dic[density]    = amu_per_bohrcu;       // unknown, GUESSED

  units_micro_dic[mass]       = picogram;
  units_micro_dic[distance]   = micrometer;
  units_micro_dic[time]       = microsecond;
  units_micro_dic[energy]     = picogram_micrometersq_per_microsecondsq;
  units_micro_dic[velocity]   = micrometer_per_microsecond;
  units_micro_dic[force]      = picogram_micrometer_per_microsecondsq;
  units_micro_dic[torque]     = picogram_micrometersq_per_microsecondsq;
  units_micro_dic[temperature]= kelvin;
  units_micro_dic[pressure]   = picogram_per_micrometer_microsecondsq;
  units_micro_dic[viscosity]  = picogram_per_micrometer_microsecond;
  units_micro_dic[charge]     = picocoulomb;
  units_micro_dic[dipole]     = picocoulomb_micrometer;
  units_micro_dic[efield]     = volt_per_micrometer;
  units_micro_dic[density]    = picogram_per_micrometercu;

  units_nano_dic[mass]       = attogram;
  units_nano_dic[distance]   = nanometer;
  units_nano_dic[time]       = nanosecond;
  units_nano_dic[energy]     = attogram_nanometersq_per_nanosecondsq;
  units_nano_dic[velocity]   = nanometer_per_nanosecond;
  units_nano_dic[force]      = attogram_nanometer_per_nanosecondsq;
  units_nano_dic[torque]     = attogram_nanometersq_per_nanosecondsq;
  units_nano_dic[temperature]= kelvin;
  units_nano_dic[pressure]   = attogram_per_nanometer_nanosecondsq;
  units_nano_dic[viscosity]  = attogram_per_nanometer_nanosecond;
  units_nano_dic[charge]     = echarge;
  units_nano_dic[dipole]     = electron_nanometer;
  units_nano_dic[efield]     = volt_per_nanometer;
  units_nano_dic[density]    = attogram_per_nanometercu;

}

/* ---------------------------------------------------------------------- */

// Get the enumeration for the unit of type `unit_type_enum`
// for LAMMPS system `system_enum`.
units kim_units::get_lammps_system_unit(sys_type system_enum, unit_type unit_type_enum)
{
  switch(system_enum) {
    case real :
      return units_real_dic[unit_type_enum];
    case metal :
      return units_metal_dic[unit_type_enum];
    case si :
      return units_si_dic[unit_type_enum];
    case cgs :
      return units_cgs_dic[unit_type_enum];
    case electron :
      return units_electron_dic[unit_type_enum];
    case micro :
      return units_micro_dic[unit_type_enum];
    case nano :
    default :    // This is here to a prevent a compiler warning
      return units_nano_dic[unit_type_enum];
  }
}

/* ---------------------------------------------------------------------- */

// Mass conversion
double kim_units::get_mass_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[kilogram][kilogram]           = 1.0;
  conv[kilogram][gram_per_mole]      = 1.0/gram_per_mole_si;
  conv[kilogram][gram]               = 1.0/gram_si;
  conv[kilogram][amu]                = 1.0/amu_si;
  conv[kilogram][picogram]           = 1.0/picogram_si;
  conv[kilogram][attogram]           = 1.0/attogram_si;

  to_si = 1.0/conv[kilogram][gram_per_mole];
  conv[gram_per_mole][kilogram]      = to_si*conv[kilogram][kilogram];
  conv[gram_per_mole][gram_per_mole] = 1.0;
  conv[gram_per_mole][gram]          = to_si*conv[kilogram][gram];
  conv[gram_per_mole][amu]           = to_si*conv[kilogram][amu];
  conv[gram_per_mole][picogram]      = to_si*conv[kilogram][picogram];
  conv[gram_per_mole][attogram]      = to_si*conv[kilogram][attogram];

  to_si = 1.0/conv[kilogram][gram];
  conv[gram][kilogram]               = to_si*conv[kilogram][kilogram];
  conv[gram][gram_per_mole]          = to_si*conv[kilogram][gram_per_mole];
  conv[gram][gram]                   = 1.0;
  conv[gram][amu]                    = to_si*conv[kilogram][amu];
  conv[gram][picogram]               = to_si*conv[kilogram][picogram];
  conv[gram][attogram]               = to_si*conv[kilogram][attogram];

  to_si = 1.0/conv[kilogram][amu];
  conv[amu][kilogram]                = to_si*conv[kilogram][kilogram];
  conv[amu][gram_per_mole]           = to_si*conv[kilogram][gram_per_mole];
  conv[amu][gram]                    = to_si*conv[kilogram][gram];
  conv[amu][amu]                     = 1.0;
  conv[amu][picogram]                = to_si*conv[kilogram][picogram];
  conv[amu][attogram]                = to_si*conv[kilogram][attogram];

  to_si = 1.0/conv[kilogram][picogram];
  conv[picogram][kilogram]           = to_si*conv[kilogram][kilogram];
  conv[picogram][gram_per_mole]      = to_si*conv[kilogram][gram_per_mole];
  conv[picogram][gram]               = to_si*conv[kilogram][gram];
  conv[picogram][amu]                = to_si*conv[kilogram][amu];
  conv[picogram][picogram]           = 1.0;
  conv[picogram][attogram]           = to_si*conv[kilogram][attogram];

  to_si = 1.0/conv[kilogram][attogram];
  conv[attogram][kilogram]           = to_si*conv[kilogram][kilogram];
  conv[attogram][gram_per_mole]      = to_si*conv[kilogram][gram_per_mole];
  conv[attogram][gram]               = to_si*conv[kilogram][gram];
  conv[attogram][amu]                = to_si*conv[kilogram][amu];
  conv[attogram][picogram]           = to_si*conv[kilogram][picogram];
  conv[attogram][attogram]           = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Distance conversion
double kim_units::get_distance_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[meter][meter]           = 1.0;
  conv[meter][angstrom]        = 1.0/angstrom_si;
  conv[meter][centimeter]      = 1.0/centimeter_si;
  conv[meter][bohr]            = 1.0/bohr_si;
  conv[meter][micrometer]      = 1.0/micrometer_si;
  conv[meter][nanometer]       = 1.0/nanometer_si;

  to_si = 1.0/conv[meter][angstrom];
  conv[angstrom][meter]        = to_si*conv[meter][meter];
  conv[angstrom][angstrom]     = 1.0;
  conv[angstrom][centimeter]   = to_si*conv[meter][centimeter];
  conv[angstrom][bohr]         = to_si*conv[meter][bohr];
  conv[angstrom][micrometer]   = to_si*conv[meter][micrometer];
  conv[angstrom][nanometer]    = to_si*conv[meter][nanometer];

  to_si = 1.0/conv[meter][centimeter];
  conv[centimeter][meter]      = to_si*conv[meter][meter];
  conv[centimeter][angstrom]   = to_si*conv[meter][angstrom];
  conv[centimeter][centimeter] = 1.0;
  conv[centimeter][bohr]       = to_si*conv[meter][bohr];
  conv[centimeter][micrometer] = to_si*conv[meter][micrometer];
  conv[centimeter][nanometer]  = to_si*conv[meter][nanometer];

  to_si = 1.0/conv[meter][bohr];
  conv[bohr][meter]            = to_si*conv[meter][meter];
  conv[bohr][angstrom]         = to_si*conv[meter][angstrom];
  conv[bohr][centimeter]       = to_si*conv[meter][centimeter];
  conv[bohr][bohr]             = 1.0;
  conv[bohr][micrometer]       = to_si*conv[meter][micrometer];
  conv[bohr][nanometer]        = to_si*conv[meter][nanometer];

  to_si = 1.0/conv[meter][micrometer];
  conv[micrometer][meter]      = to_si*conv[meter][meter];
  conv[micrometer][angstrom]   = to_si*conv[meter][angstrom];
  conv[micrometer][centimeter] = to_si*conv[meter][centimeter];
  conv[micrometer][bohr]       = to_si*conv[meter][bohr];
  conv[micrometer][micrometer] = 1.0;
  conv[micrometer][nanometer]  = to_si*conv[meter][nanometer];

  to_si = 1.0/conv[meter][nanometer];
  conv[nanometer][meter]       = to_si*conv[meter][meter];
  conv[nanometer][angstrom]    = to_si*conv[meter][angstrom];
  conv[nanometer][centimeter]  = to_si*conv[meter][centimeter];
  conv[nanometer][bohr]        = to_si*conv[meter][bohr];
  conv[nanometer][micrometer]  = to_si*conv[meter][micrometer];
  conv[nanometer][nanometer]   = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Time conversion
double kim_units::get_time_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[second][second]           = 1.0;
  conv[second][femtosecond]      = 1.0/femtosecond_si;
  conv[second][picosecond]       = 1.0/picosecond_si;
  conv[second][microsecond]      = 1.0/microsecond_si;
  conv[second][nanosecond]       = 1.0/nanosecond_si;

  to_si = 1.0/conv[second][femtosecond];
  conv[femtosecond][second]      = to_si*conv[second][second];
  conv[femtosecond][femtosecond] = 1.0;
  conv[femtosecond][picosecond]  = to_si*conv[second][picosecond];
  conv[femtosecond][microsecond] = to_si*conv[second][microsecond];
  conv[femtosecond][nanosecond]  = to_si*conv[second][nanosecond];

  to_si = 1.0/conv[second][picosecond];
  conv[picosecond][second]       = to_si*conv[second][second];
  conv[picosecond][femtosecond]  = to_si*conv[second][femtosecond];
  conv[picosecond][picosecond]   = 1.0;
  conv[picosecond][microsecond]  = to_si*conv[second][microsecond];
  conv[picosecond][nanosecond]   = to_si*conv[second][nanosecond];

  to_si = 1.0/conv[second][microsecond];
  conv[microsecond][second]      = to_si*conv[second][second];
  conv[microsecond][femtosecond] = to_si*conv[second][femtosecond];
  conv[microsecond][picosecond]  = to_si*conv[second][picosecond];
  conv[microsecond][microsecond] = 1.0;
  conv[microsecond][nanosecond]  = to_si*conv[second][nanosecond];

  to_si = 1.0/conv[second][nanosecond];
  conv[nanosecond][second]       = to_si*conv[second][second];
  conv[nanosecond][femtosecond]  = to_si*conv[second][femtosecond];
  conv[nanosecond][picosecond]   = to_si*conv[second][picosecond];
  conv[nanosecond][microsecond]  = to_si*conv[second][microsecond];
  conv[nanosecond][nanosecond]   = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Energy conversion
double kim_units::get_energy_conversion_factor(units from_unit_enum, units to_unit_enum)
{

  map<units, map<units, double> > conv;
  double to_si;

  conv[joule][joule]                                                                     = 1.0;
  conv[joule][kcal_per_mole]                                                             = 1.0/kcal_per_mole_si;
  conv[joule][ev]                                                                        = 1.0/ev_si;
  conv[joule][erg]                                                                       = 1.0/erg_si;
  conv[joule][hartree]                                                                   = 1.0/hartree_si;
  conv[joule][picogram_micrometersq_per_microsecondsq]                                   = 1.0/picogram_micrometersq_per_microsecondsq_si;
  conv[joule][attogram_nanometersq_per_nanosecondsq]                                     = 1.0/attogram_nanometersq_per_nanosecondsq_si;

  to_si = 1.0/conv[joule][kcal_per_mole];
  conv[kcal_per_mole][joule]                                                             = to_si*conv[joule][joule];
  conv[kcal_per_mole][kcal_per_mole]                                                     = 1.0;
  conv[kcal_per_mole][ev]                                                                = to_si*conv[joule][ev];
  conv[kcal_per_mole][erg]                                                               = to_si*conv[joule][erg];
  conv[kcal_per_mole][hartree]                                                           = to_si*conv[joule][hartree];
  conv[kcal_per_mole][picogram_micrometersq_per_microsecondsq]                           = to_si*conv[joule][picogram_micrometersq_per_microsecondsq];
  conv[kcal_per_mole][attogram_nanometersq_per_nanosecondsq]                             = to_si*conv[joule][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[joule][ev];
  conv[ev][joule]                                                                        = to_si*conv[joule][joule];
  conv[ev][kcal_per_mole]                                                                = to_si*conv[joule][kcal_per_mole];
  conv[ev][ev]                                                                           = 1.0;
  conv[ev][erg]                                                                          = to_si*conv[joule][erg];
  conv[ev][hartree]                                                                      = to_si*conv[joule][hartree];
  conv[ev][picogram_micrometersq_per_microsecondsq]                                      = to_si*conv[joule][picogram_micrometersq_per_microsecondsq];
  conv[ev][attogram_nanometersq_per_nanosecondsq]                                        = to_si*conv[joule][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[joule][erg];
  conv[erg][joule]                                                                       = to_si*conv[joule][joule];
  conv[erg][kcal_per_mole]                                                               = to_si*conv[joule][kcal_per_mole];
  conv[erg][ev]                                                                          = to_si*conv[joule][ev];
  conv[erg][erg]                                                                         = 1.0;
  conv[erg][hartree]                                                                     = to_si*conv[joule][hartree];
  conv[erg][picogram_micrometersq_per_microsecondsq]                                     = to_si*conv[joule][picogram_micrometersq_per_microsecondsq];
  conv[erg][attogram_nanometersq_per_nanosecondsq]                                       = to_si*conv[joule][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[joule][hartree];
  conv[hartree][joule]                                                                   = to_si*conv[joule][joule];
  conv[hartree][kcal_per_mole]                                                           = to_si*conv[joule][kcal_per_mole];
  conv[hartree][ev]                                                                      = to_si*conv[joule][ev];
  conv[hartree][erg]                                                                     = to_si*conv[joule][erg];
  conv[hartree][hartree]                                                                 = 1.0;
  conv[hartree][picogram_micrometersq_per_microsecondsq]                                 = to_si*conv[joule][picogram_micrometersq_per_microsecondsq];
  conv[hartree][attogram_nanometersq_per_nanosecondsq]                                   = to_si*conv[joule][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[joule][picogram_micrometersq_per_microsecondsq];
  conv[picogram_micrometersq_per_microsecondsq][joule]                                   = to_si*conv[joule][joule];
  conv[picogram_micrometersq_per_microsecondsq][kcal_per_mole]                           = to_si*conv[joule][kcal_per_mole];
  conv[picogram_micrometersq_per_microsecondsq][ev]                                      = to_si*conv[joule][ev];
  conv[picogram_micrometersq_per_microsecondsq][erg]                                     = to_si*conv[joule][erg];
  conv[picogram_micrometersq_per_microsecondsq][hartree]                                 = to_si*conv[joule][hartree];
  conv[picogram_micrometersq_per_microsecondsq][picogram_micrometersq_per_microsecondsq] = 1.0;
  conv[picogram_micrometersq_per_microsecondsq][attogram_nanometersq_per_nanosecondsq]   = to_si*conv[joule][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[joule][attogram_nanometersq_per_nanosecondsq];
  conv[attogram_nanometersq_per_nanosecondsq][joule]                                     = to_si*conv[joule][joule];
  conv[attogram_nanometersq_per_nanosecondsq][kcal_per_mole]                             = to_si*conv[joule][kcal_per_mole];
  conv[attogram_nanometersq_per_nanosecondsq][ev]                                        = to_si*conv[joule][ev];
  conv[attogram_nanometersq_per_nanosecondsq][erg]                                       = to_si*conv[joule][erg];
  conv[attogram_nanometersq_per_nanosecondsq][hartree]                                   = to_si*conv[joule][hartree];
  conv[attogram_nanometersq_per_nanosecondsq][picogram_micrometersq_per_microsecondsq]   = to_si*conv[joule][picogram_micrometersq_per_microsecondsq];
  conv[attogram_nanometersq_per_nanosecondsq][attogram_nanometersq_per_nanosecondsq]     = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Velocity conversion
double kim_units::get_velocity_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[meter_per_second][meter_per_second]                     = 1.0;
  conv[meter_per_second][angstrom_per_femtosecond]             = 1.0/angstrom_per_femtosecond_si;
  conv[meter_per_second][angstrom_per_picosecond]              = 1.0/angstrom_per_picosecond_si;
  conv[meter_per_second][centimeter_per_second]                = 1.0/centimeter_per_second_si;
  conv[meter_per_second][bohr_per_atu]                         = 1.0/bohr_per_atu_electron_si;
  conv[meter_per_second][micrometer_per_microsecond]           = 1.0/micrometer_per_microsecond_si;
  conv[meter_per_second][nanometer_per_nanosecond]             = 1.0/nanometer_per_nanosecond_si;

  to_si = 1.0/conv[meter_per_second][angstrom_per_femtosecond];
  conv[angstrom_per_femtosecond][meter_per_second]             = to_si*conv[meter_per_second][meter_per_second];
  conv[angstrom_per_femtosecond][angstrom_per_femtosecond]     = 1.0;
  conv[angstrom_per_femtosecond][angstrom_per_picosecond]      = to_si*conv[meter_per_second][angstrom_per_picosecond];
  conv[angstrom_per_femtosecond][centimeter_per_second]        = to_si*conv[meter_per_second][centimeter_per_second];
  conv[angstrom_per_femtosecond][bohr_per_atu]                 = to_si*conv[meter_per_second][bohr_per_atu];
  conv[angstrom_per_femtosecond][micrometer_per_microsecond]   = to_si*conv[meter_per_second][micrometer_per_microsecond];
  conv[angstrom_per_femtosecond][nanometer_per_nanosecond]     = to_si*conv[meter_per_second][nanometer_per_nanosecond];

  to_si = 1.0/conv[meter_per_second][angstrom_per_picosecond];
  conv[angstrom_per_picosecond][meter_per_second]              = to_si*conv[meter_per_second][meter_per_second];
  conv[angstrom_per_picosecond][angstrom_per_femtosecond]      = to_si*conv[meter_per_second][angstrom_per_femtosecond];
  conv[angstrom_per_picosecond][angstrom_per_picosecond]       = 1.0;
  conv[angstrom_per_picosecond][centimeter_per_second]         = to_si*conv[meter_per_second][centimeter_per_second];
  conv[angstrom_per_picosecond][bohr_per_atu]                  = to_si*conv[meter_per_second][bohr_per_atu];
  conv[angstrom_per_picosecond][micrometer_per_microsecond]    = to_si*conv[meter_per_second][micrometer_per_microsecond];
  conv[angstrom_per_picosecond][nanometer_per_nanosecond]      = to_si*conv[meter_per_second][nanometer_per_nanosecond];

  to_si = 1.0/conv[meter_per_second][centimeter_per_second];
  conv[centimeter_per_second][meter_per_second]                = to_si*conv[meter_per_second][meter_per_second];
  conv[centimeter_per_second][angstrom_per_femtosecond]        = to_si*conv[meter_per_second][angstrom_per_femtosecond];
  conv[centimeter_per_second][angstrom_per_picosecond]         = to_si*conv[meter_per_second][angstrom_per_picosecond];
  conv[centimeter_per_second][centimeter_per_second]           = 1.0;
  conv[centimeter_per_second][bohr_per_atu]                    = to_si*conv[meter_per_second][bohr_per_atu];
  conv[centimeter_per_second][micrometer_per_microsecond]      = to_si*conv[meter_per_second][micrometer_per_microsecond];
  conv[centimeter_per_second][nanometer_per_nanosecond]        = to_si*conv[meter_per_second][nanometer_per_nanosecond];

  to_si = 1.0/conv[meter_per_second][bohr_per_atu];
  conv[bohr_per_atu][meter_per_second]                         = to_si*conv[meter_per_second][meter_per_second];
  conv[bohr_per_atu][angstrom_per_femtosecond]                 = to_si*conv[meter_per_second][angstrom_per_femtosecond];
  conv[bohr_per_atu][angstrom_per_picosecond]                  = to_si*conv[meter_per_second][angstrom_per_picosecond];
  conv[bohr_per_atu][centimeter_per_second]                    = to_si*conv[meter_per_second][centimeter_per_second];
  conv[bohr_per_atu][bohr_per_atu]                             = 1.0;
  conv[bohr_per_atu][micrometer_per_microsecond]               = to_si*conv[meter_per_second][micrometer_per_microsecond];
  conv[bohr_per_atu][nanometer_per_nanosecond]                 = to_si*conv[meter_per_second][nanometer_per_nanosecond];

  to_si = 1.0/conv[meter_per_second][micrometer_per_microsecond];
  conv[micrometer_per_microsecond][meter_per_second]           = to_si*conv[meter_per_second][meter_per_second];
  conv[micrometer_per_microsecond][angstrom_per_femtosecond]   = to_si*conv[meter_per_second][angstrom_per_femtosecond];
  conv[micrometer_per_microsecond][angstrom_per_picosecond]    = to_si*conv[meter_per_second][angstrom_per_picosecond];
  conv[micrometer_per_microsecond][centimeter_per_second]      = to_si*conv[meter_per_second][centimeter_per_second];
  conv[micrometer_per_microsecond][bohr_per_atu]               = to_si*conv[meter_per_second][bohr_per_atu];
  conv[micrometer_per_microsecond][micrometer_per_microsecond] = 1.0;
  conv[micrometer_per_microsecond][nanometer_per_nanosecond]   = to_si*conv[meter_per_second][nanometer_per_nanosecond];

  to_si = 1.0/conv[meter_per_second][nanometer_per_nanosecond];
  conv[nanometer_per_nanosecond][meter_per_second]             = to_si*conv[meter_per_second][meter_per_second];
  conv[nanometer_per_nanosecond][angstrom_per_femtosecond]     = to_si*conv[meter_per_second][angstrom_per_femtosecond];
  conv[nanometer_per_nanosecond][angstrom_per_picosecond]      = to_si*conv[meter_per_second][angstrom_per_picosecond];
  conv[nanometer_per_nanosecond][centimeter_per_second]        = to_si*conv[meter_per_second][centimeter_per_second];
  conv[nanometer_per_nanosecond][bohr_per_atu]                 = to_si*conv[meter_per_second][bohr_per_atu];
  conv[nanometer_per_nanosecond][micrometer_per_microsecond]   = to_si*conv[meter_per_second][micrometer_per_microsecond];
  conv[nanometer_per_nanosecond][nanometer_per_nanosecond]     = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Force conversion
double kim_units::get_force_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[newton][newton]                                                               = 1.0;
  conv[newton][kcal_per_mole_angstrom]                                               = 1.0/kcal_per_mole_angstrom_si;
  conv[newton][ev_per_angstrom]                                                      = 1.0/ev_per_angstrom_si;
  conv[newton][dyne]                                                                 = 1.0/dyne_si;
  conv[newton][hartree_per_bohr]                                                     = 1.0/hartree_per_bohr_si;
  conv[newton][picogram_micrometer_per_microsecondsq]                                = 1.0/picogram_micrometer_per_microsecondsq_si;
  conv[newton][attogram_nanometer_per_nanosecondsq]                                  = 1.0/attogram_nanometer_per_nanosecondsq_si;

  to_si = 1.0/conv[newton][kcal_per_mole_angstrom];
  conv[kcal_per_mole_angstrom][newton]                                               = to_si*conv[newton][newton];
  conv[kcal_per_mole_angstrom][kcal_per_mole_angstrom]                               = 1.0;
  conv[kcal_per_mole_angstrom][ev_per_angstrom]                                      = to_si*conv[newton][ev_per_angstrom];
  conv[kcal_per_mole_angstrom][dyne]                                                 = to_si*conv[newton][dyne];
  conv[kcal_per_mole_angstrom][hartree_per_bohr]                                     = to_si*conv[newton][hartree_per_bohr];
  conv[kcal_per_mole_angstrom][picogram_micrometer_per_microsecondsq]                = to_si*conv[newton][picogram_micrometer_per_microsecondsq];
  conv[kcal_per_mole_angstrom][attogram_nanometer_per_nanosecondsq]                  = to_si*conv[newton][attogram_nanometer_per_nanosecondsq];

  to_si = 1.0/conv[newton][ev_per_angstrom];
  conv[ev_per_angstrom][newton]                                                      = to_si*conv[newton][newton];
  conv[ev_per_angstrom][kcal_per_mole_angstrom]                                      = to_si*conv[newton][kcal_per_mole_angstrom];
  conv[ev_per_angstrom][ev_per_angstrom]                                             = 1.0;
  conv[ev_per_angstrom][dyne]                                                        = to_si*conv[newton][dyne];
  conv[ev_per_angstrom][hartree_per_bohr]                                            = to_si*conv[newton][hartree_per_bohr];
  conv[ev_per_angstrom][picogram_micrometer_per_microsecondsq]                       = to_si*conv[newton][picogram_micrometer_per_microsecondsq];
  conv[ev_per_angstrom][attogram_nanometer_per_nanosecondsq]                         = to_si*conv[newton][attogram_nanometer_per_nanosecondsq];

  to_si = 1.0/conv[newton][dyne];
  conv[dyne][newton]                                                                 = to_si*conv[newton][newton];
  conv[dyne][kcal_per_mole_angstrom]                                                 = to_si*conv[newton][kcal_per_mole_angstrom];
  conv[dyne][ev_per_angstrom]                                                        = to_si*conv[newton][ev_per_angstrom];
  conv[dyne][dyne]                                                                   = 1.0;
  conv[dyne][hartree_per_bohr]                                                       = to_si*conv[newton][hartree_per_bohr];
  conv[dyne][picogram_micrometer_per_microsecondsq]                                  = to_si*conv[newton][picogram_micrometer_per_microsecondsq];
  conv[dyne][attogram_nanometer_per_nanosecondsq]                                    = to_si*conv[newton][attogram_nanometer_per_nanosecondsq];

  to_si = 1.0/conv[newton][hartree_per_bohr];
  conv[hartree_per_bohr][newton]                                                     = to_si*conv[newton][newton];
  conv[hartree_per_bohr][kcal_per_mole_angstrom]                                     = to_si*conv[newton][kcal_per_mole_angstrom];
  conv[hartree_per_bohr][ev_per_angstrom]                                            = to_si*conv[newton][ev_per_angstrom];
  conv[hartree_per_bohr][dyne]                                                       = to_si*conv[newton][dyne];
  conv[hartree_per_bohr][hartree_per_bohr]                                           = 1.0;
  conv[hartree_per_bohr][picogram_micrometer_per_microsecondsq]                      = to_si*conv[newton][picogram_micrometer_per_microsecondsq];
  conv[hartree_per_bohr][attogram_nanometer_per_nanosecondsq]                        = to_si*conv[newton][attogram_nanometer_per_nanosecondsq];

  to_si = 1.0/conv[newton][picogram_micrometer_per_microsecondsq];
  conv[picogram_micrometer_per_microsecondsq][newton]                                = to_si*conv[newton][newton];
  conv[picogram_micrometer_per_microsecondsq][kcal_per_mole_angstrom]                = to_si*conv[newton][kcal_per_mole_angstrom];
  conv[picogram_micrometer_per_microsecondsq][ev_per_angstrom]                       = to_si*conv[newton][ev_per_angstrom];
  conv[picogram_micrometer_per_microsecondsq][dyne]                                  = to_si*conv[newton][dyne];
  conv[picogram_micrometer_per_microsecondsq][hartree_per_bohr]                      = to_si*conv[newton][hartree_per_bohr];
  conv[picogram_micrometer_per_microsecondsq][picogram_micrometer_per_microsecondsq] = 1.0;
  conv[picogram_micrometer_per_microsecondsq][attogram_nanometer_per_nanosecondsq]   = to_si*conv[newton][attogram_nanometer_per_nanosecondsq];

  to_si = 1.0/conv[newton][attogram_nanometer_per_nanosecondsq];
  conv[attogram_nanometer_per_nanosecondsq][newton]                                  = to_si*conv[newton][newton];
  conv[attogram_nanometer_per_nanosecondsq][kcal_per_mole_angstrom]                  = to_si*conv[newton][kcal_per_mole_angstrom];
  conv[attogram_nanometer_per_nanosecondsq][ev_per_angstrom]                         = to_si*conv[newton][ev_per_angstrom];
  conv[attogram_nanometer_per_nanosecondsq][dyne]                                    = to_si*conv[newton][dyne];
  conv[attogram_nanometer_per_nanosecondsq][hartree_per_bohr]                        = to_si*conv[newton][hartree_per_bohr];
  conv[attogram_nanometer_per_nanosecondsq][picogram_micrometer_per_microsecondsq]   = to_si*conv[newton][picogram_micrometer_per_microsecondsq];
  conv[attogram_nanometer_per_nanosecondsq][attogram_nanometer_per_nanosecondsq]     = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Torque conversion
double kim_units::get_torque_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[newton_meter][newton_meter]                                                       = 1.0;
  conv[newton_meter][kcal_per_mole]                                                      = 1.0/kcal_per_mole_si;
  conv[newton_meter][ev]                                                                 = 1.0/ev_si;
  conv[newton_meter][dyne_centimeter]                                                    = 1.0/dyne_centimeter_si;
  conv[newton_meter][hartree]                                                            = 1.0/hartree_si;
  conv[newton_meter][picogram_micrometersq_per_microsecondsq]                            = 1.0/picogram_micrometersq_per_microsecondsq_si;
  conv[newton_meter][attogram_nanometersq_per_nanosecondsq]                              = 1.0/attogram_nanometersq_per_nanosecondsq_si;

  to_si = 1.0/conv[newton_meter][kcal_per_mole];
  conv[kcal_per_mole][newton_meter]                                                      = to_si*conv[newton_meter][newton_meter];
  conv[kcal_per_mole][kcal_per_mole]                                                     = 1.0;
  conv[kcal_per_mole][ev]                                                                = to_si*conv[newton_meter][ev];
  conv[kcal_per_mole][dyne_centimeter]                                                   = to_si*conv[newton_meter][dyne_centimeter];
  conv[kcal_per_mole][hartree]                                                           = to_si*conv[newton_meter][hartree];
  conv[kcal_per_mole][picogram_micrometersq_per_microsecondsq]                           = to_si*conv[newton_meter][picogram_micrometersq_per_microsecondsq];
  conv[kcal_per_mole][attogram_nanometersq_per_nanosecondsq]                             = to_si*conv[newton_meter][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[newton_meter][ev];
  conv[ev][newton_meter]                                                                 = to_si*conv[newton_meter][newton_meter];
  conv[ev][kcal_per_mole]                                                                = to_si*conv[newton_meter][kcal_per_mole];
  conv[ev][ev]                                                                           = 1.0;
  conv[ev][dyne_centimeter]                                                              = to_si*conv[newton_meter][dyne_centimeter];
  conv[ev][hartree]                                                                      = to_si*conv[newton_meter][hartree];
  conv[ev][picogram_micrometersq_per_microsecondsq]                                      = to_si*conv[newton_meter][picogram_micrometersq_per_microsecondsq];
  conv[ev][attogram_nanometersq_per_nanosecondsq]                                        = to_si*conv[newton_meter][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[newton_meter][dyne_centimeter];
  conv[dyne_centimeter][newton_meter]                                                    = to_si*conv[newton_meter][newton_meter];
  conv[dyne_centimeter][kcal_per_mole]                                                   = to_si*conv[newton_meter][kcal_per_mole];
  conv[dyne_centimeter][ev]                                                              = to_si*conv[newton_meter][ev];
  conv[dyne_centimeter][dyne_centimeter]                                                 = 1.0;
  conv[dyne_centimeter][hartree]                                                         = to_si*conv[newton_meter][hartree];
  conv[dyne_centimeter][picogram_micrometersq_per_microsecondsq]                         = to_si*conv[newton_meter][picogram_micrometersq_per_microsecondsq];
  conv[dyne_centimeter][attogram_nanometersq_per_nanosecondsq]                           = to_si*conv[newton_meter][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[newton_meter][hartree];
  conv[hartree][newton_meter]                                                            = to_si*conv[newton_meter][newton_meter];
  conv[hartree][kcal_per_mole]                                                           = to_si*conv[newton_meter][kcal_per_mole];
  conv[hartree][ev]                                                                      = to_si*conv[newton_meter][ev];
  conv[hartree][dyne_centimeter]                                                         = to_si*conv[newton_meter][dyne_centimeter];
  conv[hartree][hartree]                                                                 = 1.0;
  conv[hartree][picogram_micrometersq_per_microsecondsq]                                 = to_si*conv[newton_meter][picogram_micrometersq_per_microsecondsq];
  conv[hartree][attogram_nanometersq_per_nanosecondsq]                                   = to_si*conv[newton_meter][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[newton_meter][picogram_micrometersq_per_microsecondsq];
  conv[picogram_micrometersq_per_microsecondsq][newton_meter]                            = to_si*conv[newton_meter][newton_meter];
  conv[picogram_micrometersq_per_microsecondsq][kcal_per_mole]                           = to_si*conv[newton_meter][kcal_per_mole];
  conv[picogram_micrometersq_per_microsecondsq][ev]                                      = to_si*conv[newton_meter][ev];
  conv[picogram_micrometersq_per_microsecondsq][dyne_centimeter]                         = to_si*conv[newton_meter][dyne_centimeter];
  conv[picogram_micrometersq_per_microsecondsq][hartree]                                 = to_si*conv[newton_meter][hartree];
  conv[picogram_micrometersq_per_microsecondsq][picogram_micrometersq_per_microsecondsq] = 1.0;
  conv[picogram_micrometersq_per_microsecondsq][attogram_nanometersq_per_nanosecondsq]   = to_si*conv[newton_meter][attogram_nanometersq_per_nanosecondsq];

  to_si = 1.0/conv[newton_meter][attogram_nanometersq_per_nanosecondsq];
  conv[attogram_nanometersq_per_nanosecondsq][newton_meter]                              = to_si*conv[newton_meter][newton_meter];
  conv[attogram_nanometersq_per_nanosecondsq][kcal_per_mole]                             = to_si*conv[newton_meter][kcal_per_mole];
  conv[attogram_nanometersq_per_nanosecondsq][ev]                                        = to_si*conv[newton_meter][ev];
  conv[attogram_nanometersq_per_nanosecondsq][dyne_centimeter]                           = to_si*conv[newton_meter][dyne_centimeter];
  conv[attogram_nanometersq_per_nanosecondsq][hartree]                                   = to_si*conv[newton_meter][hartree];
  conv[attogram_nanometersq_per_nanosecondsq][picogram_micrometersq_per_microsecondsq]   = to_si*conv[newton_meter][picogram_micrometersq_per_microsecondsq];
  conv[attogram_nanometersq_per_nanosecondsq][attogram_nanometersq_per_nanosecondsq]     = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Temperature conversion
double kim_units::get_temperature_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;

  conv[kelvin][kelvin] = 1.0;
  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Pressure conversion
double kim_units::get_pressure_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[pascal][pascal]                                                               = 1.0;
  conv[pascal][atmosphere]                                                           = 1.0/atmosphere_si;
  conv[pascal][bar]                                                                  = 1.0/bar_si;
  conv[pascal][dyne_per_centimetersq]                                                = 1.0/dyne_per_centimetersq_si;
  conv[pascal][picogram_per_micrometer_microsecondsq]                                = 1.0/picogram_per_micrometer_microsecondsq_si;
  conv[pascal][attogram_per_nanometer_nanosecondsq]                                  = 1.0/attogram_per_nanometer_nanosecondsq_si;

  to_si = 1.0/conv[pascal][atmosphere];
  conv[atmosphere][pascal]                                                           = to_si*conv[pascal][pascal];
  conv[atmosphere][atmosphere]                                                       = 1.0;
  conv[atmosphere][bar]                                                              = to_si*conv[pascal][bar];
  conv[atmosphere][dyne_per_centimetersq]                                            = to_si*conv[pascal][dyne_per_centimetersq];
  conv[atmosphere][picogram_per_micrometer_microsecondsq]                            = to_si*conv[pascal][picogram_per_micrometer_microsecondsq];
  conv[atmosphere][attogram_per_nanometer_nanosecondsq]                              = to_si*conv[pascal][attogram_per_nanometer_nanosecondsq];

  to_si = 1.0/conv[pascal][bar];
  conv[bar][pascal]                                                                  = to_si*conv[pascal][pascal];
  conv[bar][atmosphere]                                                              = to_si*conv[pascal][atmosphere];
  conv[bar][bar]                                                                     = 1.0;
  conv[bar][dyne_per_centimetersq]                                                   = to_si*conv[pascal][dyne_per_centimetersq];
  conv[bar][picogram_per_micrometer_microsecondsq]                                   = to_si*conv[pascal][picogram_per_micrometer_microsecondsq];
  conv[bar][attogram_per_nanometer_nanosecondsq]                                     = to_si*conv[pascal][attogram_per_nanometer_nanosecondsq];

  to_si = 1.0/conv[pascal][dyne_per_centimetersq];
  conv[dyne_per_centimetersq][pascal]                                                = to_si*conv[pascal][pascal];
  conv[dyne_per_centimetersq][atmosphere]                                            = to_si*conv[pascal][atmosphere];
  conv[dyne_per_centimetersq][bar]                                                   = to_si*conv[pascal][bar];
  conv[dyne_per_centimetersq][dyne_per_centimetersq]                                 = 1.0;
  conv[dyne_per_centimetersq][picogram_per_micrometer_microsecondsq]                 = to_si*conv[pascal][picogram_per_micrometer_microsecondsq];
  conv[dyne_per_centimetersq][attogram_per_nanometer_nanosecondsq]                   = to_si*conv[pascal][attogram_per_nanometer_nanosecondsq];

  to_si = 1.0/conv[pascal][picogram_per_micrometer_microsecondsq];
  conv[picogram_per_micrometer_microsecondsq][pascal]                                = to_si*conv[pascal][pascal];
  conv[picogram_per_micrometer_microsecondsq][atmosphere]                            = to_si*conv[pascal][atmosphere];
  conv[picogram_per_micrometer_microsecondsq][bar]                                   = to_si*conv[pascal][bar];
  conv[picogram_per_micrometer_microsecondsq][dyne_per_centimetersq]                 = to_si*conv[pascal][dyne_per_centimetersq];
  conv[picogram_per_micrometer_microsecondsq][picogram_per_micrometer_microsecondsq] = 1.0;
  conv[picogram_per_micrometer_microsecondsq][attogram_per_nanometer_nanosecondsq]   = to_si*conv[pascal][attogram_per_nanometer_nanosecondsq];

  to_si = 1.0/conv[pascal][attogram_per_nanometer_nanosecondsq];
  conv[attogram_per_nanometer_nanosecondsq][pascal]                                  = to_si*conv[pascal][pascal];
  conv[attogram_per_nanometer_nanosecondsq][atmosphere]                              = to_si*conv[pascal][atmosphere];
  conv[attogram_per_nanometer_nanosecondsq][bar]                                     = to_si*conv[pascal][bar];
  conv[attogram_per_nanometer_nanosecondsq][dyne_per_centimetersq]                   = to_si*conv[pascal][dyne_per_centimetersq];
  conv[attogram_per_nanometer_nanosecondsq][picogram_per_micrometer_microsecondsq]   = to_si*conv[pascal][picogram_per_micrometer_microsecondsq];
  conv[attogram_per_nanometer_nanosecondsq][attogram_per_nanometer_nanosecondsq]     = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Viscosity conversion
double kim_units::get_viscosity_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[pascal_second][pascal_second]                                             = 1.0;
  conv[pascal_second][poise]                                                     = 1.0/poise_si;
  conv[pascal_second][amu_per_bohr_femtosecond]                                  = 1.0/amu_per_bohr_femtosecond_si;
  conv[pascal_second][picogram_per_micrometer_microsecond]                       = 1.0/picogram_per_micrometer_microsecond_si;
  conv[pascal_second][attogram_per_nanometer_nanosecond]                         = 1.0/attogram_per_nanometer_nanosecond_si;

  to_si = 1.0/conv[pascal_second][poise];
  conv[poise][pascal_second]                                                     = to_si*conv[pascal_second][pascal_second];
  conv[poise][poise]                                                             = 1.0;
  conv[poise][amu_per_bohr_femtosecond]                                          = to_si*conv[pascal_second][amu_per_bohr_femtosecond];
  conv[poise][picogram_per_micrometer_microsecond]                               = to_si*conv[pascal_second][picogram_per_micrometer_microsecond];
  conv[poise][attogram_per_nanometer_nanosecond]                                 = to_si*conv[pascal_second][attogram_per_nanometer_nanosecond];

  to_si = 1.0/conv[pascal_second][amu_per_bohr_femtosecond];
  conv[amu_per_bohr_femtosecond][pascal_second]                                  = to_si*conv[pascal_second][pascal_second];
  conv[amu_per_bohr_femtosecond][poise]                                          = to_si*conv[pascal_second][poise];
  conv[amu_per_bohr_femtosecond][amu_per_bohr_femtosecond]                       = 1.0;
  conv[amu_per_bohr_femtosecond][picogram_per_micrometer_microsecond]            = to_si*conv[pascal_second][picogram_per_micrometer_microsecond];
  conv[amu_per_bohr_femtosecond][attogram_per_nanometer_nanosecond]              = to_si*conv[pascal_second][attogram_per_nanometer_nanosecond];

  to_si = 1.0/conv[pascal_second][picogram_per_micrometer_microsecond];
  conv[picogram_per_micrometer_microsecond][pascal_second]                       = to_si*conv[pascal_second][pascal_second];
  conv[picogram_per_micrometer_microsecond][poise]                               = to_si*conv[pascal_second][poise];
  conv[picogram_per_micrometer_microsecond][amu_per_bohr_femtosecond]            = to_si*conv[pascal_second][amu_per_bohr_femtosecond];
  conv[picogram_per_micrometer_microsecond][picogram_per_micrometer_microsecond] = 1.0;
  conv[picogram_per_micrometer_microsecond][attogram_per_nanometer_nanosecond]   = to_si*conv[pascal_second][attogram_per_nanometer_nanosecond];

  to_si = 1.0/conv[pascal_second][attogram_per_nanometer_nanosecond];
  conv[attogram_per_nanometer_nanosecond][pascal_second]                         = to_si*conv[pascal_second][pascal_second];
  conv[attogram_per_nanometer_nanosecond][poise]                                 = to_si*conv[pascal_second][poise];
  conv[attogram_per_nanometer_nanosecond][amu_per_bohr_femtosecond]              = to_si*conv[pascal_second][amu_per_bohr_femtosecond];
  conv[attogram_per_nanometer_nanosecond][picogram_per_micrometer_microsecond]   = to_si*conv[pascal_second][picogram_per_micrometer_microsecond];
  conv[attogram_per_nanometer_nanosecond][attogram_per_nanometer_nanosecond]     = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Charge conversion
double kim_units::get_charge_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[coulomb][coulomb]         = 1.0;
  conv[coulomb][echarge]         = 1.0/echarge_si;
  conv[coulomb][statcoulomb]     = 1.0/statcoulomb_si;
  conv[coulomb][picocoulomb]     = 1.0/picocoulomb_si;

  to_si = 1.0/conv[coulomb][echarge];
  conv[echarge][coulomb]         = to_si*conv[coulomb][coulomb];
  conv[echarge][echarge]         = 1.0;
  conv[echarge][statcoulomb]     = to_si*conv[coulomb][statcoulomb];
  conv[echarge][picocoulomb]     = to_si*conv[coulomb][picocoulomb];

  to_si = 1.0/conv[coulomb][statcoulomb];
  conv[statcoulomb][coulomb]     = to_si*conv[coulomb][coulomb];
  conv[statcoulomb][echarge]     = to_si*conv[coulomb][echarge];
  conv[statcoulomb][statcoulomb] = 1.0;
  conv[statcoulomb][picocoulomb] = to_si*conv[coulomb][picocoulomb];

  to_si = 1.0/conv[coulomb][picocoulomb];
  conv[picocoulomb][coulomb]     = to_si*conv[coulomb][coulomb];
  conv[picocoulomb][echarge]     = to_si*conv[coulomb][echarge];
  conv[picocoulomb][statcoulomb] = to_si*conv[coulomb][statcoulomb];
  conv[picocoulomb][picocoulomb] = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Dipole conversion
double kim_units::get_dipole_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[coulomb_meter][coulomb_meter]                   = 1.0;
  conv[coulomb_meter][electron_angstrom]               = 1.0/electron_angstrom_si;
  conv[coulomb_meter][statcoulomb_centimeter]          = 1.0/statcoulomb_centimeter_si;
  conv[coulomb_meter][debye]                           = 1.0/debye_si;
  conv[coulomb_meter][picocoulomb_micrometer]          = 1.0/picocoulomb_micrometer_si;
  conv[coulomb_meter][electron_nanometer]              = 1.0/electron_nanometer_si;

  to_si = 1.0/conv[coulomb_meter][electron_angstrom];
  conv[electron_angstrom][coulomb_meter]               = to_si*conv[coulomb_meter][coulomb_meter];
  conv[electron_angstrom][electron_angstrom]           = 1.0;
  conv[electron_angstrom][statcoulomb_centimeter]      = to_si*conv[coulomb_meter][statcoulomb_centimeter];
  conv[electron_angstrom][debye]                       = to_si*conv[coulomb_meter][debye];
  conv[electron_angstrom][picocoulomb_micrometer]      = to_si*conv[coulomb_meter][picocoulomb_micrometer];
  conv[electron_angstrom][electron_nanometer]          = to_si*conv[coulomb_meter][electron_nanometer];

  to_si = 1.0/conv[coulomb_meter][statcoulomb_centimeter];
  conv[statcoulomb_centimeter][coulomb_meter]          = to_si*conv[coulomb_meter][coulomb_meter];
  conv[statcoulomb_centimeter][electron_angstrom]      = to_si*conv[coulomb_meter][electron_angstrom];
  conv[statcoulomb_centimeter][statcoulomb_centimeter] = 1.0;
  conv[statcoulomb_centimeter][debye]                  = to_si*conv[coulomb_meter][debye];
  conv[statcoulomb_centimeter][picocoulomb_micrometer] = to_si*conv[coulomb_meter][picocoulomb_micrometer];
  conv[statcoulomb_centimeter][electron_nanometer]     = to_si*conv[coulomb_meter][electron_nanometer];

  to_si = 1.0/conv[coulomb_meter][debye];
  conv[debye][coulomb_meter]                           = to_si*conv[coulomb_meter][coulomb_meter];
  conv[debye][electron_angstrom]                       = to_si*conv[coulomb_meter][electron_angstrom];
  conv[debye][statcoulomb_centimeter]                  = to_si*conv[coulomb_meter][statcoulomb_centimeter];
  conv[debye][debye]                                   = 1.0;
  conv[debye][picocoulomb_micrometer]                  = to_si*conv[coulomb_meter][picocoulomb_micrometer];
  conv[debye][electron_nanometer]                      = to_si*conv[coulomb_meter][electron_nanometer];

  to_si = 1.0/conv[coulomb_meter][picocoulomb_micrometer];
  conv[picocoulomb_micrometer][coulomb_meter]          = to_si*conv[coulomb_meter][coulomb_meter];
  conv[picocoulomb_micrometer][electron_angstrom]      = to_si*conv[coulomb_meter][electron_angstrom];
  conv[picocoulomb_micrometer][statcoulomb_centimeter] = to_si*conv[coulomb_meter][statcoulomb_centimeter];
  conv[picocoulomb_micrometer][debye]                  = to_si*conv[coulomb_meter][debye];
  conv[picocoulomb_micrometer][picocoulomb_micrometer] = 1.0;
  conv[picocoulomb_micrometer][electron_nanometer]     = to_si*conv[coulomb_meter][electron_nanometer];

  to_si = 1.0/conv[coulomb_meter][electron_nanometer];
  conv[electron_nanometer][coulomb_meter]              = to_si*conv[coulomb_meter][coulomb_meter];
  conv[electron_nanometer][electron_angstrom]          = to_si*conv[coulomb_meter][electron_angstrom];
  conv[electron_nanometer][statcoulomb_centimeter]     = to_si*conv[coulomb_meter][statcoulomb_centimeter];
  conv[electron_nanometer][debye]                      = to_si*conv[coulomb_meter][debye];
  conv[electron_nanometer][picocoulomb_micrometer]     = to_si*conv[coulomb_meter][picocoulomb_micrometer];
  conv[electron_nanometer][electron_nanometer]         = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Electric field conversion
double kim_units::get_efield_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[volt_per_meter][volt_per_meter]                   = 1.0;
  conv[volt_per_meter][volt_per_angstrom]                = 1.0/volt_per_angstrom_si;
  conv[volt_per_meter][statvolt_per_centimeter]          = 1.0/statvolt_per_centimeter_si;
  conv[volt_per_meter][volt_per_centimeter]              = 1.0/volt_per_centimeter_si;
  conv[volt_per_meter][volt_per_micrometer]              = 1.0/volt_per_micrometer_si;
  conv[volt_per_meter][volt_per_nanometer]               = 1.0/volt_per_nanometer_si;

  to_si = 1.0/conv[volt_per_meter][volt_per_angstrom];
  conv[volt_per_angstrom][volt_per_meter]                = to_si*conv[volt_per_meter][volt_per_meter];
  conv[volt_per_angstrom][volt_per_angstrom]             = 1.0;
  conv[volt_per_angstrom][statvolt_per_centimeter]       = to_si*conv[volt_per_meter][statvolt_per_centimeter];
  conv[volt_per_angstrom][volt_per_centimeter]           = to_si*conv[volt_per_meter][volt_per_centimeter];
  conv[volt_per_angstrom][volt_per_micrometer]           = to_si*conv[volt_per_meter][volt_per_micrometer];
  conv[volt_per_angstrom][volt_per_nanometer]            = to_si*conv[volt_per_meter][volt_per_nanometer];

  to_si = 1.0/conv[volt_per_meter][statvolt_per_centimeter];
  conv[statvolt_per_centimeter][volt_per_meter]          = to_si*conv[volt_per_meter][volt_per_meter];
  conv[statvolt_per_centimeter][volt_per_angstrom]       = to_si*conv[volt_per_meter][volt_per_angstrom];
  conv[statvolt_per_centimeter][statvolt_per_centimeter] = 1.0;
  conv[statvolt_per_centimeter][volt_per_centimeter]     = to_si*conv[volt_per_meter][volt_per_centimeter];
  conv[statvolt_per_centimeter][volt_per_micrometer]     = to_si*conv[volt_per_meter][volt_per_micrometer];
  conv[statvolt_per_centimeter][volt_per_nanometer]      = to_si*conv[volt_per_meter][volt_per_nanometer];

  to_si = 1.0/conv[volt_per_meter][volt_per_centimeter];
  conv[volt_per_centimeter][volt_per_meter]              = to_si*conv[volt_per_meter][volt_per_meter];
  conv[volt_per_centimeter][volt_per_angstrom]           = to_si*conv[volt_per_meter][volt_per_angstrom];
  conv[volt_per_centimeter][statvolt_per_centimeter]     = to_si*conv[volt_per_meter][statvolt_per_centimeter];
  conv[volt_per_centimeter][volt_per_centimeter]         = 1.0;
  conv[volt_per_centimeter][volt_per_micrometer]         = to_si*conv[volt_per_meter][volt_per_micrometer];
  conv[volt_per_centimeter][volt_per_nanometer]          = to_si*conv[volt_per_meter][volt_per_nanometer];

  to_si = 1.0/conv[volt_per_meter][volt_per_micrometer];
  conv[volt_per_micrometer][volt_per_meter]              = to_si*conv[volt_per_meter][volt_per_meter];
  conv[volt_per_micrometer][volt_per_angstrom]           = to_si*conv[volt_per_meter][volt_per_angstrom];
  conv[volt_per_micrometer][statvolt_per_centimeter]     = to_si*conv[volt_per_meter][statvolt_per_centimeter];
  conv[volt_per_micrometer][volt_per_centimeter]         = to_si*conv[volt_per_meter][volt_per_centimeter];
  conv[volt_per_micrometer][volt_per_micrometer]         = 1.0;
  conv[volt_per_micrometer][volt_per_nanometer]          = to_si*conv[volt_per_meter][volt_per_nanometer];

  to_si = 1.0/conv[volt_per_meter][volt_per_nanometer];
  conv[volt_per_nanometer][volt_per_meter]               = to_si*conv[volt_per_meter][volt_per_meter];
  conv[volt_per_nanometer][volt_per_angstrom]            = to_si*conv[volt_per_meter][volt_per_angstrom];
  conv[volt_per_nanometer][statvolt_per_centimeter]      = to_si*conv[volt_per_meter][statvolt_per_centimeter];
  conv[volt_per_nanometer][volt_per_centimeter]          = to_si*conv[volt_per_meter][volt_per_centimeter];
  conv[volt_per_nanometer][volt_per_micrometer]          = to_si*conv[volt_per_meter][volt_per_micrometer];
  conv[volt_per_nanometer][volt_per_nanometer]           = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

// Density conversion
double kim_units::get_density_conversion_factor(units from_unit_enum, units to_unit_enum)
{
  map<units, map<units, double> > conv;
  double to_si;

  conv[kilogram_per_metercu][kilogram_per_metercu]           = 1.0;
  conv[kilogram_per_metercu][gram_per_centimetercu]          = 1.0/gram_per_centimetercu_si;
  conv[kilogram_per_metercu][amu_per_bohrcu]                 = 1.0/amu_per_bohrcu_si;
  conv[kilogram_per_metercu][picogram_per_micrometercu]      = 1.0/picogram_per_micrometercu_si;
  conv[kilogram_per_metercu][attogram_per_nanometercu]       = 1.0/attogram_per_nanometercu_si;

  to_si = 1.0/conv[kilogram_per_metercu][gram_per_centimetercu];
  conv[gram_per_centimetercu][kilogram_per_metercu]          = to_si*conv[kilogram_per_metercu][kilogram_per_metercu];
  conv[gram_per_centimetercu][gram_per_centimetercu]         = 1.0;
  conv[gram_per_centimetercu][amu_per_bohrcu]                = to_si*conv[kilogram_per_metercu][amu_per_bohrcu];
  conv[gram_per_centimetercu][picogram_per_micrometercu]     = to_si*conv[kilogram_per_metercu][picogram_per_micrometercu];
  conv[gram_per_centimetercu][attogram_per_nanometercu]      = to_si*conv[kilogram_per_metercu][attogram_per_nanometercu];

  to_si = 1.0/conv[kilogram_per_metercu][amu_per_bohrcu];
  conv[amu_per_bohrcu][kilogram_per_metercu]                 = to_si*conv[kilogram_per_metercu][kilogram_per_metercu];
  conv[amu_per_bohrcu][gram_per_centimetercu]                = to_si*conv[kilogram_per_metercu][gram_per_centimetercu];
  conv[amu_per_bohrcu][amu_per_bohrcu]                       = 1.0;
  conv[amu_per_bohrcu][picogram_per_micrometercu]            = to_si*conv[kilogram_per_metercu][picogram_per_micrometercu];
  conv[amu_per_bohrcu][attogram_per_nanometercu]             = to_si*conv[kilogram_per_metercu][attogram_per_nanometercu];

  to_si = 1.0/conv[kilogram_per_metercu][picogram_per_micrometercu];
  conv[picogram_per_micrometercu][kilogram_per_metercu]      = to_si*conv[kilogram_per_metercu][kilogram_per_metercu];
  conv[picogram_per_micrometercu][gram_per_centimetercu]     = to_si*conv[kilogram_per_metercu][gram_per_centimetercu];
  conv[picogram_per_micrometercu][amu_per_bohrcu]            = to_si*conv[kilogram_per_metercu][amu_per_bohrcu];
  conv[picogram_per_micrometercu][picogram_per_micrometercu] = 1.0;
  conv[picogram_per_micrometercu][attogram_per_nanometercu]  = to_si*conv[kilogram_per_metercu][attogram_per_nanometercu];

  to_si = 1.0/conv[kilogram_per_metercu][attogram_per_nanometercu];
  conv[attogram_per_nanometercu][kilogram_per_metercu]       = to_si*conv[kilogram_per_metercu][kilogram_per_metercu];
  conv[attogram_per_nanometercu][gram_per_centimetercu]      = to_si*conv[kilogram_per_metercu][gram_per_centimetercu];
  conv[attogram_per_nanometercu][amu_per_bohrcu]             = to_si*conv[kilogram_per_metercu][amu_per_bohrcu];
  conv[attogram_per_nanometercu][picogram_per_micrometercu]  = to_si*conv[kilogram_per_metercu][picogram_per_micrometercu];
  conv[attogram_per_nanometercu][attogram_per_nanometercu]   = 1.0;

  return conv[from_unit_enum][to_unit_enum];
}

/* ---------------------------------------------------------------------- */

//  This routine returns the unit conversion factor between the
//  `from_system_enum` to the `to_system_enum` for the `unit_type_enum`.
double kim_units::get_unit_conversion_factor(unit_type &unit_type_enum,
                                             sys_type from_system_enum,
                                             sys_type to_system_enum)
{
  units from_unit = get_lammps_system_unit(from_system_enum, unit_type_enum);
  units to_unit   = get_lammps_system_unit(to_system_enum, unit_type_enum);
  switch(unit_type_enum) {
    case mass :
      return get_mass_conversion_factor(from_unit, to_unit);
    case distance :
      return get_distance_conversion_factor(from_unit, to_unit);
    case time :
      return get_time_conversion_factor(from_unit, to_unit);
    case energy :
      return get_energy_conversion_factor(from_unit, to_unit);
    case velocity :
      return get_velocity_conversion_factor(from_unit, to_unit);
    case force :
      return get_force_conversion_factor(from_unit, to_unit);
    case torque :
      return get_torque_conversion_factor(from_unit, to_unit);
    case temperature :
      return get_temperature_conversion_factor(from_unit, to_unit);
    case pressure :
      return get_pressure_conversion_factor(from_unit, to_unit);
    case viscosity :
      return get_viscosity_conversion_factor(from_unit, to_unit);
    case charge :
      return get_charge_conversion_factor(from_unit, to_unit);
    case dipole :
      return get_dipole_conversion_factor(from_unit, to_unit);
    case efield :
      return get_efield_conversion_factor(from_unit, to_unit);
    case density :
    default :    // This is here to a prevent a compiler warning
      return get_density_conversion_factor(from_unit, to_unit);
  }
}

/* ---------------------------------------------------------------------- */
// clang-format on
//  Wrapper to the routine that gets the unit conversion. Translates strings
//  to enumerations and then call get_unit_conversion_factor()
int kim_units::lammps_unit_conversion(const string &unit_type_str, const string &from_system_str,
                                      const string &to_system_str, double &conversion_factor)
{
  // initialize
  conversion_factor = 0.0;
  initialize_dictionaries();

  // convert input to enumeration
  unit_type unit_type_enum;
  {
    map<string, unit_type>::const_iterator itr = unit_dic.find(unit_type_str);
    if (itr != unit_dic.end())
      unit_type_enum = itr->second;
    else
      return 1;    // error
  }
  sys_type from_system_enum;
  {
    map<string, sys_type>::const_iterator itr = system_dic.find(from_system_str);
    if (itr != system_dic.end())
      from_system_enum = itr->second;
    else
      return 1;    // error
  }
  sys_type to_system_enum;
  {
    map<string, sys_type>::const_iterator itr = system_dic.find(to_system_str);
    if (itr != system_dic.end())
      to_system_enum = itr->second;
    else
      return 1;
  }

  // process unit conversions
  conversion_factor = get_unit_conversion_factor(unit_type_enum, from_system_enum, to_system_enum);
  return 0;
}

void kim_units::get_kim_unit_names(char const *const system, KIM_LengthUnit &lengthUnit,
                                   KIM_EnergyUnit &energyUnit, KIM_ChargeUnit &chargeUnit,
                                   KIM_TemperatureUnit &temperatureUnit, KIM_TimeUnit &timeUnit,
                                   Error *error)
{
  const std::string system_str(system);
  if (system_str == "real") {
    lengthUnit = KIM_LENGTH_UNIT_A;
    energyUnit = KIM_ENERGY_UNIT_kcal_mol;
    chargeUnit = KIM_CHARGE_UNIT_e;
    temperatureUnit = KIM_TEMPERATURE_UNIT_K;
    timeUnit = KIM_TIME_UNIT_fs;
  } else if (system_str == "metal") {
    lengthUnit = KIM_LENGTH_UNIT_A;
    energyUnit = KIM_ENERGY_UNIT_eV;
    chargeUnit = KIM_CHARGE_UNIT_e;
    temperatureUnit = KIM_TEMPERATURE_UNIT_K;
    timeUnit = KIM_TIME_UNIT_ps;
  } else if (system_str == "si") {
    lengthUnit = KIM_LENGTH_UNIT_m;
    energyUnit = KIM_ENERGY_UNIT_J;
    chargeUnit = KIM_CHARGE_UNIT_C;
    temperatureUnit = KIM_TEMPERATURE_UNIT_K;
    timeUnit = KIM_TIME_UNIT_s;
  } else if (system_str == "cgs") {
    lengthUnit = KIM_LENGTH_UNIT_cm;
    energyUnit = KIM_ENERGY_UNIT_erg;
    chargeUnit = KIM_CHARGE_UNIT_statC;
    temperatureUnit = KIM_TEMPERATURE_UNIT_K;
    timeUnit = KIM_TIME_UNIT_s;
  } else if (system_str == "electron") {
    lengthUnit = KIM_LENGTH_UNIT_Bohr;
    energyUnit = KIM_ENERGY_UNIT_Hartree;
    chargeUnit = KIM_CHARGE_UNIT_e;
    temperatureUnit = KIM_TEMPERATURE_UNIT_K;
    timeUnit = KIM_TIME_UNIT_fs;
  } else if ((system_str == "lj") || (system_str == "micro") || (system_str == "nano")) {
    error->all(FLERR, "LAMMPS unit_style {} not supported by KIM models", system_str);
  } else {
    error->all(FLERR, "Unknown unit_style {}", system_str);
  }
}
