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 classes for simulating ellipsoid polymer systems. #102

Merged
merged 58 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
f2fa27b
create EllipsoidChain class
chrisjonesBSU Nov 9, 2023
0f62383
remove blank space
chrisjonesBSU Nov 9, 2023
953242b
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Nov 13, 2023
d42e8a7
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Nov 14, 2023
2aa4308
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Nov 30, 2023
f490cfd
add class for ellipsoid chain FF
chrisjonesBSU Nov 30, 2023
498cdc8
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Nov 30, 2023
97aff78
add FF class to library
chrisjonesBSU Dec 1, 2023
c6b8326
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 1, 2023
20c69c1
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Dec 4, 2023
4af2795
update particle mass, add bead particle types sequence
marjanalbooyeh Dec 7, 2023
2460a7f
update class parent name
marjanalbooyeh Dec 7, 2023
fb0708f
add func to create rigid frame
marjanalbooyeh Dec 7, 2023
dca5a86
fix formatting, remove extra param
marjanalbooyeh Dec 7, 2023
e8247bc
fix numpy bug, add box to config
marjanalbooyeh Dec 7, 2023
50819ee
fix rigid body sim bugs
marjanalbooyeh Dec 7, 2023
52cdea3
add bonds between B particles after building the chain
chrisjonesBSU Dec 8, 2023
cb70367
add fix_orientation variable to pack
marjanalbooyeh Dec 8, 2023
e2aab60
add bond, angle and dihedral
marjanalbooyeh Dec 8, 2023
6a088c7
Merge branch 'ellipsoids' of github.com:chrisjonesBSU/flowerMD into e…
marjanalbooyeh Dec 8, 2023
fe25155
remove create_rigid_body from sim
marjanalbooyeh Dec 8, 2023
0c09d82
add body tags to rigid frame
marjanalbooyeh Dec 8, 2023
963b8cf
add new functions to __init__
marjanalbooyeh Dec 8, 2023
42a8f91
add unit tests
marjanalbooyeh Dec 8, 2023
ca67661
move private func to bottom
marjanalbooyeh Dec 8, 2023
394be08
add doc strings
chrisjonesBSU Dec 8, 2023
2b49384
add example code to docstring
marjanalbooyeh Dec 8, 2023
418bc29
replace bead_length with lpar in EllipsoidChain class
chrisjonesBSU Dec 11, 2023
e635f13
add rigid body handling of mass
chrisjonesBSU Dec 11, 2023
0c719c6
add overlap to Pack
chrisjonesBSU Dec 11, 2023
1e9cd6f
remove bead_length from Ellipsoid FF class, fix unit test
chrisjonesBSU Dec 12, 2023
74eae9d
fix rigid unit test
chrisjonesBSU Dec 12, 2023
9d963fe
add 2 unit tests for rigid simulations
chrisjonesBSU Dec 12, 2023
375f40c
fix check for body tags in mass, add assertion for mass
chrisjonesBSU Dec 13, 2023
72fdfba
ignore E203 in precommit
chrisjonesBSU Dec 13, 2023
b37cb0d
remove unused var
chrisjonesBSU Dec 13, 2023
3b382c6
use >= when checking for ascending next body tag
chrisjonesBSU Dec 13, 2023
adfa064
actaully, we shouldn't use >= in body tag count
chrisjonesBSU Dec 14, 2023
7e4cbf4
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Dec 15, 2023
f76027d
Merge branch 'ellipsoids' of github.com:chrisjonesBSU/flowerMD into e…
chrisjonesBSU Dec 15, 2023
ba234a0
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Dec 18, 2023
f8f5673
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Dec 19, 2023
3eacfba
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Dec 21, 2023
6c134e4
merge and fix conflicts with main
chrisjonesBSU Dec 22, 2023
4bedece
fix conflicts
chrisjonesBSU Feb 19, 2024
347b331
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 19, 2024
b8279fb
fix rigid body import
chrisjonesBSU Feb 25, 2024
319e4a3
fix conflicts, merge with upstream
chrisjonesBSU Mar 8, 2024
76f5bae
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Mar 8, 2024
3121b59
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Mar 21, 2024
da7eff1
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Mar 26, 2024
c4abc75
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Apr 2, 2024
b09f86e
Switch to z-axis for ellipsoid chain alignment, add param for initial…
chrisjonesBSU Apr 3, 2024
94488b8
Merge branch 'ellipsoids' of github.com:chrisjonesBSU/flowerMD into e…
chrisjonesBSU Apr 3, 2024
ef833c9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 3, 2024
6e33305
fix test
chrisjonesBSU Apr 8, 2024
a17986d
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Apr 8, 2024
15041a1
Merge branch 'main' of github.com:cmelab/flowerMD into ellipsoids
chrisjonesBSU Apr 9, 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 .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ repos:
- id: flake8
args:
- --max-line-length=80
- --extend-ignore=E203
exclude: '__init__.py'

