/* 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
#include
#include "Geant4/G4Navigator.hh"
#include "Geant4/G4TransportationManager.hh"
#include "Geant4/G4NistManager.hh"
#include "gsl/gsl_odeiv.h"
#include "gsl/gsl_errno.h"
#include "src/common_cpp/DataStructure/ReconEvent.hh"
#include "src/common_cpp/Recon/Global/Particle.hh"
#include "src/common_cpp/Simulation/GeometryNavigator.hh"
#include "src/common_cpp/Simulation/MAUSGeant4Manager.hh"
#include "src/common_cpp/Utils/Exception.hh"
#include "src/common_cpp/Utils/Globals.hh"
#include "src/legacy/BeamTools/BTField.hh"
#include "src/legacy/Config/MiceModule.hh"
#include "src/common_cpp/Recon/Kalman/Global/CentroidTracking.hh"
#include "src/common_cpp/Recon/Global/GlobalTools.hh"
namespace MAUS {
namespace GlobalTools {
std::map
GetReconDetectors(GlobalEvent* global_event) {
std::map recon_detectors;
for (int i = 0; i < 27; i++) {
recon_detectors[static_cast(i)] =
false;
}
DataStructure::Global::TrackPArray* imported_tracks =
global_event->get_tracks();
for (auto imported_track_iter = imported_tracks->begin();
imported_track_iter != imported_tracks->end();
++imported_track_iter) {
std::vector track_detectors =
(*imported_track_iter)->GetDetectorPoints();
for (size_t i = 0; i < track_detectors.size(); i++) {
recon_detectors[track_detectors[i]] = true;
}
}
std::vector*
imported_spacepoints = global_event->get_space_points();
for (auto sp_iter = imported_spacepoints->begin();
sp_iter != imported_spacepoints->end();
++sp_iter) {
recon_detectors[(*sp_iter)->get_detector()] = true;
}
return recon_detectors;
}
std::vector* GetSpillDetectorTracks(Spill* spill,
DataStructure::Global::DetectorPoint detector, std::string mapper_name) {
std::vector* detector_tracks = new
std::vector;
ReconEventPArray* recon_events = spill->GetReconEvents();
if (recon_events) {
for (auto recon_event_iter = recon_events->begin();
recon_event_iter != recon_events->end();
++recon_event_iter) {
GlobalEvent* global_event = (*recon_event_iter)->GetGlobalEvent();
if (global_event) {
std::vector* global_tracks =
global_event->get_tracks();
for (auto track_iter = global_tracks->begin();
track_iter != global_tracks->end();
++track_iter) {
// The third condition is a bit of a dirty hack here to make sure that
// if we select for EMR tracks, we only get primaries.
if (((*track_iter)->HasDetector(detector)) and
((*track_iter)->get_mapper_name() == mapper_name) and
((*track_iter)->get_emr_range_secondary() < 0.001)) {
detector_tracks->push_back((*track_iter));
}
}
}
}
}
return detector_tracks;
}
std::vector* GetEventDetectorTracks(GlobalEvent* global_event,
DataStructure::Global::DetectorPoint detector, std::string mapper_name) {
std::vector* detector_tracks = new
std::vector;
if (global_event) {
std::vector* global_tracks =
global_event->get_tracks();
for (auto track_iter = global_tracks->begin();
track_iter != global_tracks->end();
++track_iter) {
// The third condition is a bit of a dirty hack here to make sure that
// if we select for EMR tracks, we only get primaries.
if (((*track_iter)->HasDetector(detector)) and
((*track_iter)->get_mapper_name() == mapper_name) and
((*track_iter)->get_emr_range_secondary() < 0.001)) {
detector_tracks->push_back((*track_iter));
}
}
}
return detector_tracks;
}
std::vector* GetSpillSpacePoints(
Spill* spill, DataStructure::Global::DetectorPoint detector) {
std::vector* spill_spacepoints =
new std::vector;
ReconEventPArray* recon_events = spill->GetReconEvents();
if (recon_events) {
for (auto recon_event_iter = recon_events->begin();
recon_event_iter != recon_events->end();
++recon_event_iter) {
GlobalEvent* global_event = (*recon_event_iter)->GetGlobalEvent();
if (global_event) {
std::vector* spacepoints =
global_event->get_space_points();
for (auto sp_iter = spacepoints->begin(); sp_iter != spacepoints->end();
++sp_iter) {
if ((*sp_iter)->get_detector() == detector) {
spill_spacepoints->push_back(*sp_iter);
}
}
}
}
}
if (spill_spacepoints->size() > 0) {
return spill_spacepoints;
} else {
delete spill_spacepoints;
return 0;
}
}
std::vector* GetEventSpacePoints(
GlobalEvent* global_event, DataStructure::Global::DetectorPoint detector) {
std::vector* spacepoints =
new std::vector;
if (global_event) {
std::vector* temp_spacepoints =
global_event->get_space_points();
for (auto sp_iter = temp_spacepoints->begin(); sp_iter != temp_spacepoints->end();
++sp_iter) {
if ((*sp_iter)->get_detector() == detector) {
spacepoints->push_back(*sp_iter);
}
}
}
if (spacepoints->size() > 0) {
return spacepoints;
} else {
delete spacepoints;
return 0;
}
}
std::vector* GetTracksByMapperName(
GlobalEvent* global_event, std::string mapper_name) {
std::vector* global_tracks =
global_event->get_tracks();
std::vector* selected_tracks = new
std::vector;
for (auto global_track_iter = global_tracks->begin();
global_track_iter != global_tracks->end();
++global_track_iter) {
if ((*global_track_iter)->get_mapper_name() == mapper_name) {
selected_tracks->push_back(*global_track_iter);
}
}
return selected_tracks;
}
std::vector* GetTracksByMapperName(
GlobalEvent* global_event, std::string mapper_name,
DataStructure::Global::PID pid) {
std::vector* global_tracks =
global_event->get_tracks();
std::vector* selected_tracks = new
std::vector;
for (auto global_track_iter = global_tracks->begin();
global_track_iter != global_tracks->end();
++global_track_iter) {
if ((*global_track_iter)->get_mapper_name() == mapper_name) {
if ((*global_track_iter)->get_pid() == pid) {
selected_tracks->push_back(*global_track_iter);
}
}
}
return selected_tracks;
}
std::vector GetTrackerPlane(const DataStructure::Global::TrackPoint*
track_point, std::vector z_positions) {
std::vector tracker_plane(3, 0);
double z = track_point->get_position().Z();
int plane = 100;
// First we get a plane number as an integer from 0 to 29 (counting from upstream)
for (size_t i = 0; i < z_positions.size(); i++) {
if (approx(z, z_positions[i], 0.25)) {
plane = i;
break;
}
}
if (plane < 15) {
// Tracker 0
tracker_plane[0] = 0;
tracker_plane[1] = 5 - plane/3;
tracker_plane[2] = 2 - plane%3;
} else if (plane < 30) {
// Tracker 1
tracker_plane[0] = 1;
tracker_plane[1] = plane/3 - 4;
tracker_plane[2] = plane%3;
} else {
// error output
tracker_plane[0] = 99;
}
return tracker_plane;
}
std::vector GetTrackerPlaneZPositions(std::string geo_filename) {
MiceModule* geo_module = new MiceModule(geo_filename);
std::vector tracker_planes =
geo_module->findModulesByPropertyString("SensitiveDetector", "SciFi");
std::vector z_positions;
for (size_t i = 0; i < tracker_planes.size(); i++) {
z_positions.push_back(tracker_planes.at(i)->globalPosition().getZ());
}
std::sort(z_positions.begin(), z_positions.end());
delete geo_module;
return z_positions;
}
bool approx(double a, double b, double tolerance) {
if (std::abs(a - b) > std::abs(tolerance)) {
return false;
} else {
return true;
}
}
DataStructure::Global::TrackPoint* GetNearestZTrackPoint(
const DataStructure::Global::Track* track, double z_position) {
std::vector trackpoints =
track->GetTrackPoints();
size_t nearest_index = 0;
double z_distance = 1.0e20;
for (size_t i = 0; i < trackpoints.size(); i++) {
double current_distance = std::abs(z_position -
trackpoints.at(i)->get_position().Z());
if (current_distance < z_distance) {
nearest_index = i;
z_distance = current_distance;
}
}
DataStructure::Global::TrackPoint* nearest_track_point = new
DataStructure::Global::TrackPoint(*trackpoints.at(nearest_index));
return nearest_track_point;
}
double dEdx(const G4Material* material, double E, double m) {
// Equation according to PDG 2014 eq. 32.5
// Converted constant so we can use electron number density instead of A/Z
double constant = 0.5*100*0.307075/Avogadro;
double m_e = 0.510998928;
double beta = std::sqrt(1 - (m*m)/(E*E));
double beta2 = beta*beta;
double gamma = 1/std::sqrt(1 - beta2);
double bg = beta*gamma;
double bg2 = bg*bg;
double mRatio = m_e/m;
double T_max = 2.0*m_e*bg2/(1.0 + 2.0*gamma*mRatio + mRatio*mRatio);
double n_e = material->GetElectronDensity();
double I = material->GetIonisation()->GetMeanExcitationEnergy();
double logterm = std::log(2.0*m_e*bg2*T_max/(I*I));
// density correction
double x = std::log(bg)/std::log(10);
double delta = material->GetIonisation()->DensityCorrection(x);
double dEdx = -constant*n_e/beta2*(logterm - 2*beta2 - delta);
return dEdx;
}
// Need some global variables here
static const BTField* _field;
static int _charge;
int propagate2(double* x, double target_z, const BTField* field,
double step_size, DataStructure::Global::PID pid,
int energy_loss, int geometry_algorithm) {
using Kalman::Global::CentroidTracking;
CentroidTracking tracking;
tracking.SetMaxStepSize(step_size);
// if pz was in backwards direction, should use em_backwards and bethe_bloch_backwards
tracking.SetEnergyLossModel(CentroidTracking::bethe_bloch_forwards);
tracking.SetTrackingModel(CentroidTracking::em_forwards_dynamic);
if (!energy_loss) {
tracking.SetEnergyLossModel(CentroidTracking::no_eloss);
}
if (geometry_algorithm == 1) {
tracking.SetGeometryModel(CentroidTracking::axial_lookup);
} else if (geometry_algorithm == 2) {
tracking.SetGeometryModel(CentroidTracking::geant4);
} else {
Squeak::mout(Squeak::warning) << "Did not recognise algorithm lookup" << std::endl;
return 1;
}
tracking.SetCharge(recon::global::Particle::GetInstance().GetCharge(pid));
tracking.Propagate(x, target_z);
return 0;
}
std::string x_dump(double* x) {
std::stringstream ios;
ios << " pos: " << x[0] << " " << x[1] << " " << x[2] << " " << x[3] << std::endl
<< " mom: " << x[4] << " " << x[5] << " " << x[6] << " " << x[7];
return ios.str();
}
int propagate(double* x, double target_z, const BTField* field,
double step_size, DataStructure::Global::PID pid,
int energy_loss, int algorithm) {
std::string event_in = x_dump(x);
if (std::abs(x[1]) > 700 or std::abs(x[2]) > 700 or std::abs(x[3]) > 50000) {
Squeak::mout(Squeak::debug) << "Bad input coordinates " << event_in << std::endl;
return 1;
}
if (std::abs(target_z) > 100000) {
Squeak::mout(Squeak::debug) << "Extreme target z "
<< target_z << std::endl;
return 1;
}
if (std::isnan(x[0]) || std::isnan(x[1]) || std::isnan(x[2]) || std::isnan(x[3]) ||
std::isnan(x[4]) || std::isnan(x[5]) || std::isnan(x[6]) || std::isnan(x[7]) ||
std::isinf(x[0]) || std::isinf(x[1]) || std::isinf(x[2]) || std::isinf(x[3]) ||
std::isinf(x[4]) || std::isinf(x[5]) || std::isinf(x[6]) || std::isinf(x[7])) {
Squeak::mout(Squeak::debug) << "Some components of tracker trackpoint are nan/inf\n"
<< event_in << std::endl;
return 1;
}
if (algorithm != 0) {
// we can do axial lookup etc here
return propagate2(x, target_z, field, step_size, pid, energy_loss, algorithm);
}
if (energy_loss != 0 and energy_loss != 1) {
Squeak::mout(Squeak::warning) << "Energy loss algorithm not available for "
<< "'geant4' geometry model" << std::endl;
return 1;
}
int prop_dir = 1;
_field = field;
_charge = recon::global::Particle::GetInstance().GetCharge(pid);
double mass = recon::global::Particle::GetInstance().GetMass(pid);
G4Navigator* g4navigator = G4TransportationManager::GetTransportationManager()
->GetNavigatorForTracking();
bool backwards = false;
// If we propagate backwards, reverse momentum 4-vector
if (target_z < x[3]) {
backwards = true;
_charge *= -1;
prop_dir = -1;
for (size_t i = 4; i < 8; i++) {
x[i] *= -1;
}
}
const gsl_odeiv_step_type * T = gsl_odeiv_step_rk4;
double absolute_error = (*Globals::GetInstance()->GetConfigurationCards())
["field_tracker_absolute_error"].asDouble();
double relative_error = (*Globals::GetInstance()->GetConfigurationCards())
["field_tracker_relative_error"].asDouble();
gsl_odeiv_step * step = gsl_odeiv_step_alloc(T, 8);
gsl_odeiv_control * control = gsl_odeiv_control_y_new(absolute_error,
relative_error);
gsl_odeiv_evolve * evolve = gsl_odeiv_evolve_alloc(8);
int (*FuncEqM)(double z, const double y[], double f[], void *params)=NULL;
FuncEqM = z_equations_of_motion;
gsl_odeiv_system system = {FuncEqM, NULL, 8, NULL};
double h = step_size*prop_dir;
size_t max_steps = 10000000;
size_t n_steps = 0;
double z = x[3];
// The next 2 lines take ~8ms to execute, perhaps consider moving these outside
// of this function somehow?
GeometryNavigator* geometry_navigator = Globals::GetMCGeometryNavigator();
int return_value = 0;
while (fabs(z - target_z) > 1e-6) {
n_steps++;
h = step_size*prop_dir; // revert step size as large step size problematic for dEdx
int status;
if (energy_loss == 1) {
const CLHEP::Hep3Vector posvector(x[1], x[2], x[3]);
double mommag = std::sqrt(x[5]*x[5] + x[6]*x[6] + x[7]*x[7]);
const CLHEP::Hep3Vector momvector(x[5]/mommag, x[6]/mommag, x[7]/mommag);
g4navigator->LocateGlobalPointAndSetup(posvector, &momvector);
double safety = std::fabs(step_size);
double boundary_dist = g4navigator->ComputeStep(posvector, momvector, h, safety);
if (boundary_dist > 1e6) {
boundary_dist = safety;
}
double z_dist = boundary_dist*momvector.z();
// We need to check if we're in a material with high stopping power, and if yes,
// decrease the step size as otherwise the underestimated path length due to the
// curved trajectory will cause problems
double n_e = geometry_navigator->GetMaterial()->GetElectronDensity();
double max_h = 100.0;
if (n_e > 1e+18) {
max_h = 1.0*prop_dir;
}
// Check if z distance to next material boundary is smaller than step size
// if yes, we impose a tight limit on the step size to avoid issues
// arising from the track not being straight
if (std::abs(z_dist) > 0.00000000001 and
std::abs(z_dist) < std::abs(h)) {
if (std::abs(z_dist) > 1.0) {
h = 0.9*z_dist;
} else {
h = z_dist; // will have proper sign from momvector
}
}
if (std::abs(h) > std::abs(max_h)) {
h = max_h;
}
// Making sure we don't get stuck in Zeno's paradox
if (std::abs(h) < 0.1) {
h = 0.1*prop_dir;
}
double x_prev[] = {x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]};
status = gsl_odeiv_evolve_apply(evolve, control, step, &system, &z,
target_z, &h, x);
// Calculate energy loss for the step
geometry_navigator->SetPoint(ThreeVector((x[1] + x_prev[1])/2,
(x[2] + x_prev[2])/2, (x[3] + x_prev[3])/2));
double step_distance = std::sqrt((x[1]-x_prev[1])*(x[1]-x_prev[1]) +
(x[2]-x_prev[2])*(x[2]-x_prev[2]) +
(x[3]-x_prev[3])*(x[3]-x_prev[3]));
G4Material* material = geometry_navigator->GetMaterial();
double step_energy_loss = dEdx(material, x[4], mass)*step_distance;
changeEnergy(x, step_energy_loss, mass);
} else {
status = gsl_odeiv_evolve_apply(evolve, control, step, &system, &z,
target_z, &h, x);
}
if (status != GSL_SUCCESS) {
Squeak::mout(Squeak::debug) << "Propagation failed from\n"
<< event_in << std::endl << x_dump(x) << std::endl;
return_value = 1;
} else if (n_steps > max_steps) {
Squeak::mout(Squeak::debug) << "Exceeded maximum number of steps\n"
<< event_in << std::endl << x_dump(x) << std::endl;
return_value = 1;
} else if (std::abs(x[1]) > 700 || std::abs(x[2]) > 700) {
// Terminate if the propagation moves too far from the beamline (should be lost to us anyway)
// Appears to fix an irregular and so far not fully explained segfault.
Squeak::mout(Squeak::debug) << "Out of beam pipe\n"
<< event_in << std::endl << x_dump(x) << std::endl;
return_value = 1;
} else if (std::abs(x[4]) < (mass + 0.1) or std::abs(x[7]) < 0.01) {
// Need to catch the case where the particle is stopped
Squeak::mout(Squeak::debug) << "Terminated with 0 momentum\n"
<< event_in << std::endl << x_dump(x) << std::endl;
return_value = 1;
} else if (std::abs(x[4]) > 1000) {
// When propagating backwards, can get caught in material and massively increase energy
Squeak::mout(Squeak::debug) << "Too much energy growth\n"
<< event_in << std::endl << x_dump(x) << std::endl;
return_value = 1;
}
}
gsl_odeiv_evolve_free(evolve);
gsl_odeiv_control_free(control);
gsl_odeiv_step_free(step);
// If we propagate backwards, reverse momentum 4-vector back to original sign
if (backwards) {
for (size_t i = 4; i < 8; i++) {
x[i] *= -1;
}
}
return return_value;
}
int z_equations_of_motion(double z, const double x[8], double dxdz[8],
void* params) {
if (fabs(x[7]) < 1e-9) {
// z-momentum is 0
return GSL_ERANGE;
}
const double c_l = 299.792458; // mm*ns^{-1}
double field[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
double xfield[4] = {x[1], x[2], x[3], x[0]};
_field->GetFieldValue(xfield, field);
double dtdz = x[4]/x[7];
double dir = fabs(x[7])/x[7]; // direction of motion
dxdz[0] = dtdz/c_l; // dt/dz
dxdz[1] = x[5]/x[7]; // dx/dz = px/pz
dxdz[2] = x[6]/x[7]; // dy/dz = py/pz
dxdz[3] = 1.0; // dz/dz
// dE/dz only contains electric field as B conserves energy, not relevant at
// least in step 4 as all fields are static.
dxdz[4] = (dxdz[1]*_charge*field[3] + dxdz[2]*_charge*field[4] +
_charge*field[5])*dir; // dE/dz
// dpx/dz = q*c*(dy/dz*Bz - dz/dz*By) + q*Ex*dt/dz
dxdz[5] = _charge*c_l*(dxdz[2]*field[2] - dxdz[3]*field[1])
+ _charge*field[3]*dtdz*dir; // dpx/dz
dxdz[6] = _charge*c_l*(dxdz[3]*field[0] - dxdz[1]*field[2])
+ _charge*field[4]*dtdz*dir; // dpy/dz
dxdz[7] = _charge*c_l*(dxdz[1]*field[1] - dxdz[2]*field[0])
+ _charge*field[5]*dtdz*dir; // dpz/dz
return GSL_SUCCESS;
}
void changeEnergy(double* x, double deltaE, double mass) {
if (std::isnan(deltaE)) {
deltaE = -x[4];
}
double old_momentum = std::sqrt(x[5]*x[5] + x[6]*x[6] + x[7]*x[7]);
x[4] += deltaE;
double new_momentum, momentum_ratio;
if (std::abs(x[4]) > mass) {
new_momentum = std::sqrt(x[4]*x[4] - mass*mass);
} else {
new_momentum = 0.0;
}
momentum_ratio = new_momentum / old_momentum;
x[5] *= momentum_ratio;
x[6] *= momentum_ratio;
x[7] *= momentum_ratio;
}
bool TrackPointSort(const DataStructure::Global::TrackPoint* tp1,
const DataStructure::Global::TrackPoint* tp2) {
return (tp1->get_position().Z() < tp2->get_position().Z());
}
std::vector getVirtualZPlanes() {
Squeak::mout(Squeak::debug) << "Virtual Planes for track matching z: " << std::flush;
VirtualPlaneManager* man = Globals::GetGeant4Manager()->GetVirtualPlanes();
std::vector planeVec = man->GetPlanes();
std::vector virtualZPlanes;
for (size_t i = 0; i < planeVec.size(); ++i) {
Squeak::mout(Squeak::debug) << planeVec[i]->GetPlaneIndependentVariable() << " ";
// we only want the virtual planes that are slices in z (it is also
// possible to define slices in time and rotated planes)
if (planeVec[i]->GetPlaneIndependentVariableType() == BTTracker::z) {
double z = planeVec[i]->GetPlaneIndependentVariable();
virtualZPlanes.push_back(z);
}
}
Squeak::mout(Squeak::debug) << std::endl;
return virtualZPlanes;
}
} // ~namespace GlobalTools
} // ~namespace MAUS