Files
geant4/source/externals/clhep/src/RotationE.cc
T
2020-12-04 12:30:43 +01:00

273 lines
7.5 KiB
C++

// -*- C++ -*-
// ---------------------------------------------------------------------------
//
// This file is a part of the CLHEP - a Class Library for High Energy Physics.
//
// This is the implementation of methods of the HepRotation class which
// were introduced when ZOOM PhysicsVectors was merged in, and which involve
// Euler Angles representation.
//
// Apr 28, 2003 mf Modified way of computing Euler angles to avoid flawed
// answers in the case where theta is near 0 of pi, and
// the matrix is not a perfect rotation (due to roundoff).
#include "CLHEP/Vector/Rotation.h"
#include "CLHEP/Vector/EulerAngles.h"
#include "CLHEP/Units/PhysicalConstants.h"
#include <cmath>
#include <iostream>
namespace CLHEP {
static inline double safe_acos (double x) {
if (std::abs(x) <= 1.0) return std::acos(x);
return ( (x>0) ? 0 : CLHEP::pi );
}
// ---------- Constructors and Assignment:
// Euler angles
HepRotation & HepRotation::set(double phi1, double theta1, double psi1) {
double sinPhi = std::sin( phi1 ), cosPhi = std::cos( phi1 );
double sinTheta = std::sin( theta1 ), cosTheta = std::cos( theta1 );
double sinPsi = std::sin( psi1 ), cosPsi = std::cos( psi1 );
rxx = cosPsi * cosPhi - cosTheta * sinPhi * sinPsi;
rxy = cosPsi * sinPhi + cosTheta * cosPhi * sinPsi;
rxz = sinPsi * sinTheta;
ryx = - sinPsi * cosPhi - cosTheta * sinPhi * cosPsi;
ryy = - sinPsi * sinPhi + cosTheta * cosPhi * cosPsi;
ryz = cosPsi * sinTheta;
rzx = sinTheta * sinPhi;
rzy = - sinTheta * cosPhi;
rzz = cosTheta;
return *this;
} // Rotation::set(phi, theta, psi)
HepRotation::HepRotation( double phi1, double theta1, double psi1 )
{
set (phi1, theta1, psi1);
}
HepRotation & HepRotation::set( const HepEulerAngles & e ) {
return set(e.phi(), e.theta(), e.psi());
}
HepRotation::HepRotation ( const HepEulerAngles & e )
{
set(e.phi(), e.theta(), e.psi());
}
double HepRotation::phi () const {
double s2 = 1.0 - rzz*rzz;
if (s2 < 0) {
std::cerr << "HepRotation::phi() - "
<< "HepRotation::phi() finds | rzz | > 1 " << std::endl;
s2 = 0;
}
const double sinTheta = std::sqrt( s2 );
if (sinTheta < .01) { // For theta close to 0 or PI, use the more stable
// algorithm to get all three Euler angles
HepEulerAngles ea = eulerAngles();
return ea.phi();
}
const double cscTheta = 1/sinTheta;
double cosabsphi = - rzy * cscTheta;
if ( std::fabs(cosabsphi) > 1 ) { // NaN-proofing
std::cerr << "HepRotation::phi() - "
<< "HepRotation::phi() finds | cos phi | > 1 " << std::endl;
cosabsphi = 1;
}
const double absPhi = std::acos ( cosabsphi );
if (rzx > 0) {
return absPhi;
} else if (rzx < 0) {
return -absPhi;
} else {
return (rzy < 0) ? 0 : CLHEP::pi;
}
} // phi()
double HepRotation::theta() const {
return safe_acos( rzz );
} // theta()
double HepRotation::psi () const {
double sinTheta;
if ( std::fabs(rzz) > 1 ) { // NaN-proofing
std::cerr << "HepRotation::psi() - "
<< "HepRotation::psi() finds | rzz | > 1" << std::endl;
sinTheta = 0;
} else {
sinTheta = std::sqrt( 1.0 - rzz*rzz );
}
if (sinTheta < .01) { // For theta close to 0 or PI, use the more stable
// algorithm to get all three Euler angles
HepEulerAngles ea = eulerAngles();
return ea.psi();
}
const double cscTheta = 1/sinTheta;
double cosabspsi = ryz * cscTheta;
if ( std::fabs(cosabspsi) > 1 ) { // NaN-proofing
std::cerr << "HepRotation::psi() - "
<< "HepRotation::psi() finds | cos psi | > 1" << std::endl;
cosabspsi = 1;
}
const double absPsi = std::acos ( cosabspsi );
if (rxz > 0) {
return absPsi;
} else if (rxz < 0) {
return -absPsi;
} else {
return (ryz > 0) ? 0 : CLHEP::pi;
}
} // psi()
// Helpers for eulerAngles():
static
void correctByPi ( double& psi1, double& phi1 ) {
if (psi1 > 0) {
psi1 -= CLHEP::pi;
} else {
psi1 += CLHEP::pi;
}
if (phi1 > 0) {
phi1 -= CLHEP::pi;
} else {
phi1 += CLHEP::pi;
}
}
static
void correctPsiPhi ( double rxz, double rzx, double ryz, double rzy,
double& psi1, double& phi1 ) {
// set up quatities which would be positive if sin and cosine of
// psi1 and phi1 were positive:
double w[4];
w[0] = rxz; w[1] = rzx; w[2] = ryz; w[3] = -rzy;
// find biggest relevant term, which is the best one to use in correcting.
double maxw = std::abs(w[0]);
int imax = 0;
for (int i = 1; i < 4; ++i) {
if (std::abs(w[i]) > maxw) {
maxw = std::abs(w[i]);
imax = i;
}
}
// Determine if the correction needs to be applied: The criteria are
// different depending on whether a sine or cosine was the determinor:
switch (imax) {
case 0:
if (w[0] > 0 && psi1 < 0) correctByPi ( psi1, phi1 );
if (w[0] < 0 && psi1 > 0) correctByPi ( psi1, phi1 );
break;
case 1:
if (w[1] > 0 && phi1 < 0) correctByPi ( psi1, phi1 );
if (w[1] < 0 && phi1 > 0) correctByPi ( psi1, phi1 );
break;
case 2:
if (w[2] > 0 && std::abs(psi1) > CLHEP::halfpi) correctByPi ( psi1, phi1 );
if (w[2] < 0 && std::abs(psi1) < CLHEP::halfpi) correctByPi ( psi1, phi1 );
break;
case 3:
if (w[3] > 0 && std::abs(phi1) > CLHEP::halfpi) correctByPi ( psi1, phi1 );
if (w[3] < 0 && std::abs(phi1) < CLHEP::halfpi) correctByPi ( psi1, phi1 );
break;
}
}
HepEulerAngles HepRotation::eulerAngles() const {
// Please see the mathematical justification in eulerAngleComputations.ps
double phi1, theta1, psi1;
double psiPlusPhi, psiMinusPhi;
theta1 = safe_acos( rzz );
// if (rzz > 1 || rzz < -1) {
// std::cerr << "HepRotation::eulerAngles() - "
// << "HepRotation::eulerAngles() finds | rzz | > 1 " << std::endl;
// }
double cosTheta = rzz;
if (cosTheta > 1) cosTheta = 1;
if (cosTheta < -1) cosTheta = -1;
if (cosTheta == 1) {
psiPlusPhi = std::atan2 ( rxy - ryx, rxx + ryy );
psiMinusPhi = 0;
} else if (cosTheta >= 0) {
// In this realm, the atan2 expression for psi + phi is numerically stable
psiPlusPhi = std::atan2 ( rxy - ryx, rxx + ryy );
// psi - phi is potentially more subtle, but when unstable it is moot
double s1 = -rxy - ryx; // sin (psi-phi) * (1 - cos theta)
double c1 = rxx - ryy; // cos (psi-phi) * (1 - cos theta)
psiMinusPhi = std::atan2 ( s1, c1 );
} else if (cosTheta > -1) {
// In this realm, the atan2 expression for psi - phi is numerically stable
psiMinusPhi = std::atan2 ( -rxy - ryx, rxx - ryy );
// psi + phi is potentially more subtle, but when unstable it is moot
double s1 = rxy - ryx; // sin (psi+phi) * (1 + cos theta)
double c1 = rxx + ryy; // cos (psi+phi) * (1 + cos theta)
psiPlusPhi = std::atan2 ( s1, c1 );
} else { // cosTheta == -1
psiMinusPhi = std::atan2 ( -rxy - ryx, rxx - ryy );
psiPlusPhi = 0;
}
psi1 = .5 * (psiPlusPhi + psiMinusPhi);
phi1 = .5 * (psiPlusPhi - psiMinusPhi);
// Now correct by pi if we have managed to get a value of psiPlusPhi
// or psiMinusPhi that was off by 2 pi:
correctPsiPhi ( rxz, rzx, ryz, rzy, psi1, phi1 );
return HepEulerAngles( phi1, theta1, psi1 );
} // eulerAngles()
void HepRotation::setPhi (double phi1) {
set ( phi1, theta(), psi() );
}
void HepRotation::setTheta (double theta1) {
set ( phi(), theta1, psi() );
}
void HepRotation::setPsi (double psi1) {
set ( phi(), theta(), psi1 );
}
} // namespace CLHEP