Skip to content

Commit 2c0909b

Browse files
author
Steven Ayoub
committed
Created Boresch restraint integrator within RandomLigandRotation
1 parent c2306a7 commit 2c0909b

3 files changed

Lines changed: 136 additions & 129 deletions

File tree

blues/integrators.py

Lines changed: 86 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -310,140 +310,120 @@ def __init__(self,
310310
nsteps_neq=0,
311311
nprop=1,
312312
prop_lambda=0.3,
313-
lambda_restraints='max(0, 1 - 2*abs(lambda - 0.5))', # tent fn (0→1 at 0.5→0)
313+
lambda_restraints = 'max(0, 1-(1/0.10)*abs(lambda-0.5))',
314+
#relax_steps=500, #'max(0, 1-(1/0.10)*abs(lambda-0.5))', #"3*lambda^2 - 2*lambda^3", # old: 'max(0, 1-(1/0.10)*abs(lambda-0.5))'
314315
relax_steps=50,
315316
*args, **kwargs):
316-
317-
# Store the expression string separately to avoid name collisions
318-
self.lambda_restraints_expr = lambda_restraints
319-
320-
# Which energy group holds the restraint energy (so we can subtract it cleanly)
321-
self.restraint_energy_group = str(restraint_group)
322-
self.restraint_energy_ref = f"energy{self.restraint_energy_group}"
323-
324-
super().__init__(
325-
alchemical_functions=alchemical_functions,
326-
splitting=splitting,
327-
temperature=temperature,
328-
collision_rate=collision_rate,
329-
timestep=timestep,
330-
constraint_tolerance=constraint_tolerance,
331-
measure_shadow_work=measure_shadow_work,
332-
measure_heat=measure_heat,
333-
nsteps_neq=nsteps_neq,
334-
nprop=nprop,
335-
prop_lambda=prop_lambda,
336-
*args, **kwargs
337-
)
338-
317+
318+
self.lambda_restraints = lambda_restraints
319+
self.restraint_energy = "energy"+str(restraint_group)
320+
321+
super(AlchemicalExternalRestrainedLangevinIntegrator, self).__init__(
322+
alchemical_functions,
323+
splitting,
324+
temperature,
325+
collision_rate,
326+
timestep,
327+
constraint_tolerance,
328+
measure_shadow_work,
329+
measure_heat,
330+
nsteps_neq,
331+
nprop,
332+
prop_lambda,
333+
*args, **kwargs)
334+
335+
try:
336+
self.addGlobalVariable("restraint_energy", 0)
337+
except:
338+
pass
339339
logger.info(f'[LAMBDA STEPS] N_lambda_steps: {self._n_lambda_steps}')
340-
341-
# === Declare NEW integrator globals (names MUST match force globals if you want propagation) ===
340+
# Only declare NEW variables
342341
self.addGlobalVariable("debug_lambda", 0.0)
343-
self.addGlobalVariable("restraint_energy", 0.0) # running value read via energy group
342+
#self.addGlobalVariable("restraint_energy", 0.0)
344343

344+
# Set existing globals from parent
345+
# self.setGlobalVariableByName("lambda_step", 0.0)
346+
# self.setGlobalVariableByName("lambda", 0.0)
345347

346348
# Optional debug
347349
self.addComputeGlobal("debug_lambda", "lambda")
348350

349-
logger.info(f"Current nsteps_neq: {nsteps_neq}")
350-
logger.info(f'lambda_restraints expression: {self.lambda_restraints_expr}')
351-
351+
# Now safe to use lambda_restraints in update
352+
#self.updateRestraints()
352353

354+
logger.info(f"Current nsteps_neq: {nsteps_neq}")
355+
logger.info(f'lambda_restraints selected: {self.lambda_restraints}')
356+
# compute the mid‐point slice index once
357+
mid = int(self._n_lambda_steps/2)
358+
self.addGlobalVariable("mid_step", float(mid))
359+
self.addGlobalVariable("relax_counter", 0.0)
360+
self.addGlobalVariable("relax_steps", float(relax_steps))
353361

