Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
sasa.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 // :noTabs=false:tabSize=4:indentSize=4:
4 //
5 // (c) Copyright Rosetta Commons Member Institutions.
6 // (c) This file is part of the Rosetta software suite and is made available under license.
7 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
8 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
9 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
10 
11 /// @file src/core/scoring/sasa.cc
12 /// @brief routines which calculate solvent accessible surface area
13 /// @author Jeff Gray
14 /// @author Ron Jacak (hydrophobic sasa method, comments, unit tests)
15 
19 #include <core/id/AtomID.hh>
20 #include <core/id/AtomID_Map.hh>
21 #include <basic/database/open.hh>
22 #include <core/pose/Pose.hh>
23 #include <core/pose/util.hh>
24 // AUTO-REMOVED #include <core/pose/PDBInfo.hh> // temp
25 #include <core/scoring/sasa.hh>
26 #include <core/types.hh>
27 #include <basic/Tracer.hh>
28 
29 // ObjexxFCL Headers
30 #include <ObjexxFCL/ubyte.hh>
31 #include <ObjexxFCL/FArray2D.hh>
32 #include <ObjexxFCL/Fmath.hh>
33 #include <ObjexxFCL/format.hh>
34 
35 // Numeric Headers
36 #include <numeric/constants.hh>
37 #include <numeric/trig.functions.hh>
38 
39 // Utility Headers
40 #include <utility/io/izstream.hh>
41 // AUTO-REMOVED #include <utility/string_util.hh> // temp
42 
43 // C++ Headers
44 #include <cmath>
45 #include <cstdlib>
46 #include <fstream>
47 #include <iostream>
48 
49 #include <utility/vector1.hh>
50 
51 //Auto Headers
52 #include <core/pose/util.tmpl.hh>
53 
54 //#define FILE_DEBUG 1
55 
56 //#ifdef FILE_DEBUG
57 static basic::Tracer TR("core.scoring.sasa");
58 //#endif
59 
60 using namespace ObjexxFCL::fmt;
61 
62 namespace core {
63 namespace scoring {
64 
65 
66 // this lookup table is used in sasa computation (also in void.cc)
67 // the first 8 ints represent the number of 1 bits you would find in the binary equivalents of the decimal numbers 0-7.
68 // e.g. 1 in binary is 0000 0001, so 1 '1'-bit
69 // 7 in binary is 0000 0111, so 3 '1'-bits
70 short const bit_count[] = { // lookup table for number of 1 bits in a ubyte
71  0,1,1,2,1,2,2,3, 1,2,2,3,2,3,3,4, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, // 0x 1x
72  1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, // 2x 3x
73  1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, // 4x 5x
74  2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, // 6x 7x
75  1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, // 8x 9x
76  2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, // Ax Bx
77  2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, // Cx Dx
78  3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8, // Ex Fx
79 };
80 
81 
82 int const num_bytes = 21;
83 int const num_phi = 64;
84 int const num_theta = 64;
85 int const num_overlaps = 100;
86 int const num_orientations = 162;
87 int const maskbits = 162;
88 
89 // This database file is used to find the number of the closest point to the point of intersection between two spheres.
90 // The surface of each atom is covered by 162 points. Very rarely is it the case that two spheres will intersect exactly
91 // on a point. This database table is used to map the actual point of intersection to the closest predetermined dot.
92 ObjexxFCL::FArray2D_int angles( num_phi, num_theta );
93 
94 // a 2D FArray of ubytes, that is num_bytes (21) by overlaps * orientations.
95 // Each line of this database file contains 21 ints ranging from 0-255. These values in binary represent the mask
96 // that should get applied to the state of an atoms current set of dots. So if the line is 21 0's that means that no
97 // bits in the atoms state should be changed. (No overlap occurs between these two atoms.) On the other hand, if the
98 // line is 21 255's, that means the atom is completely covered by the other atom.
99 ObjexxFCL::FArray2D_ubyte masks( num_bytes, num_overlaps * num_orientations );
100 
101 
102 ///
103 /// @begin sasa.cc::input_sasa_dats
104 ///
105 /// @brief
106 /// Reads in the SASA database files sampling/SASA-angles.dat and sampling/SASA-masks.dat into FArrays above.
107 ///
109 
110  // this booleans checks whether we've done this already or not. if we have, then return immediately.
111  static bool init = false;
112  if ( init )
113  return;
114  init = true;
115 
116  //j inputting the masks. they are 21 ubytes long, 162x100 (see header). expects file to be complete
117  utility::io::izstream masks_stream( basic::database::full_name("sampling/SASA-masks.dat" ) );
118 
119  //ronj iterate over all 16200 bit masks in the database file and save them to the FArray 'masks'
120  //ronj the lines have 21 space-delimited DECIMAL values which we need to convert to binary values that are 8-bits long
121  //ronj so save the int (or short) somewhere and then cast that value into an unsigned char
122 
123  for ( int ii=1; ii <= num_overlaps * num_orientations; ++ii ) {
124  short tmp; // a 'short' is a int type that is usually 16-bits
125  for ( int jj = 1; jj <= num_bytes; ++jj ) {
126  //ronj operator>> of izstream (aka the extraction operator) performs an input operation on a stream generally
127  //ronj involving some interpretation of the data
128  masks_stream >> tmp;
129  masks(jj,ii) = static_cast< unsigned char >( tmp );
130  }
131  masks_stream >> skip; // calls skip() on the izstream which skips the rest of the line and the line terminator
132  }
133  masks_stream.close();
134 
135  //ronj iterate over all 64 lines of the sampling/SASA-angles.database file and save them to the FArray 'angles'
136  //ronj these lines have 64 space-delimited ints which need to be stored
137  utility::io::izstream angles_stream( basic::database::full_name( "sampling/SASA-angles.dat" ) );
138 
139  for ( int ii = 1; ii <= num_phi; ++ii ) {
140  for ( int jj = 1; jj <= num_theta; ++jj ) {
141  angles_stream >> angles( ii, jj );
142  }
143  angles_stream >> skip;
144  }
145  angles_stream.close();
146 
147 }
148 
149 
150 ///
151 /// @begin sasa.cc::get_overlap
152 ///
153 /// @detailed
154 /// getting overlap from a to b (or i to j, as the atoms are referred to in calc_per_atom_sasa below).
155 /// this returns the degree of overlap between two atoms adapted from erics code in area.c GetD2 and returns value
156 /// from 1 to 100. This calculation is based on the law of cosines.
157 /// See LeGrand and Merz, Journal of Computational Chemistry 14(3):349-52 (1993).
158 /// Note that equation (4) is wrong, the denominator should be 2*ri*riq instead of 2*ri*rq (j)
159 ///
160 /// The function gets passed in the sasa radius of atom i (plus the probe radius), the sasa radius of atom j (plus
161 /// the probe radius), the distance between the atom centers, and a reference to the degree of overlap (represented
162 /// as an int). The degree of overlap that's returned can be thought of as how much of atom a is covered by atom b.
163 /// A value of 100 means that atom a is completely covered up by atom b. A value of 1 means that not much of the surface
164 /// of 'a' is covered up by 'b'.
165 /// The law of cosines relates the cosine of one angle of a triangle to the lengths of its sides. More specifically,
166 /// c^2 = a^2 + b^2 - 2*a*b*cos theta, where theta is the angle between sides a and b. For the function we want to
167 /// compute the angle of the cone of intersection between spheres 'a' and 'b'. Let the radius of atom a be ri, and the
168 /// radius of atom b be rq, and the distance between atom centers be riq. Let the angle between ri and riq be theta_iq.
169 /// The cosine of theta_iq will be equivalent to ( ri^2 + riq^2 - rq^2 ) / 2 * ri * riq
170 ///
171 void
172 get_overlap( Real const radius_a, Real const radius_b, Real const distance_ijxyz, int & degree_of_overlap ) {
173 
174  //j min distance cutoff
175  Real epsilon = 0.01;
176 
177  if ( distance_ijxyz < epsilon ) {
178  //j atoms too close, causes round off error. use this cutoff.
179  //ronj not sure what the rationale for using the cutoffs below is.
180  if ( radius_a < radius_b ) {
181  degree_of_overlap = 100;
182  } else {
183  degree_of_overlap = 1;
184  }
185 
186  } else if ( radius_b + distance_ijxyz <= radius_a ) {
187  //j If atom a completely engulfs atom b, consider a to have no overlap due to atom b.
188  degree_of_overlap = 1;
189 
190  } else if ( radius_a + distance_ijxyz <= radius_b ) {
191  //j If atom a is completely engulfed by atom b, then turn it completely off (i.e. d2 = 99).
192  degree_of_overlap = 100;
193 
194  } else {
195  //j Otherwise, compute the amount of overlap using the law of cosines. "costh" is the angle of the cone of
196  //j intersection that atom b imposes on atom a.
197  Real cosine_theta_iq = ( ( radius_a*radius_a ) + ( distance_ijxyz*distance_ijxyz ) - ( radius_b*radius_b ) ) / ( 2 * radius_a * distance_ijxyz );
198 
199  //ronj when 'b' and 'a' are barely overlapping, the theta angle will be small. as 'b' moves closer to 'a', the point of
200  //ronj intersection moves farther and farther away from 'b' causing the theta angle to become wider and wider.
201  //ronj the theta angle will vary from 0deg to 180deg. the cos of theta will vary from 1 to 0 to -1.
202  //ronj therefore, subtracting cos theta from 1 and multiplying by 50 will result in the degree_of_overlap varying
203  //ronj from 1 (when cos theta is 1, or angle is 0) to 100 (when cos theta is -1, or theta is 180deg).
204  degree_of_overlap = static_cast< int >( (1.0f - cosine_theta_iq) * 50 ) + 1;
205 
206  if ( degree_of_overlap > 100 ) {
207  degree_of_overlap = 100;
208 
209  } else if ( degree_of_overlap < 0 ) {
210  //j We already hopefully accounted for this possibility by requiring that distance_ijxyz > epsilon,
211  //j but in case not we don't want a potential bug to go unnoticed.
212  std::cout << "Problem in calculating overlap between atoms. Terminating calculation.\n";
213  std::cout << "radius_a: " << SS( radius_a ) << ", radius_b: " << SS( radius_b )
214  << ", distance_ijxyz: " << SS( distance_ijxyz ) << ", cosine theta: " << SS( cosine_theta_iq ) << std::endl;
215  utility::exit( EXIT_FAILURE, __FILE__, __LINE__);
216  }
217  }
218 }
219 
220 
221 ///
222 /// @begin sasa.cc::get_orientation
223 ///
224 /// @brief
225 /// Gets the orientation of a to b (i to j, see below). Does this by calculating two angles, aphi and theta. (j)
226 ///
227 /// @detailed
228 /// ronj This function is used to get two indexes (phi and theta) which are used to get the index of a dot on the
229 /// ronj surface of the 'a' sphere. When calculating how much surface area sphere b covers on a, we can get the degree
230 /// ronj of overlap from the function above, but it's not necessarily the case that the vector that connects the center
231 /// ronj of atom 'a' and atom 'b' goes through one of the predetermined dot locations on the surface of 'a'. In fact,
232 /// ronj it's very unlikely that the vector goes through a predetermined dot. Instead, what is done is the actual point
233 /// ronj of intersection (the outermost point of a on the line from the center of 'a' to center of 'b') is converted
234 /// ronj to spherical polar coordinates. Then, the values are used to find the location of the closest predetermined
235 /// ronj point on the surface of 'a' using a lookup table. So what this function needs to do is convert the
236 /// ronj cartesian coordinate of the actual point of intersection into polar coordinates.
237 /// ronj
238 /// ronj To get the spherical, polar coordinates of a cartesian point x,y,z, use these equations:
239 /// ronj r = sqrt( x^2 + y^2 + z^2 )
240 /// ronj theta = arccos( z / r )
241 /// ronj phi = arctan( y / x )
242 ///
243 /// ronj Then, once we have the true phi and theta, we need to translate this into an index (or offset) for the correct
244 /// ronj value in the database file. There are 64 phi angle bin and 64 theta bins in the database file sampling/SASA-angles.dat.
245 /// ronj We need to convert the phi and theta into indexes for this file by multiplying them by num_phi / 2*pi.
246 // ronj pi_2 is 2*pi
247 ///
248 /// ronj Note: I think phi and theta have been reversed in the function below. The code below uses the following:
249 /// ronj phi = arccos( z )
250 /// ronj theta = arctan( y / x )
251 ///
252 /// ronj After a couple of weeks trying to write tests for this function, I have been unsuccessful in figuring out why
253 /// ronj it's doing what it does. Despite using the wrong equations, it seems to work. Comparing the total residue
254 /// ronj SASA values calculated by calc_per_atom_sasa() below results in a correlation of 0.98 against what the program
255 /// ronj NACCESS finds for the same residues. This test was done on a small 110aa protein. I also looked at the per-atom
256 /// ronj total SASA and the correlation for all atoms (mini v. NACCESS) was approximately 0.94. I'm using exactly the same
257 /// ronj van der Waals radii for both programs so I feel like the correlations should be 1.0. Explanations for the
258 /// ronj differences can be 1) this method is doing something wrong in calculating the closest surface point, 2) this
259 /// ronj method is correct but the masks that are in the database are not aligned to the surface points correctly, 3) the
260 /// ronj differences are solely due to the different way that the two program calculate surface area.
261 ///
262 ///
263 void get_orientation( Vector const & a_xyz, Vector const & b_xyz, int & phi_index, int & theta_index, Real distance_ijxyz ) {
264 
265  using namespace numeric::constants::d;
266  using numeric::sin_cos_range;
267 
268  //static FArray1D_float diff( 3 ); //apl allocate this once only
269  //pb -- static can cause problems in multi-threading
270  Vector ab_diff( ( a_xyz - b_xyz ) / distance_ijxyz );
271 
272  //j now figure out polar values of phi and theta. Normalize the difference. first get the length of the vector.
273  //vector_normalize(ab_diff);
274 
275  //j figuring phi
276  //ronj sin_cos_range adjust the range of the passed in argument to a valid [-1,1]. acos(x) returns the arccos of x
277  //ronj expressed in radians. (arccos is the inverse operation of cos.) the return value is in the range [0,pi].
278  Real phi = std::acos( sin_cos_range( ab_diff(3) ) );
279 #ifdef FILE_DEBUG
280  Real temp_phi = phi;
281 #endif
282  //ronj if z = -1, then phi becomes arccos( -1 ) = pi
283  //ronj if z = -0.5, then phi becomes arccos( -0.5 ) = 2pi/3
284  //ronj if z = 0, then phi becomes arccos( 0 ) = pi/2
285  //ronj if z = 0.5, then phi becomes arccos( 0.5 ) = pi/3
286  //ronj if z = 1, then phi becomes arccos( 1 ) = 0
287 
288  //ronj phi ranges from [0,pi], so...
289  //ronj if phi = 0, then p becomes 0 * ( num_phi / 2*pi ) = 0
290  //ronj if phi = pi/2, then p becomes pi/2 * ( num_phi / 2*pi ) = 64 / 4 = 16
291  //ronj if phi = pi, then p becomes pi * ( num_phi / 2*pi ) = num_phi / 2 = 32
292  phi = phi * ( num_phi / pi_2 );
293 #ifdef FILE_DEBUG
294  Real precasted_phi = phi;
295 #endif
296  phi_index = static_cast< int >( phi );
297  ++phi_index; // for fortran goes from 1 to n
298  if ( phi_index > num_phi )
299  phi_index = 1; // reset back to index of 1?
300 
301  //j figuring theta
302  //ronj atan2 is the arc tangent of y/x, expressed in radians.
303  //ronj atan2 ranges from [-pi,pi], so...
304  //ronj if atan2 = -pi, then t becomes -pi * ( num_theta / 2*pi ) = -64 / 2 = -32 += 64 = 32
305  //ronj if atan2 = -pi/2, then t becomes -pi/2 * ( num_theta / 2*pi ) = -64 / 4 = -16 += 64 = 48
306  //ronj if atan2 = 0, then t becomes 0 * ( num_theta / 2*pi ) = 0
307  //ronj if atan2 = pi/2, then t becomes pi/2 * ( num_theta / 2*pi ) = 64 / 4 = 16
308  //ronj if atan2 = pi, then t becomes pi * ( num_theta / 2*pi ) = 64 / 2 = 32
309  Real theta = std::atan2( ab_diff(2), ab_diff(1) );
310 #ifdef FILE_DEBUG
311  Real temp_theta = theta;
312 #endif
313 
314  theta = theta * ( num_theta / pi_2 );
315 #ifdef FILE_DEBUG
316  Real precasted_theta = theta;
317 #endif
318  theta_index = static_cast< int >( theta );
319  ++theta_index; // for fortran goes from 1 to n
320  if ( theta_index < 1 ) { // as in the case when atan2 returns a negative...
321  theta_index += num_theta; // let t = -32; -32 + 64 = 32
322  } else if ( theta_index > num_theta ) {
323  theta_index = 1; // reset back to index of 1?
324  }
325 
326 #ifdef FILE_DEBUG
327  /*TR << "get_orientation(): ";
328  TR << "a: " << F(5,2,a_xyz.x()) << "," << F(5,2,a_xyz.y()) << "," << F(5,2,a_xyz.z())
329  << ", b: " << F(5,2,b_xyz.x()) << "," << F(5,2,b_xyz.y()) << "," << F(5,2,b_xyz.z())
330  << ", a-b: " << F(6,3,ab_diff.x()) << "," << F(6,3,ab_diff.y()) << "," << F(6,3,ab_diff.z())
331  << ", distance_ijxyz: " << F( 5,3,distance_ijxyz )
332  << ", phi: " << temp_phi << ", unrounded_phi: " << precasted_phi << ", phi_index: " << SS( phi_index )
333  << ", theta: " << temp_theta << ", unrounded_theta: " << precasted_theta << ", theta_index: " << SS( theta_index )
334  << std::endl;
335  TR << "get_orientation(): closest dot: " << angles( phi_index, theta_index ) << std::endl;*/
336 #endif
337 }
338 
339 
340 ///
341 /// @begin sasa.cc::get_2way_orientation
342 ///
343 /// @brief
344 /// Gets the orientation of a to b (i to j, see below). Does this by calculating two angles, aphi and theta. (j)
345 ///
346 /// @detailed
347 /// ronj This function is the same as the function above but get the orientation of a to b simultaneously with the
348 /// ronj orientation of b to a. The same result could be achieved by making two separate get_2way_orientation() calls
349 /// ronj but this method does it more efficiently by avoiding an atan2 and acos call. Instead, once you compute the
350 /// ronj phi and theta for a on b, you can add/subtrate pi factors to get the phi and theta for b on a.
351 /// ronj Still not sure how this method returns the correct values, though.
352 ///
353 void
354 get_2way_orientation( Vector const & a_xyz, Vector const & b_xyz, int & phi_a2b_index, int & theta_a2b_index, int & phi_b2a_index, int & theta_b2a_index, Real distance_ijxyz ) {
355 
356  using namespace numeric::constants::d;
357  using numeric::sin_cos_range;
358 
359  //static FArray1D_float diff( 3 ); //apl allocate this once only
360  //pb -- static can cause problems in multi-threading
361  Vector ab_diff( ( a_xyz - b_xyz ) / distance_ijxyz );
362 
363  //j now figure out polar values of phi and theta. Normalize the difference. first get the length of the vector.
364  //vector_normalize(ab_diff);
365 
366  //j figuring phi, for a2b
367  //ronj sin_cos_range adjust the range of the passed in argument to a valid [-1,1]. acos(x) returns the arccos of x
368  //ronj expressed in radians. (arccos is the inverse operation of cos.) the return value is in the range [0,pi].
369  Real phi_a2b = std::acos( sin_cos_range( ab_diff(3) ) );
370 #ifdef FILE_DEBUG
371  Real temp_phi_a2b = phi_a2b;
372 #endif
373  //ronj if z = -1, then phi_a2b becomes arccos( -1 ) = pi
374  //ronj if z = -0.5, then phi_a2b becomes arccos( -0.5 ) = 2pi/3
375  //ronj if z = 0, then phi_a2b becomes arccos( 0 ) = pi/2
376  //ronj if z = 0.5, then phi_a2b becomes arccos( 0.5 ) = pi/3
377  //ronj if z = 1, then phi_a2b becomes arccos( 1 ) = 0
378 
379  //ronj phi ranges from [0,pi], so...
380  //ronj if phi_a2b = 0, then p becomes 0 * ( num_phi / 2*pi ) = 0
381  //ronj if phi_a2b = pi/2, then p becomes pi/2 * ( num_phi / 2*pi ) = 64 / 4 = 16
382  //ronj if phi_a2b = pi, then p becomes pi * ( num_phi / 2*pi ) = num_phi / 2 = 32
383  phi_a2b = phi_a2b * ( num_phi / pi_2 );
384 #ifdef FILE_DEBUG
385  Real precasted_phi_a2b = phi_a2b;
386 #endif
387  phi_a2b_index = static_cast< int >( phi_a2b );
388  ++phi_a2b_index; // for fortran goes from 1 to n
389  if ( phi_a2b_index > num_phi )
390  phi_a2b_index = 1; // reset back to index of 1?
391 
392  // can take a shortcut to get phi_b2a
393  Real phi_b2a = num_phi / 2 - phi_a2b;
394 #ifdef FILE_DEBUG
395  Real temp_phi_b2a = phi_b2a;
396 #endif
397  phi_b2a_index = static_cast< int >( phi_b2a );
398  ++phi_b2a_index; // for fortran goes from 1 to n
399  if ( phi_b2a_index > num_phi )
400  phi_b2a_index = 1;
401 
402 
403  //j figuring theta
404  //ronj atan2 is the arc tangent of y/x, expressed in radians.
405  //ronj atan2 ranges from [-pi,pi], so...
406  //ronj if atan2 = -pi, then t becomes -pi * ( num_theta / 2*pi ) = -64 / 2 = -32 += 64 = 32
407  //ronj if atan2 = -pi/2, then t becomes -pi/2 * ( num_theta / 2*pi ) = -64 / 4 = -16 += 64 = 48
408  //ronj if atan2 = 0, then t becomes 0 * ( num_theta / 2*pi ) = 0
409  //ronj if atan2 = pi/2, then t becomes pi/2 * ( num_theta / 2*pi ) = 64 / 4 = 16
410  //ronj if atan2 = pi, then t becomes pi * ( num_theta / 2*pi ) = 64 / 2 = 32
411  Real theta_a2b = std::atan2( ab_diff(2), ab_diff(1) );
412 #ifdef FILE_DEBUG
413  Real temp_theta_a2b = theta_a2b;
414 #endif
415 
416  theta_a2b = theta_a2b * ( num_theta / pi_2 );
417 #ifdef FILE_DEBUG
418  Real precasted_theta_a2b = theta_a2b;
419 #endif
420  theta_a2b_index = static_cast< int >( theta_a2b );
421  ++theta_a2b_index; // for fortran goes from 1 to n
422  if ( theta_a2b_index < 1 ) { // as in the case when atan2 returns a negative...
423  theta_a2b_index += num_theta; // let t = -32; -32 + 64 = 32
424  } else if ( theta_a2b_index > num_theta ) {
425  theta_a2b_index = 1; // reset back to index of 1?
426  }
427 
428  // can use a shortcut to get theta_b2a
429  Real theta_b2a = num_theta / 2.0f + theta_a2b;
430  if ( theta_b2a > num_theta / 2.0f )
431  theta_b2a -= num_theta;
432 #ifdef FILE_DEBUG
433  Real temp_theta_b2a = theta_b2a;
434 #endif
435 
436  theta_b2a_index = static_cast< int >( theta_b2a );
437  ++theta_b2a_index; // for fortran goes from 1 to n
438  if ( theta_b2a_index < 1 ) {
439  theta_b2a_index += num_theta;
440  } else if ( theta_b2a_index > num_theta ) {
441  theta_b2a_index = 1;
442  }
443 
444 
445 #ifdef FILE_DEBUG
446  /*TR << "get_2way_orientation(): ";
447  TR << "a: " << F(5,2,a_xyz.x()) << "," << F(5,2,a_xyz.y()) << "," << F(5,2,a_xyz.z())
448  << ", b: " << F(5,2,b_xyz.x()) << "," << F(5,2,b_xyz.y()) << "," << F(5,2,b_xyz.z())
449  << ", a-b: " << F(6,3,ab_diff.x()) << "," << F(6,3,ab_diff.y()) << "," << F(6,3,ab_diff.z())
450  << ", distance_ijxyz: " << F( 5,3,distance_ijxyz )
451  << ", phi_a2b: " << temp_phi_a2b << ", unrounded_phi_a2b: " << precasted_phi_a2b << ", phi_a2b_index: " << SS( phi_a2b_index )
452  << ", theta_a2b: " << temp_theta_a2b << ", unrounded_theta_a2b: " << precasted_theta_a2b << ", theta_a2b_index: " << SS( theta_a2b_index )
453  << ", phi_b2a: " << temp_phi_b2a << ", phi_b2a_index: " << SS( phi_b2a_index )
454  << ", theta_b2a: " << temp_theta_b2a << ", theta_b2a_index: " << SS( theta_b2a_index )
455  << std::endl;*/
456 #endif
457 
458 }
459 
460 
461 Real
462 calc_total_sasa( pose::Pose const & pose, Real const probe_radius ) {
463 
464  id::AtomID_Map< Real > atom_sasa;
465  utility::vector1< Real > rsd_sasa;
466 
467  return calc_per_atom_sasa( pose, atom_sasa, rsd_sasa, probe_radius );
468 }
469 
470 
471 Real
473  Real const probe_radius, bool const use_big_polar_H /* = false */ ) {
474 
475  id::AtomID_Map< bool > atom_subset;
476  atom_subset.clear();
477  core::pose::initialize_atomid_map( atom_subset, pose, true ); // jk use all atoms if atom_subset is not specified
478 
479  return calc_per_atom_sasa( pose, atom_sasa, rsd_sasa, probe_radius, use_big_polar_H, atom_subset );
480 }
481 
482 
483 Real
485  pose::Pose const & pose,
486  id::AtomID_Map< Real > & atom_sasa,
487  utility::vector1< Real > & rsd_sasa,
488  Real const probe_radius,
489  bool const use_big_polar_H,
490  id::AtomID_Map< bool > & atom_subset,
491  bool const use_naccess_sasa_radii /* =false */,
492  bool const expand_polar_radii /* =false */,
493  Real const polar_expansion_radius /* =1.0 */,
494  bool const include_probe_radius_in_atom_radii, /* = true; used in calc of final sasas */
495  bool const use_lj_radii /* = false */
496  ) {
497 
500  using core::id::AtomID;
501 
502  if ( pose.total_residue() < 1 )
503  return 0.0; // nothing to do
504 
505  // read sasa datafiles
506  input_sasa_dats();
507 
508  Real const big_polar_H_radius( 1.08 ); // increase radius of polar hydrogens, eg when doing unsatisfied donorH check
509 
510  //j setup the radii array, indexed by the atom type int. atom index for looking up an extra data type stored in the AtomTypes
511  //ronj reads the values out of the database file sasa_radii.txt in the extras folder of atom_type_sets and stores the values
512  //ronj for each atom type into the radii array. each index of the radii array corresponds to some atom type.
513  core::chemical::AtomTypeSet const & atom_type_set = pose.residue(1).atom_type_set();
514 
515  core::Size SASA_RADIUS_INDEX;
516  if ( use_naccess_sasa_radii ) {
517  SASA_RADIUS_INDEX = atom_type_set.extra_parameter_index( "NACCESS_SASA_RADIUS" );
518  } else {
519  SASA_RADIUS_INDEX = atom_type_set.extra_parameter_index( "SASA_RADIUS" );
520  }
521 
522  utility::vector1< Real > radii( atom_type_set.n_atomtypes() );
523 
524  for ( core::Size ii=1; ii <= radii.size(); ++ii ) {
525  core::chemical::AtomType const & at( atom_type_set[ii] );
526 
527  if ( use_lj_radii ) {
528  radii[ii] = atom_type_set[ii].lj_radius();
529 // std::cout << "Using LJ radius "<< radii[ii] << " instead of " << atom_type_set[ii].extra_parameter( SASA_RADIUS_INDEX ) <<
530 // " for " << atom_type_set[ii].name() << std::endl;
531  continue; // note continue, no other checks applied
532  }
533 
534  radii[ii] = atom_type_set[ii].extra_parameter( SASA_RADIUS_INDEX );
535 
536  if ( use_big_polar_H && at.is_polar_hydrogen() && big_polar_H_radius > radii[ii] ) {
537  std::cout << "Using " << big_polar_H_radius << " instead of " << radii[ii] << " for atomtype " << at.name() << " in sasa calculation!\n";
538  radii[ii] = big_polar_H_radius;
539  }
540 
541  if ( expand_polar_radii && ( at.element() == "N" || at.element() == "O" ) ) {
542 #ifdef FILE_DEBUG
543  std::cout << "Using " << radii[ii] + polar_expansion_radius << " instead of " << radii[ii] << " for atomtype " << at.name() << " in sasa calculation!\n";
544 #endif
545  radii[ii] = radii[ii] + polar_expansion_radius;
546  }
547 
548  }
549 
550  // create an AtomID_Map which will map atom ids to the mask for that atom. right now, just make all atom ids point
551  // to a mask of all zeros. later we'll have to set different masks.
553  utility::vector1< ObjexxFCL::ubyte > zero_mask( num_bytes, ObjexxFCL::ubyte( 0 ) );
554  core::pose::initialize_atomid_map( atom_masks, pose, zero_mask ); // calls AtomID_Map.Pose.hh::initialize()
555 
556 
557  // identify the maxium radius we have for all atoms in the pose. this will be used to set a cutoff distance for use
558  // in skipping residue pairs if their nbr atoms are too far apart.
559  core::Real max_radius = 0.0;
560  for ( Size ii=1; ii <= pose.total_residue(); ++ii ) {
561  Residue const & rsd( pose.residue( ii ) );
562  // iterate over all the atoms in residue ii and
563  for ( Size jj=1; jj <= rsd.natoms(); ++jj ) {
564  max_radius = std::max( max_radius, radii[ rsd.atom(jj).type() ] );
565  //ronj assert that the sum of the first 8 bits and the last 8 bits (or first byte and last byte) in the vector
566  //ronj of masks for this atom id is less than 0.001. what is the point of this assertion?
567  runtime_assert( std::abs( atom_masks[ AtomID(jj,ii) ][1] + atom_masks[ AtomID(jj,ii) ][num_bytes] ) < 1e-3 );
568  }
569  }
570 
571  core::Real cutoff_distance = 0.0;
572  cutoff_distance = 2 * ( max_radius + probe_radius );
573 
574 
575  //j now do calculations: get the atom_masks by looping over all_atoms x all_atoms
576  for ( Size ii=1; ii <= pose.total_residue(); ++ii ) {
577  Residue const & irsd( pose.residue( ii ) );
578 
579  //ronj for the other 'j' residue, only iterate over residues which have indexes > residue 'i'
580  for ( Size jj=ii; jj <= pose.total_residue(); ++jj ) {
581  Residue const & jrsd( pose.residue( jj ) );
582 
583 
585  irsd, jrsd,
586  probe_radius, cutoff_distance, radii,
587  atom_subset,
588  atom_masks );
589 
590  } // iia
591  } // ir
592 
593  //j calculate the residue and atom sasa
594  Real total_sasa( 0.0 );
595 
596  rsd_sasa.clear();
597  rsd_sasa.resize( pose.total_residue(), 0.0 );
598 
599  atom_sasa.clear();
600  core::pose::initialize_atomid_map( atom_sasa, pose, (Real) -1.0 ); // jk initialize to -1 for "not computed"
601 
602  Real const four_pi = 4.0f * Real( numeric::constants::d::pi );
603 
604  int num_ones = 0;
605  for ( Size ii=1; ii <= pose.total_residue(); ++ii ) {
606  Residue const & rsd( pose.residue(ii) );
607  rsd_sasa[ ii ] = 0.0;
608 
609 #ifdef FILE_DEBUG
610  TR << "sasa dots: res: " << rsd.name3() << pose.pdb_info()->number(ii) << std::endl;
611 #endif
612  for ( Size iia = 1; iia <= rsd.natoms(); ++iia ) {
613 
614  AtomID const id( iia, ii );
615  if ( ! atom_subset[ id ] )
616  continue; // jk skip this atom if not part of the subset
617 
618  Real iia_rad = radii[ rsd.atom(iia).type() ];
619  if ( include_probe_radius_in_atom_radii ) iia_rad += probe_radius; // this is the default, not used for sasapack
620 
621  //j to get SASA:
622  //j - count the number of 1's
623  //j - figure fraction that they are
624  //j - multiply by 4*pi*r_sqared
625 
626  int ctr = 0;
627  utility::vector1< ObjexxFCL::ubyte > const & iia_masks( atom_masks[ id ] );
628  for ( int bb = 1; bb <= num_bytes; ++bb ) {
629  ctr += bit_count[ iia_masks[bb] ]; // atm_masks(bb,iia,ii)
630  }
631  num_ones += ctr;
632 
633  Real const fraction_ones = static_cast< Real >( ctr ) / maskbits; //ronj this is equivalent to l(buried)/l(total)
634  Real const total_sa = four_pi * ( iia_rad * iia_rad ); //ronj total atom surface area
635  Real const area_exposed = ( 1.0f - fraction_ones ) * total_sa; //ronj ones must indicate buried area if we're subtracting from 1.0
636 
637 #ifdef FILE_DEBUG
638  std::cout << "atom: " << rsd.atom_name( iia ) << ", rad: " << ObjexxFCL::fmt::F(4,2,radii[ rsd.atom(iia).type() ])
639  << ", covered: " << ObjexxFCL::fmt::I(3,ctr) << ", counts: "; // use std::cout NOT the TR
640  print_dot_bit_string( atom_masks[ id ] );
641 #endif
642  atom_sasa[ id ] = area_exposed;
643  // jk Water SASA doesn't count toward the residue's SASA
644  if ( ! rsd.atom_type(iia).is_h2o() ) {
645  rsd_sasa[ ii ] += area_exposed;
646  total_sasa += area_exposed;
647  }
648 
649  } // iia
650 #ifdef FILE_DEBUG
651  std::cout << "num_ones: " << num_ones << ", sasa: " << rsd_sasa[ ii ] << std::endl; // use std::cout NOT the TR
652 #endif
653  num_ones = 0;
654 
655  } // ii
656 
657 #ifdef FILE_DEBUG
658  /*std::cout << std::endl;
659  for ( core::Size ii=1; ii <= pose.total_residue(); ++ii ) {
660  conformation::Residue const & rsd = pose.residue( ii );
661  TR << "residue " << rsd.name3() << pose.pdb_info()->number(ii) << " atom_sasas: [ ";
662  for ( Size at=1; at <= rsd.natoms(); ++at ) {
663  core::id::AtomID atid( at, ii );
664  TR << utility::trim( rsd.atom_name( at ) ) << ":" << atom_sasa[ atid ] << ", ";
665  }
666  TR << "], total SASA: " << rsd_sasa[ ii ] << std::endl;
667  }*/
668 #endif
669 
670  return total_sasa;
671 }
672 
673 
674 void
676  core::conformation::Residue const & irsd,
677  core::conformation::Residue const & jrsd,
678  Real const probe_radius,
679  Real const cutoff_distance,
680  utility::vector1< Real > const & radii,
681  id::AtomID_Map< bool > const & atom_subset,
683 ){
684 
685  using core::id::AtomID;
687 
688 
689  Size const ii = irsd.seqpos();
690  Size const jj = jrsd.seqpos();
691 
692  // use distance rather than distance_squared since the nbr_radii might be negative
693  //ronj what residue would have a negative nbr radius?
694  //ronj looks like GB_placeholder, SUCK, and VIRT residue types can have negative nbr radii
695  Real distance_ij_nbratoms = irsd.atom( irsd.nbr_atom() ).xyz().distance( jrsd.atom( jrsd.nbr_atom() ).xyz() );
696  if ( distance_ij_nbratoms > ( irsd.nbr_radius() + jrsd.nbr_radius() + cutoff_distance ) )
697  return;
698 
699  for ( Size iia=1; iia <= irsd.natoms(); ++iia ) {
700 
701  //ronj check to see if this atom is in the subset of atoms we're considering. if not, continue to the next one.
702  //ronj have to translate the index 'iia' into an AtomID to check if we're in the subset.
703  AtomID const iia_atomid( iia, ii );
704  if ( ! atom_subset[ iia_atomid ] )
705  continue; // jk skip this atom if not part of the subset
706 
707  //ronj convert the atom index into an Atom 'iia_atom' to make getting the xyz() and type() easier
708  Atom const & iia_atom( irsd.atom( iia ) );
709  Vector const & iia_atom_xyz = iia_atom.xyz();
710  Real const iia_atom_radius = radii[ iia_atom.type() ] + probe_radius;
711 
712  for ( Size jja = 1; jja <= jrsd.natoms(); ++jja ) {
713 
714  //ronj for all atoms in residue 'j', check to make sure that atom is in the subset of atoms we're considering
715  AtomID const jji_atomid( jja, jj );
716  if ( ! atom_subset[ jji_atomid ] )
717  continue; // jk skip this atom if not part of the subset
718 
719  Atom const & jja_atom( jrsd.atom( jja ) );
720  Vector const & jja_atom_xyz = jja_atom.xyz();
721  Real const jja_atom_radius = radii[ jja_atom.type() ] + probe_radius;
722 
723  Real const distance_ijxyz( iia_atom_xyz.distance( jja_atom_xyz ) ); // could be faster w/o sqrt, using Jeff Gray's rsq_min stuff
724  if ( distance_ijxyz <= iia_atom_radius + jja_atom_radius ) {
725 
726  if ( distance_ijxyz <= 0.0 )
727  continue;
728 
729  // account for atom j overlapping atom i:
730  // jk Note: compute the water SASA, but DON'T allow the water to contribute to the burial of non-water atoms
731  int degree_of_overlap, aphi, theta, point, masknum;
732 
733  if ( ! jrsd.atom_type( jja ).is_h2o() ) {
734  get_overlap( iia_atom_radius, jja_atom_radius, distance_ijxyz, degree_of_overlap );
735  #ifdef FILE_DEBUG
736  //TR << "calculated degree of overlap: " << degree_of_overlap << std::endl;
737  //TR << "calculating orientation of " << jrsd.name3() << jj << " atom " << jrsd.atom_name( jja ) << " on "
738  // << irsd.name3() << ii << " atom " << irsd.atom_name ( iia ) << std::endl;
739  #endif
740 
741  get_orientation( iia_atom_xyz, jja_atom_xyz, aphi, theta, distance_ijxyz );
742  point = angles( aphi, theta );
743  masknum = point * 100 + degree_of_overlap;
744  #ifdef FILE_DEBUG
745  //TR << "calculated masknum " << masknum << std::endl;
746  #endif
747 
748  //ronj overlap bit values for all atoms should have been init'd to zero before the main for loops
749  utility::vector1< ObjexxFCL::ubyte > & iia_bit_values = atom_masks[ AtomID( iia, ii ) ];
750 
751  // iterate bb over all 21 bytes or 168 bits (of which we care about 162)
752  // bitwise_or the atoms current values with the values from the database/masks array
753  #ifdef FILE_DEBUG
754  //TR << "starting bit values for atom " << irsd.name3() << ii << "-" << irsd.atom_name( iia ) << ": ";
755  //print_dot_bit_string( iia_bit_values );
756  //TR << "mask bit values for atom " << irsd.name3() << ii << "-" << irsd.atom_name( iia ) << ": ";
757  #endif
758  for ( int bb = 1, m = masks.index( bb, masknum ); bb <= num_bytes; ++bb, ++m ) {
759  iia_bit_values[ bb ] = ObjexxFCL::bit::bit_or( iia_bit_values[ bb ], masks[ m ] );
760 
761  #ifdef FILE_DEBUG
762  //int bit;
763  //TR << (bb-1) * 8 << ":";
764  //for ( int index=7; index >= 0; index-- ) {
765  // bit = ( ( (int)masks[m] >> index ) & 1 );
766  // TR << bit;
767  //}
768  //TR << " ";
769  #endif
770 
771  }
772  #ifdef FILE_DEBUG
773  //TR << std::endl;
774  //TR << "overlap bit values for atom " << irsd.name3() << ii << "-" << irsd.atom_name( iia ) << ": ";
775  //print_dot_bit_string( iia_bit_values );
776  #endif
777  }
778 
779  // account for i overlapping j:
780  // jk Note: compute the water SASA, but DON'T allow the water to contribute to the burial of non-water atoms
781  // ronj I don't think this is necessary since we'll eventually perform this calculation when we start
782  // ronj iterating over the j atoms
783  if ( !irsd.atom_type(iia).is_h2o() ) {
784  get_overlap( jja_atom_radius, iia_atom_radius, distance_ijxyz, degree_of_overlap );
785  #ifdef FILE_DEBUG
786  //TR << "calculated degree of overlap: " << degree_of_overlap << std::endl;
787  //TR << "calculating orientation of " << irsd.name3() << ii << " atom " << irsd.atom_name( iia ) << " on "
788  // << jrsd.name3() << jj << " atom " << jrsd.atom_name ( jja ) << std::endl;
789  #endif
790 
791  get_orientation( jja_atom_xyz, iia_atom_xyz, aphi, theta, distance_ijxyz );
792  point = angles( aphi, theta );
793  masknum = point * 100 + degree_of_overlap;
794  #ifdef FILE_DEBUG
795  //TR << "calculated masknum " << masknum << std::endl;
796  #endif
797 
798  utility::vector1< ObjexxFCL::ubyte > & jja_bit_values( atom_masks[ AtomID( jja, jj ) ] );
799 
800  // iterate bb over all 21 bytes or 168 bits (of which we care about 162)
801  // bitwise_or the atoms current values with the values from the database/masks array
802  #ifdef FILE_DEBUG
803  //TR << "mask bit values for atom " << jrsd.name3() << jj << "-" << jrsd.atom_name( jja ) << ": ";
804  #endif
805  for ( int bb = 1, m = masks.index(bb,masknum); bb <= num_bytes; ++bb, ++m ) {
806  jja_bit_values[ bb ] = ObjexxFCL::bit::bit_or( jja_bit_values[ bb ], masks[ m ] );
807 
808  #ifdef FILE_DEBUG
809  //int bit;
810  //TR << (bb-1) * 8 << ":";
811  //for ( int index=7; index >= 0; index-- ) {
812  // bit = ( ( (int)masks[m] >> index ) & 1 );
813  // TR << bit;
814  //}
815  //TR << " ";
816  #endif
817 
818  }
819  #ifdef FILE_DEBUG
820  //TR << std::endl;
821  //TR << "final bit values for atom " << jrsd.name3() << jj << "-" << jrsd.atom_name( jja ) << ": ";
822  //print_dot_bit_string( jja_bit_values );
823  #endif
824  }
825 
826  } // distance_ijxyz <= iia_atom_radius + jja_atom_radius
827  #ifdef FILE_DEBUG
828  //TR << "------" << std::endl;
829  #endif
830  } // jja
831  } // jr
832 }
833 
834 
835 ///
836 /// @begin get_angles
837 ///
838 /// @brief
839 /// Returns the number of bytes the overlap arrays use for tracking SASA.
840 /// Adding this in so that the values in the SASA database files can be used in SASA-based scores. (ronj)
841 ///
842 int get_num_bytes() { return num_bytes; }
843 
844 ///
845 /// @begin get_angles
846 ///
847 /// @brief
848 /// Returns const access to the angles FArray, which contains the information in the SASA database file sampling/SASA-angles.dat.
849 /// Adding this in so that the values in the SASA database files can be used in SASA-based scores. (ronj)
850 ///
851 ObjexxFCL::FArray2D_int const & get_angles() {
852  input_sasa_dats(); // read sasa datafiles; will immediately return if already done
853  return angles;
854 }
855 
856 ///
857 /// @begin get_masks
858 ///
859 /// @brief
860 /// Returns const access to the masks FArray, which contains the information in the SASA database file sampling/SASA-masks.dat.
861 /// Adding this in so that the values in the SASA database files can be used in SASA-based scores. (ronj)
862 ///
863 ObjexxFCL::FArray2D_ubyte const & get_masks() {
864  input_sasa_dats(); // read sasa datafiles; will immediately return if already done
865  return masks;
866 }
867 
868 ///
869 /// @begin sasa.cc::calc_per_atom_hydrophobic_sasa
870 ///
871 /// @brief
872 /// Uses the method above to calculate total SASA and then only looks at the hydrophobic contribution. Returns the total
873 /// hydrophobic SASA for the passed in pose. This method is being used for a protein surface score being developed by ronj.
874 /// Note: Uses an atom id mask that ignores H's in the pose - only sees and computes the SASA for heavy atoms in the pose.
875 /// This is done to keep things fast. Only computes the amount of hSASA per residue, not per atom. Doesn't make sense to
876 /// calculate a per-atom hSASA. (ronj)
877 ///
878 Real
880  utility::vector1< Real > & rsd_sasa, utility::vector1< Real > & rsd_hydrophobic_sasa, Real const probe_radius,
881  bool use_naccess_sasa_radii ) {
882 
883  // an atomID map is needed for the calc_per_atom_sasa method; it stores the actual calculated sasa for every atom
885  core::pose::initialize_atomid_map( atom_sasa, pose, (core::Real)0.0 ); // initialize to 0.0 for "not computed"
886 
887  // clear and init the passed in vector of residue sasa
888  rsd_sasa.clear();
889  rsd_sasa.resize( pose.total_residue(), 0.0 );
890 
891  // create an atom_subset mask such that only the heavy atoms will have their sasa computed (ignore H's to make it faster)
892  id::AtomID_Map< bool > atom_subset;
893 
894  //ronj calling init_heavy_only leads to uninit'd values in the map
895  //ronj what happens is that a vector1 exists for every position in the pose, and when init_heavy_only is called,
896  //ronj the first heavy atom number of atoms at the beginning of the vector are set to 'true' or whatever the value
897  //ronj type is and then the non-heavy atom indices of the vector are init'd with random values. This behavior occurs
898  //ronj even though the vector at each residue position is resized to either the number of atoms or the number of heavy
899  //ronj atoms depending on what function was called. It seems like the resize method is not really resizing the vector1
900  //ronj at each position, leading to incorrect values being calculated in this method if it's used. Not about to try to
901  //ronj debug a vector1 problem; therefore, I'm init'ing the values of the atom_subset atomid_map myself.
902  //id::initialize_heavy_only( atom_subset, pose, true ); // this call leads to uninit'd values in the map, causing random results
903  atom_subset.clear();
904  atom_subset.resize( pose.n_residue() );
905  for ( Size ii=1; ii <= pose.n_residue(); ++ii ) {
906  atom_subset.resize( ii, pose.residue_type(ii).natoms(), false );
907  for ( Size jj = 1; jj <= pose.residue_type(ii).nheavyatoms(); ++jj ) {
908  atom_subset[ ii ][ jj ] = true;
909  }
910  }
911 
912  core::Real total_sasa = 0.0;
913  total_sasa = core::scoring::calc_per_atom_sasa( pose, atom_sasa, rsd_sasa, probe_radius, false /* no big polar H */, atom_subset, use_naccess_sasa_radii );
914  //#ifdef FILE_DEBUG
915  TR.Debug << "total_sasa: " << total_sasa << std::endl;
916  //#endif
917 
918  // now we have to figure out how much hydrophobic sasa each atom/residue has
919  core::Real total_hydrophobic_sasa = 0.0;
920  core::Real res_hsasa = 0.0;
921 
922  rsd_hydrophobic_sasa.clear();
923  rsd_hydrophobic_sasa.resize( pose.total_residue(), 0.0 );
924 
925  for ( core::Size ii=1; ii <= pose.total_residue(); ++ii ) {
926  conformation::Residue const & rsd = pose.residue( ii );
927  res_hsasa = 0.0;
928 
929  for ( Size at=1; at <= rsd.nheavyatoms(); ++at ) {
930  core::id::AtomID atid( at, ii );
931 
932  // exclude hydrogens from consideration of hydrophobic SASA.
933  // they should be excluded already because of the atom_subset mask, but just in case.
934  if ( rsd.atom_type( at ).is_hydrogen() )
935  continue;
936 
937  if ( rsd.atom_type( at ).element() == "C" || rsd.atom_type( at ).element() == "S" ) {
938  res_hsasa += atom_sasa[ atid ];
939  }
940  }
941 
942  rsd_hydrophobic_sasa[ ii ] = res_hsasa;
943  total_hydrophobic_sasa += res_hsasa;
944  }
945 
946 #ifdef FILE_DEBUG
947  for ( core::Size ii=1; ii <= pose.total_residue(); ++ii ) {
948  conformation::Residue const & rsd = pose.residue( ii );
949  TR << "residue " << rsd.name3() << ii << " atom_sasas: [ ";
950  for ( Size at=1; at <= rsd.natoms(); ++at ) {
951  core::id::AtomID atid( at, ii );
952  TR << rsd.atom_type( at ).element() << ":" << atom_sasa[ atid ] << ", ";
953  }
954  TR << "], total hydrophobic SASA: " << rsd_hydrophobic_sasa[ ii ] << std::endl;
955  }
956 #endif
957 
958  return total_hydrophobic_sasa;
959 }
960 
961 #ifdef FILE_DEBUG
962 ///
963 /// @begin sasa.cc::print_dot_bit_string
964 ///
965 /// @brief
966 /// helper method I was using to try to confirm that the dots are being overlapped and bits are being set correctly (ronj).
967 ///
968 void print_dot_bit_string( utility::vector1< ObjexxFCL::ubyte > & values ) {
969  for ( int bb = 1; bb <= num_bytes; ++bb ) {
970  int bit;
971 //#ifdef FILE_DEBUG
972  if ( (bb-1)*8 % 16 == 0 ) std::cout << (bb-1) * 8 << ":";
973 //#endif
974  for ( int index=7; index >= 0; index-- ) {
975  bit = ( ( (int)values[ bb ] >> index ) & 1 );
976 //#ifdef FILE_DEBUG
977  std::cout << bit;
978 //#endif
979  }
980 //#ifdef FILE_DEBUG
981  std::cout << " ";
982 //#endif
983  }
984 //#ifdef FILE_DEBUG
985  std::cout << std::endl;
986 //#endif
987 }
988 #endif
989 
990 
991 } // namespace scoring
992 } // namespace core