Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
LigandBaseProtocol.cc
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// @file protocols/ligand_docking/LigandBaseProtocol.cc
11 ///
12 /// @brief
13 /// @author Ian W. Davis
14 
15 
17 
18 #include <core/graph/Graph.hh>
19 
24 
26 
27 #include <core/id/AtomID.hh>
28 #include <basic/database/open.hh>
29 
33 // AUTO-REMOVED #include <core/kinematics/visualize.hh>
34 
35 #include <basic/options/option.hh>
36 //#include <core/pack/packer_neighbors.hh>
40 #include <core/pose/Pose.hh>
41 #include <core/scoring/Energies.hh>
44 // AUTO-REMOVED #include <core/scoring/constraints/ConstraintSet.hh>
46 //#include <core/scoring/constraints/Func.hh>
50 
51 #include <basic/Tracer.hh>
52 
53 #include <protocols/loops/Loop.hh>
54 // AUTO-REMOVED #include <protocols/loops/loops_main.hh>
57 
58 #include <ObjexxFCL/FArray1D.hh>
59 //#include <ObjexxFCL/FArray1.io.hh>
60 // AUTO-REMOVED #include <numeric/conversions.hh>
61 #include <numeric/random/random.hh>
62 
63 #include <algorithm>
64 #include <cmath>
65 
66 
67 // option key includes
68 
69 #include <basic/options/keys/docking.OptionKeys.gen.hh>
70 #include <basic/options/keys/enzdes.OptionKeys.gen.hh>
71 #include <basic/options/keys/score.OptionKeys.gen.hh>
72 
74 #include <core/pose/util.hh>
75 #include <utility/vector1.hh>
76 
77 //Auto Headers
80 
81 
82 
83 
84 namespace protocols {
85 namespace ligand_docking {
86 
87 
88 using namespace ObjexxFCL;
89 
90 
91 static basic::Tracer TR("protocols.ligand_docking.LigandBaseProtocol");
92 static numeric::random::RandomGenerator my_RG(810323); // <- Magic number, do not change it!!!
93 
94 
96  Mover(),
97  sc_interface_padding_(0),
98  bb_interface_cutoff_(0)
99 {
100  Mover::type( "LigandBaseProtocol" );
101 
103  unboundrot_->initialize_from_command_line();
104 
105  using namespace basic::options;
106  use_soft_rep_ = option[ OptionKeys::docking::ligand::soft_rep ];
107  bool const rosetta_electrostatics = option[ OptionKeys::docking::ligand::old_estat ];
108  bool const hbonds_downweight = true;
109 
110  // Set up scoring function
111  // Meiler & Baker 06 uses the soft-repulsive weights, but David wants me to use the hard ones...
112  // TODO: look into using standard weights (score12) as the base, with hack_elec added in and hbonds set to 1.3 (Lin's recommendation)
113 
114  if ( option[ OptionKeys::docking::ligand::tweak_sxfn ] ) {
115  TR.Debug << "Using regular tweaked scorefunctions" << std::endl;
116  hard_scorefxn_ = make_tweaked_scorefxn("ligand", rosetta_electrostatics, rosetta_electrostatics, hbonds_downweight);
117  soft_scorefxn_ = make_tweaked_scorefxn("ligand_soft_rep", rosetta_electrostatics, rosetta_electrostatics, hbonds_downweight);
118  } else {
119  // Use plain scorefunctions - user specified ones if given, else the regular ones (but non-tweaked).
120  // Note that you should now be able to specify exclude_protein_protein_hack_elec in the weights files.
121  if ( option[ OptionKeys::score::weights ].user() || option[ OptionKeys::score::patch ].user() ) {
122  TR.Debug << "Using untweaked command-line specified (hard) scorefunction. " << std::endl;
124  } else {
125  TR.Debug << "Using untweaked ligand.wts hard scorefunction." << std::endl;
127  }
128  if ( option[ OptionKeys::score::soft_wts ].user() ) {
129  TR.Debug << "Using untweaked -score:soft_wts specified soft scorefunction." << std::endl;
130  soft_scorefxn_ = core::scoring::ScoreFunctionFactory::create_score_function( option[ OptionKeys::score::soft_wts ] );
131  } else {
132  TR.Debug << "Using untweaked ligand_soft_rep.wts soft scorefunction." << std::endl;
134  }
135  }
136 
137  // "Default" score is always hard; use soft-rep only inside of apply()
138  scorefxn_ = hard_scorefxn_; //( use_soft_rep_ ? soft_scorefxn_ : hard_scorefxn_ );
139 }
140 
141 
143 
145  return scorefxn_;
146 }
148  return scorefxn_;
149 }
150 
151 void LigandBaseProtocol::apply( core::pose::Pose & /*pose*/ ) { utility_exit_with_message("Not intended to actually be used!");}
152 
153 
156  return "LigandBaseProtocol";
157 }
158 
161  std::string const & weights_tag,
162  bool estat_exclude_protein,
163  bool estat_upweight,
164  bool hbonds_downweight
165 )
166 {
167  using namespace core::scoring;
168 
169  ScoreFunctionOP sfxn = new ScoreFunction();
170  sfxn->reset();
171 
172  // manipulate EnergyMethodOptions here
173  methods::EnergyMethodOptions options( sfxn->energy_method_options() );
174  options.exclude_protein_protein_hack_elec( estat_exclude_protein );
175  sfxn->set_energy_method_options( options );
176 
177  sfxn->add_weights_from_file( basic::database::full_name( "scoring/weights/"+weights_tag+".wts" ) );
178 
179  // Tiny weight here (like standard.wts) presumably eliminates the worst intra-ligand clashes...
180  // Weight increased because I was still getting significant overlaps between ligand atoms,
181  // enough to get new "bonds" in PyMol (though no more interpenetrating rings even at 0.004).
182  // However, using 0.04 meant total fa_intra_rep ~ 20 in most structures, which I'm concerned
183  // biased the selection of ligand conformers too much. Needs more rigorous testing.
184  if( sfxn->has_zero_weight( fa_intra_rep ) ) sfxn->set_weight( fa_intra_rep, 0.004 ); // from standard.wts
185 
186  // For some reason, electrostatics is not in the .wts files...
187  // hack_elec has a different dielectric constant than Rosetta++ (10r vs. 6r in ++)
188  // It also includes all atom pairs instead of only ligand-protein interactions.
189  if( sfxn->has_zero_weight( hack_elec ) ) sfxn->set_weight( hack_elec, 0.25 ); // from Meiler & Baker 2006
190 
191  if( estat_upweight ) sfxn->set_weight( hack_elec, (10./6.) * sfxn->get_weight( hack_elec ) ); // make like Rosetta++
192 
193  if( hbonds_downweight ) {
194  sfxn->set_weight( hbond_sc, 1.30 ); // from Lin Jiang
195  sfxn->set_weight( hbond_bb_sc, 1.30 ); // from Lin Jiang
196  }
197 
198  // You can adjust later, but at least make sure it's enabled!
199  // Shouldn't cause problems when no constraints are present.
200  // Need atom_pair_constraint and angle_constraint to work with EnzDes constraints.
201  if( sfxn->has_zero_weight( coordinate_constraint ) ) sfxn->set_weight( coordinate_constraint, 1.0 );
202  if( sfxn->has_zero_weight( atom_pair_constraint ) ) sfxn->set_weight( atom_pair_constraint, 1.0 );
203  if( sfxn->has_zero_weight( angle_constraint ) ) sfxn->set_weight( angle_constraint, 1.0 );
204  if( sfxn->has_zero_weight( dihedral_constraint ) ) sfxn->set_weight( dihedral_constraint, 1.0 );
205  if( sfxn->has_zero_weight( chainbreak ) ) sfxn->set_weight( chainbreak, 1.0 );
206  if( sfxn->has_zero_weight( omega ) ) sfxn->set_weight( omega, 0.5 ); // from score12.wts_patch
207  if( sfxn->has_zero_weight( rama ) ) sfxn->set_weight( rama, 0.2 ); // from score12.wts_patch
208 
209  return sfxn;
210 }
211 
212 /// @details First discards ligands that aren't touching, then takes the top 5% by total_score.
213 /// (Take given number of poses if to_keep > 1.0).
217  core::Real to_keep /* = 0.05 */
218 )
219 {
220  // Keep only the top 5% by total score
221  //scores_out.reserve( scores_in.size() );
222  //std::cout << "scores_in.size() = " << scores_in.size() << "\n";
223  //std::cout << "scores_out.size() = " << scores_out.size() << " (0)\n";
224  for(core::Size ii = 1; ii <= scores_in.size(); ++ii) {
225  // Drop out cases where the ligand isn't touching the protein
226  core::import_pose::atom_tree_diffs::Scores scores = scores_in[ii].second;
227  if( scores.find("ligand_is_touching") == scores.end()
228  || scores["ligand_is_touching"] != 0 ) {
229  scores_out.push_back( scores_in[ii] );
230  }
231  }
232  //std::cout << "scores_out.size() = " << scores_out.size() << " (1)\n";
234  //std::cout << "scores_out.size() = " << scores_out.size() << " (2)\n";
235  if( to_keep <= 1.0 && to_keep >= 0.0 ) {
236  scores_out.resize( (core::Size) std::ceil(to_keep * scores_out.size()) );
237  } else if ( to_keep > 1.0 && scores_out.size() > to_keep ){
238  scores_out.resize( (core::Size) std::ceil(to_keep) );
239  } else {
240  utility_exit_with_message("Cannot select a negative quantity of poses.");
241  }
242  //std::cout << "scores_out.size() = " << scores_out.size() << " (3)\n";
243  // Although not everyone will care, this is useful for some applications.
244  core::import_pose::atom_tree_diffs::AtomTreeDiff::sort_by("interface_delta", scores_out);
245  //for(core::Size jj = 1; jj <= 3; ++jj)
246  // std::cout << "Best structure: " << scores_out[jj].first << '\n';
247 }
248 
252  core::Real to_keep /* = 0.05*/
253 )
254 {
255  core::import_pose::atom_tree_diffs::ScoresPairList const & scores_list = atdiff.scores();
256  select_best_poses(scores_list, scores_out, to_keep);
257 }
258 
261  std::set< std::string > & tags_out
262 )
263 {
265  scores_list2.reserve( atdiff.scores().size() );
266  select_best_poses(atdiff, scores_list2);
267  for(core::Size ii = 1; ii <= scores_list2.size(); ++ii) {
268  tags_out.insert( scores_list2[ii].first );
269  }
270 }
271 
272 /// @details Currently considers only heavy atoms, not hydrogens.
274  core::conformation::Residue const & rsd1,
275  core::conformation::Residue const & rsd2,
276  utility::vector1< core::Real > const & cutoffs,
277  utility::vector1< core::Real > & fractions_out
278 )
279 {
280  using namespace core;
281  using namespace core::chemical;
282  using namespace core::conformation;
283  // name() and total number of atoms may actually be different, if we're comparing e.g. tautomers
284  if( rsd1.type().name3() != rsd2.type().name3() ) utility_exit_with_message("Residue type name3 mismatch");
285  if( rsd1.nheavyatoms() != rsd2.nheavyatoms() ) utility_exit_with_message("Residue number-of-heavy-atoms mismatch");
286  fractions_out.resize(cutoffs.size(), 0);
287  int counter = 0;
288  // Make atom-number translation table
289  ResidueTypeCOP rsd1_type( &(rsd1.type()) );
290  AutomorphismIterator ai( rsd1_type );
291  AtomIndices old2new( ai.next() );
292  // For each permutation of automorphisms...
293  while( old2new.size() > 0 ) {
294  counter++;
295  //if( counter%10000 == 0 ) tr.Info << counter << " so far..." << std::endl;
296 
297  // Print out translation table for debugging
298  //std::cout << "[";
299  //for(Size i = 1; i <= old2new.size(); ++i) std::cout << " " << old2new[i];
300  //std::cout << " ]\n";
301  //for(Size j = 1; j <= old2new.size(); ++j) std::cout << " " << j << " --> " << old2new[j] << " / " << rsd1.type().atom_name(j) << " --> " << rsd1.type().atom_name(old2new[j]) << "\n";
302 
303  // Each cutoff might find its maximum fraction from a different automorphism (?)
304  // I'm not sure what this means, if anything, for the validity of this measure...
305  for(core::Size i = 1; i <= cutoffs.size(); ++i) {
306  core::Real const cutoff2 = cutoffs[i] * cutoffs[i];
307  core::Real nwithin( 0 );
308  core::Real natoms( 0 );
309  for ( core::Size j = 1; j <= rsd1.type().natoms(); ++j ) {
310  if ( !rsd1.atom_type(j).is_hydrogen() ) {
311  natoms += 1;
312  // This is the step where we effectively re-assign atom names
313  // in hopes of reducing RMS (e.g. by "flipping" a phenyl ring).
314  core::Vector diff = rsd1.xyz( j ) - rsd2.xyz( old2new[j] );
315  if( diff.length_squared() <= cutoff2 ) nwithin += 1;
316  }
317  }
318  core::Real fracwithin = (natoms > 0 ? nwithin / natoms : 0);
319  if( fracwithin > fractions_out[i] ) {
320  //tr.Debug << "New rms of " << curr_rms << " beats previous best of " << best_rms << std::endl;
321  fractions_out[i] = fracwithin;
322  }
323  }
324  old2new = ai.next();
325  } // done checking all automorphisms
326  //tr.Info << counter << " automorphisms from iterator; best rms is " << best_rms << std::endl;
327 }
328 
331  core::pose::Pose const & pose
332 )const{
333  int jump_id = pose.num_jump(); // assume ligand attached by last jump
334  if ( jump_id == 0 ) {
335  utility_exit_with_message("Pose has no jumps!");
336  }
337  return jump_id;
338 }
339 
340 /// @brief Return the residue sequence number for our ligand.
341 /// @details Only works with single residue ligands, really.
342 /// Reconsider this in the future.
345  core::pose::Pose const & pose
346 ) const
347 {
348  return get_ligand_id(pose, get_ligand_jump_id(pose));
349 }
350 
351 /// @brief Return the residue sequence number for our ligand.
352 /// @details Only works with single residue ligands, really.
353 /// Reconsider this in the future.
356  core::pose::Pose const & pose,
357  core::Size jump_id
358 ) const
359 {
360  core::Size const lig_id = (core::Size) pose.fold_tree().downstream_jump_residue(jump_id);
361 
362  // Safety checks...
363  FArray1D_bool is_upstream ( pose.total_residue(), false );
364  pose.fold_tree().partition_by_jump( jump_id, is_upstream );
365  core::Size num_downstream = 0;
366  for(core::Size i = 1; i <= pose.total_residue(); ++i) if(!is_upstream(i)) num_downstream += 1;
367  if(lig_id != pose.total_residue() || num_downstream != 1 || is_upstream(lig_id) || pose.residue(lig_id).is_polymer()) {
368  utility_exit_with_message("Expected ligand to be last residue in pose and only one downstream of the jump");
369  }
370 
371  return lig_id;
372 }
373 
375  core::pose::Pose const & pose,
376  core::Size jump_id,
377  utility::vector1< core::Vector > start_from_pts
378 ){
379  // Choose desired centroid: either -start_from or the current position.
380  core::Vector desired_centroid;
381  if( !start_from_pts.empty() ) {
382  int const which_triple = numeric::random::RG.random_range(1, start_from_pts.size());
383  desired_centroid = start_from_pts[ which_triple ];
384  } else {
385  core::Vector dummy;
386  protocols::geometry::centroids_by_jump(pose, jump_id, dummy, desired_centroid);
387  }
388  //std::cout << "Desired centroid: " << desired_centroid << std::endl;
389  return desired_centroid;
390 }
392  core::pose::Pose & pose,
393  core::Size jump_id,
394  utility::vector1< core::Vector > start_from_pts
395 ){
396  core::Vector desired_centroid= choose_desired_centroid(pose, jump_id, start_from_pts);
397  move_ligand_to_desired_centroid(pose, jump_id, desired_centroid);
398 }
399 
401  core::pose::Pose & pose,
402  core::Size jump_id,
403  core::Vector desired_centroid
404 ){//(This could be a no-op if desired == current)
405  core::Vector ligand_centroid = protocols::geometry::downstream_centroid_by_jump(pose, jump_id);
406  //std::cout << "Centroid before: " << ligand_centroid << std::endl;
407  core::Vector const trans_vec = desired_centroid - ligand_centroid;
408  core::Real const trans_len = trans_vec.length();
409  if(trans_len > 1e-3) { // otherwise we get NaNs
410  protocols::rigid::RigidBodyTransMover mover( pose, jump_id);
411  mover.step_size(trans_len);
412  mover.trans_axis(trans_vec);
413  mover.apply(pose);
414  }
415  //std::cout << "Centroid after: " << protocols::geometry::downstream_centroid_by_jump(pose, jump_id) << std::endl;
416 }
417 
420  core::pose::Pose const & pose,
421  core::Size jump_id,
422  core::Real sc_padding,
423  bool include_all_rsds,
424  bool include_backbone,
425  bool include_ligands,
426  bool include_water
427 ) const
428 {
429  // All DOF start false (frozen)
431  movemap->set_jump(jump_id, true);
432  //if( include_backbone ) movemap->set_bb(true); // held in check by restraints (elsewhere)
433 
434  FArray1D_bool allow_min( pose.total_residue(), true );
435  utility::vector1< bool > dont_care( pose.total_residue(), false );
436  utility::vector1< bool > allow_min_bb( pose.total_residue(), false );
437  if ( !include_all_rsds ) {
438  find_interface_rsds(pose, jump_id, sc_padding, allow_min);
439  if( include_backbone ) {
440  find_interface_backbone(pose, jump_id, bb_interface_cutoff_, dont_care, allow_min_bb);
441  }
442  }
443  if ( !include_ligands ) {
444  for(core::Size i = 1, i_end = pose.total_residue(); i <= i_end; ++i) {
445  if ( !pose.residue(i).is_polymer() ) allow_min(i) = false;
446  }
447  }
448  for(core::Size i = 1, i_end = pose.total_residue(); i <= i_end; ++i) {
449  if ( allow_min(i) ) {
450  //std::cout << "Allow residue to minimize: " << i << "\n";
451  movemap->set_chi(i, true);
452  if ( include_backbone && allow_min_bb[i] ) movemap->set_bb(i, true);
453  }
454  }
455  if( include_water ) {
456  for(core::Size i = 1, i_end = pose.total_residue(); i <= i_end; ++i) {
457  if( ! pose.residue(i).has_property("WATER") ) continue;
458  core::kinematics::Edge const & e = pose.fold_tree().get_residue_edge(i);
459  if( ! e.is_jump() ) continue;
460  movemap->set_jump( e.label(), true );
461  TR << "Minimize water jump " << e.label() << " to residue " << i << " " << pose.residue_type(i).name3() << std::endl;
462  }
463  }
464 
465  return movemap;
466 }
467 
468 /// @details If ligand_protonation is true, ligand will be allowed to "mutate"
469 /// to any residue type with the same name3 (3-letter PDB name).
470 /// If these residues are all protonation / tautomer states, then effectively
471 /// it is protonation / tautomer states that will be sampled.
472 /// Some care is required to ensure superposition works -- the nbr_atom must have
473 /// 2+ heavy atom neighbors, and heavy atoms must be named consistently.
476  core::pose::Pose const & pose,
477  FArray1D_bool const & allow_repack,
478  bool ligand_protonation
479 ) const
480 {
481  using namespace core::pack::task;
482  PackerTaskOP pack_task = TaskFactory::create_packer_task(pose);
483  pack_task->initialize_from_command_line(); // -ex1 -ex2 etc.
484  pack_task->append_rotamerset_operation( unboundrot_ );
485  //pack_task->restrict_to_repacking(); // all residues -- now set individually below
486 
487  // Can be accomplished from the command line with -packing:use_input_sc
488  // Meiler and Baker points out that in some cases you want this off for honesty's sake.
489  //pack_task->or_include_current(true);
490 
491  for(core::Size i = 1, i_end = pose.total_residue(); i <= i_end; ++i) {
492  core::conformation::Residue const & this_rsd = pose.residue(i);
493  if( !this_rsd.is_polymer() && ligand_protonation ) {
494  using namespace core::chemical;
495  ResidueTypeSet const & rsd_type_set = this_rsd.residue_type_set();
496  ResidueTypeCOPs allowed_types = rsd_type_set.name3_map( this_rsd.name3() ); // a vector1
497  for( core::Size j = 1; j <= allowed_types.size(); ++j ) {
498  if( allowed_types[j]->name() == this_rsd.name() ) continue; // already in the task's list
499  pack_task->nonconst_residue_task( i ).allow_noncanonical_aa( allowed_types[j]->name() );
500  }
501  TR << "Allowed residues at position " << i << ":" << std::endl;
502  for(ResidueLevelTask::ResidueTypeCOPListConstIter rt = pack_task->nonconst_residue_task( i ).allowed_residue_types_begin();
503  rt != pack_task->nonconst_residue_task( i ).allowed_residue_types_end(); ++rt) {
504  TR << " " << (*rt)->name() << std::endl;
505  }
506  } else {
507  pack_task->nonconst_residue_task( i ).restrict_to_repacking();
508  }
509  if ( !allow_repack(i) /*|| !pose.residue(i).is_polymer()*/ )
510  pack_task->nonconst_residue_task( i ).prevent_repacking();
511  }
512 
513  return pack_task;
514 }
515 
518  core::pose::Pose const & pose,
519  int jump_id,
520  core::Real sc_padding,
521  bool include_all_rsds,
522  bool ligand_protonation
523 ) const
524 {
525  FArray1D_bool allow_repack( pose.total_residue(), true );
526  // Disable packing for residues that are too far from the ligand.
527  if ( !include_all_rsds ) find_interface_rsds(pose, jump_id, sc_padding, allow_repack);
528  return make_packer_task(pose, allow_repack, ligand_protonation);
529 }
530 
533  core::pose::Pose const & pose,
534  int /*jump_id*/,
535  bool ligand_protonation
536 ) const
537 {
538  FArray1D_bool allow_repack( pose.total_residue(), false );
539  for(core::Size i = 1; i <= pose.total_residue(); ++i) {
540  if( !pose.residue(i).is_polymer() ) allow_repack(i) = true;
541  }
542  return make_packer_task(pose, allow_repack, ligand_protonation);
543 }
544 
545 void
547  core::pose::Pose const & pose,
548  int jump_id,
549  core::Real padding,
550  FArray1D_bool & is_interface //< output
551 ) const
552 {
553  // The Rosetta++ criterion for which sidechains repack/minimize in docking:
554  // ligand heavy atom within paircutoff(aa,GLY)+1 of aa's CB
555  // See
556  // docking_minimize.cc docking_MCM_pack_side_chains()
557  // docking_movement.cc docking_repack()
558  // docking_scoring.cc docking_interf_residues()
559  // ligand.cc detect_ligand_interface[_res]()
560  // hetero_atom_amino_acid_distance()
561  // Ian's criterion to approximate this in Mini:
562  // ligand heavy atom within rsd.nbr_radius()+6 of rsd.nbr_atom()
563  // 6A is an eyeballed magic number to get ~ agreement w/ Rosetta++ paircutoffs+1
564 
565  int num_in_interface = 0;
566  is_interface.dimension( pose.total_residue(), false ); // init all positions to false
567  FArray1D_bool is_upstream ( pose.total_residue(), false );
568  pose.fold_tree().partition_by_jump( jump_id, is_upstream );
569  for(core::Size i = 1, i_end = pose.total_residue(); i <= i_end; ++i) {
570  // all residues on ligand side can move
571  if ( ! is_upstream(i) ) {
572  is_interface(i) = true;
573  num_in_interface += 1;
574  continue;
575  }
576  // on protein side, have to do distance check
577  core::conformation::Residue const & prot_rsd = pose.residue(i);
578  for(core::Size j = 1, j_end = pose.total_residue(); j <= j_end; ++j) {
579  if ( is_upstream(j) ) continue; // compare against only ligand residues
580  core::conformation::Residue const & lig_rsd = pose.residue(j);
581  for(core::Size k = 1, k_end = lig_rsd.nheavyatoms(); k <= k_end; ++k) {
582  double dist2 = lig_rsd.xyz(k).distance_squared( prot_rsd.xyz(prot_rsd.nbr_atom()) );
583  double cutoff = prot_rsd.nbr_radius() + 6.0 + padding;
584  if ( dist2 <= cutoff * cutoff ) {
585  is_interface(i) = true;
586  num_in_interface += 1;
587  goto END_LIGRES_LOOP; // C++ lacks multi-level break :(
588  }
589  }
590  }
591  END_LIGRES_LOOP: ; // compiler needs ; as a no-op before end of loop
592  }
593  TR << "Interface is " << num_in_interface << " / " << pose.total_residue()
594  << " residues (" << 100*(double(num_in_interface) / double(pose.total_residue())) << "%)" << std::endl;
595 }
596 
597 /// Find residues that would most benefit docking by backbone movement.
598 /// Based on absolute distance to CA/CB -- being near the end of a long sc
599 /// is no reason its backbone should move.
600 /// This is a new invention; not taken from Rosetta++.
601 void
603  core::pose::Pose const & pose,
604  int jump_id,
605  core::Real cutoff_dist,
606  utility::vector1< bool > & is_interface, //< output
607  utility::vector1< bool > & is_around_interface //< output
608 ) const
609 {
610  runtime_assert( cutoff_dist > 0 );
611  double const cutoff2 = cutoff_dist * cutoff_dist;
612 
613  int num_in_interface = 0;
614  is_interface.resize( pose.total_residue(), false ); // init all positions to false
615  is_around_interface.resize( pose.total_residue(), false ); // init all positions to false
616 
617  FArray1D_bool is_upstream ( pose.total_residue(), false );
618  pose.fold_tree().partition_by_jump( jump_id, is_upstream );
619  for(core::Size i = 1, i_end = pose.total_residue(); i <= i_end; ++i) {
620  // all residues on ligand side can move
621  if ( ! is_upstream(i) ) {
622  is_interface[i] = true;
623  num_in_interface += 1;
624  continue;
625  }
626  // on protein side, have to do distance check
627  core::conformation::Residue const & prot_rsd = pose.residue(i);
628  if( !prot_rsd.is_protein() ) continue; // only minimize protein backbone, not other stuff. Maybe also DNA/RNA in the future?
629  core::Vector prot_cb;
630  if( prot_rsd.has("CB") ) prot_cb = prot_rsd.xyz("CB");
631  else if( prot_rsd.has("CA") ) prot_cb = prot_rsd.xyz("CA"); // GLY
632  else {
633  TR << "Can't find CA/CB for residue " << i << std::endl;
634  continue; // non-protein residues not part of the interface
635  }
636  for(core::Size j = 1, j_end = pose.total_residue(); j <= j_end; ++j) {
637  if ( is_upstream(j) ) continue; // compare against only ligand residues
638  core::conformation::Residue const & lig_rsd = pose.residue(j);
639  for(core::Size k = 1, k_end = lig_rsd.nheavyatoms(); k <= k_end; ++k) {
640  double dist2 = lig_rsd.xyz(k).distance_squared( prot_cb );
641  if ( dist2 <= cutoff2 ) {
642  is_interface[i] = true;
643  //std::cout << "{bb iface " << prot_rsd.name3() << " " << i << "} "
644  // << prot_cb.x() << " " << prot_cb.y() << " " << prot_cb.z() << "\n";
645  num_in_interface += 1;
646  goto END_LIGRES_LOOP; // C++ lacks multi-level break :(
647  }
648  }
649  }
650  END_LIGRES_LOOP: ; // compiler needs ; as a no-op before end of loop
651  }
652  TR << "Backbone interface is " << num_in_interface << " / " << pose.total_residue()
653  << " residues (" << 100*(double(num_in_interface) / double(pose.total_residue())) << "%)" << std::endl;
654 
655  int const window = 3; // how many residues on either side of the truly mobile ones?
656  for(Size i = 1, nres = pose.total_residue(); i <= nres; ++i) {
657  if( pose.residue_type(i).is_polymer() ) {
658  // Track backwards
659  for(Size j = i; j >= Size(std::max(1,int(i)-window)); --j) {
660  if( !pose.residue_type(j).is_polymer() || pose.residue(j).is_upper_terminus() ) break;
661  is_around_interface[i] = is_around_interface[i] | is_interface[j];
662  if( pose.residue(j).is_lower_terminus() ) break;
663  }
664  // Track forwards
665  for(Size j = i; j <= std::min(nres,i+window); ++j) {
666  if( !pose.residue_type(j).is_polymer() || pose.residue(j).is_lower_terminus() ) break;
667  is_around_interface[i] = is_around_interface[i] | is_interface[j];
668  if( pose.residue(j).is_upper_terminus() ) break;
669  }
670  }
671  //std::cout << i << " unrestr " << is_interface[i] << " mobile " << is_around_interface[i] << std::endl;
672  }
673 }
674 
675 /// @details Depends on atomtree topology; atomtree setup should be finished before calling this.
676 void
678  core::pose::Pose & pose,
679  utility::vector1< bool > const & is_restrained,
680  //core::Real stddev_Angstroms,
682 ) const
683 {
684  using namespace core::scoring::constraints;
688  using core::id::AtomID;
689 
690  // Can use same function for all b/c target is always 0 distance to orig coords.
691  //FuncOP restr_func = new HarmonicFunc(0, stddev_Angstroms);
692  // An atom that should never move in terms of absolute coordinates.
693  // Needed as a proxy for the origin, b/c Rosetta assumes all energies are
694  // translation-invariant. So it's a "two-body" energy with this fixed atom.
695  AtomID fixed_pt( pose.atom_tree().root()->atom_id() );
696 
697  for(core::Size i = 1, i_end = pose.total_residue(); i <= i_end; ++i) {
698  if ( pose.residue(i).is_protein() && is_restrained[i] ) { // protein residues
699  Residue const & rsd = pose.residue(i);
700  ResidueType const & rsd_type = pose.residue_type(i);
701  ConstraintOP constraint = new CoordinateConstraint(
702  AtomID(rsd_type.atom_index("CA"), i),
703  fixed_pt,
704  rsd.xyz("CA"),
705  restr_func
706  );
707  TR.Debug << "Restraining C-alpha of residue " << i << std::endl;
708  pose.add_constraint( constraint );
709  }
710  }
711 
712  // You can adjust later, but at least make sure it's enabled!
713  if( scorefxn_->has_zero_weight(core::scoring::coordinate_constraint) ) {
715  }
716 }
717 
718 /// @details Depends on atomtree topology; atomtree setup should be finished before calling this.
719 /// Returns the newly created Constraint for future reference, but also adds it to the Pose.
722  core::pose::Pose & pose,
723  core::Size lig_id,
724  core::Real stddev_Angstroms
725 ) const
726 {
727  using namespace core::scoring::constraints;
731  using core::id::AtomID;
732 
733  FuncOP restr_func = new HarmonicFunc(0, stddev_Angstroms);
734  // An atom that should never move in terms of absolute coordinates.
735  // Needed as a proxy for the origin, b/c Rosetta assumes all energies are
736  // translation-invariant. So it's a "two-body" energy with this fixed atom.
737  AtomID fixed_pt( pose.atom_tree().root()->atom_id() );
738 
739  Residue const & rsd = pose.residue(lig_id);
740  ConstraintOP constraint = new CoordinateConstraint(
741  AtomID(rsd.nbr_atom(), lig_id),
742  fixed_pt,
743  rsd.nbr_atom_xyz(),
744  restr_func
745  );
746  TR << "Restraining ligand residue " << lig_id << std::endl;
747  pose.add_constraint( constraint );
748 
749  // You can adjust later, but at least make sure it's enabled!
750  if( scorefxn_->has_zero_weight(core::scoring::coordinate_constraint) ) {
752  }
753  return constraint;
754 }
755 
756 /// @details This function actually alters both the pose, and the jump_id to be docked.
757 /// The pose will have chainbreak variant types and distance constraints added.
758 void
760  core::pose::Pose & pose,
761  core::Size const & jump_id,
762  core::Real cutoff_dist,
763  core::Real stddev_Angstroms
764 )
765 {
766  core::Size const nres = pose.total_residue();
767  core::Size const lig_id = get_ligand_id(pose, jump_id);
768 
769  // Residues whose backbone can move freely
770  // This includes the ligand residue for some reason... why did I do that?
771  utility::vector1< bool > unrestr_bb( nres, false );
772  // Residues whose backbone can move, but is held in place by restraints
773  utility::vector1< bool > mobile_bb( nres, false );
774 
775  find_interface_backbone(pose, jump_id, cutoff_dist, unrestr_bb, mobile_bb);
776 
777  //TR<< "unrestr_bb"<< unrestr_bb << std::endl;
778  //TR<< "mobile_bb"<< mobile_bb << std::endl;
779 
780  reorder_foldtree_around_mobile_regions( pose, jump_id, mobile_bb, lig_id);
781 
782  // Set allow_move_bb to only restrain mobile residues
783  utility::vector1< bool > allow_move_bb( nres, true );
784  for(core::Size i = 1; i <= nres; ++i) {
785  //allow_move_bb[i] = mobile_bb[i] & !unrestr_bb[i];
786  allow_move_bb[i] = mobile_bb[i];
787  }
788  // Add constraints
790  restrain_protein_Calphas(pose, allow_move_bb, restr_func);
791 
792 }//setup_bbmin_foldtree
793 
794 
795 /// @brief reorders a fold tree such that movement in the mobile regions will
796 /// @brief have zero effect on the non-mobile regions. for every contiguous string
797 /// @brief of mobile regions, the downstream non-mobile stretch will be connected to the
798 /// @brief upstream non-mobile stretch through a jump. further, a cutpoint will be
799 /// @brief introduced into every mobile stretch. the ligand will be connected to the
800 /// @brief nearest non-mobile residue.
801 /// @brief Note: every contiguous stretch in the mobile regions must be at least 4 residues long
802 void
804  core::pose::Pose & pose,
805  core::Size const & jump_id,
806  utility::vector1< bool > const & mobile_bb,
807  core::Size const & lig_id
808 ) const
809 {
810 
811  using namespace core::kinematics;
812  FoldTree f = pose.fold_tree(); // a copy
813  core::Size const nres = pose.total_residue();
814 
815  //sanity check
816  if(mobile_bb.size() != nres){
817  std::cerr << "Error: vector containing mobile residue information doesn't have the same number of residues as the pose." << std::endl;
818  utility::exit( EXIT_FAILURE, __FILE__, __LINE__);
819  }
820 
821  // Attach ligand to nearest CA that is not mobile!
822  // This used to be activated by the -shorten_jump flag, but now is the default
823  if( lig_id != 0 ){ //flo sept '12this function now can also be used to modify fold trees for poses that contain new ligands
824  Size attach_pt = 1; // for now, attach ligand to residue 1
825  core::Real shortest_dist2 = 1e99;
826  core::Vector lig_nbr_atom = pose.residue(lig_id).nbr_atom_xyz();
827  for(core::Size i = 1; i <= nres; ++i) {
828  if( !pose.residue(i).is_polymer() ) continue;
829  if( i == lig_id ) continue; // should be unneccessary
830  if( !pose.residue(i).has("CA") ) continue;
831  if( mobile_bb[i] ) continue;
832  core::Real const new_dist2 = lig_nbr_atom.distance_squared( pose.residue(i).xyz("CA") );
833  if( new_dist2 < shortest_dist2 ) {
834  shortest_dist2 = new_dist2;
835  attach_pt = i;
836  }
837  }
838  Size old_nres = f.nres(), old_njump = f.num_jump();
839  // Deleting edges is a dicey proposition ... better to add them to a new foldtree
840  FoldTree f_new;
841  // Without the stupid cast-to-const, GCC tries to use the private, non-const version.
842  FoldTree const & f_const = f;
843  for( FoldTree::const_iterator e = f_const.begin(), edge_end = f_const.end(); e != edge_end; ++e ) {
844  bool contains_attach_pt = (( e->start() < int(attach_pt) && int(attach_pt) < e->stop() )
845  || ( e->stop() < int(attach_pt) && int(attach_pt) < e->start() ));
846  if( e->label() == (int) jump_id ) {
847  f_new.add_edge( attach_pt, lig_id, jump_id );
848  } else if( e->label() == Edge::PEPTIDE && contains_attach_pt ) {
849  f_new.add_edge( e->start(), attach_pt, Edge::PEPTIDE );
850  f_new.add_edge( attach_pt, e->stop(), Edge::PEPTIDE );
851  } else {
852  f_new.add_edge( e->start(), e->stop(), e->label() );
853  }
854  }
855  f = f_new;
856  TR << "Moved ligand " << f << std::endl;
857  if( !f.connected() ) utility_exit_with_message("Fold tree not connected?!");
858  if( !f.check_fold_tree() ) utility_exit_with_message("Fold tree did not pass check!");
859  if( old_njump != f.num_jump() ) utility_exit_with_message("Number of jumps changed?!");
860  if( old_nres != f.nres() ) utility_exit_with_message("Number of residues changed?!");
861  if( lig_id != (Size) f.downstream_jump_residue(jump_id) ) utility_exit_with_message("Ligand no longer at end of last jump!");
862  }
863 
864  {
865  // Deleting edges is a dicey proposition ... better to add them to a new foldtree
866  //for(Size k = 1; k <= nres; ++k) std::cerr << k << ' ' << mobile_bb[k] << std::endl;
867  FoldTree f_new;
868  int new_jump = f.num_jump();
869  // Without the stupid cast-to-const, GCC tries to use the private, non-const version.
870  FoldTree const & f_const = f;
871  for( FoldTree::const_iterator edge_itr = f_const.begin(), edge_end = f_const.end(); edge_itr != edge_end; ++edge_itr ) {
872  Size const e_start = edge_itr->start();
873  Size const e_stop = edge_itr->stop();
874  if( e_stop < e_start ) utility_exit_with_message("Not prepared to deal with backwards fold tree edges!");
875  //std::cout << "Considering edge from " << e_start << " to " << e_stop << ", label " << edge_itr->label() << std::endl;
876  if( !edge_itr->is_polymer() ) {
877  f_new.add_edge( *edge_itr );
878  continue; // only subdivide "peptide" edges of the fold tree
879  }
880  // else:
881  using std::max; using std::min;
882  using namespace protocols::loops;
884  for(Size i = e_start; i <= e_stop; ++i) {
885  while( i <= e_stop && !mobile_bb[i] ) ++i; // find the start of a mobile region
886  if( i > e_stop ) break; // no more mobile regions
887  Size const start = i; // first mobile residue
888  //std::cout << " Start from " << start << std::endl;
889  while( i <= e_stop && mobile_bb[i] ) ++i; // find the end of the mobile region
890  if( i > e_stop ) i = e_stop;
891  if( !mobile_bb[i] ) --i; // back up one unless the end is flexible
892  Size const stop = i; // last mobile residue
893  //std::cout << " Stop at " << stop << std::endl;
894 
895  if( (stop-start+1) < 4 ){
896  // This kind of thing can come up at chain terminii, and should not cause a fatal error.
897  //for(Size k = 1; k <= nres; ++k) std::cerr << k << ' ' << mobile_bb[k] << std::endl;
898  TR.Warning << "WARNING: for backbone minimization to work properly, a stretch of at least 4 residues needs to be allowed to move. Stretch between " << start << " and " << stop << " is too short." << std::endl;
899  //utility::exit( EXIT_FAILURE, __FILE__, __LINE__);
900  // But it will probably break the cutpoint logic below, so we have to skip this group of residues.
901  continue;
902  }
903 
904  // Cutpoint should fall between start and stop, but not if the rsd on either side is a terminus.
905  // Because this happens within one peptide edge, no "internal" residue should ever be a terminus.
906  Size const cut_start = ( pose.residue(start).is_terminus() ? start+1 : start );
907  Size const cut_end = ( pose.residue(stop).is_terminus() ? stop-2 : stop-1 );
908  runtime_assert( cut_start <= cut_end );
909  Size cutpt = Size( my_RG.random_range(cut_start, cut_end) ); // cut is made between cutpt and cutpt+1
910  // Can't use this function while iterating -- invalidates the iterators!
911  //f.new_jump( max(e_start,start-1), min(e_stop,stop+1), cutpt );
912  //loops.push_back( Loop( max(e_start,start-1), min(e_stop,stop+1), cutpt ) );
913  loops.push_back( Loop( start, stop, cutpt ) );
914  // also need to set up residue variants so chainbreak score works correctly!
917  }
918  int last_rigid = e_start;
919  for(Size i = 1; i <= loops.size(); ++i) {
920  Loop const & l = loops[i];
921  int first = max(int(e_start), int(l.start()-1) );
922  int last = min(int(e_stop), int(l.stop()+1) );
923  if( first != last_rigid ) {
924  f_new.add_edge( last_rigid, first, Edge::PEPTIDE );
925  }
926  f_new.add_edge( first, l.cut(), Edge::PEPTIDE );
927  //f_new.add_edge( first, last, ++new_jump );
928  // Using CA instead of N may help keep downstream res from moving as much?
929  f_new.add_edge( Edge( first, last, ++new_jump, "CA", "CA", false /* default val but req'd by compiler */ ) );
930  f_new.add_edge( last, l.cut()+1, Edge::PEPTIDE );
931  last_rigid = last;
932  }
933  if( last_rigid != int(e_stop) ) {
934  f_new.add_edge( last_rigid, e_stop, Edge::PEPTIDE );
935  }
936  }
937  f = f_new;
938  }
939 
941  // Find the first non-mobile residue to be the root
942  for( Size i = 1; i <= nres; ++i ) {
943  if( pose.residue(i).is_polymer() && !mobile_bb[i] ) {
944  f.reorder( i );
945  break;
946  }
947  }
948 
949  TR << "Final loops foldtree " << f << std::endl;
950  if( !f.check_fold_tree() ) {
951  utility_exit_with_message("Invalid fold tree after trying to set up for minimization!");
952  }
953 
954  // The new jump is the assigned the next number in sequence!
955  //jump_id = foldtree.num_jump();
956  pose.fold_tree( f );
957 
958  //core::kinematics::dump_pose_kinemage("final_foldtree.kin", pose);
959  if( lig_id != 0 ) runtime_assert( lig_id == get_ligand_id(pose, jump_id) ); // runtime_assert ligand ID hasn't changed
960 
961 
962 
963 } // reorder_foldtree_around_mobile_regions
964 
965 
966 
967 void
969  core::pose::Pose const & pose,
970  core::Size seqpos,
973 ) const {
974  using namespace core;
975 
977 
978  Real bb_bump_cutoff = basic::options::option[ basic::options::OptionKeys::enzdes::bb_bump_cutoff ];
979 
980  pack::task::PackerTaskOP help_task = pack::task::TaskFactory::create_packer_task( pose );
981  for( core::Size i = 1; i <= pose.total_residue(); ++i ){
982  if( i == seqpos ) help_task->nonconst_residue_task( i ).restrict_to_repacking();
983  else help_task->nonconst_residue_task( i ).prevent_repacking();
984  }
985 
986  //let's see if this works
987  //graph::GraphOP neighbor_graph = pack::create_packer_graph( pose, *scofx, help_task );
988  graph::GraphOP neighbor_graph = new graph::Graph( pose.energies().energy_graph() );
989 
990  chemical::ResidueTypeCOP res_type = & pose.residue_type( seqpos );
991  conformation::Residue const & existing_residue( pose.residue( seqpos ) );
992 
993  utility::vector1< utility::vector1< Real > > extra_chi_steps( res_type->nchi() );
994 
996 
997  if( rotlib ){
998  rotlib->fill_rotamer_vector( pose, *scofx, *help_task, neighbor_graph, res_type, existing_residue, extra_chi_steps, true /*buried*/, suggested_rotamers );
999  }
1000  else return;
1001 
1002 
1003  //now that we have the suggested rotamers, let's do the bump_check
1004  utility::vector1< core::conformation::ResidueOP > temp_accepted_rotamers; //buffer to allow sorting
1006  core::Real bestE(1000000.0);
1007 
1008  for( utility::vector1< conformation::ResidueOP >::iterator rot_it = suggested_rotamers.begin(); rot_it != suggested_rotamers.end(); ++rot_it )
1009  {
1010 
1011  scoring::EnergyMap emap;
1013  ir = neighbor_graph->get_node( seqpos )->const_edge_list_begin(),
1014  ire = neighbor_graph->get_node( seqpos )->const_edge_list_end();
1015  ir != ire; ++ir ) {
1016 
1017  int const neighbor_id( (*ir)->get_other_ind( seqpos ) );
1018  conformation::Residue const & neighbor( pose.residue( neighbor_id ) );
1019 
1020  scofx->bump_check_backbone( **rot_it, neighbor, pose, emap );
1021  }
1022  core::Real thisrotE = scofx->weights().dot( emap ) ;
1023 
1024  if( thisrotE < bb_bump_cutoff ){
1025  temp_accepted_rotamers.push_back( *rot_it );
1026  if( thisrotE < bestE ){
1027  best_rot = *rot_it;
1028  bestE = thisrotE;
1029  }
1030  }
1031  //debug
1032  //std::cerr << "rotamer has energy " << (scofx->weights().dot( emap )) << " ... " << std::endl;
1033 
1034  }
1035 
1036  //we half-sort the output array such that the lowest energy rotamer is the first one in the output array
1037  if( temp_accepted_rotamers.size() > 0 ){
1038  accepted_rotamers.push_back( best_rot );
1039  for( utility::vector1< conformation::ResidueOP >::iterator rot_it = temp_accepted_rotamers.begin(); rot_it != temp_accepted_rotamers.end(); ++rot_it ){
1040 
1041  if( *rot_it != best_rot ) accepted_rotamers.push_back( *rot_it );
1042  }
1043 
1044  }
1045 
1046 
1047 } //get_non_bb_clashing_rotamers
1048 
1049 
1050 
1051 } // namespace ligand_docking
1052 } // namespace protocols