RotationE.cc

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 // ---------------------------------------------------------------------------
00003 //
00004 // This file is a part of the CLHEP - a Class Library for High Energy Physics.
00005 //
00006 // This is the implementation of methods of the HepRotation class which
00007 // were introduced when ZOOM PhysicsVectors was merged in, and which involve
00008 // Euler Angles representation.
00009 //
00010 // Apr 28, 2003  mf  Modified way of computing Euler angles to avoid flawed
00011 //                   answers in the case where theta is near 0 of pi, and
00012 //                   the matrix is not a perfect rotation (due to roundoff).
00013 
00014 #ifdef GNUPRAGMA
00015 #pragma implementation
00016 #endif
00017 
00018 #include "CLHEP/Vector/Rotation.h"
00019 #include "CLHEP/Vector/EulerAngles.h"
00020 #include "CLHEP/Units/PhysicalConstants.h"
00021 
00022 #include <cmath>
00023 
00024 namespace CLHEP  {
00025 
00026 static inline double safe_acos (double x) {
00027   if (std::abs(x) <= 1.0) return std::acos(x);
00028   return ( (x>0) ? 0 : CLHEP::pi );
00029 }
00030 
00031 // ----------  Constructors and Assignment:
00032 
00033 // Euler angles
00034 
00035 HepRotation & HepRotation::set(double phi1, double theta1, double psi1) {
00036 
00037   register double sinPhi   = std::sin( phi1   ), cosPhi   = std::cos( phi1   );
00038   register double sinTheta = std::sin( theta1 ), cosTheta = std::cos( theta1 );
00039   register double sinPsi   = std::sin( psi1   ), cosPsi   = std::cos( psi1   );
00040 
00041   rxx =   cosPsi * cosPhi - cosTheta * sinPhi * sinPsi;
00042   rxy =   cosPsi * sinPhi + cosTheta * cosPhi * sinPsi;
00043   rxz =   sinPsi * sinTheta;
00044 
00045   ryx = - sinPsi * cosPhi - cosTheta * sinPhi * cosPsi;
00046   ryy = - sinPsi * sinPhi + cosTheta * cosPhi * cosPsi;
00047   ryz =   cosPsi * sinTheta;
00048 
00049   rzx =   sinTheta * sinPhi;
00050   rzy = - sinTheta * cosPhi;
00051   rzz =   cosTheta;
00052 
00053   return  *this;
00054 
00055 }  // Rotation::set(phi, theta, psi)
00056 
00057 HepRotation::HepRotation( double phi1, double theta1, double psi1 ) 
00058 {
00059   set (phi1, theta1, psi1);
00060 }
00061 HepRotation & HepRotation::set( const HepEulerAngles & e ) {
00062   return set(e.phi(), e.theta(), e.psi());
00063 }
00064 HepRotation::HepRotation ( const HepEulerAngles & e ) 
00065 {
00066   set(e.phi(), e.theta(), e.psi());
00067 }
00068 
00069  
00070 double HepRotation::phi  () const {
00071 
00072   double s2 =  1.0 - rzz*rzz;
00073   if (s2 < 0) {
00074     std::cerr << "HepRotation::phi() - "
00075         << "HepRotation::phi() finds | rzz | > 1 " << std::endl;
00076     s2 = 0;
00077   }
00078   const double sinTheta = std::sqrt( s2 );
00079 
00080   if (sinTheta < .01) { // For theta close to 0 or PI, use the more stable
00081                         // algorithm to get all three Euler angles
00082     HepEulerAngles ea = eulerAngles();
00083     return ea.phi();
00084   }
00085   
00086   const double cscTheta = 1/sinTheta;
00087   double cosabsphi =  - rzy * cscTheta;
00088   if ( std::fabs(cosabsphi) > 1 ) {     // NaN-proofing
00089     std::cerr << "HepRotation::phi() - "
00090       << "HepRotation::phi() finds | cos phi | > 1 " << std::endl;
00091     cosabsphi = 1;
00092   }
00093   const double absPhi = std::acos ( cosabsphi );
00094   if (rzx > 0) {
00095     return   absPhi;
00096   } else if (rzx < 0) {
00097     return  -absPhi;
00098   } else {
00099     return  (rzy < 0) ? 0 : CLHEP::pi;
00100   }
00101 
00102 } // phi()
00103 
00104 double HepRotation::theta() const {
00105 
00106   return  safe_acos( rzz );
00107 
00108 } // theta()
00109 
00110 double HepRotation::psi  () const {
00111 
00112   double sinTheta;
00113   if ( std::fabs(rzz) > 1 ) {   // NaN-proofing
00114     std::cerr << "HepRotation::psi() - "
00115       << "HepRotation::psi() finds | rzz | > 1" << std::endl;
00116     sinTheta = 0;
00117   } else { 
00118     sinTheta = std::sqrt( 1.0 - rzz*rzz );
00119   }
00120   
00121   if (sinTheta < .01) { // For theta close to 0 or PI, use the more stable
00122                         // algorithm to get all three Euler angles
00123     HepEulerAngles ea = eulerAngles();
00124     return ea.psi();
00125   }
00126 
00127   const double cscTheta = 1/sinTheta;
00128   double cosabspsi =  ryz * cscTheta;
00129   if ( std::fabs(cosabspsi) > 1 ) {     // NaN-proofing
00130     std::cerr << "HepRotation::psi() - "
00131       << "HepRotation::psi() finds | cos psi | > 1" << std::endl;
00132     cosabspsi = 1;
00133   }
00134   const double absPsi = std::acos ( cosabspsi );
00135   if (rxz > 0) {
00136     return   absPsi;
00137   } else if (rxz < 0) {
00138     return  -absPsi;
00139   } else {
00140     return  (ryz > 0) ? 0 : CLHEP::pi;
00141   }
00142 
00143 } // psi()
00144 
00145 // Helpers for eulerAngles():
00146 
00147 static               
00148 void correctByPi ( double& psi1, double& phi1 ) {
00149   if (psi1 > 0) {
00150     psi1 -= CLHEP::pi;
00151   } else {
00152     psi1 += CLHEP::pi;
00153   }
00154   if (phi1 > 0) {
00155     phi1 -= CLHEP::pi;
00156   } else {
00157     phi1 += CLHEP::pi;
00158   }  
00159 }
00160 
00161 static
00162 void correctPsiPhi ( double rxz, double rzx, double ryz, double rzy, 
00163                      double& psi1, double& phi1 ) {
00164 
00165   // set up quatities which would be positive if sin and cosine of
00166   // psi1 and phi1 were positive:
00167   double w[4];
00168   w[0] = rxz; w[1] = rzx; w[2] = ryz; w[3] = -rzy;
00169 
00170   // find biggest relevant term, which is the best one to use in correcting.
00171   double maxw = std::abs(w[0]); 
00172   int imax = 0;
00173   for (int i = 1; i < 4; ++i) {
00174     if (std::abs(w[i]) > maxw) {
00175       maxw = std::abs(w[i]);
00176       imax = i;
00177     }
00178   }
00179   // Determine if the correction needs to be applied:  The criteria are 
00180   // different depending on whether a sine or cosine was the determinor: 
00181   switch (imax) {
00182     case 0:
00183       if (w[0] > 0 && psi1 < 0)           correctByPi ( psi1, phi1 );
00184       if (w[0] < 0 && psi1 > 0)           correctByPi ( psi1, phi1 );
00185       break;
00186     case 1:
00187       if (w[1] > 0 && phi1 < 0)           correctByPi ( psi1, phi1 );
00188       if (w[1] < 0 && phi1 > 0)           correctByPi ( psi1, phi1 );
00189       break;
00190     case 2:
00191       if (w[2] > 0 && std::abs(psi1) > CLHEP::halfpi) correctByPi ( psi1, phi1 );    
00192       if (w[2] < 0 && std::abs(psi1) < CLHEP::halfpi) correctByPi ( psi1, phi1 );    
00193       break;
00194     case 3:
00195       if (w[3] > 0 && std::abs(phi1) > CLHEP::halfpi) correctByPi ( psi1, phi1 );    
00196       if (w[3] < 0 && std::abs(phi1) < CLHEP::halfpi) correctByPi ( psi1, phi1 );    
00197       break;
00198   }          
00199 }
00200 
00201 HepEulerAngles HepRotation::eulerAngles() const {
00202 
00203   // Please see the mathematical justification in eulerAngleComputations.ps
00204 
00205   double phi1, theta1, psi1;
00206   double psiPlusPhi, psiMinusPhi;
00207   
00208   theta1 = safe_acos( rzz );
00209   
00210 //  if (rzz > 1 || rzz < -1) {
00211 //    std::cerr << "HepRotation::eulerAngles() - "
00212 //        << "HepRotation::eulerAngles() finds | rzz | > 1 " << std::endl;
00213 //  }
00214   
00215   double cosTheta = rzz;
00216   if (cosTheta > 1)  cosTheta = 1;
00217   if (cosTheta < -1) cosTheta = -1;
00218 
00219   if (cosTheta == 1) {
00220     psiPlusPhi = std::atan2 ( rxy - ryx, rxx + ryy );
00221     psiMinusPhi = 0;     
00222 
00223   } else if (cosTheta >= 0) {
00224 
00225     // In this realm, the atan2 expression for psi + phi is numerically stable
00226     psiPlusPhi = std::atan2 ( rxy - ryx, rxx + ryy );
00227 
00228     // psi - phi is potentially more subtle, but when unstable it is moot
00229     double s1 = -rxy - ryx; // sin (psi-phi) * (1 - cos theta)
00230     double c1 =  rxx - ryy; // cos (psi-phi) * (1 - cos theta)
00231     psiMinusPhi = std::atan2 ( s1, c1 );
00232         
00233   } else if (cosTheta > -1) {
00234 
00235     // In this realm, the atan2 expression for psi - phi is numerically stable
00236     psiMinusPhi = std::atan2 ( -rxy - ryx, rxx - ryy );
00237 
00238    // psi + phi is potentially more subtle, but when unstable it is moot
00239     double s1 = rxy - ryx; // sin (psi+phi) * (1 + cos theta)
00240     double c1 = rxx + ryy; // cos (psi+phi) * (1 + cos theta)
00241     psiPlusPhi = std::atan2 ( s1, c1 );
00242 
00243   } else { // cosTheta == -1
00244 
00245     psiMinusPhi = std::atan2 ( -rxy - ryx, rxx - ryy );
00246     psiPlusPhi = 0;
00247 
00248   }
00249   
00250   psi1 = .5 * (psiPlusPhi + psiMinusPhi); 
00251   phi1 = .5 * (psiPlusPhi - psiMinusPhi); 
00252 
00253   // Now correct by pi if we have managed to get a value of psiPlusPhi
00254   // or psiMinusPhi that was off by 2 pi:
00255   correctPsiPhi ( rxz, rzx, ryz, rzy, psi1, phi1 );
00256   
00257   return  HepEulerAngles( phi1, theta1, psi1 );
00258 
00259 } // eulerAngles()
00260 
00261 
00262 void HepRotation::setPhi (double phi1) {
00263   set ( phi1, theta(), psi() );
00264 }
00265 
00266 void HepRotation::setTheta (double theta1) {
00267   set ( phi(), theta1, psi() );
00268 }
00269 
00270 void HepRotation::setPsi (double psi1) {
00271   set ( phi(), theta(), psi1 );
00272 }
00273 
00274 }  // namespace CLHEP
00275 

Generated on Mon May 27 17:50:34 2013 for Geant4 by  doxygen 1.4.7