R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
ElasticScattering.cxx
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) 2019 GSI Helmholtzzentrum für Schwerionenforschung GmbH *
3 * Copyright (C) 2019-2025 Members of R3B Collaboration *
4 * *
5 * This software is distributed under the terms of the *
6 * GNU General Public Licence (GPL) version 3, *
7 * copied verbatim in the file "LICENSE". *
8 * *
9 * In applying this license GSI does not waive the privileges and immunities *
10 * granted to it by virtue of its status as an Intergovernmental Organization *
11 * or submit itself to any jurisdiction. *
12 ******************************************************************************/
13
14#include "ElasticScattering.h"
15#include "TVector3.h"
16#include <cmath>
17
18using std::pow;
19using std::sqrt;
20
21namespace Neuland
22{
23 auto RecoilProtonEnergy(const R3BNeulandCluster& cluster) -> double
24 {
25 // Range of the proton in material is proportional to its energy E(R) = aR^b
26 // Here, the "energy moment" is used as a more robust representation of the neutron track length
27 // Values for a and b fitted to simulated data by shooting protons at NeuLAND
28 const double a = 55.629;
29 const double b = 0.652103;
30 return a * std::pow(cluster.GetEnergyMoment(), b);
31 }
32
33 auto RecoilScatteringAngle(const R3BNeulandCluster& cluster) -> double
34 {
35 const TVector3 pNUnit = cluster.GetFirstHit().GetPosition().Unit();
36 const TVector3 pp_Unit = (cluster.GetEnergyCentroid() - cluster.GetFirstHit().GetPosition()).Unit();
37 const double cosTheta = pNUnit.Dot(pp_Unit);
38 return cosTheta;
39 }
40
41 auto ScatteredNeutronEnergy(const R3BNeulandCluster& first, const R3BNeulandCluster& second) -> double
42 {
43 static constexpr auto neutron_mass = 938.; // Rest Mass Neutron [MeV]
44 static constexpr auto light_speed_sq = 898.75517873681758374; // cm²/ns²
45
46 const TVector3 first_hit_distance = second.GetFirstHit().GetPosition() - first.GetFirstHit().GetPosition();
47 const double time_diff = second.GetT() - first.GetT();
48
49 const double velocity_sq = first_hit_distance.Mag2() / std::pow(time_diff, 2); // cm²/ns²
50 if (velocity_sq > light_speed_sq ||
51 velocity_sq > first.GetFirstHit().GetPosition().Mag2() / std::pow(first.GetT(), 2))
52 {
53 return 0;
54 }
55
56 const double gamma = 1. / std::sqrt(1. - (velocity_sq / light_speed_sq));
57 const double kinetic_energy = (gamma - 1.) * neutron_mass;
58 return kinetic_energy;
59 }
60
61 auto ScatteredNeutronAngle(const R3BNeulandCluster& first, const R3BNeulandCluster& second) -> double
62 {
63 const TVector3 pNUnit = first.GetFirstHit().GetPosition().Unit();
64 const TVector3 pN_Unit = (second.GetFirstHit().GetPosition() - first.GetFirstHit().GetPosition()).Unit();
65 const double cosTheta = pNUnit.Dot(pN_Unit); // \cos(\theta_{NN'})
66 return cosTheta;
67 }
68
70 {
71 const double minimalProtonKineticEnergy = 0.; // MeV
72 // E0n: Rest mass proton/neutron
73 // En: Total energy of incoming neutron
74 // Ep_: Total energy of scattered proton
75 // Ep_Kin: Kinetic energy of scattered proton
76 // pp_: Momentum Vector of scattered proton
77 // pn: Momentum Vector of incoming neutron
78 // cosTheta: Angle between incoming neutron and scattered proton
79
80 // Use the Energy Moment of the cluster to determine the proton kinetic energy.
81 const double Ep_Kin = RecoilProtonEnergy(cluster);
82 if (Ep_Kin < minimalProtonKineticEnergy)
83 {
84 // If the kinetic energy of the proton does not match, this method doesn't work
85 return 0;
86 }
87
88 // Rest Mass of Proton/Neutron [MeV]
89 const double E0n = 938.; // MeV
90 const double Ep_ = E0n + Ep_Kin;
91
92 const double cosTheta = RecoilScatteringAngle(cluster);
93
94 /*if (pp_Unit.Z() < 0. || cosTheta < 0.)
95 {
96 // No scattering in backward directions
97 // No scattering angle > 90°
98 return 0;
99 }*/
100
101 // From Four-Momentum conservation one can deduce:
102 // \frac{E_p' + E0n}{E_p' - E0n} \cos^2(\theta_{np'}) = \frac{E_N + E0n}{E_N - E0n}
103 const double a = cosTheta * cosTheta * (Ep_ + E0n) / (Ep_ - E0n);
104 const double En = E0n * ((a + 1.) / (a - 1.));
105
106 // return EKin
107 return En - E0n;
108 }
109
111 const R3BNeulandCluster& second,
112 double target_mass) -> double
113 {
114 const double neutron_m = 938.; // Rest Mass Neutron [MeV]
115
116 const double scattered_neutron_KE = ScatteredNeutronEnergy(first, second) + neutron_m;
117 // \cos(\theta_{NN'})
118 const double scattered_angle = ScatteredNeutronAngle(first, second);
119
120 // Formula for En. Yes its long, no the pows do not make it slow
121 const double neutron_energy =
122 (sqrt((pow(scattered_angle, 4) * pow(scattered_neutron_KE, 4) * pow(neutron_m, 2)) -
123 (2 * pow(scattered_angle, 4) * pow(scattered_neutron_KE, 2) * pow(neutron_m, 4)) +
124 (pow(scattered_angle, 4) * pow(neutron_m, 6)) +
125 (pow(scattered_angle, 2) * pow(scattered_neutron_KE, 4) * pow(target_mass, 2)) -
126 (pow(scattered_angle, 2) * pow(scattered_neutron_KE, 4) * pow(neutron_m, 2)) -
127 (2 * pow(scattered_angle, 2) * pow(scattered_neutron_KE, 2) * pow(target_mass, 2) *
128 pow(neutron_m, 2)) +
129 (2 * pow(scattered_angle, 2) * pow(scattered_neutron_KE, 2) * pow(neutron_m, 4)) +
130 (pow(scattered_angle, 2) * pow(target_mass, 2) * pow(neutron_m, 4)) -
131 (pow(scattered_angle, 2) * pow(neutron_m, 6))) +
132 pow(scattered_neutron_KE, 2) * target_mass - scattered_neutron_KE * pow(target_mass, 2) -
133 scattered_neutron_KE * pow(neutron_m, 2) + target_mass * pow(neutron_m, 2)) /
134 (pow(scattered_angle, 2) * pow(scattered_neutron_KE, 2) - pow(scattered_angle, 2) * pow(neutron_m, 2) -
135 pow(scattered_neutron_KE, 2) + 2 * scattered_neutron_KE * target_mass - pow(target_mass, 2));
136
137 return neutron_energy - neutron_m;
138 }
139
141 const R3BNeulandCluster& second,
142 const double targetMass) -> double
143 {
144 const double E0n = 938.;
145 const double E0k = targetMass;
146
147 const double En = first.GetFirstHit().GetEToF() + E0n;
148 const double En_ = ScatteredNeutronEnergy(first, second) + E0n;
149 const double Ek_ = first.GetE() + targetMass;
150 const double cosTheta = ScatteredNeutronAngle(first, second);
151
152 const double zero = (En_ * En_) + (En_ * Ek_) - (En * E0k) - (E0n * E0n) -
153 (sqrt((En_ * En_ - E0n * E0n) * (En * En - E0n * E0n)) * cosTheta);
154 return zero;
155 }
156
157 auto ElasticScatteringTargetMass(const R3BNeulandCluster& first, const R3BNeulandCluster& second) -> double
158 {
159 const double E0n = 938.;
160
161 const double En = first.GetFirstHit().GetEToF() + E0n;
162 const double En_ = ScatteredNeutronEnergy(first, second) + E0n;
163 const double Ek_kin = first.GetE();
164 const double cosTheta = ScatteredNeutronAngle(first, second);
165
166 const double E0k =
167 (En_ * En_ - E0n * E0n - sqrt((En_ * En_ - E0n * E0n) * (En * En - E0n * E0n)) * cosTheta + En_ * Ek_kin) /
168 (En - En_);
169
170 return E0k;
171 }
172} // namespace Neuland
Simulation of NeuLAND Bar/Paddle.
auto NeutronEnergyFromElasticProtonScattering(const R3BNeulandCluster &cluster) -> double
auto ElasticScatteringTargetMass(const R3BNeulandCluster &first, const R3BNeulandCluster &second) -> double
auto NeutronEnergyFromElasticScattering(const R3BNeulandCluster &first, const R3BNeulandCluster &second, double target_mass) -> double
Calculate neutron kinetic energy from the elastic scattering.
auto ScatteredNeutronEnergy(const R3BNeulandCluster &first, const R3BNeulandCluster &second) -> double
Calculate neutron kinetic energy from the scattering.
auto ScatteredNeutronAngle(const R3BNeulandCluster &first, const R3BNeulandCluster &second) -> double
Calculate the scattering angle of the neutron.
auto RecoilProtonEnergy(const R3BNeulandCluster &cluster) -> double
auto RecoilScatteringAngle(const R3BNeulandCluster &cluster) -> double
auto MaybeElasticScattering(const R3BNeulandCluster &first, const R3BNeulandCluster &second, const double targetMass) -> double