void KVFlowTensor::Calculate() { // Calculate eigenvalues & eigenvectors of tensor TMatrixT<double> evectors = fTensor.EigenVectors(fEVal); for (int i = 0; i < 3; i++) { TVectorD col = TMatrixDColumn(evectors, i); fEVec[i].SetXYZ(col[0], col[1], col[2]); } // check orientation of flow axis // by symmetry/convention, we allow FlowAngle between 0 & 90° i.e. flow vector always // points in the forward beam direction. if (fEVec[0].Theta() > TMath::PiOver2()) { fEVec[0] = -fEVec[0]; } // check we have an orthonormal basis // we choose evec[0]=>'z' evec[1]=>'y' evec[2]=>'x' // therefore we should have evec[2].Cross(evec[1])=evec[0] // if not we change the sign of evec[2] if (fEVec[2].Cross(fEVec[1]) != fEVec[0]) fEVec[2] = -fEVec[2]; // set up azimuthal rotation of CM axes around beam axis in order to put new 'X'-axis in reaction plane // we use the phi of the major axis (largest eigenvector) fAziReacPlane.SetToIdentity(); fAziReacPlane.RotateZ(e(1).Phi()); fFlowReacPlane.SetToIdentity(); fFlowReacPlane.RotateY(e(1).Theta()); fFlowReacPlane.RotateZ(e(1).Phi()); // calculate Gutbrod squeeze angle (PRC42(1990)640 // defined as the angle by which the middle eigenvector e2 // needs to be rotated around the e1 flow axis // in order to be brought in to the reaction plane TVector3 normReac = e(1).Cross(TVector3(0, 0, 1)); //normal to reaction plane: e1 x z Double_t angle = TMath::RadToDeg() * e(2).Angle(normReac); // on the contrary to Gutbrod et al, we define this angle between 0 and 90 degrees. // see Fig. 3 of paper: only 0-90 angles can be defined fSqueezeAngle = (angle < 90 ? 90 - angle : angle - 90); // now calculate the in-plane and out-of-plane flow according to Fig. 5 of Gutbrod et al Double_t a = pow(f(2), 0.5); //semi-axes of ellipsoid given by square roots of eigenvalues Double_t b = pow(f(3), 0.5); Double_t tan_t = -a / b * TMath::Tan(fSqueezeAngle * TMath::DegToRad()); Double_t t0 = TMath::ATan(tan_t); Double_t inPlane = a * cos(t0) * cos(fSqueezeAngle * TMath::DegToRad()) - b * sin(t0) * sin(fSqueezeAngle * TMath::DegToRad()); tan_t = a / (b * tan(fSqueezeAngle * TMath::DegToRad())); t0 = atan(tan_t); Double_t outOfPlane = a * cos(t0) * sin(fSqueezeAngle * TMath::DegToRad()) + b * sin(t0) * cos(fSqueezeAngle * TMath::DegToRad()); if (inPlane <= 0.) fSqOutRatio = -1.; else fSqOutRatio = TMath::Min(1.e+03, outOfPlane / inPlane); fCalculated = kTRUE; }
void solveLinear(Double_t eps = 1.e-12) { cout << "Perform the fit y = c0 + c1 * x in four different ways" << endl; const Int_t nrVar = 2; const Int_t nrPnts = 4; Double_t ax[] = {0.0,1.0,2.0,3.0}; Double_t ay[] = {1.4,1.5,3.7,4.1}; Double_t ae[] = {0.5,0.2,1.0,0.5}; // Make the vectors 'Use" the data : they are not copied, the vector data // pointer is just set appropriately TVectorD x; x.Use(nrPnts,ax); TVectorD y; y.Use(nrPnts,ay); TVectorD e; e.Use(nrPnts,ae); TMatrixD A(nrPnts,nrVar); TMatrixDColumn(A,0) = 1.0; TMatrixDColumn(A,1) = x; cout << " - 1. solve through Normal Equations" << endl; const TVectorD c_norm = NormalEqn(A,y,e); cout << " - 2. solve through SVD" << endl; // numerically preferred method // first bring the weights in place TMatrixD Aw = A; TVectorD yw = y; for (Int_t irow = 0; irow < A.GetNrows(); irow++) { TMatrixDRow(Aw,irow) *= 1/e(irow); yw(irow) /= e(irow); } TDecompSVD svd(Aw); Bool_t ok; const TVectorD c_svd = svd.Solve(yw,ok); cout << " - 3. solve with pseudo inverse" << endl; const TMatrixD pseudo1 = svd.Invert(); TVectorD c_pseudo1 = yw; c_pseudo1 *= pseudo1; cout << " - 4. solve with pseudo inverse, calculated brute force" << endl; TMatrixDSym AtA(TMatrixDSym::kAtA,Aw); const TMatrixD pseudo2 = AtA.Invert() * Aw.T(); TVectorD c_pseudo2 = yw; c_pseudo2 *= pseudo2; cout << " - 5. Minuit through TGraph" << endl; TGraphErrors *gr = new TGraphErrors(nrPnts,ax,ay,0,ae); TF1 *f1 = new TF1("f1","pol1",0,5); gr->Fit("f1","Q"); TVectorD c_graph(nrVar); c_graph(0) = f1->GetParameter(0); c_graph(1) = f1->GetParameter(1); // Check that all 4 answers are identical within a certain // tolerance . The 1e-12 is somewhat arbitrary . It turns out that // the TGraph fit is different by a few times 1e-13. Bool_t same = kTRUE; same &= VerifyVectorIdentity(c_norm,c_svd,0,eps); same &= VerifyVectorIdentity(c_norm,c_pseudo1,0,eps); same &= VerifyVectorIdentity(c_norm,c_pseudo2,0,eps); same &= VerifyVectorIdentity(c_norm,c_graph,0,eps); if (same) cout << " All solutions are the same within tolerance of " << eps << endl; else cout << " Some solutions differ more than the allowed tolerance of " << eps << endl; }