Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
PDBOutput.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 /// @author ashworth
11 /// @brief
12 
14 #include <protocols/jd2/Job.hh>
15 
16 #include <protocols/dna/util.hh> // dna_full_name3
20 
23 #include <basic/options/option.hh>
25 #include <core/pose/Pose.hh>
26 #include <core/pose/PDBInfo.hh>
27 #include <core/scoring/Energies.hh>
32 #include <core/scoring/hbonds/hbonds_geom.hh> // make_hbBasetoAcc_unitvector
35 #include <basic/MetricValue.hh>
36 #include <basic/Tracer.hh>
37 
38 #include <utility/vector1.hh>
39 
40 #include <utility/file/file_sys_util.hh> // file_exists, create_directory
41 #include <utility/file/gzip_util.hh>
42 #include <utility/string_util.hh>
43 
44 #include <algorithm> // std::min
45 #include <iostream>
46 #include <fstream>
47 
48 // option key includes
49 
50 #include <basic/options/keys/dna.OptionKeys.gen.hh>
51 #include <basic/options/keys/score.OptionKeys.gen.hh>
52 #include <basic/options/keys/run.OptionKeys.gen.hh>
53 
55 #include <utility/io/ozstream.hh>
56 #include <ObjexxFCL/format.hh>
57 
58 
59 namespace protocols {
60 namespace dna {
61 
62 using utility::string_split;
63 using utility::vector1;
64 using namespace core;
65 using namespace core::chemical;
66 using namespace core::conformation;
67 using namespace core::scoring;
68 using namespace core::pack;
69 using namespace basic::options;
70 using namespace ObjexxFCL::fmt;
71 
72 using basic::t_info;
73 using basic::t_debug;
74 using basic::t_trace;
75 static basic::Tracer TR( "protocols.dna.PDBOutput", t_info );
76 ////////////////////////////////////////////////////////////////////////////////////////////////////
77 
79  : jd2::PDBJobOutputter(),
80  pose_copy_(0),
81  reference_pose_(0),
82  chi_diff_threshold_(0.0001),
83  mainchain_torsion_diff_threshold_(0.0001),
84  enabled_(true)
85 {}
86 
88 
89 ////////////////////////////////////////////////////////////////////////////////////////////////////
90 /// @begin PDBOutput::final_pose
91 /// @details pose is const here, so it must be scored already if score information is expected in output file
92 /// @author ashworth
93 void
94 PDBOutput::final_pose( JobCOP job, Pose const & pose )
95 {
96  if ( !enabled_ ) return; // to allow easy overrides of excess pdb writing in higher-level code
97  pose_copy_ = new Pose( pose );
98  ozstream pdbout( extended_name(job) );
99  if ( !pdbout.good() )
100  utility_exit_with_message( "Unable to open file: " + extended_name(job) + "\n" );
101  pdbout << "REMARK BEGIN ROSETTA INFO\n";
102  extract_data_from_Job( job, pdbout );
103  output_pdb( pdbout );
104  // append job info to pdb (parent virtual function call)
105  pdbout.close();
106 }
107 
108 ////////////////////////////////////////////////////////////////////////////////////////////////////
109 /// @begin PDBOutput () operator -- non-JobDistributor usage
110 /// @details scores pdb
111 /// @author ashworth
112 void
114  Pose const & pose,
115  std::string const & name
116 )
117 {
118  if ( !enabled_ ) return; // to allow easy overrides of excess pdb writing in higher-level code
119  pose_copy_ = new Pose( pose );
120  make_subdirs( name );
121  ozstream pdbout( name );
122  if ( !pdbout.good() )
123  utility_exit_with_message( "Unable to open file: " + name + "\n" );
124  pdbout << "REMARK BEGIN ROSETTA INFO\n";
125  output_info( pdbout );
126  output_pdb( pdbout );
127  pdbout.close();
128 }
129 
130 ////////////////////////////////////////////////////////////////////////////////////////////////////
131 /// @begin PDBOutput::output_pdb
132 /// @details
133 /// @author ashworth
134 void
136 {
137  if ( ! enabled_ ) return; // to allow easy overrides of excess pdb writing in higher-level code
138  runtime_assert( pose_copy_ );
140  ( *score_function_ )( *pose_copy_ );
141  // if the sparse_pdb_output option is used, this limits most output to residues which differ from the reference structure (if it exists)
143  output_score_info( pdbout );
144  output_design_tags( pdbout );
145  output_hbond_info( pdbout );
147  pose_copy_->dump_pdb( pdbout, res_indices_to_output_ );
148 }
149 
150 ////////////////////////////////////////////////////////////////////////////////////////////////////
151 void PDBOutput::starting_pose( Pose const & pose ) { reference_pose( pose ); }
152 
153 void PDBOutput::reference_pose( Pose const & pose )
154 {
155  // make hard copy to guarantee that the reference pose remains unchanged
156  reference_pose_ = new Pose( pose );
157  designed_residues_.assign( reference_pose_->total_residue(), false );
158 }
160 
162 {
163  score_function_ = new ScoreFunction( sf );
164 }
166 
168  Size index,
169  bool value /* = true */
170 )
171 {
172  Size const s( designed_residues_.size() );
173  int const ext( index - s );
174  // not sure this auto-expansion is working...
175  if ( ext > 0 ) designed_residues_.insert( designed_residues_.end(), ext, false );
176  designed_residues_[ index ] = value;
177 }
178 
179 ////////////////////////////////////////////////////////////////////////////////////////////////////
180 // update designed_residues_ from a PackerTask
181 void
183 {
184  for ( Size index(1), end( ptask->total_residue() ); index < end; ++index ) {
185  if ( ptask->design_residue(index) ||
186  ptask->residue_task(index).has_behavior("TARGET") ) {
187  designed_residue(index);
188  }
189  }
190 }
191 
192 ////////////////////////////////////////////////////////////////////////////////////////////////////
193 void
195 {
196  for ( StringsMap::const_iterator itr( info_map_.begin() ), end( info_map_.end() );
197  itr != end; ++itr ) {
198  // the 'key' or title for this particular set of information
199  pdbout << itr->first << '\n';
200  for ( Strings::const_iterator line( itr->second.begin() ), end2( itr->second.end() );
201  line != end2; ++line ) {
202  pdbout << *line << '\n';
203  }
204  pdbout << "REMARK\n";
205  }
206 }
207 
208 ////////////////////////////////////////////////////////////////////////////////////////////////////
209 void
211  std::string const & key,
212  Strings const & info,
213  bool append /* = true */
214 )
215 {
216  StringsMap::iterator finditer( info_map_.find( key ) );
217  if ( finditer == info_map_.end() || !append ) {
218  info_map_[ key ] = info;
219  } else {
220  Strings & existing_info( finditer->second );
221  for ( Strings::const_iterator it( info.begin() ), end( info.end() ); it != end; ++it ) {
222  existing_info.push_back( *it );
223  }
224  }
225 }
226 
227 bool
229 {
230  return info_map_.erase( key );
231 }
232 
233 ////////////////////////////////////////////////////////////////////////////////////////////////////
234 // private methods
235 
236 ///@brief compares identity, then internal degrees of freedom for between residues
237 bool
239  Residue const & res1,
240  Residue const & res2
241 ) const
242 {
243  // check aa
244  if ( res1.aa() != res2.aa() ) return true;
245  // check number of heavy atoms
246  if ( res1.nheavyatoms() != res2.nheavyatoms() ) return true;
247  // check sidechain torsions
248  vector1< Real > const & res1chi( res1.chi() ), res2chi( res2.chi() );
249  for ( vector1< Real >::const_iterator it1( res1chi.begin() ), it2( res2chi.begin() ),
250  end1( res1chi.end() ), end2( res2chi.end() ); it1 != end1 && it2 != end2; ++it1, ++it2 ) {
251  if ( std::abs( *it1 - *it2 ) > chi_diff_threshold_ ) {
252 // TR(t_info) << "Chis differ: " << F(6,3,*it1) << " " << F(6,3,*it2) << std::endl;
253  return true;
254  }
255  }
256  // check mainchain torsions
257  vector1< Real > const & res1mc( res1.mainchain_torsions() ), res2mc( res2.mainchain_torsions() );
258  for ( vector1< Real >::const_iterator it1( res1mc.begin() ), it2( res2mc.begin() ),
259  end1( res1mc.end() ), end2( res2mc.end() ); it1 != end1 && it2 != end2; ++it1, ++it2 ) {
260  if ( std::abs( *it1 - *it2 ) > mainchain_torsion_diff_threshold_ ) {
261 // TR(t_info) << "mainchain dihedrals differ: " << F(6,3,*it1) << " " << F(6,3,*it2) << std::endl;
262  return true;
263  }
264  }
265  // 'not different'
266  return false;
267 }
268 
269 ////////////////////////////////////////////////////////////////////////////////////////////////////
270 void
272 {
273  res_indices_to_output_.clear();
274  if ( !reference_pose_ || !option[ OptionKeys::dna::design::sparse_pdb_output ]() ) {
275  for ( Size i(1), end( pose_copy_->total_residue() ); i <= end; ++i ) {
276  res_indices_to_output_.push_back(i);
277  }
278  } else {
279  for ( Size i(1), endpose( pose_copy_->total_residue() ),
280  endref( reference_pose_->total_residue() ); i <= endpose; ++i ) {
281  if ( i <= endref &&
282  !residues_are_different( pose_copy_->residue(i), reference_pose_->residue(i) ) ) continue;
283  res_indices_to_output_.push_back(i);
284  }
285  }
286 }
287 
288 ////////////////////////////////////////////////////////////////////////////////////////////////////
291  PDBOutput::Strings const & list,
292  std::string sep = ", "
293 )
294 {
295  std::string os;
296  for ( std::list< std::string >::const_iterator it( list.begin() ), end( list.end() );
297  it != end; ++it ) os += sep + *it;
298  return os;
299 }
300 
301 ////////////////////////////////////////////////////////////////////////////////////////////////////
302 /// @begin output design tags
303 /// @details outputs a set of tags describing how residues varied
304 /// @author ashworth
305 void
307 {
308  pdbout << "REMARK Residues varied in this design:\n";
309 
310  std::list< std::string > moved, moved_DNA, mutated, mutated_DNA;
311 
312  for ( Size index(1), nres( pose_copy_->total_residue() ); index <= nres; ++index ) {
313  std::ostringstream os;
314  if ( pose_copy_->pdb_info() )
315  os << pose_copy_->pdb_info()->number( index ) << " "
316  << pose_copy_->pdb_info()->chain( index );
317  else os << index << " " << pose_copy_->chain( index );
318 
319  if ( reference_pose_ ) {
320  // compare to reference pose if it exists
321  // mutation
322  if ( pose_copy_->residue_type(index).aa() != reference_pose_->residue_type(index).aa() ) {
323  if ( pose_copy_->residue_type(index).is_DNA() ) mutated_DNA.push_back( os.str() );
324  else mutated.push_back( os.str() );
325  // no mutation, but structure changed
326  } else if ( residues_are_different( pose_copy_->residue(index),
327  reference_pose_->residue(index) ) ) {
328  if ( pose_copy_->residue_type(index).is_DNA() ) moved_DNA.push_back( os.str() );
329  else moved.push_back( os.str() );
330  }
331  }
332  }
333  pdbout << "REMARK MovedRes" << string_join( moved ) << '\n';
334  pdbout << "REMARK MovedDNA" << string_join( moved_DNA ) << '\n';
335  pdbout << "REMARK MutatedRes" << string_join( mutated ) << '\n';
336  pdbout << "REMARK MutatedDNA" << string_join( mutated_DNA ) << '\n';
337  pdbout << "REMARK\n";
338 }
339 
340 ////////////////////////////////////////////////////////////////////////////////////////////////////
341 void
343 {
344  // weights (non-zero)
345  EnergyMap const & weights( score_function_->weights() );
346  pdbout << "REMARK Non-zero ScoreFunction weights:\n";
347  for ( int i(1); i <= n_score_types; ++i ) {
348  if ( weights[ ScoreType(i) ] == 0 ) continue;
350  pdbout << "REMARK " << A(16,name) << " " << F( 6, 4, weights[ ScoreType(i) ] ) << '\n';
351  }
352  pdbout << "REMARK\n";
353  // end weights
354 
355  // per-residue energies
356  if ( option[ OptionKeys::score::output_residue_energies ]() ) {
357  pdbout << "REMARK Weighted per-residue energies\n";
358  // header
359  int const colwidth(10);
360  pdbout << "REMARK " << A( 4, "pdb" ) << A( 4, "type" ) << A( 2, "ch" );
361  for ( int i(1); i <= n_score_types; ++i ) {
362  if ( weights[ ScoreType(i) ] == 0 ) continue;
363  pdbout << A( colwidth, ScoreTypeManager::name_from_score_type( ScoreType(i) ) );
364  }
365  pdbout << '\n';
366  // end header
367  // residues
369  end( res_indices_to_output_.end() ); pos != end; ++pos ) {
370  pdbout << "REMARK ";
371  if ( pose_copy_->pdb_info() ) {
372  pdbout << I( 4, pose_copy_->pdb_info()->number(*pos) )
373  << A( 4, pose_copy_->residue(*pos).type().name3() )
374  << A( 2, pose_copy_->pdb_info()->chain(*pos) );
375  } else {
376  pdbout << I( 4, *pos ) << A( 4, pose_copy_->residue(*pos).type().name3() )
377  << A( 2, pose_copy_->chain(*pos) );
378  }
379  EnergyMap const & res_energies( pose_copy_->energies().residue_total_energies(*pos) );
380  for ( int st(1); st <= n_score_types; ++st ) {
381  if ( weights[ ScoreType(st) ] == 0 ) continue;
382  pdbout << F( colwidth, 3, res_energies[ ScoreType(st) ] * weights[ ScoreType(st) ] );
383  }
384  pdbout << '\n';
385  }
386  // end residues
387  pdbout << "REMARK\n";
388  // end per-residue energies
389  }
390 
391  pdbout << "REMARK Weighted total energies:\n";
392  EnergyMap const & total_energies( pose_copy_->energies().total_energies() );
393  for ( int i(1); i <= n_score_types; ++i ) {
394  Real const E( total_energies[ ScoreType(i) ] ),
395  W( weights[ ScoreType(i) ] );
396 // if ( E == 0 || W == 0 ) continue;
397  if ( W == 0 ) continue;
398  Real const weighted_E( E * W );
400  pdbout << "REMARK " << A(16,name) << " " << F( 8, 2, weighted_E ) << '\n';
401  }
402  // "special" ('weight' value for total_score defaults to zero?)
403  pdbout << "REMARK " << A(16,"total_score") << " " << F( 8,2,total_energies[total_score] ) << '\n';
404  pdbout << "REMARK \n";
405 }
406 
407 ////////////////////////////////////////////////////////////////////////////////////////////////////
408 /// @begin output_hbond_info
409 /// @brief Generates a table of hydrogen bond info and categorizes hbonds by type
410 /// @author ashworth
411 void
413 {
414  if ( ! option[ OptionKeys::run::output_hbond_info ]() ) return;
415 
416  using namespace scoring::hbonds;
417  HBondSet hbond_set;
418  // omission can result in failed 'graph_state_ == GOOD' assertion, but pose is const...
419 // pose.update_residue_neighbors();
420  // this yields UNWEIGHTED energies
421  fill_hbond_set( *pose_copy_, false, hbond_set );
422 
423  // first output column headers for file readability
424  pdbout << "REMARK Loc, res, pos, pdb, chain, atom, x, y, z, res, pos, pdb, chain, atom, x, y, z, hbE, HAdis, xD, xH\n";
425 
426  // total protein-BASE hydrogen-bonding energy
427  Real hb_spec( 0.0 );
428  std::string category("NONE");
429 
430  DnaInterfaceFinder interface;
432 
433  // expand list of residues to consider into nres bool vector for simple lookup
434  vector1< bool > relevant_residue( pose_copy_->total_residue(), false );
436  end( res_indices_to_output_.end() ); index != end; ++index ) {
437  relevant_residue[ *index ] = true;
438  }
439 
440  for ( Size i(1); i <= hbond_set.nhbonds(); ++i ) {
441 
442  HBond const & hb( hbond_set.hbond(i) );
443  Size const don_res_i( hb.don_res() ), acc_res_i( hb.acc_res() );
444  Residue const & don_rsd( pose_copy_->residue( don_res_i ) ),
445  acc_rsd( pose_copy_->residue( acc_res_i ) );
446  // skip NA-NA
447  if ( don_rsd.is_DNA() && acc_rsd.is_DNA() ) continue;
448  // skip (protein) backbone-backbone hbonds
449  if ( hb.don_hatm_is_protein_backbone() || hb.acc_atm_is_protein_backbone() ) continue;
450  // skip hbonds that don't involve a relevant residue
451  if ( !relevant_residue[ don_res_i ] && !relevant_residue[ acc_res_i ] ) continue;
452  // skip protein residues that are not close to DNA
453  if ( !don_rsd.is_DNA() && !interface.protein_neighbors().find( don_res_i )->second.close() )
454  continue;
455  if ( !acc_rsd.is_DNA() && !interface.protein_neighbors().find( acc_res_i )->second.close() )
456  continue;
457 
458  Size const donH_i( hb.don_hatm() ), acc_i( hb.acc_atm() );
459  std::string const don_hatm_name( don_rsd.atom_name( donH_i ) ),
460  acc_atm_name( acc_rsd.atom_name( acc_i ) );
461 
462  Real weight(0.0);
463  if ( acc_rsd.atom_is_backbone( acc_i ) || don_rsd.atom_is_backbone( donH_i ) ) {
464  category = "sc_bb";
465  weight = score_function_->weights()[ hbond_bb_sc ];
466  } else {
467  category = "sc_sc";
468  weight = score_function_->weights()[ hbond_sc ];
469  }
470  if ( acc_rsd.is_DNA() || don_rsd.is_DNA() ) {
471  category = ( category == "sc_bb" ? "dna_bb" : "dna_base" );
472  // dna water adduct
473  if ( acc_rsd.atom_type( acc_i ).name() == "HOH" ||
474  don_rsd.atom_type( donH_i ).name() == "HOH" ) {
475  category = "dna_wat";
476  weight = score_function_->weights()[ h2o_hbond ];
477  }
478  }
479  Real const weighted_E( hb.energy() * weight );
480  if ( category == "dna_base" || category == "dna_wat" ) hb_spec += weighted_E;
481 
482  // assume donH_i indexes a hydrogen whose only bonded neighbor is the donor heavy
483  Size const don_i( don_rsd.bonded_neighbor( donH_i ).front() );
484 
485  // output information
486  numeric::xyzVector< Real > const Hxyz( don_rsd.atom( donH_i ).xyz() ),
487  Dxyz( don_rsd.atom( don_i ).xyz() ),
488  Axyz( acc_rsd.atom( acc_i ).xyz() ),
489  Bxyz( acc_rsd.atom( acc_rsd.atom_base( acc_i ) ).xyz() ),
490  B2xyz( acc_rsd.atom( acc_rsd.abase2( acc_i ) ).xyz() );
491  // AHdis, xD, xH : Acc-Hyd dist, angle A-H-D, angle B-A-H. used in scoring
492  // copied from hbonds_geom::hb_energy_deriv
493  Vector const HD( Dxyz - Hxyz ), AH( Hxyz - Axyz );
494  Real const HDdis( HD.length() ), AHdis( AH.length() );
495  Real const inv_HDdis( 1.0f / HDdis ), inv_AHdis( 1.0f / AHdis );
496  Vector const HDunit( HD * inv_HDdis ), AHunit( AH * inv_AHdis );
497  Vector BAunit, PBxyz;
499  hbond_set.hbond_options(),
500  get_hbe_acc_hybrid(hb.eval_type()),
501  Axyz, Bxyz, B2xyz, PBxyz, BAunit );
502  Real const xD( dot( AHunit, HDunit ) ), xH( dot( BAunit, AHunit ) );
503 
504  int don_pdb(0), acc_pdb(0);
505  if ( pose_copy_->pdb_info() ) {
506  don_pdb = pose_copy_->pdb_info()->number( don_res_i );
507  acc_pdb = pose_copy_->pdb_info()->number( acc_res_i );
508  }
509  pdbout << "REMARK " << A(8,category) << " ";
510  pdbout << don_rsd.name3() << " " << I(4,don_res_i) << " " << I(4,don_pdb) << " ";
511  if ( pose_copy_->pdb_info() ) pdbout << pose_copy_->pdb_info()->chain( don_res_i );
512  else pdbout << pose_copy_->chain( don_res_i );
513  pdbout << " " << don_hatm_name << " "
514  // these coordinates are used by PyMOL plugins to create (and display) hydrogen bond cgo objects
515  << F(8,3,Hxyz[0]) << " " << F(8,3,Hxyz[1]) << " " << F(8,3,Hxyz[2]) << " ";
516  pdbout << acc_rsd.name3() << " " << I(4,acc_res_i) << " " << I(4,acc_pdb) << " ";
517  if ( pose_copy_->pdb_info() ) pdbout << pose_copy_->pdb_info()->chain( acc_res_i );
518  else pdbout << pose_copy_->chain( acc_res_i );
519  pdbout << " " << acc_atm_name << " "
520  // these coordinates are used by PyMOL plugins to create (and display) hydrogen bond cgo objects
521  << F(8,3,Axyz[0]) << " " << F(8,3,Axyz[1]) << " " << F(8,3,Axyz[2]) << " ";
522  pdbout << F(6,2, weighted_E ) << " " << AHdis << " " << xD << " " << xH << '\n';
523  }
524 // pdbout << "REMARK base-specific hbond energy: " << F(6,2,hb_spec) << '\n';
525 }
526 
527 ////////////////////////////////////////////////////////////////////////////////////////////////////
528 /// @begin output_buried_unsatisfied_hbonds
529 /// @details
530 /// @author ashworth
531 void
533 {
534  if ( !option[ OptionKeys::run::decoystats ]() ) return;
535 
536  using namespace protocols::toolbox::pose_metric_calculators;
537  using id::AtomID_Map;
538  BuriedUnsatisfiedPolarsCalculator bu_calc( "default", "default" );
539 
540  basic::MetricValue< AtomID_Map< bool > > map;
541  bu_calc.get( "atom_bur_unsat", map, *pose_copy_ );
543  end( res_indices_to_output_.end() ); pos != end; ++pos ) {
544  for( Size atomi(1); atomi <= map.value().n_atom(*pos); ++atomi ) {
545  if( map.value()(*pos,atomi) ) {
546  ResidueType const & rt( pose_copy_->residue_type(*pos) );
547  std::string name3( rt.name3() );
548  if ( rt.is_DNA() ) name3 = dna_full_name3( name3 );
549 
550  pdbout << "REMARK Unsat ";
551  if ( pose_copy_->pdb_info() ) {
552  pdbout << pose_copy_->pdb_info()->chain(*pos) << '.'
553  << pose_copy_->pdb_info()->number(*pos);
554  } else {
555  pdbout << pose_copy_->chain(*pos) << '.' << *pos;
556  }
557  pdbout << '.' << name3 << " " << rt.atom_name(atomi) << std::endl;
558  }
559  }
560  }
561 
562  // instantiation of BuriedUnsatisfiedPolarsCalculator looks up (or creates) instantiations of NumberHBondsCalculator and SasaCalculator -- output of info from these too while we're at it
563  // somehow pose has become aware of these calculators and will not recompute them(?)
564 
565 // basic::MetricValue< id::AtomID_Map< Size > > atom_hbonds;
566 // pose_copy_->metric( bu_calc.name_of_hbond_calc(), "atom_Hbonds", atom_hbonds );
567  basic::MetricValue< Size > nhbonds;
568  pose_copy_->metric( bu_calc.name_of_hbond_calc(), "all_Hbonds", nhbonds );
569 
570  TR(t_info) << "pdb nhbonds " << nhbonds.value() << std::endl;
571  pdbout << "REMARK nhbonds " << nhbonds.value() << '\n';
572 
573 // basic::MetricValue< id::AtomID_Map< Real > > atom_sasa;
574 // pose_copy_->metric( bu_calc.name_of_sasa_calc(), "atom_sasa", atom_sasa );
575  basic::MetricValue< Real > total_sasa;
576  pose_copy_->metric( bu_calc.name_of_sasa_calc(), "total_sasa", total_sasa );
577 
578  TR(t_info) << "pdb total_sasa " << total_sasa.value() << std::endl;
579  pdbout << "REMARK total_sasa " << total_sasa.value() << '\n';
580 
581  // packing statistical score
582  PackstatCalculator calc_packstat;
583  basic::MetricValue< Real > total_packstat;
584  calc_packstat.get( "total_packstat", total_packstat, *pose_copy_ );
585  TR(t_info) << "pdb total_packstat " << total_packstat.value() << std::endl;
586  pdbout << "REMARK total_packstat " << total_packstat.value() << '\n';
587 
588 }
589 
590 ////////////////////////////////////////////////////////////////////////////////////////////////////
591 void
592 make_subdirs( std::string const & name )
593 {
594  // for names that include directory paths: try to create any such directories that do not yet exist
595  typedef utility::vector1< std::string > StringVec;
596  StringVec const subdirs( string_split( name, '/' ) );
597  for ( StringVec::const_iterator dir( subdirs.begin() ); dir != subdirs.end()-1; ++dir ) {
598  if ( utility::file::file_exists( dir->c_str() ) ) continue;
599  utility::file::create_directory( dir->c_str() );
600  }
601 }
602 
603 } // namespace dna
604 } // namespace protocols