Skip to content

Commit 88d5a70

Browse files
committed
Implement the DMR approach and make it default for using when using Morse restraints
1 parent 208c15f commit 88d5a70

1 file changed

Lines changed: 61 additions & 8 deletions

File tree

src/sire/restraints/_restraints.py

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,9 @@ def morse_potential(
732732
de=None,
733733
use_pbc=None,
734734
name=None,
735-
auto_parametrise=False,
735+
auto_parametrise=True,
736+
direct_morse_replacement=True,
737+
retain_harmonic_bond=False,
736738
map=None,
737739
):
738740
"""
@@ -795,6 +797,16 @@ def morse_potential(
795797
and 'atoms1', the equilibrium distance r0 will be set to the original
796798
bond length, and the force constant k will be set to the force constant
797799
of the bond in the unperturbed state. Note that 'de' must still be provided.
800+
Default is True.
801+
802+
direct_morse_replacement : bool, optional
803+
If True, and if auto_parametrise is True, then the function will attempt to directly
804+
replace an existing bond with a Morse potential. Default is True.
805+
806+
retain_harmonic_bond : bool, optional
807+
If True, and if auto_parametrise is True, then the function will only nullify the force
808+
constant of the existing harmonic bond, rather than removing the bond potential entirely.
809+
If False, then the existing harmonic bond will be removed entirely.
798810
Default is False.
799811
800812
Returns
@@ -803,6 +815,9 @@ def morse_potential(
803815
A container of Morse restraints, where the first restraint is
804816
the MorsePotentialRestraint created. The Morse restraint created can be
805817
extracted with MorsePotentialRestraints[0].
818+
819+
mols : sire.system._system.System
820+
The system containing the atoms, which will have been modified if auto_parametrise is True and direct_morse_replacement is True.
806821
"""
807822

808823
from .. import u
@@ -875,10 +890,7 @@ def morse_potential(
875890
atom0_idx = [bond_name.atom0().index().value()][0]
876891
atom1_idx = [bond_name.atom1().index().value()][0]
877892

878-
# Divide k0 by 2 to convert from force constant to sire half
879-
# force constant k
880893
if k is None:
881-
k0 = k0 / 2.0
882894
k0 = u(f"{k0} kJ mol-1 nm-2")
883895
k = [k0]
884896

@@ -897,6 +909,47 @@ def morse_potential(
897909
f"molecule property is_perturbable and atomidx {atom1_idx}"
898910
]
899911
break
912+
if direct_morse_replacement:
913+
from ..legacy import MM as _MM
914+
import re as _re
915+
from ..legacy.CAS import Symbol as _Symbol
916+
917+
search_pattern = r"r - (\d+\.\d+)"
918+
mol = mol[0]
919+
info = mol.info()
920+
921+
# We need to loop through both bond0 and bond1 properties, as we don't know
922+
# which one the bond of interest will be in (bond forming or bond breaking)
923+
for bond_prop in ("bond0", "bond1"):
924+
bonds = mol.property(bond_prop)
925+
new_bonds = _MM.TwoAtomFunctions(info)
926+
927+
for p in bonds.potentials():
928+
idx0 = info.atom_idx(p.atom0())
929+
idx1 = info.atom_idx(p.atom1())
930+
931+
# Attempt to match the bond of interest using previously identified atom indices
932+
if idx0.value() == atom0_idx and idx1.value() == atom1_idx:
933+
bond_potential_string = p.function().to_string()
934+
match = _re.search(search_pattern, bond_potential_string)
935+
936+
if not match:
937+
raise ValueError(
938+
f"No match found in the string: {bond_potential_string}"
939+
)
940+
941+
# If we retaining the harmonic bond, then set the harmonic bond force constant
942+
# to zero. Otherwise, the harmonic bond entirely removed from the force list.
943+
if retain_harmonic_bond:
944+
r = float(match.group(1))
945+
amber_bond = _MM.AmberBond(0, r)
946+
expression = amber_bond.to_expression(_Symbol("r"))
947+
new_bonds.set(idx0, idx1, expression)
948+
else:
949+
new_bonds.set(idx0, idx1, p.function())
950+
951+
mol = mol.edit().set_property(bond_prop, new_bonds).molecule().commit()
952+
mols.update(mol)
900953

901954
try:
902955
atoms0 = _to_atoms(mols, atoms0)
@@ -943,16 +996,16 @@ def morse_potential(
943996
except:
944997
raise ValueError(f"Unable to parse 'de' as a Sire GeneralUnit: {de}")
945998

946-
mols = mols.atoms()
999+
mols_atms = mols.atoms()
9471000

9481001
if name is None:
9491002
restraints = MorsePotentialRestraints()
9501003
else:
9511004
restraints = MorsePotentialRestraints(name=name)
9521005

9531006
for i, (atom0, atom1) in enumerate(zip(atoms0, atoms1)):
954-
idxs0 = mols.find(atom0)
955-
idxs1 = mols.find(atom1)
1007+
idxs0 = mols_atms.find(atom0)
1008+
idxs1 = mols_atms.find(atom1)
9561009

9571010
if type(idxs0) is int:
9581011
idxs0 = [idxs0]
@@ -989,7 +1042,7 @@ def morse_potential(
9891042
# Set the use_pbc flag.
9901043
restraints.set_uses_pbc(use_pbc)
9911044

992-
return restraints
1045+
return restraints, mols
9931046

9941047

9951048
def bond(*args, use_pbc=False, **kwargs):

0 commit comments

Comments
 (0)