354-
# ---- Helper: recompute + propagate lambda_restraints each time lambda changes ----
355-
def _recompute_and_push_lambda_restraints(self):
356-
# 1) Recompute integrator global from current 'lambda'
357-
self.addComputeGlobal('lambda_restraints', self.lambda_restraints_expr)
358-
# 2) Propagate integrator globals -> Context parameters (names must match in forces)
359-
self.addUpdateContextState()
362+
def updateRestraints(self):
363+
logger.info(f"UPDATE RESTAINTS: {self.lambda_restraints}")
364+
self.addComputeGlobal('lambda_restraints', self.lambda_restraints)
360365

361-
# ---- Helper: refresh restraint energy and split PE bookkeeping ----
362-
def _refresh_restraint_and_pes(self):
363-
self.addComputeGlobal("restraint_energy", self.restraint_energy_ref)
364-
# Exclude restraint energy so protocol work uses only alchemical parts
365-
self.addComputeGlobal("perturbed_pe", "energy - restraint_energy")
366-
self.addComputeGlobal("unperturbed_pe", "energy - restraint_energy")
367366

368367
def _add_integrator_steps(self):
369368
"""
370-
Override to insert (a) consistent initialization and (b) frequent
371-
lambda_restraints recompute + propagation around each propagation call.
369+
Override the base class to insert reset steps around the integrator.
372370
"""
373-
374-
# =========================
375-
# Initialization (step == 0)
376-
# =========================
371+
372+
# First step: Constrain positions and velocities and reset work accumulators and alchemical integrators
373+
logger.info("sams protocol")
377374
self.beginIfBlock('step = 0')
378-
379-
# Make sure restraints start consistent with lambda=0
380-
self._recompute_and_push_lambda_restraints()
381-
self._refresh_restraint_and_pes()
382-
383-
# Usual initialization: constrain and reset accumulators
375+
self.addComputeGlobal("restraint_energy", self.restraint_energy)
376+
self.addComputeGlobal("perturbed_pe", "energy - restraint_energy")
377+
self.addComputeGlobal("unperturbed_pe", "energy - restraint_energy")
384378
self.addConstrainPositions()
385379
self.addConstrainVelocities()
386380
self._add_reset_protocol_work_step()
387381
self._add_alchemical_reset_step()
382+
self.endBlock()
388383

