/* 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/map/MapCppTrackerTOFCombinedFit/MapCppTrackerTOFCombinedFit.hh" #include #include "src/common_cpp/API/PyWrapMapBase.hh" #include "src/common_cpp/Recon/Kalman/MAUSTrackWrapper.hh" #include "src/common_cpp/Recon/Global/MaterialModelAxialLookup.hh" #include "TMatrixD.h" #include "Geant4/G4Material.hh" namespace MAUS { PyMODINIT_FUNC init_MapCppTrackerTOFCombinedFit(void) { PyWrapMapBase::PyWrapMapBaseModInit ("MapCppTrackerTOFCombinedFit", "", "", "", ""); } MapCppTrackerTOFCombinedFit::MapCppTrackerTOFCombinedFit() : \ MapBase("MapCppTrackerTOFCombinedFit"), \ _radius_cut(5.0), _tof_resolution(.1), \ _has_tof(false), \ _helical_track_fitter(NULL) { } MapCppTrackerTOFCombinedFit::~MapCppTrackerTOFCombinedFit() { } void MapCppTrackerTOFCombinedFit::_birth(const std::string& argJsonConfigDocument) { // Pull out the global settings if ( !Globals::HasInstance() ) GlobalsManager::InitialiseGlobals(argJsonConfigDocument); Json::Value* json = Globals::GetConfigurationCards(); _radius_cut = (*json)["SciFiHelicalRadiusLimit"].asDouble(); _tof_resolution = .1; // [ns], could to smarter? Currently conservative (TODO) // Check that the TOFs are in the geometry, extract position of TOF1 std::vector tof_modules = Globals::GetReconstructionMiceModules()-> findModulesByPropertyString("SensitiveDetector", "TOF"); _has_tof = tof_modules.size() > 0; if ( !_has_tof ) Squeak::mout(Squeak::warning) << "Tracker-TOF Combined Fit Mapper is initialised but " << "there is no TOF in the reconstruction geometry"; bool found0(false), found1(false); for (const MiceModule* parent : tof_modules) { if ( parent->propertyInt("Station") == 0 ) { _tof0_z = parent->globalPosition().z(); found0 = true; } else if ( parent->propertyInt("Station") == 1 ) { _tof1_z = parent->globalPosition().z(); found1 = true; } } if ( !found0 || !found1 ) throw Exceptions::Exception(Exceptions::nonRecoverable, "MapCppTrackerTOFCombinedFit::_birth()", "Could not locate the TOF1 Detetector"); // Set the initial TOF1 position as the front of the detector double lower, upper; MaterialModelAxialLookup::GetBounds(_tof0_z, lower, upper); _tof0_z = lower; MaterialModelAxialLookup::GetBounds(_tof1_z, lower, upper); _tof1_z = lower; // Set up final track fit (Kalman filter) HelicalPropagator* helical_prop = new HelicalPropagator(Globals::GetSciFiGeometryHelper()); helical_prop->SetCorrectPz(true); helical_prop->SetIncludeMCS(true); helical_prop->SetSubtractELoss(true); _helical_track_fitter = new Kalman::TrackFit(helical_prop); // Each measurement plane has a unique alignment and rotation, // they all need their own measurement object. SciFiTrackerMap& geo_map = Globals::GetSciFiGeometryHelper()->GeometryMap(); for (std::pair tracker_geo : geo_map) { int tracker_const = (tracker_geo.first == 0 ? -1 : 1); for (std::pair plane_geo : tracker_geo.second.Planes) { int id = plane_geo.first * tracker_const; _helical_track_fitter->AddMeasurement(id, new MAUS::SciFiHelicalMeasurements(plane_geo.second)); if ( id == -1 ) _tku_z = plane_geo.second.GlobalPosition.Z(); if ( id == 1 ) _tkd_z = plane_geo.second.GlobalPosition.Z(); } } } void MapCppTrackerTOFCombinedFit::_death() { // Delete the Kalman fitter if it has been initialized if ( _helical_track_fitter ) { delete _helical_track_fitter; _helical_track_fitter = NULL; } } void MapCppTrackerTOFCombinedFit::_process(Data* data) const { // If there is no TOF module, skip if ( !_has_tof ) return; // Get the spill, check that it contains physics Spill& spill = *(data->GetSpill()); if ( spill.GetDaqEventType() != "physics_event" ) return; // Loop over the reconstructed events if ( spill.GetReconEvents() ) { for (unsigned int k = 0; k < spill.GetReconEvents()->size(); k++) { // Check that there is a TOF event and a SciFi event TOFEvent *tof_event = spill.GetReconEvents()->at(k)->GetTOFEvent(); SciFiEvent *scifi_event = spill.GetReconEvents()->at(k)->GetSciFiEvent(); if ( !tof_event || !scifi_event ) continue; // Check that there are SciFi tracks SciFiTrackPArray tracks = scifi_event->scifitracks(); bool has_tku(false), has_tkd(false); for (SciFiTrack* track : tracks) track->tracker() ? has_tkd = true : has_tku = true; if ( !has_tku && !has_tkd ) continue; // Calculate the momentum of the muon from its time-of-flight TOF0->1 double tof_p, tof_dp; if ( !calculate_tof_momentum(tof_event, tof_p, tof_dp) ) continue; // Propagate the momentum to the necessary tracker(s) double start(_tof1_z), end; double tku_p(tof_p), tku_dp(tof_dp), tkd_p(tof_p), tkd_dp(tof_dp); if ( has_tku ) { end = _tku_z; if ( !propagate_momentum(tku_p, tku_dp, start, end) ) continue; } if ( has_tkd ) { if ( has_tku ) { start = _tku_z; tkd_p = tku_p; tkd_dp = tku_dp; } end = _tkd_z; if ( !propagate_momentum(tkd_p, tkd_dp, start, end) ) // TODO ? (marginal) continue; } // Loop over the SciFi tracks SciFiTrackPArray new_tracks; for (SciFiTrack* track : tracks) { try { // Refit the tracks that need refitting (Poor fits and straight tracks) SciFiSeed* seed = track->scifi_seed(); double tk_p = track->tracker() ? tkd_p : tku_p; double tk_dp = track->tracker() ? tkd_dp : tku_dp; if ( seed->getAlgorithm() ) { // The current fit is a helical track if ( needs_refitting(seed) || track->is_dud() ) { update_seed(seed, tk_p); SciFiTrack* new_track = track_fit_helix(seed); new_track->SetWasRefit(1); new_tracks.push_back(new_track); delete track; } else { new_tracks.push_back(track); } } else { // The current fit is a staight track update_straight_seed(seed, tk_p); SciFiTrack* new_track = track_fit_helix(seed); new_track->SetWasRefit(2); new_tracks.push_back(new_track); delete track; } // Integrate the TOF information into the fit update_track(new_tracks.back(), tk_p, tk_dp); } catch (Exceptions::Exception& e) { std::cerr << "TOF information merge failed: " << e.what(); } } scifi_event->set_scifitracks(new_tracks); } } else { std::cout << "No recon events found\n"; } } bool MapCppTrackerTOFCombinedFit::calculate_tof_momentum(TOFEvent* tof_event, double& tof_p, double& tof_dp) const { // If there is not exactly 1 SP in each station, cannot reseed TOFEventSpacePoint* spacepoints = tof_event->GetTOFEventSpacePointPtr(); if ( spacepoints->GetTOF0SpacePointArraySize() != 1 || spacepoints->GetTOF1SpacePointArraySize() != 1 ) return false; // Get the time-of-flight TOFSpacePoint tof0 = spacepoints->GetTOF0SpacePointArrayElement(0); TOFSpacePoint tof1 = spacepoints->GetTOF1SpacePointArrayElement(0); double delta_t = tof1.GetTime() - tof0.GetTime(); // [ns] // Reconstruct the velocity, skip if too close to c or unphysical double beta = (_tof1_z - _tof0_z) / (delta_t * CLHEP::c_light); // c_light in [mm/ns] if ( beta > .98 || beta <= 0 ) return false; // Reconstruct the momentum, assuming muon species double gamma = 1.0 / sqrt(1.0 - beta*beta); tof_p = beta*gamma*Recon::Constants::MuonMass; // [MeV/c] // Evaluate the uncertainty on the momentum tof_dp = tof_p*gamma*gamma*_tof_resolution/delta_t; // [MeV/c] return true; } bool MapCppTrackerTOFCombinedFit::propagate_momentum(double& p, double& dp, double start, double end) const { // Get the total energy of the muon double muon_mass = Recon::Constants::MuonMass; double energy = std::sqrt(muon_mass*muon_mass + p*p); // Convert the uncertainty on the input momentum into an uncertainty on the energy double de = p*dp/energy; double estrag2 = 0.; // Perform the energy loss step-by-step double lower, upper; double start_position = start; double position = start; MaterialModelAxialLookup lookup(0, 0, position); double dEdx, dEstrag2dx; do { lookup.SetMaterial(0, 0, position); MaterialModelAxialLookup::GetBounds(position, lower, upper); if ( lower < start ) { start_position = start; } else { start_position = lower; } if ( upper > end ) { position = end; } else { position = upper; } dEdx = lookup.dEdx(energy, muon_mass, +1); dEstrag2dx = lookup.estrag2(energy, muon_mass, +1); energy += dEdx*(position-start_position); estrag2 += dEstrag2dx*(position-start_position); position += MaterialModelAxialLookup::GetZTolerance(); } while ( position < end && energy > muon_mass ); // If the energy is smaller than the muon mass, the muon has stopped, cannot proceed if ( energy <= muon_mass ) return false; // Evaluate the momentum from the energy p = std::sqrt(energy*energy - muon_mass*muon_mass); // Convert the total uncertainty on the energy into an uncertainty on the momentum de = sqrt(de*de + estrag2); dp = energy*de/p; return true; } bool MapCppTrackerTOFCombinedFit::needs_refitting(MAUS::SciFiSeed* seed) const { // Not sure this is the right way, rather make a cut on the uncertainty on pz? // A poor fit could produce a radius way larger than 150 for instance return static_cast(seed->getPRTrackTobject())->get_R() < _radius_cut; } void MapCppTrackerTOFCombinedFit::update_seed(MAUS::SciFiSeed* seed, double tof_p) const { // Get the existing seed, set pz to that measured using the TOF system TMatrixD vector = seed->getStateVector(); if (vector(4, 0) >= 0.0) { vector(4, 0) = 1.0 / tof_p; } else { vector(4, 0) = -1.0 / tof_p; } seed->setStateVector(vector); } void MapCppTrackerTOFCombinedFit::update_straight_seed(MAUS::SciFiSeed* seed, double tof_p) const { // Use the pz measured in the TOF system to produce a new seed entirely TMatrixD vector = seed->getStateVector(); TMatrixD new_vector(5, 1); new_vector(0, 0) = vector(0, 0); new_vector(1, 0) = 0.0; new_vector(2, 0) = vector(2, 0); new_vector(3, 0) = 0.0; if (seed->getTracker() == 0) { new_vector(4, 0) = 1.0 / tof_p; } else { new_vector(4, 0) = -1.0 / tof_p; } seed->setStateVector(new_vector); TMatrixD covariance = seed->getCovariance(); TMatrixD new_covariance(5, 5); for (unsigned int i = 0; i < 5; ++i) { for (unsigned int j = 0; j < 5; ++j) { new_covariance(i, j) = 0.0; } } new_covariance(0, 0) = 100.0; new_covariance(1, 1) = 100.0; new_covariance(2, 2) = 100.0; new_covariance(3, 3) = 100.0; new_covariance(4, 4) = 2.5e-7; seed->setCovariance(new_covariance); } SciFiTrack* MapCppTrackerTOFCombinedFit::track_fit_helix(SciFiSeed* seed) const { // Use the new seed to build a Kalman track SciFiHelicalPRTrack* helical = static_cast(seed->getPRTrackTobject()); Kalman::Track data_track = BuildTrack(helical, Globals::GetSciFiGeometryHelper(), 5); Kalman::State kalman_seed(seed->getStateVector(), seed->getCovariance()); _helical_track_fitter->SetTrack(data_track); _helical_track_fitter->SetSeed(kalman_seed); _helical_track_fitter->Filter(false); _helical_track_fitter->Smooth(false); SciFiTrack* track = ConvertToSciFiTrack(_helical_track_fitter, Globals::GetSciFiGeometryHelper(), helical); track->set_scifi_seed_tobject(seed); ThreeVector seed_pos = track->GetSeedPosition(); ThreeVector seed_mom = track->GetSeedMomentum(); return track; } bool MapCppTrackerTOFCombinedFit::update_track(MAUS::SciFiTrack* track, double tof_p, double tof_dp) const { // Get the momentum at the reference plane, find the weighted average MAUS::SciFiTrackPointPArray tps = track->scifitrackpoints(); double tk_p, tk_dp, tk_pz; bool found = false; for (const MAUS::SciFiTrackPoint *tpoint : tps ) if ( !tpoint->plane() && tpoint->station() == 1 ) { tk_p = tpoint->mom().mag(); tk_pz = tpoint->mom().z(); tk_dp = tpoint->mom_error().mag(); found = true; } if ( !found ) return false; double sum_of_weights = 1./(tof_dp*tof_dp)+1./(tk_dp*tk_dp); double weighted_p = (tof_p/(tof_dp*tof_dp)+tk_p/(tk_dp*tk_dp))/sum_of_weights; // Get the momentum correction double factor = weighted_p/tk_p; double corr = (factor-1.)*tk_pz; // Loop over the Kalman track planes, update the momentum vectors MAUS::ThreeVector new_mom; for (MAUS::SciFiTrackPoint *tpoint : tps) { new_mom = tpoint->mom(); new_mom.SetZ(tpoint->mom().z() + corr); tpoint->set_mom(new_mom); } return true; } } // ~namespace MAUS