diff --git a/pytests/xmol/polymer/test_Atom.py b/pytests/xmol/polymer/test_Atom.py index a187fac6..411b31a7 100644 --- a/pytests/xmol/polymer/test_Atom.py +++ b/pytests/xmol/polymer/test_Atom.py @@ -500,28 +500,3 @@ def test_bad_selection_construction_from_list(): AtomSelection([ a for a in frame.asChains ]) with pytest.raises(Exception): ChainSelection([ a for a in frame.asResidues ]) - - - -def test_shorthands(): - # from pyxmolpp2.polymer import AtomSelection, ChainSelection - - frame = make_polyglycine([("A", 20)]) - - asel = frame.asAtoms - - asel.geom_center() - asel.mass_center([1.0]*asel.size) - asel.inertia_tensor([1.0]*asel.size) - asel.geom_inertia_tensor() - asel.rmsd(asel) - asel.rmsd(asel.toCoords) - asel.alignment(asel) - asel.alignment(asel.toCoords) - asel.align_to(asel) - asel.align_to(asel.toCoords) - - - - - diff --git a/pytests/xmol/polymer/test_shortcuts.py b/pytests/xmol/polymer/test_shortcuts.py new file mode 100644 index 00000000..f4eefd8b --- /dev/null +++ b/pytests/xmol/polymer/test_shortcuts.py @@ -0,0 +1,68 @@ +def make_polyglycine(chain_lengths, no_reserve=True): + from pyxmolpp2.polymer import Frame + from pyxmolpp2.polymer import ChainName + from pyxmolpp2.polymer import AtomName + from pyxmolpp2.polymer import ResidueName, ResidueId + from pyxmolpp2.geometry import XYZ + + aid = 1 + rid = 1 + frame = Frame(0) + for chainId, N in chain_lengths: + if no_reserve: + c = frame.emplace(ChainName(chainId)) + else: + c = frame.emplace(ChainName(chainId), N) + for i in range(N): + if no_reserve: + r = c.emplace(ResidueName("GLY"), ResidueId(rid)) + else: + r = c.emplace(ResidueName("GLY"), ResidueId(rid), 7) + + rid += 1 + for aname in ["N", "H", "CA", "HA2", "HA3", "C", "O"]: + r.emplace(AtomName(aname), aid, XYZ(1, 2, 3)) + aid += 1 + + return frame + + +def test_shorthands(): + import numpy as np + + from pyxmolpp2.geometry import Rotation3d, Translation3d, XYZ, Degrees, calc_alignment + + frame = make_polyglycine([("A", 20)]) + for a in frame.asAtoms: + a.r = XYZ(*np.random.random(3)) + frame2 = frame.copy() + + asel = frame.asAtoms + asel2 = frame2.asAtoms + + asel.geom_center() + asel.mass_center([1.0] * asel.size) + asel.inertia_tensor([1.0] * asel.size) + asel.geom_inertia_tensor() + + T = Translation3d(XYZ(1, 0, 0)) + asel2.transform(T) + + assert np.isclose(asel.rmsd(asel2), 1.0) + assert np.isclose(asel.rmsd(asel2.toCoords), 1.0) + assert np.isclose(asel.rmsd(asel2.toCoords.transform(T.inverted())), 0.0) + + T = Translation3d(XYZ(1, 0, 0)) * Rotation3d(XYZ(1, 1, 1), Degrees(45)) + asel.align_to(asel2) + assert np.isclose(asel.rmsd(asel2), 0) + + asel2.transform(T) + asel.align_to(asel2.toCoords) + assert np.isclose(asel.rmsd(asel2), 0) + + T = Translation3d(XYZ(1, 0, 0)) * Rotation3d(XYZ(1, 1, 1), Degrees(45)) + asel2.transform(T) + + assert np.allclose(T.matrix3d(), calc_alignment(asel2.toCoords, asel.toCoords).matrix3d()) + assert np.allclose(T.matrix3d(), asel.alignment_to(asel2).matrix3d()) + assert np.allclose(T.matrix3d(), asel.alignment_to(asel2.toCoords).matrix3d()) diff --git a/pyxmolpp/polymer/Atom.cpp b/pyxmolpp/polymer/Atom.cpp index 3748040f..08634cf6 100644 --- a/pyxmolpp/polymer/Atom.cpp +++ b/pyxmolpp/polymer/Atom.cpp @@ -617,7 +617,7 @@ Order is preserved across manipulations with :py:class:`ChainSelection` .def( "rmsd", [](AtomSelection& sel, AtomSelection& ref) { - return xmol::geometry::calc_rmsd(sel.toCoords(),ref.toCoords()); + return xmol::geometry::calc_rmsd(sel.toCoords(), ref.toCoords()); }, py::arg("ref"), "Returns rmsd between two selections") @@ -631,18 +631,18 @@ Order is preserved across manipulations with :py:class:`ChainSelection` "Returns rmsd between selection and reference coordinates") .def( - "alignment", + "alignment_to", [](AtomSelection& sel, std::vector ref) { - return xmol::geometry::calc_alignment(sel.toCoords(), ref); + return xmol::geometry::calc_alignment(ref, sel.toCoords()); }, py::arg("ref"), "Equivalent to :code:`calc_alignment(ref.toCoords(), self.toCoords())`") .def( - "alignment", + "alignment_to", [](AtomSelection& sel, AtomSelection& ref) { - return xmol::geometry::calc_alignment(sel.toCoords(), ref.toCoords()); + return xmol::geometry::calc_alignment(ref.toCoords(), sel.toCoords()); }, py::arg("ref"), "Equivalent to :code:`calc_alignment(ref.toCoords(), self.toCoords())`") @@ -650,7 +650,8 @@ Order is preserved across manipulations with :py:class:`ChainSelection` .def( "align_to", [](AtomSelection& sel, std::vector ref) -> AtomSelection& { - xmol::geometry::calc_alignment(sel.toCoords(), ref); + auto al = xmol::geometry::calc_alignment(ref, sel.toCoords()); + sel.for_each([&](Atom& a) { a.set_r(al.transform(a.r())); }); return sel; }, py::arg("ref"), @@ -660,7 +661,8 @@ Order is preserved across manipulations with :py:class:`ChainSelection` .def( "align_to", [](AtomSelection& sel, AtomSelection& ref) -> AtomSelection& { - xmol::geometry::calc_alignment(sel.toCoords(), ref.toCoords()); + auto al = xmol::geometry::calc_alignment(ref.toCoords(), sel.toCoords()); + sel.for_each([&](Atom& a) { a.set_r(al.transform(a.r())); }); return sel; }, py::arg("ref"),