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 "R3BNeulandCluster.h"
16#include "TVector3.h"
17#include <cmath>
18
19using std::pow;
20using std::sqrt;
21
22namespace Neuland
23{
24 auto RecoilProtonEnergy(const R3BNeulandCluster& cluster) -> double
25 {
26 // Range of the proton in material is proportional to its energy E(R) = aR^b
27 // Here, the "energy moment" is used as a more robust representation of the neutron track length
28 // Values for a and b fitted to simulated data by shooting protons at NeuLAND
29 const double a_val = 55.629;
30 const double b_val = 0.652103;
31 return a_val * std::pow(cluster.GetEnergyMoment(), b_val);
32 }
33
34 auto RecoilScatteringAngle(const R3BNeulandCluster& cluster) -> double
35 {
36 const TVector3 pNUnit = cluster.GetFirstHit().GetPosition().Unit();
37 const TVector3 pp_Unit = (cluster.GetEnergyCentroid() - cluster.GetFirstHit().GetPosition()).Unit();
38 const double cosTheta = pNUnit.Dot(pp_Unit);
39 return cosTheta;
40 }
41
42 auto ScatteredNeutronEnergy(const R3BNeulandCluster& first, const R3BNeulandCluster& second) -> double
43 {
44 static constexpr auto neutron_mass = 938.; // Rest Mass Neutron [MeV]
45 static constexpr auto light_speed_sq = 898.75517873681758374; // cm²/ns²
46
47 const TVector3 first_hit_distance = second.GetFirstHit().GetPosition() - first.GetFirstHit().GetPosition();
48 const double time_diff = second.GetT() - first.GetT();
49
50 const double velocity_sq = first_hit_distance.Mag2() / std::pow(time_diff, 2); // cm²/ns²
51 if (velocity_sq > light_speed_sq ||
52 velocity_sq > first.GetFirstHit().GetPosition().Mag2() / std::pow(first.GetT(), 2))
53 {
54 return 0;
55 }
56
57 const double gamma = 1. / std::sqrt(1. - (velocity_sq / light_speed_sq));
58 const double kinetic_energy = (gamma - 1.) * neutron_mass;
59 return kinetic_energy;
60 }
61
62 auto ScatteredNeutronAngle(const R3BNeulandCluster& first, const R3BNeulandCluster& second) -> double
63 {
64 const TVector3 pNUnit = first.GetFirstHit().GetPosition().Unit();
65 const TVector3 pN_Unit = (second.GetFirstHit().GetPosition() - first.GetFirstHit().GetPosition()).Unit();
66 const double cosTheta = pNUnit.Dot(pN_Unit); // \cos(\theta_{NN'})
67 return cosTheta;
68 }
69
71 {
72 const double minimalProtonKineticEnergy = 0.; // MeV
73 // E0n: Rest mass proton/neutron
74 // En: Total energy of incoming neutron
75 // Ep_: Total energy of scattered proton
76 // Ep_Kin: Kinetic energy of scattered proton
77 // pp_: Momentum Vector of scattered proton
78 // pn: Momentum Vector of incoming neutron
79 // cosTheta: Angle between incoming neutron and scattered proton
80
81 // Use the Energy Moment of the cluster to determine the proton kinetic energy.
82 const double Ep_Kin = RecoilProtonEnergy(cluster);
83 if (Ep_Kin < minimalProtonKineticEnergy)
84 {
85 // If the kinetic energy of the proton does not match, this method doesn't work
86 return 0;
87 }
88
89 // Rest Mass of Proton/Neutron [MeV]
90 const double E0n = 938.; // MeV
91 const double Ep_ = E0n + Ep_Kin;
92
93 const double cosTheta = RecoilScatteringAngle(cluster);
94
95 /*if (pp_Unit.Z() < 0. || cosTheta < 0.)
96 {
97 // No scattering in backward directions
98 // No scattering angle > 90°
99 return 0;
100 }*/
101
102 // From Four-Momentum conservation one can deduce:
103 // \frac{E_p' + E0n}{E_p' - E0n} \cos^2(\theta_{np'}) = \frac{E_N + E0n}{E_N - E0n}
104 const double value = cosTheta * cosTheta * (Ep_ + E0n) / (Ep_ - E0n);
105 const double Etot = E0n * ((value + 1.) / (value - 1.));
106
107 // return EKin
108 return Etot - E0n;
109 }
110
112 const R3BNeulandCluster& second,
113 double target_mass) -> double
114 {
115 const double neutron_m = 938.; // Rest Mass Neutron [MeV]
116
117 const double scattered_neutron_KE = ScatteredNeutronEnergy(first, second) + neutron_m;
118 // \cos(\theta_{NN'})
119 const double scattered_angle = ScatteredNeutronAngle(first, second);
120
121 // Formula for En. Yes its long, no the pows do not make it slow
122 const double neutron_energy =
123 (sqrt((pow(scattered_angle, 4) * pow(scattered_neutron_KE, 4) * pow(neutron_m, 2)) -
124 (2 * pow(scattered_angle, 4) * pow(scattered_neutron_KE, 2) * pow(neutron_m, 4)) +
125 (pow(scattered_angle, 4) * pow(neutron_m, 6)) +
126 (pow(scattered_angle, 2) * pow(scattered_neutron_KE, 4) * pow(target_mass, 2)) -
127 (pow(scattered_angle, 2) * pow(scattered_neutron_KE, 4) * pow(neutron_m, 2)) -
128 (2 * pow(scattered_angle, 2) * pow(scattered_neutron_KE, 2) * pow(target_mass, 2) *
129 pow(neutron_m, 2)) +
130 (2 * pow(scattered_angle, 2) * pow(scattered_neutron_KE, 2) * pow(neutron_m, 4)) +
131 (pow(scattered_angle, 2) * pow(target_mass, 2) * pow(neutron_m, 4)) -
132 (pow(scattered_angle, 2) * pow(neutron_m, 6))) +
133 pow(scattered_neutron_KE, 2) * target_mass - scattered_neutron_KE * pow(target_mass, 2) -
134 scattered_neutron_KE * pow(neutron_m, 2) + target_mass * pow(neutron_m, 2)) /
135 (pow(scattered_angle, 2) * pow(scattered_neutron_KE, 2) - pow(scattered_angle, 2) * pow(neutron_m, 2) -
136 pow(scattered_neutron_KE, 2) + 2 * scattered_neutron_KE * target_mass - pow(target_mass, 2));
137
138 return neutron_energy - neutron_m;
139 }
140
142 const R3BNeulandCluster& second,
143 const double targetMass) -> double
144 {
145 const double E0n = 938.;
146 const double E0k = targetMass;
147
148 const double Etot = first.GetFirstHit().GetEToF() + E0n;
149 const double E_neutron = ScatteredNeutronEnergy(first, second) + E0n;
150 const double E_kinetic = first.GetE() + targetMass;
151 const double cosTheta = ScatteredNeutronAngle(first, second);
152
153 const double zero = (E_neutron * E_neutron) + (E_neutron * E_kinetic) - (Etot * E0k) - (E0n * E0n) -
154 (sqrt((E_neutron * E_neutron - E0n * E0n) * (Etot * Etot - E0n * E0n)) * cosTheta);
155 return zero;
156 }
157
158 auto ElasticScatteringTargetMass(const R3BNeulandCluster& first, const R3BNeulandCluster& second) -> double
159 {
160 const double E0n = 938.;
161
162 const double Etot = first.GetFirstHit().GetEToF() + E0n;
163 const double E_neutron = ScatteredNeutronEnergy(first, second) + E0n;
164 const double Ek_kin = first.GetE();
165 const double cosTheta = ScatteredNeutronAngle(first, second);
166
167 const double E0k =
168 (E_neutron * E_neutron - E0n * E0n -
169 sqrt((E_neutron * E_neutron - E0n * E0n) * (Etot * Etot - E0n * E0n)) * cosTheta + E_neutron * Ek_kin) /
170 (Etot - E_neutron);
171
172 return E0k;
173 }
174} // 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