Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
3a27d99
add rigid_body field to atom, populate in from_mbuild
Sep 17, 2024
bbb65c3
move rigid_id property to abstract_site from atom, write out rigid_id…
Sep 17, 2024
92534db
add rigid types and typeids
Sep 17, 2024
62edfeb
Merge branch 'fix/molecule' into rigid-bodies
Sep 18, 2024
ab00493
add test for rigid_id assignment
Sep 18, 2024
3d28187
Merge branch 'main' of github.com:mosdef-hub/gmso into rigid-bodies
Sep 19, 2024
f6480bc
write out rigid xyz, com, tags
Sep 23, 2024
ad603fa
add if statement before writing rigid tags
Sep 23, 2024
fdba315
add basic rigid body snapshot test
Sep 23, 2024
3a06f24
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 23, 2024
60e3538
Merge branch 'main' of github.com:mosdef-hub/gmso into rigid-bodies
Sep 24, 2024
8842973
remove rigid id from convert mbuild, remove rigid id prop from abstra…
Sep 25, 2024
6a383c6
parse rigid sites from site.molecule, update test
Sep 26, 2024
7c65624
remove un-needed var
Sep 30, 2024
a732661
Merge branch 'main' into rigid-bodies
chrisjonesBSU Oct 14, 2024
8da6fe5
update tests
Oct 16, 2024
e9de935
Merge branch 'main' of github.com:mosdef-hub/gmso into rigid-bodies
Oct 16, 2024
75289bb
fix charge array in np.concatenate call
Oct 17, 2024
0d6e7d2
adjust snapshot groups by num rigid bodies
Oct 17, 2024
a72d898
update tests
Oct 17, 2024
5e8635f
fix _parse_pairs
Oct 17, 2024
b96a83c
use unyt_arrays for charges and mass instead of lists
Oct 18, 2024
937f913
Merge branch 'main' into rigid-bodies
chrisjonesBSU Oct 22, 2024
ffe639c
handle mix of rigid and non rigid
Oct 30, 2024
4eaf697
remove if statement to filter -1 ids
Oct 30, 2024
fb161b1
merge and fix conflicts. Pin hoomd<5
Jan 21, 2025
c712555
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 21, 2025
91642f0
Merge branch 'main' of github.com:mosdef-hub/gmso into rigid-bodies
Jan 23, 2025
da69c18
Merge branch 'main' of github.com:mosdef-hub/gmso into rigid-bodies
Jan 28, 2025
24537d1
Merge branch 'main' into rigid-bodies
chrisjonesBSU Mar 5, 2025
8546680
add moit util, fill in more snapshot info
Mar 5, 2025
1e388b0
Merge branch 'main' into rigid-bodies
chrisjonesBSU Mar 18, 2025
c2738e7
bump molecule numbres, return rigid body info dict
Mar 18, 2025
e23bb51
refactor to work with multiple kinds of rigid mols
Mar 18, 2025
e81ddb6
add more code comments
Mar 19, 2025
14c4deb
remove unused variable
Mar 19, 2025
fc73e46
fix existing tests
Mar 20, 2025
104b2de
Merge branch 'rigid-bodies' of github.com:chrisjonesBSU/gmso into rig…
Mar 20, 2025
91a3ffb
clean things up a bit
Mar 20, 2025
f4d0dd6
add test with 2 kinds of rigid mols
Mar 20, 2025
ff6512f
remove snapshot.validate() for hoomd snapshot
Mar 20, 2025
c8b66d2
simplify tests
Mar 20, 2025
d24b36b
Merge branch 'main' of github.com:mosdef-hub/gmso into rigid-bodies
Mar 25, 2025
86c1684
add conditional return, update doc strings
Mar 25, 2025
3ea4599
minor doc string fixes
Mar 25, 2025
e69e1df
add test for moit function
Mar 25, 2025
078cc18
quick doc string change
Mar 25, 2025
67ebc62
merge and fix conflicts
Mar 26, 2025
3dc1ce2
Merge branch 'main' into rigid-bodies
chrisjonesBSU Apr 2, 2025
1e39411
Merge branch 'main' into rigid-bodies
chrisjonesBSU Apr 9, 2025
15088f8
Merge branch 'main' into rigid-bodies
chrisjonesBSU Apr 30, 2025
76a4b64
Fix doc strings and spelling typos
May 2, 2025
1476288
add link to hoomd base units doc
May 2, 2025
6a95912
fix import in test_utils
May 2, 2025
104c80c
Merge branch 'main' of github.com:mosdef-hub/gmso into rigid-bodies
May 2, 2025
0501c95
add check and test for wrong rigid hierarchy
May 5, 2025
081229f
Merge branch 'main' into rigid-bodies
chrisjonesBSU May 5, 2025
4be2fa6
remove unused variables
May 5, 2025
449f8d4
Merge branch 'rigid-bodies' of github.com:chrisjonesBSU/gmso into rig…
May 5, 2025
81ed4f0
remove unused rigid_bodies param from methods
May 5, 2025
1d497e2
remove unused rigid bodies param from gsd writer
May 5, 2025
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
333 changes: 251 additions & 82 deletions gmso/external/convert_hoomd.py

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion gmso/external/convert_mbuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ def _parse_molecule_residue(site_map, compound):
connected_subgraph = compound.bond_graph.connected_components()
molecule_tracker = dict()
residue_tracker = dict()
total_molecule_count = 0
for molecule in connected_subgraph:
if len(molecule) == 1:
ancestors = [molecule[0]]
Expand All @@ -305,8 +306,9 @@ def _parse_molecule_residue(site_map, compound):
if molecule_tag.name in molecule_tracker:
molecule_tracker[molecule_tag.name] += 1
else:
molecule_tracker[molecule_tag.name] = 0
molecule_tracker[molecule_tag.name] = 0 + total_molecule_count
molecule_number = molecule_tracker[molecule_tag.name]
total_molecule_count += 1
"""End of molecule parsing"""

