R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BNeulandClusterMon.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
15#include "ElasticScattering.h"
16#include "FairLogger.h"
17#include "FairRootManager.h"
18#include "R3BNeulandCluster.h"
19#include "TClonesArray.h"
20#include "TDirectory.h"
21#include "TH1D.h"
22#include "TH2D.h"
23#include "TH3D.h"
24#include <FairTask.h>
25#include <Rtypes.h>
26#include <RtypesCore.h>
27#include <TFile.h>
28#include <TH1.h>
29#include <TString.h>
30#include <algorithm>
31#include <cmath>
32#include <cstdlib>
33#include <fairlogger/Logger.h>
34#include <numeric>
35#include <utility>
36
37namespace
38{
39 constexpr double rad2deg = 180. / 3.141592653589793238463;
40
41 inline auto GetTheta(const R3BNeulandCluster& cluster) -> double
42 {
43 const auto direction = cluster.GetLastHit().GetPosition() - cluster.GetFirstHit().GetPosition();
44 const auto degree = std::acos(direction.Y() / direction.r()) * rad2deg;
45 // Not sure, but Kondos Theta is -90:90
46 static constexpr auto retate_angle = 90.;
47 return degree - retate_angle;
48 }
49} // namespace
50
51R3BNeulandClusterMon::R3BNeulandClusterMon(TString input, TString output, const Option_t* option)
52 : FairTask("R3B NeuLAND NeulandCluster Monitor")
53 , fNeulandClusters(input)
54 , fOutput(std::move(output))
55 , fBeta(0.793) // 600 Mev?
56{
57 LOG(info) << "Using R3B NeuLAND NeulandCluster Monitor";
58
59 TString opt = option;
60 opt.ToUpper();
61
62 if (opt.Contains("3DTRACK"))
63 {
64 fIs3DTrackEnabled = true;
65 LOG(info) << "... with 3D track visualization";
66 }
67 else
68 {
69 fIs3DTrackEnabled = false;
70 }
71}
72
74{
75 fNeulandClusters.init();
76
77 FairRootManager* ioman = FairRootManager::Instance();
78 if (!ioman)
79 {
80 LOG(fatal) << "R3BNeulandClusterMon::Init: No FairRootManager";
81 return kFATAL;
82 }
83
85 {
86 // XYZ -> ZXY (side view)
87 fh3 = new TH3D("hClusters", "hClusters", 60, 1400, 1700, 50, -125, 125, 50, -125, 125);
88 fh3->SetTitle("NeuLAND Clusters");
89 fh3->GetXaxis()->SetTitle("Z");
90 fh3->GetYaxis()->SetTitle("X");
91 fh3->GetZaxis()->SetTitle("Y");
92
93 ioman->Register(fOutput, "Cluster TH3Ds in NeuLAND", fh3, kTRUE);
94 }
95
96 TH1::AddDirectory(kFALSE);
97
98 fhClusters = new TH1D("nClusters", "Number of clusters in one event", 100, 0, 100);
99 fhClusterSize = new TH1D("ClusterSize", "Number of Digis for each Cluster", 100, 0, 100);
100 fhClusterEnergy = new TH1D("ClusterEnergy", "Energy for each Cluster", 2000, 0., 2000.);
102 new TH2D("ClusterEnergyVSSize", "Energy for each Cluster vs Cluster Size", 2000, 0., 2000., 100, 0., 100.);
103 fhClusterTime = new TH1D("ClusterTime", "Time for each Cluster", 5000, 0., 500.);
104 fhClusterEToF = new TH1D("ClusterEToF", "Cluster EToF", 2000, 0, 2000);
105 fhClusterRValue = new TH1D("ClusterRValue", "Log Cluster R Value", 20000, -10, 1);
106
108 new TH2D("ClusterSizeVSEToF", "Number of Digis for each Cluster vs EToF", 100, 0, 100, 2000, 0, 2000);
110 new TH2D("ClusterEnergyVSEToF", "Energy for each Cluster vs EToF", 2000, 0., 2000., 2000, 0, 2000);
111 fhClusterEnergyVSSizeVSEToF = new TH3D("ClusterEnergyVSSizeVSEToF",
112 "Energy for each Cluster vs Cluster Size vs EToF",
113 1000,
114 0.,
115 1000.,
116 100,
117 0.,
118 100.,
119 1200,
120 0,
121 1200.);
122
123 fhENFromScatterVSEToF = new TH2D("ENFromScatterVSEToF", "ENFromScatterVSEToF", 1000, 0, 1000, 1000, 0, 1000);
124
126 new TH2D("ClusterNumberEtot", "Number of Clusters vs. Total Energy", 200, 0, 2000, 50, 0, 50);
127 fhClusterNumberVSEnergy->GetXaxis()->SetTitle("Total energy [MeV]");
128 fhClusterNumberVSEnergy->GetYaxis()->SetTitle("Number of Clusters");
129
131 new TH2D("ClusterEToFVSEnergy", "Cluster E_{ToF} vs. Cluster Energy", 100, 0, 1000, 100, 0, 1000);
132 fhClusterEToFVSEnergy->GetXaxis()->SetTitle("Cluster E_{ToF} [MeV]");
133 fhClusterEToFVSEnergy->GetYaxis()->SetTitle("Cluster E [MeV]");
134
135 fhClusterEToFVSTime = new TH2D("ClusterEToFVSTime", "Cluster E_{ToF} vs. Cluster Time", 100, 0, 1000, 500, 0, 500);
136 fhClusterEToFVSTime->GetXaxis()->SetTitle("Cluster E_{ToF} [MeV]");
137 fhClusterEToFVSTime->GetYaxis()->SetTitle("Cluster t [ns]");
138
139 fhClusterEVSTime = new TH2D("ClusterEVSTime", "Cluster E vs. Cluster Time", 250, 0, 250, 500, 0, 250);
140 fhClusterEVSTime->GetXaxis()->SetTitle("Cluster E [MeV]");
141 fhClusterEVSTime->GetYaxis()->SetTitle("Cluster t [ns]");
142
144 "ClusterLastMinusFirstDigiMagVSEnergy", "ClusterLastMinusFirstDigiMagVSEnergy", 101, 0, 100, 60, 0, 600);
145
147 new TH2D("ClusterForemostMinusCentroidVSEnergy",
148 "Distance between Foremost Digi and Energy Centroid vs Cluster Energy",
149 101,
150 0,
151 100,
152 60,
153 0,
154 600);
155 fhClusterForemostMinusCentroidVSEnergy->GetXaxis()->SetTitle("|#vec{r_{z_{min}}}-#vec{r_{EC}}| [cm]");
156 fhClusterForemostMinusCentroidVSEnergy->GetYaxis()->SetTitle("Cluster Energy [MeV]");
157
159 new TH2D("ClusterForemostMinusMaxEnergyDigiPosVSEnergy",
160 "Distance between Foremost Digi and Max Energy Digi vs Cluster Energy",
161 101,
162 0,
163 100,
164 60,
165 0,
166 600);
167 fhClusterForemostMinusMaxEnergyDigiPosVSEnergy->GetXaxis()->SetTitle("|#vec{r_{z_{min}}}-#vec{r_{E_{max}}}| [cm]");
168 fhClusterForemostMinusMaxEnergyDigiPosVSEnergy->GetYaxis()->SetTitle("Cluster Energy [MeV]");
169
171 new TH2D("ClusterCentroidMinusFirstDigiPosVSEnergy",
172 "Distance between Cluster Energy Centroid and First Hit vs Cluster Energy",
173 101,
174 0,
175 100,
176 60,
177 0,
178 600);
179 fhClusterCentroidMinusFirstDigiPosVSEnergy->GetXaxis()->SetTitle("|#vec{r_{EC}}-#vec{r_{t_{min}}}| [cm]");
180 fhClusterCentroidMinusFirstDigiPosVSEnergy->GetYaxis()->SetTitle("Cluster Energy [MeV]");
181
183 new TH2D("ClusterMaxEnergyDigiMinusFirstDigiPosVSEnergy",
184 "Distance between Max Energy Digi and First Digi vs Cluster Energy",
185 101,
186 0,
187 100,
188 60,
189 0,
190 600);
191 fhClusterMaxEnergyDigiMinusFirstDigiPosVSEnergy->GetXaxis()->SetTitle("|#vec{r_{E_{max}}}-#vec{r_{t_{min}}}| [cm]");
192 fhClusterMaxEnergyDigiMinusFirstDigiPosVSEnergy->GetYaxis()->SetTitle("Cluster Energy [MeV]");
193
195 new TH2D("ClusterMaxEnergyDigiMinusCentroidVSEnergy",
196 "Distance between Max Energy Digi and Energy Centroid vs Cluster Energy",
197 101,
198 0,
199 100,
200 60,
201 0,
202 600);
203 fhClusterMaxEnergyDigiMinusCentroidVSEnergy->GetXaxis()->SetTitle("|#vec{r_{E_{max}}}-#vec{r_{EC}}| [cm]");
204 fhClusterMaxEnergyDigiMinusCentroidVSEnergy->GetYaxis()->SetTitle("Cluster Energy [MeV]");
205
207 new TH2D("ClusterEnergyMomentVSEnergy", "Cluster Energy Moment VS Energy", 100, 0, 100, 60, 0, 600);
208 fhClusterEnergyMomentVSEnergy->GetXaxis()->SetTitle("|#vec{EM}| [cm]");
209 fhClusterEnergyMomentVSEnergy->GetYaxis()->SetTitle("Cluster Energy [MeV]");
210
211 fhClusterEnergyMoment = new TH1D("ClusterEnergyMoment", "Cluster Energy Moment", 100, 0, 100);
213 new TH1D("ClusterMaxEnergyDigiMinusFirstDigiMag", "ClusterMaxEnergyDigiMinusFirstDigiMag", 100, 0, 1000);
214
216 new TH2D("ClusterEnergyMomentVSClusterSize", "Cluster Energy Moment VS Cluster Size", 100, 0, 100, 30, 0, 30);
217
218 fhEToFVSEelastic = new TH2D("EToFVSEelastic", "EToFVSEelastic", 50, 0, 1000, 50, 10, 1000);
219 fhScatteredNEnergyVSAngle = new TH2D("ScatteredNEnergyVSAngle", "ScatteredNEnergyVSAngle", 50, 0, 1000, 50, 0, 3.2);
220 fhScatteredNEnergyVSEdep = new TH2D("ScatteredNEnergyVSEdep", "ScatteredNEnergyVSEdep", 100, 0, 1000, 100, 0, 1000);
221
223 new TH2D("ScatterAngleVSRecoilAngle", "ScatterAngleVSRecoilAngle", 50, 0, 3.2, 50, 0, 3.2);
224
225 fhSumAngleVSRatioErecoEtof = new TH2D("SumAngleVSRatioErecoEtof", "SumAngleVSRatioErecoEtof", 50, 0, 3.2, 50, 0, 2);
227 new TH2D("ClusterEnergyVSScatteredRecoilAngle", "ClusterEnergyVSScatteredRecoilAngle", 50, 0, 1000, 50, 0, 3.2);
229 "ClusterEnergyVSScatteredNeutronAngle", "ClusterEnergyVSScatteredNeutronAngle", 50, 0, 1000, 50, 0, 3.2);
230
231 fhElasticTargetMass = new TH2D("ElasticTargetMass", "ElasticTargetMass", 200, -50000, 50000, 100, 0, 1000);
232
233 /*fhtheh = new TH2D("theh", "", 100, 0, 1000, 100, 0, 2000);
234 ediff = new TH2D("ediff", "Reconstructed Proton Energy VS Measured Cluster Energy", 100, 0, 300, 100, 0, 300);
235 ediff->GetXaxis()->SetTitle("E_{p'}");
236 ediff->GetYaxis()->SetTitle("E_{cluster}");
237
238 etheta = new TH2D("etheta", "", 100, 0, 1000, 100, -1, 1);
239
240 fhProtonTrack*/
241
242 fhZ = new TH1D("Z", "Z", 5000, 0., 5000.);
243 fhZVSEToF = new TH2D("ZVSEToF", "Z vs EToF", 4000, 1000, 5000, 2000, 0, 2000);
244 fhDistFromCenter = new TH1D("DistFromCenter", "DistFromCenter", 500, 0., 500.);
245 fhDistFromCenterVSEToF = new TH2D("DistFromCenterVSEToF", "DistFromCenter vs EToF", 500, 0, 500, 2000, 0, 2000);
246
247 fhDeltaT = new TH1D("fhDeltaT", "T_{Last Digi} - T_{First Digi}", 1000, 0, 100);
249 new TH1D("fhForemostMinusFirstDigiTime", "T_{Foremost} - T_{First Digi}", 1000, -10, 10);
250
251 fhThetaEDigi = new TH2D("fhThetaEDigi", "fhThetaEDigi", 1000, -500, 500, 400, 0, 400);
252 fhThetaEDigiCosTheta = new TH2D("fhThetaEDigiCosTheta", "fhThetaEDigiCosTheta", 1000, -500, 500, 400, 0, 400);
253
254 hT = new TH1D("hT", "Cluster Digi Delta T", 3000, 0., 15.);
255 hTNeigh = new TH1D("hTNeigh", "Cluster Digi Neigh Delta T", 3000, 0., 15.);
256
257 return kSUCCESS;
258}
259
261{
264 std::remove_if(fNeulandClustersBuffer.begin(),
266 [&](R3BNeulandCluster& cluster) { return !(fClusterFilters.IsValid(&cluster)); }),
268
269 const auto nClusters = fNeulandClustersBuffer.size();
270
272 {
273 fh3->Reset("ICES");
274 for (const auto& cluster : fNeulandClustersBuffer)
275 {
276 const auto start = cluster.GetFirstHit().GetPosition();
277 // XYZ -> ZXY (side view)
278 fh3->Fill(start.Z(), start.X(), start.Y(), cluster.GetE());
279 }
280 }
281
282 const Double_t etot =
283 std::accumulate(fNeulandClustersBuffer.begin(),
285 0.,
286 [](Double_t sum, const R3BNeulandCluster& cluster) { return sum + cluster.GetE(); });
287
288 fhClusterNumberVSEnergy->Fill(etot, nClusters);
289
290 fhClusters->Fill(nClusters);
291 for (const auto& cluster : fNeulandClustersBuffer)
292 {
293 fhClusterTime->Fill(cluster.GetT());
294 fhClusterSize->Fill(cluster.GetSize());
295 fhClusterEnergy->Fill(cluster.GetE());
296 fhClusterRValue->Fill(std::log10(cluster.GetRCluster(fBeta)));
297 fhClusterEnergyVSSize->Fill(cluster.GetE(), cluster.GetSize());
298 fhClusterEToF->Fill(cluster.GetFirstHit().GetEToF());
299 fhClusterEToFVSEnergy->Fill(cluster.GetFirstHit().GetEToF(), cluster.GetE());
300 fhClusterEToFVSTime->Fill(cluster.GetFirstHit().GetEToF(), cluster.GetT());
301 fhClusterEVSTime->Fill(cluster.GetFirstHit().GetE(), cluster.GetT());
302
303 fhClusterEnergyVSEToF->Fill(cluster.GetE(), cluster.GetFirstHit().GetEToF());
304 fhClusterSizeVSEToF->Fill(cluster.GetSize(), cluster.GetFirstHit().GetEToF());
305 fhClusterEnergyVSSizeVSEToF->Fill(cluster.GetE(), cluster.GetSize(), cluster.GetFirstHit().GetEToF());
306 if (cluster.GetSize() > 2)
307 {
309 (cluster.GetForemostHit().GetPosition() - cluster.GetEnergyCentroid()).r(), cluster.GetE());
310
312 (cluster.GetForemostHit().GetPosition() - cluster.GetMaxEnergyHit().GetPosition()).r(), cluster.GetE());
313
315 (cluster.GetEnergyCentroid() - cluster.GetFirstHit().GetPosition()).r(), cluster.GetE());
316
318 (cluster.GetMaxEnergyHit().GetPosition() - cluster.GetFirstHit().GetPosition()).r(), cluster.GetE());
320 (cluster.GetMaxEnergyHit().GetPosition() - cluster.GetEnergyCentroid()).r(), cluster.GetE());
321 fhClusterEnergyMomentVSEnergy->Fill(cluster.GetEnergyMoment(), cluster.GetE());
323
325 (cluster.GetLastHit().GetPosition() - cluster.GetFirstHit().GetPosition()).r(), cluster.GetE());
326
327 fhClusterEnergyMoment->Fill(cluster.GetEnergyMoment());
329 (cluster.GetMaxEnergyHit().GetPosition() - cluster.GetFirstHit().GetPosition()).r());
330 }
331
332 fhZ->Fill(cluster.GetFirstHit().GetPosition().Z());
333 fhZVSEToF->Fill(cluster.GetFirstHit().GetPosition().Z(), cluster.GetEToF());
334 fhDistFromCenter->Fill(std::sqrt(std::pow(cluster.GetFirstHit().GetPosition().X(), 2) +
335 std::pow(cluster.GetFirstHit().GetPosition().Y(), 2)));
336 fhDistFromCenterVSEToF->Fill(std::sqrt(std::pow(cluster.GetFirstHit().GetPosition().X(), 2) +
337 std::pow(cluster.GetFirstHit().GetPosition().Y(), 2)),
338 cluster.GetEToF());
339 fhDeltaT->Fill(cluster.GetLastHit().GetT() - cluster.GetFirstHit().GetT());
340
341 fhForemostMinusFirstDigiTime->Fill(cluster.GetForemostHit().GetT() - cluster.GetFirstHit().GetT());
342
343 if (cluster.GetSize() > 4)
344 {
345 const auto theta = GetTheta(cluster);
346 for (const auto& digi : cluster.GetHits())
347 {
348 fhThetaEDigi->Fill(theta, digi.GetE());
349 fhThetaEDigiCosTheta->Fill(theta, digi.GetE() * std::cos(theta / rad2deg));
350 }
351 }
352 }
353
354 std::sort(fNeulandClustersBuffer.begin(),
356 [](const R3BNeulandCluster& left, const R3BNeulandCluster& right) { return left.GetT() < right.GetT(); });
357
358 for (const auto& cluster : fNeulandClustersBuffer)
359 {
360 if (cluster.GetSize() >= 3)
361 {
363
365 std::acos(Neuland::RecoilScatteringAngle(cluster)));
366 }
367 }
368
369 for (auto ita = fNeulandClustersBuffer.cbegin(); ita != fNeulandClustersBuffer.cend(); ita++)
370 {
371 for (auto itb = ita + 1; itb != fNeulandClustersBuffer.cend(); itb++)
372 {
373
375 std::acos(Neuland::ScatteredNeutronAngle(*ita, *itb)));
376
377 fhScatteredNEnergyVSEdep->Fill(Neuland::ScatteredNeutronEnergy(*ita, *itb), (*ita).GetE());
378
379 const Double_t EelasticHeavy = Neuland::NeutronEnergyFromElasticScattering(*ita, *itb, 11000);
380 fhEToFVSEelastic->Fill((*ita).GetFirstHit().GetEToF(), EelasticHeavy);
381 // const Double_t EelasticProton = Neuland::NeutronEnergyFromElasticScattering(*ita, *itb, 1000);
382 // fhEToFVSEelastic->Fill((*ita)->GetFirstHit().GetEToF(), EelasticProton);
383
384 if (Neuland::ScatteredNeutronEnergy(*ita, *itb) > 10.)
385 {
386 fhElasticTargetMass->Fill(Neuland::ElasticScatteringTargetMass(*ita, *itb), (*ita).GetE());
387 fhClusterEnergyVSScatteredNeutronAngle->Fill((*ita).GetE(),
388 std::acos(Neuland::ScatteredNeutronAngle(*ita, *itb)));
389
390 if ((*ita).GetSize() >= 3)
391 {
393 std::acos(Neuland::RecoilScatteringAngle(*ita)));
395 std::acos(Neuland::ScatteredNeutronAngle(*ita, *itb)) +
396 std::acos(Neuland::RecoilScatteringAngle(*ita)),
397 Neuland::NeutronEnergyFromElasticProtonScattering(*ita) / (*ita).GetFirstHit().GetEToF());
398 }
399 }
400 }
401 }
402
403 for (const auto& cluster : fNeulandClustersBuffer)
404 {
405 const auto& digis = cluster.GetHits();
406 for (auto it1 = digis.begin(); it1 != digis.end(); it1++)
407 {
408 for (auto it2 = it1 + 1; it2 != digis.end(); it2++)
409 {
410 if (std::abs(it1->GetPosition().X() - it2->GetPosition().X()) < 7.5 &&
411 std::abs(it1->GetPosition().Y() - it2->GetPosition().Y()) < 7.5 &&
412 std::abs(it1->GetPosition().Z() - it2->GetPosition().Z()) < 7.5)
413 {
414 hTNeigh->Fill(std::abs(it1->GetT() - it2->GetT()));
415 }
416 hT->Fill(std::abs(it1->GetT() - it2->GetT()));
417 }
418 }
419 }
420}
421
423{
424 TDirectory* tmp = gDirectory;
425 FairRootManager::Instance()->GetOutFile()->cd();
426
427 gDirectory->mkdir(fOutput);
428 gDirectory->cd(fOutput);
429
430 fhClusters->Write();
431 fhClusterSize->Write();
432 fhClusterEnergyVSSize->Write();
433 fhClusterEnergy->Write();
434 fhClusterRValue->Write();
435 fhClusterTime->Write();
437 fhClusterEToF->Write();
438 fhClusterEToFVSEnergy->Write();
439 fhClusterEToFVSTime->Write();
440 fhElasticTargetMass->Write();
441
442 fhClusterSizeVSEToF->Write();
443 fhClusterEnergyVSEToF->Write();
444 // fhClusterEnergyVSSizeVSEToF->Write();
445
453 fhClusterEVSTime->Write();
454 fhClusterEnergyMoment->Write();
457
458 fhENFromScatterVSEToF->Write();
459
460 fhEToFVSEelastic->Write();
467
468 fhZ->Write();
469 fhZVSEToF->Write();
470 fhDistFromCenter->Write();
471 fhDistFromCenterVSEToF->Write();
472 fhDeltaT->Write();
474
475 fhThetaEDigi->Write();
476 fhThetaEDigiCosTheta->Write();
477
478 hT->Write();
479 hTNeigh->Write();
480
481 gDirectory = tmp;
482}
483
ClassImp(R3B::Neuland::Cal2HitPar)
Double_t GetTheta(const R3BMCTrack *mcTrack)
auto GetEToF() const -> double
auto GetForemostHit() const -> R3BNeulandHit
auto GetT() const -> double
auto GetFirstHit() const -> R3BNeulandHit
auto GetSize() const -> std::size_t
auto GetHits() const -> const std::vector< R3BNeulandHit > &
auto GetEnergyCentroid() const -> ROOT::Math::XYZVector
auto GetMaxEnergyHit() const -> R3BNeulandHit
auto GetLastHit() const -> R3BNeulandHit
auto GetRCluster(double beta) const -> double
auto GetE() const -> double
auto GetEnergyMoment() const -> double
TH2D * fhClusterMaxEnergyDigiMinusFirstDigiPosVSEnergy
void Exec(Option_t *) override
TH2D * fhClusterForemostMinusMaxEnergyDigiPosVSEnergy
TH2D * fhClusterCentroidMinusFirstDigiPosVSEnergy
std::vector< R3BNeulandCluster > fNeulandClustersBuffer
R3B::InputVectorConnector< R3BNeulandCluster > fNeulandClusters
TH2D * fhClusterMaxEnergyDigiMinusCentroidVSEnergy
R3BNeulandClusterMon(TString input="NeulandClusters", TString output="NeulandClusterMon", const Option_t *option="")
auto Init() -> InitStatus override
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 RecoilScatteringAngle(const R3BNeulandCluster &cluster) -> double