389-
self.endBlock() # end init
390-
391-
# =========================
392384
# Main body
393-
# =========================
385+
394386
if self._n_steps_neq == 0:
395-
# Force a single execution on first step
387+
# If nsteps = 0, we need to force execution on the first step only.
396388
self.beginIfBlock('step = 0')
397-
# Track lambda_restraints before the parent propagation
398-
self._recompute_and_push_lambda_restraints()
399-
self._refresh_restraint_and_pes()
400-
super()._add_integrator_steps() # call parent’s propagation
389+
super(AlchemicalNonequilibriumLangevinIntegrator, self)._add_integrator_steps()
401390
self.addComputeGlobal("step", "step + 1")
402391
self.endBlock()
403-
return
404-
405-
# NEQ loop
406-
self.beginIfBlock("step < n_lambda_steps")
407-
408-
# --- First substep at this NEQ step ---
409-
# Update lambda_restraints to track the *new* lambda for this substep
410-
self._recompute_and_push_lambda_restraints()
411-
self._refresh_restraint_and_pes()
412-
413-
# First-step bookkeeping (only once)
414-
self.beginIfBlock("first_step < 1")
415-
self.addComputeGlobal("first_step", "1")
416-
self._refresh_restraint_and_pes()
417-
self.endBlock()
418-
419-
# Protocol work increment (before propagation) using split PEs
420-
self.addComputeGlobal("protocol_work", "protocol_work + (perturbed_pe - unperturbed_pe)")
421-
422-
# === Primary propagation for this lambda value ===
423-
self._recompute_and_push_lambda_restraints()
424-
super()._add_integrator_steps() # parent does the actual R/V/O/H... steps
425-
426-
# ============ Optional extra propagation windows at same lambda ============
427-
self.beginIfBlock("lambda > prop_lambda_min")
428-
self.beginIfBlock("lambda <= prop_lambda_max")
429-
430-
self.beginWhileBlock("prop < nprop")
431-
self.addComputeGlobal("prop", "prop + 1")
432-
433-
self._recompute_and_push_lambda_restraints()
434-
super()._add_integrator_steps()
435-
436-
self.endBlock() # while
437-
self.endBlock() # if <= prop_lambda_max
438-
self.endBlock() # if > prop_lambda_min
392+
else:
393+
#call the superclass function to insert the appropriate steps, provided the step number is less than n_steps
394+
self.beginIfBlock("step < n_lambda_steps")#
395+
self.addComputeGlobal("restraint_energy", self.restraint_energy)
396+
self.addComputeGlobal("perturbed_pe", "energy - restraint_energy")
397+
self.beginIfBlock("first_step < 1")##
398+
#TODO write better test that checks that the initial work isn't gigantic
399+
self.addComputeGlobal("first_step", "1")
400+
self.addComputeGlobal("restraint_energy", self.restraint_energy)
401+
self.addComputeGlobal("unperturbed_pe", "energy-restraint_energy")
402+
self.endBlock()##
403+
#initial iteration
404+
# Gill put this first:
405+
self.addComputeGlobal("protocol_work",
406+
"protocol_work + (perturbed_pe - unperturbed_pe)"
407+
)
408+
super(AlchemicalNonequilibriumLangevinIntegrator, self)._add_integrator_steps()
409+
logger.info("COMPUTE WORKSSSS")
410+
#if more propogation steps are requested
411+
self.beginIfBlock("lambda > prop_lambda_min")###
412+
self.beginIfBlock("lambda <= prop_lambda_max")####
439413

440-
# ---- End-of-step bookkeeping ----
441-
# Recompute to ensure end-of-step energies are coherent
442-
self._recompute_and_push_lambda_restraints()
443-
self._refresh_restraint_and_pes()
414+
self.beginWhileBlock("prop < nprop")#####
415+
self.addComputeGlobal("prop", "prop + 1")
444416

445-
self.addComputeGlobal("step", "step + 1")
446-
self.addComputeGlobal("prop", "1")
417+
super(AlchemicalNonequilibriumLangevinIntegrator, self)._add_integrator_steps()
418+
self.endBlock()#####
419+
self.endBlock()####
420+
self.endBlock()###
421+
#ending variables to reset
422+
self.updateRestraints()
423+
self.addComputeGlobal("restraint_energy", self.restraint_energy)
424+
self.addComputeGlobal("unperturbed_pe", "energy-restraint_energy")
425+
self.addComputeGlobal("step", "step + 1")
426+
self.addComputeGlobal("prop", "1")
447427

448-
self.endBlock() # if step < n_lambda_steps
428+
self.endBlock()#
449429

blues/moves.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ def beforeMove(self, context):
483483
if not self.restraints:
484484
return context
485485
for i in range(len(self.binding_mode_traj)):
486-
context.setParameter(f'restraint_pose_{i}', 0.0)
486+
context.setParameter(f'restraint_pose_{i}', 1.0)
487487

488488
return context
489489

blues/simulation.py

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,19 @@ def _syncStatesMDtoNCMC(self):
10431043

10441044
def _stepNCMC(self, nstepsNC, moveStep, move_engine=None):
10451045
"""Advance the NCMC simulation."""
1046+
#print("Running _stepNCMC...")
1047+
#print(f"nsteps: {nstepsNC}, moveStep: {moveStep}")
10461048

