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 ability to use number density when calculating target box lenghts for shrinking and NVT. #112

Merged
merged 35 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9fd764f
add support for number density
chrisjonesBSU Jan 10, 2024
fe53c61
clean up a few lines
chrisjonesBSU Jan 10, 2024
b51c873
fix n density variable
chrisjonesBSU Jan 10, 2024
9f5d382
Merge branch 'main' of github.com:cmelab/flowerMD into number-density
chrisjonesBSU Jan 11, 2024
9d68d38
fix f strings, start adding unit tests
chrisjonesBSU Jan 11, 2024
366758b
fix unit tests
chrisjonesBSU Jan 11, 2024
89feaa2
fix param names to match new ones
chrisjonesBSU Jan 11, 2024
5946794
fix param names
chrisjonesBSU Jan 11, 2024
dc80e48
remove set target_box from Lattice system, fix tests
chrisjonesBSU Jan 11, 2024
410b5e3
Fix tests
chrisjonesBSU Jan 11, 2024
2d8801f
remove N_beads property for now
chrisjonesBSU Jan 11, 2024
084a8b1
implement new design choices for getting target boxes
chrisjonesBSU Jan 12, 2024
c61d2b2
remove target box attr from System, fix unit test
chrisjonesBSU Jan 12, 2024
a934902
update tests
chrisjonesBSU Jan 16, 2024
790c06a
make sure target box array has units, convert to nm in Pack
chrisjonesBSU Jan 16, 2024
762626b
add warning message if units not given for density
chrisjonesBSU Jan 16, 2024
3132e0d
fix tests, convert box in run update vol
chrisjonesBSU Jan 16, 2024
fa0dd65
fix density in droplet module
chrisjonesBSU Jan 16, 2024
7de8652
fix test
chrisjonesBSU Jan 16, 2024
e11e6b5
fix tests
chrisjonesBSU Jan 17, 2024
340f0c7
add units check, remove debug line
chrisjonesBSU Jan 17, 2024
97e406d
update basics tutorial
chrisjonesBSU Jan 18, 2024
9fb2744
use amu units in System.mass property
chrisjonesBSU Jan 18, 2024
2d712e8
finish checking the rest of the tutorials
chrisjonesBSU Jan 18, 2024
4344d84
fix unit tests
chrisjonesBSU Jan 18, 2024
732670b
add doc strings for target box funcs
chrisjonesBSU Jan 19, 2024
15e0581
add unit test for no refs, add example to doc strings
chrisjonesBSU Jan 19, 2024
6146a4d
create internal module, add doc for utils
chrisjonesBSU Jan 19, 2024
3e8d8fd
delete swp files, fix imports in tests
chrisjonesBSU Jan 22, 2024
893a4ed
remove swp file
chrisjonesBSU Jan 22, 2024
2ec25b3
add swp files to git ignore
chrisjonesBSU Jan 22, 2024
d2cb45e
fix utils rst file
marjanalbooyeh Jan 22, 2024
746b85f
add docstrings
marjanalbooyeh Jan 22, 2024
f76a5c0
finish default density behavior in surface wetting
chrisjonesBSU Jan 22, 2024
a919fed
Merge branch 'number-density' of github.com:chrisjonesBSU/flowerMD in…
chrisjonesBSU Jan 22, 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
2 changes: 1 addition & 1 deletion flowermd/base/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ def run_update_volume(
final_density, u.g / u.cm**3
)
mass_g = self.mass.to("g")
L = calculate_box_length(mass_g, density_quantity)
L = calculate_box_length(mass=mass_g, density=density_quantity)
# convert L from cm to reference units
L = (
L.to(self.reference_length.units) / self.reference_length.value
Expand Down
33 changes: 15 additions & 18 deletions flowermd/base/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,6 @@ class System(ABC):
----------
molecules : flowermd.Molecule or a list of Molecule objects, required
The molecules to be placed in the system.
density : float, required
The desired density of the system (g/cm^3). 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.
base_units : dict, default {}
Dictionary of base units to use for scaling.
Dictionary keys are "length", "mass", and "energy". Values should be an
Expand All @@ -71,11 +66,9 @@ class System(ABC):
def __init__(
self,
molecules,
density: float,
base_units=dict(),
):
self._molecules = check_return_iterable(molecules)
self.density = density
self.all_molecules = []
self.gmso_system = None
self._reference_values = base_units
Expand Down Expand Up @@ -623,7 +616,7 @@ def apply_forcefield(
self._hoomd_snapshot = self._create_hoomd_snapshot()

def set_target_box(
self, x_constraint=None, y_constraint=None, z_constraint=None
self, density, x_constraint=None, y_constraint=None, z_constraint=None
):
"""Set the target box size of the system.

Expand All @@ -633,6 +626,8 @@ def set_target_box(

Parameters
----------
density : float, required
Density used when calculating the lengths (g/cm^3).
x_constraint : float, optional, defualt=None
Fixes the box length (nm) along the x axis.
y_constraint : float, optional, default=None
Expand All @@ -642,13 +637,13 @@ def set_target_box(

"""
if not any([x_constraint, y_constraint, z_constraint]):
Lx = Ly = Lz = self._calculate_L()
Lx = Ly = Lz = self._calculate_L(density=density)
else:
constraints = np.array([x_constraint, y_constraint, z_constraint])
fixed_L = constraints[np.not_equal(constraints, None).nonzero()]
# Conv from nm to cm for _calculate_L
fixed_L *= 1e-7
L = self._calculate_L(fixed_L=fixed_L)
L = self._calculate_L(density=density, fixed_L=fixed_L)
constraints[np.equal(constraints, None).nonzero()] = L
Lx, Ly, Lz = constraints

Expand All @@ -663,7 +658,7 @@ def visualize(self):
"The initial configuraiton has not been created yet."
)

def _calculate_L(self, fixed_L=None):
def _calculate_L(self, density, fixed_L=None):
"""Calculate the box length.

Calculate the required box length(s) given the mass of a system and
Expand All @@ -680,11 +675,11 @@ def _calculate_L(self, fixed_L=None):

"""
mass_quantity = u.unyt_quantity(self.mass, u.g / u.mol).to("g")
density_quantity = u.unyt_quantity(self.density, u.g / u.cm**3)
density_quantity = u.unyt_quantity(density, u.g / u.cm**3)
if fixed_L is not None:
fixed_L = u.unyt_array(fixed_L, u.cm)
L = calculate_box_length(
mass_quantity, density_quantity, fixed_L=fixed_L
mass=mass_quantity, density=density_quantity, fixed_L=fixed_L
)
return L.to("nm").value

Expand All @@ -697,6 +692,11 @@ class Pack(System):

Parameters
----------
density : float, required
The desired density of the system (g/cm^3). 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.
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 @@ -731,17 +731,17 @@ def __init__(
edge=0.2,
overlap=0.2,
):
self.density = density * u.g / (u.cm ** (3))
self.packing_expand_factor = packing_expand_factor
self.edge = edge
self.overlap = overlap
super(Pack, self).__init__(
molecules=molecules,
density=density,
base_units=base_units,
)

def _build_system(self):
self.set_target_box()
self.set_target_box(density=self.density)
system = mb.packing.fill_box(
compound=self.all_molecules,
n_compounds=[1 for i in self.all_molecules],
Expand Down Expand Up @@ -773,7 +773,6 @@ class Lattice(System):
def __init__(
self,
molecules,
density: float,
x: float,
y: float,
n: int,
Expand All @@ -786,7 +785,6 @@ def __init__(
self.basis_vector = basis_vector
super(Lattice, self).__init__(
molecules=molecules,
density=density,
base_units=base_units,
)

Expand Down Expand Up @@ -820,7 +818,6 @@ def _build_system(self):
x_len = bounding_box.lengths[0] + self.x
y_len = bounding_box.lengths[1] + self.y
z_len = bounding_box.lengths[2] + 0.2
self.set_target_box(x_constraint=x_len, y_constraint=y_len)
# Center the lattice in its box
system.box = mb.box.Box(np.array([x_len, y_len, z_len]))
system.translate_to(
Expand Down
1 change: 0 additions & 1 deletion flowermd/library/surfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def __init__(
surface_mol = Molecule(num_mols=1, compound=surface)
super(Graphene, self).__init__(
molecules=[surface_mol],
density=1.0,
base_units=base_units,
)

Expand Down
2 changes: 0 additions & 2 deletions flowermd/tests/base/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,6 @@ def test_lattice_polymer(self, polyethylene):
polyethylene = polyethylene(lengths=2, num_mols=32)
system = Lattice(
molecules=[polyethylene],
density=1.0,
x=1,
y=1,
n=4,
Expand All @@ -807,7 +806,6 @@ def test_lattice_molecule(self, benzene_molecule):
benzene_mol = benzene_molecule(n_mols=32)
system = Lattice(
molecules=[benzene_mol],
density=1.0,
x=1,
y=1,
n=4,
Expand Down
1 change: 0 additions & 1 deletion flowermd/tests/library/test_tensile.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ def test_tensile(self):
pps = PPS(lengths=6, num_mols=32)
system = Lattice(
molecules=[pps],
density=1.0,
x=1.2,
y=1.2,
n=4,
Expand Down
30 changes: 26 additions & 4 deletions flowermd/tests/utils/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,44 @@ def test_validate_ref_value(self):
with pytest.raises(ValueError):
validate_ref_value("test g", u.dimensions.mass)

def test_calculate_box_length(self):
def test_calculate_box_length_mass_density(self):
mass = u.unyt_quantity(4.0, u.g)
density = u.unyt_quantity(0.5, u.g / u.cm**3)
box_length = calculate_box_length(mass, density)
box_length = calculate_box_length(density=density, mass=mass)
assert box_length == 2.0 * u.cm

def test_calculate_box_length_number_density(self):
sigma = 1 * u.nm
n_beads = 100
density = 1 / sigma**3
box_length = calculate_box_length(density=density, n_beads=n_beads)
assert box_length == 100 ** (1 / 3) * u.nm

def test_calculate_box_length_bad_args(self):
mass_density = 1 * u.g / (u.cm**3)
number_density = 1 / (1 * u.nm) ** 3
invalid_density = 1 * u.J / u.kg
with pytest.raises(ValueError):
calculate_box_length(density=mass_density, n_beads=100)
with pytest.raises(ValueError):
calculate_box_length(density=number_density, mass=100)
with pytest.raises(ValueError):
calculate_box_length(density=invalid_density, mass=100)

def test_calculate_box_length_fixed_l_1d(self):
mass = u.unyt_quantity(6.0, u.g)
density = u.unyt_quantity(0.5, u.g / u.cm**3)
fixed_L = u.unyt_quantity(3.0, u.cm)
box_length = calculate_box_length(mass, density, fixed_L=fixed_L)
box_length = calculate_box_length(
mass=mass, density=density, fixed_L=fixed_L
)
assert box_length == 2.0 * u.cm

def test_calculate_box_length_fixed_l_2d(self):
mass = u.unyt_quantity(12.0, u.g)
density = u.unyt_quantity(0.5, u.g / u.cm**3)
fixed_L = u.unyt_array([3.0, 2.0], u.cm)
box_length = calculate_box_length(mass, density, fixed_L=fixed_L)
box_length = calculate_box_length(
mass=mass, density=density, fixed_L=fixed_L
)
assert box_length == 4.0 * u.cm
39 changes: 32 additions & 7 deletions flowermd/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,21 +96,26 @@ def _is_float(num):
)


def calculate_box_length(mass, density, fixed_L=None):
def calculate_box_length(density, mass=None, n_beads=None, fixed_L=None):
"""Calculates the required box length(s) given the
mass of a sytem and the target density.

Box edge length constraints can be set by set_target_box().
If constraints are set, this will solve for the required
lengths of the remaining non-constrained edges to match
the target density.
#TODO: Add example of using box constraints

Parameters
----------
mass : unyt.unyt_quantity, required
Mass of the system
density : unyt.unyt_quantity, required
Target density of the system
mass : unyt.unyt_quantity, required
Mass of the system.
Use when using mass density rather than number density.
n_beads : int, optional
Number of beads in the system.
Use when using number density rather than mass density.
fixed_L : np.array, optional, defualt=None
Array of fixed box lengths to be accounted for
when solving for L
Expand All @@ -120,13 +125,33 @@ def calculate_box_length(mass, density, fixed_L=None):
L : float
Box edge length
"""

vol = mass / density # cm^3
# Check units of density
mass_density = u.Unit("kg") / u.Unit("m**3")
number_density = u.Unit("m**-3")
if density.units.dimensions == mass_density.dimensions:
if not mass:
raise ValueError(
f"The given density has units of {mass_density.dimensions} "
"but the mass is not given."
)
vol = mass / density
elif density.units.dimensions == number_density.dimensions:
if not n_beads:
raise ValueError(
f"The given density has units of {number_density.dimensions} "
"but the number of beads is not given."
)
vol = n_beads / density
else:
raise ValueError(
f"Density units of {density.units.dimensions} were given. "
f"Only mass density ({mass_density.units.dimensions}) and "
f"number density ({number_density.dimensions}) are supported."
)
if fixed_L is None:
L = vol ** (1 / 3)
else:
L = vol / np.prod(fixed_L)
if fixed_L.size == 1: # L is cm^2
if fixed_L.size == 1: # L is units of area
L = L ** (1 / 2)
# L is cm
return L