Skip to content

Commit 76feb22

Browse files
committed
Apply restraints on receptor
1 parent 6bdcb82 commit 76feb22

File tree

6 files changed

+134
-9
lines changed

6 files changed

+134
-9
lines changed

bin/simulation/lightdock_setup.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from lightdock.util.parser import SetupCommandLineParser
1414
from lightdock.prep.simulation import read_input_structure, save_lightdock_structure, \
1515
calculate_starting_positions, prepare_results_environment, \
16-
create_setup_file, calculate_anm
16+
create_setup_file, calculate_anm, parse_restraints_file, \
17+
get_restraints
1718
from lightdock.constants import DEFAULT_LIGHTDOCK_PREFIX, DEFAULT_ELLIPSOID_DATA_EXTENSION, \
1819
DEFAULT_NMODES_REC, DEFAULT_REC_NM_FILE, DEFAULT_NMODES_LIG, DEFAULT_LIG_NM_FILE
1920
from lightdock.mathutil.ellipsoid import MinimumVolumeEllipsoid
@@ -35,8 +36,8 @@
3536
ligand = read_input_structure(args.ligand_pdb, args.noxt)
3637

3738
# Move structures to origin
38-
receptor.move_to_origin()
39-
ligand.move_to_origin()
39+
rec_translation = receptor.move_to_origin()
40+
lig_translation = ligand.move_to_origin()
4041

4142
# Calculate reference points for receptor
4243
log.info("Calculating reference points for receptor %s..." % args.receptor_pdb)
@@ -63,12 +64,34 @@
6364
calculate_anm(receptor, args.anm_rec, DEFAULT_REC_NM_FILE)
6465
calculate_anm(ligand, args.anm_lig, DEFAULT_LIG_NM_FILE)
6566

67+
# Parse restraints if any:
68+
if args.restraints:
69+
log.info("Reading restraints from %s" % args.restraints)
70+
restraints = parse_restraints_file(args.restraints)
71+
# Check if restraints have been defined for the ligand, but not to the receptor
72+
if len(restraints['ligand']) and not len(restrains['receptor']):
73+
raise LightDockError("Restraints defined for ligand, but not receptor. Try switching structures.")
74+
75+
if not len(restraints['receptor']) and not len(restraints['ligand']):
76+
raise LightDockError("Restraints file specified, but not a single restraint found")
77+
78+
# Check if restraints correspond with real residues
79+
receptor_restraints = get_restraints(receptor, restraints['receptor'])
80+
args.receptor_restraints = restraints['receptor']
81+
ligand_restraints = get_restraints(ligand, restraints['ligand'])
82+
args.ligand_restraints = restraints['ligand']
83+
6684
# Calculate surface points (swarm centers) over receptor structure
6785
starting_points_files = calculate_starting_positions(receptor, ligand,
6886
args.swarms, args.glowworms,
69-
args.starting_points_seed,
87+
args.starting_points_seed,
88+
receptor_restraints, ligand_restraints,
89+
rec_translation, lig_translation,
7090
args.ftdock_file, args.use_anm, args.anm_seed,
7191
args.anm_rec, args.anm_lig)
92+
if len(starting_points_files) != args.swarms:
93+
args.swarms = len(starting_points_files)
94+
log.info('Number of swarms is %d after applying restraints' % args.swarms)
7295

7396
# Create simulation folders
7497
prepare_results_environment(args.swarms)

lightdock/prep/poses.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"""Module to prepare initial poses for docking"""
22

33
import os
4+
import operator
45
from lightdock.pdbutil.PDBIO import create_pdb_from_points
56
from lightdock.prep.starting_points import calculate_surface_points
67
from lightdock.prep.ftdock import FTDockCoordinatesParser, classify_ftdock_poses
78
from lightdock.mathutil.lrandom import MTGenerator, NormalGenerator
89
from lightdock.mathutil.cython.quaternion import Quaternion
10+
from lightdock.mathutil.cython.cutil import distance as cdistance
911
from lightdock.constants import CLUSTERS_CENTERS_FILE,\
1012
DEFAULT_PDB_STARTING_PREFIX, DEFAULT_STARTING_PREFIX, DEFAULT_BILD_STARTING_PREFIX, DEFAULT_EXTENT_MU, \
1113
DEFAULT_EXTENT_SIGMA
@@ -51,8 +53,32 @@ def create_file_from_poses(pos_file_name, poses):
5153
positions_file.close()
5254

