diff --git a/corelib/src/libs/SireMol/atommapping.cpp b/corelib/src/libs/SireMol/atommapping.cpp index c38616017..5722d1de8 100644 --- a/corelib/src/libs/SireMol/atommapping.cpp +++ b/corelib/src/libs/SireMol/atommapping.cpp @@ -31,6 +31,7 @@ #include "SireMaths/align.h" #include "SireMol/core.h" +#include "SireMol/moleditor.h" #include "SireStream/datastream.h" #include "SireStream/shareddatastream.h" @@ -723,20 +724,40 @@ AtomMapping AtomMapping::alignTo0() const QVector coords0 = this->atms0.property(map0["coordinates"]).toVector(); QVector coords1 = this->atms1.property(map1["coordinates"]).toVector(); - // calculate the transform to do a RMSD aligment of the two sets of coordinates - auto transform = SireMaths::getAlignment(coords0, coords1, true); - - auto mols1 = this->orig_atms1.molecules(); - AtomMapping ret(*this); - for (int i = 0; i < mols1.count(); ++i) + if ((this->count() == 1) and ((coords0.count() == 1) or (coords1.count() == 1))) { - auto mol = mols1[i].move().transform(transform, map1).commit(); + // if we've only mapped a single atom and one molecule is a monatomic ion, + // then simply replace the coordinates of the mapped atom. + + auto atom0 = this->atms0[0]; + auto atom1 = this->atms1[0]; + + auto mol = this->orig_atms1.molecules()[0]; + + mol = mol.edit().atom(atom1.index()) + .setProperty(map1["coordinates"].source(), coords0[0]) + .molecule().commit(); ret.atms1.update(mol); ret.orig_atms1.update(mol); } + else + { + // calculate the transform to do a RMSD aligment of the two sets of coordinates + auto transform = SireMaths::getAlignment(coords0, coords1, true); + + auto mols1 = this->orig_atms1.molecules(); + + for (int i = 0; i < mols1.count(); ++i) + { + auto mol = mols1[i].move().transform(transform, map1).commit(); + + ret.atms1.update(mol); + ret.orig_atms1.update(mol); + } + } return ret; } @@ -754,20 +775,40 @@ AtomMapping AtomMapping::alignTo1() const QVector coords0 = this->atms0.property(map0["coordinates"]).toVector(); QVector coords1 = this->atms1.property(map1["coordinates"]).toVector(); - // calculate the transform to do a RMSD aligment of the two sets of coordinates - auto transform = SireMaths::getAlignment(coords1, coords0, true); - - auto mols0 = this->orig_atms0.molecules(); - AtomMapping ret(*this); - for (int i = 0; i < mols0.count(); ++i) + if ((this->count() == 1) and ((coords0.count() == 1) or (coords1.count() == 1))) { - auto mol = mols0[i].move().transform(transform, map0).commit(); + // if we've only mapped a single atom and one molecule is a monatomic ion, + // then simply replace the coordinates of the mapped atom. + + auto atom0 = this->atms0[0]; + auto atom1 = this->atms1[0]; + + auto mol = this->orig_atms0.molecules()[0]; + + mol = mol.edit().atom(atom0.index()) + .setProperty(map0["coordinates"].source(), coords0[0]) + .molecule().commit(); ret.atms0.update(mol); ret.orig_atms0.update(mol); } + else + { + // calculate the transform to do a RMSD aligment of the two sets of coordinates + auto transform = SireMaths::getAlignment(coords1, coords0, true); + + auto mols0 = this->orig_atms0.molecules(); + + for (int i = 0; i < mols0.count(); ++i) + { + auto mol = mols0[i].move().transform(transform, map0).commit(); + + ret.atms0.update(mol); + ret.orig_atms0.update(mol); + } + } return ret; } diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index cee9ded34..b55abf931 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -18,6 +18,7 @@ organisation on `GitHub `__. * Please add an item to this changelog when you create your PR * Print residue indices of perturbed water molecules to SOMD1 log. * Add support for creating Na+ and Cl- ions. +* Fix ``sire.morph.merge`` function when one molecule is a monatomic ion. `2024.2.0 `__ - June 2024 ----------------------------------------------------------------------------------------- diff --git a/tests/morph/test_merge.py b/tests/morph/test_merge.py index 8d5311569..25f9550b6 100644 --- a/tests/morph/test_merge.py +++ b/tests/morph/test_merge.py @@ -202,3 +202,23 @@ def test_merge_neopentane_methane(neopentane_methane, openmm_platform): # These energies aren't correct - extra ghost atom internals? assert nrg_neo.value() == pytest.approx(nrg_merged_0.value(), abs=1e-3) # assert nrg_met.value() == pytest.approx(nrg_merged_1.value(), abs=1e-3) + + +@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows") +def test_ion_merge(ala_mols): + water = ala_mols[-1] + ion = sr.legacy.IO.createSodiumIon(water.atoms()[-1].coordinates(), "tip3p") + + merged = sr.morph.merge(water, ion) + + coords0 = merged.property("coordinates0").to_vector()[0] + coords1 = merged.property("coordinates1").to_vector()[0] + + assert coords0 == coords1 + + merged = sr.morph.merge(ion, water) + + coords0 = merged.property("coordinates0").to_vector()[0] + coords1 = merged.property("coordinates1").to_vector()[0] + + assert coords0 == coords1