1049+
self.worker_tracker[self.currentIter] = []
1050+
self.lambda_tracker[self.currentIter] = []
1051+
self.steric_tracker[self.currentIter] = []
1052+
self.restraint_tracker[self.currentIter] = []
1053+
1054+
logger.info('Advancing %i NCMC switching steps...' % (nstepsNC))
1055+
1056+
1057+
# Retrieve NCMC state before proposed move
1058+
logger.info('running getStateFromContext')
10471059
ncmc_state0 = self.getStateFromContext(self._ncmc_sim.context, self._state_keys)
10481060
#print(ncmc_state0['positions'])
10491061
self._setStateTable('ncmc', 'state0', ncmc_state0)
@@ -1062,12 +1074,8 @@ def _stepNCMC(self, nstepsNC, moveStep, move_engine=None):
10621074
try:
10631075
if not step:
10641076
logger.info("Calling beforeMove()")
1065-
self._ncmc_sim.context = move_engine.selected_move.beforeMove(self._ncmc_sim.context)
1066-
try:
1067-
lambda_rest = self._ncmc_sim.context.getParameter("lambda_restraints")
1068-
logger.info(f"[Step {step}] lambda_restraints = {lambda_rest}")
1069-
except Exception as e:
1070-
logger.warning(f"[Step {step}] Could not retrieve lambda_restraints: {e}")
1077+
self._ncmc_sim.context = move_engine.selected_move.beforeMove(self._ncmc_sim.context)
1078+
10711079
if step == moveStep:
10721080
if hasattr(logger, 'report'):
10731081
logger.info = logger.report
@@ -1082,9 +1090,7 @@ def _stepNCMC(self, nstepsNC, moveStep, move_engine=None):
10821090
# Perform the NCMC move (lambda 0 → 0.5 and apply move)
10831091
self._ncmc_sim.context = move_engine.runEngine(self._ncmc_sim.context)
10841092

1085-
# state = self._ncmc_sim.context.getState(getPositions=True, getEnergy=True)
1086-
# steric_state = self._ncmc_sim.context.getState(getEnergy=True, groups={move_engine.selected_move.steric_group})
1087-
# steric_energy = steric_state.getPotentialEnergy().value_in_unit(unit.kilojoules_per_mole)
1093+
10881094
lambda_val = self._ncmc_sim.context._integrator.getGlobalVariableByName("lambda")
10891095
if abs(lambda_val - 0.0) < 1e-4 or abs(lambda_val - 0.5) < 1e-4 or abs(lambda_val - 0.9) < 1e-4:
10901096
steric_state = self._ncmc_sim.context.getState(getEnergy=True, groups={move_engine.selected_move.steric_group})
@@ -1095,17 +1101,19 @@ def _stepNCMC(self, nstepsNC, moveStep, move_engine=None):
10951101
lambda_s = self._ncmc_sim.context.getParameter("lambda_sterics")
10961102
lambda_e = self._ncmc_sim.context.getParameter("lambda_electrostatics")
10971103
logger.info(f"[Step {step}] λ_sterics = {lambda_s}, λ_electrostatics = {lambda_e}")
1098-
try:
1099-
lambda_rest = self._ncmc_sim.context.getParameter("lambda_restraints")
1100-
logger.info(f"[Step {step}] lambda_restraints = {lambda_rest}")
1101-
except Exception as e:
1102-
logger.warning(f"[Step {step}] Could not retrieve lambda_restraints: {e}")
1104+
self._log_restraint_energies(step=step, move_engine = move_engine)
1105+
11031106
self._ncmc_sim.step(1)
11041107

11051108
integrator = self._ncmc_sim.context._integrator
11061109
lambda_val = integrator.getGlobalVariableByName("lambda")
1110+
protocol_work = integrator.getGlobalVariableByName("protocol_work")
11071111
#logger.info(f"[NCMC step {step}] lambda = {lambda_val:.6f} | protocol_work = {protocol_work:.4f}")
1108-
#self._log_restraint_energies(step=step, move_engine = move_engine, print_output=False)
1112+
self.worker_tracker[self.currentIter].append(protocol_work)
1113+
self.lambda_tracker[self.currentIter].append(lambda_val)
1114+
steric_state = self._ncmc_sim.context.getState(getEnergy=True, groups={move_engine.selected_move.steric_group})
1115+
steric_energy = steric_state.getPotentialEnergy().value_in_unit(unit.kilojoules_per_mole)
1116+
self.steric_tracker[self.currentIter].append(steric_energy)
11091117

