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

Packing fixes #385

Merged
merged 27 commits into from
Nov 13, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7056e92
Add edge buffer in packing functions
mattwthompson Sep 7, 2017
3bef5a8
Support aspect ratio for generated boxes
mattwthompson Sep 7, 2017
a2d0b7d
Start working with compound_ratio
mattwthompson Sep 7, 2017
7b5472f
Support compound_ratio and add test
mattwthompson Sep 7, 2017
1f8504e
Cleanup
mattwthompson Sep 7, 2017
ea5ef80
Actually build n_compounds and test it
mattwthompson Sep 9, 2017
dff4b1c
Ensure n_compounds contains ints if list
mattwthompson Sep 10, 2017
85af741
Add docstrings for packing functions
mattwthompson Sep 21, 2017
20ae518
Merge branch 'pack-fixes' of https://github.com/mattwthompson/mbuild …
mattwthompson Sep 21, 2017
ac28876
Test for bad combinations of input parameters
mattwthompson Oct 14, 2017
3af9378
Raise an error if compound and n_compounds are of different len
mattwthompson Oct 14, 2017
b806d3c
Add optional flag for writing packmol output
mattwthompson Oct 19, 2017
5d7ed28
Copy packmol PDB directly; don't write from mbuild compound
mattwthompson Oct 19, 2017
87c4739
First attempt at handling packmol errors
mattwthompson Oct 19, 2017
91a203e
Merge branch 'pack-fixes-2' of https://github.com/mattwthompson/mbuil…
mattwthompson Oct 19, 2017
ae715c7
Merge branch 'pack-tempfile' into pack-fixes-2
mattwthompson Oct 19, 2017
19e8940
Reorder arguments to match previous versions
mattwthompson Oct 20, 2017
ac7938a
Force ruby 2.3 for homebrew (#392)
mattwthompson Oct 20, 2017
08480eb
Test packmol error and warning
mattwthompson Oct 20, 2017
a87c47a
Merge branch 'master' into pack-fixes
mattwthompson Nov 9, 2017
c38dc36
Small requested fixes
mattwthompson Nov 11, 2017
f2747db
Add _run_packmol helper function and rework tempfile
mattwthompson Nov 11, 2017
018aafb
Add new arguments to some docstrings
mattwthompson Nov 11, 2017
94aec64
Add _check_packmol helper function
mattwthompson Nov 11, 2017
3c312d5
Test temp_file behavior in each packing function
mattwthompson Nov 11, 2017
b583953
Cleanup some docstrings
mattwthompson Nov 11, 2017
cc2a436
Cleanup in fill_region: applying edge buffer & docstrings
mattwthompson Nov 11, 2017
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
52 changes: 40 additions & 12 deletions mbuild/packing.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"""


def fill_box(compound, n_compounds=None, box=None, density=None, overlap=0.2, seed=12345):
def fill_box(compound, n_compounds=None, box=None, aspect_ratio=None,
density=None, compound_ratio=None, overlap=0.2, edge=0.2, seed=12345):
"""Fill a box with a compound using packmol.

Two arguments of `n_compounds, box, and density` must be specified.
Expand All @@ -52,20 +53,24 @@ def fill_box(compound, n_compounds=None, box=None, density=None, overlap=0.2, se
If `box` and `density` are not None, the corresponding number of
compounds will be calculated internally.

For the cases in which `box` is not specified but generated internally,
the default behavior is to calculate a cubic box. Optionally,
`aspect_ratio` can be passed to generate a non-cubic box.

Parameters
----------
compound : mb.Compound or list of mb.Compound
n_compounds : int or list of int
box : mb.Box
aspect_ratio : list of float
overlap : float, units nm
edge : float, units nm
density : float, units kg/m^3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add descriptions for each of these arguments in the docstring (I know several of these existed already without a description)?


Returns
-------
filled : mb.Compound

TODO : Support aspect ratios in generated boxes
TODO : Support ratios of n_compounds
"""
if not PACKMOL:
msg = "Packmol not found."
Expand Down Expand Up @@ -93,22 +98,42 @@ def fill_box(compound, n_compounds=None, box=None, density=None, overlap=0.2, se
for c,n in zip(compound, n_compounds)])
# Conversion from (amu/(kg/m^3))**(1/3) to nm
L = (total_mass/density)**(1/3)*1.1841763
box = _validate_box(Box(3*[L]))
if n_compounds is None and box is not None:
if len(compound) > 1:
msg = ("Determing `n_compounds` from `density` and `box` "
"currently only supported for systems with one "
"compound type.")
raise ValueError(msg)
if aspect_ratio is None:
box = _validate_box(Box(3*[L]))
else:
L *= np.prod(aspect_ratio) ** (-1/3)
box = _validate_box(Box([val*L for val in aspect_ratio]))
if n_compounds is None and box is not None:
if len(compound) == 1:
compound_mass = np.sum([a.mass for a in compound[0].to_parmed().atoms])
# Conversion from kg/m^3 / amu * nm^3 to dimensionless units
n_compounds = [int(density/compound_mass*np.prod(box.lengths)*.60224)]
else:
if compound_ratio is None:
msg = ("Determing `n_compounds` from `density` and `box` "
"for systems with more than one compound type requires"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add indentation here? Makes it visually clear that it's a continuation of the previous line even though it's not a syntax error in this case.

"`compound_ratio`")
raise ValueError(msg)
if len(compound) != len(compound_ratio):
msg = ("Length of `compound_ratio` must equal length of "
"`compound`")
raise ValueError(msg)
prototype_mass = 0
for c, r in zip(compound, compound_ratio):
prototype_mass += r * np.sum([a.mass for a in c.to_parmed().atoms])
# Conversion from kg/m^3 / amu * nm^3 to dimensionless units
n_prototypes = int(density/prototype_mass*np.prod(box.lengths)*.60224)
print(n_prototypes)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stray debug output (add these checks to the tests)

print(compound_ratio)
n_compounds = n_prototypes * compound_ratio

# In angstroms for packmol.
box_mins = box.mins * 10
box_maxs = box.maxs * 10
overlap *= 10

# Apply edge buffer
box_maxs = box_maxs - edge * 10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here as below about always applying edge buffer.


# Build the input file for each compound and call packmol.
filled_pdb = tempfile.mkstemp(suffix='.pdb')[1]
Expand Down Expand Up @@ -137,7 +162,7 @@ def fill_box(compound, n_compounds=None, box=None, density=None, overlap=0.2, se
return filled


def fill_region(compound, n_compounds, region, overlap=0.2, seed=12345):
def fill_region(compound, n_compounds, region, overlap=0.2, edge=0.2, seed=12345):
"""Fill a region of a box with a compound using packmol.

Parameters
Expand Down Expand Up @@ -203,7 +228,7 @@ def fill_region(compound, n_compounds, region, overlap=0.2, seed=12345):
return filled


def solvate(solute, solvent, n_solvent, box, overlap=0.2, seed=12345):
def solvate(solute, solvent, n_solvent, box, overlap=0.2, edge=0.2, seed=12345):
"""Solvate a compound in a box of solvent using packmol.

Parameters
Expand Down Expand Up @@ -233,6 +258,9 @@ def solvate(solute, solvent, n_solvent, box, overlap=0.2, seed=12345):
box_maxs = box.maxs * 10
overlap *= 10
center_solute = (box_maxs + box_mins) / 2

# Apply edge buffer
box_maxs = box_maxs - edge * 10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always want to add this buffer? This is changing prior behavior so need to be a bit careful

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should exist as default. Currently it is easy to create a box that has overlaps across the PBC because packmol doesn't. This should really be a default behavior in packmol itself, imo, but they instead give an optional add_box_sides argument. I prefer a buffer approach because if we have a volume inside we we want molecules to be placed, it makes sense to add the buffer inside of the box than increasing the box size. If we want to hash out a list of uses cases of this function I could dig into that, but this handles all the cases that I am working with at the moment and otherwise come to mind.


# Build the input file for each compound and call packmol.
solvated_pdb = tempfile.mkstemp(suffix='.pdb')[1]
Expand Down
10 changes: 10 additions & 0 deletions mbuild/tests/test_packing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,21 @@ def test_fill_box_density_box(self, h2o):
filled = mb.fill_box(h2o, n_compounds=1000, density=1000)
assert [3.1042931 < period < 3.1042932 for period in filled.periodicity]

def test_fill_box_aspect_ratio(self, h2o):
filled = mb.fill_box(h2o, n_compounds=1000,
density=1000, aspect_ratio=[1, 2, 1])
assert filled.periodicity[0]/filled.periodicity[1] == 0.5
assert filled.periodicity[1]/filled.periodicity[2] == 2

def test_fill_box_density_n_compounds(self, h2o):
filled = mb.fill_box(h2o, density=1000,
box=mb.Box([3.1042931, 3.1042931, 3.1042931]))
assert filled.n_particles == 3000

def test_fill_box_compound_ratio(self, h2o, ethane):
filled = mb.fill_box(compound=[h2o, ethane], density=800,
compound_ratio=[2, 1], box=[2, 2, 2, 4, 4, 4])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also assert here that you indeed have placed h2o/ethane in the correct ratios?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well that test looks silly. But it would have failed anway if it that assertion existed. Now should be passing!


def test_fill_region(self, h2o):
filled = mb.fill_region(h2o, n_compounds=50,
region=[3, 2, 2, 4, 4, 3])
Expand Down