RMSD and Rotational Constants Modules #230
Conversation
timmyte
left a comment
There was a problem hiding this comment.
Solid first PR.
But the tools are bit to decoupled from OPI. You should use and add to the data structures that we already have in OPI.
|
As discussed, I would design the RMSD function as follows: class Structure:
def rmsd(self, other: Structure, /, only_atoms: Sequence[int] = (), *, ignore_hs: bool = False) -> float:
return _rmsd(self.coordinates, other.coordinates, only_atoms=only_atoms, ignore_hs=ignore_hs)
def _rmsd(coords1: NDArray[float], coords2: NDArray[float], /, only_atoms: Sequence[int] = (), *, ignore_hs: bool = False)
# We ignore `ignore_hs` if `only_atoms` is populated. This should also be documented
if coords1.shape != coords2.shape:
raise ValueError
def rmsd_kabsch(self, other: Structure, /, only_atoms: Sequence[int] = (), *, ignore_hs: bool = False) -> float:
# 1) Translate to center of geometry (centroid)
# 2) find optimal rotation
# `only_atoms` and `ignore_hs` don't need to be passed here anymore as `coords1` and `coords2_rotated` should only contain the coordinates of the relevant atoms.
return _rmsd(coords1, coords2_rotated) |
timmyte
left a comment
There was a problem hiding this comment.
Very nice improvement of the code. We are getting there.
I really appreciate how well you documented the code.
timmyte
left a comment
There was a problem hiding this comment.
Thanks for tackling my remarks. There are still some unresolved issues though.
Please also see the few additional points I raised.
…ment.py. Added units.py and rotconts.py
…onstatns, Inertia Moments, and Rotor Type
…olerance for rotor classification. Improved unit tests
…e_masses(), and updated unit tests
…SES_FROM_ELEMENT)
timmyte
left a comment
There was a problem hiding this comment.
Very good, especially the exhaustive amount of tests. BRAVO!
| """ | ||
| Compute RMSD between this structure and *other* (no rotational alignment). | ||
|
|
||
| Both structures are translated to their centroid before comparison. |
There was a problem hiding this comment.
I would be a bit more precise here:
The two structure objects themselves are not modified. Please mention this.
If the objects themselves were changed, there could be side-effects in any code coming after calling this method. This is important to avoid!
| """ | ||
| Compute RMSD between this structure and *other* using the Kabsch algorithm. | ||
|
|
||
| Translates both structures to their centroid, then finds the optimal |
There was a problem hiding this comment.
Same here. Mention that the Structure object themselves are not modified.
| # ------------------------------------------------------------------ # | ||
| def _resolve_masses( | ||
| self, | ||
| elem_masses: dict[str, float] | None = None, |
There was a problem hiding this comment.
| elem_masses: dict[str, float] | None = None, | |
| elem_masses: dict[str | Element, float] | None = None, |
There was a problem hiding this comment.
Also update the docstring below, please.
|
|
||
| def calc_moments_of_inertia( | ||
| self, | ||
| elem_masses: dict[str, float] | None = None, |
There was a problem hiding this comment.
| elem_masses: dict[str, float] | None = None, | |
| elem_masses: dict[str | Element, float] | None = None, |
There was a problem hiding this comment.
Also update the docstring below, please.
| `None` the moments are computed via | ||
| :meth:`calc_moments_of_inertia` using *mass_kwargs*. | ||
| **mass_kwargs | ||
| Forwarded to :meth:`calc_moments_of_inertia` when *moments* is |
There was a problem hiding this comment.
| Forwarded to :meth:`calc_moments_of_inertia` when *moments* is | |
| Forwarded to :meth:`calc_moments_of_inertia()` when `moments` is |
| """All atoms in water are real.""" | ||
| assert len(water.real_atoms) == 3 | ||
|
|
||
| def test_return_type(self, water): |
There was a problem hiding this comment.
Please add docstring, briefly describing the purpose of the test.
|
|
||
| class TestGetCoordinates: | ||
| def test_shape(self, water): | ||
| coords = water.get_coordinates() |
There was a problem hiding this comment.
Please add a docstring briefly describing the purpose of the test.
| class TestGetCoordinatesAtCentroid: | ||
| def test_centroid_is_zero(self, water): | ||
| coords = water.get_coordinates_at_centroid() | ||
| np.testing.assert_allclose(coords.mean(axis=0), [0.0, 0.0, 0.0], atol=1e-12) |
There was a problem hiding this comment.
Please document why so picked this value for atol?
Please do this for all tests below.
It's not obvious why its 1e-12 here and 1e-6 below.
|
|
||
| class TestSetCoordinates: | ||
| def test_mutates_in_place(self, water): | ||
| """set_coordinates should modify the structure in place.""" |
There was a problem hiding this comment.
| """set_coordinates should modify the structure in place.""" | |
| """set_coordinates should modify the structure in-place.""" |
| pm = water.calc_moments_of_inertia() | ||
| assert pm is not None | ||
| s = str(pm) | ||
| assert "Moments of inertia (amu·Å²):" in s |
There was a problem hiding this comment.
You should know the extract string that comes out.
Why not compare directly against that?
Closes Issues
Closes #220 #219
Description
Release Notes
(Project adheres to Keep a Changelog; Every entry should start in upper-case and end with
(#<pr-id>); Remove sections that don't apply)Added
constants.pycontaining some universal constantsrotconst.pycontaining auxiliary dictionaries for Rotational Constants CalculationChanged
structure.pywith RMSD and RotConst as Class methodselements.pyadding atomic masses for moment of inertia calculationsRemoved
rmsd.pymodule