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.
| Only ``Atom`` instances contribute; ``GhostAtom``, ``PointCharge``, | ||
| and ``EmbeddingPotential`` atoms are silently ignored. | ||
|
|
||
| Mass priority |
There was a problem hiding this comment.
This block should come after Parameters. This applies to every docstring that has this.
First explain your terminology, then you can explain the rules.
There was a problem hiding this comment.
Change made, thanks for the explanation
There was a problem hiding this comment.
Did you forget to push the change?
There was a problem hiding this comment.
Please move the block after Parameters.
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.
| Coordinates in the same order as the input list. | ||
| """ | ||
| atom_list = atoms if atoms is not None else self.atoms | ||
| return np.array([a.coordinates.coordinates for a in atom_list], dtype=np.float64) |
There was a problem hiding this comment.
We should refrain from setting dtype explicitly everywhere, as this introduces an extra chore, which might be overlooked by developers.
Or is there a particular reason, why we need to set this explicitly?
There was a problem hiding this comment.
dtype is still set explicitly. Please remove this, if there is no particular reason to do so. Otherwise, add comment mentioning the reason.
| Only ``Atom`` instances contribute; ``GhostAtom``, ``PointCharge``, | ||
| and ``EmbeddingPotential`` atoms are silently ignored. | ||
|
|
||
| Mass priority |
There was a problem hiding this comment.
Did you forget to push the change?
…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!
| """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 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.
timmyte
left a comment
There was a problem hiding this comment.
Thanks for quickly tackling my remarks. But please make sure you addressed all of them.
| Coordinates in the same order as the input list. | ||
| """ | ||
| atom_list = atoms if atoms is not None else self.atoms | ||
| return np.array([a.coordinates.coordinates for a in atom_list], dtype=np.float64) |
There was a problem hiding this comment.
dtype is still set explicitly. Please remove this, if there is no particular reason to do so. Otherwise, add comment mentioning the reason.
| Only ``Atom`` instances contribute; ``GhostAtom``, ``PointCharge``, | ||
| and ``EmbeddingPotential`` atoms are silently ignored. | ||
|
|
||
| Mass priority |
There was a problem hiding this comment.
Please move the block after Parameters.
| @@ -1475,13 +1494,14 @@ def calc_rotational_constants( | |||
|
|
|||
| Mass priority | |||
There was a problem hiding this comment.
Same here: Please make that the Parameters block comes first and then the Mass priority
| effectively zero (degenerate / linear axis). | ||
| """ | ||
| if inertia is None or inertia < 1e-6: | ||
| if inertia is None or inertia < 1e-3: |
There was a problem hiding this comment.
Why did this number change now!?
Please document why exactly 10e-3.
| def test_rotated_zero_rmsd(self, water, water_rotated): | ||
| """Kabsch should align the rotation and give ~0 RMSD.""" | ||
| centered = water.centered_structure() | ||
| print(centered.get_coordinates()) |
There was a problem hiding this comment.
Why the print statement here?
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