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

Last change on this file since 20051 was 19852, checked in by tbretz, 5 years ago
Changed the default atmosphere without user initailization to Keilhauer US Std. Make sure that the overburden below the obs level is 0. Split the Init functions such that the observation level can be changed indendently from the atmospshere. Copy the height levels from MRunHeader.
File size: 21.3 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-2019
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MAtmosphere
28//
29// Class to calculate atmospheric absorption.
30//
31//////////////////////////////////////////////////////////////////////////////
32#include "MAtmosphere.h"
33
34#include <fstream>
35
36#include <TGraph.h>
37
38#include "MLog.h"
39#include "MLogManip.h"
40
41#include "MParList.h"
42
43#include "MCorsikaRunHeader.h"
44#include "MPhotonEvent.h"
45#include "MPhotonData.h"
46
47ClassImp(MAtmosphere);
48ClassImp(MAtmRayleigh);
49
50using namespace std;
51
52// ==========================================================================
53//
54// January 2002, A. Moralejo: We now precalculate the slant paths for the
55// aerosol and Ozone vertical profiles, and then do an interpolation in
56// wavelength for every photon to get the optical depths. The parameters
57// used, defined below, have been taken from "Atmospheric Optics", by
58// L. Elterman and R.B. Toolin, chapter 7 of the "Handbook of geophysics
59// and Space environments". (S.L. Valley, editor). McGraw-Hill, NY 1965.
60//
61// WARNING: the Mie scattering and the Ozone absorption are implemented
62// to work only with photons produced at a height a.s.l larger than the
63// observation level. So this is not expected to work well for simulating
64// the telescope pointing at theta > 90 deg (for instance for neutrino
65// studies. Rayleigh scattering works even for light coming from below.
66//
67// Fixed bugs (of small influence) in Mie absorption implementation: there
68// were errors in the optical depths table, as well as a confusion:
69// height a.s.l. was used as if it was height above the telescope level.
70// The latter error was also present in the Ozone aborption implementation.
71//
72// On the other hand, now we have the tables AE_ABI and OZ_ABI with optical
73// depths between sea level and a height h (before it was between 2km a.s.l
74// and a height h). So that we can simulate also in the future a different
75// observation level.
76//
77// AM: WARNING: IF VERY LARGE zenith angle simulations are to be done (say
78// above 85 degrees, for neutrino primaries or any other purpose) this code
79// will have to be adapted accordingly and checked, since in principle it has
80// been written and tested to simulate the absorption of Cherenkov photons
81// arriving at the telescope from above.
82//
83// AM: WARNING 2: not to be used for wavelengths outside the range 250-700 nm
84//
85// January 2003, Abelardo Moralejo: found error in Ozone absorption treatment.
86// At large zenith angles, the air mass used was the one calculated for
87// Rayleigh scattering, but since the Ozone distribution is rather different
88// from the global distribution of air molecules, this is not a good
89// approximation. Now I have left in this code only the Rayleigh scattering,
90// and moved to atm.c the Mie scattering and Ozone absorption calculated in
91// a better way.
92//
93// A. Moralejo, January 2003: added some parameters for Mie scattering
94// and Ozone absorption derived from the clear standard atmosphere model
95// in "Atmospheric Optics", by L. Elterman and R.B. Toolin, chapter 7 of
96// the "Handbook of geophysics and Space environments". S.L. Valley,
97// editor. McGraw-Hill, NY 1965.
98//
99// AM, Jan 2003: Changed the meaning of the argument height: now it is the
100// true height above sea level at which a photon has been emitted, before
101// it was the height given by Corsika, measured in the vertical of the
102// observer and not in the vertical of the emitting particle.
103//
104//
105// MAGIC-Winter and MAGIC-Summer by M. Haffke,
106// parametrizing profiles obtained with MSIS:
107// http://uap-www.nrl.navy.mil/models_web/msis/msis_home.htm
108//
109//
110// The MAGIC-Winter and MAGIC-Summer parametrisations reproduce the MSIS
111// profiles for the 3 atmospheric layers from 0 up to 40 km height. Beyond
112// that height, it was not possible to achieve a good fit, but the amount
113// of residual atmosphere is so small that light absorption would be
114// negligible anyway. Showers develop well below 40 km.
115//
116//
117// The mass overburden is given by T = AATM + BATM * exp(-h/CATM)
118// The last layer of the US standard atmosphere (in which T varies
119// linearly with h) is above 100 km and has not been included here
120// because it should not matter.
121//
122
123const Double_t MAtmosphere::STEPTHETA = 1.74533e-2; // aprox. 1 degree
124
125const Double_t MAtmRayleigh::fgMeanFreePath = 2970;
126
127const 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};
128
129const 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};
130
131MAtmRayleigh::MAtmRayleigh() : fR(MCorsikaRunHeader::fgEarthRadius),
132 // U.S. Standard Atmosphere (after Keilhauer)
133 fHeight{0, 7.0e5, 11.4e5, 37.0e5, 100e5},
134 //fAtmA{-149.801663, -57.932486, 0.63631894, 4.35453690e-4},
135 fAtmB{1183.6071, 1143.0425, 1322.9748, 655.67307},
136 fAtmC{954248.34, 800005.34, 629568.93, 737521.77},
137 fObsLevel(-1)
138
139{
140}
141
142// --------------------------------------------------------------------------
143//
144// Precalcalculate the integrals from the observer level to the next
145// atmpsheric layer below including the lower boundary. Thus a
146// correct calculation is reduced to the calculation of the upper
147// boundary.
148//
149// fRho[0] = B0;
150// fRho[1] = B0-A0 + B1;
151// fRho[2] = B0-A0 + B1-A1 + B2;
152// fRho[3] = B0-A0 + B1-A1 + B2+A2 + B3;
153// fRho[4] = B0-A0 + B1-A1 + B2+A2 + B3 - A3;
154//
155void MAtmRayleigh::PreCalcRho()
156{
157 // Limits (height in cm) of the four layers in which
158 // atmosphere is parametrized.
159 // This is a stupid trick giving 0 for the integrals below
160 // the observer
161
162 fRho[0] = 0;
163 for (int i=0; i<4; i++)
164 {
165 // Below the observation level, the atmospheric overburden is 0
166 const Float_t &h1 = fHeight[i+1];
167 if (h1<=fObsLevel)
168 fRho[i] = 0;
169
170 // Start integration of the overburden at fObsLevel
171 const Float_t &h0 = TMath::Max(fHeight[i], fObsLevel);
172
173 const Float_t &b = fAtmB[i];
174 const Float_t &c = fAtmC[i];
175
176 const Double_t B = b*TMath::Exp(-h0/c);
177 const Double_t A = b*TMath::Exp(-h1/c);
178
179 // Calculate rho for the i-th layer from the lower
180 // to the higher layer boundary.
181 // If height is within the layer only calculate up to height.
182 fRho[i] += B;
183 fRho[i+1] = fRho[i] - A;
184 }
185}
186
187void MAtmRayleigh::Init(Float_t obs)
188{
189 // Observation level above earth radius
190 fObsLevel = obs;
191 PreCalcRho();
192}
193
194void MAtmRayleigh::Init(Float_t obs, const Float_t *atmb, const Float_t *atmc, const Float_t *height)
195{
196 memcpy(fAtmB, atmb, sizeof(Float_t)*4);
197 memcpy(fAtmC, atmc, sizeof(Float_t)*4);
198 memcpy(fHeight, height, sizeof(Float_t)*5);
199
200 Init(obs);
201}
202
203// Init an atmosphere from the data stored in MCorsikaRunHeader
204// This initialized fObsLevel, fR, fAtmB and fAtmC and
205// PreCalcRho
206void MAtmRayleigh::Init(const MCorsikaRunHeader &h)
207{
208 // Use earth radius as defined in Corsika
209 fR = h.EarthRadius();
210
211 //memcpy(fAtmA, h.GetAtmosphericCoeffA(), sizeof(Float_t)*4);
212 Init(h.GetObsLevel(), h.GetAtmosphericCoeffB(), h.GetAtmosphericCoeffC(), h.GetAtmosphericLayers());
213}
214
215// Return the vertical thickness between the observer and height.
216// Therefor the integral of the layers below (including the lower
217// boudary) Have been precalculated by PreCalcRho
218Double_t MAtmRayleigh::GetVerticalThickness(Double_t height) const
219{
220 if (height<=fObsLevel)
221 return 0;
222
223 // FIXME: We could store the start point above obs-level
224 // (Does this really gain anything?)
225 Int_t i=0;
226 while (i<4 && height>fHeight[i+1])
227 i++;
228
229 const Double_t b = fAtmB[i];
230 const Double_t c = fAtmC[i];
231
232 // fRho is the integral up to the lower bound of the layer or the
233 // observation level is the obervation level is within the bin
234 return fRho[i] - b*TMath::Exp(-height/c);
235}
236
237/*
238// The "orginal" code for the vertical thickness
239Double_t GetVerticalThickness(Double_t obslev, Double_t height) const
240{
241 // This is a C++-version of the original code from attenu.c
242
243 // Limits (height in cm) of the four layers in which atmosphere is parametrized:
244 const double lahg[5] =
245 {
246 obslev,
247 TMath::Max(obslev, 4e5),
248 1.0e6,
249 4.0e6,
250 1.0e7
251 };
252
253 Double_t Rho_Tot = 0.0;
254 for (int i=0; i<4; i++)
255 {
256 const Double_t b = fAtmB[i];
257 const Double_t c = fAtmC[i];
258
259 const Double_t h0 = TMath::Min(height, lahg[i+1]);
260 const Double_t h1 = lahg[i];
261
262 // Calculate rho for the i-th layer from the lower
263 // to the higher layer boundary.
264 // If height is within the layer only calculate up to height.
265 Rho_Tot += b*(exp(-h1/c) - exp(-h0/c));
266
267 if (lahg[i+1] > height)
268 break;
269 }
270
271 return Rho_Tot;
272}
273*/
274
275Double_t MAtmRayleigh::CalcTransmission(Double_t height, Double_t wavelength, Double_t sin2) const
276{
277 // sin2: sin(theta)^2
278 // height is the true height a.s.l.
279
280 // LARGE ZENITH ANGLE FACTOR (AIR MASS FACTOR):
281 // Air mass factor "airmass" calculated using a one-exponential
282 // density profile for the atmosphere,
283 //
284 // rho = rho_0 exp(-height/hscale) with hscale = 7.4 km
285 //
286 // The air mass factor is defined as I(theta)/I(0), the ratio of
287 // the optical paths I (in g/cm2) traversed between two given
288 // heights, at theta and at 0 deg z.a.
289
290 const Double_t H = height-fObsLevel;
291 const Double_t h = 2*H;
292
293 // Scale-height (cm) for Exponential density profile
294 const Double_t hscale = 7.4e5;
295 const Double_t f = 2*hscale;
296
297 // Precalc R*cos(theta)^2 (FIXME: Is ph.GetCosW2 more precise?)
298 const Double_t Rcos2 = fR * (1-sin2); // cos2 = 1 - sin2
299
300 const Double_t x1 = TMath::Sqrt((Rcos2 ) / f);
301 const Double_t x2 = TMath::Sqrt((Rcos2 + 2*h) / f);
302 const Double_t x3 = TMath::Sqrt((fR ) / f);
303 const Double_t x4 = TMath::Sqrt((fR + 2*h) / f);
304
305 // Return a -1 transmittance in the case the photon comes
306 // exactly from the observation level, because in that case the
307 // "air mass factor" would become infinity and the calculation
308 // is not valid!
309 if (fabs(x3-x4) < 1.e-10)
310 return -1.;
311
312 const Double_t e12 = erfc(x1) - erfc(x2);
313 const Double_t e34 = erfc(x3) - erfc(x4);
314
315 const Double_t airmass = TMath::Exp(-fR*sin2 / f) * e12/e34;
316
317 // Calculate the traversed "vertical thickness" of air using the
318 // US Standard atmosphere:
319 const Double_t Rho_tot = GetVerticalThickness(/*fObsLevel,*/ height);
320
321 // We now convert from "vertical thickness" to "slanted thickness"
322 // traversed by the photon on its way to the telescope, simply
323 // multiplying by the air mass factor m:
324 const Double_t Rho_Fi = airmass * Rho_tot;
325
326 // Finally we calculate the transmission coefficient for the Rayleigh
327 // scattering:
328 // AM Dec 2002, introduced ABS below to account (in the future) for
329 // possible photons coming from below the observation level.
330
331 const Double_t wl = 400./wavelength;
332
333 // Mean free path for scattering Rayleigh XR (g/cm^2)
334 return TMath::Exp(-TMath::Abs(Rho_Fi/fgMeanFreePath)*wl*wl*wl*wl);
335}
336
337// ==========================================================================
338
339// Interpolate the graph at wavelength
340Double_t MAtmosphere::GetBeta(Double_t wavelength, const TGraph &g) const
341{
342 // FIXME: This might not be the fastest because range
343 // checks are done for each call!
344 return g.GetN()==0 ? 0 : g.Eval(wavelength)*1e-5; // from km^-1 to cm^-1
345/*
346 // Linear interpolation
347 // (FIXME: Is it faster to be replaced with a binary search?)
348 // ( This might be faster because we have more photons
349 // with smaller wavelengths)
350 //int index;
351 //for (index = 1; index <g.GetN()-1; index++)
352 // if (wavelength < g.GetX()[index])
353 // break;
354 const Int_t index = TMath::BinarySearch(g.GetN(), g.GetX(), wavelength)+1;
355
356 const Double_t t0 = g.GetY()[index-1];
357 const Double_t t1 = g.GetY()[index];
358
359 const Double_t w0 = g.GetX()[index-1];
360 const Double_t w1 = g.GetX()[index];
361
362 const Double_t beta0 = t0+(t1-t0)*(wavelength-w0)/(w1-w0);
363
364 return beta0 * 1e-5; // from km^-1 to cm^-1
365*/
366}
367
368MAtmosphere::~MAtmosphere()
369{
370 if (fAbsCoeffOzone)
371 delete fAbsCoeffOzone;
372 if (fAbsCoeffAerosols)
373 delete fAbsCoeffAerosols;
374}
375
376Float_t MAtmosphere::GetWavelengthMin() const
377{
378 return fAbsCoeffOzone && fAbsCoeffAerosols ? TMath::Max(fAbsCoeffOzone->GetX()[0], fAbsCoeffAerosols->GetX()[0]) : -1;
379}
380
381Float_t MAtmosphere::GetWavelengthMax() const
382{
383 return fAbsCoeffOzone && fAbsCoeffAerosols ? TMath::Min(fAbsCoeffOzone->GetX()[fAbsCoeffOzone->GetN()-1], fAbsCoeffAerosols->GetX()[fAbsCoeffAerosols->GetN()-1]) : -1;
384}
385
386Bool_t MAtmosphere::HasValidOzone() const
387{
388 return fAbsCoeffOzone && fAbsCoeffOzone->GetN()>0;
389}
390
391Bool_t MAtmosphere::HasValidAerosol() const
392{
393 return fAbsCoeffAerosols && fAbsCoeffAerosols->GetN()>0;
394}
395
396void MAtmosphere::PreCalcOzone()
397{
398 // It follows a precalculation of the slant path integrals we need
399 // for the estimate of the Mie scattering and Ozone absorption:
400 Double_t dh = 1.e3;
401 //const Double_t STEPTHETA = 1.74533e-2; // aprox. 1 degree
402
403 // Ozone absorption
404 for (Int_t j = 0; j < 90; j++)
405 {
406 const Double_t theta = j * STEPTHETA;
407 const Double_t sin2 = sin(theta)*sin(theta);
408 const Double_t H = R()+fObsLevel;
409
410 Double_t path_slant = 0;
411 for (Double_t h=fObsLevel; h<50e5+dh/2; h+=dh)
412 {
413 // h is the true height vertical above ground
414 //if (fmod(h,1e4) == 0)
415 {
416 ozone_path[TMath::FloorNint(h/1e4)][j] = path_slant;
417 }
418
419 const Double_t km = h/1e5;
420 const Int_t i = TMath::FloorNint(km);
421 const Double_t l = R()+h;
422
423 const Double_t L = TMath::Sqrt(l*l - H*H * sin2);
424 const Double_t f = dh * l / L;
425
426 // Linear interpolation at h/1e5
427 Double_t interpol = oz_conc[i] + fmod(km, 1) * (oz_conc[i+1]-oz_conc[i]);
428
429 path_slant += f * interpol;
430 }
431 }
432}
433
434void MAtmosphere::PreCalcAerosol()
435{
436 // It follows a precalculation of the slant path integrals we need
437 // for the estimate of the Mie scattering and Ozone absorption:
438 Double_t dh = 1.e3;
439 //const Double_t STEPTHETA = 1.74533e-2; // aprox. 1 degree
440
441 /* Mie (aerosol): */
442 for (Int_t j = 0; j < 90; j++)
443 {
444 const Double_t theta = j * STEPTHETA;
445 const Double_t sin2 = sin(theta)*sin(theta);
446 const Double_t H = R()+fObsLevel;
447
448 Double_t path_slant = 0;
449 for (Double_t h=fObsLevel; h<=30e5+dh/2; h+=dh)
450 {
451 // h is the true height vertical above ground
452 //if (fmod(h,1e4) == 0)
453 {
454 aerosol_path[TMath::FloorNint(h/1e4)][j] = path_slant;
455 }
456
457 const Double_t km = h/1e5;
458 const Int_t i = TMath::FloorNint(km);
459 const Double_t l = R()+h;
460
461 const Double_t L = TMath::Sqrt(l*l - H*H * sin2);
462 const Double_t f = dh * l / L;
463
464 // Linear interpolation at h/1e5
465 Double_t interpol = aero_n[i] + fmod(km, 1)*(aero_n[i+1]-aero_n[i]);
466
467 path_slant += f * interpol/aero_n[0]; // aero_n [km^-1]
468 }
469 }
470}
471
472Bool_t MAtmosphere::InitOzone(const TString name)
473{
474 if (!name.IsNull())
475 {
476 if (fAbsCoeffOzone)
477 delete fAbsCoeffOzone;
478
479 fAbsCoeffOzone = new TGraph(name);
480 fAbsCoeffOzone->Sort();
481 }
482
483 if (!HasValidOzone())
484 return kFALSE;
485
486 if (IsValid())
487 PreCalcOzone();
488
489 return kTRUE;
490}
491
492Bool_t MAtmosphere::InitAerosols(const TString name)
493{
494 if (!name.IsNull())
495 {
496 if (fAbsCoeffAerosols)
497 delete fAbsCoeffAerosols;
498
499 fAbsCoeffAerosols = new TGraph(name);
500 fAbsCoeffAerosols->Sort();
501 }
502
503 if (!HasValidAerosol())
504 return kFALSE;
505
506 if (IsValid())
507 PreCalcAerosol();
508
509 return kTRUE;
510}
511
512/*
513Double_t GetOz(Double_t height, Double_t theta) const
514{
515 // Distance between two points D = 1km /cos(theta)
516 // Density along y within this km: f = (x[i+1]-x[i])/1km * dy
517 // Integral of this density f = (x[i+1]-x[i])/1km * (y[i+1]-y[i])
518 // f(h) = int [ (c1-c0)/1km*(h-h0)*dh + c0 ] dh
519 // = (c-co)*(h-h0)
520
521 Double_t rc = 0;
522 int i;
523 for (i=0; i<49; i++)
524 if (i>=2 && i+1<height/1e5) // cm -> km
525 rc += oz_conc[i] * 1e5/cos(theta);
526
527 rc -= oz_conc[2]*0.2*1e5/cos(theta);
528 rc += oz_conc[i+1]*fmod(height/1e5,1)*1e5/cos(theta);
529
530 return rc;
531}
532*/
533
534Double_t MAtmosphere::CalcOzoneAbsorption(Double_t h, Double_t wavelength, Double_t theta) const
535{
536 if (!fAbsCoeffOzone)
537 return 1;
538
539 //******* Ozone absorption *******
540
541 // Vigroux Ozone absorption coefficient a.s.l. through interpolation:
542 //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};
543 //const Double_t beta0 = getbeta(wavelength, oz_vigroux);
544 const Double_t beta0 = GetBeta(wavelength, *fAbsCoeffOzone);
545
546 // Now use the pre-calculated values of the path integral
547 // for h and theta
548 const UInt_t H = TMath::Min(500, TMath::Nint(h/1e4));
549 const UInt_t T = TMath::Min( 89, TMath::Nint(theta/STEPTHETA));
550
551 const Double_t path = ozone_path[H][T];
552
553 return TMath::Exp(-beta0*path);
554}
555
556Double_t MAtmosphere::CalcAerosolAbsorption(Double_t h, Double_t wavelength, Double_t theta) const
557{
558 if (!fAbsCoeffAerosols)
559 return 1;
560
561 //******* Mie (aerosol) *******
562
563 //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};
564 //const Double_t beta0 = getbeta(wavelength, aero_betap);
565 const Double_t beta0 = GetBeta(wavelength, *fAbsCoeffAerosols);
566
567 // Now use the pre-calculated values of the path integral
568 // for h and theta
569 const UInt_t H = TMath::Min(300, TMath::Nint(h/1e4));
570 const UInt_t T = TMath::Min( 89, TMath::Nint(theta/STEPTHETA));
571
572 const Double_t path = aerosol_path[H][T];
573
574 return TMath::Exp(-beta0*path);
575}
576
577Double_t MAtmosphere::GetTransmission(const MPhotonData &ph) const
578{
579 const Double_t wavelength = ph.GetWavelength();
580 const Double_t height = ph.GetProductionHeight();
581
582 // Reduce the necessary number of floating point operations
583 // by storing the intermediate results
584 const Double_t sin2 = ph.GetSinW2();
585 const Double_t cost = TMath::Sqrt(1-sin2);
586 const Double_t theta = TMath::ACos(cost);
587
588 // Path from production height to obslevel
589 const Double_t z = height-fObsLevel;
590
591 // Distance of emission point to incident point on ground
592 const Double_t d = z/cost;
593
594 // Avoid problems if photon is very close to telescope:
595 if (TMath::Abs(d)<1)
596 return 1;
597
598 // Earth radius plus observation height (distance of telescope
599 // from earth center)
600 const Double_t H = R() + fObsLevel;
601
602 // We calculate h, the true height a.s.l.
603 // of the photon emission point in cm
604 const Double_t h = TMath::Sqrt(H*H + d*d + 2*H*z) - R();
605
606 //**** Rayleigh scattering: *****
607 const Double_t T_Ray = CalcTransmission(h, wavelength, sin2);
608 if (T_Ray<0)
609 return 0;
610
611 //****** Ozone absorption: ******
612 const Double_t T_Oz = CalcOzoneAbsorption(h, wavelength, theta);
613
614 //******** Mie (aerosol) ********
615 const Double_t T_Mie = CalcAerosolAbsorption(h, wavelength, theta);
616
617 // FIXME: What if I wanna display these values?
618
619 // Calculate final transmission coefficient
620 return T_Ray * T_Oz * T_Mie;
621}
622
Note: See TracBrowser for help on using the repository browser.