/* This file is part of MAUS: http://micewww.pp.rl.ac.uk:8080/projects/maus * * MAUS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MAUS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MAUS. If not, see . * */ #include "src/common_cpp/Recon/Kalman/KalmanPropagator.hh" #include "src/common_cpp/Utils/Globals.hh" #include "src/common_cpp/Utils/Constants.hh" // TODO: add straggling to propagation. (optional) namespace MAUS { KalmanPropagator::KalmanPropagator() : _n_parameters(0) { Json::Value *json = Globals::GetConfigurationCards(); _use_MCS = (*json)["SciFiKalman_use_MCS"].asBool(); _use_Eloss = (*json)["SciFiKalman_use_Eloss"].asBool(); // Set Fibre Parameters. FibreParameters.Z = (*json)["SciFiParams_Z"].asDouble(); FibreParameters.Plane_Width = (*json)["SciFiParams_Plane_Width"].asDouble(); FibreParameters.Radiation_Legth= (*json)["SciFiParams_Radiation_Legth"].asDouble(); FibreParameters.Density = (*json)["SciFiParams_Density"].asDouble(); FibreParameters.Mean_Excitation_Energy = (*json)["SciFiParams_Mean_Excitation_Energy"].asDouble(); FibreParameters.A = (*json)["SciFiParams_A"].asDouble(); FibreParameters.Pitch = (*json)["SciFiParams_Pitch"].asDouble(); FibreParameters.Station_Radius = (*json)["SciFiParams_Station_Radius"].asDouble(); FibreParameters.RMS = (*json)["SciFiParams_RMS"].asDouble(); AirParameters.Z = (*json)["AirParams_Z"].asDouble(); AirParameters.Radiation_Legth = (*json)["AirParams_Radiation_Legth"].asDouble(); AirParameters.Density = (*json)["AirParams_Density"].asDouble(); AirParameters.Mean_Excitation_Energy = (*json)["AirParams_Mean_Excitation_Energy"].asDouble(); AirParameters.A = (*json)["AirParams_A"].asDouble(); } void KalmanPropagator::Extrapolate(KalmanStatesPArray sites, int i) { // Get current site... KalmanState *new_site = sites.at(i); // ... and the site we will extrapolate from. const KalmanState *old_site = sites.at(i-1); // Calculate prediction for the state vector. CalculatePredictedState(old_site, new_site); // Calculate the energy loss for the projected state. if ( _n_parameters == 5 && _use_Eloss ) SubtractEnergyLoss(old_site, new_site); // Calculate the system noise... if ( _use_MCS ) CalculateSystemNoise(old_site, new_site); // ... so that we can add it to the prediction for the // covariance matrix. CalculateCovariance(old_site, new_site); new_site->set_current_state(KalmanState::Projected); } // C_proj = _F * C_old * _Ft + _Q; void KalmanPropagator::CalculateCovariance(const KalmanState *old_site, KalmanState *new_site) { TMatrixD C_old = old_site->covariance_matrix(KalmanState::Filtered); TMatrixD F_transposed(_n_parameters, _n_parameters); F_transposed.Transpose(_F); TMatrixD C_new(_n_parameters, _n_parameters); C_new = _F*C_old*F_transposed + _Q; new_site->set_covariance_matrix(C_new, KalmanState::Projected); } // Returns (beta) * (-dE/dx). Formula and constants from PDG. double KalmanPropagator::BetheBlochStoppingPower(double p) { double muon_mass = Recon::Constants::MuonMass; double electron_mass = Recon::Constants::ElectronMass; double muon_mass2 = muon_mass*muon_mass; double E = TMath::Sqrt(muon_mass2+p*p); double beta = p/E; double beta2= beta*beta; double gamma = E/muon_mass; double gamma2= gamma*gamma; double K = Recon::Constants::BetheBlochParameters::K(); double A = FibreParameters.A; double I = FibreParameters.Mean_Excitation_Energy; double I2= I*I; double Z = FibreParameters.Z; double outer_term = K*Z/(A*beta2); double Tmax = 2.*electron_mass*beta2*gamma2/(1.+(2.*gamma*electron_mass/muon_mass) + (electron_mass*electron_mass/(muon_mass*muon_mass))); double log_term = TMath::Log(2.*electron_mass*beta2*gamma2*Tmax/(I2)); double last_term = Tmax*Tmax/(gamma2*muon_mass2); double density = FibreParameters.Density; double plasma_energy = 28.816*TMath::Sqrt(density*Z/A); // eV double density_term = TMath::Log(plasma_energy/I)+TMath::Log(beta*gamma)-0.5; double dEdx = outer_term*(0.5*log_term-beta2-density_term/2.+last_term/8.); return beta*dEdx; } void KalmanPropagator::SubtractEnergyLoss(const KalmanState *old_site, KalmanState *new_site) { // // Get the momentum vector to be corrected. TMatrixD a_old(_n_parameters, 1); a_old = new_site->a(KalmanState::Projected); double kappa = a_old(4, 0); double px = a_old(1, 0)/kappa; double py = a_old(3, 0)/kappa; double pz = 1./fabs(kappa); int sign = static_cast (kappa/fabs(kappa)); ThreeVector old_momentum(px, py, pz); // // Compute the correction using Bethe Bloch's formula. double plane_width = FibreParameters.Plane_Width; double Delta_p = BetheBlochStoppingPower(old_momentum.mag())*plane_width; // // Reduce/increase momentum vector accordingly. // Indeed, for tracker 0, we want to ADD energy // beause we are not following the energy loss. if ( old_site->id() < 0 ) { Delta_p = -Delta_p; } double reduction_factor = (old_momentum.mag()-Delta_p)/old_momentum.mag(); ThreeVector new_momentum = old_momentum*reduction_factor; // // Update momentum estimate at the site. TMatrixD a_subtracted(_n_parameters, 1); a_subtracted = new_site->a(KalmanState::Projected); a_subtracted(1, 0) = new_momentum.x()/new_momentum.z(); a_subtracted(3, 0) = new_momentum.y()/new_momentum.z(); a_subtracted(4, 0) = sign/new_momentum.z(); new_site->set_a(a_subtracted, KalmanState::Projected); } void KalmanPropagator::CalculateSystemNoise(const KalmanState *old_site, const KalmanState *new_site) { // Define fibre material parameters. double plane_width = FibreParameters.Plane_Width; int numb_planes = fabs(new_site->id() - old_site->id()); double total_plane_length = numb_planes*plane_width; // Plane lenght in units of radiation lenght (~0.0015 per plane). double plane_L0 = FibreParameters.R0(total_plane_length); // Get particle's parameters (gradients and total momentum). TMatrixD a = new_site->a(KalmanState::Projected); double mx = a(1, 0); double my = a(3, 0); double p = GetTrackMomentum(old_site); // Compute the fibre effect. TMatrixD Q1(_n_parameters, _n_parameters); Q1 = BuildQ(plane_L0, total_plane_length, mx, my, p); // Compute Air effect (if necessary). TMatrixD Q2(_n_parameters, _n_parameters); double deltaZ = new_site->z() - old_site->z(); // Check if we need to add propagation in air. if ( deltaZ > total_plane_length ) { double air_lenght = deltaZ-total_plane_length; double air_L0 = AirParameters.R0(air_lenght); Q2 = BuildQ(air_L0, air_lenght, mx, my, p); } _Q = Q1+Q2; } TMatrixD KalmanPropagator::BuildQ(double L0, double deltaZ, double mx, double my, double p) { double deltaZ_squared = deltaZ*deltaZ; double muon_mass = Recon::Constants::MuonMass; double muon_mass2 = muon_mass*muon_mass; double E = TMath::Sqrt(muon_mass2+p*p); double beta = p/E; double C = HighlandFormula(L0, beta, p); double C2 = C*C; double c_mx_mx = C2 * (1. + mx*mx) * (1.+ mx*mx + my*my); double c_my_my = C2 * (1. + my*my) * (1.+ mx*mx + my*my); double c_mx_my = C2 * mx*my * (1.+ mx*mx + my*my); TMatrixD Q(_n_parameters, _n_parameters); // x x Q(0, 0) = deltaZ_squared*c_mx_mx; // x mx Q(0, 1) = deltaZ*c_mx_mx; // x y Q(0, 2) = deltaZ_squared*c_mx_my; // x my Q(0, 3) = deltaZ*c_mx_my; // mx x Q(1, 0) = deltaZ*c_mx_mx; // mx mx Q(1, 1) = c_mx_mx; // mx y Q(1, 2) = deltaZ*c_mx_my; // mx my Q(1, 3) = c_mx_my; // y x Q(2, 0) = deltaZ_squared*c_mx_my; // y mx Q(2, 1) = deltaZ*c_mx_my; // y y Q(2, 2) = deltaZ_squared*c_my_my; // y my Q(2, 3) = deltaZ*c_my_my; // my x Q(3, 0) = deltaZ*c_mx_my; // my mx Q(3, 1) = c_mx_my; // my y Q(3, 2) = deltaZ*c_my_my; // my my Q(3, 3) = c_my_my; return Q; } double KalmanPropagator::HighlandFormula(double L0, double beta, double p) { double HighlandConstant = Recon::Constants::HighlandConstant; // Note that the z factor (charge of the incoming particle) is omitted. // We don't need to consider |z| > 1. double result = HighlandConstant*TMath::Sqrt(L0)*(1.+0.038*TMath::Log(L0))/(beta*p); return result; } //////////////// void KalmanPropagator::PrepareForSmoothing(KalmanStatesPArray sites) { KalmanState *last_site = sites.back(); TMatrixD a_smooth = last_site->a(KalmanState::Filtered); last_site->set_a(a_smooth, KalmanState::Smoothed); TMatrixD C_smooth = last_site->covariance_matrix(KalmanState::Filtered); last_site->set_covariance_matrix(C_smooth, KalmanState::Smoothed); TMatrixD residual(2, 1); residual = last_site->residual(KalmanState::Filtered); last_site->set_residual(residual, KalmanState::Smoothed); last_site->set_current_state(KalmanState::Smoothed); // Set smoothed chi2. double f_chi2 = last_site->chi2(KalmanState::Filtered); last_site->set_chi2(f_chi2, KalmanState::Smoothed); } void KalmanPropagator::Smooth(KalmanStatesPArray sites, int id) { // Get site to be smoothed... KalmanState *smoothing_site = sites.at(id); // ... and the already perfected site. const KalmanState *optimum_site = sites.at(id+1); // Set the propagator right. UpdatePropagator(optimum_site, smoothing_site); // Compute A_k. UpdateBackTransportationMatrix(optimum_site, smoothing_site); // Compute smoothed a_k and C_k SmoothBack(optimum_site, smoothing_site); smoothing_site->set_current_state(KalmanState::Smoothed); } void KalmanPropagator::UpdateBackTransportationMatrix(const KalmanState *optimum_site, const KalmanState *smoothing_site) { UpdatePropagator(smoothing_site, optimum_site); TMatrixD Cp(_n_parameters, _n_parameters); Cp = optimum_site->covariance_matrix(KalmanState::Projected); Cp.Invert(); TMatrixD C(_n_parameters, _n_parameters); C = smoothing_site->covariance_matrix(KalmanState::Filtered); TMatrixD _F_transposed(_n_parameters, _n_parameters); _F_transposed.Transpose(_F); _A = C*_F_transposed*Cp; } void KalmanPropagator::SmoothBack(const KalmanState *optimum_site, KalmanState *smoothing_site) { // Set smoothed state. TMatrixD a = smoothing_site->a(KalmanState::Filtered); TMatrixD a_opt = optimum_site->a(KalmanState::Smoothed); TMatrixD ap = optimum_site->a(KalmanState::Projected); TMatrixD a_smooth = a + _A* (a_opt - ap); smoothing_site->set_a(a_smooth, KalmanState::Smoothed); // Set the smoothed covariance matrix. TMatrixD C = smoothing_site->covariance_matrix(KalmanState::Filtered); TMatrixD C_opt = optimum_site->covariance_matrix(KalmanState::Smoothed); TMatrixD Cp = optimum_site->covariance_matrix(KalmanState::Projected); TMatrixD temp1 = _A*(C_opt - Cp); TMatrixD temp2 = TMatrixD(temp1, TMatrixD::kMultTranspose, _A); TMatrixD C_smooth = C+temp2; smoothing_site->set_covariance_matrix(C_smooth, KalmanState::Smoothed); } } // ~namespace MAUS