Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit class to FlowerMD and allow using real units in Simulation runs #137

Open
wants to merge 54 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
d38aba8
calculate real T from kT
marjanalbooyeh Mar 15, 2024
05d594e
unit test for real_temperature
marjanalbooyeh Mar 15, 2024
0c47ee3
precommit reformat
marjanalbooyeh Mar 15, 2024
d2bad99
allow temperature to be passed to run methods instead of kT
marjanalbooyeh Mar 15, 2024
ee71094
test temperature for nvt runs
marjanalbooyeh Mar 15, 2024
27a2d74
add time_length parameter to run methods
marjanalbooyeh Mar 15, 2024
17b6043
fix None conditions
marjanalbooyeh Mar 18, 2024
822861f
add tests for time length
marjanalbooyeh Mar 20, 2024
b422a11
update docstrings
marjanalbooyeh Mar 20, 2024
4f44b55
Merge branch 'cmelab:main' into main
marjanalbooyeh Mar 21, 2024
e83b3c4
save reference values to pickle
marjanalbooyeh Mar 21, 2024
e4ce1c6
tests for save ref values
marjanalbooyeh Mar 21, 2024
4d91a49
added more unit tests for temperature and time length
marjanalbooyeh Mar 21, 2024
26af373
real temperature without energy unit
marjanalbooyeh Mar 21, 2024
68777e1
Merge branch 'cmelab:main' into main
marjanalbooyeh May 1, 2024
b70dd70
add units class
marjanalbooyeh May 1, 2024
f17dc4b
add temperature and duration parameters
marjanalbooyeh May 1, 2024
1cbfb07
Add an example to NVT docstring with real units for temperature and d…
marjanalbooyeh May 1, 2024
45ee688
update docstrings
marjanalbooyeh May 1, 2024
5521716
move units class to internal dir
marjanalbooyeh May 2, 2024
73c7b97
use validate_unit function to validate units
marjanalbooyeh May 2, 2024
b33279d
Add a utils function to validate units.
marjanalbooyeh May 2, 2024
2e4ea71
update system unit tests according to new changes in units
marjanalbooyeh May 2, 2024
68c6d78
add new units
marjanalbooyeh May 2, 2024
eee22fd
update unit error messages
marjanalbooyeh May 2, 2024
75cd5f4
update simulation unit tests according to units
marjanalbooyeh May 2, 2024
6f9f04d
fix unit test bugs
marjanalbooyeh May 2, 2024
6a9be73
remove validate_ref_value
marjanalbooyeh May 2, 2024
419f2d1
use flowermd units instead of unyt and fix tests
marjanalbooyeh May 2, 2024
faa114f
update utils tests
marjanalbooyeh May 2, 2024
8e72bec
add tests for validate_unit
marjanalbooyeh May 2, 2024
0eda3d4
update Units docstrings.
marjanalbooyeh May 2, 2024
b5c24f2
add femtosecond
marjanalbooyeh May 6, 2024
d7e3f2d
merge changes from upstream.
marjanalbooyeh Nov 23, 2024
2e7a4f8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 23, 2024
a4fccd4
Add functionalitiy to handle real pressure units. Add properties for …
marjanalbooyeh Nov 28, 2024
dc13ebb
Add density and pressure units.
marjanalbooyeh Nov 28, 2024
2ff5d79
Check for flowermd Units instance in unit validation.
marjanalbooyeh Nov 28, 2024
455b2ec
Merge branch 'main' of github.com:marjanalbooyeh/flowerMD
marjanalbooyeh Nov 28, 2024
7156c79
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 28, 2024
5d70e13
Simplify functions.
marjanalbooyeh Nov 28, 2024
e8c70cb
Add reduced temperature and reduced pressure propoerties to sim.
marjanalbooyeh Nov 28, 2024
a194244
Merge branch 'main' of github.com:cmelab/flowerMD
marjanalbooyeh Nov 28, 2024
ae0d07d
updated pre-commit.
marjanalbooyeh Nov 28, 2024
0199a8d
merge with main
marjanalbooyeh Nov 28, 2024
0c93dc2
Test real units in simulations.
marjanalbooyeh Nov 28, 2024
15a2e8e
Add number density units.
marjanalbooyeh Nov 28, 2024
49e3657
Use flowermd Units class.
marjanalbooyeh Nov 28, 2024
1035eb5
Use and explain new variable names (duration, temperature, etc) in tu…
marjanalbooyeh Nov 29, 2024
06b35fb
Use correct unit import.
marjanalbooyeh Nov 29, 2024
652fcc8
Use duration and temperature variable names in recipes and tests.
marjanalbooyeh Nov 29, 2024
5f0def9
Fix variable names.
marjanalbooyeh Nov 29, 2024
b71ab0d
Temporary check for temperature Ramp.
marjanalbooyeh Nov 29, 2024
628acec
Fix tutorials bugs.
marjanalbooyeh Nov 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions flowermd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
Simulation,
System,
)
from .internal.units import Units
399 changes: 294 additions & 105 deletions flowermd/base/simulation.py

