source: branches/MarsMoreSimulationTruth/msim/MSimAtmosphere.cc@ 19364

Last change on this file since 19364 was 18548, checked in by tbretz, 10 years ago
If the CEFFIC option is enables and the user has not requested execution explicitly, it is skipped.
File size: 31.2 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of CheObs, the Modular Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appears in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 1/2009 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: CheObs Software Development, 2000-2009
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MSimAtmosphere
28//
29// Task to calculate wavelength or incident angle dependent absorption
30//
31// Input Containers:
32// MPhotonEvent
33// MCorsikaRunHeader
34//
35// Output Containers:
36// MPhotonEvent
37//
38//////////////////////////////////////////////////////////////////////////////
39#include "MSimAtmosphere.h"
40
41#include <fstream>
42
43#include <TGraph.h>
44#include <TRandom.h>
45
46#include "MLog.h"
47#include "MLogManip.h"
48
49#include "MParList.h"
50
51#include "MCorsikaRunHeader.h"
52#include "MPhotonEvent.h"
53#include "MPhotonData.h"
54
55ClassImp(MSimAtmosphere);
56
57using namespace std;
58
59// ==========================================================================
60//
61// January 2002, A. Moralejo: We now precalculate the slant paths for the
62// aerosol and Ozone vertical profiles, and then do an interpolation in
63// wavelength for every photon to get the optical depths. The parameters
64// used, defined below, have been taken from "Atmospheric Optics", by
65// L. Elterman and R.B. Toolin, chapter 7 of the "Handbook of geophysics
66// and Space environments". (S.L. Valley, editor). McGraw-Hill, NY 1965.
67//
68// WARNING: the Mie scattering and the Ozone absorption are implemented
69// to work only with photons produced at a height a.s.l larger than the
70// observation level. So this is not expected to work well for simulating
71// the telescope pointing at theta > 90 deg (for instance for neutrino
72// studies. Rayleigh scattering works even for light coming from below.
73//
74// Fixed bugs (of small influence) in Mie absorption implementation: there
75// were errors in the optical depths table, as well as a confusion:
76// height a.s.l. was used as if it was height above the telescope level.
77// The latter error was also present in the Ozone aborption implementation.
78//
79// On the other hand, now we have the tables AE_ABI and OZ_ABI with optical
80// depths between sea level and a height h (before it was between 2km a.s.l
81// and a height h). So that we can simulate also in the future a different
82// observation level.
83//
84// AM: WARNING: IF VERY LARGE zenith angle simulations are to be done (say
85// above 85 degrees, for neutrino primaries or any other purpose) this code
86// will have to be adapted accordingly and checked, since in principle it has
87// been written and tested to simulate the absorption of Cherenkov photons
88// arriving at the telescope from above.
89//
90// AM: WARNING 2: not to be used for wavelengths outside the range 250-700 nm
91//
92// January 2003, Abelardo Moralejo: found error in Ozone absorption treatment.
93// At large zenith angles, the air mass used was the one calculated for
94// Rayleigh scattering, but since the Ozone distribution is rather different
95// from the global distribution of air molecules, this is not a good
96// approximation. Now I have left in this code only the Rayleigh scattering,
97// and moved to atm.c the Mie scattering and Ozone absorption calculated in
98// a better way.
99//
100// A. Moralejo, January 2003: added some parameters for Mie scattering
101// and Ozone absorption derived from the clear standard atmosphere model
102// in "Atmospheric Optics", by L. Elterman and R.B. Toolin, chapter 7 of
103// the "Handbook of geophysics and Space environments". S.L. Valley,
104// editor. McGraw-Hill, NY 1965.
105//
106// AM, Jan 2003: Changed the meaning of the argument height: now it is the
107// true height above sea level at which a photon has been emitted, before
108// it was the height given by Corsika, measured in the vertical of the
109// observer and not in the vertical of the emitting particle.
110//
111//
112// MAGIC-Winter and MAGIC-Summer by M. Haffke,
113// parametrizing profiles obtained with MSIS:
114// http://uap-www.nrl.navy.mil/models_web/msis/msis_home.htm
115//
116//
117// The MAGIC-Winter and MAGIC-Summer parametrisations reproduce the MSIS
118// profiles for the 3 atmospheric layers from 0 up to 40 km height. Beyond
119// that height, it was not possible to achieve a good fit, but the amount
120// of residual atmosphere is so small that light absorption would be
121// negligible anyway. Showers develop well below 40 km.
122//
123//
124// The mass overburden is given by T = AATM + BATM * exp(-h/CATM)
125// The last layer of the US standard atmosphere (in which T varies
126// linearly with h) is above 100 km and has not been included here
127// because it should not matter.
128//
129class MAtmRayleigh
130{
131private:
132 static const Double_t fgMeanFreePath; // [g/cm^2] Mean free path for scattering Rayleigh XR
133
134 Double_t fR; // [cm] Earth radius to be used
135
136 Double_t fHeight[5]; // Layer boundaries (atmospheric layer)
137
138 // Parameters of the different atmospheres. We use the same parametrization
139 // shape as in Corsika atmospheric models (see Corsika manual, appendix D).
140 // The values here can be/are obtained from the Corsika output
141 //Float_t fAtmA[4]; // The index refers to the atmospheric layer (starting from sea level and going upwards)
142 Float_t fAtmB[4]; // The index refers to the atmospheric layer (starting from sea level and going upwards)
143 Float_t fAtmC[4]; // The index refers to the atmospheric layer (starting from sea level and going upwards)
144
145 Double_t fRho[5]; // Precalculated integrals for rayleigh scatterning
146
147 // --------------------------------------------------------------------------
148 //
149 // Precalcalculate the integrals from the observer level to the next
150 // atmpsheric layer below including the lower boundary. Thus a
151 // correct calculation is reduced to the calculation of the upper
152 // boundary.
153 //
154 // fRho[0] = B0;
155 // fRho[1] = B0-A0 + B1;
156 // fRho[2] = B0-A0 + B1-A1 + B2;
157 // fRho[3] = B0-A0 + B1-A1 + B2+A2 + B3;
158 // fRho[4] = B0-A0 + B1-A1 + B2+A2 + B3 - A3;
159 //
160 void PreCalcRho()
161 {
162 // Limits (height in cm) of the four layers in which
163 // atmosphere is parametrized.
164 // This is a stupid trick giving 0 for the integrals below
165 // the observer
166
167 // FIXME: Could be replaced by 0, AtmLay[0]-fAtmLay[3]
168
169 const Double_t h[5] =
170 {
171 fObsLevel, // fObsLevel, // 0km
172 TMath::Max(fObsLevel, 7.75e5), // TMath::Max(fObsLevel, 4e5), // 4km
173 16.5e5, // 10e5, // 10km
174 50.0e5, // 40e5, // 40km
175 105.0e5, // 100e5 // 100km
176 };
177
178 memcpy(fHeight, h, sizeof(Double_t)*5);
179
180 fRho[0] = 0;
181 for (int i=0; i<4; i++)
182 {
183 const Double_t b = fAtmB[i];
184 const Double_t c = fAtmC[i];
185
186 const Double_t h1 = h[i+1];
187 const Double_t h0 = h[i];
188
189 const Double_t B = b*TMath::Exp(-h0/c);
190 const Double_t A = b*TMath::Exp(-h1/c);
191
192 // Calculate rho for the i-th layer from the lower
193 // to the higher layer boundary.
194 // If height is within the layer only calculate up to height.
195 fRho[i] += B;
196 fRho[i+1] = fRho[i] - A;
197 }
198 }
199
200protected:
201 Double_t fObsLevel; // [cm] observation level a.s.l.
202
203public:
204 // Init an atmosphere from the data stored in MCorsikaRunHeader
205 MAtmRayleigh(const MCorsikaRunHeader &h)
206 {
207 Init(h);
208 }
209
210 // Defualt constructor
211 MAtmRayleigh() : fObsLevel(-1) { }
212
213 // Check if the ovservation level has been correctly initialized
214 // Used as a marker for correct initialization
215 Bool_t IsValid() const { return fObsLevel>=0; }
216
217 // Get the Earth radius to be used
218 Double_t R() const { return fR; }
219
220 // Init an atmosphere from the data stored in MCorsikaRunHeader
221 // This initialized fObsLevel, fR, fAtmB and fAtmC and
222 // PreCalcRho
223 void Init(const MCorsikaRunHeader &h)
224 {
225 // Observation level above earth radius
226 fObsLevel = h.GetObsLevel();
227
228 // Use earth radius as defined in Corsika
229 fR = h.EarthRadius();
230
231 //memcpy(fAtmA, (Float_t*)h.GetAtmosphericCoeffA(), sizeof(Float_t)*4);
232 memcpy(fAtmB, (Float_t*)h.GetAtmosphericCoeffB(), sizeof(Float_t)*4);
233 memcpy(fAtmC, (Float_t*)h.GetAtmosphericCoeffC(), sizeof(Float_t)*4);
234
235 PreCalcRho();
236 }
237
238 // Return the vertical thickness between the observer and height.
239 // Therefor the integral of the layers below (including the lower
240 // boudary) Have been precalculated by PreCalcRho
241 Double_t GetVerticalThickness(Double_t height) const
242 {
243 // FIXME: We could store the start point above obs-level
244 // (Does this really gain anything?)
245 Int_t i=0;
246 while (i<4 && height>fHeight[i+1])
247 i++;
248
249 const Double_t b = fAtmB[i];
250 const Double_t c = fAtmC[i];
251
252 return fRho[i] - b*TMath::Exp(-height/c);
253 }
254
255 /*
256 // The "orginal" code for the vertical thickness
257 Double_t GetVerticalThickness(Double_t obslev, Double_t height) const
258 {
259 // This is a C++-version of the original code from attenu.c
260
261 // Limits (height in cm) of the four layers in which atmosphere is parametrized:
262 const double lahg[5] =
263 {
264 obslev,
265 TMath::Max(obslev, 4e5),
266 1.0e6,
267 4.0e6,
268 1.0e7
269 };
270
271 Double_t Rho_Tot = 0.0;
272 for (int i=0; i<4; i++)
273 {
274 const Double_t b = fAtmB[i];
275 const Double_t c = fAtmC[i];
276
277 const Double_t h0 = TMath::Min(height, lahg[i+1]);
278 const Double_t h1 = lahg[i];
279
280 // Calculate rho for the i-th layer from the lower
281 // to the higher layer boundary.
282 // If height is within the layer only calculate up to height.
283 Rho_Tot += b*(exp(-h1/c) - exp(-h0/c));
284
285 if (lahg[i+1] > height)
286 break;
287 }
288
289 return Rho_Tot;
290 }
291 */
292 Double_t CalcTransmission(Double_t height, Double_t wavelength, Double_t sin2) const
293 {
294 // sin2: sin(theta)^2
295 // height is the true height a.s.l.
296
297 // LARGE ZENITH ANGLE FACTOR (AIR MASS FACTOR):
298 // Air mass factor "airmass" calculated using a one-exponential
299 // density profile for the atmosphere,
300 //
301 // rho = rho_0 exp(-height/hscale) with hscale = 7.4 km
302 //
303 // The air mass factor is defined as I(theta)/I(0), the ratio of
304 // the optical paths I (in g/cm2) traversed between two given
305 // heights, at theta and at 0 deg z.a.
306
307 const Double_t H = height-fObsLevel;
308 const Double_t h = 2*H;
309
310 // Scale-height (cm) for Exponential density profile
311 const Double_t hscale = 7.4e5;
312 const Double_t f = 2*hscale;
313
314 // Precalc R*cos(theta)^2 (FIXME: Is ph.GetCosW2 more precise?)
315 const Double_t Rcos2 = fR * (1-sin2); // cos2 = 1 - sin2
316
317 const Double_t x1 = TMath::Sqrt((Rcos2 ) / f);
318 const Double_t x2 = TMath::Sqrt((Rcos2 + 2*h) / f);
319 const Double_t x3 = TMath::Sqrt((fR ) / f);
320 const Double_t x4 = TMath::Sqrt((fR + 2*h) / f);
321
322 // Return a -1 transmittance in the case the photon comes
323 // exactly from the observation level, because in that case the
324 // "air mass factor" would become infinity and the calculation
325 // is not valid!
326 if (fabs(x3-x4) < 1.e-10)
327 return -1.;
328
329 const Double_t e12 = erfc(x1) - erfc(x2);
330 const Double_t e34 = erfc(x3) - erfc(x4);
331
332 const Double_t airmass = TMath::Exp(-fR*sin2 / f) * e12/e34;
333
334 // Calculate the traversed "vertical thickness" of air using the
335 // US Standard atmosphere:
336 const Double_t Rho_tot = GetVerticalThickness(/*fObsLevel,*/ height);
337
338 // We now convert from "vertical thickness" to "slanted thickness"
339 // traversed by the photon on its way to the telescope, simply
340 // multiplying by the air mass factor m:
341 const Double_t Rho_Fi = airmass * Rho_tot;
342
343 // Finally we calculate the transmission coefficient for the Rayleigh
344 // scattering:
345 // AM Dec 2002, introduced ABS below to account (in the future) for
346 // possible photons coming from below the observation level.
347
348 const Double_t wl = 400./wavelength;
349
350 // Mean free path for scattering Rayleigh XR (g/cm^2)
351 return TMath::Exp(-TMath::Abs(Rho_Fi/fgMeanFreePath)*wl*wl*wl*wl);
352 }
353};
354
355// ==========================================================================
356
357class MAtmosphere : public MAtmRayleigh
358{
359private:
360 static const Double_t STEPTHETA; // aprox. 1 degree
361
362 // Aerosol number density for 31 heights a.s.l., from 0 to 30 km,
363 // in 1 km steps (units: cm^-3)
364 static const Double_t aero_n[31];
365
366 // Ozone concentration for 51 heights a.s.l., from 0 to 50 km,
367 // in 1 km steps (units: cm/km)
368 static const Double_t oz_conc[51];
369
370 // aerosol_path contains the path integrals for the aerosol number
371 // density (relative to the number density at sea level) between the
372 // observation level and a height h for different zenith angles. The
373 // first index indicate height above sea level in units of 100m, the
374 // second is the zenith angle in degrees.
375 float aerosol_path[301][90];
376
377 // ozone_path contains the path integrals for the ozone concentration
378 // between the observation level and a height h for different zenith
379 // angles. The first index indicate height above sea level in units
380 // of 100m, the second is the zenith angle in degrees.
381 float ozone_path[501][90];
382
383 // Interpolate the graph at wavelength
384 Double_t GetBeta(Double_t wavelength, const TGraph &g) const
385 {
386 // FIXME: This might not be the fastest because range
387 // checks are done for each call!
388 return g.GetN()==0 ? 0 : g.Eval(wavelength)*1e-5; // from km^-1 to cm^-1
389/*
390 // Linear interpolation
391 // (FIXME: Is it faster to be replaced with a binary search?)
392 // ( This might be faster because we have more photons
393 // with smaller wavelengths)
394 //int index;
395 //for (index = 1; index <g.GetN()-1; index++)
396 // if (wavelength < g.GetX()[index])
397 // break;
398 const Int_t index = TMath::BinarySearch(g.GetN(), g.GetX(), wavelength)+1;
399
400 const Double_t t0 = g.GetY()[index-1];
401 const Double_t t1 = g.GetY()[index];
402
403 const Double_t w0 = g.GetX()[index-1];
404 const Double_t w1 = g.GetX()[index];
405
406 const Double_t beta0 = t0+(t1-t0)*(wavelength-w0)/(w1-w0);
407
408 return beta0 * 1e-5; // from km^-1 to cm^-1
409 */
410 }
411
412
413 //MSpline3 *fAbsCoeffOzone;
414 //MSpline3 *fAbsCoeffAerosols;
415
416 TGraph *fAbsCoeffOzone;
417 TGraph *fAbsCoeffAerosols;
418
419public:
420 MAtmosphere(const MCorsikaRunHeader &h) : fAbsCoeffOzone(0), fAbsCoeffAerosols(0)
421 {
422 Init(h);//, "ozone.txt", "aerosols.txt");
423 }
424
425 MAtmosphere(const char *name1=0, const char *name2=0) : fAbsCoeffOzone(0), fAbsCoeffAerosols(0)
426 {
427 if (name1)
428 InitOzone(name1);
429 if (name2)
430 InitAerosols(name2);
431 }
432
433 ~MAtmosphere()
434 {
435 if (fAbsCoeffOzone)
436 delete fAbsCoeffOzone;
437 if (fAbsCoeffAerosols)
438 delete fAbsCoeffAerosols;
439 }
440
441 Float_t GetWavelengthMin() const { return fAbsCoeffOzone && fAbsCoeffAerosols ? TMath::Max(fAbsCoeffOzone->GetX()[0], fAbsCoeffAerosols->GetX()[0]) : -1; }
442 Float_t GetWavelengthMax() const { return fAbsCoeffOzone && fAbsCoeffAerosols ? TMath::Min(fAbsCoeffOzone->GetX()[fAbsCoeffOzone->GetN()-1], fAbsCoeffAerosols->GetX()[fAbsCoeffAerosols->GetN()-1]) : -1; }
443
444 Bool_t HasValidOzone() const { return fAbsCoeffOzone && fAbsCoeffOzone->GetN()>0; }
445 Bool_t HasValidAerosol() const { return fAbsCoeffAerosols && fAbsCoeffAerosols->GetN()>0; }
446
447 Bool_t IsAllValid() const { return IsValid() && HasValidOzone() && HasValidAerosol(); }
448
449 void PreCalcOzone()
450 {
451 // It follows a precalculation of the slant path integrals we need
452 // for the estimate of the Mie scattering and Ozone absorption:
453 Double_t dh = 1.e3;
454 const Double_t STEPTHETA = 1.74533e-2; // aprox. 1 degree
455
456 // Ozone absorption
457 for (Int_t j = 0; j < 90; j++)
458 {
459 const Double_t theta = j * STEPTHETA;
460 const Double_t sin2 = sin(theta)*sin(theta);
461 const Double_t H = R()+fObsLevel;
462
463 Double_t path_slant = 0;
464 for (Double_t h = fObsLevel; h <= 50e5; h += dh)
465 {
466 // h is the true height vertical above ground
467 if (fmod(h,1e4) == 0)
468 ozone_path[(int)(h/1e4)][j] = path_slant;
469
470 const Double_t km = h/1e5;
471 const Int_t i = TMath::FloorNint(km);
472 const Double_t l = R()+h;
473
474 const Double_t L = TMath::Sqrt(l*l - H*H * sin2);
475 const Double_t f = dh * l / L;
476
477 // Linear interpolation at h/1e5
478 Double_t interpol = oz_conc[i] + fmod(km, 1) * (oz_conc[i+1]-oz_conc[i]);
479
480 path_slant += f * interpol;
481 }
482 }
483 }
484
485 void PreCalcAerosol()
486 {
487 // It follows a precalculation of the slant path integrals we need
488 // for the estimate of the Mie scattering and Ozone absorption:
489 Double_t dh = 1.e3;
490 const Double_t STEPTHETA = 1.74533e-2; // aprox. 1 degree
491
492 /* Mie (aerosol): */
493 for (Int_t j = 0; j < 90; j++)
494 {
495 const Double_t theta = j * STEPTHETA;
496 const Double_t sin2 = sin(theta)*sin(theta);
497 const Double_t H = R()+fObsLevel;
498
499 Double_t path_slant = 0;
500 for (Double_t h = fObsLevel; h <= 30e5; h += dh)
501 {
502 // h is the true height vertical above ground
503 if (fmod(h,1e4) == 0)
504 aerosol_path[(int)(h/1e4)][j] = path_slant;
505
506 const Double_t km = h/1e5;
507 const Int_t i = TMath::FloorNint(km);
508 const Double_t l = R()+h;
509
510 const Double_t L = TMath::Sqrt(l*l - H*H * sin2);
511 const Double_t f = dh * l / L;
512
513 // Linear interpolation at h/1e5
514 Double_t interpol = aero_n[i] + fmod(km, 1)*(aero_n[i+1]-aero_n[i]);
515
516 path_slant += f * interpol/aero_n[0]; // aero_n [km^-1]
517 }
518 }
519 }
520
521 Bool_t InitOzone(const TString name="")
522 {
523 if (!name.IsNull())
524 {
525 if (fAbsCoeffOzone)
526 delete fAbsCoeffOzone;
527
528 fAbsCoeffOzone = new TGraph(name);
529 fAbsCoeffOzone->Sort();
530 }
531
532 if (!HasValidAerosol())
533 return kFALSE;
534
535 if (IsValid())
536 PreCalcOzone();
537
538 return kTRUE;
539 }
540
541 Bool_t InitAerosols(const TString name="")
542 {
543 if (!name.IsNull())
544 {
545 if (fAbsCoeffAerosols)
546 delete fAbsCoeffAerosols;
547
548 fAbsCoeffAerosols = new TGraph(name);
549 fAbsCoeffAerosols->Sort();
550 }
551
552 if (!HasValidAerosol())
553 return kFALSE;
554
555 if (IsValid())
556 PreCalcAerosol();
557
558 return kTRUE;
559 }
560
561 void Init(const MCorsikaRunHeader &h, const char *name1=0, const char *name2=0)
562 {
563 MAtmRayleigh::Init(h);
564
565 InitOzone(name1);
566 InitAerosols(name2);
567 }
568/*
569 Double_t GetOz(Double_t height, Double_t theta) const
570 {
571 // Distance between two points D = 1km /cos(theta)
572 // Density along y within this km: f = (x[i+1]-x[i])/1km * dy
573 // Integral of this density f = (x[i+1]-x[i])/1km * (y[i+1]-y[i])
574 // f(h) = int [ (c1-c0)/1km*(h-h0)*dh + c0 ] dh
575 // = (c-co)*(h-h0)
576
577 Double_t rc = 0;
578 int i;
579 for (i=0; i<49; i++)
580 if (i>=2 && i+1<height/1e5) // cm -> km
581 rc += oz_conc[i] * 1e5/cos(theta);
582
583 rc -= oz_conc[2]*0.2*1e5/cos(theta);
584 rc += oz_conc[i+1]*fmod(height/1e5,1)*1e5/cos(theta);
585
586 return rc;
587 }
588 */
589
590 Double_t CalcOzoneAbsorption(Double_t h, Double_t wavelength, Double_t theta) const
591 {
592 if (!fAbsCoeffOzone)
593 return 1;
594
595 //******* Ozone absorption *******
596 if (h > 50.e5)
597 h = 50.e5;
598
599 // Vigroux Ozone absorption coefficient a.s.l. through interpolation:
600 //const float oz_vigroux[15]= {1.06e2, 1.01e1, 8.98e-1, 6.40e-2, 1.80e-3, 0, 0, 3.50e-3, 3.45e-2, 9.20e-2, 1.32e-1, 6.20e-2, 2.30e-2, 1.00e-2, 0.00};
601 //const Double_t beta0 = getbeta(wavelength, oz_vigroux);
602 const Double_t beta0 = GetBeta(wavelength, *fAbsCoeffOzone);
603
604 // Now use the pre-calculated values of the path integral
605 // for h and theta
606 const UInt_t H = TMath::Nint(h/1e4);
607 const UInt_t T = TMath::Min(89, TMath::Nint(theta/STEPTHETA));
608
609 const Double_t path = ozone_path[H][T];
610
611 return TMath::Exp(-beta0*path);
612 }
613
614 Double_t CalcAerosolAbsorption(Double_t h, Double_t wavelength, Double_t theta) const
615 {
616 if (!fAbsCoeffAerosols)
617 return 1;
618
619 //******* Mie (aerosol) *******
620 if (h > 30.e5)
621 h = 30.e5;
622
623 //const float aero_betap[15] = {0.27, 0.26, 0.25, 0.24, 0.24, 0.23, 0.20, 0.180, 0.167, 0.158, 0.150, 0.142, 0.135, 0.127, 0.120};
624 //const Double_t beta0 = getbeta(wavelength, aero_betap);
625 const Double_t beta0 = GetBeta(wavelength, *fAbsCoeffAerosols);
626
627 // Now use the pre-calculated values of the path integral
628 // for h and theta
629 const UInt_t H = TMath::Nint(h/1e4);
630 const UInt_t T = TMath::Min(89, TMath::Nint(theta/STEPTHETA));
631
632
633 const Double_t path = aerosol_path[H][T];
634
635 return TMath::Exp(-beta0*path);
636 }
637
638 Double_t GetTransmission(const MPhotonData &ph) const
639 {
640 const Double_t wavelength = ph.GetWavelength();
641 const Double_t height = ph.GetProductionHeight();
642
643 // Reduce the necessary number of floating point operations
644 // by storing the intermediate results
645 const Double_t sin2 = ph.GetSinW2();
646 const Double_t cost = TMath::Sqrt(1-sin2);
647 const Double_t theta = TMath::ACos(cost);
648
649 // Path from production height to obslevel
650 const Double_t z = height-fObsLevel;
651
652 // Distance of emission point to incident point on ground
653 const Double_t d = z/cost;
654
655 // Avoid problems if photon is very close to telescope:
656 if (TMath::Abs(d)<1)
657 return 1;
658
659 // Earth radius plus observation height (distance of telescope
660 // from earth center)
661 const Double_t H = R() + fObsLevel;
662
663 // We calculate h, the true height a.s.l.
664 // of the photon emission point in cm
665 const Double_t h = TMath::Sqrt(H*H + d*d + 2*H*z) - R();
666
667 //**** Rayleigh scattering: *****
668 const Double_t T_Ray = CalcTransmission(h, wavelength, sin2);
669 if (T_Ray<0)
670 return 0;
671
672 //****** Ozone absorption: ******
673 const Double_t T_Oz = CalcOzoneAbsorption(h, wavelength, theta);
674
675 //******** Mie (aerosol) ********
676 const Double_t T_Mie = CalcAerosolAbsorption(h, wavelength, theta);
677
678 // FIXME: What if I wanna display these values?
679
680 // Calculate final transmission coefficient
681 return T_Ray * T_Oz * T_Mie;
682 }
683};
684
685const Double_t MAtmosphere::STEPTHETA = 1.74533e-2; // aprox. 1 degree
686
687const Double_t MAtmRayleigh::fgMeanFreePath = 2970;
688
689const Double_t MAtmosphere::aero_n[31] = {200, 87, 38, 16, 7.2, 3.1, 1.1, 0.4, 0.14, 5.0e-2, 2.6e-2, 2.3e-2, 2.1e-2, 2.3e-2, 2.5e-2, 4.1e-2, 6.7e-2, 7.3e-2, 8.0e-2, 9.0e-2, 8.6e-2, 8.2e-2, 8.0e-2, 7.6e-2, 5.2e-2, 3.6e-2, 2.5e-2, 2.4e-2, 2.2e-2, 2.0e-2, 1.9e-2};
690
691const Double_t MAtmosphere::oz_conc[51]={0.3556603E-02, 0.3264150E-02, 0.2933961E-02, 0.2499999E-02, 0.2264150E-02, 0.2207546E-02, 0.2160377E-02, 0.2226414E-02, 0.2283018E-02, 0.2811320E-02, 0.3499999E-02, 0.4603772E-02, 0.6207545E-02, 0.8452828E-02, 0.9528299E-02, 0.9905657E-02, 0.1028302E-01, 0.1113207E-01, 0.1216981E-01, 0.1424528E-01, 0.1641509E-01, 0.1839622E-01, 0.1971697E-01, 0.1981131E-01, 0.1933962E-01, 0.1801886E-01, 0.1632075E-01, 0.1405660E-01, 0.1226415E-01, 0.1066037E-01, 0.9028300E-02, 0.7933960E-02, 0.6830187E-02, 0.5820753E-02, 0.4830188E-02, 0.4311319E-02, 0.3613206E-02, 0.3018867E-02, 0.2528301E-02, 0.2169811E-02, 0.1858490E-02, 0.1518867E-02, 0.1188679E-02, 0.9301884E-03, 0.7443394E-03, 0.5764149E-03, 0.4462263E-03, 0.3528301E-03, 0.2792452E-03, 0.2226415E-03, 0.1858490E-03};
692
693// ==========================================================================
694
695// --------------------------------------------------------------------------
696//
697// Default Constructor.
698//
699MSimAtmosphere::MSimAtmosphere(const char* name, const char *title)
700 : fRunHeader(0), fEvt(0), fAtmosphere(0),
701 fFileAerosols("resmc/atmosphere-aerosols.txt"),
702 fFileOzone("resmc/atmosphere-ozone.txt"),
703 fForce(kFALSE)
704{
705 fName = name ? name : "MSimAtmosphere";
706 fTitle = title ? title : "Simulate the wavelength and height-dependant atmpsheric absorption";
707
708 fAtmosphere = new MAtmosphere;
709}
710
711// --------------------------------------------------------------------------
712//
713// Calls Clear()
714//
715MSimAtmosphere::~MSimAtmosphere()
716{
717 delete fAtmosphere;
718}
719
720// --------------------------------------------------------------------------
721//
722// Search for the needed parameter containers. Read spline from file
723// calling ReadFile();
724//
725Int_t MSimAtmosphere::PreProcess(MParList *pList)
726{
727 fEvt = (MPhotonEvent*)pList->FindObject("MPhotonEvent");
728 if (!fEvt)
729 {
730 *fLog << err << "MPhotonEvent not found... aborting." << endl;
731 return kFALSE;
732 }
733
734
735 return kTRUE;
736}
737
738// --------------------------------------------------------------------------
739//
740Bool_t MSimAtmosphere::ReInit(MParList *pList)
741{
742 fRunHeader = (MCorsikaRunHeader*)pList->FindObject("MCorsikaRunHeader");
743 if (!fRunHeader)
744 {
745 *fLog << err << "MCorsikaRunHeader not found... aborting." << endl;
746 return kFALSE;
747 }
748
749 if (fRunHeader->Has(MCorsikaRunHeader::kCeffic) && !fForce)
750 {
751 *fLog << inf << "CEFFIC enabled... task will be skipped." << endl;
752 return kTRUE;
753 }
754
755 //if (fRunHeader->Has(MCorsikaRunHeader::kRefraction))
756 // *fLog << inf << "Atmospheric refraction already applied in Corsika... skipping our own." << endl;
757
758 // FIXME: Check wavelength range
759
760 /*
761 if (h->GetWavelengthMin()<fSpline->GetXmin())
762 *fLog << warn << "WARNING - Lower bound of wavelength bandwidth exceeds lower bound of spline." << endl;
763
764 if (h->GetWavelengthMax()>fSpline->GetXmax())
765 *fLog << warn << "WARNING - Upper bound of wavelength bandwidth exceeds upper bound of spline." << endl;
766 */
767
768 fAtmosphere->Init(*fRunHeader, fFileOzone, fFileAerosols);
769
770 if (!fAtmosphere->IsAllValid())
771 {
772 *fLog << err << "ERROR - Something with the atmoshere's initialization went wrong!" << endl;
773 return kFALSE;
774 }
775
776 if (fRunHeader->GetWavelengthMin()<fAtmosphere->GetWavelengthMin())
777 *fLog << warn << "WARNING - Lower bound of wavelength bandwidth exceeds valid range of atmosphere." << endl;
778
779 if (fRunHeader->GetWavelengthMax()>fAtmosphere->GetWavelengthMax())
780 *fLog << warn << "WARNING - Lower bound of wavelength bandwidth exceeds valid range of atmosphere." << endl;
781
782 if (!fRunHeader->Has(MCorsikaRunHeader::kAtmext))
783 *fLog << warn << "WARNING - ATMEXT option not used for Corsika data." << endl;
784
785 if (!fRunHeader->Has(MCorsikaRunHeader::kRefraction))
786 *fLog << warn << "WARNING - Refraction calculation disabled for Corsika data." << endl;
787
788 return kTRUE;
789}
790
791// --------------------------------------------------------------------------
792//
793Int_t MSimAtmosphere::Process()
794{
795 // Skip the task if the CEFFIC option has been enabled and
796 // its excution has not been forced by the user
797 if (!fForce && fRunHeader->Has(MCorsikaRunHeader::kCeffic))
798 return kTRUE;
799
800 // Get the number of photons in the list
801 const Int_t num = fEvt->GetNumPhotons();
802
803 // FIMXE: Add checks for
804 // * upgoing particles
805 // * Can we take the full length until the camera into account?
806
807 // Counter for number of total and final events
808 Int_t cnt = 0;
809 for (Int_t i=0; i<num; i++)
810 {
811 // Get i-th photon from the list
812 const MPhotonData &ph = (*fEvt)[i];
813
814 // Get atmospheric transmission for this photon
815 const Double_t eff = fAtmosphere->GetTransmission(ph);
816
817 // Get a random value between 0 and 1 to determine whether the photon will survive
818 // gRandom->Rndm() = [0;1[
819 if (gRandom->Rndm()>=eff)
820 continue;
821
822 // Copy the surviving events bakc in the list
823 (*fEvt)[cnt++] = ph;
824 }
825
826 // Now we shrink the array to the number of new entries.
827 fEvt->Shrink(cnt);
828
829 return kTRUE;
830}
831
832/*
833 Int_t MSimWavelength::Process()
834 {
835 // Get the number of photons in the list
836 const Int_t num = fEvt->GetNumPhotons();
837
838 // FIMXE: Add checks for
839 // * upgoing particles
840 // * wavelength range
841 // * check if corsika atmosphere is switched on
842 // * Can we take the full length until the camera into account?
843
844 // Counter for number of total and final events
845 Int_t cnt = 0;
846 for (Int_t i=0; i<num; i++)
847 {
848 // Get i-th photon from the list
849 MPhotonData &ph = (*fEvt)[i];
850
851 const Double_t min = fRunHeader->GetWavelengthMin(); // WAVLGL
852 const Double_t max = fRunHeader->GetWavelengthMax(); // WAVLGU
853 const Double_t f = (max-min)/max;
854
855 // WAVELENGTH = 1. / (1/min - RD(1)/(min*max/(max-min)))
856
857
858 ph.SetWavelength(TMath::Nint(min / (1. - gRandom->Rndm()*f)));
859 }
860
861 return kTRUE;
862 }
863 */
864
865// --------------------------------------------------------------------------
866//
867// FileAerosols: resmc/atmosphere-aerosols.txt
868// FileOzone: resmc/atmosphere-ozone.txt
869//
870Int_t MSimAtmosphere::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
871{
872 Bool_t rc = kFALSE;
873
874 if (IsEnvDefined(env, prefix, "FileAerosols", print))
875 {
876 rc = kTRUE;
877 fFileAerosols = GetEnvValue(env, prefix, "FileAerosols", fFileAerosols);
878 }
879
880 if (IsEnvDefined(env, prefix, "FileOzone", print))
881 {
882 rc = kTRUE;
883 fFileOzone = GetEnvValue(env, prefix, "FileOzone", fFileOzone);
884 }
885
886 if (IsEnvDefined(env, prefix, "Force", print))
887 {
888 rc = kTRUE;
889 fForce = GetEnvValue(env, prefix, "Force", fForce);
890 }
891
892 return rc;
893}
Note: See TracBrowser for help on using the repository browser.