-
Notifications
You must be signed in to change notification settings - Fork 80
mBuild 2.0 polymer and simulation improvements: Introduce Path class, Polymer.build_from_path() and add new HOOMD-Blue simulation methods.
#1261
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
base: develop
Are you sure you want to change the base?
Conversation
updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.8 → v0.12.9](astral-sh/ruff-pre-commit@v0.12.8...v0.12.9)
…te-config [pre-commit.ci] pre-commit autoupdate
updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.8 → v0.12.9](astral-sh/ruff-pre-commit@v0.12.8...v0.12.9)
Transition to logging instead of warnings module
…ds, fixes to CompoundRandomWalk (tbd if this stays)
updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.9 → v0.12.10](astral-sh/ruff-pre-commit@v0.12.9...v0.12.10)
…te-config [pre-commit.ci] pre-commit autoupdate
… setting graph edges as the path is built
| from mbuild.coordinate_transform import _rotate, _translate | ||
| from mbuild.exceptions import MBuildError | ||
| from mbuild.periodic_kdtree import PeriodicKDTree | ||
| from mbuild.utils.geometry import bounding_box |
Check notice
Code scanning / CodeQL
Cyclic import Note
mbuild.utils.geometry
| ) | ||
| return thetas, r | ||
|
|
||
| def _initial_points(self): |
Check notice
Code scanning / CodeQL
Explicit returns mixed with implicit (fall through) returns Note
| self.mins = self.center - np.array([Lx / 2, Ly / 2, Lz / 2]) | ||
| self.maxs = self.center + np.array([Lx / 2, Ly / 2, Lz / 2]) | ||
|
|
||
| def is_inside(self, points, buffer): |
Check warning
Code scanning / CodeQL
Signature mismatch in overriding method Warning
overridden method
| self.mins = self.center - self.radius | ||
| self.maxs = self.center + self.radius | ||
|
|
||
| def is_inside(self, points, buffer): |
Check warning
Code scanning / CodeQL
Signature mismatch in overriding method Warning
overridden method
| ] | ||
| ) | ||
|
|
||
| def is_inside(self, points, buffer): |
Check warning
Code scanning / CodeQL
Signature mismatch in overriding method Warning
overridden method
| __all__ = ["Polymer"] | ||
|
|
||
|
|
||
| class Monomer(Compound): |
Check failure
Code scanning / CodeQL
Missing call to superclass `__init__` during object initialization Error
This PR begins the first efforts towards development of mBuild 2.0 (i.e., we can break things), and focuses mostly on improving the polymer-building experience in mBuild as discussed in #1205 and #1212. There are also quite a few changes that introduce more energy minimization methods that utilize HOOMD-Blue, and can run on GPU for larger systems and/or HPC workflows. I didn't mean for so many changes to be included in this PR, but as I worked on this, more and more needs kept creeping up. This PR isn't necessarily the completed effort on the area of initializing polymer systems and adding more feature-rich energy minimization approaches, but it's close to a good stopping place for now. I'll try to summarize the most notable changes. This won't be merged to the
mainbranch, but instead will be the first commit on thedevelopbranch.Addition of the
mbuild.path.PathclassThis introduces a new data structure for creating "paths" (i.e., molecular configurations). The primary purpose of this class is to generate better starting polymer-chain configurations, as opposed to the wonky ones you often get by default from the
Polymer.build()method.The basic idea is that a path represents a coarse-grained configuration that atomistic polymers can be mapped onto during the build step. Most of the classes include non-random, deterministic paths (3-D lamellar structures, ring polymers, etc.), but there is a
HardSphereRandomWalkpath as well.Here are some examples of the idea and API behind this:
Build a 3D lamellar polymer by specifying parameters such as layer length, separation, number of layers and stacks (3D):
Build a polymer from a random walk:
HardSphereRandomWalkandmbuild.utils.volume.Constraint:Some highlights of this Path class specifically:
mbuild.Compound.volumeis added in this PR, using RDKit)mbuild.utils.volume.Constraint. Right now this PR addsCuboidConstraint,SphereConstraintandCylinderConstraint. These objects can be passed intoHardSphereRandomWalk, making it easy to add to and extend these constraints separately from the random walk logic.mbuild.path.Pathinstance.Lamellarpath, then run a random walk from the last site. The resulting path includes all the coordinates of the first, as well as the results of the random walk. This can be used to start constructing semi-crystalline morphologies.trial_batch_sizeparameter). This should help constrained random walks finish, but possibly at a performance cost. I haven't had a chance to profile this one yet.Here are some examples:
Stitching together a lamellar path and random walk
Including a volume constraint in the random walk
mbuild.simulation module
We already had a pretty great set of simulation methods for performing energy minimization. This PR pulled all of that code out of
compound.pyand added it to asimulation.py. Now, you pass the compound into these methods rather than calling it as a class method (e.g,energy_minimize(compound)instead ofcompound.energy_minimize()). This trims down theCompoundclass quite a bit (~700 lines of code).This PR also adds a few things that allows us to use HOOMD in addition to OpenBabel and OpenMM. The main new addition here is a wrapper class around HOOMD's own simulation class. This makes it easy to design more specific workflows that can go beyond energy minimization. For example, a quick compress simulation is planned for a future PR, which can be ran after the
packing.pymethods. Hopefully, this approach makes it easy for users to make their own quick simulation protocols when needed, and contribute upstream when possible.This PR adds 2 methods that use this HOOMD base class:
hoomd_cap_displacement: This uses the capped displacement method in HOOMD
hoomd_fire: This uses HOOMD's implementation of the FIRE algorithm.
These can both be better options for the energy minimization demands of building larger polymers from paths. They can use the GPU if available, and even on CPU, they are much faster than Openbabel after certain system sizes.
hoomd_cap_displacementwas able to optimize the geometry for a polyethylene 200mer in about the same amount of time it took openbabel to optimize an 80mer (around 12 seconds). They were nearly equally efficient at smaller system sizes.The biggest bottleneck here is the lack of support for universal forcefields (e.g., UFF) that aren't hampered by missing parameters. For now, we are keeping the openbabel stuff in, even though there are some upstream issues with using it as a dependency. Also, we are planning on keeping and expanding the OpenMM energy minimization as well. I might look at making it more modular in the future, similar to the HOOMD implementation.
One important feature of the HOOMD implementation is that all the simulation information is saved, making it faster to call these methods multiple times (e.g., in a for loop or while loop) while skipping repeated calls to the under-the-hood steps of atom typing, applying the forcefield, writing out HOOMD data structures, etc.
For example run
hoomd_cap_displacementin small increments with a condition to stop once all overlaps are removed:Combining Everything:
Here is the result of combining some of the things discussed above. Here, in a while loop, I ran 50 random walks of 25 steps each in a cubic box with 5nm edges. From each random walk, a polyethylene chain was built, and$\approx 0.5 \frac{g}{cm^3}$ . This is significantly better than using PACKMOL to pack a huge box with long, straight 25mers. This took ~3-4 minutes total.
hoomd_cap_displacementwas ran on the system of 50 polymers for 2,000 steps to relax bonds, angles and remove any overlaps. The result is a distribution of initial chain configurations packed at a density ofRemaining To-dos:
Some of these may or may not be included in this PR
General:
Path and Polymer.build_from_path():
Polymer.build_from_pathorPath.apply_backmapping().nx.path_graph()), but everything we need to support things like branching polymers is there, it just needs to be implemented. Cross-linking will require some extra thinking and design. As of 45a6eed, bond graphs are built on the fly, and include ability to track branching paths.from_box()class method forCuboidConstraintso one can be created from a box of another compound. This would be nice when using an existing mBuild compound in a random walk that also has volume constraints.simulation.py:
simulation.pysimulation.pythat uses Hoomd's box updater.Some discussion points
Pretty much everything in
path.py,density.pyandvolumes.pyis completely separate from theCompoundandPolymerdata structures. It kind of makes me wonder if there would be some utility inpath.py,density.pyandvolumes.pybeing in a separate MoSDeF package? It creates some overhead with handling an additional package, but it also keeps mBuild's code-base cleaner and more concise, and opens up more flexibility for further development aroundPathandConstraintthat isn't necessarily limited by mBuild's existing design and data structures. I'm not opposed to it, but also not opposed to keeping all of this in mBuild. Just something to think about.With these additions, there are a lot of workflows that become possible by essentially creating another Monte Carlo like layer of choices (e.g., stringing together multiple random walks, random branching from other random walks, random walks of different lengths to achieve a certain polydispersity, etc..). Once the new features in this PR, and subsequent PRs, become more fleshed-out, it might be worth thinking about adding some recipes/wrappers that implement these at a higher level.