5355

56+
def apply_restraints(swarm_centers, receptor_restraints, distance_cutoff, translation, swarms_per_restraint=10):
57+
58+
closer_swarms = []
59+
for i, residue in enumerate(receptor_restraints):
60+
distances = {}
61+
ca = residue.get_calpha()
62+
for swarm_id, center in enumerate(swarm_centers):
63+
distances[swarm_id] = cdistance(ca.x + translation[0], ca.y + translation[1], ca.z + translation[2],
64+
center[0], center[1], center[2])
65+
sorted_distances = sorted(distances.items(), key=operator.itemgetter(1))
66+
swarms_considered = 0
67+
for swarm in sorted_distances:
68+
swarm_id, distance = swarm[0], swarm[1]
69+
if distance <= distance_cutoff:
70+
closer_swarms.append(swarm_id)
71+
swarms_considered += 1
72+
if swarms_considered == swarms_per_restraint:
73+
break
74+
closer_swarms = list(set(closer_swarms))
75+
return closer_swarms
76+
77+
5478
def calculate_initial_poses(receptor, ligand, num_clusters, num_glowworms,
55-
seed, dest_folder, ftdock_file='', nm_mode=False, nm_seed=0, rec_nm=0, lig_nm=0):
79+
seed, receptor_restraints, ligand_restraints,
80+
rec_translation, lig_translation,
81+
dest_folder, ftdock_file='', nm_mode=False, nm_seed=0, rec_nm=0, lig_nm=0):
5682
"""Calculates the starting points for each of the glowworms using the center of clusters
5783
and FTDock poses.
5884
"""
@@ -70,6 +96,11 @@ def calculate_initial_poses(receptor, ligand, num_clusters, num_glowworms,
7096
ligand,
7197
num_clusters,
7298
distance_step=1.0)
99+
# Filter cluster centers far from the restraints
100+
if receptor_restraints:
101+
filtered_swarms = apply_restraints(cluster_centers, receptor_restraints, ligand_diameter / 2., rec_translation)
102+
cluster_centers = [cluster_centers[i] for i in filtered_swarms]
103+
73104
pdb_file_name = os.path.join(dest_folder, CLUSTERS_CENTERS_FILE)
74105
create_pdb_from_points(pdb_file_name, cluster_centers)
75106

lightdock/prep/simulation.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,9 @@ def check_starting_file(file_name, glowworms, use_anm, anm_rec, anm_lig):
9292
return num_glowworms == glowworms
9393

9494

