######################################################################
# BioSimSpace: Making biomolecular simulation a breeze!
#
# Copyright: 2017-2024
#
# Authors: Lester Hedges <lester.hedges@gmail.com>
#
# BioSimSpace 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 3 of the License, or
# (at your option) any later version.
#
# BioSimSpace 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 BioSimSpace. If not, see <http://www.gnu.org/licenses/>.
#####################################################################
"""An energy type."""
__author__ = "Lester Hedges"
__email__ = "lester.hedges@gmail.com"
__all__ = ["Energy"]
from sire.legacy import Units as _SireUnits
from ._type import Type as _Type
[docs]
class Energy(_Type):
"""An energy type."""
# A list of the supported Sire unit names.
_sire_units = ["kcal_per_mol", "kJ_per_mol"]
# Dictionary of allowed units.
_supported_units = {
"KILO CALORIES PER MOL": _SireUnits.kcal_per_mol,
"KILO JOULES PER MOL": _SireUnits.kJ_per_mol,
"KT": 2.479 * _SireUnits.kJ_per_mol,
}
# Map unit abbreviations to the full name.
_abbreviations = {
"KCAL/MOL": "KILO CALORIES PER MOL",
"KJ/MOL": "KILO JOULES PER MOL",
"KT": "KT",
}
# Print formatting.
_print_format = {
"KILO CALORIES PER MOL": "kcal/mol",
"KILO JOULES PER MOL": "kJ/mol",
"KT": "KT",
}
# Documentation strings.
_doc_strings = {
"KILO CALORIES PER MOL": "An energy in kcal per mol.",
"KILO JOULES PER MOL": "An energy in kJ per mol.",
"KT": "An energy in KT.",
}
# Null type unit for avoiding issue printing configargparse help.
_default_unit = "KILO CALORIES PER MOL"
# The dimension mask.
_dimensions = tuple(list(_supported_units.values())[0].dimensions())
[docs]
def __init__(self, *args):
"""
Constructor.
``*args`` can be a value and unit, or a string representation
of the energy, e.g. "78.4 kcal/mol".
Parameters
----------
value : float
The value.
unit : str
The unit.
string : str
A string representation of the energy.
Examples
--------
Create an object representing an energy of -1038 kilo calories per
mol and print the energy in kilo joules per mol.
>>> import BioSimSpace as BSS
>>> energy = BSS.Types.Energy(-1038, "kcal/mol")
>>> print(energy.kj_per_mol())
The same as above, except passing a string representation of the
energy to the constructor.
>>> import BioSimSpace as BSS
>>> energy = BSS.Types.Energy("-1038 kcal/mol")
>>> print(energy.kj_per_mol())
The string matching is extremeley flexible, so all of the following
would be valid arguments: "-1038 kcal/mol", "-1.038e3 kcal/mol",
"-1038 kilo cal per mol".
"""
# Call the base class constructor.
super().__init__(*args)
[docs]
def kcal_per_mol(self):
"""
Return the energy in kcal per mol.
Returns
-------
energy : :class:`Energy <BioSimSpace.Types.Energy>`
The energy in kcal per mol.
"""
return Energy(
(self._value * self._supported_units[self._unit]).to(
_SireUnits.kcal_per_mol
),
"KILO CALORIES PER MOL",
)
[docs]
def kj_per_mol(self):
"""
Return the energy in kJ per mol.
Returns
-------
energy : :class:`Energy <BioSimSpace.Types.Energy>`
The energy in kJ per mol.
"""
return Energy(
(self._value * self._supported_units[self._unit]).to(_SireUnits.kJ_per_mol),
"KILO JOULES PER MOL",
)
[docs]
def kt(self):
"""
Return the energy in KT.
Returns
-------
energy : :class:`Energy <BioSimSpace.Types.Energy>`
The energy in KT.
"""
return Energy(
(self._value * self._supported_units[self._unit]).to(
2.479 * _SireUnits.kJ_per_mol
),
"KT",
)
def _to_default_unit(self, mag=None):
"""
Internal method to return an object of the same type in the default unit.
Parameters
----------
mag : float
The value (optional).
Returns
-------
energy : :class:`Energy <BioSimSpace.Types.Energy>`
The energy in the default unit of kcal per mol.
"""
if mag is None:
return self.kcal_per_mol()
else:
return Energy(mag, "KILO CALORIES PER MOL")
def _convert_to(self, unit):
"""
Return the energy in a different unit.
Parameters
----------
unit : str
The unit to convert to.
Returns
-------
energy : :class:`Energy <BioSimSpace.Types.Energy>`
The energy in the specified unit.
"""
if unit == "KILO CALORIES PER MOL":
return self.kcal_per_mol()
elif unit == "KILO JOULES PER MOL":
return self.kj_per_mol()
elif unit == "KT":
return self.kt()
else:
raise ValueError(
"Supported units are: '%s'" % list(self._supported_units.keys())
)
@classmethod
def _validate_unit(cls, unit):
"""Validate that the unit are supported."""
# Strip whitespace and convert to upper case.
unit = unit.replace(" ", "").upper()
# Replace all instances of KILO with K.
unit = unit.replace("KILO", "K")
# Replace all instances of PER with the / character.
unit = unit.replace("PER", "/")
# Replace all instance of MOLE with MOL.
unit = unit.replace("MOLE", "MOL")
# Replace all instances of CALORIES with CAL.
unit = unit.replace("CALORIES", "CAL")
# Replace all instances of JOULES with J.
unit = unit.replace("JOULES", "J")
# Check that the unit is supported.
if unit in cls._abbreviations:
return cls._abbreviations[unit]
else:
raise ValueError(
"Supported units are: '%s'" % list(cls._supported_units.keys())
)
@staticmethod
def _to_sire_format(unit):
"""
Reformat the unit string so it adheres to the Sire unit formatting.
Parameters
----------
unit : str
A string representation of the unit.
Returns
-------
sire_unit : str
The unit string in Sire compatible format.
"""
unit = unit.replace("mole", "mol")
unit = unit.replace("mols", "mol")
unit = unit.replace("calories", "cal")
unit = unit.replace("joules", "J")
unit = unit.replace("kilocal", "kcal")
unit = unit.replace("kiloJ", "kJ")
unit = unit.replace("kj", "kJ")
unit = unit.replace("kcals", "kcal")
unit = unit.replace("kJs", "kJ")
unit = unit.replace("kcalpermol", "kcal_per_mol")
unit = unit.replace("kJpermol", "kJ_per_mol")
# Convert powers.
unit = unit.replace("kcal_per_mol2", "(kcal_per_mol*kcal_per_mol)")
unit = unit.replace("kcal_per_mol3", "(kcal_per_mol*kcal_per_mol*kcal_per_mol)")
unit = unit.replace("kcal_per_mol-1", "(1/kcal_per_mol)")
unit = unit.replace("kcal_per_mol-2", "(1/(kcal_per_mol*kcal_per_mol))")
unit = unit.replace(
"kcal_per_mol-3", "(1/(kcal_per_mol*kcal_per_mol*kcal_per_mol))"
)
unit = unit.replace("kJ_per_mol2", "(kJ_per_mol*kJ_per_mol)")
unit = unit.replace("kJ_per_mol3", "(kJ_per_mol*kJ_per_mol*kJ_per_mol)")
unit = unit.replace("kJ_per_mol-1", "(1/kJ_per_mol)")
unit = unit.replace("kJ_per_mol-2", "(1/(kJ_per_mol*kJ_per_mol))")
unit = unit.replace("kJ_per_mol-3", "(1/(kJ_per_mol*kJ_per_mol*kJ_per_mol))")
return unit