Skip to content

Commit a44f9ce

Browse files
authored
Merge pull request #148 from OpenBioSim/feature_ring_break_softcore
2 parents efd0f47 + 78a311d commit a44f9ce

3 files changed

Lines changed: 323 additions & 6 deletions

File tree

src/somd2/_utils/_schedules.py

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def ring_break_morph():
151151
"""
152152
Build a lambda schedule for ring-breaking perturbations.
153153
154-
Three stages: potential_swap → restraints_off → morph.
154+
Four stages: potential_swap → restraints_off → ring_open → morph.
155155
156156
Returns
157157
-------
@@ -162,8 +162,36 @@ def ring_break_morph():
162162
from sire.cas import LambdaSchedule as _LambdaSchedule
163163

164164
s = _LambdaSchedule.standard_morph()
165+
s.set_stage_weight("morph", 2)
166+
167+
# ring_open: Morse is already off; ring-break nonbonded interaction ramps
168+
# on (alpha: 1→0, kappa: 0→1) while non-bonded terms stay at initial and
169+
# bonded terms remain at final. The softcore interaction gently pushes the
170+
# atoms into the open-chain geometry before the full nonbonded morph begins,
171+
# improving HREX overlap at the ring-break boundary.
172+
s.prepend_stage("ring_open", s.initial(), weight=1)
173+
s.set_equation(stage="ring_open", lever="morse_hard", equation=0)
174+
s.set_equation(stage="ring_open", lever="morse_soft", equation=0)
175+
s.set_equation(stage="ring_open", lever="bond_k", equation=s.final())
176+
s.set_equation(stage="ring_open", lever="bond_length", equation=s.final())
177+
s.set_equation(stage="ring_open", lever="angle_k", equation=s.final())
178+
s.set_equation(stage="ring_open", lever="angle_size", equation=s.final())
179+
s.set_equation(stage="ring_open", lever="torsion_k", equation=s.final())
180+
s.set_equation(stage="ring_open", lever="torsion_phase", equation=s.final())
181+
s.set_equation(
182+
stage="ring_open", force="ring-break", lever="alpha", equation=1 - s.lam()
183+
)
184+
s.set_equation(
185+
stage="ring_open", force="ring-break", lever="kappa", equation=s.lam()
186+
)
187+
s.set_equation(
188+
stage="ring_open", force="ring-make", lever="alpha", equation=s.lam()
189+
)
190+
s.set_equation(
191+
stage="ring_open", force="ring-make", lever="kappa", equation=1 - s.lam()
192+
)
165193

166-
s.prepend_stage("restraints_off", s.initial())
194+
s.prepend_stage("restraints_off", s.initial(), weight=1)
167195
s.set_equation(stage="restraints_off", lever="morse_soft", equation=1 - s.lam())
168196
s.set_equation(stage="restraints_off", lever="morse_hard", equation=0)
169197
s.set_equation(stage="restraints_off", lever="bond_k", equation=s.final())
@@ -189,7 +217,7 @@ def ring_break_morph():
189217
equation=(1 - s.lam()) * s.initial() + s.lam() * s.final(),
190218
)
191219

192-
s.prepend_stage("potential_swap", s.initial())
220+
s.prepend_stage("potential_swap", s.initial(), weight=2)
193221
s.set_equation(stage="potential_swap", lever="morse_hard", equation=1 - s.lam())
194222
s.set_equation(stage="potential_swap", lever="morse_soft", equation=0 + s.lam())
195223
s.set_equation(
@@ -207,6 +235,8 @@ def ring_break_morph():
207235
s.set_equation(stage="potential_swap", lever="torsion_k", equation=s.initial())
208236
s.set_equation(stage="potential_swap", lever="torsion_phase", equation=s.initial())
209237

238+
# morph: standard nonbonded morphing. Ring-break is fixed at fully open
239+
# (kappa=1, alpha=0) since geometry has already relaxed in ring_open.
210240
s.set_equation(stage="morph", lever="morse_hard", equation=0)
211241
s.set_equation(stage="morph", lever="morse_soft", equation=0)
212242
s.set_equation(stage="morph", lever="bond_k", equation=s.final())
@@ -215,6 +245,10 @@ def ring_break_morph():
215245
s.set_equation(stage="morph", lever="angle_size", equation=s.final())
216246
s.set_equation(stage="morph", lever="torsion_k", equation=s.final())
217247
s.set_equation(stage="morph", lever="torsion_phase", equation=s.final())
248+
s.set_equation(stage="morph", force="ring-break", lever="alpha", equation=0)
249+
s.set_equation(stage="morph", force="ring-break", lever="kappa", equation=1)
250+
s.set_equation(stage="morph", force="ring-make", lever="alpha", equation=1)
251+
s.set_equation(stage="morph", force="ring-make", lever="kappa", equation=0)
218252

219253
return s
220254

@@ -223,7 +257,7 @@ def reverse_ring_break_morph():
223257
"""
224258
Build a lambda schedule for reverse ring-breaking perturbations.
225259
226-
Three stages: morph → bonded_perturb → potential_swap.
260+
Four stages: morph → ring_close → bonded_perturb → potential_swap.
227261
228262
Returns
229263
-------
@@ -234,7 +268,10 @@ def reverse_ring_break_morph():
234268
from sire.cas import LambdaSchedule as _LambdaSchedule
235269