for particle in molecule:
Expand Down
7 changes: 0 additions & 7 deletions gmso/formats/gsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ def write_gsd(
top,
filename,
base_units=None,
rigid_bodies=None,
shift_coords=True,
write_special_pairs=True,
):
Expand All @@ -33,11 +32,6 @@ def write_gsd(
gmso.Topology object
filename : str
Path of the output file.
rigid_bodies : list of int, optional, default=None
List of rigid body information. An integer value is required for each
atom corresponding to the index of the rigid body the particle is to be
associated with. A value of None indicates the atom is not part of a
rigid body.
shift_coords : bool, optional, default=True
Shift coordinates from (0, L) to (-L/2, L/2) if necessary.
write_special_pairs : bool, optional, default=True
Expand All @@ -54,7 +48,6 @@ def write_gsd(
gsd_snapshot = to_gsd_snapshot(
top=top,
base_units=base_units,
rigid_bodies=rigid_bodies,
shift_coords=shift_coords,
parse_special_pairs=write_special_pairs,
)[0]
Expand Down
2 changes: 1 addition & 1 deletion gmso/tests/test_convert_mbuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def test_uneven_hierarchy(self):
for site in top.sites:
if site.name == "particle2":
assert site.group == "mid"
assert site.molecule == ("particle2", 0)
assert site.molecule == ("particle2", 1)
elif site.name == "particle1":
assert site.molecule == ("particle1", 0)

Expand Down
132 changes: 130 additions & 2 deletions gmso/tests/test_hoomd.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import forcefield_utilities as ffutils
import hoomd
import numpy as np
import pytest
import unyt as u

from gmso import ForceField
from gmso.external import from_mbuild
from gmso.external.convert_hoomd import to_hoomd_forcefield, to_hoomd_snapshot
from gmso.external.convert_hoomd import (
to_gsd_snapshot,
to_hoomd_forcefield,
to_hoomd_snapshot,
)
from gmso.parameterization import apply
from gmso.tests.base_test import BaseTest
from gmso.tests.utils import get_path
Expand All @@ -24,7 +29,7 @@ def run_hoomd_nvt(snapshot, forces):
sim = hoomd.Simulation(device=cpu)
sim.create_state_from_snapshot(snapshot)

integrator = hoomd.md.Integrator(dt=0.001)
integrator = hoomd.md.Integrator(dt=0.0001)
integrator.forces = list(set().union(*forces.values()))

temp = 300 * u.K
Expand All @@ -45,6 +50,129 @@ def run_hoomd_nvt(snapshot, forces):
return sim


@pytest.mark.skipif(not has_mbuild, reason="mbuild not installed")
class TestGsd(BaseTest):
def test_rigid_bodies(self):
ethane = mb.lib.molecules.Ethane()
box = mb.fill_box(ethane, n_compounds=2, box=[2, 2, 2])
top = from_mbuild(box)
for site in top.sites:
site.molecule.isrigid = True
top.identify_connections()

top_no_rigid = from_mbuild(box)
top_no_rigid.identify_connections()

rigid_ids = [site.molecule.number for site in top.sites]
assert set(rigid_ids) == {0, 1}

snapshot, refs, rigid = to_gsd_snapshot(top)
snapshot.validate()
snapshot_no_rigid, refs = to_gsd_snapshot(top_no_rigid)
# Check that snapshot has rigid particles added
assert "Ethane" in snapshot.particles.types
assert "Ethane" not in snapshot_no_rigid.particles.types
assert snapshot.particles.N - 2 == snapshot_no_rigid.particles.N
assert np.array_equal(snapshot.particles.typeid[:2], np.array([0, 0]))
assert np.array_equal(
snapshot.particles.body[2:10], np.array([0] * ethane.n_particles)
)
assert np.array_equal(
snapshot.particles.body[10:], np.array([1] * ethane.n_particles)
)
assert np.allclose(snapshot.particles.mass[0], ethane.mass, atol=1e-2)
# Check that topology isn't changed by using rigid bodies
assert snapshot.bonds.N == snapshot_no_rigid.bonds.N
assert snapshot.angles.N == snapshot_no_rigid.angles.N
assert snapshot.dihedrals.N == snapshot_no_rigid.dihedrals.N
# Check if particle indices in snapshot groups are adjusted by N rigid bodies
for group1, group2 in zip(snapshot.bonds.group, snapshot_no_rigid.bonds.group):
assert np.array_equal(np.array(group1), np.array(group2) + 2)
for group1, group2 in zip(
snapshot.angles.group, snapshot_no_rigid.angles.group
):
assert np.array_equal(np.array(group1), np.array(group2) + 2)
for group1, group2 in zip(
snapshot.dihedrals.group, snapshot_no_rigid.dihedrals.group
):
assert np.array_equal(np.array(group1), np.array(group2) + 2)
for group1, group2 in zip(
snapshot.impropers.group, snapshot_no_rigid.impropers.group
):
assert np.array_equal(np.array(group1), np.array(group2) + 2)

for site in top.iter_sites_by_molecule("Ethane"):
assert site.name in rigid.body["Ethane"]["constituent_types"]

def test_multiple_rigid_bodies(self, gaff_forcefield):
ethane = mb.lib.molecules.Ethane()
benzene = mb.load("c1ccccc1", smiles=True)
benzene.name = "Benzene"
box = mb.fill_box([ethane, benzene], n_compounds=[1, 1], box=[2, 2, 2])
top = from_mbuild(box)
for site in top.sites:
site.molecule.isrigid = True
apply(top, gaff_forcefield, identify_connections=True)

rigid_ids = [site.molecule.number for site in top.sites]
assert set(rigid_ids) == {0, 1}

snapshot, refs, rigid = to_gsd_snapshot(top)
snapshot.validate()

for site in top.iter_sites_by_molecule("Ethane"):
assert site.atom_type.name in rigid.body["Ethane"]["constituent_types"]

for site in top.iter_sites_by_molecule("Benzene"):
assert site.atom_type.name in rigid.body["Benzene"]["constituent_types"]

assert np.array_equal(np.zeros(8), snapshot.particles.body[2:10])
assert np.array_equal(np.ones(12), snapshot.particles.body[10:])
assert round(float(snapshot.particles.mass[0]), 4) == round(
float(ethane.mass), 4
)
assert round(float(snapshot.particles.mass[1]), 4) == round(
float(benzene.mass), 4
)

def test_rigid_bodies_mix(self):
ethane = mb.lib.molecules.Ethane()
ethane.name = "ethane"
benzene = mb.load("c1ccccc1", smiles=True)
benzene.name = "benzene"
box = mb.fill_box([benzene, ethane], n_compounds=[1, 1], box=[2, 2, 2])
top = from_mbuild(box)
for site in top.sites:
if site.molecule.name == "benzene":
site.molecule.isrigid = True

snapshot, refs, rigid = to_gsd_snapshot(top)
snapshot.validate()
assert snapshot.particles.typeid[0] == 0
assert snapshot.particles.N == top.n_sites + 1
assert np.array_equal(
snapshot.particles.body[-ethane.n_particles :],
np.array([-1] * ethane.n_particles),
)
assert np.array_equal(
snapshot.particles.body[1 : benzene.n_particles + 1],
np.array([0] * benzene.n_particles),
)

def test_rigid_bodies_bad_hierarchy(self):
ethane = mb.lib.molecules.Ethane()
ethane.name = "ethane"
benzene = mb.load("c1ccccc1", smiles=True)
benzene.name = "benzene"
box = mb.fill_box([ethane, benzene], n_compounds=[1, 1], box=[2, 2, 2])
top = from_mbuild(box)
for site in top.sites:
if site.molecule.name == "benzene":
site.molecule.isrigid = True
with pytest.raises(RuntimeError):
to_gsd_snapshot(top)


class TestHoomd(BaseTest):
def test_hoomd_simulation(self):
compound = mb.load("CCC", smiles=True)
Expand Down
26 changes: 26 additions & 0 deletions gmso/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import numpy as np
import pytest
import unyt as u

from gmso.core.atom import Atom
from gmso.core.dihedral import Dihedral
from gmso.utils.geometry import moment_of_inertia
from gmso.utils.io import run_from_ipython
from gmso.utils.misc import unyt_to_hashable
from gmso.utils.sorting import sort_connection_members, sort_connection_strings
Expand Down Expand Up @@ -55,3 +57,27 @@ def test_sorting():
"atom2",
"atom4",
)


def test_moment_of_inertia():
xyz = [
np.array([-0.5, -0.5, 0]),
np.array([0.5, -0.5, 0]),
np.array([0.5, 0.5, 0]),
np.array([-0.5, 0.5, 0]),
]
tensor = moment_of_inertia(xyz=xyz, masses=np.array([1.0 for i in xyz]))
assert np.array_equal(tensor, np.array([1, 1, 2]))

xyz = [
np.array([0, 0, 0]),
np.array([1, 0, 0]),
np.array([1, 1, 0]),
np.array([0, 1, 0]),
]
tensor = moment_of_inertia(
xyz=xyz,
center=np.array((0.5, 0.5, 0)),
masses=np.array([1.0 for i in xyz]),
)
assert np.array_equal(tensor, np.array([1, 1, 2]))
31 changes: 31 additions & 0 deletions gmso/utils/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,34 @@ def coord_shift(xyz, box_lengths):
xyz += box_max

return xyz


def moment_of_inertia(xyz, masses, center=np.zeros(3)):
"""Find the moment of inertia tensor given a set of
particle coordinates and their corresponding masses.

This method is used in setting rigid body moments
of inertia.

Parameters
----------
xyz : numpy.ndarray (N,3)
Coordinates of the particles
masses : numpy.ndarray (N,)
Masses of the particles
center : numpy.ndarray (3,), default (0,0,0)
Coordinates of the particle's center

Returns
-------
numpy.ndarray (3,)
Diagonal components of the moment of inertia tensor.
"""
xyz -= np.asarray(center)
x = xyz[:, 0]
y = xyz[:, 1]
z = xyz[:, 2]
Ixx = np.sum((y**2 + z**2) * masses)
Iyy = np.sum((x**2 + z**2) * masses)
Izz = np.sum((x**2 + y**2) * masses)
return np.array((Ixx, Iyy, Izz))
Loading