Large diffs are not rendered by default.

69 changes: 40 additions & 29 deletions flowermd/base/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pickle
import warnings
from abc import ABC, abstractmethod
from typing import List
from typing import List, Union

import gsd
import mbuild as mb
Expand All @@ -14,7 +14,7 @@

from flowermd.base.forcefield import BaseHOOMDForcefield, BaseXMLForcefield
from flowermd.base.molecule import Molecule
from flowermd.internal import check_return_iterable, validate_ref_value
from flowermd.internal import Units, check_return_iterable, validate_unit
from flowermd.internal.exceptions import ForceFieldError, MoleculeLoadError
from flowermd.utils import (
get_target_box_mass_density,
Expand Down Expand Up @@ -208,12 +208,10 @@ def reference_length(self, length):

Parameters
----------
length : string or unyt.unyt_quantity, required
length : reference length * `flowermd.Units`, required
The reference length of the system.
It can be provided in the following forms:
1) A string with the format of "value unit", for example "1 nm".
2) A unyt.unyt_quantity object with the correct dimension. For
example, unyt.unyt_quantity(1, "nm").
It can be provided in the following form of:
value * `flowermd.Units`, for example 1 * `flowermd.Units.angstrom`.

"""
if self.auto_scale:
Expand All @@ -222,7 +220,7 @@ def reference_length(self, length):
"Setting reference length manually disables auto "
"scaling."
)
validated_length = validate_ref_value(length, u.dimensions.length)
validated_length = validate_unit(length, u.dimensions.length)
self._reference_values["length"] = validated_length

@reference_energy.setter
Expand All @@ -231,12 +229,10 @@ def reference_energy(self, energy):

Parameters
----------
energy : string or unyt.unyt_quantity, required
energy : reference energy * `flowermd.Units`, required
The reference energy of the system.
It can be provided in the following forms:
1) A string with the format of "value unit", for example "1 kJ/mol".
2) A unyt.unyt_quantity object with the correct dimension. For
example, unyt.unyt_quantity(1, "kJ/mol").
It can be provided in the following form of:
value * `flowermd.Units`, for example 1 * `flowermd.Units.kcal/mol`.

"""
if self.auto_scale:
Expand All @@ -245,7 +241,7 @@ def reference_energy(self, energy):
"Setting reference energy manually disables auto "
"scaling."
)
validated_energy = validate_ref_value(energy, u.dimensions.energy)
validated_energy = validate_unit(energy, u.dimensions.energy)
self._reference_values["energy"] = validated_energy

@reference_mass.setter
Expand All @@ -254,21 +250,18 @@ def reference_mass(self, mass):

Parameters
----------
mass : string or unyt.unyt_quantity, required
mass : reference mass * `flowermd.Units`, required
The reference mass of the system.
It can be provided in the following forms:
1) A string with the format of "value unit", for example "1 amu".
2) A unyt.unyt_quantity object with the correct dimension. For
example, unyt.unyt_quantity(1, "amu").

It can be provided in the following form of:
value * `flowermd.Units`, for example 1 * `flowermd.Units.amu`.
"""
if self.auto_scale:
warnings.warn(
"`auto_scale` was set to True for this system. "
"Setting reference mass manually disables auto "
"scaling."
)
validated_mass = validate_ref_value(mass, u.dimensions.mass)
validated_mass = validate_unit(mass, u.dimensions.mass)
self._reference_values["mass"] = validated_mass

@reference_values.setter
Expand Down Expand Up @@ -404,6 +397,23 @@ def to_gsd(self, file_name):
with gsd.hoomd.open(file_name, "w") as traj:
traj.append(self.hoomd_snapshot)

def save_reference_values(self, file_path="reference_values.pickle"):
"""Save the reference values of the system to a pickle file.

