source: trunk/Mars/msim/MAtmosphere.cc@ 19766

Last change on this file since 19766 was 19765, checked in by tbretz, 5 years ago
fHeight is not used right now... (note that the current default coeffieints also don't match). The loops for ozone and aerosols stopped one entry to early which produced a step in aerosol contents as for all hogher values the last entry was used, aerosol and ozone was only properly initialized if numericall by chance the counting hit the bin border, InitOzone errornesously checked for valid Aerosols, prepared a more reaosnable default consztrucotr: the current one did more or less nothing as the fObLevel was not yet initialized.
File size: 21.4 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 "MAtmosphere.h"
40
41#include <fstream>
42
43#include <TGraph.h>
44
45#include "MLog.h"
46#include "MLogManip.h"
47
48#include "MParList.h"
49
50#include "MCorsikaRunHeader.h"
51#include "MPhotonEvent.h"
52#include "MPhotonData.h"
53
54ClassImp(MAtmosphere);
55ClassImp(MAtmRayleigh);
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//
129
130const Double_t MAtmosphere::STEPTHETA = 1.74533e-2; // aprox. 1 degree
131
132const Double_t MAtmRayleigh::fgMeanFreePath = 2970;
133
134const 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};
135
136const 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};
137
138MAtmRayleigh::MAtmRayleigh() : fR(MCorsikaRunHeader::fgEarthRadius),
139 //fHeight{0, 300000, 1e+06, 5e+06, 1e+07},
140 //fAtmA{-144.838, -124.071, 0.360027, -0.000824761, 0.00221589},
141 fAtmB{1192.34, 1173.98, 1412.08, 810.682, 1},
142 fAtmC{994186, 967530, 636143, 735640, 4.92961e9},
143 fObsLevel(-1)
144{
145}
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//
160void MAtmRayleigh::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
200void MAtmRayleigh::Init(Double_t obs, const Float_t *atmb, const Float_t *atmc)
201{
202 // Observation level above earth radius
203 fObsLevel = obs;
204
205 //memcpy(fAtmA, (Float_t*)h.GetAtmosphericCoeffA(), sizeof(Float_t)*4);
206 if (atmb)
207 memcpy(fAtmB, atmb, sizeof(Float_t)*5);
208 if (atmc)
209 memcpy(fAtmC, atmc, sizeof(Float_t)*5);
210
211 PreCalcRho();
212}
213
214// Init an atmosphere from the data stored in MCorsikaRunHeader
215// This initialized fObsLevel, fR, fAtmB and fAtmC and
216// PreCalcRho
217void MAtmRayleigh::Init(const MCorsikaRunHeader &h)
218{
219 // Use earth radius as defined in Corsika
220 fR = h.EarthRadius();
221
222 Init(h.GetObsLevel(), h.GetAtmosphericCoeffB(), h.GetAtmosphericCoeffC());
223}
224
225// Return the vertical thickness between the observer and height.
226// Therefor the integral of the layers below (including the lower
227// boudary) Have been precalculated by PreCalcRho
228Double_t MAtmRayleigh::GetVerticalThickness(Double_t height) const
229{
230 // FIXME: We could store the start point above obs-level
231 // (Does this really gain anything?)
232 Int_t i=0;
233 while (i<4 && height>fHeight[i+1])
234 i++;
235
236 const Double_t b = fAtmB[i];
237 const Double_t c = fAtmC[i];
238
239 return fRho[i] - b*TMath::Exp(-height/c);
240}
241
242/*
243// The "orginal" code for the vertical thickness
244Double_t GetVerticalThickness(Double_t obslev, Double_t height) const
245{
246 // This is a C++-version of the original code from attenu.c
247
248 // Limits (height in cm) of the four layers in which atmosphere is parametrized:
249 const double lahg[5] =
250 {
251 obslev,
252 TMath::Max(obslev, 4e5),
253 1.0e6,
254 4.0e6,
255 1.0e7
256 };
257
258 Double_t Rho_Tot = 0.0;
259 for (int i=0; i<4; i++)
260 {
261 const Double_t b = fAtmB[i];
262 const Double_t c = fAtmC[i];
263
264 const Double_t h0 = TMath::Min(height, lahg[i+1]);
265 const Double_t h1 = lahg[i];
266
267 // Calculate rho for the i-th layer from the lower
268 // to the higher layer boundary.
269 // If height is within the layer only calculate up to height.
270 Rho_Tot += b*(exp(-h1/c) - exp(-h0/c));
271
272 if (lahg[i+1] > height)
273 break;
274 }
275
276 return Rho_Tot;
277}
278*/
279
280Double_t MAtmRayleigh::CalcTransmission(Double_t height, Double_t wavelength, Double_t sin2) const
281{
282 // sin2: sin(theta)^2
283 // height is the true height a.s.l.
284
285 // LARGE ZENITH ANGLE FACTOR (AIR MASS FACTOR):
286 // Air mass factor "airmass" calculated using a one-exponential
287 // density profile for the atmosphere,
288 //
289 // rho = rho_0 exp(-height/hscale) with hscale = 7.4 km
290 //
291 // The air mass factor is defined as I(theta)/I(0), the ratio of
292 // the optical paths I (in g/cm2) traversed between two given
293 // heights, at theta and at 0 deg z.a.
294
295 const Double_t H = height-fObsLevel;
296 const Double_t h = 2*H;
297
298 // Scale-height (cm) for Exponential density profile
299 const Double_t hscale = 7.4e5;
300 const Double_t f = 2*hscale;
301
302 // Precalc R*cos(theta)^2 (FIXME: Is ph.GetCosW2 more precise?)
303 const Double_t Rcos2 = fR * (1-sin2); // cos2 = 1 - sin2
304
305 const Double_t x1 = TMath::Sqrt((Rcos2 ) / f);
306 const Double_t x2 = TMath::Sqrt((Rcos2 + 2*h) / f);
307 const Double_t x3 = TMath::Sqrt((fR ) / f);
308 const Double_t x4 = TMath::Sqrt((fR + 2*h) / f);
309
310 // Return a -1 transmittance in the case the photon comes
311 // exactly from the observation level, because in that case the
312 // "air mass factor" would become infinity and the calculation
313 // is not valid!
314 if (fabs(x3-x4) < 1.e-10)
315 return -1.;
316
317 const Double_t e12 = erfc(x1) - erfc(x2);
318 const Double_t e34 = erfc(x3) - erfc(x4);
319
320 const Double_t airmass = TMath::Exp(-fR*sin2 / f) * e12/e34;
321
322 // Calculate the traversed "vertical thickness" of air using the
323 // US Standard atmosphere:
324 const Double_t Rho_tot = GetVerticalThickness(/*fObsLevel,*/ height);
325
326 // We now convert from "vertical thickness" to "slanted thickness"
327 // traversed by the photon on its way to the telescope, simply
328 // multiplying by the air mass factor m:
329 const Double_t Rho_Fi = airmass * Rho_tot;
330
331 // Finally we calculate the transmission coefficient for the Rayleigh
332 // scattering:
333 // AM Dec 2002, introduced ABS below to account (in the future) for
334 // possible photons coming from below the observation level.
335
336 const Double_t wl = 400./wavelength;
337
338 // Mean free path for scattering Rayleigh XR (g/cm^2)
339 return TMath::Exp(-TMath::Abs(Rho_Fi/fgMeanFreePath)*wl*wl*wl*wl);
340}
341
342// ==========================================================================
343
344// Interpolate the graph at wavelength
345Double_t MAtmosphere::GetBeta(Double_t wavelength, const TGraph &g) const
346{
347 // FIXME: This might not be the fastest because range
348 // checks are done for each call!
349 return g.GetN()==0 ? 0 : g.Eval(wavelength)*1e-5; // from km^-1 to cm^-1
350/*
351 // Linear interpolation
352 // (FIXME: Is it faster to be replaced with a binary search?)
353 // ( This might be faster because we have more photons
354 // with smaller wavelengths)
355 //int index;
356 //for (index = 1; index <g.GetN()-1; index++)
357 // if (wavelength < g.GetX()[index])
358 // break;
359 const Int_t index = TMath::BinarySearch(g.GetN(), g.GetX(), wavelength)+1;
360
361 const Double_t t0 = g.GetY()[index-1];
362 const Double_t t1 = g.GetY()[index];
363
364 const Double_t w0 = g.GetX()[index-1];
365 const Double_t w1 = g.GetX()[index];
366
367 const Double_t beta0 = t0+(t1-t0)*(wavelength-w0)/(w1-w0);
368
369 return beta0 * 1e-5; // from km^-1 to cm^-1
370*/
371}
372
373MAtmosphere::~MAtmosphere()
374{
375 if (fAbsCoeffOzone)
376 delete fAbsCoeffOzone;
377 if (fAbsCoeffAerosols)
378 delete fAbsCoeffAerosols;
379}
380
381Float_t MAtmosphere::GetWavelengthMin() const
382{
383 return fAbsCoeffOzone && fAbsCoeffAerosols ? TMath::Max(fAbsCoeffOzone->GetX()[0], fAbsCoeffAerosols->GetX()[0]) : -1;
384}
385
386Float_t MAtmosphere::GetWavelengthMax() const
387{
388 return fAbsCoeffOzone && fAbsCoeffAerosols ? TMath::Min(fAbsCoeffOzone->GetX()[fAbsCoeffOzone->GetN()-1], fAbsCoeffAerosols->GetX()[fAbsCoeffAerosols->GetN()-1]) : -1;
389}
390
391Bool_t MAtmosphere::HasValidOzone() const
392{
393 return fAbsCoeffOzone && fAbsCoeffOzone->GetN()>0;
394}
395
396Bool_t MAtmosphere::HasValidAerosol() const
397{
398 return fAbsCoeffAerosols && fAbsCoeffAerosols->GetN()>0;
399}
400
401void MAtmosphere::PreCalcOzone()
402{
403 // It follows a precalculation of the slant path integrals we need
404 // for the estimate of the Mie scattering and Ozone absorption:
405 Double_t dh = 1.e3;
406 //const Double_t STEPTHETA = 1.74533e-2; // aprox. 1 degree
407
408 // Ozone absorption
409 for (Int_t j = 0; j < 90; j++)
410 {
411 const Double_t theta = j * STEPTHETA;
412 const Double_t sin2 = sin(theta)*sin(theta);
413 const Double_t H = R()+fObsLevel;
414
415 Double_t path_slant = 0;
416 for (Double_t h=fObsLevel; h<50e5+dh/2; h+=dh)
417 {
418 // h is the true height vertical above ground
419 //if (fmod(h,1e4) == 0)
420 {
421 ozone_path[TMath::FloorNint(h/1e4)][j] = path_slant;
422 }
423
424 const Double_t km = h/1e5;
425 const Int_t i = TMath::FloorNint(km);
426 const Double_t l = R()+h;
427
428 const Double_t L = TMath::Sqrt(l*l - H*H * sin2);
429 const Double_t f = dh * l / L;
430
431 // Linear interpolation at h/1e5
432 Double_t interpol = oz_conc[i] + fmod(km, 1) * (oz_conc[i+1]-oz_conc[i]);
433
434 path_slant += f * interpol;
435 }
436 }
437}
438
439void MAtmosphere::PreCalcAerosol()
440{
441 // It follows a precalculation of the slant path integrals we need
442 // for the estimate of the Mie scattering and Ozone absorption:
443 Double_t dh = 1.e3;
444 //const Double_t STEPTHETA = 1.74533e-2; // aprox. 1 degree
445
446 /* Mie (aerosol): */
447 for (Int_t j = 0; j < 90; j++)
448 {
449 const Double_t theta = j * STEPTHETA;
450 const Double_t sin2 = sin(theta)*sin(theta);
451 const Double_t H = R()+fObsLevel;
452
453 Double_t path_slant = 0;
454 for (Double_t h=fObsLevel; h<=30e5+dh/2; h+=dh)
455 {
456 // h is the true height vertical above ground
457 //if (fmod(h,1e4) == 0)
458 {
459 aerosol_path[TMath::FloorNint(h/1e4)][j] = path_slant;
460 }
461
462 const Double_t km = h/1e5;
463 const Int_t i = TMath::FloorNint(km);
464 const Double_t l = R()+h;
465
466 const Double_t L = TMath::Sqrt(l*l - H*H * sin2);
467 const Double_t f = dh * l / L;
468
469 // Linear interpolation at h/1e5
470 Double_t interpol = aero_n[i] + fmod(km, 1)*(aero_n[i+1]-aero_n[i]);
471
472 path_slant += f * interpol/aero_n[0]; // aero_n [km^-1]
473 }
474 }
475}
476
477Bool_t MAtmosphere::InitOzone(const TString name)
478{
479 if (!name.IsNull())
480 {
481 if (fAbsCoeffOzone)
482 delete fAbsCoeffOzone;
483
484 fAbsCoeffOzone = new TGraph(name);
485 fAbsCoeffOzone->Sort();
486 }
487
488 if (!HasValidOzone())
489 return kFALSE;
490
491 if (IsValid())
492 PreCalcOzone();
493
494 return kTRUE;
495}
496
497Bool_t MAtmosphere::InitAerosols(const TString name)
498{
499 if (!name.IsNull())
500 {
501 if (fAbsCoeffAerosols)
502 delete fAbsCoeffAerosols;
503
504 fAbsCoeffAerosols = new TGraph(name);
505 fAbsCoeffAerosols->Sort();
506 }
507
508 if (!HasValidAerosol())
509 return kFALSE;
510
511 if (IsValid())
512 PreCalcAerosol();
513
514 return kTRUE;
515}
516
517/*
518Double_t GetOz(Double_t height, Double_t theta) const
519{
520 // Distance between two points D = 1km /cos(theta)
521 // Density along y within this km: f = (x[i+1]-x[i])/1km * dy
522 // Integral of this density f = (x[i+1]-x[i])/1km * (y[i+1]-y[i])
523 // f(h) = int [ (c1-c0)/1km*(h-h0)*dh + c0 ] dh
524 // = (c-co)*(h-h0)
525
526 Double_t rc = 0;
527 int i;
528 for (i=0; i<49; i++)
529 if (i>=2 && i+1<height/1e5) // cm -> km
530 rc += oz_conc[i] * 1e5/cos(theta);
531
532 rc -= oz_conc[2]*0.2*1e5/cos(theta);
533 rc += oz_conc[i+1]*fmod(height/1e5,1)*1e5/cos(theta);
534
535 return rc;
536}
537*/
538
539Double_t MAtmosphere::CalcOzoneAbsorption(Double_t h, Double_t wavelength, Double_t theta) const
540{
541 if (!fAbsCoeffOzone)
542 return 1;
543
544 //******* Ozone absorption *******
545
546 // Vigroux Ozone absorption coefficient a.s.l. through interpolation:
547 //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};
548 //const Double_t beta0 = getbeta(wavelength, oz_vigroux);
549 const Double_t beta0 = GetBeta(wavelength, *fAbsCoeffOzone);
550
551 // Now use the pre-calculated values of the path integral
552 // for h and theta
553 const UInt_t H = TMath::Min(500, TMath::Nint(h/1e4));
554 const UInt_t T = TMath::Min( 89, TMath::Nint(theta/STEPTHETA));
555
556 const Double_t path = ozone_path[H][T];
557
558 return TMath::Exp(-beta0*path);
559}
560
561Double_t MAtmosphere::CalcAerosolAbsorption(Double_t h, Double_t wavelength, Double_t theta) const
562{
563 if (!fAbsCoeffAerosols)
564 return 1;
565
566 //******* Mie (aerosol) *******
567
568 //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};
569 //const Double_t beta0 = getbeta(wavelength, aero_betap);
570 const Double_t beta0 = GetBeta(wavelength, *fAbsCoeffAerosols);
571
572 // Now use the pre-calculated values of the path integral
573 // for h and theta
574 const UInt_t H = TMath::Min(300, TMath::Nint(h/1e4));
575 const UInt_t T = TMath::Min( 89, TMath::Nint(theta/STEPTHETA));
576
577 const Double_t path = aerosol_path[H][T];
578
579 return TMath::Exp(-beta0*path);
580}
581
582Double_t MAtmosphere::GetTransmission(const MPhotonData &ph) const
583{
584 const Double_t wavelength = ph.GetWavelength();
585 const Double_t height = ph.GetProductionHeight();
586
587 // Reduce the necessary number of floating point operations
588 // by storing the intermediate results
589 const Double_t sin2 = ph.GetSinW2();
590 const Double_t cost = TMath::Sqrt(1-sin2);
591 const Double_t theta = TMath::ACos(cost);
592
593 // Path from production height to obslevel
594 const Double_t z = height-fObsLevel;
595
596 // Distance of emission point to incident point on ground
597 const Double_t d = z/cost;
598
599 // Avoid problems if photon is very close to telescope:
600 if (TMath::Abs(d)<1)
601 return 1;
602
603 // Earth radius plus observation height (distance of telescope
604 // from earth center)
605 const Double_t H = R() + fObsLevel;
606
607 // We calculate h, the true height a.s.l.
608 // of the photon emission point in cm
609 const Double_t h = TMath::Sqrt(H*H + d*d + 2*H*z) - R();
610
611 //**** Rayleigh scattering: *****
612 const Double_t T_Ray = CalcTransmission(h, wavelength, sin2);
613 if (T_Ray<0)
614 return 0;
615
616 //****** Ozone absorption: ******
617 const Double_t T_Oz = CalcOzoneAbsorption(h, wavelength, theta);
618
619 //******** Mie (aerosol) ********
620 const Double_t T_Mie = CalcAerosolAbsorption(h, wavelength, theta);
621
622 // FIXME: What if I wanna display these values?
623
624 // Calculate final transmission coefficient
625 return T_Ray * T_Oz * T_Mie;
626}
627
Note: See TracBrowser for help on using the repository browser.