-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from mattwthompson/add-internal-coordinate-rmsds
Add internal coordinate RMSD method
- Loading branch information
Showing
11 changed files
with
355 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
"""Code vendored from ForceBalance.""" | ||
import numpy | ||
|
||
|
||
def periodic_diff(a, b, v_periodic): | ||
"""convenient function for computing the minimum difference in periodic coordinates | ||
Parameters | ||
---------- | ||
a: np.ndarray or float | ||
Reference values in a numpy array | ||
b: np.ndarray or float | ||
Target values in a numpy arrary | ||
v_periodic: float > 0 | ||
Value of the periodic boundary | ||
Returns | ||
------- | ||
diff: np.ndarray | ||
The array of same shape containing the difference between a and b | ||
All return values are in range [-v_periodic/2, v_periodic/2), | ||
"( )" means exclusive, "[ ]" means inclusive | ||
Examples | ||
------- | ||
periodic_diff(0.0, 2.1, 2.0) => -0.1 | ||
periodic_diff(0.0, 1.9, 2.0) => 0.1 | ||
periodic_diff(0.0, 1.0, 2.0) => -1.0 | ||
periodic_diff(1.0, 0.0, 2.0) => -1.0 | ||
periodic_diff(1.0, 0.1, 2.0) => -0.9 | ||
periodic_diff(1.0, 10.1, 2.0) => 0.9 | ||
periodic_diff(1.0, 9.9, 2.0) => -0.9 | ||
""" | ||
assert v_periodic > 0 | ||
h = 0.5 * v_periodic | ||
return (a - b + h) % v_periodic - h | ||
|
||
|
||
def compute_rmsd(ref, tar, v_periodic=None): | ||
""" | ||
Compute the RMSD between two arrays, supporting periodic difference | ||
""" | ||
assert len(ref) == len(tar), "array length must match" | ||
n = len(ref) | ||
if n == 0: | ||
return 0.0 | ||
if v_periodic is not None: | ||
diff = periodic_diff(ref, tar, v_periodic) | ||
else: | ||
diff = ref - tar | ||
rmsd = numpy.sqrt(numpy.sum(diff**2) / n) | ||
return rmsd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
"""Molecule conversion utilities""" | ||
from typing import TYPE_CHECKING | ||
|
||
from openff.toolkit import Molecule | ||
|
||
from ibstore._base.array import Array | ||
|
||
if TYPE_CHECKING: | ||
from geometric.molecule import Molecule as GeometricMolecule | ||
|
||
|
||
def _to_geometric_molecule( | ||
molecule: Molecule, | ||
coordinates: Array, | ||
) -> "GeometricMolecule": | ||
from geometric.molecule import Molecule as GeometricMolecule | ||
|
||
geometric_molecule = GeometricMolecule() | ||
|
||
geometric_molecule.Data = { | ||
"resname": ["UNK"] * molecule.n_atoms, | ||
"resid": [0] * molecule.n_atoms, | ||
"elem": [atom.symbol for atom in molecule.atoms], | ||
"bonds": [(bond.atom1_index, bond.atom2_index) for bond in molecule.bonds], | ||
"name": molecule.name, | ||
"xyzs": [coordinates], | ||
} | ||
|
||
return geometric_molecule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
""" | ||
def get_internal_coordinate_rmsds( | ||
molecule: Molecule, | ||
reference: Array, | ||
target: Array, | ||
_types: tuple[str] = ("Bond", "Angle", "Dihedral", "Improper"), | ||
) -> dict[str, float]: | ||
""" | ||
from ibstore.analysis import get_internal_coordinate_rmsds | ||
|
||
|
||
class TestInternalCoordinateRMSD: | ||
def test_rmsds_between_conformers(self, ligand): | ||
assert ligand.n_conformers | ||
|
||
rmsds = get_internal_coordinate_rmsds( | ||
molecule=ligand, | ||
reference=ligand.conformers[0], | ||
target=ligand.conformers[-1], | ||
) | ||
|
||
assert all( | ||
[rmsds[key] > 0.0 for key in ["Bond", "Angle", "Dihedral", "Improper"]], | ||
) | ||
|
||
def test_matching_conformers_zero_rmsd(self, ligand): | ||
rmsds = get_internal_coordinate_rmsds( | ||
molecule=ligand, | ||
reference=ligand.conformers[0], | ||
target=ligand.conformers[0], | ||
) | ||
|
||
assert all( | ||
[rmsds[key] == 0.0 for key in ["Bond", "Angle", "Dihedral", "Improper"]], | ||
) | ||
|
||
def test_no_torsions(self, water): | ||
rmsds = get_internal_coordinate_rmsds( | ||
molecule=water, | ||
reference=water.conformers[0], | ||
target=water.conformers[0], | ||
) | ||
|
||
assert rmsds["Bond"] == 0.0 | ||
assert rmsds["Angle"] == 0.0 | ||
|
||
assert "Dihedral" not in rmsds | ||
assert "Improper" not in rmsds | ||
|
||
def test_no_impropers(self, hydrogen_peroxide): | ||
rmsds = get_internal_coordinate_rmsds( | ||
molecule=hydrogen_peroxide, | ||
reference=hydrogen_peroxide.conformers[0], | ||
target=hydrogen_peroxide.conformers[0], | ||
) | ||
|
||
assert "Dihedral" in rmsds | ||
assert "Improper" not in rmsds |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import numpy | ||
from openff.toolkit import Molecule | ||
from openff.units import unit | ||
|
||
from ibstore._molecule import _to_geometric_molecule | ||
|
||
|
||
def test_to_geometric_molecule(): | ||
molecule = Molecule.from_smiles("C1([H])CCCCN1") | ||
molecule.generate_conformers(n_conformers=1) | ||
|
||
geometric_molecule = _to_geometric_molecule(molecule, molecule.conformers[0].m) | ||
|
||
assert molecule.n_atoms == len(geometric_molecule.Data["elem"]) | ||
assert molecule.n_bonds == len(geometric_molecule.Data["bonds"]) | ||
|
||
assert numpy.allclose( | ||
molecule.conformers[0].m_as(unit.angstrom), | ||
geometric_molecule.Data["xyzs"][0], | ||
) |
Oops, something went wrong.