Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
SingleLigandRotamerLibrary.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 core/scoring/dunbrack/SingleLigandRotamerLibrary.cc
11 ///
12 /// @brief
13 /// @author Ian W. Davis
14 #include <utility/fixedsizearray1.hh>
15 // Unit headers
17 
18 // Package headers
22 
23 // Project headers
24 //#include <core/chemical/automorphism.hh>
25 //#include <core/chemical/ResidueType.hh>
30 #include <core/io/pdb/pose_io.hh>
32 #include <core/pose/Pose.hh>
33 #include <basic/Tracer.hh>
34 
35 // Utility headers
36 //#include <numeric/xyz.functions.hh>
37 //#include <numeric/xyzMatrix.hh>
38 //#include <numeric/model_quality/rms.hh>
39 //#include <ObjexxFCL/FArray1D.hh>
40 //#include <ObjexxFCL/FArray2D.hh>
41 #include <utility/string_util.hh>
42 #include <utility/io/izstream.hh>
43 
44 
45 // C++ headers
46 #include <cstdlib>
47 #include <fstream>
48 #include <string>
49 #include <set>
50 
51 #include <utility/vector1.hh>
52 
53 
54 namespace core {
55 namespace pack {
56 namespace dunbrack {
57 
58 
59 static basic::Tracer TR("core.pack.dunbrack.SingleLigandRotamerLibrary");
60 
61 
62 // helper for debugging
64 {
65  std::ofstream out( filename.c_str() );
66  for(Size i = 1; i <= rotamers.size(); ++i) {
67  out << "MODEL \n";
68  Size atomno = 1;
69  core::io::pdb::dump_pdb_residue(*rotamers[i], atomno, out);
70  out << "ENDMDL\n";
71  }
72  out.close();
73 }
74 
75 
78  rotamers_(),
79  ref_energy_(0.0)
80 // rigid_frags_(),
81 // automorphs_(),
82 // frag_automorphs_(),
83 // total_superpos_(0)
84 {}
85 
87 {}
88 
89 
92  Real ref_E_in ):
94  rotamers_( rotamers_in ),
95  ref_energy_( ref_E_in )
96 {}
97 
98 /// @details Reads conformers from PDB-format file.
99 /// Chain ID, residue name and number, etc are all ignored -- must have TER records.
100 void
102  std::string const & filename,
104 )
105 {
106  //std::cout << "Loading from: " << filename << "\n";
107  utility::io::izstream data( filename.c_str() );
108  if ( !data.good() ) {
109  utility_exit_with_message( "Unable to open file: " + filename + '\n' );
110  }
111  rotamers_.clear();
112 
113  std::set< std::string > skipped_atom_names;
114  conformation::ResidueOP rsd = NULL;
115  bool found_ref_energy = false;
116  std::string line;
117  // This code is not currently as smart as the general-purpose PDB reader.
118  // Any atoms in the residue that don't have coordinate entries will be
119  // left with their default values, leading to really weird bugs.
120  // We can do a limited building from ideal coordinates, for hydrogens and virtual atoms.
121  core::Size set_xyzs = 0;
122  utility::vector1< bool > missing(restype->natoms(),true);
123  utility::vector1< bool > missed(restype->natoms(),false); // Don't reset - only notify once, instead of for each library entry
124  while( std::getline( (std::istream&)data, line) ) {
125  if( utility::startswith(line, "ATOM ") || utility::startswith(line, "HETATM") ) {
126  if( line.length() < 54 ) {
127  TR << "ATOM/HETATM line too short in PDB-format rotamer file!" << std::endl;
128  continue; // to next line
129  }
130  std::string atom_name = line.substr(12,4);
131  core::Real x, y, z;
132  x = std::atof( line.substr(30,8).c_str() );
133  y = std::atof( line.substr(38,8).c_str() );
134  z = std::atof( line.substr(46,8).c_str() );
135  //std::cout << x << " " << y << " " << z << "\n";
136  if( rsd.get() == NULL ) {
138  missing.clear(); missing.resize( restype->natoms(), true );
139  set_xyzs = 0;
140  }
141  if( rsd->has( atom_name ) ) {
142  rsd->set_xyz( atom_name, core::Vector( x, y, z ) );
143  missing[ rsd->atom_index(atom_name) ] = false;
144  set_xyzs += 1;
145  } else if( skipped_atom_names.count(atom_name) == 0 ) {
146  TR.Warning << "Skipping unrecognized atom '" << atom_name << "' in library for " << restype->name() << std::endl;
147  skipped_atom_names.insert(atom_name);
148  }
149  } else if( utility::startswith(line, "REF_EN") ) {
150  if( found_ref_energy ) {
151  TR.Error << "Reference energy specified more than once in PDB-format rotamer file!" << std::endl;
152  }
153  found_ref_energy = true;
154  ref_energy_ = std::atof( line.substr(6).c_str() );
155  TR << "Reference energy for " << restype->name() << " is " << ref_energy_ << std::endl;
156  } else { // e.g. TER lines
157  if( rsd.get() != NULL ) {
158  if( set_xyzs < rsd->natoms() ) { fill_missing_atoms( missing, rsd, missed ); }
159  rotamers_.push_back( rsd );
160  rsd = NULL;
161  set_xyzs = 0;
162  }
163  }
164  }
165  if( rsd.get() != NULL ) {
166  if( set_xyzs < rsd->natoms() ) { fill_missing_atoms( missing, rsd, missed ); }
167  rotamers_.push_back( rsd );
168  }
169 
170  // Torsion angles are not automatically calculated from the coordinates.
171  // We should manually assign chi() and mainchain_torsions() for each conformer.
172  for(Size i = 1, i_end = rotamers_.size(); i <= i_end; ++i) {
174  //for(Size j = 1, j_end = rotamers_[i]->nchi(); j <= j_end; ++j) std::cout << rotamers_[i]->chi(j) << std::endl;
175  }
176 
177  TR << "Read in " << rotamers_.size() << " rotamers for " << restype->name() << "!" << std::endl;
178  data.close();
179 
180  // Breaking the ligand into rigid fragments that would supply (putative) pharamacophores
181  // to superimpose on was a nice idea, but it breaks the packer assumption that nbr_atom doesn't move.
182 
183  //find_fragments(restype);
184  //list_automorphisms(restype);
185  //unique_auto_for_frags();
186 }
187 
188 /// @details Not currently implemented -- returns 0.
189 Real
191  conformation::Residue const & rsd,
193 ) const
194 {
195  Real3 & dE_dbb( scratch.dE_dbb() );
196  Real4 & dE_dchi( scratch.dE_dchi() );
197  std::fill( dE_dbb.begin(), dE_dbb.end(), 0 );
198  std::fill( dE_dchi.begin(), dE_dchi.end(), 0 );
199  return rotamer_energy(rsd, scratch);
200 }
201 
202 
203 /// @details Not currently implemented -- returns 0.
204 /// For most things, it's actually quite hard to correctly rank the conformers by energy.
205 /// Thus, I've chosen not to introduce noise by attempting to rank them and doing it badly.
206 Real
208  conformation::Residue const & /*rsd*/,
209  RotamerLibraryScratchSpace & /*scratch*/
210 ) const
211 {
212  return ref_energy_; //0.0;
213 }
214 
215 
216 /// @details Not currently implemented -- returns 0.
217 /// For most things, it's actually quite hard to correctly rank the conformers by energy.
218 /// Thus, I've chosen not to introduce noise by attempting to rank them and doing it badly.
219 Real
221  conformation::Residue const & /*rsd*/,
222  bool /*curr_rotamer_only*/,
223  RotamerLibraryScratchSpace & /*scratch*/
224 ) const
225 {
226  return ref_energy_; //0.0;
227 }
228 
229 
230 /// Helper function for superposition
231 //void SingleLigandRotamerLibrary::superimpose(
232 // conformation::Residue const & existing,
233 // conformation::Residue & conformer,
234 // Fragment const & frag,
235 // Automorphism const & morph
236 //) const
237 //{
238 // //utility_exit_with_message("not implemented yet");
239 // using ObjexxFCL::FArray1D;
240 // using ObjexxFCL::FArray2D;
241 // using namespace numeric;
242 //
243 // Size const natoms = frag.size();
244 // FArray2D< Real > xx( 3, natoms, 0. );
245 // FArray2D< Real > yy( 3, natoms, 0. );
246 // FArray1D< Real > ww( natoms, 1.0 ); // uniform weighting
247 // FArray2D< Real > uu( 3, 3, 0.0 );
248 // Real ctx(0); // not really used
249 //
250 // Vector e_ctr(0.), c_ctr(0.);
251 // for(Size i = 1; i <= natoms; ++i) {
252 // Size const e_atom = frag[i];
253 // Size const c_atom = morph[e_atom];
254 // for(Size j = 1; j <= 3; ++j) {
255 // xx(j, i) = existing.xyz(e_atom)(j);
256 // yy(j, i) = conformer.xyz(c_atom)(j);
257 // }
258 // e_ctr += existing.xyz(e_atom);
259 // c_ctr += conformer.xyz(c_atom);
260 // }
261 // assert(natoms > 0);
262 // e_ctr /= natoms;
263 // c_ctr /= natoms;
264 //
265 // // This is not actually very accurate, in my experience so far!
266 // numeric::model_quality::findUU( xx, yy, ww, natoms, uu, ctx );
267 //
268 // typedef xyzMatrix< Real > Rotation;
269 // Rotation rot(Rotation::rows( uu(1,1), uu(1,2), uu(1,3), uu(2,1), uu(2,2), uu(2,3), uu(3,1), uu(3,2), uu(3,3) ));
270 // for(Size i = 1, i_end = existing.natoms(); i <= i_end; ++i) {
271 // conformer.set_xyz( i, (rot * (conformer.xyz(i) - c_ctr)) + e_ctr );
272 // }
273 //}
274 
275 
276 /// @brief Helper function, combines existing's metadata with conformer's conformation.
279  conformation::Residue const & existing,
280  conformation::Residue const & conformer
281 )
282 {
283  // This is bad: fields like seqpos, chain, etc. don't match existing residue!
284  //conformation::ResidueOP newrsd = rotamers_[i]->clone();
285 
286  // Could start by cloning either one, but I think people are more likely to introduce
287  // new metadata than new conformational data, so I'll let clone() copy the metadata.
288  //conformation::ResidueOP newrsd = existing.clone();
289  //newrsd->atoms() = conformer.atoms();
290  //newrsd->chi() = conformer.chi();
291  //newrsd->mainchain_torsions() = conformer.mainchain_torsions();
292  //newrsd->actcoord() = conformer.actcoord();
293 
294  // The above is also bad: existing may not be the same residue type as conformer!
295  conformation::ResidueOP newrsd = conformer.clone();
296  newrsd->chain( existing.chain() );
297  newrsd->seqpos( existing.seqpos() );
298  newrsd->copy_residue_connections_from( existing ); // this is probably not good enough if residue types diverge more than protonation state...
299 
300  return newrsd;
301 }
302 
303 
304 /// @details Assumes that rotamers is already empty or doesn't need to be cleared...
305 void
307  pose::Pose const & pose,
308  scoring::ScoreFunction const &,
309  pack::task::PackerTask const & task,
311  chemical::ResidueTypeCOP concrete_residue,
312  conformation::Residue const& existing_residue,
313  utility::vector1< utility::vector1< Real > > const & /*extra_chi_steps*/,
314  bool buried,
315  RotamerVector & rotamers //utility::vector1< conformation::ResidueOP >
316 ) const
317 {
318  //std::cout << "SingleLigandRotamerLibrary :: fill_rotamer_vector() being called...\n";
319  //rotamers.clear(); // am I supposed to do this? No: might contain rotamers for other residue types already!
320  int const start_size = rotamers.size();
321 
322  bool const expand_proton_chi = ( concrete_residue->n_proton_chi() != 0 );
323  Size const max_total_rotamers = 21654; // = 401 rotamers * 54 hydroxyl variations = 401 * (2 * 3^3)
324  RotamerVector new_rotamers;
325 
326  // Logic for creating proton_chi rotamers copied from APL (RotamerSet_.cc)
328  if ( expand_proton_chi ) {
329  proton_chi_chisets.push_back( new pack::dunbrack::ChiSet( concrete_residue->nchi() ) );
330  for ( Size ii = 1; ii <= concrete_residue->n_proton_chi(); ++ii ) {
332  task.residue_task( existing_residue.seqpos() ).extrachi_sample_level(
333  buried,
334  concrete_residue->proton_chi_2_chi( ii ),
335  concrete_residue ),
336  concrete_residue,
337  ii, proton_chi_chisets);
338  // In a pathological case, I've seen 30 rotamers * 20,000 proton chi variations = 600,000 rotamers = out of memory
339  // That's 9, count 'em 9, hydroxyls in the ligand for PDB 1u33.
340  // Wait, wait -- I can do better -- 19 hydroxyls in PDB 1xd1.
341  if( rotamers_.size()*proton_chi_chisets.size() > max_total_rotamers ) {
342  TR.Warning << "Aborting proton_chi expansion for " << concrete_residue->name() << " because we would exceed " << max_total_rotamers << " rotamers!" << std::endl;
343  proton_chi_chisets.resize( max_total_rotamers / rotamers_.size() );
344  break;
345  }
346  }
347  new_rotamers.reserve( rotamers_.size()*proton_chi_chisets.size() );
348  } else {
349  new_rotamers.reserve( rotamers_.size() );
350  }
351 
352  // Fill new_rotamers with new Residues, including proton_chi expansions
353  for(Size i = 1; i <= rotamers_.size(); ++i)
354  {
355  assert( concrete_residue->name() == rotamers_[i]->name() );
356  assert( concrete_residue->residue_type_set().name() == rotamers_[i]->residue_type_set().name() ); // fa_standard / centroid
357  if ( expand_proton_chi ) {
358  for ( Size ii = 1; ii <= proton_chi_chisets.size(); ++ii ) {
359  conformation::ResidueOP newrsd = dup_residue( existing_residue, *rotamers_[i] );
360  new_rotamers.push_back( newrsd );
361  for ( Size jj = 1; jj <= concrete_residue->n_proton_chi(); ++jj ) {
362  newrsd->set_chi(
363  concrete_residue->proton_chi_2_chi( jj ),
364  proton_chi_chisets[ ii ]->chi[ jj ] );
365  }
366  }
367  } else {
368  // This is bad: fields like seqpos, chain, etc. don't match existing residue!
369  //conformation::ResidueOP newrsd = rotamers_[i]->clone();
370  conformation::ResidueOP newrsd = dup_residue( existing_residue, *rotamers_[i] );
371  new_rotamers.push_back( newrsd );
372  }
373  }
374 
375  // Superimpose
376  // This was a nice idea, superimposing on the "pharmacophores" of the ligand,
377  // and just trying them all. But no doubt it violates the packer's expectation
378  // that the residue's NBR_ATOM does not move during repacking!
379  // So, this code is not sound during scoring -- DO NOT USE.
380  //bool const do_multiple_superpos = false;
381  //if( do_multiple_superpos ) {
382  // using utility::vector1;
383  // rotamers.reserve( rotamers.size() + new_rotamers.size()*total_superpos_ );
384  // // For all rigid fragments...
385  // for(Size i = 1, i_end = rigid_frags_.size(); i <= i_end; ++i) {
386  // Fragment const & frag = rigid_frags_[i];
387  // // And for all of their automorphisms...
388  // vector1< Automorphism * > const & morphs = frag_automorphs_[i];
389  // for(Size j = 1, j_end = morphs.size(); j <= j_end; ++j) {
390  // Automorphism const & morph = *(morphs[j]);
391  // // Try superimposing each conformer using that grouping of atoms!
392  // for(Size k = 1, k_end = new_rotamers.size(); k <= k_end; ++k) {
393  // conformation::ResidueOP newrsd = dup_residue( existing_residue, *new_rotamers[k] );
394  // superimpose( existing_residue, *newrsd, frag, morph );
395  // rotamers.push_back(newrsd);
396  // }
397  // }
398  // }
399  //} else {
400  rotamers.reserve( rotamers.size() + new_rotamers.size() );
401  for(Size k = 1, k_end = new_rotamers.size(); k <= k_end; ++k) {
402  conformation::ResidueOP newrsd = new_rotamers[k];
403  // Superimposes on nbr_atom and 2 of its neighbors
404  newrsd->place( existing_residue, pose.conformation() );
405  rotamers.push_back(newrsd);
406  }
407  //}
408 
409  int const end_size = rotamers.size();
410  TR << "Added " << end_size - start_size << " rotamers for " << concrete_residue->name() << std::endl;
411 
412  // Debugging:
413  //dump_library(concrete_residue->name()+".rotlib.pdb", rotamers);
414 }
415 
416 /// @brief Fills in missing hydrogens/virtual atoms from library load
417 void
419 {
420  assert( rsd );
421  assert( missing.size() == rsd->natoms() );
422  //Unlike Residue::fill_missing_atoms(), only do a single pass -
423  // The residue should be constructed so that any atoms which would be typically missing
424  // (i.e. hydrogens and virtual atoms) are either built from present atoms, or can be built
425  // from "missing" atoms which are built earlier
426  for ( Size i=1; i<= rsd->natoms(); ++i ) {
427  if ( missing[i] ) {
428  if ( ! rsd->atom_is_hydrogen( i ) && ! rsd->is_virtual( i ) ) {
429  utility_exit_with_message("Non-virtual heavy atom "+rsd->atom_name(i)+" is missing in rotamer library for residue "+rsd->name()+"!");
430  }
431 
432  chemical::AtomICoor const & ic( rsd->icoor(i) );
433  // check to see if any of our stub atoms are missing:
434  for ( Size j=1; j<= 3; ++j ) {
435  Size stubno( ic.stub_atom(j).atomno() );
436  if ( missing[ stubno ] ) {
437  TR.Error << "[ ERROR ] Missing atom " << stubno << " (" << rsd->atom_name(stubno) << ") when trying to place atom " <<
438  i << " (" << rsd->atom_name(i) << ") in " << rsd->name() << std::endl;
439  utility_exit_with_message("Cannot build missing atoms in ligand rotamer library");
440  }
441  }
442 
443  // no stub atoms missing: build our ideal coordinates
444  missing[i] = false; // In case we're building later residues off of this one.
445  rsd->set_xyz( i, ic.build( *rsd ) ); // We just checked that all stub atoms exist in this residue, so we should be safe with the build call.
446  if( ! missed[ i ] ) {
447  missed[ i ] = true;
448  TR << "Atom " << rsd->atom_name(i) << " from residue " << rsd->name() << " not found in PDB_ROTAMERS library, creating based on idealized geometry." << std::endl;
449  }
450  }
451  }
452 }
453 
454 //XRW_B_T1
455 /*
456 /// @details Not implemented -- will cause program termination.
457 /// This is used only by coarse representations!
458 SingleResidueRotamerLibraryOP
459 SingleLigandRotamerLibrary::coarsify(coarse::Translator const & map) const
460 {
461  utility_exit_with_message("Ligand residue rotamers can't be coarsified!");
462  return NULL; // make compiler happy
463 }
464 */
465 //XRW_E_T1
466 
467 
468 /// @details Not implemented -- will cause program termination.
469 /// Is this only used by coarse representations?
470 void
471 SingleLigandRotamerLibrary::write_to_file( utility::io::ozstream & /*out*/ ) const
472 {
473  utility_exit_with_message("Ligand residue rotamers can't be written to file!");
474 }
475 
476 
477 // Helper function
478 //bool bond_is_rotatable(chemical::ResidueTypeCOP restype, core::Size a1, core::Size a2)
479 //{
480 // for(core::Size i = 1, i_end = restype->nchi(); i <= i_end; ++i) {
481 // chemical::AtomIndices chi = restype->chi_atoms(i);
482 // assert( chi.size() == 4 );
483 // if( (chi[2] == a1 && chi[3] == a2) || (chi[2] == a2 && chi[3] == a1) ) return true;
484 // }
485 // return false;
486 //}
487 
488 
489 //void SingleLigandRotamerLibrary::find_fragments(chemical::ResidueTypeCOP restype)
490 //{
491 // // Fragments are delimited by rotatable bonds, but the atom on the far end
492 // // of the bond is still part of the fragment.
493 // // Thus, fragments may overlap slightly.
494 // using core::Size;
495 // using utility::vector1;
496 // using namespace core::chemical;
497 //
498 // Size const natoms = restype->nheavyatoms();
499 // vector1<bool> in_frag_core(natoms, false); // atoms on far side of rot bond aren't in "core"
500 // for(Size root = 1; root <= natoms; ++root) {
501 // if( in_frag_core[root] ) continue; // already got this fragment!
502 // Fragment the_frag;
503 // vector1<bool> visited(natoms, false);
504 // AtomIndices to_visit;
505 // to_visit.push_back(root); // will only hold atoms in fragment core
506 // while( !to_visit.empty() ) {
507 // Size const curr = to_visit.back();
508 // to_visit.pop_back();
509 // if( visited[curr] ) continue;
510 // visited[curr] = true;
511 // the_frag.push_back(curr);
512 // in_frag_core[curr] = true;
513 // AtomIndices const & nbrs = restype->nbrs(curr);
514 // for(Size i = 1; i <= nbrs.size(); ++i) {
515 // Size const nbr = nbrs[i];
516 // if( nbr > natoms ) continue; // e.g. hydrogens!
517 // if( visited[nbr] ) continue;
518 // if( bond_is_rotatable(restype, curr, nbr) ) {
519 // // don't "recurse", handle everything here
520 // visited[nbr] = true;
521 // the_frag.push_back(nbr);
522 // } else { // non-rotatable bond, part of fragment core
523 // // "recurse"; flags will be set in main loop
524 // to_visit.push_back(nbr);
525 // }
526 // }
527 // }
528 // // Only keep frags with 3+ atoms -- can't superimpose on less.
529 // if( the_frag.size() >= 3 ) rigid_frags_.push_back(the_frag);
530 // }
531 // assert( rigid_frags_.size() <= restype->nchi()+1 );
532 //
533 // for(Size i = 1; i <= rigid_frags_.size(); ++i) {
534 // TR << "Fragment " << i << ":";
535 // Fragment const & frag = rigid_frags_[i];
536 // for(Size j = 1; j <= frag.size(); ++j) TR << " " << restype->atom_name(frag[j]);
537 // TR << std::endl;
538 // }
539 //}
540 
541 
542 //void SingleLigandRotamerLibrary::list_automorphisms(chemical::ResidueTypeCOP restype)
543 //{
544 // using namespace core::chemical;
545 // AutomorphismIterator ai( restype, false /*don't include H*/ );
546 // while(true) {
547 // Automorphism a = ai.next();
548 // if( a.empty() ) break;
549 // automorphs_.push_back( a );
550 // }
551 // assert( automorphs_.size() > 0 );
552 // TR << "Ligand has " << automorphs_.size() << " automorphisms" << std::endl;
553 //}
554 
555 
556 //void SingleLigandRotamerLibrary::unique_auto_for_frags()
557 //{
558 // using utility::vector1;
559 // total_superpos_ = 0;
560 // frag_automorphs_.resize( rigid_frags_.size() ); // one entry per fragment
561 // // For each fragment...
562 // for(Size i = 1, i_end = rigid_frags_.size(); i <= i_end; ++i) {
563 // Fragment const & frag = rigid_frags_[i];
564 // vector1< Automorphism * > const & frag_morphs = frag_automorphs_[i];
565 // // Look at each whole-molecule automorphism...
566 // for(Size j = 1, j_end = automorphs_.size(); j <= j_end; ++j) {
567 // Automorphism /*const*/ & new_morph = automorphs_[j];
568 // bool already_have_it = false;
569 // // And add it if it's not equivalent to some other one we already added.
570 // for(Size k = 1, k_end = frag_morphs.size(); k <= k_end && !already_have_it; ++k) {
571 // Automorphism const & old_morph = *(frag_morphs[k]);
572 // assert( new_morph.size() == old_morph.size() );
573 // bool are_same = true;
574 // // Two automorphisms are the same from a fragment's point of view
575 // // if they contain the same mapping for all fragment atom positions.
576 // for(Size l = 1, l_end = frag.size(); l <= l_end; ++l) {
577 // Size const atom = frag[l];
578 // if( new_morph[atom] != old_morph[atom] ) {
579 // are_same = false;
580 // break;
581 // }
582 // } // end compare two automorphisms
583 // if( are_same ) already_have_it = true;
584 // } // end search over existing automorphisms for fragment
585 // if( !already_have_it ) {
586 // frag_automorphs_[i].push_back( &new_morph );
587 // total_superpos_ += 1;
588 // }
589 // } // end search over whole-molecule automorphisms
590 // } // end search over fragments
591 // assert( total_superpos_ > 0 );
592 // TR << total_superpos_ << " unique possible rotamer-substitution superpositions" << std::endl;
593 //}
594 
595 
596 } // namespace dunbrack
597 } // namespace scoring
598 } // namespace core