- repo: https://github.com/pycqa/pydocstyle
Expand Down
38 changes: 35 additions & 3 deletions flowermd/base/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def __init__(
log_write_freq=1e3,
log_file_name="sim_data.txt",
thermostat=HOOMDThermostats.MTTK,
rigid_constraint=None,
):
if not isinstance(forcefield, Iterable) or isinstance(forcefield, str):
raise ValueError(
Expand Down Expand Up @@ -103,7 +104,17 @@ def __init__(
self._dt = dt
self._reference_values = dict()
self._reference_values = reference_values
self._integrate_group = hoomd.filter.All()
if rigid_constraint and not isinstance(
rigid_constraint, hoomd.md.constrain.Rigid
):
raise ValueError(
"Invalid rigid constraint. Please provide a "
"hoomd.md.constrain.Rigid object."
)
self._rigid_constraint = rigid_constraint
self._integrate_group = self._create_integrate_group(
rigid=True if rigid_constraint else False
)
self._wall_forces = dict()
self._create_state(self.initial_state)
# Add a gsd and thermo props logger to sim operations
Expand Down Expand Up @@ -289,7 +300,16 @@ def volume(self):
def mass_reduced(self):
"""The total mass of the system in reduced units."""
with self.state.cpu_local_snapshot as snap:
return sum(snap.particles.mass)
if self._rigid_constraint:
last_body_tag = -1
for body_tag in snap.particles.body:
if body_tag > last_body_tag:
last_body_tag += 1
else:
break
return sum(snap.particles.mass[last_body_tag + 1 :])
else:
return sum(snap.particles.mass)

@property
def mass(self):
Expand Down Expand Up @@ -529,7 +549,14 @@ def set_integrator_method(self, integrator_method, method_kwargs):

"""
if not self.integrator: # Integrator and method not yet created
self.integrator = hoomd.md.Integrator(dt=self.dt)
self.integrator = hoomd.md.Integrator(
dt=self.dt,
integrate_rotational_dof=(
True if self._rigid_constraint else False
),
)
if self._rigid_constraint:
self.integrator.rigid = self._rigid_constraint
self.integrator.forces = self._forcefield
self.operations.add(self.integrator)
new_method = integrator_method(**method_kwargs)
Expand Down Expand Up @@ -1098,6 +1125,11 @@ def _lj_force(self):
][0]
return lj_force

def _create_integrate_group(self, rigid):
if rigid:
return hoomd.filter.Rigid(("center", "free"))
return hoomd.filter.All()

def _create_state(self, initial_state):
"""Create the simulation state.

Expand Down
5 changes: 5 additions & 0 deletions flowermd/base/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,8 @@ class Pack(System):
The factor by which to expand the box for packing.
edge : float, default 0.2
The space (nm) between the edge of the box and the molecules.
overlap : float, default 0.2
Minimum separation (nm) between particles of different molecules.


.. warning::
Expand Down Expand Up @@ -653,6 +655,7 @@ def __init__(
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")
Expand All @@ -665,6 +668,7 @@ def __init__(
self.packing_expand_factor = packing_expand_factor
self.edge = edge
self.overlap = overlap
self.fix_orientation = fix_orientation
super(Pack, self).__init__(molecules=molecules, base_units=base_units)

def _build_system(self):
Expand Down Expand Up @@ -692,6 +696,7 @@ def _build_system(self):
box=list(target_box * self.packing_expand_factor),
overlap=self.overlap,
edge=self.edge,
fix_orientation=self.fix_orientation,
)
return system

Expand Down
2 changes: 2 additions & 0 deletions flowermd/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
BaseHOOMDForcefield,
BaseXMLForcefield,
BeadSpring,
EllipsoidForcefield,
FF_from_file,
KremerGrestBeadSpring,
TableForcefield,
Expand All @@ -17,6 +18,7 @@
PEEK,
PEKK,
PPS,
EllipsoidChain,
LJChain,
PEKK_meta,
PEKK_para,
Expand Down
89 changes: 89 additions & 0 deletions flowermd/library/forcefields.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,92 @@ def _check_widths(self):
"number of points for table energies and forces."
)
return bond_width, angle_width, dih_width


class EllipsoidForcefield(BaseHOOMDForcefield):
"""A forcefield for modeling anisotropic bead polymers.

Notes
-----
This is designed to be used with `flowermd.library.polymers.EllipsoidChain`
and uses ghost particles of type "A" and "B" for intra-molecular
interactions of bonds and two-body angles.
Ellipsoid centers (type "R") are used in inter-molecular pair interations.

The set of interactions are:
1. `hoomd.md.bond.Harmonic`: Models ellipsoid bonds as tip-to-tip bonds
2. `hoomd.md.angle.Harmonic`: Models angles of two neighboring ellipsoids.
3. `hoomd.md.pair.aniso.GayBerne`" Model pair interactions between beads.

Parameters
----------
epsilon : float, required
energy
lpar: float, required
Semi-axis length of the ellipsoid along the major axis.
lperp : float, required
Semi-axis length of the ellipsoid along the minor axis.
r_cut : float, required
Cut off radius for pair interactions
bond_k : float, required
Spring constant in harmonic bond
bond_r0: float, required
Equilibrium tip-to-tip bond length.
angle_k : float, required
Spring constant in harmonic angle.
angle_theta0: float, required
Equilibrium angle between 2 consecutive beads.

"""

def __init__(
self,
epsilon,
lpar,
lperp,
r_cut,
bond_k,
bond_r0,
angle_k=None,
angle_theta0=None,
):
self.epsilon = epsilon
self.lperp = lperp
self.lpar = lpar
self.r_cut = r_cut
self.bond_k = bond_k
self.bond_r0 = bond_r0
self.angle_k = angle_k
self.angle_theta0 = angle_theta0
hoomd_forces = self._create_forcefield()
super(EllipsoidForcefield, self).__init__(hoomd_forces)

def _create_forcefield(self):
forces = []
# Bonds
bond = hoomd.md.bond.Harmonic()
bond.params["A-A"] = dict(k=self.bond_k, r0=self.bond_r0)
bond.params["B-B"] = dict(k=0, r0=self.lpar)
forces.append(bond)
# Angles
if all([self.angle_k, self.angle_theta0]):
angle = hoomd.md.angle.Harmonic()
angle.params["B-B-B"] = dict(k=self.angle_k, t0=self.angle_theta0)
forces.append(angle)
# Gay-Berne Pairs
nlist = hoomd.md.nlist.Cell(buffer=0.40)
gb = hoomd.md.pair.aniso.GayBerne(nlist=nlist, default_r_cut=self.r_cut)
gb.params[("R", "R")] = dict(
epsilon=self.epsilon, lperp=self.lperp, lpar=self.lpar
)
# Add zero pairs
for pair in [
("A", "A"),
("B", "B"),
("A", "B"),
("A", "R"),
("B", "R"),
]:
gb.params[pair] = dict(epsilon=0.0, lperp=0.0, lpar=0.0)
forces.append(gb)
return forces
79 changes: 79 additions & 0 deletions flowermd/library/polymers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import mbuild as mb
from mbuild.coordinate_transform import z_axis_transform
from mbuild.lib.recipes import Polymer as mbPolymer

from flowermd import CoPolymer, Polymer
from flowermd.assets import MON_DIR
Expand Down Expand Up @@ -272,3 +273,81 @@ def _build(self, length):
chain.add_bond([next_bead, last_bead])
last_bead = next_bead
return chain


class EllipsoidChain(Polymer):
"""Create an ellipsoid polymer chain.

This is a coarse-grained molecule where each monomer is modeled
as an anisotropic bead (i.e. ellipsoid).

Notes
-----
In order to form chains of connected ellipsoids, "ghost"
particles of types "A" and "B" are used.

This is meant to be used with
`flowermd.library.forcefields.EllipsoidForcefield`
and requires using `flowermd.utils.rigid_body` to set up
the rigid bodies correctly in HOOMD-Blue.

Parameters
----------
lengths : int, required
The number of monomer repeat units in the chain.
num_mols : int, required
The number of chains to create.
lpar : float, required
The semi-axis length of the ellipsoid bead along its major axis.
bead_mass : float, required
The mass of the ellipsoid bead.
bond_length : float, required
The bond length between connected beads.
This is used as the bond length between ellipsoid tips
rather than between ellipsoid centers.

"""

def __init__(self, lengths, num_mols, lpar, bead_mass, bond_length):
self.bead_mass = bead_mass
self.bead_bond_length = bond_length
self.lpar = lpar
# get the indices of the particles in a rigid body
self.bead_constituents_types = ["A", "A", "B", "B"]
super(EllipsoidChain, self).__init__(lengths=lengths, num_mols=num_mols)

def _build(self, length):
# Build up ellipsoid bead
bead = mb.Compound(name="ellipsoid")
head = mb.Compound(
pos=(0, 0, self.lpar), name="A", mass=self.bead_mass / 4
)
tail = mb.Compound(
pos=(0, 0, -self.lpar), name="A", mass=self.bead_mass / 4
)
head_mid = mb.Compound(
pos=(0, 0, self.lpar / 2), name="B", mass=self.bead_mass / 4
)
tail_mid = mb.Compound(
pos=(0, 0, -self.lpar / 2), name="B", mass=self.bead_mass / 4
)
bead.add([head, tail, head_mid, tail_mid])
# Build the bead chain
chain = mbPolymer()
chain.add_monomer(
bead,
indices=[0, 1],
orientation=[[0, 0, 1], [0, 0, -1]],
replace=False,
separation=self.bead_bond_length,
)
chain.build(n=length, add_hydrogens=False)
# Generate bonds between the mid-particles.
# This is needed to use an angle potential between 2 beads.
chain.freud_generate_bonds(
name_a="B",
name_b="B",
dmin=self.lpar - 0.1,
dmax=self.lpar + self.bead_bond_length + 0.1,
)
return chain
49 changes: 46 additions & 3 deletions flowermd/tests/base/test_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
import unyt as u

from flowermd import Simulation
from flowermd.base import Pack
from flowermd.library import OPLS_AA_PPS
from flowermd.library.forcefields import EllipsoidForcefield
from flowermd.library.polymers import EllipsoidChain
from flowermd.tests import BaseTest
from flowermd.utils import ( # get_target_box_number_density,
get_target_box_mass_density,
)
from flowermd.utils import create_rigid_body, get_target_box_mass_density


class TestSimulate(BaseTest):
Expand Down Expand Up @@ -294,6 +295,48 @@ def test_pickle_ff(self, benzene_system):
assert type(i) is type(j)
os.remove("forcefield.pickle")

def test_bad_rigid(self, benzene_system):
with pytest.raises(ValueError):
Simulation.from_system(benzene_system, rigid_constraint="A")

def test_rigid_sim(self):
ellipsoid_chain = EllipsoidChain(
lengths=4,
num_mols=2,
lpar=0.5,
bead_mass=100,
bond_length=0.01,
)
system = Pack(
molecules=ellipsoid_chain,
density=0.1,
base_units=dict(),
fix_orientation=True,
)
ellipsoid_ff = EllipsoidForcefield(
lpar=0.5,
lperp=0.25,
epsilon=1.0,
r_cut=2.0,
bond_k=500,
bond_r0=0.01,
angle_k=250,
angle_theta0=2.2,
)
rigid_frame, rigid = create_rigid_body(
system.hoomd_snapshot,
ellipsoid_chain.bead_constituents_types,
bead_name="R",
)
sim = Simulation(
initial_state=rigid_frame,
forcefield=ellipsoid_ff.hoomd_forces,
rigid_constraint=rigid,
)
sim.run_NVT(n_steps=0, kT=1.0, tau_kt=sim.dt * 100)
assert sim.integrator.integrate_rotational_dof is True
assert sim.mass_reduced == 800.0

def test_save_restart_gsd(self, benzene_system):
sim = Simulation.from_system(benzene_system)
sim.save_restart_gsd("restart.gsd")
Expand Down
Loading
Loading