-
Notifications
You must be signed in to change notification settings - Fork 80
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
Added custom box passing when saving #330
Conversation
Modified `to_parmed()` function to accept custom box sizes. Modified `saver()` to accept custom box sizes Modified test_compound to accept custom boxes.
Looks good, thanks! Will merge once you've got some appropriate unit tests in there to make sure the info makes it all the way through to parmed and the saved files. |
Ran into a few limitations when trying to save/load for my unit tests. 1. Cannot load `.lammps` data files. - Seems not an issue, contains a lot of information not really needed 2. HOOMDXML unit conversion on loading - Writing out `.hoomdxml` files converts box parameters to Angstroms - when loading back in, units stay in Angstroms - Unsure how to correct, since we are delegating the loading to mdtraj 2.1 HOOMDXML box custom issues - possibly incorrectly moving to origin? - just a padded box has correct lengths, but angstroms - custom box has wildly incorrect lengths - unsure what is causing it - custom box parameters are affecting bounding box upon loading - still a mystery at the moment 3. Hard to test every file that parmed and mbuild can save to - Not every file type we write out can be loaded back in: (`.lammps`, `.gsd`, etc.) - Unsure how to check using tests within mBuild to ensure data we write out is correct - havent really tested using unit tests if the data we write out in the mBuild supported savers are correct (at least in the tests region). 4. Box seems pretty rudimentary - Possible issue to convert to a triclinic system? - lots of assumptions about the boxes in the savers (angles = 90, etc.) - might be a pretty hefty overhaul of the `Box` class 5. Units conversions everywhere - no real method to the madness in terms of when the units are converted from nm to Angstroms - Since `save()` is the way to output files, all unit conversions there? - Assuming using save is the only way to output files - would make sense to enforce this? Things to Note 1. Lots of print statements in here now - Still very much a WIP - want to get some feedback and discussion 2. WIP - I have my tests failing on purpose at the moment to observe outputs. 3. For `parmed` supported structures and `mdtraj` supported loadable files - Seem to work properly - unsure even angstrom to nm conversion is correct? - maybe `.hoomdxml` just a fluke in the loading using `mdtraj`?
Since commit message is a bit hard to read, posting it here, since it will also be useful for the questions I have. Ran into a few limitations when trying to save/load for my unit tests.
2.1 HOOMDXML box custom issues
Things to Note
|
Removed wrapping issue from hoomdxml. Bounding box preserves proper length of box based on particle positions. Custom box support to allow smaller or larger boxes to write to data files.
Had extraneous arguments in the load function. **kwargs handles this.
Can you resolve the merge conflicts here? Need to get this fix in sooner rather than later. |
@justinGilmer will you have time to fix this up today? |
Error where box was not being properly set, causing DIV0 errors. Maxs and mins of `Box` class was not being set correctly, not updating lengths, which was essential for `to_parmed()` to function properly.
I think all the issues have been ironed out. Just waiting for the CI's to finish. |
mbuild/compound.py
Outdated
@@ -1304,16 +1304,18 @@ def save(self, filename, show_ports=False, forcefield_name=None, | |||
# pad non periodic faces with .25nm buffers | |||
if box is None: | |||
box = self.boundingbox | |||
box_vec_max = box.maxs.tolist() | |||
box_vec_min = box.maxs.tolist() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should definitely be box.mins
but could you explain why the list cast is necessary here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue was when we were just changing these values, the setter was not being called it seemed. box.lengths was not being updated. At least with the 2 single residue test case. The best way I could see would be to just unwrap that numpy array and perform the operations on it. Then pass it back as one numpy array which seemed to update box.lengths as well.
EDIT:
TLDR, it seemed that changing in place the numpy array values would not force the box setter to update the lengths. By converting to a list so it could be operated on and then passing that back in as a numpy array, that seemed to alleviate this issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was just that the residue case was very different. It had no bounding box essentially. It was a [0,0,0] box. Which seemed to affect the setters.
EDIT:
I see what you mean about the two max calls. Fixing now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that makes sense. The setters don't work for elements of the array getting changed. Probably not worth tackling until the box rewrite.
box mins list was assigned the maxs of the box.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, thanks! Just some final tidying up and it's good to go.
mbuild/tests/test_compound.py
Outdated
for attr in box_attributes: | ||
pad_attr = getattr(padded_ch3.boundingbox, attr) | ||
custom_attr = getattr(custom_ch3.boundingbox, attr) | ||
assert(np.array_equal(pad_attr, custom_attr)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert is a statement so parentheses should be removed.
mbuild/compound.py
Outdated
**kwargs : keyword arguments | ||
Arbitrary keyword arguments. | ||
|
||
Args: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These args belong in the save function where I believe they already are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, load is it's own separate function, outside of Compound
. Since save
and its **kwargs
are passed to other savers and such, would it be reasonable to have both defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you asking if both *args
and **kwargs
should be defined here? *args
gets a bit tricky because it cares about order. **kwargs
could have a bit more description like "arguments passed down to mdtraj".
That being said, my original comment was about the content of the Args:
section that I tagged. ref_distance/mass/energy
are not arguments relevant to any of the loaders. They only apply to the hoomd saver so they shouldnt be in the docstring for the load function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah gotcha, that makes sense. And youre correct, I was recalling the source code for mdTraj incorrectly. Just looked it up and I understand it now.
mbuild/tests/test_compound.py
Outdated
@@ -393,10 +407,10 @@ def test_intermol_conversion2(self, ethane, h2o): | |||
def test_parmed_conversion(self, ethane, h2o): | |||
compound = mb.Compound([ethane, h2o]) | |||
|
|||
structure = compound.to_parmed() | |||
structure = compound.to_parmed(box=compound.boundingbox) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default right now will be the boundingbox plus a buffer is nothing specified, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is if they call save()
. If they just call to_parmed
, that is not currently happening.
Unsure how we would want to handle this actually. Since we were wanting the padding to be handled by the saver. I guess I could check if the box passed in is None
, if so, pad and move on. That would handle use cases when just calling this function. Since save()
does the padding before passing that box to to_parmed()
Incorrect description of arguments that could be passed to load function.
Almost done! Just gotta remove the parentheses around that assert statement. |
to_parmed now supports generation of padded boxes without calling save(). Can also pass in custom box as well. If no box passed in, will default to a .25nm buffer on all sides of box.
box_vec_max[dim] += 0.25 | ||
box_vec_min[dim] -= 0.25 | ||
box.mins = np.asarray(box_vec_min) | ||
box.maxs = np.asarray(box_vec_max) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is duplicated now. I think you can re-organize this so it only exists in to_parmed
1. Modified to_parmed() to handle the box padding. 2. save() no longer handles box padding 3. Modified the 3 mbuild data file writers (hoomd, lammps, gsd) to also use the parmed structure for box information.
Changed mBuild file writers to instantiate the box in 1 line.
mbuild/formats/hoomdxml.py
Outdated
forcefield = True | ||
if structure[0].type == '': | ||
forcefield = False | ||
xyz = np.array([[atom.xx, atom.xy, atom.xz] for atom in structure.atoms]) | ||
|
||
box.lengths = np.array([structure.box[0], structure.box[1], structure.box[2]]) | ||
box.maxs *= 10. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to delete this?
Deleted angstroms conversion of the box maxs member.
Looped over 3 dimensions instead of enumerating the periodicity.
Added custom box passing when saving
Modified
to_parmed()
function to accept custom box sizes.Modified
saver()
to accept custom box sizesModified test_compound to accept custom boxes.
I need to add more tests to ensure that the data is properly saved and converted to Angstroms.
Made
box
a required argument forto_parmed()
.