1 | // ESAF : Euso Simulation and Analysis Framework |
---|
2 | // $Id: SlastShowerGenerator.cc 2991 2011-09-26 10:08:50Z mabl $ |
---|
3 | // Author: Dmitry V.Naumov 03/03/2004 |
---|
4 | |
---|
5 | /***************************************************************************** |
---|
6 | * ESAF: Euso Simulation and Analysis Framework * |
---|
7 | * * |
---|
8 | * Id: SlastShowerGenerator * |
---|
9 | * Package: Shower * |
---|
10 | * Coordinator: Sergio.Bottai, Dmitry Naumov * |
---|
11 | * * |
---|
12 | *****************************************************************************/ |
---|
13 | |
---|
14 | //_____________________________________________________________________________ |
---|
15 | // |
---|
16 | // SlastShowerGenerator |
---|
17 | // |
---|
18 | // Slast is short of "Shower Light Attenuated to the Space Telescope". |
---|
19 | // Originally written by Dmitry V.Naumov as a collection of F77 routines doing |
---|
20 | // the full chain: |
---|
21 | // Shower generation, light production (fluorescence and cherenkov), light |
---|
22 | // attenuation in the atmosphere. |
---|
23 | // See SlastLightToEuso C++ / Fortran interface which is doing the full chain. |
---|
24 | // |
---|
25 | // SlastShowerGenerator is the simple generator written in pure C++ which is |
---|
26 | // NOT doing the full simulation chain. Instead it returnes only the |
---|
27 | // ShowerTrack object which is taken later by ShowerLightSource and |
---|
28 | // RadiativeTransfer parts of the ESAF package. |
---|
29 | // |
---|
30 | // ESAF prodides some testing macros which can help to understand how to use |
---|
31 | // SlastShowerGenerator. See macros/CheckDistrs.C as an example. |
---|
32 | // |
---|
33 | // A simple way to use SlastShowerGenerator is directly in root: |
---|
34 | // |
---|
35 | // root[] gSystem->Load("libatmosphere.so"); |
---|
36 | // root[] gSystem->Load("libgenbase.so"); |
---|
37 | // root[] gSystem->Load("libshowers.so"); |
---|
38 | // root[] SlastShowerGenerator *ShowerGenerator = new SlastShowerGenerator; |
---|
39 | // root[] ShowerTrack *track = ShowerGenerator->Get(); |
---|
40 | // root[] track->DrawXYZ() |
---|
41 | // |
---|
42 | // as an example. |
---|
43 | // |
---|
44 | // Now having the track object in hand one can use whatever functions avalaible |
---|
45 | // for it. See ShowerTrack class for documentation of ShowerTrack object |
---|
46 | |
---|
47 | #include "SlastShowerGenerator.hh" |
---|
48 | #include "EsafRandom.hh" |
---|
49 | #include "MCTruth.hh" |
---|
50 | #include "ShowerTrack.hh" |
---|
51 | #include "Atmosphere.hh" |
---|
52 | #include "Config.hh" |
---|
53 | #include "EConst.hh" |
---|
54 | #include <TMath.h> |
---|
55 | #include <TRandom.h> |
---|
56 | #include <TProfile.h> |
---|
57 | #include <TH1F.h> |
---|
58 | #include <TGraph.h> |
---|
59 | #include "NumbersFileParser.hh" |
---|
60 | |
---|
61 | ClassImp(SlastShowerGenerator) |
---|
62 | using namespace TMath; |
---|
63 | using namespace sou; |
---|
64 | using namespace EConst; |
---|
65 | |
---|
66 | //_________________________________________________________________________________________ |
---|
67 | SlastShowerGenerator::SlastShowerGenerator(Bool_t quiet) : EventGenerator("slast++"), EsafMsgSource(), |
---|
68 | fTrack(0), fTruth(0), fInCome(0), fOutCome(0), fQuiet(kFALSE) { |
---|
69 | // This is ctor |
---|
70 | fQuiet = quiet; |
---|
71 | fSpectrumRdnm = 0; |
---|
72 | fEnergy = -1; |
---|
73 | if(!Init()) Msg(EsafMsg::Panic)<<"SlastShowerGenerator can not be initialized"<<MsgDispatch; |
---|
74 | } |
---|
75 | |
---|
76 | |
---|
77 | //_________________________________________________________________________________________ |
---|
78 | Bool_t SlastShowerGenerator::Init() { |
---|
79 | // |
---|
80 | // SlastShowerGenerator is initialized reading from config/LightToEuso/GeneratorLightToEuso.cfg file |
---|
81 | // Edit that file to setup desired parameters of the showers such as energy, theta, phi intervals |
---|
82 | // or initial position in case of unique parameters. |
---|
83 | // Init() is called in SlastShowerGenerator ctor |
---|
84 | // |
---|
85 | |
---|
86 | // General Shower Generator initializations |
---|
87 | ConfigFileParser *pConfig = Config::Get()->GetCF("LightToEuso","GeneratorLightToEuso"); |
---|
88 | fFoV = pConfig->GetNum("GeneratorLightToEuso.FoV")*deg; |
---|
89 | fSpectrumType = pConfig->GetStr("GeneratorLightToEuso.SpectrumType"); |
---|
90 | fEnergyMin = pConfig->GetNum("GeneratorLightToEuso.EnergyRangeMin"); |
---|
91 | fEnergyMax = pConfig->GetNum("GeneratorLightToEuso.EnergyRangeMax"); |
---|
92 | fEnergySlope = pConfig->GetNum("GeneratorLightToEuso.EnergySlope"); |
---|
93 | fThetaMin = pConfig->GetNum("GeneratorLightToEuso.ThetaRangeMin")*deg; |
---|
94 | fThetaMax = pConfig->GetNum("GeneratorLightToEuso.ThetaRangeMax")*deg; |
---|
95 | fPhiMin = pConfig->GetNum("GeneratorLightToEuso.PhiRangeMin")*deg; |
---|
96 | fPhiMax = pConfig->GetNum("GeneratorLightToEuso.PhiRangeMax")*deg; |
---|
97 | fType = pConfig->GetStr("GeneratorLightToEuso.UhecrType"); |
---|
98 | fDepthStep = pConfig->GetNum("GeneratorLightToEuso.DepthStep")*g/cm2; |
---|
99 | fFirstPointX = pConfig->GetNum("GeneratorLightToEuso.InteractionVectorX")*km; |
---|
100 | fFirstPointY = pConfig->GetNum("GeneratorLightToEuso.InteractionVectorY")*km; |
---|
101 | fFirstPointZ = pConfig->GetNum("GeneratorLightToEuso.InteractionVectorZ")*km; |
---|
102 | fFirstType = pConfig->GetStr("GeneratorLightToEuso.InteractionType"); |
---|
103 | fEusoAltitude = pConfig->GetNum("GeneratorLightToEuso.altitude")*km; //TOFIX : to be changed when more general FoV handling achieved |
---|
104 | fEusoVector.SetXYZ(0,0,fEusoAltitude); |
---|
105 | fRejectFakeEvents = pConfig->GetBool("GeneratorLightToEuso.RejectFakeEvents"); |
---|
106 | fRejectNoXmax = pConfig->GetBool("GeneratorLightToEuso.RejectNoXmax"); |
---|
107 | if(!fRejectFakeEvents && fRejectNoXmax) |
---|
108 | Msg(EsafMsg::Panic) << "<Init> if RejectFakeEvents not required, RejectNoXmax cannot be required" << MsgDispatch; |
---|
109 | |
---|
110 | // check if bounds are > 1deg -- EXCEPTED in "POS" option of fFirstType |
---|
111 | if((fThetaMax - fThetaMin) < kTolerance && fFirstType != "POS") { |
---|
112 | Msg(EsafMsg::Warning) << "<Init> theta bounds width cannot be zero : theta max increased by 1 degree" << MsgDispatch; |
---|
113 | fThetaMax += 1*DegToRad(); |
---|
114 | } |
---|
115 | if((fPhiMax - fPhiMin) < kTolerance && fFirstType != "POS") { |
---|
116 | Msg(EsafMsg::Warning) << "<Init> phi bounds width cannot be zero : phi max increased by 1 degree" << MsgDispatch; |
---|
117 | fPhiMax += 1*DegToRad(); |
---|
118 | } |
---|
119 | |
---|
120 | if (!fQuiet) { |
---|
121 | Msg(EsafMsg::Info) << "SlastShowerGenerator Enabled" << MsgDispatch; |
---|
122 | Msg(EsafMsg::Info) << "SlastShowerGenerator Enables Atmosphere " << Atmosphere::Get()->GetType() << MsgDispatch; |
---|
123 | MsgForm(EsafMsg::Info,"Generate %s event(s) in:",fType.c_str()); |
---|
124 | MsgForm(EsafMsg::Info,"Energy interval [%.4E,%.4E]",fEnergyMin,fEnergyMax); |
---|
125 | MsgForm(EsafMsg::Info,"Theta interval [%.4f,%.4f]",fThetaMin,fThetaMax); |
---|
126 | MsgForm(EsafMsg::Info,"Phi interval [%.4f,%.4f]",fPhiMin,fPhiMax); |
---|
127 | } |
---|
128 | else { |
---|
129 | MsgForm(EsafMsg::Debug,"Generate %s event(s) in:",fType.c_str()); |
---|
130 | MsgForm(EsafMsg::Debug,"Energy interval [%.4E,%.4E]",fEnergyMin,fEnergyMax); |
---|
131 | MsgForm(EsafMsg::Debug,"Theta interval [%.4f,%.4f]",fThetaMin,fThetaMax); |
---|
132 | MsgForm(EsafMsg::Debug,"Phi interval [%.4f,%.4f]",fPhiMin,fPhiMax); |
---|
133 | |
---|
134 | } |
---|
135 | fAtomicMass = AtomicMass(fType); |
---|
136 | |
---|
137 | // if non-analytical RC spectrum |
---|
138 | if(fSpectrumType == "GZKHiRes2005") { |
---|
139 | // GZK shape from best fit of HiRes data ( Phys. Lett. B 619 (2005) ) |
---|
140 | // graph is recopied "by hand" using g3data software |
---|
141 | // between 1e19-1e21 eV only |
---|
142 | if(fEnergyMin < 1e19 || fEnergyMax > 1e21) |
---|
143 | Msg(EsafMsg::Panic) << "<Init> GZKHiRes2005 spectrum stands within [1e19 - 1e21] only !" << MsgDispatch; |
---|
144 | NumbersFileParser nf("config/Tools/GZKHiRes2005.dat",2); |
---|
145 | vector<Double_t> log10E_v = nf.GetCol(0); // expressed in eV |
---|
146 | vector<Double_t> fluxE3_v = nf.GetCol(1); // expressed in 10^24 eV2.m-2.s-1.sr-1 |
---|
147 | TArrayD log10E(log10E_v.size()), fluxE3(log10E_v.size()); |
---|
148 | for(size_t i=0; i < log10E_v.size(); i++) { |
---|
149 | log10E[i] = log10E_v[i]; |
---|
150 | fluxE3[i] = fluxE3_v[i]; |
---|
151 | } |
---|
152 | TGraph* gr = new TGraph(log10E_v.size(),log10E.GetArray(),fluxE3.GetArray()); |
---|
153 | fSpectrumRdnm = new TH1F("GZK","GZK",10000,Log10(fEnergyMin),Log10(fEnergyMax)); |
---|
154 | for(Int_t i=0; i < 10000; i++) { |
---|
155 | fSpectrumRdnm->SetBinContent(i+1,gr->Eval(fSpectrumRdnm->GetBinCenter(i+1),0,"") * log(10.) / pow(10,fSpectrumRdnm->GetBinCenter(i+1)*2. - 24.)); |
---|
156 | } |
---|
157 | } |
---|
158 | |
---|
159 | |
---|
160 | // Initialize SLAST specific options |
---|
161 | pConfig = Config::Get()->GetCF("LightToEuso","SlastLightToEuso"); |
---|
162 | fEnergyDistribution = pConfig->GetStr("SlastLightToEuso.EnergyDistributionParametrization"); |
---|
163 | fShowerParametrization = pConfig->GetStr("SlastLightToEuso.ShowerParametrization"); |
---|
164 | return kTRUE; |
---|
165 | } |
---|
166 | |
---|
167 | //_________________________________________________________________________________________ |
---|
168 | SlastShowerGenerator::~SlastShowerGenerator() { |
---|
169 | // This is dtor |
---|
170 | SafeDelete(fTrack); |
---|
171 | SafeDelete(fTruth); |
---|
172 | SafeDelete(fSpectrumRdnm); |
---|
173 | } |
---|
174 | |
---|
175 | //_________________________________________________________________________________________ |
---|
176 | void SlastShowerGenerator::SetShower(Double_t e,Double_t t,Double_t p,EarthVector pos) { |
---|
177 | // This is a special method to setup desired showertrack parameters from the code (not from the config file) |
---|
178 | // This can be a usefull function in the reconstruction code |
---|
179 | |
---|
180 | SetEnergy(fEnergyMin = fEnergyMax = e); |
---|
181 | SetTheta(fThetaMin = fThetaMax = t); |
---|
182 | SetPhi(fPhiMin = fPhiMax = p); |
---|
183 | fFirstPointX = pos.X(); |
---|
184 | fFirstPointY = pos.Y(); |
---|
185 | fFirstPointZ = pos.Z(); |
---|
186 | fFirstType = "POS"; |
---|
187 | } |
---|
188 | |
---|
189 | //_________________________________________________________________________________________ |
---|
190 | void SlastShowerGenerator::GetX1() { |
---|
191 | // |
---|
192 | // Returns atmosphere depth before the first interaction. |
---|
193 | // Attention: units are ESAF internal. In order to get in human way one has to multiple by cm2/g |
---|
194 | // |
---|
195 | |
---|
196 | if ( fFirstType == "POS" ) { |
---|
197 | fX1 = Atmosphere::Get()->Grammage(fInitPoint,-fOmega,"dir"); |
---|
198 | } |
---|
199 | else if (fFirstType == "X1" ) { |
---|
200 | ConfigFileParser *pConfig = Config::Get()->GetCF("LightToEuso","GeneratorLightToEuso"); |
---|
201 | fX1 = pConfig->GetNum("GeneratorLightToEuso.InteractionX1")*g/cm2; |
---|
202 | } |
---|
203 | else { |
---|
204 | TRandom *rndm = EsafRandom::Get(); |
---|
205 | fX1 = -Log(rndm->Rndm())*HadronInteractionLength(fAtomicMass,fEnergy)*g/cm2; |
---|
206 | } |
---|
207 | } |
---|
208 | |
---|
209 | //_________________________________________________________________________________________ |
---|
210 | Double_t SlastShowerGenerator::HadronInteractionLength(Double_t A, Double_t E) { |
---|
211 | // SlastShowerGenerator internal function which computes the hadronic interactio length. |
---|
212 | // Used by GetX1() method |
---|
213 | Float_t R0 = 1.287e-13, Aair = 14.483, q = 0.93, AN = 14.00674, AO = 15.9994; |
---|
214 | Float_t AA = 39.948, AC = 12.011, CN = 0.7847, CO = 0.2105, CA = 0.0047, CC = 0.0001; |
---|
215 | Float_t S_A = Pi()*Power(R0,2); |
---|
216 | Float_t F_A1 = Power(S_A*Na(),-1); |
---|
217 | Float_t F_A2 = Aair*F_A1; |
---|
218 | Float_t X = Power(A,1./3); |
---|
219 | Float_t Y = 1./X; |
---|
220 | Float_t B = Power(AN,1./3); |
---|
221 | Float_t S = CN*Power(B+X-q*(1./B+Y),2); // Nitrogen (N2); |
---|
222 | B = Power(AO,1./3); |
---|
223 | S += CO*Power(B+X-q*(1./B+Y),2); // Oxigen (O2) |
---|
224 | B = Power(AA,1./3); |
---|
225 | S += CA*Power(B+X-q*(1./B+Y),2); // Argon (Ar) |
---|
226 | B = Power(AC,1./3); |
---|
227 | S += CC*Power(B+X-q*(1./B+Y),2); // Carbon (in CO2) |
---|
228 | return F_A2/S*NucleonAirCrossSection(1.e11/A)/NucleonAirCrossSection(E/A); |
---|
229 | } |
---|
230 | |
---|
231 | //_________________________________________________________________________________________ |
---|
232 | Double_t SlastShowerGenerator::NucleonAirCrossSection(Double_t E) { |
---|
233 | /* |
---|
234 | Computes NUCLEN + AIR CROSS SECTION: |
---|
235 | ... USES AN EMPIRICAL PARAMETRIZATION OF TOTAL INELASTIC |
---|
236 | ... NUCLEON AIR CROSS SECTION (in mbarn) |
---|
237 | ... INPUT: NUCLEON ENERGY in eV |
---|
238 | ... OUTPUT: NUCLEON AIR CROSS SECTION (in mbarn) |
---|
239 | ... REFERENCES: |
---|
240 | ... 1. Mielke H.H., Foller, M, Knapp J. |
---|
241 | ... // J.Phys.G: Nucl.Part.Phys. 1994. V 20, P637 |
---|
242 | ... 2. V.A. Naumov, T.S. Sinegovskaya (eq (17)) |
---|
243 | */ |
---|
244 | return 290 - 8.7*Log(E*1.e-9) + 1.14*Power(Log(E*1.e-9),2); |
---|
245 | } |
---|
246 | |
---|
247 | //_________________________________________________________________________________________ |
---|
248 | const ShowerStep& SlastShowerGenerator::GetShowerStep() { |
---|
249 | // Filling the ShowerStep object with relevant information |
---|
250 | fStep.Clear(); |
---|
251 | fStep.fXi = fXcurrent + fX1; |
---|
252 | fStep.fXf = fXnext + fX1; |
---|
253 | fStep.fXYZi = fCurrentPoint; |
---|
254 | fStep.fXYZf = fNextPoint; |
---|
255 | fStep.fTimei = fTimecurrent; |
---|
256 | fStep.fTimef = fTimenext; |
---|
257 | GetShowerParametrization(fXcurrent); |
---|
258 | fStep.fAgei = fAge; |
---|
259 | GetShowerParametrization(fXnext); |
---|
260 | fStep.fAgef = fAge; |
---|
261 | GetShowerParametrization((fXnext+fXcurrent)/2); |
---|
262 | fStep.fNelectrons = fNe; |
---|
263 | return fStep; |
---|
264 | } |
---|
265 | |
---|
266 | //_________________________________________________________________________________________ |
---|
267 | void SlastShowerGenerator::GetShowerParametrization(Double_t x) { |
---|
268 | // |
---|
269 | // Assigning of fNe - number of electrons in the current step according to the Shower Parameterization. |
---|
270 | // GIL stands for Greizen-Ilina-Linsley which is the QGSJET model |
---|
271 | // parameterization. |
---|
272 | // GFA stands for Gaussian Function in Age |
---|
273 | // GHF stands for Gaisser Hillas Function |
---|
274 | // |
---|
275 | |
---|
276 | if (fShowerParametrization == "GIL") GIL(x); |
---|
277 | else Msg(EsafMsg::Panic) << "<GetShowerParametrization> Only GIL formula available so far" <<MsgDispatch; |
---|
278 | //TOFIX : GFA and GHF not coded correctly, they do not included X1 fluctuations |
---|
279 | /* |
---|
280 | else if (fShowerParametrization == "GFA") GFA(x + fX1); |
---|
281 | else if (fShowerParametrization == "GHF") GHF(x + fX1); |
---|
282 | else Msg(EsafMsg::Panic) << "<GetShowerParametrization> Wrong argument for ShowerParametrization :" |
---|
283 | << fShowerParametrization <<MsgDispatch; |
---|
284 | */ |
---|
285 | } |
---|
286 | |
---|
287 | //_________________________________________________________________________________________ |
---|
288 | Bool_t SlastShowerGenerator::FindImpactOnTOA() { |
---|
289 | // |
---|
290 | // Generates point uniformly on top of atmosphere on the upper part of the earth hemisphere |
---|
291 | // theta, phi angles are taken at TOA impact, then translated into MES frame for further steps of event simulation |
---|
292 | // |
---|
293 | |
---|
294 | TRandom *rndm = EsafRandom::Get(); |
---|
295 | ConfigFileParser* pConfig = Config::Get()->GetCF("LightToEuso","GeneratorLightToEuso"); |
---|
296 | string ImpactMode = pConfig->GetStr("GeneratorLightToEuso.ImpactMode"); |
---|
297 | string thetaMode = pConfig->GetStr("GeneratorLightToEuso.ThetaMode"); |
---|
298 | const Atmosphere* atmo = Atmosphere::Get(); |
---|
299 | |
---|
300 | |
---|
301 | #ifdef DEBUG |
---|
302 | Int_t counterr = 0; |
---|
303 | #endif |
---|
304 | |
---|
305 | |
---|
306 | // find impact on TOA, then find impact at sea level if any. Otherwise, set earthimpact to ******* TOA outgoing point ********** |
---|
307 | // principle : - uniform distribution on a sphere of radius EarthRadius + TOA_altitude |
---|
308 | // - sin(2theta) distribution, phi uniform distribution (LOCAL angles, at TOA impact centered frame) |
---|
309 | // - check if defined track cut the EUSO FoV cone, if NOT --> re-try |
---|
310 | if(ImpactMode == "TOA") { |
---|
311 | Double_t TOA_alt = atmo->GetTOAAltitude(); |
---|
312 | Double_t Euso_alt = fEusoVector.Z(); |
---|
313 | Double_t FoV_radius = Euso_alt * Tan(fFoV + 1*deg); |
---|
314 | Double_t L1 = Sqrt( (EarthRadius() + TOA_alt)*(EarthRadius() + TOA_alt) - EarthRadius()*EarthRadius() ); |
---|
315 | Double_t Zmin,x,y,z; |
---|
316 | Zmin = EarthRadius()*Sqrt(1 + 2*TOA_alt/EarthRadius() + (TOA_alt*TOA_alt - FoV_radius*FoV_radius)/(EarthRadius()*EarthRadius())); |
---|
317 | Zmin -= 2*L1 * Sqrt(1 - EarthRadius()*EarthRadius() / (EarthRadius() + TOA_alt) / (EarthRadius() + TOA_alt)); |
---|
318 | |
---|
319 | #ifdef DEBUG |
---|
320 | //cout << "********** Zmin = "<<Zmin <<endl; |
---|
321 | |
---|
322 | // *********** if EUSO tilted mode, Zmin is different. The rest of the code (FoV intersection etc..) should be adapted too ********* |
---|
323 | /* |
---|
324 | else if(ImpactMode == "TOA_tiltedEuso") { |
---|
325 | Double_t L2 = Sqrt( (EarthRadius() + Euso_alt)*(EarthRadius() + Euso_alt) - EarthRadius()*EarthRadius() ); |
---|
326 | Zmin = EarthRadius() + Euso_alt; |
---|
327 | Zmin -= (L1 + L2) * Sqrt(1 - EarthRadius()*EarthRadius() / (EarthRadius() + Euso_alt) / (EarthRadius() + Euso_alt)); |
---|
328 | } |
---|
329 | */ |
---|
330 | #endif |
---|
331 | |
---|
332 | // find a good candidate shower, which has an opportunity to be seen by EUSO |
---|
333 | Bool_t eventisfound = kFALSE; |
---|
334 | Int_t infloop = 0; |
---|
335 | while(!eventisfound) { |
---|
336 | #ifdef DEBUG |
---|
337 | counterr++; |
---|
338 | #endif |
---|
339 | // prevent infinite loop |
---|
340 | if(infloop++ > 100000) { |
---|
341 | Msg(EsafMsg::Warning) << "<FindImpactOnTOA> TOA impact mode : Inifinite loop broken by hand" << MsgDispatch; |
---|
342 | return kFALSE; |
---|
343 | } |
---|
344 | |
---|
345 | // 1. get an impact on TOA, uniformly distributed on the upper part of a sphere |
---|
346 | // cut in Z (i.e. Zmin, expressed in earth-centered frame) is determined from geometrical considerations |
---|
347 | z = (EarthRadius()+TOA_alt - Zmin)*rndm->Rndm() + Zmin; |
---|
348 | Double_t phi = rndm->Rndm()*TwoPi(); |
---|
349 | x = EarthRadius()*Sin(ACos(z / (EarthRadius()+TOA_alt) ))*Cos(phi); |
---|
350 | y = EarthRadius()*Sin(ACos(z / (EarthRadius()+TOA_alt) ))*Sin(phi); |
---|
351 | |
---|
352 | |
---|
353 | // 2. Generate random Theta and Phi w.r.t. local frame |
---|
354 | // sin(2x) OR linear OR Hmax modes |
---|
355 | // last one achieves flat Hmax distribution, at E=1e20eV with USStd -> theta in [0,70], comes from Hmax = 1.89 - 7.59*log(cos(th)) |
---|
356 | fPhi_local = (TwoPi() - 0)*rndm->Rndm() + 0; |
---|
357 | if(thetaMode == "sinus") { |
---|
358 | Double_t r1 = 1.; // cos(2*0) |
---|
359 | Double_t r2 = -1.; // cos(2*halfpi) |
---|
360 | fTheta_local = 0.5*ACos(r1 - rndm->Rndm()*(r1-r2)); |
---|
361 | } |
---|
362 | else if(thetaMode == "linear") fTheta_local = (PiOver2() - 0)*rndm->Rndm() + 0; |
---|
363 | else if(thetaMode == "FlatHmax") fTheta_local = ACos( Exp((1.89 - 10)*rndm->Rndm() / 7.59)); |
---|
364 | else Msg(EsafMsg::Panic) << "Wrong argument for theta Mode -> " <<thetaMode << MsgDispatch; |
---|
365 | |
---|
366 | |
---|
367 | // 3. Change fOmega from local frame to MES frame, using impact on TOA as the reference vector |
---|
368 | EarthVector TOA_impact_try(x,y,z); // in earth-centered frame |
---|
369 | EarthVector v1 = TOA_impact_try.Unit(); |
---|
370 | EarthVector vrot; |
---|
371 | fOmega.SetXYZ(1,0,0); |
---|
372 | EarthVector Uz(0,0,1); |
---|
373 | vrot = Uz.Cross(v1); |
---|
374 | if(vrot.Mag() > kTolerance) { |
---|
375 | fOmega.Rotate(v1.Theta(),vrot); |
---|
376 | fOmega.Rotate(fPhi_local,v1); |
---|
377 | vrot = v1.Cross(fOmega); |
---|
378 | fOmega.Rotate(PiOver2()+fTheta_local,vrot); |
---|
379 | } |
---|
380 | else fOmega.SetMagThetaPhi(1.,Pi() - fTheta_local,fPhi_local + Pi()); |
---|
381 | |
---|
382 | // set angles in MES frame + check if they are within required bounds |
---|
383 | fTheta_mes = Pi() - fOmega.Theta(); |
---|
384 | if(fTheta_mes < fThetaMin || fTheta_mes > fThetaMax) continue; |
---|
385 | |
---|
386 | // to get phi in [0,2pi] (root returns it within [-pi,pi]) |
---|
387 | if(fOmega.Phi() < 0) fPhi_mes = (fOmega.Phi() + TwoPi()) - Pi(); |
---|
388 | else fPhi_mes = fOmega.Phi() + Pi(); |
---|
389 | if(fPhi_mes < fPhiMin || fPhi_mes > fPhiMax) continue; |
---|
390 | |
---|
391 | |
---|
392 | // returns in MES frame |
---|
393 | TOA_impact_try.SetZ(TOA_impact_try.Z() - EarthRadius()); |
---|
394 | |
---|
395 | |
---|
396 | fOutOfFoV = kFALSE; |
---|
397 | EarthVector intersection, impactASL; |
---|
398 | // 4. FoV handling |
---|
399 | if(IsInFoV(TOA_impact_try)) eventisfound = kTRUE; // look if TOA impact is in FoV |
---|
400 | else if(!fRejectFakeEvents) { |
---|
401 | // event always kept in this case |
---|
402 | EarthVector intersection = FoVintersection(TOA_impact_try,fOmega); |
---|
403 | EarthVector impactASL = atmo->ImpactASL(TOA_impact_try,fOmega); |
---|
404 | // check |
---|
405 | // - if track cuts the EUSO FoV |
---|
406 | // - if intersection is above ground |
---|
407 | // - if intersection is below TOA |
---|
408 | // - if track has reached ground before intersection |
---|
409 | intersection = FoVintersection(TOA_impact_try,fOmega); |
---|
410 | impactASL = atmo->ImpactASL(TOA_impact_try,fOmega); |
---|
411 | if(intersection.Z() == -HUGE || intersection.Z() == HUGE || intersection.IsUnderSeaLevel()) fOutOfFoV = kTRUE; |
---|
412 | // check if intersection is below TOA altitude |
---|
413 | else if(intersection.Zv() > TOA_alt) fOutOfFoV = kTRUE; |
---|
414 | // check if track reaches ground before its intersection with euso FoV cone |
---|
415 | else if((impactASL - TOA_impact_try).Mag() < (intersection - TOA_impact_try).Mag()) fOutOfFoV = kTRUE; |
---|
416 | eventisfound = kTRUE; |
---|
417 | } |
---|
418 | else { |
---|
419 | // track is kept |
---|
420 | // - if track cuts the EUSO FoV |
---|
421 | // - if intersection is above ground |
---|
422 | // - if intersection is below TOA |
---|
423 | // - if track has reached ground before intersection |
---|
424 | intersection = FoVintersection(TOA_impact_try,fOmega); |
---|
425 | impactASL = atmo->ImpactASL(TOA_impact_try,fOmega); |
---|
426 | if(intersection.Z() == -HUGE || intersection.Z() == HUGE || intersection.IsUnderSeaLevel()) continue; |
---|
427 | // check if intersection is below TOA altitude |
---|
428 | else if(intersection.Zv() > TOA_alt) continue; |
---|
429 | // check if track reaches ground before its intersection with euso FoV cone |
---|
430 | else if((impactASL - TOA_impact_try).Mag() < (intersection - TOA_impact_try).Mag()) continue; |
---|
431 | else eventisfound = kTRUE; |
---|
432 | fOutOfFoV = kFALSE; // flag is disabled in 'fRejectFakeEvents == kTRUE' mode |
---|
433 | } |
---|
434 | } |
---|
435 | |
---|
436 | |
---|
437 | // 5. a valid TOA impact has been found |
---|
438 | fTOAImpact.SetXYZ(x,y,z - EarthRadius()); |
---|
439 | |
---|
440 | #ifdef DEBUG |
---|
441 | Msg(EsafMsg::Debug) <<"********** Nb of trials = "<<counterr<<MsgDispatch; |
---|
442 | /* |
---|
443 | Msg(EsafMsg::Debug) <<"********** TOA found = "<<fTOAImpact<<MsgDispatch; |
---|
444 | Msg(EsafMsg::Debug) <<"********** TOA found = "<<fTOAImpact.Mag()<<", "<<fTOAImpact.Theta()*RadToDeg()<<", "<<fTOAImpact.Phi()*RadToDeg()<<MsgDispatch; |
---|
445 | |
---|
446 | Msg(EsafMsg::Debug) <<"********** LOCAL theta,phi = "<<fTheta_local*RadToDeg()<<", "<<fPhi_local*RadToDeg()<<MsgDispatch; |
---|
447 | Msg(EsafMsg::Debug) <<"********** direction found = "<<fOmega<<MsgDispatch; |
---|
448 | Msg(EsafMsg::Debug) <<"********** direction found = "<<fOmega.Mag()<<", "<<fOmega.Theta()*RadToDeg()<<", "<<fOmega.Phi()*RadToDeg()<<MsgDispatch; |
---|
449 | */ |
---|
450 | #endif |
---|
451 | |
---|
452 | |
---|
453 | // 4. Find impact at sea level if exists |
---|
454 | // If it does not, set impact to outgoing point at TOA |
---|
455 | fEarthImpact = atmo->ImpactASL(fTOAImpact,fOmega); |
---|
456 | fHitGround = 0; |
---|
457 | if(fEarthImpact.Z() == -HUGE) Msg(EsafMsg::Warning) << "<FindImpactOnTOA> ImpactASL says fTOAImpact is under sea level" << MsgDispatch; |
---|
458 | else if(fEarthImpact.Z() == HUGE) Msg(EsafMsg::Debug) << "<FindImpactOnTOA> No ImpactASL found" << MsgDispatch; |
---|
459 | else fHitGround = 1; |
---|
460 | |
---|
461 | if(fHitGround == 0) { |
---|
462 | fEarthImpact = atmo->ImpactAtTOA(fTOAImpact,fOmega); |
---|
463 | if(fEarthImpact.Z() == -HUGE) Msg(EsafMsg::Warning) << "<FindImpactOnTOA> ImpactAtTOA says fTOAImpact is under sea level" << MsgDispatch; |
---|
464 | else if(fEarthImpact.Z() == HUGE) Msg(EsafMsg::Warning) << "<FindImpactOnTOA> No ImpactAtTOA found. IT SHOULD NOT OCCUR HERE" << MsgDispatch; |
---|
465 | } |
---|
466 | } |
---|
467 | |
---|
468 | |
---|
469 | |
---|
470 | |
---|
471 | |
---|
472 | |
---|
473 | // find impact at sea level, then find impact on TOA |
---|
474 | else if(ImpactMode == "ASL") { |
---|
475 | Msg(EsafMsg::Warning) << "<FindImpactOnTOA> ASL impact mode : Cosmic Ray LOCAL direction is taken at sea level" << MsgDispatch; |
---|
476 | Msg(EsafMsg::Warning) << "<FindImpactOnTOA> ASL impact mode : DOES NOT CHECK IF TRACK CUTS THE FOV" << MsgDispatch; |
---|
477 | |
---|
478 | Int_t infloop = 0; |
---|
479 | while(kTRUE) { |
---|
480 | if(infloop++ > 100000) { |
---|
481 | Msg(EsafMsg::Warning) << "<FindImpactOnTOA> ASL impact mode : Inifinite loop broken by hand" << MsgDispatch; |
---|
482 | return kFALSE; |
---|
483 | } |
---|
484 | |
---|
485 | // 1. find impact at sea level |
---|
486 | Double_t x,y,z; |
---|
487 | Double_t Xmin = pConfig->GetNum("GeneratorLightToEuso.ImpactXmin")*km; |
---|
488 | Double_t Ymin = pConfig->GetNum("GeneratorLightToEuso.ImpactYmin")*km; |
---|
489 | Double_t Xmax = pConfig->GetNum("GeneratorLightToEuso.ImpactXmax")*km; |
---|
490 | Double_t Ymax = pConfig->GetNum("GeneratorLightToEuso.ImpactYmax")*km; |
---|
491 | x = rndm->Rndm()*(Xmax - Xmin) + Xmin; |
---|
492 | y = rndm->Rndm()*(Ymax - Ymin) + Ymin; |
---|
493 | z = sqrt(EarthRadius()*EarthRadius() - (x*x + y*y)); // needs -EarthRadius() before setting z !! |
---|
494 | |
---|
495 | fEarthImpact.SetXYZ(x,y,z); |
---|
496 | fEarthImpact.SetZ(fEarthImpact.Z() - EarthRadius()); |
---|
497 | fHitGround = 1; |
---|
498 | |
---|
499 | |
---|
500 | // 2. Generate random Theta and Phi with respect to the MSS (local frame) |
---|
501 | // sin(2x) OR linear OR Hmax modes |
---|
502 | // last one achieving flat Hmax distribution, at E=1e20eV with USStd -> theta in [0,70], comes from Hmax = 1.89 - 7.59*log(cos(th)) |
---|
503 | fPhi_local = (TwoPi() - 0)*rndm->Rndm() + 0; |
---|
504 | if(thetaMode == "sinus") { |
---|
505 | Double_t r1 = 1.; // cos(2*0) |
---|
506 | Double_t r2 = -1.; // cos(2*halfpi) |
---|
507 | fTheta_local = 0.5*ACos(r1 - rndm->Rndm()*(r1-r2)); |
---|
508 | } |
---|
509 | else if(thetaMode == "linear") fTheta_local = (PiOver2() - 0)*rndm->Rndm() + 0; |
---|
510 | else if(thetaMode == "FlatHmax") fTheta_local = ACos( Exp((1.89 - 10)*rndm->Rndm() / 7.59)); |
---|
511 | else Msg(EsafMsg::Panic) << "Wrong argument for theta Mode -> " <<thetaMode << MsgDispatch; |
---|
512 | |
---|
513 | |
---|
514 | // 3. Change fOmega from local frame (MSS) to MES frame, using impact on ground as the reference vector |
---|
515 | EarthVector v1(x,y,z); |
---|
516 | v1 = v1.Unit(); |
---|
517 | EarthVector vrot; |
---|
518 | fOmega.SetXYZ(1,0,0); |
---|
519 | EarthVector Uz(0,0,1); |
---|
520 | vrot = Uz.Cross(v1); |
---|
521 | if(vrot.Mag() > kTolerance) { |
---|
522 | fOmega.Rotate(v1.Theta(),vrot); |
---|
523 | fOmega.Rotate(fPhi_local,v1); |
---|
524 | vrot = v1.Cross(fOmega); |
---|
525 | fOmega.Rotate(Pi()/2+fTheta_local,vrot); |
---|
526 | } |
---|
527 | else fOmega.SetMagThetaPhi(1.,Pi() - fTheta_local,fPhi_local + Pi()); |
---|
528 | |
---|
529 | |
---|
530 | // set angles in MES frame + check if they are within required bounds |
---|
531 | fTheta_mes = Pi() - fOmega.Theta(); |
---|
532 | fOutOfFoV = kFALSE; |
---|
533 | // to get phi in [0,2pi] (root returns it within [-pi,pi] |
---|
534 | if(fOmega.Phi() < 0) fPhi_mes = (fOmega.Phi() + TwoPi()) - Pi(); |
---|
535 | else fPhi_mes = fOmega.Phi() + Pi(); |
---|
536 | if(fPhi_mes < fPhiMin || fPhi_mes > fPhiMax || fTheta_mes < fThetaMin || fTheta_mes > fThetaMax) continue; |
---|
537 | else break; |
---|
538 | } |
---|
539 | |
---|
540 | |
---|
541 | // 4. Find TOA impact |
---|
542 | fTOAImpact = atmo->ImpactAtTOA(fEarthImpact,EarthVector(-fOmega)); |
---|
543 | if(fTOAImpact.Z() == -HUGE) Msg(EsafMsg::Warning) << "<FindImpactOnTOA> ImpactAtTOA says fEarthImpact is under sea level" << MsgDispatch; |
---|
544 | if(fTOAImpact.Z() == HUGE) Msg(EsafMsg::Warning) << "<FindImpactOnTOA> No ImpactAtTOA found : it Should not occur here" << MsgDispatch; |
---|
545 | |
---|
546 | |
---|
547 | // 4. refine value of impact at sea level (should exist) |
---|
548 | // If it does not, set impact to outgoing point at TOA |
---|
549 | fEarthImpact = atmo->ImpactASL(fTOAImpact,fOmega); |
---|
550 | fHitGround = 0; |
---|
551 | if(fEarthImpact.Z() == -HUGE) Msg(EsafMsg::Warning) << "<FindImpactOnTOA> ImpactASL says fTOAImpact is under sea level" << MsgDispatch; |
---|
552 | else if(fEarthImpact.Z() == HUGE) Msg(EsafMsg::Debug) << "<FindImpactOnTOA> No ImpactASL found" << MsgDispatch; |
---|
553 | else fHitGround = 1; |
---|
554 | if(fHitGround == 0) { |
---|
555 | Msg(EsafMsg::Warning) << "<FindImpactASL> No earth impact found. IT SHOULD NOT OCCUR IN ASL mode" << MsgDispatch; |
---|
556 | fEarthImpact = atmo->ImpactAtTOA(fTOAImpact,fOmega); |
---|
557 | if(fEarthImpact.Z() == -HUGE) Msg(EsafMsg::Warning) << "<FindImpactOnTOA> ImpactAtTOA says fTOAImpact is under sea level" << MsgDispatch; |
---|
558 | else if(fEarthImpact.Z() == HUGE) Msg(EsafMsg::Warning) << "<FindImpactOnTOA> No ImpactAtTOA found. IT SHOULD NOT OCCUR HERE" << MsgDispatch; |
---|
559 | } |
---|
560 | } |
---|
561 | |
---|
562 | else Msg(EsafMsg::Panic) << "Wrong argument for GeneratorLightToEuso.ImpactMode config parameter" <<MsgDispatch; |
---|
563 | |
---|
564 | |
---|
565 | return kTRUE; |
---|
566 | } |
---|
567 | |
---|
568 | //_____________________________________________________________________________________________________ |
---|
569 | Bool_t SlastShowerGenerator::IsInFoV(const EarthVector& pos) const { |
---|
570 | // |
---|
571 | // returns true if pos is within Euso FoV cone |
---|
572 | // |
---|
573 | |
---|
574 | // 1. change of frame : from MES to NES (local frame with origin on EUSO + Z-axis directed toward the Earth) |
---|
575 | // translation --> EUSO vector translation + rotation around x-axis to change Z direction to the opposite |
---|
576 | // thus MES/NES Y-axes are in the opposite sens |
---|
577 | EarthVector Ux(1,0,0); |
---|
578 | EarthVector pos_e = pos; |
---|
579 | pos_e -= fEusoVector; |
---|
580 | pos_e.Rotate(Pi(),Ux); |
---|
581 | |
---|
582 | if(pos_e.Theta() < fFoV) return kTRUE; |
---|
583 | else return kFALSE; |
---|
584 | } |
---|
585 | |
---|
586 | //_____________________________________________________________________________________________________ |
---|
587 | EarthVector SlastShowerGenerator::FoVintersection(const EarthVector& pos, const EarthVector& dir) const { |
---|
588 | // |
---|
589 | // finds the intersection between the given track and the Euso FoV cone, if exists |
---|
590 | // if does not exist, returns (0,0,HUGE) |
---|
591 | // |
---|
592 | // intersection considered only above ground |
---|
593 | // |
---|
594 | |
---|
595 | Double_t w = Cos(fFoV)*Cos(fFoV); |
---|
596 | EarthVector direc = dir.Unit(); |
---|
597 | |
---|
598 | // 1. if pos is underground |
---|
599 | EarthVector rtn; |
---|
600 | if( (pos.Zv() + kAltitudeTolerance) < 0 ) { |
---|
601 | Msg(EsafMsg::Warning) << "<FoVintersection> Starting position is underground, SHOULD NOT OCCUR" << MsgDispatch; |
---|
602 | rtn.SetXYZ(0,0,-HUGE); |
---|
603 | return rtn; |
---|
604 | } |
---|
605 | |
---|
606 | // 2. change of frame : from MES to NES (local frame with origin on EUSO + Z-axis directed toward the Earth) |
---|
607 | // translation --> EUSO vector translation + rotation around x-axis to change Z direction to the opposite |
---|
608 | // thus MES/NES Y-axes are in the opposite sens |
---|
609 | EarthVector Ux(1,0,0); |
---|
610 | EarthVector pos_e = pos; |
---|
611 | EarthVector direc_e = direc; |
---|
612 | |
---|
613 | pos_e -= fEusoVector; |
---|
614 | direc_e.Rotate(Pi(),Ux); |
---|
615 | pos_e.Rotate(Pi(),Ux); |
---|
616 | |
---|
617 | |
---|
618 | // 3. now intersection calculation |
---|
619 | Double_t mag(0); |
---|
620 | // trinome defining solutions |
---|
621 | Double_t a = direc_e.Z()*direc_e.Z() - w; |
---|
622 | Double_t b = 2*pos_e.Z()*direc_e.Z() - 2*w*pos_e*direc_e; |
---|
623 | Double_t c = pos_e.Z()*pos_e.Z() - pos_e.Mag2()*w; |
---|
624 | pair<Int_t,Double_t*>& p = findRoots(a,b,c); // special cases with a=0 and b=c=0 handled |
---|
625 | if(p.first == 0) { |
---|
626 | rtn.SetXYZ(0,0,HUGE); |
---|
627 | return rtn; |
---|
628 | } |
---|
629 | if(p.first == 1) mag = p.second[0]; |
---|
630 | else if(p.first == 2) { |
---|
631 | if((p.second[0]+kAltitudeTolerance) * (p.second[1]+kAltitudeTolerance) < 0) mag = Max(p.second[0],p.second[1]); |
---|
632 | else mag = Min(p.second[0],p.second[1]); |
---|
633 | } |
---|
634 | // if intersection stands before pos, it is rejected |
---|
635 | if(mag < -kAltitudeTolerance) { |
---|
636 | rtn.SetXYZ(0,0,HUGE); |
---|
637 | return rtn; |
---|
638 | } |
---|
639 | |
---|
640 | rtn = pos_e + mag*direc_e; |
---|
641 | |
---|
642 | |
---|
643 | // 4. change of frame : from NES to MES frame |
---|
644 | rtn.Rotate(-Pi(),Ux); |
---|
645 | rtn += fEusoVector; |
---|
646 | |
---|
647 | return rtn; |
---|
648 | } |
---|
649 | |
---|
650 | //_________________________________________________________________________________________ |
---|
651 | Bool_t SlastShowerGenerator::GetFirstPoint(){ |
---|
652 | // |
---|
653 | // X1 is known --> find the first point fInitPoint from TOA impact |
---|
654 | // if event goes out atmosphere / OR / reaches ground without interacting, returns kFALSE |
---|
655 | // |
---|
656 | |
---|
657 | Bool_t rtn = kTRUE; |
---|
658 | |
---|
659 | if(fFirstType == "POS") { |
---|
660 | fXcurrent = 0; |
---|
661 | fXnext = fXcurrent; |
---|
662 | fCurrentPoint = fInitPoint; |
---|
663 | fNextPoint = fCurrentPoint; |
---|
664 | fTimecurrent = 0; |
---|
665 | fTimenext = 0; |
---|
666 | return rtn; |
---|
667 | } |
---|
668 | |
---|
669 | Int_t status = Atmosphere::Get()->InvertGrammage(fTOAImpact,fOmega,fX1,fInitPoint); |
---|
670 | if(status == -1) Msg(EsafMsg::Info) << "<GetFirstPoint> InvertGrammage() status is -1, it Should not" << MsgDispatch; |
---|
671 | |
---|
672 | else if(status == 0) { |
---|
673 | fXcurrent = 0; |
---|
674 | fXnext = fXcurrent; |
---|
675 | fCurrentPoint = fInitPoint; |
---|
676 | fNextPoint = fCurrentPoint; |
---|
677 | fTimecurrent = 0; |
---|
678 | fTimenext = 0; |
---|
679 | } |
---|
680 | |
---|
681 | else if(status == 1) |
---|
682 | Msg(EsafMsg::Warning) << "<GetFirstPoint> InvertGrammage() status is 1, it SHOULD NOT" << MsgDispatch; |
---|
683 | |
---|
684 | else if(status == 2) { |
---|
685 | if(Atmosphere::Get()->Grammage(fTOAImpact,Atmosphere::Get()->ImpactAtTOA(fTOAImpact,fOmega)) < fX1+kTolerance) |
---|
686 | Msg(EsafMsg::Info) << "<GetFirstPoint> TOA reached : Primary has not interacted in the atmosphere" << MsgDispatch; |
---|
687 | else Msg(EsafMsg::Warning) << "<GetFirstPoint> PROBLEM : TOA reached, BUT primary SHOULD have interacted in the atmosphere" << MsgDispatch; |
---|
688 | rtn = kFALSE; |
---|
689 | } |
---|
690 | |
---|
691 | else if(status == 3) { |
---|
692 | if(Atmosphere::Get()->Grammage(fTOAImpact,Atmosphere::Get()->ImpactASL(fTOAImpact,fOmega)) < fX1+kTolerance) |
---|
693 | Msg(EsafMsg::Info) << "<GetFirstPoint> Primary has hit the ground without interacting in the atmosphere " << MsgDispatch; |
---|
694 | else Msg(EsafMsg::Warning) << "<GetFirstPoint> PROBLEM : ground reached, BUT primary SHOULD have interacted before" << MsgDispatch; |
---|
695 | rtn = kFALSE; |
---|
696 | } |
---|
697 | |
---|
698 | else Msg(EsafMsg::Warning) << "<GetFirstPoint> InvertGrammage() status unknown : "<< status << MsgDispatch; |
---|
699 | |
---|
700 | return rtn; |
---|
701 | } |
---|
702 | |
---|
703 | //_________________________________________________________________________________________ |
---|
704 | Bool_t SlastShowerGenerator::IsXmaxInFoV(){ |
---|
705 | // |
---|
706 | // Assess Xmax position and tell if it is in FoV |
---|
707 | // used to reject event with Xmax out of FoV, without simulating the dvpt |
---|
708 | // |
---|
709 | |
---|
710 | // inits |
---|
711 | Bool_t rtn = kTRUE; |
---|
712 | |
---|
713 | // get Xmax according to shower dvpt model |
---|
714 | if (fShowerParametrization != "GIL") |
---|
715 | Msg(EsafMsg::Panic) << "<GetShowerParametrization> Only GIL formula available so far" <<MsgDispatch; |
---|
716 | //TOFIX : GFA and GHF not coded correctly, they do not included X1 fluctuations |
---|
717 | // when these options are reactivated, change the code below accordingly |
---|
718 | Float_t x0 = 37.15*g/cm2; |
---|
719 | Float_t Xmax = (1.7 + 0.76*(Log(fEnergy/0.81e8) - Log(fAtomicMass))) * x0 + fX1; |
---|
720 | EarthVector Maxpos(1); |
---|
721 | |
---|
722 | |
---|
723 | Int_t status = Atmosphere::Get()->InvertGrammage(fTOAImpact,fOmega,Xmax,Maxpos); |
---|
724 | if(status == -1) Msg(EsafMsg::Warning) << "<GetFirstPoint> InvertGrammage() status is -1, it Should not" << MsgDispatch; |
---|
725 | else if(status == 1) Msg(EsafMsg::Warning) << "<GetFirstPoint> InvertGrammage() status is 1, it SHOULD NOT" << MsgDispatch; |
---|
726 | // TOA or sea level reached before |
---|
727 | else if(status == 2 || status == 3) rtn = kFALSE; |
---|
728 | else if(status == 0) rtn = IsInFoV(Maxpos); |
---|
729 | else Msg(EsafMsg::Panic) << "<GetFirstPoint> InvertGrammage() status unknown : "<< status << MsgDispatch; |
---|
730 | |
---|
731 | return rtn; |
---|
732 | } |
---|
733 | |
---|
734 | //_________________________________________________________________________________________ |
---|
735 | Bool_t SlastShowerGenerator::GetNextStep() { |
---|
736 | // This method checks if next point is visible and makes the next step if it is OK. |
---|
737 | |
---|
738 | //TOFIX : old flag |
---|
739 | fInCome = kTRUE; |
---|
740 | |
---|
741 | if(fCurrentPoint.IsUnderSeaLevel()) return kFALSE; |
---|
742 | fCurrentPoint = fNextPoint; |
---|
743 | |
---|
744 | // search next shower step position |
---|
745 | Int_t status = Atmosphere::Get()->InvertGrammage(fCurrentPoint,fOmega,fDepthStep,fNextPoint); |
---|
746 | if(status == -1) { |
---|
747 | Msg(EsafMsg::Warning) << "<GetNextStep> InvertGrammage() status is -1, it Should not" << MsgDispatch; |
---|
748 | return kFALSE; |
---|
749 | } |
---|
750 | else if(status == 0) { |
---|
751 | fXcurrent = fXnext; |
---|
752 | fXnext = fXcurrent + fDepthStep; |
---|
753 | fTimecurrent = (fCurrentPoint-fInitPoint).Mag()/Clight(); |
---|
754 | fTimenext = (fNextPoint-fInitPoint).Mag()/Clight(); |
---|
755 | } |
---|
756 | else if(status == 1) { |
---|
757 | Msg(EsafMsg::Warning) << "<GetNextStep> InvertGrammage() status is 1, it Should not" << MsgDispatch; |
---|
758 | return kFALSE; |
---|
759 | } |
---|
760 | else if(status == 2) { |
---|
761 | Msg(EsafMsg::Info) << "<GetNextStep> Shower goes out atmosphere : TOA reached " << MsgDispatch; |
---|
762 | return kFALSE; |
---|
763 | } |
---|
764 | else if(status == 3) { |
---|
765 | Msg(EsafMsg::Info) << "<GetNextStep> Shower reaches the ground " << MsgDispatch; |
---|
766 | return kFALSE; |
---|
767 | } |
---|
768 | else Msg(EsafMsg::Warning) << "<GetNextStep> InvertGrammage() status unknown : "<< status << MsgDispatch; |
---|
769 | |
---|
770 | return kTRUE; |
---|
771 | } |
---|
772 | |
---|
773 | |
---|
774 | //_________________________________________________________________________________________ |
---|
775 | void SlastShowerGenerator::GIL(Double_t x) { |
---|
776 | // |
---|
777 | // Assigning of fNe - number of electrons in the current step according to GIL parameterization. |
---|
778 | // (GIL stands for Greizen-Ilina-Linsley which is the QGSJET model parameterization. |
---|
779 | // |
---|
780 | |
---|
781 | Float_t x0 = 37.15; |
---|
782 | Float_t t = x*cm2/g/x0; |
---|
783 | Float_t t_max = 1.7 + 0.76*(Log(fEnergy/0.81e8) - Log(fAtomicMass)); |
---|
784 | fAge = 2*t/(t+t_max); |
---|
785 | fNe = 0; |
---|
786 | if(fAge<=0) { |
---|
787 | Msg(EsafMsg::Warning)<<"<GIL> Asking for depth <= 0. Zero returned"<<MsgDispatch; |
---|
788 | fNe = 0.; |
---|
789 | return; |
---|
790 | } |
---|
791 | fNe = fEnergy/1.45e9*Exp(t-t_max-2*t*Log(fAge)); |
---|
792 | } |
---|
793 | |
---|
794 | |
---|
795 | //_________________________________________________________________________________________ |
---|
796 | void SlastShowerGenerator::GFA(Double_t x) { |
---|
797 | // |
---|
798 | // Assigning of fNe - number of electrons in the current step according to Gaussian Function in Age parameterization. |
---|
799 | // Formulae from C. Song, Astropart. Physics 22(2004)151 |
---|
800 | // sigma values fitted and extrapolated, Xmax, Nmax from GHF used |
---|
801 | // Factor 1.1 added to Ne because of threshold energy used for corsika simulation (10% particles missing) |
---|
802 | // |
---|
803 | // WARNING !!! not usable yet, cause cannot currently handle X1 fluctuations |
---|
804 | // |
---|
805 | |
---|
806 | Float_t Xmax,Nmax; |
---|
807 | Float_t sigma,p0,p1; |
---|
808 | if(fAtomicMass == 1) { |
---|
809 | sigma = 0.3206 - 0.006597*Log10(fEnergy); |
---|
810 | p0 = -8.9475; |
---|
811 | p1 = 0.9874; |
---|
812 | Nmax = Exp(-20.95 + 2.292*Log10(fEnergy)); |
---|
813 | Xmax = -258.1 + 54.81*Log10(fEnergy); |
---|
814 | } |
---|
815 | else if(fAtomicMass == 56) { |
---|
816 | sigma = 0.470-0.0135*Log10(fEnergy); |
---|
817 | p0 = -9.043; |
---|
818 | p1 = 0.9923; |
---|
819 | Nmax = Exp(-21.68 + 2.329*Log10(fEnergy)); |
---|
820 | Xmax = -430.7 + 59.08*Log10(fEnergy); |
---|
821 | } |
---|
822 | else throw invalid_argument(Form("Gaussian parameterization in age not available for primaries A = %f",fAtomicMass)); |
---|
823 | |
---|
824 | Xmax = Xmax*g/cm2; |
---|
825 | fAge = 3*x/(x+2.*Xmax); |
---|
826 | if(fAge<=0) { |
---|
827 | Msg(EsafMsg::Warning)<<"<GFA> Asking for depth <= 0. Zero returned"<<MsgDispatch; |
---|
828 | fNe = 0.; |
---|
829 | return; |
---|
830 | } |
---|
831 | fNe = 1.11*Nmax * Exp( -(fAge-1)*(fAge-1) / (2.*sigma*sigma) ); |
---|
832 | return; |
---|
833 | } |
---|
834 | |
---|
835 | //_________________________________________________________________________________________ |
---|
836 | void SlastShowerGenerator::GHF(Double_t x) { |
---|
837 | // |
---|
838 | // Assigning of fNe - number of electrons in the current step according to |
---|
839 | // Gaisser Hillas parametrisation |
---|
840 | // Formulae from C. Song, Astropart. Physics 22(2004)151 |
---|
841 | // Xmax, Nmax, lambda, X1, X0 values fitted and extrapolated |
---|
842 | // Factor 1.1 added to Ne because of threshold energy used for corsika simulation (10% particles missing) |
---|
843 | // |
---|
844 | // WARNING !!! not usable yet, cause cannot currently handle X1 fluctuations |
---|
845 | // |
---|
846 | |
---|
847 | Float_t Xmax,Nmax; |
---|
848 | Float_t X,X1,X0,lambda; |
---|
849 | if(fAtomicMass == 1) { |
---|
850 | X1 = 106.2 - 3.15*Log10(fEnergy); |
---|
851 | X0 = 401.9 - 25.79*Log10(fEnergy); |
---|
852 | Nmax = Exp(-20.95 + 2.292*Log10(fEnergy)); |
---|
853 | Xmax = -258.1 + 54.81*Log10(fEnergy); |
---|
854 | lambda= 98.6 -1.922*Log10(fEnergy); |
---|
855 | } |
---|
856 | else if(fAtomicMass == 56) { |
---|
857 | X1 = 29.72 - 1.031*Log10(fEnergy); |
---|
858 | X0 = 471.1 - 29.49*Log10(fEnergy); |
---|
859 | Nmax = Exp(-21.68 + 2.329*Log10(fEnergy)); |
---|
860 | Xmax = -430.7 + 59.08*Log10(fEnergy); |
---|
861 | lambda= 182.4 -6.1*Log10(fEnergy); |
---|
862 | } |
---|
863 | else throw invalid_argument(Form("Gaussian parameterization in age not available for primaries A = %f",fAtomicMass)); |
---|
864 | |
---|
865 | Xmax = Xmax*g/cm2; |
---|
866 | X1 = X1*g/cm2; |
---|
867 | X0 = X0*g/cm2; |
---|
868 | // X=x+fX1; |
---|
869 | X=x; |
---|
870 | lambda = lambda*g/cm2; |
---|
871 | fNe = 1.11*Nmax * Power((X-X0)/(Xmax-X0),(Xmax-X0)/lambda) * Exp((Xmax-X)/lambda); |
---|
872 | fAge = 3*x/(x+2.*Xmax); //should be defined ? |
---|
873 | |
---|
874 | if(fAge<=0) { |
---|
875 | Msg(EsafMsg::Warning)<<"<GHF> Asking for depth <= 0. Zero returned"<<MsgDispatch; |
---|
876 | fNe = 0.; |
---|
877 | return; |
---|
878 | } |
---|
879 | |
---|
880 | return; |
---|
881 | } |
---|
882 | |
---|
883 | //_________________________________________________________________________________________ |
---|
884 | PhysicsData* SlastShowerGenerator::Get() { |
---|
885 | // |
---|
886 | // method returning the ShowerTrack object |
---|
887 | // |
---|
888 | |
---|
889 | Reset(); |
---|
890 | |
---|
891 | // generate random Energy, Theta, Phi and the interaction point |
---|
892 | // energy slope is for differential spectrum |
---|
893 | TRandom *rndm = EsafRandom::Get(); |
---|
894 | |
---|
895 | // if power law |
---|
896 | if(fSpectrumType == "powerlaw") { |
---|
897 | // flat differential spectrum in log scale |
---|
898 | if ( fEnergySlope == -1.) { |
---|
899 | fEnergy = fEnergyMin * Exp(rndm->Rndm() * Log(fEnergyMax/fEnergyMin) ); |
---|
900 | } |
---|
901 | else { |
---|
902 | Double_t e1 = Power(fEnergyMin,fEnergySlope + 1); |
---|
903 | Double_t e2 = Power(fEnergyMax,fEnergySlope + 1); |
---|
904 | fEnergy = Power( e1 + (e2-e1)*rndm->Rndm(),1/(fEnergySlope + 1) ); |
---|
905 | } |
---|
906 | } |
---|
907 | else if(fSpectrumType == "GZKHiRes2005") { |
---|
908 | fEnergy = -HUGE; |
---|
909 | while((fEnergy < fEnergyMin) || (fEnergy > fEnergyMax)) { |
---|
910 | Double_t log10E = fSpectrumRdnm->GetRandom(); |
---|
911 | fEnergy = Power(10.,log10E); |
---|
912 | } |
---|
913 | } |
---|
914 | else Msg(EsafMsg::Panic)<<"<Get> Wrong argument for spectrum type == "<< fSpectrumType <<MsgDispatch; |
---|
915 | |
---|
916 | |
---|
917 | |
---|
918 | // Check if only a specific point should be generated |
---|
919 | if( fFirstType == "POS") { |
---|
920 | fInitPoint.SetXYZ(fFirstPointX,fFirstPointY,fFirstPointZ); |
---|
921 | fPhi_mes = (fPhiMax - fPhiMin)*rndm->Rndm() + fPhiMin; |
---|
922 | fTheta_mes = (fThetaMax - fThetaMin)*rndm->Rndm() + fThetaMin; |
---|
923 | fOmega.SetXYZ(-Sin(fTheta_mes)*Cos(fPhi_mes),-Sin(fTheta_mes)*Sin(fPhi_mes),-Cos(fTheta_mes)); |
---|
924 | Msg(EsafMsg::Info) << "<Get()> Pos option, LOCAL theta and phi set equal to MES values" << MsgDispatch; |
---|
925 | fTheta_local = fTheta_mes; |
---|
926 | fPhi_local = fPhi_mes; |
---|
927 | |
---|
928 | // find TOA impact and earth impact |
---|
929 | fTOAImpact = Atmosphere::Get()->ImpactAtTOA(fInitPoint,EarthVector(-fOmega)); |
---|
930 | fEarthImpact = Atmosphere::Get()->ImpactASL(fInitPoint,fOmega); |
---|
931 | fHitGround = 0; |
---|
932 | if(fEarthImpact.Z() == -HUGE) Msg(EsafMsg::Warning) << "<Get(), POS option> ImpactASL says fInitPoint is under sea level" << MsgDispatch; |
---|
933 | else if(fEarthImpact.Z() == HUGE) Msg(EsafMsg::Debug) << "<Get(), POS option> Shower does not reach the ground" << MsgDispatch; |
---|
934 | else fHitGround = 1; |
---|
935 | if(fHitGround == 0) { |
---|
936 | fEarthImpact = Atmosphere::Get()->ImpactAtTOA(fInitPoint,fOmega); |
---|
937 | if(fEarthImpact.Z() == -HUGE) Msg(EsafMsg::Warning) << "<Get(), POS option> ImpactAtTOA says fInitPoint is under sea level" << MsgDispatch; |
---|
938 | else if(fEarthImpact.Z() == HUGE) Msg(EsafMsg::Warning) << "<Get(), POS option> No ImpactAtTOA found. IT SHOULD NOT OCCUR HERE" << MsgDispatch; |
---|
939 | } |
---|
940 | |
---|
941 | // disable standard geometrical flags |
---|
942 | fRejectFakeEvents = kFALSE; |
---|
943 | fOutOfFoV = kFALSE; |
---|
944 | fRejectNoXmax = kFALSE; |
---|
945 | |
---|
946 | DevelopShower(); |
---|
947 | } |
---|
948 | |
---|
949 | |
---|
950 | // STANDARD WAY |
---|
951 | else { |
---|
952 | Int_t cycle(0); |
---|
953 | while (kTRUE) { |
---|
954 | if ( cycle++>1000000 ) break; |
---|
955 | if ( FindImpactOnTOA() && DevelopShower()) break; |
---|
956 | } |
---|
957 | } |
---|
958 | if (!fQuiet) { |
---|
959 | MsgForm(EsafMsg::Info,"ShowerTrack produced : Energy = %.2E MeV, Theta_local = %.2f deg, Phi_local = %.2f deg", |
---|
960 | fTrack->GetEnergy(), fTheta_local*RadToDeg(),fPhi_local*RadToDeg()); |
---|
961 | MsgForm(EsafMsg::Info," Impact on TOA : (%.2f,%.2f,%.2f) km with MES angles : Theta_mes = %.2f, Phi_mes = %.2f", |
---|
962 | fTOAImpact.X()/km,fTOAImpact.Y()/km,fTOAImpact.Z()/km,fTheta_mes*RadToDeg(),fPhi_mes*RadToDeg()); |
---|
963 | MsgForm(EsafMsg::Info," First interaction X1 = %.3f at : (%.2f,%.2f,%.2f) km gives a shower with %d steps", |
---|
964 | fTrack->fX1*cm2/g,fTrack->GetInitPos().X()/km,fTrack->GetInitPos().Y()/km,fTrack->GetInitPos().Z()/km, fTrack->GetNumStep()); |
---|
965 | } |
---|
966 | else { |
---|
967 | MsgForm(EsafMsg::Debug,"SlastShowerGenerator produced a ShowerTrack: Energy = %.2E MeV Theta = %.2f deg Phi = %.2f deg", |
---|
968 | fTrack->GetEnergy(), fTrack->GetTheta()*RadToDeg(),fTrack->GetPhi()*RadToDeg()); |
---|
969 | MsgForm(EsafMsg::Debug," originated with X1 = %.3f at: (%.2f,%.2f,%.2f) km with %d steps", |
---|
970 | fTrack->fX1*cm2/g,fTrack->GetInitPos().X()/km,fTrack->GetInitPos().Y()/km,fTrack->GetInitPos().Z()/km, fTrack->GetNumStep()); |
---|
971 | |
---|
972 | } |
---|
973 | return fTrack; |
---|
974 | |
---|
975 | } |
---|
976 | |
---|
977 | //_________________________________________________________________________________________ |
---|
978 | void SlastShowerGenerator::Reset() { |
---|
979 | // |
---|
980 | // Reset ShowerTrack and SlastShowerGenerator internal parameters |
---|
981 | // |
---|
982 | |
---|
983 | fHitGround = 0; |
---|
984 | fInCome = kFALSE; |
---|
985 | fOutCome = kFALSE; |
---|
986 | fInitPoint.SetXYZ(0,0,HUGE); |
---|
987 | fOmega.SetXYZ(0,0,HUGE); |
---|
988 | fCurrentPoint.SetXYZ(0,0,HUGE); |
---|
989 | fNextPoint.SetXYZ(0,0,HUGE); |
---|
990 | fEarthImpact.SetXYZ(0,0,HUGE); |
---|
991 | fTOAImpact.SetXYZ(0,0,HUGE); |
---|
992 | fTheta_local = -HUGE; |
---|
993 | fPhi_local = -HUGE; |
---|
994 | fTheta_mes = -HUGE; |
---|
995 | fPhi_mes = -HUGE; |
---|
996 | fX1 = -HUGE; |
---|
997 | fEnergy = -HUGE; |
---|
998 | |
---|
999 | } |
---|
1000 | |
---|
1001 | //_________________________________________________________________________________________ |
---|
1002 | void SlastShowerGenerator::GetShowerInfo() { |
---|
1003 | // Fills ShowerTrack parameters |
---|
1004 | fTrack->fEnergy = fEnergy*eV; |
---|
1005 | fTrack->fTheta = fTheta_mes; |
---|
1006 | fTrack->fPhi = fPhi_mes; |
---|
1007 | fTrack->fX1 = fX1; |
---|
1008 | fTrack->fEthrEl = 0; |
---|
1009 | fTrack->fDirVers = fOmega; |
---|
1010 | fTrack->fInitPos = fInitPoint; |
---|
1011 | fTrack->fHitGround = fHitGround; |
---|
1012 | fTrack->fEarthImpact = fEarthImpact; |
---|
1013 | } |
---|
1014 | |
---|
1015 | //_________________________________________________________________________________________ |
---|
1016 | Bool_t SlastShowerGenerator::DevelopShower() { |
---|
1017 | // |
---|
1018 | // Method developing the shower |
---|
1019 | // |
---|
1020 | |
---|
1021 | GetX1(); |
---|
1022 | Bool_t has_interacted = GetFirstPoint(); |
---|
1023 | |
---|
1024 | // init |
---|
1025 | if(!fTrack) fTrack = new ShowerTrack(); |
---|
1026 | else fTrack->Reset(); |
---|
1027 | |
---|
1028 | // 1. if fake events not allowed, re-try for another event |
---|
1029 | // NB : fOutOfFoV flag is not relevant here, cause no showers should be in this case. But kept for safety |
---|
1030 | if(fRejectFakeEvents && (!has_interacted || fOutOfFoV)) return kFALSE; |
---|
1031 | |
---|
1032 | // 1.1 if events without Xmax in the FoV are not allowed, re-try for another event |
---|
1033 | if(fRejectNoXmax) { |
---|
1034 | if(!IsXmaxInFoV()) { |
---|
1035 | Msg(EsafMsg::Info)<<"Shower Xmax is not in FoV --> retry for another event"<<MsgDispatch; |
---|
1036 | return kFALSE; |
---|
1037 | } |
---|
1038 | } |
---|
1039 | |
---|
1040 | // 2. if fake events allowed + CR has not interacted |
---|
1041 | // --> X1 related data fixed to HUGE values |
---|
1042 | if(!has_interacted) { |
---|
1043 | fX1 = -HUGE; |
---|
1044 | fInitPoint.SetXYZ(0,0,HUGE); |
---|
1045 | GetShowerInfo(); |
---|
1046 | Msg(EsafMsg::Info)<<"Cosmic ray has not interacted in the atmosphere"<<MsgDispatch; |
---|
1047 | return kTRUE; |
---|
1048 | } |
---|
1049 | |
---|
1050 | // 3. if fake events allowed + events is out of FoV, shower dvpt avoided |
---|
1051 | if(!fRejectFakeEvents && fOutOfFoV) { |
---|
1052 | GetShowerInfo(); |
---|
1053 | Msg(EsafMsg::Info)<<"ShowerTrack does NOT cut the FoV"<<MsgDispatch; |
---|
1054 | return kTRUE; |
---|
1055 | } |
---|
1056 | |
---|
1057 | // 4. "standard" candidate |
---|
1058 | // Develop a shower of shower steps |
---|
1059 | Int_t cycle(0); |
---|
1060 | while(kTRUE) { |
---|
1061 | if(cycle++>100000) return kFALSE; |
---|
1062 | if(!GetNextStep()) break; |
---|
1063 | if( fInCome) { |
---|
1064 | ShowerStep s = GetShowerStep(); |
---|
1065 | fTrack->Add(s); |
---|
1066 | } |
---|
1067 | } |
---|
1068 | GetShowerInfo(); |
---|
1069 | Msg(EsafMsg::Info)<<"ShowerTrack cut the FoV"<<MsgDispatch; |
---|
1070 | |
---|
1071 | return kTRUE; |
---|
1072 | } |
---|
1073 | |
---|
1074 | //_________________________________________________________________________________________ |
---|
1075 | MCTruth* SlastShowerGenerator::GetTruth() { |
---|
1076 | // |
---|
1077 | // Method returning the Truth object |
---|
1078 | // |
---|
1079 | |
---|
1080 | // init |
---|
1081 | if(!fTruth) fTruth = new MCTruth(); |
---|
1082 | |
---|
1083 | // can be filled even if showertrack has no steps |
---|
1084 | fTruth->SetEnergy(fTrack->GetEnergy()); |
---|
1085 | fTruth->SetThetaPhi(fTrack->GetTheta(),fTrack->GetPhi()); |
---|
1086 | fTruth->SetFirstInt(fInitPoint,fX1); |
---|
1087 | Int_t particode = Int_t(floor(fAtomicMass+0.5)); |
---|
1088 | fTruth->SetParticle(particode); |
---|
1089 | fTruth->SetTOAImpact(fTOAImpact); |
---|
1090 | fTruth->SetEarthImpact(fEarthImpact); |
---|
1091 | |
---|
1092 | // filled only if steps exist |
---|
1093 | if (fTrack->Size() > 0) { |
---|
1094 | const ShowerStep& s = fTrack->GetLastStep(); |
---|
1095 | fTruth->SetEarthAge(s.GetAgef()); |
---|
1096 | // get the shower maximum |
---|
1097 | Float_t MaxElectrons(0); |
---|
1098 | Int_t MaxIndex(-1); |
---|
1099 | for (UInt_t i(0); i<fTrack->Size(); i++) { |
---|
1100 | const ShowerStep& s = fTrack->GetStep(i); |
---|
1101 | if (MaxElectrons < s.GetNelectrons()) { |
---|
1102 | MaxIndex = i; |
---|
1103 | MaxElectrons = s.GetNelectrons(); |
---|
1104 | } |
---|
1105 | } |
---|
1106 | if (MaxIndex != -1) { |
---|
1107 | const ShowerStep& s = fTrack->GetStep(MaxIndex); |
---|
1108 | fTruth->SetShowerMax(0.5*(s.GetXYZi() + s.GetXYZf()),0.5*(s.GetXi() + s.GetXf())); |
---|
1109 | } |
---|
1110 | } |
---|
1111 | |
---|
1112 | return fTruth; |
---|
1113 | } |
---|
1114 | |
---|
1115 | //_________________________________________________________________________________________ |
---|
1116 | Double_t SlastShowerGenerator::GetXmax() { |
---|
1117 | // |
---|
1118 | // returns Xmax of the shower according to GIL parametrization (used by Reco) |
---|
1119 | // |
---|
1120 | Float_t x0 = 37.15*g/cm2; |
---|
1121 | Float_t fEnergyScale; |
---|
1122 | if (fEnergy<0) fEnergyScale = 1.e20; |
---|
1123 | else fEnergyScale = fEnergy; |
---|
1124 | Float_t t_max = 1.7 + 0.76*(Log(fEnergyScale/0.81e8) - Log(fAtomicMass)); |
---|
1125 | return t_max*x0; |
---|
1126 | } |
---|