Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DomainAssembly.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
11 /// @brief Domain Assembly
12 /// @detailed
13 /// @author Yifan Song
14 
20 
21 #include <core/pose/PDBInfo.hh>
22 #include <core/pose/util.hh>
23 #include <core/id/AtomID.hh>
24 #include <core/id/AtomID_Map.hh>
27 #include <core/types.hh>
28 
32 #include <core/scoring/Energies.hh>
33 
34 //numeric
35 #include <numeric/random/random.hh>
36 #include <numeric/random/WeightedSampler.hh>
37 
38 // options
39 #include <basic/options/option.hh>
40 #include <basic/options/keys/cm.OptionKeys.gen.hh>
41 
42 //utility
43 #include <utility/vector1.hh>
44 #include <ObjexxFCL/format.hh>
45 #include <basic/Tracer.hh>
46 
47 static basic::Tracer TR( "protocols.hybridization.DomainAssembly" );
48 static numeric::random::RandomGenerator RG(546131);
49 
50 namespace protocols {
51 //namespace comparative_modeling {
52 namespace hybridization {
53 
54 bool TMalign_poses(core::pose::Pose & aligned_pose,
55  core::pose::Pose const & ref_pose,
56  std::list <Size> const & residue_list,
57  std::list <Size> const & ref_residue_list) {
58  TMalign tm_align;
59  string seq_pose, seq_ref, aligned;
62  core::Size n_mapped_residues=0;
63  core::Size normalize_length = aligned_pose.total_residue() < ref_pose.total_residue() ? aligned_pose.total_residue() : ref_pose.total_residue();
64 
65  tm_align.apply(aligned_pose, ref_pose, residue_list, ref_residue_list);
66  tm_align.alignment2AtomMap(aligned_pose, ref_pose, residue_list, ref_residue_list, n_mapped_residues, atom_map);
67  tm_align.alignment2strings(seq_pose, seq_ref, aligned);
68  core::Real TMscore = tm_align.TMscore(normalize_length);
69 
70  using namespace ObjexxFCL::fmt;
71  TR << "Align domain with TMscore of " << F(8,3,TMscore) << std::endl;
72  TR << seq_pose << std::endl;
73  TR << aligned << std::endl;
74  TR << seq_ref << std::endl;
75 
76  std::list <Size> full_residue_list;
77  for (Size ires=1; ires<=aligned_pose.total_residue(); ++ires) {
78  full_residue_list.push_back(ires);
79  }
80 
81  if (n_mapped_residues >= 6) {
83  aln_cutoffs.push_back(6);
84  aln_cutoffs.push_back(4);
85  aln_cutoffs.push_back(3);
86  aln_cutoffs.push_back(2);
87  aln_cutoffs.push_back(1.5);
88  aln_cutoffs.push_back(1);
89  core::Real min_coverage = 0.2;
90  partial_align(aligned_pose, ref_pose, atom_map, full_residue_list, true, aln_cutoffs, min_coverage); // iterate_convergence = true
91  return true;
92  }
93 
94  return false;
95 }
96 
99  utility::vector1 < core::Real > & domain_assembly_weights
100  )
101 {
102  // initialize the order of assembly
103  utility::vector1 <core::Size> pose_index_to_add;
104  utility::vector1 <core::Size> pose_index_added;
105  for (Size i=1; i<=poses.size(); ++i) {
106  pose_index_to_add.push_back(i);
107  }
108  while (pose_index_to_add.size() > 0) {
109  core::Real sum_weight(0.0);
110  utility::vector1 < core::Real > weights_work;
111  for (Size i=1; i<=pose_index_to_add.size(); ++i) {
112  Size k_pose = pose_index_to_add[i];
113  sum_weight += domain_assembly_weights[k_pose];
114  weights_work.push_back(domain_assembly_weights[k_pose]);
115  }
116  Size ipose;
117  if (sum_weight > 1e-6) {
118  numeric::random::WeightedSampler weighted_sampler_;
119  weighted_sampler_.weights(weights_work);
120  ipose = pose_index_to_add[weighted_sampler_.random_sample(RG)];
121  }
122  else {
123  ipose = pose_index_to_add[RG.random_range(1,pose_index_to_add.size())];
124  }
125 
126  poses_.push_back(poses[ipose]);
127  pose_index_added.push_back(ipose);
128 
129  pose_index_to_add.clear();
130  for (Size ipose=1; ipose<=poses.size(); ++ipose) {
131  bool added = false;
132  for (Size j=1; j<=pose_index_added.size(); ++j) {
133  if (ipose == pose_index_added[j]) {
134  added = true;
135  break;
136  }
137  }
138  if (!added) {
139  pose_index_to_add.push_back(ipose);
140  }
141  }
142  }
143 }
144 
146  utility::vector1 <int> const resnum_list,
147  utility::vector1<int> & remaining_resnum) {
148 
149  remaining_resnum.clear();
150  utility::vector1<Size> delete_list;
151  for (Size ires=1; ires <= pose.total_residue(); ++ires ) {
152  bool deleting = false;
153  for ( Size jres=1; jres<=resnum_list.size(); ++jres) {
154  if (resnum_list[jres] == pose.pdb_info()->number(ires)) {
155  deleting = true;
156  delete_list.push_back(ires);
157  break;
158  }
159  }
160  if (!deleting) {
161  remaining_resnum.push_back(pose.pdb_info()->number(ires));
162  }
163  }
164 
165  for (Size i=delete_list.size(); i>=1; --i) {
166  pose.conformation().delete_residue_slow(delete_list[i]);
167  }
168 }
169 
171  utility::vector1 <Size> const covered_residues) {
172  utility::vector1<Size> uncovered_residues;
173  for (Size ires = 1; ires <= pose.total_residue(); ++ires) {
174  if ( pose.pdb_info() != 0) {
175  bool covered(false);
176  for ( Size i=1; i<=covered_residues.size(); ++i) {
177  if ( pose.pdb_info()->number(ires) == (int)covered_residues[i] ) {
178  covered = true;
179  break;
180  }
181  }
182  if (!covered) {
183  uncovered_residues.push_back(ires);
184  }
185  }
186  }
187  return uncovered_residues;
188 }
189 
190  core::Real
192  {
193  core::Real gap_torr_0( 4.0);
194  core::Real gap_torr_1( 7.5);
195  core::Real gap_torr_2(11.0);
196  core::Real gap_torr_3(14.5);
197  core::Real gap_torr_4(18.0);
198  core::Real gap_torr_5(21.0);
199  core::Real gap_torr_6(24.5);
200  core::Real gap_torr_7(27.5);
201  core::Real gap_torr_8(31.0);
202 
203  switch (Seq_gap) {
204  case 0:
205  return gap_torr_0; break;
206  case 1:
207  return gap_torr_1; break;
208  case 2:
209  return gap_torr_2; break;
210  case 3:
211  return gap_torr_3; break;
212  case 4:
213  return gap_torr_4; break;
214  case 5:
215  return gap_torr_5; break;
216  case 6:
217  return gap_torr_6; break;
218  case 7:
219  return gap_torr_7; break;
220  case 8:
221  return gap_torr_8; break;
222  default:
223  return 9999.;
224  }
225  return 9999.;
226  }
227 
228 void
230 {
231  if (poses_.size() < 2) return;
232 
233  utility::vector1<int> coverage_start;
234  utility::vector1<int> coverage_end;
235 
236  int range_start = poses_[1]->pdb_info()->number(1);
237  int range_end = poses_[1]->pdb_info()->number(1);
238  for (Size ires = 1; ires <= poses_[1]->total_residue(); ++ires) {
239  if (poses_[1]->pdb_info()->number(ires) < range_start) {
240  range_start = poses_[1]->pdb_info()->number(ires);
241  }
242  if (poses_[1]->pdb_info()->number(ires) > range_end) {
243  range_end = poses_[1]->pdb_info()->number(ires);
244  }
245  }
246  coverage_start.push_back(range_start);
247  coverage_end.push_back(range_end);
248 
249  for (Size ipose = 2; ipose <= poses_.size(); ++ipose) {
250  // check if the current pose is overlapped with any previous poses
251  range_start = poses_[ipose]->pdb_info()->number(1);
252  range_end = poses_[ipose]->pdb_info()->number(1);
253  for (Size ires = 1; ires <= poses_[ipose]->total_residue(); ++ires) {
254  if (poses_[ipose]->pdb_info()->number(ires) < range_start) {
255  range_start = poses_[ipose]->pdb_info()->number(ires);
256  }
257  if (poses_[ipose]->pdb_info()->number(ires) > range_end) {
258  range_end = poses_[ipose]->pdb_info()->number(ires);
259  }
260  }
261  coverage_start.push_back(range_start);
262  coverage_end.push_back(range_end);
263 
264  bool align_success(false);
265  for (Size jpose = 1; jpose < ipose; ++jpose) {
266  int covered_size = coverage_end[jpose] - coverage_start[jpose] + 1;
267  int overlap_start = range_start > coverage_start[jpose] ? range_start:coverage_start[jpose];
268  int overlap_end = range_end < coverage_end[jpose] ? range_end:coverage_end[jpose];
269  int overlap = overlap_end - overlap_start; // end could be smaller than start
270  Size normalize_length = covered_size < (int)poses_[ipose]->total_residue() ? covered_size:poses_[ipose]->total_residue();
271 
272  // if overlap, use TMalign to orient poses_[ipose]
273  if (overlap > 0.3 * normalize_length) {
274  // collect residues in ipose
275  std::list <Size> i_residue_list; // residue numbers in the overlapped region
276  std::list <Size> full_residue_list; // all residue numbers in ipose, used for transformation after alignment
277  for (Size ires = 1; ires <= poses_[ipose]->total_residue(); ++ires) {
278  full_residue_list.push_back(ires);
279  if (poses_[ipose]->pdb_info()->number(ires) >= overlap_start &&
280  poses_[ipose]->pdb_info()->number(ires) <= overlap_end) {
281  i_residue_list.push_back(ires);
282  }
283 
284  }
285 
286  // collect residues in the pose to be aligned to
287  std::list <Size> j_residue_list;
288  for (Size jres = 1; jres <= poses_[jpose]->total_residue(); ++jres) {
289  if (poses_[jpose]->pdb_info()->number(jres) >= overlap_start &&
290  poses_[jpose]->pdb_info()->number(jres) <= overlap_end) {
291  j_residue_list.push_back(jres);
292  }
293  }
294  if (j_residue_list.size() < 0.3 * normalize_length) continue;
295 
296  TMalign tm_align;
297  string seq_pose, seq_ref, aligned;
300  core::Size n_mapped_residues=0;
301 
302  tm_align.apply(*poses_[ipose], *poses_[jpose], i_residue_list, j_residue_list);
303  tm_align.alignment2AtomMap(*poses_[ipose], *poses_[jpose], i_residue_list, j_residue_list, n_mapped_residues, atom_map);
304  tm_align.alignment2strings(seq_pose, seq_ref, aligned);
305  core::Real TMscore = tm_align.TMscore(normalize_length);
306 
307  using namespace ObjexxFCL::fmt;
308  TR << "Align domain with TMscore of " << F(8,3,TMscore) << std::endl;
309  TR << seq_pose << std::endl;
310  TR << aligned << std::endl;
311  TR << seq_ref << std::endl;
312 
313  if (TMscore > 0.35) {
314  if (n_mapped_residues >= 6) {
315  utility::vector1< core::Real > aln_cutoffs;
316  aln_cutoffs.push_back(6);
317  aln_cutoffs.push_back(4);
318  aln_cutoffs.push_back(3);
319  aln_cutoffs.push_back(2);
320  aln_cutoffs.push_back(1.5);
321  aln_cutoffs.push_back(1);
322  core::Real min_coverage = 0.2;
323  partial_align(*poses_[ipose], *poses_[jpose], atom_map, full_residue_list, true, aln_cutoffs, min_coverage); // iterate_convergence = true
324  align_success = true;
325  }
326  }
327 
328  if (align_success) break;
329  }
330  }
331 
332  // if not overlap, use docking to align to the largest non-overlapped region
333  if (!align_success) {
334  using namespace ObjexxFCL::fmt;
335  // construct a pose for domain assembly
336  core::pose::PoseOP full_length_pose;
337  //Size first_domain_end;
338  utility::vector1<int> covered_resnum;
339  utility::vector1<int> resnum;
340  for (Size ires=1; ires<=poses_[ipose]->total_residue(); ++ires) {
341  covered_resnum.push_back(poses_[ipose]->pdb_info()->number(ires));
342  }
343  for (Size jpose = 1; jpose < ipose; ++jpose) {
344  utility::vector1<Size> uncovered_residues = find_uncovered_residues(*poses_[jpose], covered_resnum);
345  if (uncovered_residues.size() == 0) continue;
346 
347  core::pose::Pose inserted_pose(*poses_[jpose]);
348  utility::vector1<int> remaining_resnum;
349  remove_residues(inserted_pose, covered_resnum, remaining_resnum);
350  for (Size i=1;i<=remaining_resnum.size();++i) resnum.push_back(remaining_resnum[i]);
351  for (Size i=1;i<=remaining_resnum.size();++i) covered_resnum.push_back(remaining_resnum[i]);
352 
353  if (jpose == 1) {
354  full_length_pose = new core::pose::Pose(inserted_pose);
355  core::pose::PDBInfoOP pdb_info;
356  full_length_pose->pdb_info(pdb_info);
357  }
358  else {
359  Size njump = full_length_pose->fold_tree().num_jump();
360  full_length_pose->conformation().insert_conformation_by_jump( inserted_pose.conformation(),
361  full_length_pose->total_residue() + 1, njump+1,
362  full_length_pose->total_residue() );
363  }
364  //full_length_pose->dump_pdb("full_length_"+I(1,ipose)+"_"+I(1,jpose)+".pdb");
365  }
366 
367  Size nres_domain1 = full_length_pose->total_residue();
368 
369  for (Size ires=1;ires<=poses_[ipose]->total_residue();++ires) resnum.push_back(poses_[ipose]->pdb_info()->number(ires));
370 
371  Size jump_num = full_length_pose->fold_tree().num_jump()+1;
372  full_length_pose->conformation().insert_conformation_by_jump( poses_[ipose]->conformation(),
373  full_length_pose->total_residue() + 1, jump_num,
374  full_length_pose->total_residue() );
375  //TR << full_length_pose->fold_tree() << std::endl;
376  //full_length_pose->dump_pdb("full_length_"+I(1,ipose)+"before_docking.pdb");
377 
378  for (Size ires=1; ires <= nres_domain1; ++ires) {
379  for (Size jres=nres_domain1+1; jres <= full_length_pose->total_residue(); ++jres) {
380  Size seq_sep = abs(resnum[ires] - resnum[jres]);
381  if (seq_sep>=6 && seq_sep <=8) {
382  core::Real gd = gap_distance(seq_sep);
383  Size iatom = full_length_pose->residue_type(ires).atom_index("CA");
384  Size jatom = full_length_pose->residue_type(jres).atom_index("CA");
385 
386  TR.Debug << "Adding constraints to residue " << ires << " and " << jres << std::endl;
387  full_length_pose->add_constraint(
389  core::id::AtomID(iatom, ires),
390  core::id::AtomID(jatom, jres),
391  new core::scoring::constraints::BoundFunc( 0, gd, 5., "gap" ) )
392  );
393  }
394  }
395  }
396 
397  /*
398  if ( poses_[1]->pdb_info()->number(1) < poses_[ipose]->pdb_info()->number(1) ) {
399  full_length_pose = new core::pose::Pose(*poses_[1]);
400 
401  core::pose::PDBInfoOP pdb_info;
402  full_length_pose->pdb_info(pdb_info);
403 
404  // remove overlap residues
405  first_domain_end = full_length_pose->total_residue();
406  while (poses_[1]->pdb_info()->number(first_domain_end) >= range_start) {
407  --first_domain_end;
408  }
409  if (first_domain_end < full_length_pose->total_residue()) {
410  full_length_pose->conformation().delete_residue_range_slow(first_domain_end+1, full_length_pose->total_residue());
411  }
412 
413  full_length_pose->conformation().insert_conformation_by_jump( poses_[ipose]->conformation(),
414  full_length_pose->total_residue() + 1,
415  full_length_pose->total_residue(),
416  1);
417 
418  utility::vector1 < utility::vector1 <Size > > covered_range;
419  for (Size jpose = 1; jpose < ipose; ++jpose) {
420  Size resi_start(0);
421  Size resi_end(0);
422  for (Size jres = 1; jres <= poses_[jpose]->total_residue(); ++jres) {
423  if ( poses_[jpose]->pdb_info()->number(jres) > range_end ) {
424  if (resi_start == 0) {
425  resi_start = jres;
426  resi_end = jres;
427  }
428  else {
429  if ( poses_[jpose]->pdb_info()->number(jres) < poses_[jpose]->pdb_info()->number(resi_start) ) {
430  // add residue
431  resi_start = jres;
432  }
433  if ( poses_[jpose]->pdb_info()->number(jres) > poses_[jpose]->pdb_info()->number(resi_end) ) {
434  // add residue
435  resi_end = jres;
436  }
437  }
438  }
439  }
440  if (resi_start != 0) {
441  for (Size irange=1; irange <= covered_range.size(); ++irange) {
442 
443  }
444 
445  core::pose::Pose inserted_pose(*poses_[jpose], resi_start, resi_end);
446  full_length_pose->conformation().insert_conformation_by_jump( poses_[ipose]->conformation(),
447  full_length_pose->total_residue() + 1,
448  full_length_pose->total_residue(),
449  1);
450  }
451 
452  }
453  else {
454  full_length_pose = new core::pose::Pose(*poses_[ipose]);
455 
456  core::pose::PDBInfoOP pdb_info;
457  full_length_pose->pdb_info(pdb_info);
458 
459  first_domain_end = full_length_pose->total_residue();
460  full_length_pose->conformation().insert_conformation_by_jump( poses_[1]->conformation(),
461  full_length_pose->total_residue() + 1,
462  full_length_pose->total_residue(),
463  1);
464  }
465  */
466 
467  // docking
468  /*
469  Size iatom = full_length_pose->residue_type(first_domain_end-2).atom_index("CA");
470  Size jatom = full_length_pose->residue_type(first_domain_end+3).atom_index("CA");
471  TR << "Adding constraints to residue " << first_domain_end-2 << " and " << first_domain_end+3 << std::endl;
472  full_length_pose->add_constraint(
473  new core::scoring::constraints::AtomPairConstraint(
474  core::id::AtomID(iatom, first_domain_end-2),
475  core::id::AtomID(jatom, first_domain_end+3),
476  new core::scoring::constraints::BoundFunc( 0, 24., 5., "gap" ) )
477  );
478  */
479 
480  using namespace basic::options;
481  using namespace basic::options::OptionKeys;
483  "score4_smooth", "" );
485 
486  // randomize orientation
488  rb_mover->apply(*full_length_pose);
489 
490  // put the two domains apart
492  "score0", "" );
493 
495  translate->step_size( 1000. );
496  core::Vector translation_axis(1.0,0.0,0.0);
497  translate->trans_axis(translation_axis);
498  translate->apply( *full_length_pose );
499 
500  core::Real unbound_score = (*simple_scorefxn)(*full_length_pose);
501 
502  translation_axis = core::Vector(-1.0,0.0,0.0);
503  translate->trans_axis(translation_axis);
504  translate->apply( *full_length_pose );
505 
506  core::Real max_step_size(5.0);
507  core::Size counter(0);
508  while ((*simple_scorefxn)(*full_length_pose) > unbound_score+10.) {
509  //TR << "Random translation " << (*simple_scorefxn)(*full_length_pose) << std::endl;
510  translate->step_size( max_step_size*RG.uniform() );
511  translation_axis = core::Vector(RG.uniform(),RG.uniform(),RG.uniform());
512  translate->trans_axis(translation_axis);
513  translate->apply( *full_length_pose );
514  counter++;
515  if (counter > 100) {
516  max_step_size *= 1.5;
517  counter = 0;
518  }
519  }
520  //full_length_pose->dump_pdb("full_length_"+I(1,ipose)+"before_docking2.pdb");
521 
522  using namespace protocols::docking;
523  DockingLowResOP docking_mover = new DockingLowRes(scorefxn_, jump_num);
524  docking_mover->set_outer_cycles(20);
525  docking_mover->apply(*full_length_pose);
526  //scorefxn_->show(*full_length_pose);
527  //full_length_pose->dump_pdb("test_full_length.pdb");
528 
529  // align full_length_pose to pose1_
530  std::list <Size> residue_list1;
531  std::list <Size> residue_list2;
532  for (Size ires = 1; ires <= poses_[1]->total_residue(); ++ires) {
533  residue_list1.push_back(ires);
534  residue_list2.push_back(ires);
535  }
536  TMalign_poses(*full_length_pose, *poses_[1], residue_list1, residue_list2);
537  //full_length_pose->dump_pdb("full_length_after_align.pdb");
538 
539  // align ipose to full_length_pose
540  residue_list1.clear();
541  residue_list2.clear();
542  for (Size ires = 1; ires <= poses_[ipose]->total_residue(); ++ires) {
543  residue_list1.push_back(ires);
544  residue_list2.push_back(nres_domain1+ires);
545  }
546 
547  TMalign_poses(*poses_[ipose], *full_length_pose, residue_list1, residue_list2);
548 
549  //full_length_pose->dump_pdb("full_length_"+I(1,ipose)+"after_docking.pdb");
550  //poses_[ipose]->dump_pdb("aligned_pose.pdb");
551  }
552  }
553 }
554 
556  core::pose::Pose & pose
557  )
558 {
559  using namespace protocols::docking;
560 
561  //core::pose::PoseOP lower_pose(pose1_->pdb_info()->number(1) < pose2_->pdb_info()->number(1) ? pose1_ : pose2_);
562  //core::pose::PoseOP higher_pose(pose1_->pdb_info()->number(1) < pose2_->pdb_info()->number(1) ? pose2_ : pose1_);
563  core::pose::PoseOP lower_pose(pose1_);
564  core::pose::PoseOP higher_pose(pose2_);
565 
566  int jump_pos1 = RG.random_range(1, lower_pose->total_residue());
567  int jump_pos2 = RG.random_range(lower_pose->total_residue()+1, lower_pose->total_residue()+higher_pose->total_residue());
568 
570  int jump_num = ft.new_jump(jump_pos1, jump_pos2, (int)lower_pose->total_residue()+1);
571  pose.fold_tree( ft );
572 
573  pose.copy_segment(lower_pose->total_residue(), *lower_pose, 1, 1);
574  pose.copy_segment(higher_pose->total_residue(), *higher_pose, lower_pose->total_residue()+1, 1);
575 
576 
577  Size gap_start;
578  if (lower_pose->total_residue() > 3 ) {
579  gap_start = lower_pose->total_residue() - 3;
580  }
581  else {
582  gap_start = 1;
583  }
584 
585  Size gap_stop;
586  if (higher_pose->total_residue() > 3 ) {
587  gap_stop = 3;
588  }
589  else {
590  gap_stop = higher_pose->total_residue();
591  }
592  //int seq_sep = higher_pose->pdb_info()->number(gap_stop) - higher_pose->pdb_info()->number(gap_start);
593 
594  //if (seq_sep <= 8) {
595  if (!lower_pose->residue_type(gap_start).is_protein()) utility_exit_with_message("Error! not an amino acid!");
596  if (!higher_pose->residue_type(gap_stop ).is_protein()) utility_exit_with_message("Error! not an amino acid!");
597  Size iatom = pose.residue_type(gap_start).atom_index("CA");
598  Size jatom = pose.residue_type(lower_pose->total_residue()+gap_stop ).atom_index("CA");
599  TR << "Adding constraints to residue " << gap_start << " and " << lower_pose->total_residue()+gap_stop << std::endl;
600  pose.add_constraint(
602  core::id::AtomID(iatom, gap_start),
603  core::id::AtomID(jatom, lower_pose->total_residue()+gap_stop),
604  new core::scoring::constraints::BoundFunc( 0, 24., 5., "gap" ) )
605  );
606  //}
607 
608  DockingLowResOP docking_mover = new DockingLowRes(scorefxn_, jump_num);
609  docking_mover->apply(pose);
610 }
611 
612 } // hybridize
613 //} // comparative_modeling
614 } // protocols