Parameters
----------
file_path : str, default "reference_values.pickle"
The path to save the pickle file to.

"""
if not self.reference_values:
raise ValueError(
"Reference values have not been set. "
"See System.reference_values"
)
f = open(file_path, "wb")
pickle.dump(self.reference_values, f)

def _convert_to_gmso(self):
"""Convert the mbuild system to a gmso system."""
topology = from_mbuild(self.system)
Expand Down Expand Up @@ -615,11 +625,12 @@ class Pack(System):

Parameters
----------
density : float, required
The desired density of the system (g/cm^3). Used to set the
density : float or unyt_quantity or flowermd.internal.Units, required
The desired density of the system. Used to set the
target_box attribute. Can be useful when initializing
systems at low density and running a shrink simulation
to achieve a target density.
to achieve a target density. If no unit is provided, assuming the
density is in g/cm**3.
packing_expand_factor : int, default 5
The factor by which to expand the box for packing.
edge : float, default 0.2
Expand Down Expand Up @@ -650,19 +661,19 @@ class Pack(System):
def __init__(
self,
molecules,
density: float,
density: Union[int, float, u.unyt_quantity, u.unyt_array, Units],
base_units=dict(),
packing_expand_factor=5,
edge=0.2,
overlap=0.2,
fix_orientation=False,
):
if not isinstance(density, u.array.unyt_quantity):
self.density = density * u.Unit("g") / u.Unit("cm**3")
if isinstance(density, (int, float)):
warnings.warn(
"Units for density were not given, assuming "
"units of g/cm**3."
)
self.density = density * Units.g_cm3
else:
self.density = density
self.packing_expand_factor = packing_expand_factor
Expand All @@ -672,8 +683,8 @@ def __init__(
super(Pack, self).__init__(molecules=molecules, base_units=base_units)

def _build_system(self):
mass_density = u.Unit("kg") / u.Unit("m**3")
number_density = u.Unit("m**-3")
mass_density = Units.kg_m3
number_density = Units.n_m3
if self.density.units.dimensions == mass_density.dimensions:
target_box = get_target_box_mass_density(
density=self.density, mass=self.mass
Expand Down
3 changes: 2 additions & 1 deletion flowermd/internal/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: F401
from .ff_utils import xml_to_gmso_ff
from .utils import check_return_iterable, validate_ref_value
from .units import Units
from .utils import check_return_iterable, validate_unit
2 changes: 1 addition & 1 deletion flowermd/internal/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __init__(self, msg):
super().__init__(msg)


class ReferenceUnitError(Exception):
class UnitError(Exception):
def __init__(self, msg):
super().__init__(msg)

Expand Down
86 changes: 86 additions & 0 deletions flowermd/internal/units.py
marjanalbooyeh marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""FlowerMD Unit class."""

import unyt as u


class Units:
"""FlowerMD Unit class.

Example usage:
--------------

::
length = 1.0 * flowermd.internal.Units.angstrom
energy = 1.0 * flowermd.internal.Units.kcal_mol
mass = 1.0 * flowermd.internal.Units.amu
time = 1.0 * flowermd.internal.Units.ps
temperature = 1.0 * flowermd.internal.Units.K


"""

# length units
m = u.m
meter = u.m
cm = u.cm
centimeter = u.cm
nm = u.nm
nanometer = u.nm
pm = u.pm
picometer = u.pm
ang = u.angstrom
angstrom = u.angstrom

# energy units
J = u.J
Joule = u.J
kJ = u.kJ
cal = u.cal
kcal = u.kcal
kcal_mol = u.kcal / u.mol
kJ_mol = u.kJ / u.mol

# mass units
g = u.g
gram = u.g
amu = u.amu
kg = u.kg

mol = u.mol

# time units
chrisjonesBSU marked this conversation as resolved.
Show resolved Hide resolved
s = u.s
second = u.s
ns = u.ns
nanosecond = u.ns
ps = u.ps
picosecond = u.ps
fs = u.fs
femtosecond = u.fs

# temperature units
K = u.K
Kelvin = u.K
C = u.degC
Celsius = u.degC
F = u.degF
Fahrenheit = u.degF