11101118
if step == lastStep:
11111119
logger.info("AFTER MOVE WILL BE CALLED")
@@ -1116,20 +1124,14 @@ def _stepNCMC(self, nstepsNC, moveStep, move_engine=None):
11161124
lambda_s = self._ncmc_sim.context.getParameter("lambda_sterics")
11171125
lambda_e = self._ncmc_sim.context.getParameter("lambda_electrostatics")
11181126
logger.info(f"[Step {step}] λ_sterics = {lambda_s}, λ_electrostatics = {lambda_e}")
1119-
#self._log_restraint_energies(step=step, move_engine = move_engine)
1127+
self._log_restraint_energies(step=step, move_engine = move_engine)
11201128
# Log sterics after move
11211129
try:
11221130
steric_state = self._ncmc_sim.context.getState(getEnergy=True, groups={move_engine.selected_move.steric_group})
11231131
steric_energy = steric_state.getPotentialEnergy().value_in_unit(unit.kilojoules_per_mole)
11241132
logger.info(f"[Step {step}] Steric Energy AFTER MOVE (group {move_engine.selected_move.steric_group}): {steric_energy:.4f} kJ/mol")
11251133
except Exception as e:
11261134
logger.warning(f"[Step {step}] Could not retrieve steric energy after move: {e}")
1127-
1128-
try:
1129-
lambda_rest = self._ncmc_sim.context.getParameter("lambda_restraints")
1130-
logger.info(f"[Step {step}] lambda_restraints = {lambda_rest}")
1131-
except Exception as e:
1132-
logger.warning(f"[Step {step}] Could not retrieve lambda_restraints: {e}")
11331135

11341136
self._ncmc_sim.context = move_engine.selected_move.afterMove(self._ncmc_sim.context)
11351137
# Debug: print positions after afterMove
@@ -1145,6 +1147,26 @@ def _stepNCMC(self, nstepsNC, moveStep, move_engine=None):
11451147
ncmc_state1 = self.getStateFromContext(self._ncmc_sim.context, self._state_keys)
11461148
self._setStateTable('ncmc', 'state1', ncmc_state1)
11471149

1150+
1151+
def _log_restraint_energies(self, step, move_engine, print_output=True):
1152+
1153+
try:
1154+
for name, group in move_engine.selected_move.restraint_groups.items():
1155+
state = self._ncmc_sim.context.getState(getEnergy=True, groups={group})
1156+
energy = state.getPotentialEnergy().value_in_unit(unit.kilojoules_per_mole)
1157+
if print_output:
1158+
logger.info(f"[Step {step}] {name} restraint energy (group {group}): {energy:.4f} kJ/mol")
1159+
else:
1160+
self.restraint_tracker[self.currentIter].append(energy)
1161+
1162+
lambda_rest = self._ncmc_sim.context.getParameter("lambda_restraints")
1163+
logger.info(f"[Step {step}] lambda_restraints = {lambda_rest}")
1164+
1165+
except Exception as e:
1166+
pass
1167+
# logger.warning(f"[Step {step}] Could not retrieve lambda_restraints: {e}")
1168+
1169+
11481170
# def _stepNCMC(self, nstepsNC, moveStep, move_engine=None):
11491171
# """Advance the NCMC simulation."""
11501172
# #print("Running _stepNCMC...")
@@ -1355,6 +1377,11 @@ def run(self, nIter=0, nstepsNC=0, moveStep=0, nstepsMD=0, temperature=300, writ
13551377
if not nstepsMD: nstepsMD = self._config['nstepsMD']
13561378
if not moveStep: moveStep = self._config['moveStep']
13571379

1380+
self.worker_tracker = {}
1381+
self.lambda_tracker = {}
1382+
self.steric_tracker = {}
1383+
self.restraint_tracker = {}
1384+
13581385
logger.info('Running %i BLUES iterations...' % (nIter))
13591386
for N in range(int(nIter)):
13601387
self.currentIter = N

0 commit comments

Comments
 (0)