236270
s = _LambdaSchedule.standard_morph()
271+
s.set_stage_weight("morph", 2)
237272

273+
# morph: standard nonbonded morphing. Ring-break fixed at fully open
274+
# (kappa=1, alpha=0); ring-make fixed at full interaction (kappa=1, alpha=0).
238275
s.set_equation(stage="morph", lever="morse_hard", equation=0)
239276
s.set_equation(stage="morph", lever="morse_soft", equation=0)
240277
s.set_equation(stage="morph", lever="bond_k", equation=s.initial())
@@ -243,8 +280,36 @@ def reverse_ring_break_morph():
243280
s.set_equation(stage="morph", lever="angle_size", equation=s.initial())
244281
s.set_equation(stage="morph", lever="torsion_k", equation=s.initial())
245282
s.set_equation(stage="morph", lever="torsion_phase", equation=s.initial())
283+
s.set_equation(stage="morph", force="ring-break", lever="alpha", equation=1)
284+
s.set_equation(stage="morph", force="ring-break", lever="kappa", equation=0)
285+
s.set_equation(stage="morph", force="ring-make", lever="alpha", equation=0)
286+
s.set_equation(stage="morph", force="ring-make", lever="kappa", equation=1)
287+
288+
# ring_close: non-bonded terms fixed at final; ring-make interaction ramps
289+
# off (alpha: 0→1, kappa: 1→0) to allow atoms to relax into ring geometry
290+
# before Morse is applied. Symmetric counterpart to ring_open.
291+
s.append_stage("ring_close", s.final(), weight=1)
292+
s.set_equation(stage="ring_close", lever="morse_hard", equation=0)
293+
s.set_equation(stage="ring_close", lever="morse_soft", equation=0)
294+
s.set_equation(stage="ring_close", lever="bond_k", equation=s.initial())
295+
s.set_equation(stage="ring_close", lever="bond_length", equation=s.initial())
296+
s.set_equation(stage="ring_close", lever="angle_k", equation=s.initial())
297+
s.set_equation(stage="ring_close", lever="angle_size", equation=s.initial())
298+
s.set_equation(stage="ring_close", lever="torsion_k", equation=s.initial())
299+
s.set_equation(stage="ring_close", lever="torsion_phase", equation=s.initial())
300+
s.set_equation(stage="ring_close", force="ring-break", lever="alpha", equation=1)
301+
s.set_equation(stage="ring_close", force="ring-break", lever="kappa", equation=0)
302+
s.set_equation(
303+
stage="ring_close", force="ring-make", lever="alpha", equation=s.lam()
304+
)
305+
s.set_equation(
306+
stage="ring_close", force="ring-make", lever="kappa", equation=1 - s.lam()
307+
)
246308

247-
s.append_stage("bonded_perturb", s.final())
309+
# bonded_perturb: Morse soft ramps on; ring-make already off from ring_close.
310+
# Ring-break softcore turns on as any ring-break bond opens (alpha: 1→0,
311+
# kappa: 0→1); ring-make stays off (kappa=0, alpha=1).
312+
s.append_stage("bonded_perturb", s.final(), weight=1)
248313
s.set_equation(stage="bonded_perturb", lever="morse_soft", equation=0 + s.lam())
249314
s.set_equation(stage="bonded_perturb", lever="morse_hard", equation=0)
250315
s.set_equation(stage="bonded_perturb", lever="bond_k", equation=s.initial())
@@ -269,8 +334,16 @@ def reverse_ring_break_morph():
269334
lever="torsion_phase",
270335
equation=(1 - s.lam()) * s.initial() + s.lam() * s.final(),
271336
)
337+
s.set_equation(
338+
stage="bonded_perturb", force="ring-break", lever="alpha", equation=1 - s.lam()
339+
)
340+
s.set_equation(
341+
stage="bonded_perturb", force="ring-break", lever="kappa", equation=s.lam()
342+
)
343+
s.set_equation(stage="bonded_perturb", force="ring-make", lever="alpha", equation=1)
344+
s.set_equation(stage="bonded_perturb", force="ring-make", lever="kappa", equation=0)
272345

273-
s.append_stage("potential_swap", s.final())
346+
s.append_stage("potential_swap", s.final(), weight=2)
274347
s.set_equation(stage="potential_swap", lever="morse_hard", equation=0 + s.lam())
275348
s.set_equation(stage="potential_swap", lever="morse_soft", equation=1 - s.lam())
276349
s.set_equation(

tests/conftest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,15 @@ def pert_rev_mols():
8686
mols = sr.load_test_files("somd1_backward.prm7", "somd1_backward.rst7")
8787
pert_file = str(Path(__file__).parent / "inputs" / "backward.pert")
8888
return apply_pert(mols, pert_file)
89+
90+
91+
@pytest.fixture(scope="session")
92+
def syk_ring_break_mols():
93+
"""
94+
Load the SYK 5035→5033 ring-breaking perturbation system.
95+
96+
Reference state (λ=0): SYK-5035 with an intact ring containing a
97+
breaking bond. Perturbed state (λ=1): SYK-5033, the open-chain analogue.
98+
"""
99+
mols = sr.load_test_files("syk_5035_5033.s3")
100+
return sr.morph.link_to_reference(mols)

0 commit comments

Comments
 (0)