Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
PDBInfo.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/pose/PDBInfo.cc
11 /// @brief class to hold PDB information so it's not loose in the pose
12 /// @author Steven Lewis
13 /// @author Yih-En Andrew Ban (yab@u.washington.edu)
14 
15 // Unit headers
16 #include <core/pose/PDBInfo.hh>
17 
18 // Project headers
23 // AUTO-REMOVED #include <core/io/pdb/file_data.hh>
24 #include <core/pose/Pose.hh>
25 #include <core/pose/PDBPoseMap.hh>
26 
27 // Utility headers
28 #include <basic/Tracer.hh>
29 #include <utility/PyAssert.hh>
30 #include <utility/exit.hh>
31 #include <utility/vector1.hh>
32 
33 // C++ headers
34 #include <algorithm>
35 
36 #include <utility/vector0.hh>
37 #include <ObjexxFCL/format.hh>
38 
39 //Auto Headers
41 //using namespace ObjexxFCL;
42 using namespace ObjexxFCL::fmt;
43 
44 
45 namespace core {
46 namespace pose {
47 
48 static basic::Tracer TR("core.pose.PDBInfo");
49 
50 /// @brief default constructor, obsolete is *true*
51 PDBInfo::PDBInfo() :
52  Super(),
53  obsolete_( true ),
54  name_( "" ),
55  modeltag_( "" ),
56  conf_( NULL ),
57  num_unrecognized_res_(0),
58  num_unrecognized_atoms_(0)
59 {}
60 
61 
62 /// @brief size constructor (ensure space for 'n' residue records),
63 /// obsolete is *true*
64 PDBInfo::PDBInfo( Size const n ) :
65  Super(),
66  obsolete_( true ),
67  name_( "" ),
68  modeltag_( "" ),
69  residue_rec_( n ),
70  conf_( NULL ),
71  num_unrecognized_res_(0),
72  num_unrecognized_atoms_(0)
73 {}
74 
75 
76 /// @brief Pose constructor (ensures space for residue and atom records
77 /// relative to Pose)
78 /// @param[in] pose Pose
79 /// @param[in] init if true (default), then residue records are initialized
80 /// and obsolete set to false, otherwise obsolete is true
81 /// using Pose residue numbering and chains of the Residues in the Conformation
83  Pose const & pose,
84  bool init
85 ) :
86  Super(),
87  obsolete_( !init ),
88  name_( "" ),
89  modeltag_( "" ),
90  residue_rec_( pose.total_residue() ),
91  conf_( NULL ),
92  num_unrecognized_res_(0),
93  num_unrecognized_atoms_(0)
94 {
95  resize_atom_records( pose );
96 
97  // initialize using Pose residue numbering and chains of the Residues
98  // in the Pose's Conformation
99  if ( init ) {
100  // do this manually to save on method calls
101  for ( Size i = 1, ie = pose.total_residue(); i <= ie; ++i ) {
102  ResidueRecord & rr = residue_rec_[ i ];
103  //assert( pose.residue( i ).chain() < chr_chains.size() );
104  rr.chainID = chr_chains[ pose.residue( i ).chain() % chr_chains.size() ]; //fpd wrap around if > 62 chains
105  rr.resSeq = static_cast< int >( i );
106  }
108  }
109 }
110 
111 
112 /// @brief copy constructor
113 PDBInfo::PDBInfo( PDBInfo const & info ) :
114  Super( info ),
115  obsolete_( info.obsolete_ ),
116  name_( info.name_ ),
117  modeltag_( info.modeltag_ ),
118  header_information_( info.header_information_ ),
119  remarks_( info.remarks_ ),
120  residue_rec_( info.residue_rec_ ),
121  pdb2pose_( info.pdb2pose_ ),
122  conf_( NULL ), // no observation on copy
123  unrecognized_atoms_ ( info.unrecognized_atoms_ ),
124  unrecognized_res_num2name_( info.unrecognized_res_num2name_ ),
125  unrecognized_res_size_ ( info.unrecognized_res_size_ ),
126  num_unrecognized_res_ ( info.num_unrecognized_res_ ),
127  num_unrecognized_atoms_ ( info.num_unrecognized_atoms_ )
128 {}
129 
130 
131 /// @brief default destructor
133 {
134  detach_from(); // stop observing Conformation
135 }
136 
137 
138 /// @brief copy assignment
139 /// @details any Conformation already being observed stays constant, there is no
140 /// re-assignment
141 PDBInfo &
143 {
144  if ( this != &info ) {
145  Super::operator =( info );
146 
147  obsolete_ = info.obsolete_;
148 
149  name_ = info.name_;
150  modeltag_ = info.modeltag_;
151  remarks_ = info.remarks_;
152  if(info.header_information_){
155  }
156 
157  residue_rec_ = info.residue_rec_;
158 
159  pdb2pose_ = info.pdb2pose_;
160 
166  }
167  return *this;
168 }
169 
170 
171 /// @brief is this PDBInfo currently observing a conformation?
172 /// @return the Conformation being observed, otherwise NULL
175  return conf_;
176 }
177 
178 
179 /// @brief attach to Conformation and begin observation
180 void
182  // detach from prior Conformation if necessary
183  if ( conf_ ) {
184  detach_from();
185  }
186 
190 
191  conf_ = &conf;
192 }
193 
194 
195 /// @brief detach from Conformation and stop observation
196 /// @remarks takes no arguments because PDBInfo can only observe one
197 /// Conformation at a time
198 void
200  if ( conf_ ) {
204  }
205 
206  conf_ = NULL;
207 }
208 
209 
210 /// @brief update when connection to Conformation is changed
211 void
214 
215  switch ( event.tag ) {
216  case ConnectionEvent::DISCONNECT:
217  obsolete( true );
218  detach_from();
219  break;
220  case ConnectionEvent::TRANSFER:
221  // Disconnect -- PDBInfo does not honor TRANSFER tag.
222  break;
223  default: // do nothing
224  break;
225  }
226 
227 }
228 
229 
230 /// @brief update atom records when residue identity changes in Conformation
231 void
234 
235  switch( event.tag ) {
236  case IdentityEvent::INVALIDATE: {
237  obsolete( true );
238  // detach for now, in the future consider whether it's appropriate to
239  // rebuild all the data
240  detach_from();
241  break;
242  }
243  case IdentityEvent::RESIDUE: {
244  // replace and zero the records
245  replace_res( event.position, event.residue->natoms() );
246  break;
247  }
248  default: {
249  // do nothing, fall through
250  break;
251  }
252  }
253 }
254 
255 
256 /// @brief update residue and atom records when length changes in Conformation,
257 /// obsoletes PDBInfo
258 /// @details Residue and atoms records in PDBInfo will be automatically resized
259 /// and the obsolete flag will be set to true, causing PDBInfo to not be used
260 /// on pdb output. Existing data inside the PDBInfo is not touched. After
261 /// filling in the data for the newly updated residues, setting
262 /// PDBInfo::obsolete( false ) will re-enable usage of PDBInfo on pdb output.
263 /// If PDBInfo receives a LengthEvent::INVALIDATE, it will obsolete and then
264 /// detach itself for safety.
265 void
268 
269  switch( event.tag ) {
270  case LengthEvent::INVALIDATE: {
271  obsolete( true );
272  detach_from(); // if we don't detach, an array-out-of-bounds may occur on future signals
273  break;
274  }
275  case LengthEvent::RESIDUE_APPEND: {
276  append_res( event.position, event.residue->natoms(), abs( event.length_change ) );
277  obsolete( true );
278  break;
279  }
280  case LengthEvent::RESIDUE_PREPEND: {
281  prepend_res( event.position, event.residue->natoms(), abs( event.length_change ) );
282  obsolete( true );
283  break;
284  }
285  case LengthEvent::RESIDUE_DELETE: {
286  delete_res( event.position, abs( event.length_change ) );
287  //Shouldn't obsolete PDBInfo on delete - every residue that still exists has valid information
288  //obsolete( true );
289  break;
290  }
291  default: {
292  // do nothing, fall through
293  break;
294  }
295  }
296 }
297 
298 
299 /// @brief resize for 'n' residue records
300 /// @details Leaves atom record state inconsistent. Atom records for
301 /// remaining residues are untouched while new residues have no atom
302 /// records, so make sure and call one of resize_atom_records()
303 /// afterwards if necessary.
304 /// @warning Do not use this method for ins/del of residues, as it leaves
305 /// the data state inconsistent. See append_res/prepend_res/delete_res
306 /// for that type of functionality.
307 void
309 {
310  residue_rec_.resize( n );
312 }
313 
314 
315 /// @brief ensure 'n' available atom records for particular residue
316 /// @param[in] res residue
317 /// @param[in] n number of atoms
318 /// @param[in] zero if true, zero the atom records for this residue
319 void
321  Size const res,
322  Size const n,
323  bool const zero
324 )
325 {
326  if ( zero ) {
327  residue_rec_[ res ].atomRec = AtomRecords( n );
328  } else {
329  residue_rec_[ res ].atomRec.resize( n );
330  }
331 }
332 
333 
334 /// @brief ensure 'n' available atom records for every residue
335 /// @param[in] res residue
336 /// @param[in] n number of atoms
337 /// @param[in] zero if true, zero the atom records
338 void
340  Size const n,
341  bool const zero
342 )
343 {
344  if ( zero ) {
345  for ( ResidueRecords::iterator i = residue_rec_.begin(), ie = residue_rec_.end(); i != ie; ++i ) {
346  i->atomRec = AtomRecords( n );
347  }
348  } else {
349  for ( ResidueRecords::iterator i = residue_rec_.begin(), ie = residue_rec_.end(); i != ie; ++i ) {
350  i->atomRec.resize( n );
351  }
352  }
353 }
354 
355 
356 /// @brief update number of atom records with respect to atoms in Pose
357 /// @details Number of internally available atom records will be adjusted
358 /// to match number of atoms within each residue in Pose. Only newly
359 /// created records will be zeroed, any existing records are untouched.
360 void
362 {
364 
365  assert( residue_rec_.size() == pose.n_residue() );
366 
367  for ( Size r = 1, re = pose.n_residue(); r <= re; ++r ) {
368  residue_rec_[ r ].atomRec.resize( pose.residue( r ).natoms() );
369  }
370 }
371 
372 
373 /// @brief tighten memory usage
374 void
376 {
377  // tighten remarks vector
378  if ( remarks_.capacity() > remarks_.size() ) {
379  Remarks( remarks_ ).swap( remarks_ );
380  }
381 
382  // tighten residue vector
383  if ( residue_rec_.capacity() > residue_rec_.size() ) {
385  }
386 
387  // tighten each of the atom vectors
388  for ( ResidueRecords::iterator i = residue_rec_.begin(), ie = residue_rec_.end(); i != ie; ++i ) {
389  if ( i->atomRec.capacity() > i->atomRec.size() ) {
390  AtomRecords( i->atomRec ).swap( i->atomRec );
391  }
392  }
393 }
394 
395 
396 /// @brief translates the pose number to pdb numbering string
397 /// for use in PyRosetta.
398 /// @param[in] res pose residue number
399 /// @return pdb string containing chainID and number
401 PDBInfo::pose2pdb( Size const res ) const
402 {
403  PyAssert((res > 0) && (res <= residue_rec_.size()), "PDBInfo::pose2pdb( Size const res ): res is not in this PDBInfo!" );
404  std::stringstream pdb_num, pdb_chain;
405  pdb_num << residue_rec_[res].chainID;
406  pdb_chain << residue_rec_[res].resSeq;
407  return pdb_chain.str() + " " + pdb_num.str();
408 }
409 
410 
411 /// @brief set chain id for residue
412 /// @remarks chain id should not be the empty record character, currently '^'
413 void
415  Size const res,
416  char const chain_id
417 )
418 {
419  PyAssert((res > 0) && (res <= residue_rec_.size()), "PDBInfo::chain( Size const res, char const chain_id ): res is not in this PDBInfo!" );
420  ResidueRecord & rr = residue_rec_[ res ];
421 
422  // sync map
423  pdb2pose_.conditional_erase( rr.chainID, rr.resSeq, rr.iCode, res );
424  pdb2pose_.insert( chain_id, rr.resSeq, rr.iCode, res );
425 
426  // set new residue info
427  rr.chainID = chain_id;
428 }
429 
430 /// @brief set pdb residue sequence number
431 void
433  Size const res,
434  int const pdb_res
435 )
436 {
437  PyAssert((res > 0) && (res <= residue_rec_.size()), "PDBInfo::number( Size const res, int const pdb_res ): res is not in this PDBInfo!" );
438  ResidueRecord & rr = residue_rec_[ res ];
439 
440  // sync map
441  pdb2pose_.conditional_erase( rr.chainID, rr.resSeq, rr.iCode, res );
442  pdb2pose_.insert( rr.chainID, pdb_res, rr.iCode, res );
443 
444  // set new residue info
445  rr.resSeq = pdb_res;
446 }
447 
448 /// @brief set insertion code for residue
449 void
451  Size const res,
452  char const ins_code
453 )
454 {
455  PyAssert((res > 0) && (res <= residue_rec_.size()), "PDBInfo::icode( Size const res, ins_code ): res is not in this PDBInfo!" );
456  ResidueRecord & rr = residue_rec_[ res ];
457 
458  // sync map
459  pdb2pose_.conditional_erase( rr.chainID, rr.resSeq, rr.iCode, res );
460  pdb2pose_.insert( rr.chainID, rr.resSeq, ins_code, res );
461 
462  // set new residue info
463  rr.iCode = ins_code;
464 }
465 
466 /// @brief Displays the PDB info by expressing continuous chain segments
467 void
469  std::ostream & out
470 ) const
471 {
472  // counters
473  Size current_res_tot = 0;
474  Size current_atm_tot = 0;
475  Size atm_tot = 0;
476  Size current_start_pose = 0;
477  char icode_start_pdb(' ');
478  // first line
479  out << "PDB file name: " << name() << std::endl;
480  out << " Pose Range Chain PDB Range | #Resi #Atoms\n" << std::endl;
481  for (Size i=1 ; i <= nres() ; i++){
482  // loop through residue records
483  if ( i<nres() && icode(i)==' ' && (number(i+1)-number(i))==1 && chain(i)==chain(i+1) ){
484  if (current_start_pose<1){ // its empty
485  current_res_tot = 1;
486  current_atm_tot = natoms(i);
487  current_start_pose = i;
488  icode_start_pdb = icode(i);
489  } else { // its started, update info
490  current_res_tot += 1;
491  current_atm_tot += natoms(i);
492  }
493  } else { // print it
494  out << I(4,4,current_start_pose) << " -- " << I(4,4,i) << A(5,chain(i)) <<
495  ' ' << I(4,4,number(i)-current_res_tot) << icode_start_pdb << " -- " << I(4,4,number(i)) << icode(i) <<
496  " | " << I(6,4,current_res_tot+1) << " residues; " << I(8,5,current_atm_tot) << " atoms\n";
497  // then update/blank data
498  atm_tot += current_atm_tot;
499  current_start_pose = 0;
500  }
501  }
502  // last line
503  out << "\t\t\t TOTAL | " << I(6,4,nres()) << " residues; " << I(8,5,atm_tot) << std::endl;
504 }
505 
506 /// @brief set chain/pdb/insertion code for residue simultaneously
507 /// @note convenience method; more efficient than doing each individually
508 /// due to map updates
509 /// @param[in] res residue in pose numbering
510 /// @param[in] chain_id pdb chain id
511 /// @param[in] pdb_res residue in pdb numbering
512 /// @param[in] ins_code pdb insertion code
513 void
515  Size const res,
516  char const chain_id,
517  int const pdb_res,
518  char const ins_code
519 )
520 {
521  ResidueRecord & rr = residue_rec_[ res ];
522 
523  // sync map
524  pdb2pose_.conditional_erase( rr.chainID, rr.resSeq, rr.iCode, res );
525  pdb2pose_.insert( chain_id, pdb_res, ins_code, res );
526 
527  // set new residue info
528  rr.chainID = chain_id;
529  rr.resSeq = pdb_res;
530  rr.iCode = ins_code;
531 }
532 
533 
534 /// @brief set all residue chain IDs to a single character
535 void
536 PDBInfo::set_chains( char const id ) {
537  for ( ResidueRecords::iterator i = residue_rec_.begin(), ie = residue_rec_.end(); i < ie; ++i ) {
538  i->chainID = id;
539  }
540 
542 }
543 
544 
545 /// @brief copy a section from another PDBInfo
546 /// @param[in] input_info the PDBInfo to copy from
547 /// @param[in] copy_from the first residue position in input_info to copy
548 /// @param[in] copy_to the final residue position in input_info to copy
549 /// @param[in] start_from the first residue position in this PDBInfo to
550 /// copy into
551 void
553  PDBInfo const & input_info,
554  Size const copy_from,
555  Size const copy_to,
556  Size const start_from
557 )
558 {
559  assert( copy_from <= input_info.residue_rec_.size() );
560  assert( copy_to <= input_info.residue_rec_.size() );
561  assert( start_from <= residue_rec_.size() );
562 
563  // force erase data from map
564  Size idx = start_from;
565  ResidueRecords::const_iterator const begin = residue_rec_.begin() + ( start_from - 1 );
566  ResidueRecords::const_iterator const end = begin + ( copy_to - copy_from + 1 );
567  for ( ResidueRecords::const_iterator i = begin; i < end; ++i, ++idx ) {
568  pdb2pose_.erase( i->chainID, i->resSeq, i->iCode );
569  }
570 
571  // do the copy
572  std::copy(
573  input_info.residue_rec_.begin() + ( copy_from - 1 ),
574  input_info.residue_rec_.begin() + copy_to,
575  residue_rec_.begin() + ( start_from - 1 )
576  );
577 
578  // add new data to map
579  Size count = start_from;
580  for ( ResidueRecords::const_iterator i = begin; i < end; ++i, ++count ) {
581  pdb2pose_.insert( i->chainID, i->resSeq, i->iCode, count );
582  }
583 }
584 
585 
586 /// @brief append residue records after given residue number
587 /// @param[in] res residue to append after (in internal/pose numbering)
588 /// @param[in] natoms number of atoms in type of appended residue
589 /// @param[in] n number of residue records to append
590 void
592  Size const res,
593  Size const natoms,
594  Size const n
595 )
596 {
597  ResidueRecord rr;
598  rr.atomRec.resize( natoms );
599  residue_rec_.insert( residue_rec_.begin() + res, n, rr );
600 
601  // sync map -- rebuild from res+2 onwards
602  Size count = res + 2;
603  ResidueRecords::const_iterator i = residue_rec_.begin() + ( res + 1 );
604  for ( ResidueRecords::const_iterator ie = residue_rec_.end(); i < ie; ++i, ++count ) {
605  pdb2pose_.insert( i->chainID, i->resSeq, i->iCode, count );
606  }
607 }
608 
609 
610 /// @brief prepend residue records before given residue number
611 /// @param[in] res residue to prepend before (in internal/pose numbering)
612 /// @param[in] natoms number of atoms in type of appended residue
613 /// @param[in] n number of residue records to prepend
614 void
616  Size const res,
617  Size const natoms,
618  Size const n
619 )
620 {
621  ResidueRecord rr;
622  rr.atomRec.resize( natoms );
623  residue_rec_.insert( residue_rec_.begin() + ( res - 1 ), n, rr );
624 
625  // sync map -- rebuild from res+1 onwards
626  Size count = res + 1;
627  ResidueRecords::const_iterator i = residue_rec_.begin() + res;
628  for ( ResidueRecords::const_iterator ie = residue_rec_.end(); i < ie; ++i, ++count ) {
629  pdb2pose_.insert( i->chainID, i->resSeq, i->iCode, count );
630  }
631 }
632 
633 
634 /// @brief "replace" residue record for given residue number
635 /// @details Leaves information in residue record untouched, but resizes
636 /// and zeroes atom records for the residue.
637 /// @param[in] res residue to replace
638 /// @param[in] natoms number of atoms in type of residue
639 void
641  Size const res,
642  Size const natoms
643 )
644 {
645  AtomRecords ar( natoms );
646  residue_rec_[ res ].atomRec = ar;
647 
648  // no need to sync map, data should stay constant
649 }
650 
651 
652 /// @brief delete 'n' residue records starting from given residue
653 /// @param[in] res residue to start deleting from (in internal/pose numbering)
654 /// @param[in] n number of residue records to delete
655 void
657  Size const res,
658  Size const n
659 )
660 {
661  ResidueRecords::iterator start = residue_rec_.begin() + ( res - 1 );
662 
663  // sync map first (force erase)
664  Size idx = res;
665  for ( ResidueRecords::iterator i = start, ie = start + n; i < ie; ++i, ++idx ) {
666  pdb2pose_.erase( i->chainID, i->resSeq, i->iCode );
667  }
668 
669  // delete residue records
670  residue_rec_.erase( start, start + n );
671 
672  // sync map -- renumber existing records
673  for ( Size i = res, ie = residue_rec_.size(); i <= ie; ++i ) {
674  ResidueRecord const & rr = residue_rec_[ i ];
675  pdb2pose_.insert( rr.chainID, rr.resSeq, rr.iCode, i );
676  }
677 }
678 
679 
680 // added by sheffler
681 /// @brief remembers info about atoms not read into the pose
682 void
684  Size resnum,
685  std::string resname,
686  std::string atomname,
688  Real temp
689 ) {
690  unrecognized_atoms_.push_back( UnrecognizedAtomRecord(resnum,resname,atomname,coords,temp) );
691  unrecognized_res_num2name_[resnum] = resname;
692  if( unrecognized_res_size_.find(resnum) == unrecognized_res_size_.end() ) {
693  unrecognized_res_size_[resnum] = 0;
695  }
697  unrecognized_res_size_[resnum] += 1;
698 }
699 
700 
701 /// @brief if size of residue records != passed value, fail fast
702 /// @note This is meant to be used only for en masse methods, not individual
703 /// residue/atom methods
704 void
706 {
707  if ( residue_rec_.size() != size ) {
708  basic::Error() << "PDBInfo::check_residue_records_size() failed. An en masse action attempted to access or set more residues than exists in PDBInfo."
709  << std::endl;
710  utility_exit();
711  }
712 }
713 
714 
715 /// @brief rebuilds PDBPoseMap from scratch
716 void
718 {
719  pdb2pose_.clear();
720  pdb2pose_.fill( *this );
721 }
722 
723 std::ostream & operator << (std::ostream & os, PDBInfo const & info)
724 {
725  //os << "PDB file name: " << info.name() << std::endl;
726  info.show(os);
727  //os << "Number of Residues: " << info.nres() << std::endl;
728  // perhaps add per residue information: pose_num, pdb_num, chain, icode, occupancy, temperature, etc.
729  return os;
730 }
731 
732 } // pose
733 } // core