Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DNA_BasePotential.cc
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// @file
11 /// @brief
12 /// @author
13 
15 
17 
18 #include <core/types.hh>
20 #include <core/chemical/AA.hh>
22 #include <core/kinematics/Stub.hh>
23 // AUTO-REMOVED #include <core/pose/Pose.hh>
24 #include <basic/database/open.hh>
25 
26 #include <utility/io/izstream.hh>
27 #include <utility/vector1.hh>
28 #include <utility/exit.hh>
29 #include <numeric/conversions.hh>
30 #include <numeric/xyzVector.hh>
31 #include <numeric/xyzMatrix.hh>
32 #include <numeric/xyz.functions.hh>
33 
34 #include <ObjexxFCL/FArray1D.hh>
35 #include <ObjexxFCL/format.hh>
36 
37 
38 using namespace ObjexxFCL;
39 using namespace ObjexxFCL::fmt;
40 
41 namespace core {
42 namespace scoring {
43 namespace dna {
44 
45 /// @details Auto-generated virtual destructor
46 DNA_BasePotential::~DNA_BasePotential() {}
47 
48 typedef ObjexxFCL::FArray1D< Real > FArray1D_Real;
49 
50 DNA_BasePotential::DNA_BasePotential():
51  mean_ ( 6, 2, 16 ),
52  stddev_ ( 6, 2, 16 ),
53  stiffness_( 6, 6, 2, 16 )
54 {
56 }
57 
60 {
61  using namespace chemical;
62  switch ( rsd.aa() ) {
63  case na_ade:
64  return "A";
65  case na_cyt:
66  return "C";
67  case na_gua:
68  return "G";
69  case na_thy:
70  return "T";
71  default:
72  utility_exit_with_message("bad rsd type for DNA_BasePotential: "+rsd.name() );
73  }
74  return "X";
75 }
76 
77 ///////////////////////////////////////////////////////////////////////////////////
78 // i1 = 1,2
79 // i2 = 1,16
80 void
81 DNA_BasePotential::get_array_indices( InteractionType const & t, std::string const & bases, int & i1, int & i2 ) const
82 {
83  char const b1( bases[0] ), b2( bases[1] );
84  i1 = 0; i2 = 0;
85  if ( t == BP_type ) { // base pair ////////////////////////////////
86  i1 = 1;
87  if ( b1 == 'A' ) {
88  i2 = 1;
89  } else if ( b1 == 'C' ) {
90  i2 = 2;
91  } else if ( b1 == 'G' ) {
92  i2 = 3;
93  } else if ( b1 == 'T' ) {
94  i2 = 4;
95  }
96  } else { // base step /////////////////////////////////////////////
97  i1 = 2;
98  if ( b1 == 'A' ) {
99  i2 = 0;
100  } else if ( b1 == 'C' ) {
101  i2 = 4;
102  } else if ( b1 == 'G' ) {
103  i2 = 8;
104  } else if ( b1 == 'T' ) {
105  i2 = 12;
106  } else {
107  utility_exit_with_message( "Unknown first DNA base label: "+b1 );
108  }
109 
110  if ( b2 == 'A' ) {
111  i2 += 1;
112  } else if ( b2 == 'C' ) {
113  i2 += 2;
114  } else if ( b2 == 'G' ) {
115  i2 += 3;
116  } else if ( b2 == 'T' ) {
117  i2 += 4;
118  } else {
119  utility_exit_with_message( "Unknown first DNA base label: "+b2 );
120  }
121  }
122 }
123 
124 
125 ///////////////////////////////////////////////////////////////////////////////
126 void
128  InteractionType const & type,
129  std::string const & bases,
130  int const index,
131  Real mean,
132  Real stddev
133 )
134 {
135  int i1,i2;
136  get_array_indices( type, bases, i1, i2 );
137 
138  mean_ ( index, i1, i2 ) = mean;
139  stddev_( index, i1, i2 ) = stddev;
140 }
141 
142 
143 ///////////////////////////////////////////////////////////////////////////////
144 void
146  InteractionType const & type,
147  std::string const & bases,
148  int const index1,
149  int const index2,
150  Real const val
151 )
152 {
153  int i1,i2;
154  get_array_indices( type, bases, i1, i2 );
155 
156  stiffness_( index1, index2, i1, i2 ) = val;
157 }
158 
159 ///////////////////////////////////////////////////////////////////////////////
160 //
161 void
163 {
164 
165  // load base-pair and base-step parameters
166  utility::io::izstream data;
167  basic::database::open( data, "scoring/dna/dna_bs_bp.dat" );
168 
169 
170  std::string line, type_name, bases, param_name;
171  bool fail( false );
172  while ( getline( data,line ) ) {
173  std::istringstream l( line );
174  std::string tag, step;
175 
176  l >> tag;
177  if ( l.fail() ) continue;
178  if ( tag == "SD:" ) {
179  int param_index;
180  Real mean, stddev;
181  l >> type_name >> bases >> param_name >> param_index >> mean >> stddev;
182  assert( type_name == "BS" || type_name == "BP" );
183  InteractionType const type( type_name == "BS" ? BS_type : BP_type );
184  if ( l.fail() || param_index < 1 || param_index > 6 ) {
185  std::cout << "failline:" << line << std::endl;
186  fail = true;
187  break;
188  }
189  set_mean_and_stddev( type /* BS or BP */, bases /* eg AT */, param_index /* 1-6 */, mean, stddev );
190 
191 
192  } else if ( tag == "stiffness:" ) {
193  int i;
194  Real Fij;
195  std::string tmp;
196  l >> type_name >> bases >> tmp >> i;
197  assert( type_name == "BS" || type_name == "BP" );
198  InteractionType const type( type_name == "BS" ? BS_type : BP_type );
199  for ( int j=1; j<= 6; ++j ) {
200  l >> Fij;
201  set_stiffness( type /* BS or BP */, bases /* eg AT */, i, j, Fij );
202  }
203  if ( l.fail() ) {
204  std::cout << "failline:" << line << std::endl;
205  fail = true;
206  break;
207  }
208  }
209  }
210 
211  if ( fail ) {
212  utility_exit_with_message( "Unable to find/parse the DNA basepair and basestep params -- update your mini-DB?" );
213  }
214 }
215 
216 ////////////////////////////////////////////////////////////////////////////////////
217 // STOLEN from Alex Morozov
218 // private
219 Real
221  InteractionType const & type,
222  std::string const & bases,
223  utility::vector1< Real > const & params
224 ) const
225 {
226  assert( params.size() == 6 );
227 
228  // max allowed deviation from average beyond which the score is capped
229  // PB -- this seems very large! it's in standard deviations, right?
230  Real const MAX_SCORE_DEV = 100.0;
231 
232  // accumulate score values
233  Real score(0.0);
234 
235  for ( int i = 1; i <= 6; ++i ) {
236  Real const max_delta_i = MAX_SCORE_DEV * stddev( type, bases, i );
237 
238  Real delta_i = params[i] - mean( type, bases, i );
239  // reset outliers to max allowed values
240  // (beyond which the potential is unreliable)
241  if ( delta_i > max_delta_i ) delta_i = max_delta_i;
242  if ( delta_i < -max_delta_i ) delta_i = -max_delta_i;
243 
244  for ( int j = 1; j <= 6; ++j ) {
245  Real const max_delta_j = MAX_SCORE_DEV * stddev( type, bases, j );
246 
247  Real delta_j = params[j] - mean( type, bases, j );
248  // reset outliers to max allowed values
249  // (beyond which the potential is unreliable)
250  if ( delta_j > max_delta_j ) delta_j = max_delta_j;
251  if ( delta_j < -max_delta_j ) delta_j = -max_delta_j;
252 
253  score += stiffness( type, bases, i, j ) * delta_i * delta_j;
254  }
255  }
256 
257  return score;
258 }
259 
260 
261 
262 
263 
264 Real
266  Residue const & rsd1,
267  Residue const & rsd2
268 ) const
269 {
270  utility::vector1< Real > params(6);
271  assert( rsd2.seqpos() == rsd1.seqpos() + 1 );
272 
273  get_base_step_params( rsd1, rsd2, params );
274 
275  return base_score( BS_type, base_string(rsd1)+base_string(rsd2), params );
276 
277 }
278 
279 void
281  Residue const & rsd1,
282  Residue const & rsd2,
283  utility::vector1< Real > & z_scores
284 ) const
285 {
286  utility::vector1< Real > params(6);
287  z_scores.resize(6);
288  assert( rsd2.seqpos() == rsd1.seqpos() + 1 );
289 
290  get_base_step_params( rsd1, rsd2, params );
291 
292  std::string const bases( base_string(rsd1)+base_string(rsd2) );
293 
294  for ( Size i=1; i<= 6; ++i ) {
295  z_scores[i] = ( params[i] - mean( BS_type, bases, i ) ) / stddev( BS_type, bases, i );
296  }
297 }
298 
299 
300 void
302  Residue const & rsd1,
303  Residue const & rsd2,
304  utility::vector1< Real > & z_scores
305 ) const
306 {
307  utility::vector1< Real > params(6);
308  z_scores.resize(6);
309  get_base_pair_params( rsd1, rsd2, params );
310 
311  std::string const bases( base_string(rsd1)+base_string(rsd2) );
312 
313  for ( Size i=1; i<= 6; ++i ) {
314  z_scores[i] = ( params[i] - mean( BP_type, bases, i ) ) / stddev( BP_type, bases, i );
315  }
316 }
317 
318 
319 Real
321  Residue const & rsd1,
322  Residue const & rsd2
323 ) const
324 {
325  utility::vector1< Real > params(6);
326  //assert( rsd1.seqpos() < rsd2.seqpos() );
327  get_base_pair_params( rsd1, rsd2, params );
328  std::string const bpstr( base_string(rsd1) + base_string(rsd2) );
329 
330  // ashworth scoring potential is made highly intolerant of non-Watson-Crick basepairs. This is justified because there are currently no basepair parameter scores for non-Watson-Crick basepairs in the default database parameter file (dna_bp_bs.dat)
331  if ( bpstr == "AT" || bpstr == "CG" || bpstr == "GC" || bpstr == "TA" )
332  return base_score( BP_type, bpstr, params );
333  else return 1e9;
334 }
335 
336 
337 void
339  Residue const & rsd1,
340  Residue const & rsd2,
341  Vector & F1,
342  Vector & F2,
343  Real const external_weight_factor // should probably be +1 or -1
344 ) const
345 {
346  using numeric::conversions::degrees;
347  using numeric::conversions::radians;
348  using numeric::arccos;
349  using numeric::cross;
350 
351  bool const debug( true ), verbose( false );
352 
353  assert( rsd1.seqpos() + 1 == rsd2.seqpos() );
354 
355  kinematics::Stub const stub1( get_base_stub( rsd1, 1 ) ), stub2( get_base_stub( rsd2, 1 ) );
356 
357  // copy matrices
358  Matrix M1( stub1.M ), M2( stub2.M );
359 
360  assert( is_orthonormal( M1, 1e-3 ) );
361  assert( is_orthonormal( M2, 1e-3 ) );
362 
363  if ( dot( M1.col_z(), M2.col_z() ) < 0.0 ) {
364  std::cout << "dna_bs_deriv: base flip!" << std::endl;
365  //utility::exit( EXIT_FAILURE, __FILE__, __LINE__);
366  }
367 
368  // get angle between the z-axes
369  Real const gamma( arccos( dot( M1.col_z(), M2.col_z() ) ) );
370 
371  Vector const rt( ( cross( M2.col_z(), M1.col_z() ) ).normalized() );
372 
373  Matrix R_gamma_2( rotation_matrix( rt, gamma/2.0f ) );
374 
375  M2 = R_gamma_2 * M2;
376  M1 = R_gamma_2.transposed() * M1;
377 
378  assert( is_orthonormal( M1, 1e-3 ) );
379  assert( is_orthonormal( M2, 1e-3 ) );
380 
381  // build mid-base-pair triad
382  assert( M1.col_z().distance( M2.col_z() ) < 1e-3 );
383  assert( std::abs( dot( rt, M1.col_z() ) ) < 1e-3 );
384 
385  Matrix MBT;
386  MBT.col_z( M1.col_z() );
387 
388  assert( std::abs( dot( M1.col_x(), MBT.col_z() ) ) < 1e-3 );
389  assert( std::abs( dot( M2.col_x(), MBT.col_z() ) ) < 1e-3 );
390  assert( std::abs( dot( M1.col_y(), MBT.col_z() ) ) < 1e-3 );
391  assert( std::abs( dot( M2.col_y(), MBT.col_z() ) ) < 1e-3 );
392 
393  // get
394  MBT.col_y( ( 0.5f * ( M1.col_y() + M2.col_y() ) ).normalized() );
395  MBT.col_x( ( 0.5f * ( M1.col_x() + M2.col_x() ) ).normalized() );
396 
397  assert( is_orthonormal( MBT, 1e-3 ) );
398 
399  // angular params
400 
401  // TWIST
402  // x,y,z make rh coord system
403  utility::vector1< Real > params( 6, 0.0 );
404 
405  params[1] = std::atan2( dot( M1.col_x(), M2.col_y() ),
406  dot( M1.col_x(), M2.col_x() ) );
407  // ROLL:
408  params[2] = gamma * dot( rt, MBT.col_y() );
409 
410  // TILT
411  params[3] = gamma * dot( rt, MBT.col_x() );
412 
413  // translational params
414  Vector const displacement( stub1.v - stub2.v );
415 
416  params[4] = dot( displacement, MBT.col_x() ); // SHIFT
417  params[5] = dot( displacement, MBT.col_y() ); // SLIDE
418  params[6] = dot( displacement, MBT.col_z() ); // RISE
419 
420  // check sign conventions
421  assert( params[1] * dot( MBT.col_z(), cross( M2.col_y(), M1.col_y() ) ) > 0);
422 
423  // convert to degrees
424  Real const twist( params[1] );
425  Real const roll ( params[2] );
426  Real const tilt ( params[3] );
427 
428  params[1] = degrees( params[1] );
429  params[2] = degrees( params[2] );
430  params[3] = degrees( params[3] );
431 
432 
433  if ( debug ) {
434  // doublecheck our params calculation
435  utility::vector1< Real > new_params;
436  get_base_step_params( rsd1, rsd2, new_params );
437  for ( Size i=1; i<= 6; ++i ) {
438  assert( std::abs( params[i] - new_params[i] ) < 1e-2 );
439  }
440  }
441 
442  // max allowed deviation from average beyond which the score is capped
443  // PB -- this seems very large! it's in standard deviations, right?
444  Real const MAX_SCORE_DEV = 100.0;
445 
446  FArray1D_Real dE_dp(6,0.0);
447 
448  bool out_of_bounds( false );
449  std::string const bases( base_string( rsd1 ) + base_string( rsd2 ) );
450 
451  for ( int i = 1; i <= 6; ++i ) {
452  Real const max_delta_i = MAX_SCORE_DEV * stddev( BS_type, bases, i );
453 
454  Real delta_i = params[i] - mean( BS_type, bases, i );
455  // reset outliers to max allowed values
456  // (beyond which the potential is unreliable)
457  if ( delta_i > max_delta_i ) {
458  out_of_bounds = true;
459  delta_i = max_delta_i;
460  }
461  if ( delta_i < -max_delta_i ) {
462  out_of_bounds = true;
463  delta_i = -max_delta_i;
464  }
465 
466  for ( int j = 1; j <= 6; ++j ) {
467  Real const max_delta_j = MAX_SCORE_DEV * stddev( BS_type, bases, j );
468 
469  Real delta_j = params[j] - mean( BS_type, bases, j );
470  // reset outliers to max allowed values
471  // (beyond which the potential is unreliable)
472  if ( delta_j > max_delta_j ) {
473  out_of_bounds = true;
474  delta_j = max_delta_j;
475  }
476  if ( delta_j < -max_delta_j ) {
477  delta_j = -max_delta_j;
478  out_of_bounds = true;
479  }
480 
481  Real const Fij( stiffness( BS_type, bases, i, j ) );
482  dE_dp(i) += Fij * delta_j;
483  dE_dp(j) += Fij * delta_i;
484  }
485  }
486 
487 
488  if ( out_of_bounds ) {
489  // this is not really a big deal
490  std::cout << "WARNING:: out_of_bounds in dna base-pair derivative!" <<
491  std::endl;
492  }
493 
494 
495  // adjust for the external weight factor:
496  for ( int i=1; i<= 6; ++i ) dE_dp(i) *= external_weight_factor;
497 
498 
499  //////////////////////////
500  // twist deriv
501  //////////////////////////
502  //
503  // let xi be the original x-axis vector in triad i
504  // xi' the vector transformed so that the z-axes coincide
505  //
506  // cos( twist ) = dot( x1' , x2' )
507  // = dot( x1, R_gamma( x2 ) ) // apply R_gamma/2 to both sides
508  //
509  // ASSUMPTION: R_gamma doesn't depend on the torsion angle, which it
510  // does! but I think the primary contribution is still captured here.
511  //
512  // d/dphi( dot( x1, R_gamma(x2) )) = dot( cross(u_phi,x1), R_gamma(x2) )
513  // = dot( -u_phi , cross( R_gamma(x2), x1 )
514 
515  Real const rad2deg( degrees(1.0));
516 
517  {
518  Real const dE_dtwist( dE_dp(1) );
519 
520  // these are for preventing d_arccos from blowing up
521  static Real const small_angle( radians( 0.1f ) );
522  static Real const big_angle( radians( 179.9f ) );
523  static Real const max_c( std::cos( small_angle ));
524  static Real const min_c( std::cos( big_angle ));
525 
526  Vector x1( stub1.M.col_x() );
527  Vector R_gamma_x2( rotation_matrix( rt, gamma) * Vector(stub2.M.col_x()) );
528 
529  assert( Vector(stub1.M.col_z()).distance( rotation_matrix(rt,gamma) * Vector(stub2.M.col_z())) <1e-2);
530 
531  Real c( std::cos( twist ) );
532  assert( std::abs( c - dot( x1,R_gamma_x2 ) ) < 1e-2 );
533  c = std::min( std::max( min_c, c ), max_c );
534 
535  Real const dtwist_dc = -1 / std::sqrt( 1 - c * c );
536 
537  // since the derivatives factor through cosine we are getting
538  // the derivative of an unsigned angle
539 
540  int const sign_factor( twist < 0 ? -1 : 1 );
541 
542  if ( verbose ) std::cout << "dE_dtwist: " <<
543  F(9,3, dE_dtwist ) <<
544  F(9,3, dtwist_dc ) <<
545  F(9,3, cross(R_gamma_x2,x1).length() ) << std::endl;
546 
547  F1 += sign_factor * dE_dtwist * rad2deg * dtwist_dc*
548  cross( R_gamma_x2, x1 );
549  }
550 
551  ///////////////////
552  // roll + tilt
553  ///////////////////
554  //
555  // as confirmed in the base_pair_params routine,
556  // if we let tilt' = sin(gamma) * tilt / gamma,
557  // then tilt' = dot( cross( z2, z1 ), x_MBT )
558  //
559  // likewise roll' = dot( cross( z2, z1 ), y_MBT )
560  //
561  // but how do x_MBT and y_MBT vary as we rotate about the phi axis??
562  //
563  // since these are approximately averages between a moving vector and
564  // a fixed vector, we can assume:
565  //
566  // d/dphi( x_MBT ) = cross( u_phi, x_MBT ) / 2.0
567  //
568  // have to see if this actually works
569  //
570  // in addition we can assume that sin(gamma)/gamma is constant
571  // since gamma is small
572  //
573 
574  {
575  Real const dE_droll( dE_dp(2) );
576  Real const dE_dtilt( dE_dp(3) );
577 
578  Vector const z1( stub1.M.col_z() ), z2( stub2.M.col_z() );
579 
580  Real const gamma_sin_gamma( gamma / std::sin( gamma ) );
581 
582  runtime_assert( std::abs( roll - gamma_sin_gamma *
583  dot( cross( z2, z1 ), MBT.col_y() ) ) < 1e-2 );
584 
585  runtime_assert( std::abs( tilt - gamma_sin_gamma *
586  dot( cross( z2, z1 ), MBT.col_x() ) ) < 1e-2 );
587 
588  F1 += dE_dtilt * gamma_sin_gamma * rad2deg *
589  ( cross( z1, cross( z2, MBT.col_x() ) ) + // 1st term: dz1/dphi
590  0.5f * cross( MBT.col_x(), cross( z1, z2 ) ) );// 2nd term: dx_MBT/dphi
591 
592  F1 += dE_droll * gamma_sin_gamma * rad2deg *
593  ( cross( z1, cross( z2, MBT.col_y() ) ) +
594  0.5f * cross( MBT.col_y(), cross( z1, z2 ) ) );
595 
596  }
597 
598  ///////////////////////
599  // translational derivs
600  ///////////////////////
601  //
602  // need d/dphi( dot( M-F, x_MBT ) ) where M is rotated, F is fixed,
603  // and x_MBT is varying in some complicated way
604  //
605  // from cst_set.cc, comments to Angle_cst::helper(...)
606  // the term w x_MBT constant contributes x_MBT to F2
607  // and cross( x_MBT, M) to F1
608  //
609  // for the other term we use d/dphi( x_MBT ) = 0.5 * cross( u_phi, X_MBT)
610  // and the standard vector rearrangement
611  {
612  Vector M( stub1.v ), F( stub2.v );
613 
614  F1 += dE_dp(4) * ( cross( MBT.col_x(), M - 0.5f * ( M - F ) ) );
615  F2 += dE_dp(4) * ( MBT.col_x() );
616 
617  F1 += dE_dp(5) * ( cross( MBT.col_y(), M - 0.5f * ( M - F ) ) );
618  F2 += dE_dp(5) * ( MBT.col_y() );
619 
620  F1 += dE_dp(6) * ( cross( MBT.col_z(), M - 0.5f * ( M - F ) ) );
621  F2 += dE_dp(6) * ( MBT.col_z() );
622 
623  }
624 
625 } // eval_base_step_derivative ////////////////////////////////////////////////
626 
627 
628 
629 ///////////////////////////////////////////////////////////////////////////////
630 void
632  Residue const & rsd1,
633  Residue const & rsd2,
634  Vector & F1,
635  Vector & F2,
636  Real const external_weight_factor
637 ) const
638 {
639  using numeric::conversions::degrees;
640  using numeric::conversions::radians;
641  using numeric::arccos;
642  using numeric::cross;
643 
644  bool const debug( true );
645 
646  assert( rsd1.seqpos() < rsd2.seqpos() );
647 
648  utility::vector1< Real > params( 6, 0.0 );
649  Real const MAX_SCORE_DEV = 100.0; // should be same as in dna_bp_score
650 
651 
652  kinematics::Stub const stub1( get_base_stub( rsd1, 1 ) ), stub2( get_base_stub( rsd2, 2 ) );
653 
654  // copy matrices
655  Matrix M1( stub1.M ), M2( stub2.M );
656 
657  assert( is_orthonormal( M1, 1e-3 ) );
658  assert( is_orthonormal( M2, 1e-3 ) );
659 
660  if ( dot( M1.col_z(), M2.col_z() ) < 0.0 ) {
661  std::cout << "dna_bp_deriv: base flip!" << std::endl;
662  //utility::exit( EXIT_FAILURE, __FILE__, __LINE__);
663  }
664 
665  // get angle between the y-axes
666  Real const gamma( arccos( dot( M1.col_y(), M2.col_y() ) ) );
667 
668  Vector const bo( ( cross( M2.col_y(), M1.col_y() ) ).normalized() );
669 
670  Matrix R_gamma_2( rotation_matrix( bo, gamma/2.0f ) );
671 
672  M2 = R_gamma_2 * M2;
673  M1 = R_gamma_2.transposed() * M1;
674 
675  assert( is_orthonormal( M1, 1e-3 ) );
676  assert( is_orthonormal( M2, 1e-3 ) );
677  assert( M1.col_y().distance( M2.col_y() ) < 1e-3 );
678  assert( std::abs( dot( bo, M1.col_y() ) ) < 1e-3 );
679 
680  // build mid-base-pair triad
681  Matrix MBT;
682  MBT.col_y( M1.col_y() );
683  MBT.col_x( ( 0.5f * ( M1.col_x() + M2.col_x() ) ).normalized() );
684  MBT.col_z( ( 0.5f * ( M1.col_z() + M2.col_z() ) ).normalized() );
685 
686  /////////////////
687  // angular params
688 
689  // propeller
690  // z,x,y make rh coord system
691  Real const omega = std::atan2( dot( M1.col_z(), M2.col_x() ),
692  dot( M1.col_z(), M2.col_z() ) );
693 
694  assert( ( std::abs( std::abs( omega ) -
695  arccos( dot( M1.col_z(), M2.col_z() ) ) )<1e-2 ) &&
696  ( std::abs( std::abs( omega ) -
697  arccos( dot( M1.col_x(), M2.col_x() ) ) )<1e-2 ) );
698 
699  // buckle:
700  Real const kappa = gamma * dot( bo, MBT.col_x() );
701 
702  // opening
703  Real const sigma = gamma * dot( bo, MBT.col_z() );
704 
705  ///////////////////////
706  // translational params
707  Vector const displacement( stub1.v - stub2.v );
708 
709  params[4] = dot( displacement, MBT.col_x() );
710  params[5] = dot( displacement, MBT.col_y() );
711  params[6] = dot( displacement, MBT.col_z() );
712 
713  /////////////
714  // check sign conventions
715  assert( omega * dot( MBT.col_y(), cross( M2.col_x(), M1.col_x() ) ) > 0);
716 
717  // convert to degrees
718  params[1] = degrees( omega );
719  params[2] = degrees( kappa );
720  params[3] = degrees( sigma );
721 
722  if ( debug ) {
723  // doublecheck our params calculation
724  utility::vector1< Real > new_params;
725  get_base_pair_params( rsd1, rsd2, new_params );
726  for ( Size i=1; i<= 6; ++i ) {
727  assert( std::abs( params[i] - new_params[i] ) < 1e-2 );
728  }
729  }
730 
731  // now do deriv stuff:
732  FArray1D_Real dE_dp( 6, 0.0 );
733 
734  bool out_of_bounds( false );
735 
736  std::string const bases( base_string( rsd1 ) + base_string( rsd2 ) );
737  for ( int i = 1; i <= 6; ++i ) {
738  Real delta_i = params[i] - mean( BP_type, bases, i );
739 
740  // reset outliers to max allowed values
741  // (beyond which the potential is unreliable)
742  Real const max_delta_i( MAX_SCORE_DEV*stddev(BP_type,bases,i));
743  if ( delta_i > max_delta_i ) {
744  delta_i = max_delta_i;
745  out_of_bounds = true;
746  }
747  if ( delta_i < -max_delta_i ) {
748  delta_i = -max_delta_i;
749  out_of_bounds = true;
750  }
751 
752  for ( int j = 1; j <= 6; ++j ) {
753  Real delta_j = params[j] - mean( BP_type, bases, j );
754 
755  // reset outliers to max allowed values
756  // (beyond which the potential is unreliable)
757  Real const max_delta_j( MAX_SCORE_DEV*stddev(BP_type,bases,j));
758  if ( delta_j > max_delta_j ) {
759  delta_j = max_delta_j;
760  out_of_bounds = true;
761  }
762  if ( delta_j < -max_delta_j ) {
763  delta_j = -max_delta_j;
764  out_of_bounds = true;
765  }
766 
767  // score += Gij(ind,i,j)*delta_i*delta_j;
768  Real const Fij( stiffness( BP_type, bases, i, j ));
769  dE_dp(i) += Fij * delta_j;
770  dE_dp(j) += Fij * delta_i;
771  }
772  }
773 
774  if ( out_of_bounds ) {
775  // this is not really a big deal
776  std::cout << "WARNING:: out_of_bounds in dna base-pair derivative!" <<
777  std::endl;
778  }
779 
780  // apply external weights
781  for ( int i=1; i<= 6; ++i ) {
782  dE_dp(i) *= external_weight_factor;
783  }
784 
785 
786  //////////////////////////
787  // propeller (omega) deriv
788  //////////////////////////
789  //
790  // let xi be the original x-axis vector in triad i
791  // xi' the vector transformed so that the y-axes coincide
792  //
793  // cos(omega) = dot( x1' , x2' )
794  // = dot( x1, R_gamma( x2 ) ) // apply R_gamma/2 to both sides
795  //
796  // ASSUMPTION: R_gamma doesn't depend on the torsion angle, which it
797  // does! but I think the primary contribution is still captured here.
798  //
799  // d/dphi( dot( x1, R_gamma(x2) )) = dot( cross(u_phi,x1), R_gamma(x2) )
800  // = dot( -u_phi , cross( R_gamma(x2), x1 )
801 
802  Real const rad2deg( degrees(1.0));
803 
804  {
805  Real const dE_domega( dE_dp(1) );
806 
807  // these are for preventing d_arccos from blowing up
808  static Real const small_angle( radians( 0.1f ) );
809  static Real const big_angle( radians( 179.9f ) );
810  static Real const max_c( std::cos( small_angle ));
811  static Real const min_c( std::cos( big_angle ));
812 
813  Vector x1( stub1.M.col_x() );
814  Vector R_gamma_x2( rotation_matrix( bo, gamma) * Vector(stub2.M.col_x()) );
815 
816  assert( Vector(stub1.M.col_y()).distance( rotation_matrix(bo,gamma) * Vector(stub2.M.col_y())) <1e-2);
817 
818  Real c( std::cos( omega ) );
819  assert( std::abs( c - dot( x1,R_gamma_x2 ) ) < 1e-2 );
820  c = std::min( std::max( min_c, c ), max_c );
821 
822  Real const domega_dc = -1 / std::sqrt( 1 - c * c );
823 
824  // since the derivatives factor through cosine we are getting
825  // the derivative of an unsigned angle
826 
827  int const sign_factor2( omega < 0 ? -1 : 1 );
828 
829  F1 += sign_factor2 * dE_domega * rad2deg * domega_dc *
830  cross( R_gamma_x2, x1 );
831  }
832 
833  ///////////////////
834  // buckle + opening
835  ///////////////////
836  //
837  // as confirmed in the base_pair_params routine,
838  // if we let kappa' = sin(gamma) * kappa / gamma,
839  // then kappa' = dot( cross( y2, y1 ), x_MBT )
840  //
841  // likewise sigma' = dot( cross( y2, y1 ), z_MBT )
842  //
843  // but how do x_MBT and z_MBT vary as we rotate about the phi axis??
844  //
845  // since these are approximately averages between a moving vector and
846  // a fixed vector, we can assume:
847  //
848  // d/dphi( x_MBT ) = cross( u_phi, x_MBT ) / 2.0
849  //
850  // have to see if this actually works
851  //
852  // in addition we can assume that sin(gamma)/gamma is constant
853  // since gamma is small
854  //
855 
856  {
857  Real const dE_dkappa( dE_dp(2) );
858  Real const dE_dsigma( dE_dp(3) );
859 
860  Vector const y1( stub1.M.col_y() ), y2( stub2.M.col_y() );
861 
862  Real const gamma_sin_gamma( gamma / std::sin( gamma ) );
863 
864  F1 += dE_dkappa * gamma_sin_gamma * rad2deg *
865  ( cross( y1, cross( y2, MBT.col_x() ) ) + // 1st term: dy1/dphi
866  0.5f * cross( MBT.col_x(), cross( y1, y2 ) ) );// 2nd term: dx_MBT/dphi
867 
868  F1 += dE_dsigma * gamma_sin_gamma * rad2deg *
869  ( cross( y1, cross( y2, MBT.col_z() ) ) +
870  0.5f * cross( MBT.col_z(), cross( y1, y2 ) ) );
871 
872  }
873 
874  ///////////////////////
875  // translational derivs
876  ///////////////////////
877  //
878  // need d/dphi( dot( M-F, x_MBT ) ) where M is rotated, F is fixed,
879  // and x_MBT is varying in some complicated way
880  //
881  // from cst_set.cc, comments to Angle_cst::helper(...)
882  // the term w x_MBT constant contributes x_MBT to F2
883  // and cross( x_MBT, M) to F1
884  //
885  // for the other term we use d/dphi( x_MBT ) = 0.5 * cross( u_phi, X_MBT)
886  // and the standard vector rearrangement
887  {
888  Vector M( stub1.v ), F( stub2.v );
889 
890  F1 += dE_dp(4) * ( cross( MBT.col_x(), M - 0.5f * ( M - F ) ) );
891  F2 += dE_dp(4) * ( MBT.col_x() );
892 
893  F1 += dE_dp(5) * ( cross( MBT.col_y(), M - 0.5f * ( M - F ) ) );
894  F2 += dE_dp(5) * ( MBT.col_y() );
895 
896  F1 += dE_dp(6) * ( cross( MBT.col_z(), M - 0.5f * ( M - F ) ) );
897  F2 += dE_dp(6) * ( MBT.col_z() );
898 
899  }
900 
901 } // eval_base_pair_derivative //////////////////////////
902 
903 
904 
905 
906 } // namespace dna
907 }} // scoring core