ARTEMIS-CRIB
 
Loading...
Searching...
No Matches
TNBodyReactionProcessor.cc
Go to the documentation of this file.
1/**
2 * @file TNBodyReactionProcessor.cc
3 * @brief
4 * @author Kodai Okawa <okawa@cns.s.u-tokyo.ac.jp>
5 * @date 2023-08-01 22:36:36
6 * @note last modified: 2024-10-30 16:22:33
7 * @details for (angle) constant cross section
8 */
9
11
13#include "TParticleInfo.h"
14#include <Mass.h> // TSrim library
15#include <TRandom.h>
16#include <filesystem>
17#include <fstream>
18#include <regex>
19
21
23
25 : fInData(nullptr), fOutData(nullptr), fOutReacData(nullptr), srim(nullptr) {
26 RegisterInputCollection("InputCollection", "input branch (collection) name", fInputColName, TString("input"));
27 RegisterOutputCollection("OutputCollection", "output branch (collection) name", fOutputColName,
28 TString("reaction_particles"));
29 RegisterOutputCollection("OutputReactionCollection", "output branch (collection) name", fOutputReacColName,
30 TString("reaction"));
31
32 // beam information
33 IntVec_t init_i_vec;
34 RegisterProcessorParameter("BeamNucleus", "beam nucleus (Z, A)", fBeamNucleus, init_i_vec);
35 RegisterProcessorParameter("BeamEnergy", "initial beam energy used for initializing TSrim object",
36 fBeamEnergy, 0.0);
37
38 // Target information
39 RegisterProcessorParameter("TargetIsGas", "Bool, target is targer or not", fTargetIsGas, false);
40 RegisterProcessorParameter("TargetName", "name of target material", fTargetName, TString(""));
41 RegisterProcessorParameter("TargetMassNum", "Target mass number", fTargetMassNum, 0);
42 RegisterProcessorParameter("TargetAtomicNum", "Target atomic number", fTargetAtmNum, 0);
43 RegisterProcessorParameter("TargetThickness", "Target mass", fTargetThickness, 0.0);
44 RegisterProcessorParameter("TargetPressure", "Target presure", fTargetPressure, 0.0);
45
46 // reaction particles information
47 DoubleVec_t init_d_vec;
48 RegisterProcessorParameter("DecayParticleNum", "reaction particle number", fDecayNum, 1);
49 RegisterProcessorParameter("ReactionMassNum", "reaction particles mass number", fReacMassNum, init_i_vec);
50 RegisterProcessorParameter("ReactionAtomicNum", "reaction particles atomic number", fReacAtmNum, init_i_vec);
51 RegisterProcessorParameter("ExciteLevel", "energy level for id=0 nucleus", fExciteLevel, init_d_vec);
52 RegisterProcessorParameter("CrossSectionPath", "path to the cross section data file", fCSDataPath, TString(""));
53 RegisterProcessorParameter("CrossSectionType",
54 "energy format, 0: LAB energy like TALYS, 1: LAB at inverse kinematics, 2: Ecm", fCSType, 0);
55}
56
58 delete fOutData;
59 delete fOutReacData;
60 delete srim;
61 delete gr_generating_func;
63 fOutData = nullptr;
64 fOutReacData = nullptr;
65 srim = nullptr;
66 gr_generating_func = nullptr;
67 gr_generating_func_inv = nullptr;
68}
69
70void TNBodyReactionProcessor::Init(TEventCollection *col) {
71 Info("Init", "%s => %s, decay num: %d, reaction info => %s",
73
74 if (fDecayNum != (Int_t)fReacMassNum.size() ||
75 fDecayNum != (Int_t)fReacAtmNum.size() ||
76 fDecayNum != (Int_t)fExciteLevel.size()) {
77 SetStateError("in input yaml, DecayParticleNum and the size of other elements are different");
78 return;
79 }
80
81 if (fBeamNucleus.size() != 2) {
82 SetStateError("fBeamNucleus format need (Z, A): ex (2, 4)");
83 return;
84 }
85
86 for (Int_t i = 0; i < fDecayNum; i++) {
87 Info("Init", "reaction products: id=%d, %s (A=%d, Z=%d), Ex = %lf", i,
88 amdc::GetEl(fReacAtmNum[i]).c_str(), fReacMassNum[i], fReacAtmNum[i], fExciteLevel[i]);
89 }
90
91 fInData = reinterpret_cast<TClonesArray **>(col->GetObjectRef(fInputColName.Data()));
92 if (!fInData) {
93 SetStateError(TString::Format("input not found: %s", fInputColName.Data()));
94 return;
95 }
96 const TClass *const cl = (*fInData)->GetClass();
97 if (!cl->InheritsFrom(art::crib::TParticleInfo::Class())) {
98 SetStateError("contents of input array must inherit from art::crib::TParticleInfo");
99 return;
100 }
101
102 fOutData = new TClonesArray("art::crib::TParticleInfo");
103 fOutData->SetName(fOutputColName);
104 col->Add(fOutputColName, fOutData, fOutputIsTransparent);
105
106 fOutReacData = new TClonesArray("art::crib::TReactionInfo");
108 col->Add(fOutputReacColName, fOutReacData, fOutputIsTransparent);
109
110 const char *tsrim_path = std::getenv("TSRIM_DATA_HOME");
111 if (!tsrim_path) {
112 SetStateError("TSRIM_DATA_HOME environment variable is not defined");
113 return;
114 }
115 // get All SRIM table for the target
116 Info("Init", "Initializing SRIM table...");
117 srim = new TSrim("srim", 16,
118 Form("%s/%s/range_fit_pol16_%s.txt", tsrim_path, fTargetName.Data(), fTargetName.Data()));
119 Info("Init", "\t\"%s\" list loaded.", fTargetName.Data());
120
121 // cross section input file
123
124 gRandom->SetSeed(time(nullptr));
125}
126
128 fOutData->Clear("C");
129 fOutReacData->Clear("C");
130
131 if ((*fInData)->GetEntriesFast() != 1) {
132 SetStateError("input branch entry is not 1");
133 return;
134 }
135
136 const TDataObject *const inData = static_cast<TDataObject *>((*fInData)->At(0));
137 const TParticleInfo *const Data = dynamic_cast<const TParticleInfo *>(inData);
138
139 Double_t target_mass = amdc::Mass(fTargetAtmNum, fTargetMassNum) * amdc::amu; // MeV
140 TLorentzVector target_vec(0., 0., 0., target_mass);
141 TLorentzVector beam_vec = Data->GetLorentzVector();
142 Double_t beam_mass = amdc::Mass(fBeamNucleus[0], fBeamNucleus[1]) * amdc::amu; // MeV
143 Double_t beam_energy = Data->GetEnergy();
144
145 // calculate reaction position
146 // target should be set at z=0 (entrance of gas target)
147 Double_t range;
148 if (fTargetIsGas) {
149 range = srim->Range(fBeamNucleus[0], fBeamNucleus[1], beam_energy,
150 std::string(fTargetName.Data()), fTargetPressure, 300.0);
151 } else {
152 range = srim->Range(fBeamNucleus[0], fBeamNucleus[1], beam_energy,
153 std::string(fTargetName.Data()));
154 }
155
156 // determine using random number
157 Double_t reac_distance = GetRandomReactionDistance(range);
158 Double_t beam_energy_new = 0.0;
159 if (fTargetIsGas) {
160 beam_energy_new = srim->EnergyNew(fBeamNucleus[0], fBeamNucleus[1], beam_energy,
161 std::string(fTargetName.Data()), reac_distance, fTargetPressure, 300.0);
162 } else {
163 beam_energy_new = srim->EnergyNew(fBeamNucleus[0], fBeamNucleus[1], beam_energy,
164 std::string(fTargetName.Data()), reac_distance);
165 }
166
167 Double_t reac_posz = 0.0;
168 if (fTargetIsGas) {
169 reac_posz = reac_distance * TMath::Cos(Data->GetTrack().GetA()) * TMath::Cos(Data->GetTrack().GetB());
170 }
171 Double_t reac_posx = Data->GetTrack().GetX(reac_posz);
172 Double_t reac_posy = Data->GetTrack().GetY(reac_posz);
173
174 beam_vec = GetLossEnergyVector(beam_vec, beam_energy - beam_energy_new);
175
176 // calculate tof in the target (mean value)
177 Double_t duration_beam = 0.0;
178 if (fTargetIsGas) {
179 duration_beam += reac_distance / (TMath::Sqrt(1.0 - TMath::Power(beam_mass / beam_vec.E(), 2.0)) * c);
180 } // if solid target, tof is almost zero
181
182 DoubleVec_t reac_masses;
183 for (Int_t iPart = 0; iPart < fDecayNum; iPart++) {
184 Double_t mass = amdc::Mass(fReacAtmNum[iPart], fReacMassNum[iPart]) * amdc::amu; // MeV
185 mass += fExciteLevel[iPart];
186 reac_masses.emplace_back(mass);
187 }
188
189 TLorentzVector compound_vec = beam_vec + target_vec;
190
191 // to CM system (only beam_vec and target_vec)
192 TVector3 beta_vec = compound_vec.BoostVector();
193 beam_vec.Boost(-beta_vec);
194 target_vec.Boost(-beta_vec);
195 Double_t energy_cm = (beam_vec.E() - beam_vec.M()) + (target_vec.E() - target_mass);
196
197 // need to change MeV to GeV
198 compound_vec *= 0.001;
199 DoubleVec_t GeVMass;
200 for (decltype(reac_masses.size()) i = 0; i < reac_masses.size(); i++) {
201 GeVMass.emplace_back(reac_masses[i] * 0.001);
202 }
203
204 Bool_t isOkay = event.SetDecay(compound_vec, fDecayNum, GeVMass.data());
205 if (!isOkay) {
206 std::cerr << "forbidden kinematics" << std::endl;
207 }
208 event.Generate();
209
210 Double_t theta_cm = 0.0;
211 for (Int_t iPart = 0; iPart < fDecayNum; ++iPart) {
212 TLorentzVector reac_vec = *event.GetDecay(iPart);
213 // need to change GeV to MeV
214 reac_vec *= 1000.;
215
216 TParticleInfo *outData = static_cast<TParticleInfo *>(fOutData->ConstructedAt(iPart));
217 outData->SetID(iPart);
218 outData->SetMassNumber(fReacMassNum[iPart]);
219 outData->SetAtomicNumber(fReacAtmNum[iPart]);
220 outData->SetCharge(fReacAtmNum[iPart]); // no need?
221 outData->SetTrack(reac_posx, reac_posy, reac_posz, TMath::ATan(reac_vec.Px() / reac_vec.Pz()),
222 TMath::ATan(reac_vec.Py() / reac_vec.Pz()));
223 if (fTargetIsGas) {
224 outData->SetLorentzVector(reac_vec);
225 outData->SetEnergy(reac_vec.E() - reac_masses[iPart]);
226 outData->SetCurrentZ(reac_posz);
227 outData->SetZeroTime(); // initialize
228 outData->AddTime(duration_beam);
229 } else {
230 // for the solid target, ignore the angle
231 Double_t first_energy = reac_vec.E() - reac_masses[iPart];
232 if (first_energy > 0.01) {
233 Double_t out_thickness = fTargetThickness - reac_distance;
234 if (out_thickness < 0.0) {
235 out_thickness = 0.0;
236 }
237 TLorentzVector out_vec =
238 GetLossEnergyVector(reac_vec,
239 first_energy - srim->EnergyNew(fReacAtmNum[iPart], fReacMassNum[iPart], first_energy, std::string(fTargetName.Data()), out_thickness));
240
241 outData->SetLorentzVector(out_vec);
242 outData->SetEnergy(out_vec.E() - reac_masses[iPart]);
244 outData->SetZeroTime();
245 } else {
246 // outData->SetLorentzVector(reac_vec);
247 outData->SetLorentzVector(0.0, 0.0, 0.0, reac_masses[iPart]);
248 outData->SetEnergy(0.0);
249 outData->SetCurrentZ(reac_posz);
250 outData->SetZeroTime();
251 }
252 }
253
254 // compound_vec and reac_vec is LAB system, convert to CM system
255 reac_vec.Boost(-beta_vec);
256 outData->SetThetaCM(reac_vec.Theta() / deg2rad);
257 outData->SetPhiCM(reac_vec.Phi() / deg2rad);
258 if (iPart == 0) {
259 theta_cm = (Data->GetLorentzVector()).Angle(reac_vec.Vect()) / deg2rad;
260 }
261 }
262
263 TReactionInfo *outReacData = static_cast<TReactionInfo *>(fOutReacData->ConstructedAt(0));
264 outReacData->SetID(0);
265 outReacData->SetExEnergy(fExciteLevel[0]);
266 outReacData->SetEnergy(energy_cm);
267 outReacData->SetTheta(theta_cm);
268 outReacData->SetXYZ(reac_posx, reac_posy, reac_posz);
269}
270
272 gr_generating_func = new TGraph();
273 gr_generating_func_inv = new TGraph();
274
275 // simplize range
276 auto get_range = [&](Double_t e) {
277 if (fTargetIsGas) {
278 return srim->Range(fBeamNucleus[0], fBeamNucleus[1], e, std::string(fTargetName.Data()), fTargetPressure, 300.0);
279 } else {
280 return srim->Range(fBeamNucleus[0], fBeamNucleus[1], e, std::string(fTargetName.Data()));
281 }
282 };
283
284 // get dE/dx (x : range)
285 auto dedx = [&](Double_t e) {
286 const Double_t h = 0.1;
287 Double_t dxde = (get_range(e + h) - get_range(e)) / h;
288 return 1.0 / dxde;
289 };
290
291 auto gr_density_func = new TGraph();
292 Int_t index = 0; // index for TGraph
293 Double_t xmin = 0.0, xmax = 0.0;
294 Bool_t is_exist = std::filesystem::exists(fCSDataPath.Data());
295 if (!is_exist) {
296 Info("Init", "no input cross section file, use uniform energy distribution");
297 for (auto e = 0.0; e < fBeamEnergy * 1.5; e += 0.5) {
298 Double_t range = get_range(e);
299 gr_density_func->SetPoint(index, range, dedx(e));
300 index++;
301 xmax = range;
302 if (index == 1) {
303 xmin = range;
304 }
305 }
306 } else {
307 std::ifstream fin(fCSDataPath.Data());
308 Info("Init", "get cross section file: %s", fCSDataPath.Data());
309 Double_t ene_factor = 0.0;
310 if (fCSType == 0) {
311 ene_factor = amdc::Mass(fBeamNucleus[0], fBeamNucleus[1]) / amdc::Mass(fTargetAtmNum, fTargetMassNum);
312 } else if (fCSType == 1) {
313 ene_factor = 1.0;
314 } else if (fCSType == 2) {
315 ene_factor = (amdc::Mass(fBeamNucleus[0], fBeamNucleus[1]) + amdc::Mass(fTargetAtmNum, fTargetMassNum)) /
316 amdc::Mass(fTargetAtmNum, fTargetMassNum);
317 } else {
318 SetStateError("CrossSectionType should be 0, 1 or 2");
319 return;
320 }
321
322 std::string line;
323 std::regex commentRegex("#.*");
324 while (std::getline(fin, line)) {
325 if (line.empty() || line[0] == '#') {
326 continue;
327 }
328 line = std::regex_replace(line, commentRegex, "");
329
330 std::vector<TString> values;
331 TString line_copy = line;
332 while (line_copy.Contains(' ')) {
333 Int_t index = line_copy.First(' ');
334 TString line_val, line_remain;
335 line_val = line_copy(0, index);
336 line_remain = line_copy(index + 1, line_copy.Length());
337 line_copy = line_remain;
338 if (line_val == "") {
339 continue;
340 }
341 values.emplace_back(line_val);
342 }
343 Double_t e = ene_factor * values[0].Atof();
344 Double_t cs = values[1].Atof() / ene_factor;
345 Double_t range = get_range(e);
346 gr_density_func->SetPoint(index, range, cs * dedx(e));
347 index++;
348 xmax = range;
349 if (index == 1) {
350 xmin = range;
351 }
352 }
353 }
354
355 auto f = new TF1("f", [&](double *x, double *p) { return p[0] * gr_density_func->Eval(x[0], nullptr, "S"); }, xmin, xmax, 1);
356 f->SetParameter(0, 1.0);
357
358 Info("Init", "suppressing the error message in this initialization process");
359 Info("Init", "please wait for a moment...");
360
361 // suppress error message when calculating the integral value
362 __attribute__((unused)) auto *_dev_null = std::freopen("/dev/null", "w", stderr);
363 index = 0;
364 Double_t x_step = (xmax - xmin) / 1000.0; // 1000 steps
365 for (auto x = xmin; x < xmax; x += x_step) {
366 gr_generating_func->SetPoint(index, x, f->Integral(xmin, x));
367 gr_generating_func_inv->SetPoint(index, f->Integral(xmin, x), x);
368 index++;
369 }
370 __attribute__((unused)) auto *_dev_tty = std::freopen("/dev/tty", "w", stderr);
371
372 Info("Init", "total cross section (arbitrary unit or mb)");
373 Info("Init", "\t%lf", gr_generating_func->Eval(get_range(fBeamEnergy), nullptr, "S"));
374}
375
377 Double_t random_x = 0.0;
378 Double_t max_x = gr_generating_func->Eval(range, nullptr, "S");
379 if (range < fTargetThickness) {
380 random_x = max_x * gRandom->Uniform();
381 } else {
382 Double_t limit_range = range - fTargetThickness;
383 Double_t min_x = gr_generating_func->Eval(limit_range, nullptr, "S");
384 random_x = gRandom->Uniform(min_x, max_x);
385 }
386 Double_t distance = gr_generating_func_inv->Eval(random_x, nullptr, "S");
387
388 return range - distance;
389}
390
391TLorentzVector TNBodyReactionProcessor::GetLossEnergyVector(TLorentzVector vec, Double_t eloss) {
392 Double_t factor =
393 ((vec.E() - eloss) * (vec.E() - eloss) - vec.M() * vec.M()) / (vec.E() * vec.E() - vec.M() * vec.M());
394 factor = TMath::Sqrt(factor);
395 TLorentzVector new_vec(factor * vec.Px(), factor * vec.Py(), factor * vec.Pz(), vec.E() - eloss);
396 return new_vec;
397}
398
399// ===========================================
400// kinematics
401// lorentz invariant
402// m^2 = (E - Eloss)^2 - (factor*p)^2
403// factor*2 = ( (E - Eloss)^2 - m^2 )/p^2
404// factor = sqrt( ((E - Eloss)^2 - m2)/(E^2 - m^2) )
405//
406// tof
407// beta = v/c
408// E = m/sqrt(1-beta^2)
409// v = sqrt(1-(M/E)^2) c
ClassImp(TNBodyReactionProcessor)
particle information class
IntVec_t fBeamNucleus
used only initialization at TSrim object
TLorentzVector GetLossEnergyVector(TLorentzVector vec, Double_t eloss)
Double_t GetRandomReactionDistance(Double_t range)
void Init(TEventCollection *col) override
void SetTrack(Double_t x, Double_t y, Double_t z, Double_t a, Double_t b)
void SetMassNumber(Int_t val)
TLorentzVector GetLorentzVector() const
void SetLorentzVector(Double_t x, Double_t y, Double_t z, Double_t t)
void SetThetaCM(Double_t val)
void SetCurrentZ(Double_t val)
void SetPhiCM(Double_t val)
void AddTime(Double_t val)
Double_t GetEnergy() const
void SetCharge(Int_t val)
void SetAtomicNumber(Int_t val)
void SetEnergy(Double_t val)
void SetEnergy(Double_t arg)
void SetExEnergy(Double_t arg)
void SetTheta(Double_t arg)
void SetXYZ(Double_t x, Double_t y, Double_t z)
return to the guide