95-
def calculate_starting_positions(receptor, ligand, swarms, glowworms, starting_points_seed,
96-
ftdock_file=None, use_anm=False, anm_seed=0, anm_rec=DEFAULT_NMODES_REC, anm_lig=DEFAULT_NMODES_LIG):
95+
def calculate_starting_positions(receptor, ligand, swarms, glowworms, starting_points_seed,
96+
receptor_restraints, ligand_restraints, rec_translation, lig_translation, ftdock_file=None,
97+
use_anm=False, anm_seed=0, anm_rec=DEFAULT_NMODES_REC, anm_lig=DEFAULT_NMODES_LIG):
9798
"""Defines the starting positions of each glowworm in the simulation.
9899
99100
If the init_folder already exists, uses the starting positions from this folder.
@@ -104,12 +105,19 @@ def calculate_starting_positions(receptor, ligand, swarms, glowworms, starting_p
104105
os.mkdir(init_folder)
105106
starting_points_files = calculate_initial_poses(receptor, ligand,
106107
swarms, glowworms,
107-
starting_points_seed, init_folder,
108+
starting_points_seed,
109+
receptor_restraints, ligand_restraints,
110+
rec_translation, lig_translation,
111+
init_folder,
108112
ftdock_file, use_anm,
109113
anm_seed, anm_rec, anm_lig)
110114
log.info("Generated %d positions files" % len(starting_points_files))
111115
else:
112-
log.warning("Folder %s already exists, skipping calculation" % init_folder)
116+
if receptor_restraints:
117+
log.warning("Folder %s already exists and restraints apply. Check for consistency, possible error!" % init_folder)
118+
else:
119+
log.warning("Folder %s already exists, skipping calculation" % init_folder)
120+
113121
pattern = os.path.join(DEFAULT_POSITIONS_FOLDER, "%s*.dat" % DEFAULT_STARTING_PREFIX)
114122
starting_points_files = glob.glob(pattern)
115123
if len(starting_points_files) != swarms:
@@ -197,3 +205,49 @@ def get_default_box(use_anm, anm_rec, anm_lig):
197205
boundaries.extend([Boundary(MIN_EXTENT, MAX_EXTENT) for _ in xrange(anm_lig)])
198206

199207
return BoundingBox(boundaries)
208+
209+
210+
def parse_restraints_file(restraints_file_name):
211+
with open(restraints_file_name) as input_restraints:
212+
raw_restraints = [line.rstrip(os.linesep) for line in input_restraints.readlines()]
213+
restraints = {'receptor': [], 'ligand': []}
214+
for restraint in raw_restraints:
215+
if restraint and restraint[0] in ['R', 'L']:
216+
try:
217+
fields = restraint[2:].split('.')
218+
# Only consider first character if many
219+
chain_id = fields[0][0].upper()
220+
# Only considering 3 chars if many
221+
residue = fields[1][0:3].upper()
222+
# Check for integer
223+
residue_number = int(fields[2])
224+
parsed_restraint = "%s.%s.%d" % (chain_id, residue, residue_number)
225+
if restraint[0] == 'R':
226+
restraints['receptor'].append(parsed_restraint)
227+
else:
228+
restraints['ligand'].append(parsed_restraint)
229+
except:
230+
log.warning('Ignoring restraints %s' % restraint)
231+
232+
# Make unique the restraints:
233+
restraints['receptor'] = list(set(restraints['receptor']))
234+
restraints['ligand'] = list(set(restraints['ligand']))
235+
236+
return restraints
237+
238+
239+
def get_restraints(structure, restraints):
240+
"""Check for each restraint in the format Chain.ResidueName.ResidueNumber in
241+
restraints if they exist in the given structure.
242+
"""
243+
residues = []
244+
for restraint in restraints:
245+
chain_id, residue_name, residue_number = restraint.split('.')
246+
residue = structure.get_residue(chain_id, residue_name, residue_number)
247+
residue
248+
if not residue:
249+
raise LightDockError("Restraint %s not found in structure" % restraint)
250+
else:
251+
residues.append(residue)
252+
return residues
253+

lightdock/structure/complex.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ def move_to_origin(self):
108108
"""Moves the structure to the origin of coordinates"""
109109
translation = [-1*c for c in self.center_of_coordinates()]
110110
self.translate(translation)
111+
return translation
112+
113+
def get_residue(self, chain_id, residue_name, residue_number):
114+
for chain in self.chains:
115+
if chain_id == chain.cid:
116+
for residue in chain.residues:
117+
if residue.name == residue_name and int(residue.number) == int(residue_number):
118+
return residue
119+
return None
111120

112121
def __getitem__(self, item):
113122
return self.atom_coordinates[item]

lightdock/structure/residue.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ def get_atom(self, atom_name):
8585
return atom
8686
return None
8787

88+
def get_calpha(self):
89+
"""Get the Calpha atom"""
90+
return self.get_atom('CA')
91+
8892
def get_non_hydrogen_atoms(self):
8993
return [atom for atom in self.atoms if not atom.is_hydrogen()]
9094

lightdock/util/parser.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ def __init__(self):
9797
parser.add_argument("-anm_lig", "--anm_lig", help="Number of ANM modes for ligand",
9898
type=SetupCommandLineParser.valid_integer_number,
9999
dest="anm_lig", default=DEFAULT_NMODES_LIG)
100+
# Restraints file
101+
parser.add_argument("-rst", "--rst", help="Restraints file",
102+
dest="restraints", type=CommandLineParser.valid_file,
103+
metavar="restraints", default=None)
100104

101105
self.args = parser.parse_args()
102106

0 commit comments

Comments
 (0)