# mass density units
g_cm3 = u.g / u.cm**3
kg_m3 = u.kg / u.m**3
amu_A3 = u.amu / u.angstrom**3
# number density units
n_m3 = u.Unit("m**-3")
n_cm3 = u.Unit("cm**-3")
n_A3 = u.Unit("angstrom**-3")
n_nm3 = u.Unit("nm**-3")

# pressure units
atm = u.atm
bar = u.bar
Pa = u.Pa
kPa = u.kPa
MPa = u.MPa
GPa = u.GPa
psi = u.psi
100 changes: 33 additions & 67 deletions flowermd/internal/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import unyt
import unyt as u

from flowermd.internal.exceptions import ReferenceUnitError
from flowermd.internal import Units
from flowermd.internal.exceptions import UnitError

"""utils.py
Internal utility methods for flowerMD.
Expand All @@ -19,77 +21,41 @@ def check_return_iterable(obj):
return [obj]


def validate_ref_value(ref_value, dimension):
"""Validate the reference value and checks the unit dimension.
This function validates the reference value. The reference value can be
provided in three ways:
1. An unyt_quantity instance.
2. A string with the value and unit , for example "1.0 g".
3. A string with the value and unit separated by a "/", for example
"1.0 kcal/mol".
def validate_unit(value, dimension):
"""Validate the unit and checks the unit dimension.

Parameters
----------
ref_value : unyt_quantity or str; required
The reference value.
value : `unit value * flowermd.Units`; required
The unit value to be validated.
dimension : unyt_dimension; required
The dimension of the reference value.

Returns
-------
The validated reference value as an unyt.unyt_quantity instance.
The dimension of the unit.
"""

def _is_valid_dimension(ref_unit):
if ref_unit.dimensions != dimension:
raise ReferenceUnitError(
f"Invalid unit dimension. The reference "
f"value must be in {dimension} "
f"dimension."
def _sample_unit_str(dimension):
if dimension == u.dimensions.temperature:
return "temperature = 300 * flowermd.Units.K"
elif dimension == u.dimensions.mass:
return "mass = 1.0 * flowermd.Units.g"
elif dimension == u.dimensions.length:
return "length = 1.0 * flowermd.Units.angstrom"
elif dimension == u.dimensions.time:
return "time = 1.0 * flowermd.Units.ps"
elif dimension == u.dimensions.energy:
return "energy = 1.0 * flowermd.Units.kcal_mol"

if isinstance(value, (u.unyt_quantity, unyt.unyt_array, Units)):
if value.units.dimensions != dimension:
raise UnitError(
f"Invalid unit dimension. The unit must be in "
f"{dimension} dimension. Check `flowermd.Units` for "
f"valid units."
)
return True

def _parse_and_validate_unit(value, unit_str):
if hasattr(u, unit_str):
if unit_str == "amu":
u_unit = u.Unit("amu")
else:
u_unit = getattr(u, unit_str)
if _is_valid_dimension(u_unit):
return float(value) * u_unit
# if the unit contains "/" character, for example "g/mol", check if
# the unit is a valid unit and has the correct dimension.
if len(unit_str.split("/")) == 2:
unit1, unit2 = unit_str.split("/")
if hasattr(u, unit1) and hasattr(u, unit2):
comb_unit = getattr(u, unit1) / getattr(u, unit2)
if _is_valid_dimension(comb_unit):
return float(value) * comb_unit
raise ReferenceUnitError(
f"Invalid reference value. Please provide "
f"a reference value with unit of "
f"{dimension} dimension."
)

def _is_float(num):
try:
return float(num)
except ValueError:
raise ValueError("The reference value is not a number.")

# if ref_value is an instance of unyt_quantity, check the dimension.
if isinstance(ref_value, u.unyt_quantity) and _is_valid_dimension(
ref_value.units
):
return ref_value
# if ref_value is a string, check if it is a number and if it is, check if
# the unit exists in unyt and has the correct dimension.
elif isinstance(ref_value, str) and len(ref_value.split()) == 2:
value, unit_str = ref_value.split()
value = _is_float(value)
return _parse_and_validate_unit(value, unit_str)
return value
else:
raise ReferenceUnitError(
f"Invalid reference value. Please provide "
f"a reference value with unit of "
f"{dimension} dimension."
raise UnitError(
"The unit value must be provided from the "
"`flowermd.Units` class. For example, "
f"{_sample_unit_str(dimension)}. Check "
"`flowermd.Units` for valid units."
)
Loading
Loading