1 | // $Id: LowtranAerosol.cc 2922 2011-06-12 14:21:23Z mabl $ |
---|
2 | // Author: Sylvain Moreggia 2006/04/24 |
---|
3 | |
---|
4 | /***************************************************************************** |
---|
5 | * ESAF: Euso Simulation and Analysis Framework * |
---|
6 | * * |
---|
7 | * Id: LowtranAerosol * |
---|
8 | * Package: <packagename> * |
---|
9 | * Coordinator: <coordinator> * |
---|
10 | * * |
---|
11 | *****************************************************************************/ |
---|
12 | |
---|
13 | //_____________________________________________________________________________ |
---|
14 | // |
---|
15 | // LowtranAerosol |
---|
16 | // |
---|
17 | // <extensive class description> |
---|
18 | // |
---|
19 | // Config file parameters |
---|
20 | // ====================== |
---|
21 | // |
---|
22 | // WARNING : ground altitude is not handled internally. It must be checked in the other parts of the code, from where herebelow methods are called |
---|
23 | // |
---|
24 | |
---|
25 | #include "LowtranAerosol.hh" |
---|
26 | #include "EarthVector.hh" |
---|
27 | #include "Config.hh" |
---|
28 | #include "EConst.hh" |
---|
29 | #include "EsafRandom.hh" |
---|
30 | #include "NumbersFileParser.hh" |
---|
31 | #include "ConfigFileParser.hh" |
---|
32 | |
---|
33 | using EConst::EarthRadius; |
---|
34 | using namespace sou; |
---|
35 | using namespace TMath; |
---|
36 | |
---|
37 | ClassImp(LowtranAerosol) |
---|
38 | |
---|
39 | //_____________________________________________________________________________ |
---|
40 | LowtranAerosol::LowtranAerosol(string name) : Aerosol(name) { |
---|
41 | // |
---|
42 | // Constructor |
---|
43 | // |
---|
44 | |
---|
45 | Configure(); |
---|
46 | Build(); |
---|
47 | } |
---|
48 | |
---|
49 | //_____________________________________________________________________________ |
---|
50 | LowtranAerosol::~LowtranAerosol() { |
---|
51 | // |
---|
52 | // Destructor |
---|
53 | // |
---|
54 | } |
---|
55 | |
---|
56 | |
---|
57 | //______________________________________________________________________________ |
---|
58 | void LowtranAerosol::Configure() { |
---|
59 | // |
---|
60 | // read config files |
---|
61 | // |
---|
62 | |
---|
63 | // init |
---|
64 | fWlrange[0] = 200*nm; |
---|
65 | fWlrange[1] = 300*nm; |
---|
66 | fWlrange[2] = 337.1*nm; |
---|
67 | fWlrange[3] = 550*nm; |
---|
68 | fWlrange[4] = 694.3*nm; |
---|
69 | fWlrange[5] = 1060*nm; |
---|
70 | fWlrange[6] = 1536*nm; |
---|
71 | |
---|
72 | // aerosol model |
---|
73 | fType = Conf()->GetStr("LowtranAerosol.fType"); |
---|
74 | } |
---|
75 | |
---|
76 | //______________________________________________________________________________ |
---|
77 | void LowtranAerosol::Build() { |
---|
78 | // |
---|
79 | // build the object according to config files |
---|
80 | // Properties at 70% humidity are tabulated : extinction, absorption, DHG coeff. for phase function |
---|
81 | // DHG coeff. come from fits on tabulated lowtran phase functions |
---|
82 | // All are wavelength dependent (7 values tabulated) |
---|
83 | // For Altitude profile, 3 values tabulated at 0, 1 and 2km |
---|
84 | // |
---|
85 | |
---|
86 | // build aerosol models |
---|
87 | if(fType == "rural23") { |
---|
88 | fSz[0] = 0.158/km; |
---|
89 | fSz[1] = 0.0991/km; |
---|
90 | fSz[2] = 0.0621/km; |
---|
91 | |
---|
92 | // values for 70% rel. humidity |
---|
93 | fKwl_ext[0] = 2.09544; // 200 nm |
---|
94 | fKwl_ext[1] = 1.74165; // 300 nm |
---|
95 | fKwl_ext[2] = 1.59981; // 337.1 nm |
---|
96 | fKwl_ext[3] = 1.; // 550 nm |
---|
97 | fKwl_ext[4] = 0.75316; // 694.3 nm |
---|
98 | fKwl_ext[5] = 0.42171; // 1060 nm |
---|
99 | fKwl_ext[6] = 0.24323; // 1536 nm |
---|
100 | //DEBUG : to test w.r.t lowtran, 0% humidity values |
---|
101 | //fKwl_ext[0] = 2.09291; // 200 nm |
---|
102 | //fKwl_ext[1] = 1.74582; // 300 nm |
---|
103 | //fKwl_ext[2] = 1.60500; // 337.1 nm |
---|
104 | //fKwl_ext[3] = 1.; // 550 nm |
---|
105 | //fKwl_ext[4] = 0.75203; // 694.3 nm |
---|
106 | //fKwl_ext[5] = 0.41943; // 1060 nm |
---|
107 | //fKwl_ext[6] = 0.24070; // 1536 nm |
---|
108 | |
---|
109 | // values for 70% rel. humidity |
---|
110 | fKwl_abs[0] = 0.62968; // 200 nm |
---|
111 | fKwl_abs[1] = 0.10816; // 300 nm |
---|
112 | fKwl_abs[2] = 0.07671; // 337.1 nm |
---|
113 | fKwl_abs[3] = 0.05380; // 550 nm |
---|
114 | fKwl_abs[4] = 0.04684; // 694.3 nm |
---|
115 | fKwl_abs[5] = 0.05335; // 1060 nm |
---|
116 | fKwl_abs[6] = 0.04614; // 1536 nm |
---|
117 | |
---|
118 | // values for 70% rel. humidity |
---|
119 | fG1[0] = 0.7632; |
---|
120 | fG1[1] = 0.6928; |
---|
121 | fG1[2] = 0.6885; // not available in lowtran, linear interpolation used here |
---|
122 | fG1[3] = 0.6638; |
---|
123 | fG1[4] = 0.6638; |
---|
124 | fG1[5] = 0.6638; |
---|
125 | fG1[6] = 0.6590; |
---|
126 | fG2[0] = -0.6488; |
---|
127 | fG2[1] = -0.6202; |
---|
128 | fG2[2] = -0.6076; // not available in lowtran, linear interpolation used here |
---|
129 | fG2[3] = -0.5351; |
---|
130 | fG2[4] = -0.5351; |
---|
131 | fG2[5] = -0.5351; |
---|
132 | fG2[6] = -0.3744; |
---|
133 | fF[0] = 0.7185; |
---|
134 | fF[1] = 0.985; |
---|
135 | fF[2] = 0.9837; // not available in lowtran, linear interpolation used here |
---|
136 | fF[3] = 0.9765; |
---|
137 | fF[4] = 0.9765; |
---|
138 | fF[5] = 0.9765; |
---|
139 | fF[6] = 0.9622; |
---|
140 | } |
---|
141 | else if(fType == "rural5") { |
---|
142 | fSz[0] = 0.77/km; |
---|
143 | fSz[1] = 0.77/km; |
---|
144 | fSz[2] = 0.0621/km; |
---|
145 | |
---|
146 | // values for 70% rel. humidity |
---|
147 | fKwl_ext[0] = 2.09544; // 200 nm |
---|
148 | fKwl_ext[1] = 1.74165; // 300 nm |
---|
149 | fKwl_ext[2] = 1.59981; // 337.1 nm |
---|
150 | fKwl_ext[3] = 1.; // 550 nm |
---|
151 | fKwl_ext[4] = 0.75316; // 694.3 nm |
---|
152 | fKwl_ext[5] = 0.42171; // 1060 nm |
---|
153 | fKwl_ext[6] = 0.24323; // 1536 nm |
---|
154 | //DEBUG : to test w.r.t lowtran, 0% humidity values |
---|
155 | //fKwl_ext[0] = 2.09291; // 200 nm |
---|
156 | //fKwl_ext[1] = 1.74582; // 300 nm |
---|
157 | //fKwl_ext[2] = 1.60500; // 337.1 nm |
---|
158 | //fKwl_ext[3] = 1.; // 550 nm |
---|
159 | //fKwl_ext[4] = 0.75203; // 694.3 nm |
---|
160 | //fKwl_ext[5] = 0.41943; // 1060 nm |
---|
161 | //fKwl_ext[6] = 0.24070; // 1536 nm |
---|
162 | |
---|
163 | // values for 70% rel. humidity |
---|
164 | fKwl_abs[0] = 0.62968; // 200 nm |
---|
165 | fKwl_abs[1] = 0.10816; // 300 nm |
---|
166 | fKwl_abs[2] = 0.07671; // 337.1 nm |
---|
167 | fKwl_abs[3] = 0.05380; // 550 nm |
---|
168 | fKwl_abs[4] = 0.04684; // 694.3 nm |
---|
169 | fKwl_abs[5] = 0.05335; // 1060 nm |
---|
170 | fKwl_abs[6] = 0.04614; // 1536 nm |
---|
171 | |
---|
172 | // values for 70% rel. humidity |
---|
173 | fG1[0] = 0.7632; |
---|
174 | fG1[1] = 0.6928; |
---|
175 | fG1[2] = 0.6885; // not available in lowtran, linear interpolation used here |
---|
176 | fG1[3] = 0.6638; |
---|
177 | fG1[4] = 0.6638; |
---|
178 | fG1[5] = 0.6638; |
---|
179 | fG1[6] = 0.6590; |
---|
180 | fG2[0] = -0.6488; |
---|
181 | fG2[1] = -0.6202; |
---|
182 | fG2[2] = -0.6076; // not available in lowtran, linear interpolation used here |
---|
183 | fG2[3] = -0.5351; |
---|
184 | fG2[4] = -0.5351; |
---|
185 | fG2[5] = -0.5351; |
---|
186 | fG2[6] = -0.3744; |
---|
187 | fF[0] = 0.7185; |
---|
188 | fF[1] = 0.985; |
---|
189 | fF[2] = 0.9837; // not available in lowtran, linear interpolation used here |
---|
190 | fF[3] = 0.9765; |
---|
191 | fF[4] = 0.9765; |
---|
192 | fF[5] = 0.9765; |
---|
193 | fF[6] = 0.9622; |
---|
194 | } |
---|
195 | else if(fType == "urban5") { |
---|
196 | fSz[0] = 0.77/km; |
---|
197 | fSz[1] = 0.77/km; |
---|
198 | fSz[2] = 0.0621/km; |
---|
199 | |
---|
200 | // values for 70% rel. humidity |
---|
201 | fKwl_ext[0] = 1.95582; // 200 nm |
---|
202 | fKwl_ext[1] = 1.64994; // 300 nm |
---|
203 | fKwl_ext[2] = 1.53070; // 337.1 nm |
---|
204 | fKwl_ext[3] = 1.; // 550 nm |
---|
205 | fKwl_ext[4] = 0.77614; // 694.3 nm |
---|
206 | fKwl_ext[5] = 0.46639; // 1060 nm |
---|
207 | fKwl_ext[6] = 0.29487; // 1536 nm |
---|
208 | //DEBUG : to test w.r.t lowtran, 0% humidity values |
---|
209 | //fKwl_ext[0] = 1.88816; // 200 nm |
---|
210 | //fKwl_ext[1] = 1.63316; // 300 nm |
---|
211 | //fKwl_ext[2] = 1.51867; // 337.1 nm |
---|
212 | //fKwl_ext[3] = 1.; // 550 nm |
---|
213 | //fKwl_ext[4] = 0.77785; // 694.3 nm |
---|
214 | //fKwl_ext[5] = 0.47095; // 1060 nm |
---|
215 | //fKwl_ext[6] = 0.30006; // 1536 nm |
---|
216 | |
---|
217 | // values for 70% rel. humidity |
---|
218 | fKwl_abs[0] = 0.69032; // 200 nm |
---|
219 | fKwl_abs[1] = 0.49367; // 300 nm |
---|
220 | fKwl_abs[2] = 0.45165; // 337.1 nm |
---|
221 | fKwl_abs[3] = 0.29741; // 550 nm |
---|
222 | fKwl_abs[4] = 0.24070; // 694.3 nm |
---|
223 | fKwl_abs[5] = 0.17399; // 1060 nm |
---|
224 | fKwl_abs[6] = 0.13146; // 1536 nm |
---|
225 | |
---|
226 | //TOFIX : there is a pb in the tabulated phase function coeff. for urban5 model |
---|
227 | // so far, they have been replaced by rural5 coeff. |
---|
228 | fG1[0] = 0.7632; |
---|
229 | fG1[1] = 0.6928; |
---|
230 | fG1[2] = 0.6885; // not available in lowtran, linear interpolation used here |
---|
231 | fG1[3] = 0.6638; |
---|
232 | fG1[4] = 0.6638; |
---|
233 | fG1[5] = 0.6638; |
---|
234 | fG1[6] = 0.6590; |
---|
235 | fG2[0] = -0.6488; |
---|
236 | fG2[1] = -0.6202; |
---|
237 | fG2[2] = -0.6076; // not available in lowtran, linear interpolation used here |
---|
238 | fG2[3] = -0.5351; |
---|
239 | fG2[4] = -0.5351; |
---|
240 | fG2[5] = -0.5351; |
---|
241 | fG2[6] = -0.3744; |
---|
242 | fF[0] = 0.7185; |
---|
243 | fF[1] = 0.985; |
---|
244 | fF[2] = 0.9837; // not available in lowtran, linear interpolation used here |
---|
245 | fF[3] = 0.9765; |
---|
246 | fF[4] = 0.9765; |
---|
247 | fF[5] = 0.9765; |
---|
248 | fF[6] = 0.9622; |
---|
249 | |
---|
250 | /* |
---|
251 | // values for 70% rel. humidity (urban5 coeff) |
---|
252 | fG1[0] = 0.7906; |
---|
253 | fG1[1] = 0.7632; |
---|
254 | fG1[2] = 0.7478; // not available in lowtran, linear interpolation used here |
---|
255 | fG1[3] = 0.6592; |
---|
256 | fG1[4] = 0.6592; |
---|
257 | fG1[5] = 0.6536; |
---|
258 | fG1[6] = 0.6590; |
---|
259 | fG2[0] = -0.0; |
---|
260 | fG2[1] = -0.6488; |
---|
261 | fG2[2] = -0.6797; // not available in lowtran, linear interpolation used here |
---|
262 | fG2[3] = -0.8568; |
---|
263 | fG2[4] = -0.8568; |
---|
264 | fG2[5] = 0.5032; |
---|
265 | fG2[6] = -0.3744; |
---|
266 | fF[0] = 1.; |
---|
267 | fF[1] = 0.7185; |
---|
268 | fF[2] = 0.7602; // not available in lowtran, linear interpolation used here |
---|
269 | fF[3] = 0.9998; |
---|
270 | fF[4] = 0.9998; |
---|
271 | fF[5] = 0.7134; |
---|
272 | fF[6] = 0.9622; |
---|
273 | */ |
---|
274 | } |
---|
275 | else if(fType == "maritime23") { |
---|
276 | fSz[0] = 0.158/km; |
---|
277 | fSz[1] = 0.0991/km; |
---|
278 | fSz[2] = 0.0621/km; |
---|
279 | |
---|
280 | // values for 70% rel. humidity |
---|
281 | fKwl_ext[0] = 1.36924; // 200 nm |
---|
282 | fKwl_ext[1] = 1.25443; // 300 nm |
---|
283 | fKwl_ext[2] = 1.20835; // 337.1 nm |
---|
284 | fKwl_ext[3] = 1.; // 550 nm |
---|
285 | fKwl_ext[4] = 0.91367; // 694.3 nm |
---|
286 | fKwl_ext[5] = 0.77089; // 1060 nm |
---|
287 | fKwl_ext[6] = 0.64987; // 1536 nm |
---|
288 | //DEBUG : to test w.r.t lowtran, 0% humidity values |
---|
289 | //fKwl_ext[0] = 1.47576; // 200 nm |
---|
290 | //fKwl_ext[1] = 1.32614; // 300 nm |
---|
291 | //fKwl_ext[2] = 1.26171; // 337.1 nm |
---|
292 | //fKwl_ext[3] = 1.; // 550 nm |
---|
293 | //fKwl_ext[4] = 0.88133; // 694.3 nm |
---|
294 | //fKwl_ext[5] = 0.70297; // 1060 nm |
---|
295 | //fKwl_ext[6] = 0.56487; // 1536 nm |
---|
296 | |
---|
297 | // values for 70% rel. humidity |
---|
298 | fKwl_abs[0] = 0.23367; // 200 nm |
---|
299 | fKwl_abs[1] = 0.03127; // 300 nm |
---|
300 | fKwl_abs[2] = 0.02070; // 337.1 nm |
---|
301 | fKwl_abs[3] = 0.01297; // 550 nm |
---|
302 | fKwl_abs[4] = 0.01063; // 694.3 nm |
---|
303 | fKwl_abs[5] = 0.01285; // 1060 nm |
---|
304 | fKwl_abs[6] = 0.01190; // 1536 nm |
---|
305 | |
---|
306 | // values for 70% rel. humidity |
---|
307 | fG1[0] = 0.8; |
---|
308 | fG1[1] = 0.75; |
---|
309 | fG1[2] = 0.7458; // not available in lowtran, linear interpolation used here |
---|
310 | fG1[3] = 0.7214; |
---|
311 | fG1[4] = 0.75; |
---|
312 | fG1[5] = 0.75; |
---|
313 | fG1[6] = 0.7445; |
---|
314 | fG2[0] = -0.5396; |
---|
315 | fG2[1] = -0.75; |
---|
316 | fG2[2] = -0.7302; // not available in lowtran, linear interpolation used here |
---|
317 | fG2[3] = -0.6163; |
---|
318 | fG2[4] = -0.5840; |
---|
319 | fG2[5] = -0.5840; |
---|
320 | fG2[6] = -0.6509; |
---|
321 | fF[0] = 0.9492; |
---|
322 | fF[1] = 0.9662; |
---|
323 | fF[2] = 0.9644; // not available in lowtran, linear interpolation used here |
---|
324 | fF[3] = 0.9539; |
---|
325 | fF[4] = 0.9643; |
---|
326 | fF[5] = 0.9643; |
---|
327 | fF[6] = 0.9926; |
---|
328 | } |
---|
329 | else Msg(EsafMsg::Panic) << "<Build> Wrong type of LowtranAerosol model = "<<fType << MsgDispatch; |
---|
330 | |
---|
331 | Msg(EsafMsg::Info) << "*** LowtranAerosol built ***"<< MsgDispatch; |
---|
332 | } |
---|
333 | |
---|
334 | //______________________________________________________________________________ |
---|
335 | Double_t LowtranAerosol::Kwl(Double_t wl, Bool_t abs) const { |
---|
336 | // |
---|
337 | // interpolate fKwl coeff. |
---|
338 | // if abs=kTRUE, return value for absorption, else return value for extinction |
---|
339 | // Linear interpolation as Lowtran does |
---|
340 | // |
---|
341 | |
---|
342 | Double_t rtn(0.), k1(0.), k2(0.), wl1(0.), wl2(0.); |
---|
343 | |
---|
344 | CheckWlRange(wl); |
---|
345 | |
---|
346 | // extinction coeff. |
---|
347 | if(!abs) { |
---|
348 | for(Int_t i=0; i < 6; i++) { |
---|
349 | if(wl >= fWlrange[i] && wl <= fWlrange[i+1]) { |
---|
350 | k1 = fKwl_ext[i]; |
---|
351 | k2 = fKwl_ext[i+1]; |
---|
352 | wl1 = fWlrange[i]; |
---|
353 | wl2 = fWlrange[i+1]; |
---|
354 | break; |
---|
355 | } |
---|
356 | } |
---|
357 | } |
---|
358 | // absorption coeff. |
---|
359 | else { |
---|
360 | for(Int_t i=0; i < 6; i++) { |
---|
361 | if(wl >= fWlrange[i] && wl <= fWlrange[i+1]) { |
---|
362 | k1 = fKwl_abs[i]; |
---|
363 | k2 = fKwl_abs[i+1]; |
---|
364 | wl1 = fWlrange[i]; |
---|
365 | wl2 = fWlrange[i+1]; |
---|
366 | break; |
---|
367 | } |
---|
368 | } |
---|
369 | } |
---|
370 | |
---|
371 | rtn = (k2 - k1) / (wl2 - wl1) * (wl - wl2) + k2; |
---|
372 | |
---|
373 | return rtn; |
---|
374 | } |
---|
375 | |
---|
376 | //______________________________________________________________________________ |
---|
377 | Bool_t LowtranAerosol::IsInAerosol(const EarthVector& pos) const { |
---|
378 | // |
---|
379 | // true if position is within aerosol, false otherwise |
---|
380 | // |
---|
381 | |
---|
382 | return IsInAerosolZone(pos,ALL); |
---|
383 | } |
---|
384 | |
---|
385 | //______________________________________________________________________________ |
---|
386 | Bool_t LowtranAerosol::IsInAerosolZone(const EarthVector& pos, AerZone zone) const { |
---|
387 | // |
---|
388 | // true if position is within aerosol, false otherwise |
---|
389 | // deals with aerosol zones |
---|
390 | // |
---|
391 | |
---|
392 | // init |
---|
393 | Bool_t rtn = false; |
---|
394 | Double_t alt_low = 0*km; |
---|
395 | Double_t alt_high = 2*km; |
---|
396 | if(pos.IsUnderSeaLevel()) return false; |
---|
397 | |
---|
398 | // depends on aerosol zone |
---|
399 | if(zone == BOUND1) alt_high = 1*km; |
---|
400 | else if(zone == BOUND2) alt_low = 1*km; |
---|
401 | alt_low -= kAltitudeTolerance; |
---|
402 | alt_high += kAltitudeTolerance; |
---|
403 | if( (pos.Zv() < alt_high) && (pos.Zv() > alt_low ) ) rtn = true; |
---|
404 | |
---|
405 | return rtn; |
---|
406 | } |
---|
407 | |
---|
408 | //______________________________________________________________________________ |
---|
409 | Bool_t LowtranAerosol::CheckWlRange(Double_t wl) const { |
---|
410 | // |
---|
411 | // true if wl within [200,1536]nm |
---|
412 | // |
---|
413 | |
---|
414 | // init |
---|
415 | Bool_t rtn = kTRUE; |
---|
416 | if(wl < 200*nm) { |
---|
417 | wl = 200*nm; |
---|
418 | rtn = kFALSE; |
---|
419 | } |
---|
420 | else if(wl > 1536*nm) { |
---|
421 | wl = 1536*nm; |
---|
422 | rtn = kFALSE; |
---|
423 | } |
---|
424 | if(!rtn) { |
---|
425 | Msg(EsafMsg::Warning) << "<CheckWlRange> Required wavelength is out of range [200,1536nm] : wl = "<<wl << MsgDispatch; |
---|
426 | Msg(EsafMsg::Warning) << "---> thus set to = "<<wl << MsgDispatch; |
---|
427 | } |
---|
428 | |
---|
429 | return rtn; |
---|
430 | } |
---|
431 | |
---|
432 | //______________________________________________________________________________ |
---|
433 | Double_t LowtranAerosol::Trans(const EarthVector& startpos,const EarthVector& finalposi,Double_t wl, Double_t& scat) const { |
---|
434 | // |
---|
435 | // Returns the total transmission along the track defined by startpos and finalpos |
---|
436 | // Aerosol zones handled |
---|
437 | // Altitude profile is exponential, excepted for low visibility where profile is uniform between [0-1km] |
---|
438 | // Local earth-sphericity not taken into account for transmission calculation, to fasten simulation (but used for geometry (impact..)) |
---|
439 | // Negligeable anyway as boundary layers are pretty thin (< 2km) |
---|
440 | // |
---|
441 | |
---|
442 | // init |
---|
443 | EarthVector exit(0.,0.,0.), exit1(0.,0.,0.), enter(0.,0.,0.), enter1(0.,0.,0.), direc(0.,0.,0.); |
---|
444 | EarthVector finalpos(finalposi); |
---|
445 | Double_t exponext(0.), exponscat(0.), mag(0.), magmiddle(0.); |
---|
446 | Double_t alt1(0.), alt2(0.), altmiddle(0.), h0(0.); |
---|
447 | direc = (finalpos - startpos).Unit(); |
---|
448 | if(direc.Mag() == 0) return 1.; |
---|
449 | Int_t split = -1; |
---|
450 | |
---|
451 | // 1. check if startpos is a valid position |
---|
452 | if(startpos.IsUnderSeaLevel()) { |
---|
453 | Msg(EsafMsg::Warning) << "<Trans> Starting position is underground, zero returned" << MsgDispatch; |
---|
454 | return 0.; |
---|
455 | } |
---|
456 | |
---|
457 | // 2. get entering position |
---|
458 | enter = GetImpact(startpos,direc); |
---|
459 | // if no aerosol along the track |
---|
460 | if(enter.Z() == HUGE) { |
---|
461 | scat = 1.; |
---|
462 | return 1.; |
---|
463 | } |
---|
464 | alt1 = enter.Zv(); |
---|
465 | // 2.1. get outgoing position for AerZone=ALL |
---|
466 | exit = GetZoneImpact(startpos,direc,ALL,"outgoing"); |
---|
467 | if(exit.Z() == HUGE) Msg(EsafMsg::Warning) << "if enter exists, exit should as well" << MsgDispatch; |
---|
468 | alt2 = exit.Zv(); |
---|
469 | |
---|
470 | |
---|
471 | // 3. check if finalpos is a valid position |
---|
472 | if(finalpos.IsUnderSeaLevel()) { |
---|
473 | Msg(EsafMsg::Warning) << "<Trans> Final position is underground, set to exit position" << MsgDispatch; |
---|
474 | finalpos = exit; |
---|
475 | } |
---|
476 | |
---|
477 | // 4. look if needs to split aerosol layer in two parts (profile is not exponential in [0-1km] for low visibility) |
---|
478 | if(fType == "urban5" || fType == "rural5") { |
---|
479 | if(startpos.Zv() >= 1*km && finalpos.Zv() <= 1*km) split = 1; // finalpos is inside BOUND1 |
---|
480 | else if(finalpos.Zv() >= 1*km && startpos.Zv() <= 1*km) split = 2; // startpos is inside BOUND1 |
---|
481 | else if(finalpos.Zv() <= 1*km && startpos.Zv() <= 1*km) split = 3; // both pos are inside BOUND1 |
---|
482 | else split = 0; // both positions are outside BOUND1 |
---|
483 | } |
---|
484 | |
---|
485 | |
---|
486 | // 5. get wl dependent factor |
---|
487 | Double_t kwlext = Kwl(wl); |
---|
488 | Double_t kwlabs = Kwl(wl,kTRUE); |
---|
489 | |
---|
490 | |
---|
491 | // 6. define path length and related positions |
---|
492 | mag = (exit - enter).Mag(); |
---|
493 | if(mag > (finalpos - enter).Mag()) { |
---|
494 | exit = finalpos; |
---|
495 | mag = (exit - enter).Mag(); |
---|
496 | alt2 = exit.Zv(); |
---|
497 | } |
---|
498 | |
---|
499 | // 7. if needed, get intermediate enter/exit position on top of AerZone=BOUND1 |
---|
500 | if(split > 0) { |
---|
501 | if(split == 1) { |
---|
502 | enter1 = GetZoneImpact(startpos,direc,BOUND1); |
---|
503 | if(enter1.Z() == HUGE) Msg(EsafMsg::Warning) << "<Trans> In this case (split==1), enter position should exist" << MsgDispatch; |
---|
504 | altmiddle = enter1.Zv(); |
---|
505 | magmiddle = (enter1 - enter).Mag(); |
---|
506 | } |
---|
507 | else if(split == 2) { |
---|
508 | exit1 = GetZoneImpact(startpos,direc,BOUND1,"outgoing"); |
---|
509 | if(exit1.Z() == HUGE) Msg(EsafMsg::Warning) << "<Trans> In this case (split==2), exit position should exist" << MsgDispatch; |
---|
510 | altmiddle = exit1.Zv(); |
---|
511 | magmiddle = (exit1 - enter).Mag(); |
---|
512 | } |
---|
513 | } |
---|
514 | |
---|
515 | |
---|
516 | // 8. find other parameters for integration : scale height, local zenith angle |
---|
517 | h0 = 2.14378*km; // for high visibility (>= 23km) |
---|
518 | if(split >= 0) h0 = 0.3972*km; // for visibility = 5km |
---|
519 | Double_t angle; |
---|
520 | EarthVector temp(startpos); |
---|
521 | temp(2) += EarthRadius(); // pos expressed in earth-centered frame -> gives local vertical direction (expressed in MES frame) |
---|
522 | angle = direc.Angle(temp); // angle between dir and local vertical |
---|
523 | |
---|
524 | |
---|
525 | // 9. integrate BETAext*dl along the track |
---|
526 | // 9.1. if horizontal track |
---|
527 | if(fabs(angle - PiOver2()) < kTolerance) { |
---|
528 | if(split == 3) { |
---|
529 | exponscat = fSz[0] * (kwlext - kwlabs) * mag; |
---|
530 | exponext = fSz[0] * kwlext * mag; |
---|
531 | } |
---|
532 | else { // should occur rarely |
---|
533 | exponscat = fSz[0] * (kwlext - kwlabs) * mag * Exp(- 0.5*(alt1 + alt2)/h0); |
---|
534 | exponext = fSz[0] * kwlext * mag * Exp(- 0.5*(alt1 + alt2)/h0); |
---|
535 | } |
---|
536 | } |
---|
537 | // 9.2. else integrate exponential |
---|
538 | else { |
---|
539 | if(angle > PiOver2()) { |
---|
540 | EarthVector dirinvert = -direc; |
---|
541 | angle = dirinvert.Angle(temp); |
---|
542 | } |
---|
543 | if(split == 0) { |
---|
544 | exponscat = fSz[1] * (kwlext - kwlabs) * h0 / Cos(angle) * Exp(1*km/h0) * fabs( Exp(-alt1/h0) - Exp(-alt2/h0) ); |
---|
545 | exponext = fSz[1] * kwlext * h0 / Cos(angle) * Exp(1*km/h0) * fabs( Exp(-alt1/h0) - Exp(-alt2/h0) ); |
---|
546 | } |
---|
547 | else if(split == 1) { |
---|
548 | exponscat = fSz[1] * (kwlext - kwlabs) * h0 / Cos(angle) * Exp(1*km/h0) * fabs( Exp(-alt1/h0) - Exp(-altmiddle/h0) ); |
---|
549 | exponext = fSz[1] * kwlext * h0 / Cos(angle) * Exp(1*km/h0) * fabs( Exp(-alt1/h0) - Exp(-altmiddle/h0) ); |
---|
550 | exponscat += fSz[0] * (kwlext - kwlabs) * (mag - magmiddle); |
---|
551 | exponext += fSz[0] * kwlext * (mag - magmiddle); |
---|
552 | } |
---|
553 | else if(split == 2) { |
---|
554 | exponscat = fSz[0] * (kwlext - kwlabs) * magmiddle; |
---|
555 | exponext = fSz[0] * kwlext * magmiddle; |
---|
556 | exponscat += fSz[1] * (kwlext - kwlabs) * h0 / Cos(angle) * Exp(1*km/h0) * fabs( Exp(-altmiddle/h0) - Exp(-alt2/h0) ); |
---|
557 | exponext += fSz[1] * kwlext * h0 / Cos(angle) * Exp(1*km/h0) * fabs( Exp(-altmiddle/h0) - Exp(-alt2/h0) ); |
---|
558 | } |
---|
559 | // for low visibility and both positions inside BOUND1 : aerosol density is constant |
---|
560 | else if(split == 3) { |
---|
561 | exponscat = fSz[0] * (kwlext - kwlabs) * mag; |
---|
562 | exponext = fSz[0] * kwlext * mag; |
---|
563 | } |
---|
564 | else { |
---|
565 | exponscat = fSz[0] * (kwlext - kwlabs) * h0 / Cos(angle) * fabs( Exp(-alt1/h0) - Exp(-alt2/h0) ); |
---|
566 | exponext = fSz[0] * kwlext * h0 / Cos(angle) * fabs( Exp(-alt1/h0) - Exp(-alt2/h0) ); |
---|
567 | } |
---|
568 | } |
---|
569 | |
---|
570 | scat = Exp(-exponscat); |
---|
571 | return Exp(-exponext); |
---|
572 | } |
---|
573 | |
---|
574 | //______________________________________________________________________________ |
---|
575 | Bool_t LowtranAerosol::RandomScatPos(const EarthVector& posi,const EarthVector& dir,Double_t wl,EarthVector& scatpos) const { |
---|
576 | // |
---|
577 | // return kFALSE if absorpion, kTRUE if scattering |
---|
578 | // get a scattering position along a given track |
---|
579 | // handle aerosols zones |
---|
580 | // Altitude profile is exponential, excepted for low visibility where profile is uniform between [0-1km] |
---|
581 | // Local earth-sphericity not taken into account for transmission calculation, to fasten simulation (but used for geometry (impact..)) |
---|
582 | // Negligeable anyway as boundary layers are pretty thin (< 2km) |
---|
583 | // |
---|
584 | // - if scatpos is outside aerosol layer : (0,0,HUGE) returned |
---|
585 | // - if scatpos is underground : (0,0,HUGE) returned |
---|
586 | // |
---|
587 | |
---|
588 | |
---|
589 | // init |
---|
590 | Bool_t rtn(kFALSE); |
---|
591 | TRandom* rndm = EsafRandom::Get(); |
---|
592 | EarthVector exit1(0.,0.,0.), enter(0.,0.,0.), enter1(0.,0.,0.), outgopos(0.,0.,0.); |
---|
593 | EarthVector pos(posi); |
---|
594 | Double_t mag(0.), magmiddle(0.); |
---|
595 | Double_t alt1(0.), alt2(0.), altmiddle(0.), h0(0.); |
---|
596 | EarthVector direc = dir.Unit(); |
---|
597 | Int_t split = -1; // to deal with zones |
---|
598 | |
---|
599 | // check if pos is a valid position |
---|
600 | if(pos.IsUnderSeaLevel()) { |
---|
601 | Msg(EsafMsg::Warning) << "<RandomScatPos> Starting position is underground, HUGE vector returned" << MsgDispatch; |
---|
602 | scatpos.SetXYZ(0,0,HUGE); |
---|
603 | return kTRUE; |
---|
604 | } |
---|
605 | |
---|
606 | // check if pos is in aerosol layer (it must be) |
---|
607 | // if not, pos is set to enter position |
---|
608 | if(!IsInAerosol(pos)) { |
---|
609 | Msg(EsafMsg::Warning) << "<RandomScatPos()> Starting position SHOULD be in aerosol layers, pos is set to entering position in layer" << MsgDispatch; |
---|
610 | // get entering position |
---|
611 | enter = GetImpact(pos,direc); |
---|
612 | // if no aerosol along the track |
---|
613 | if(enter.Z() == HUGE) { |
---|
614 | Msg(EsafMsg::Warning) << "<RandomScatPos()> STRANGE CASE : randomscatpos asked, but track does not seem to intersect aerosol layer" << MsgDispatch; |
---|
615 | scatpos.SetXYZ(0,0,HUGE); |
---|
616 | return kTRUE; |
---|
617 | } |
---|
618 | pos = enter; |
---|
619 | } |
---|
620 | |
---|
621 | // 0. look if needs to split aerosol layer in two parts (profile is not exponential in [0-1km] for low visibility) |
---|
622 | outgopos = GetImpact(pos,dir,"outgoing"); |
---|
623 | if(fType == "urban5" || fType == "rural5") { |
---|
624 | if(pos.Zv() >= 1*km && outgopos.Zv() <= 1*km) split = 1; // outgopos is inside BOUND1 |
---|
625 | else if(outgopos.Zv() >= 1*km && pos.Zv() <= 1*km) split = 2; // startpos is inside BOUND1 |
---|
626 | else if(outgopos.Zv() <= 1*km && pos.Zv() <= 1*km) split = 3; // both pos are inside BOUND1 |
---|
627 | else split = 0; // both positions are outside BOUND1 |
---|
628 | } |
---|
629 | |
---|
630 | |
---|
631 | // 1. this change of variable comes from the old version of the code, which was bugged |
---|
632 | Double_t K = Kwl(wl); // = extinction coeff. !! |
---|
633 | // 1(bis). tell the kind of interaction : scatt. or abs. |
---|
634 | Double_t kwlabs = Kwl(wl,kTRUE); |
---|
635 | Double_t abs = kwlabs / K; |
---|
636 | Double_t scat = 1 - abs; |
---|
637 | if(scat > rndm->Rndm()) rtn = kTRUE; |
---|
638 | |
---|
639 | |
---|
640 | // 2. if needed, get intermediate enter/exit position on top of AerZone=BOUND1 |
---|
641 | if(split > 0) { |
---|
642 | if(split == 1) { |
---|
643 | enter1 = GetZoneImpact(pos,direc,BOUND1); |
---|
644 | if(enter1.Z() == HUGE) Msg(EsafMsg::Warning) << "<RandomScatPos> In this case (split==1), enter position should exist" << MsgDispatch; |
---|
645 | altmiddle = enter1.Zv(); |
---|
646 | magmiddle = (enter1 - pos).Mag(); |
---|
647 | } |
---|
648 | else if(split == 2) { |
---|
649 | exit1 = GetZoneImpact(pos,direc,BOUND1,"outgoing"); |
---|
650 | if(exit1.Z() == HUGE) { |
---|
651 | Msg(EsafMsg::Warning) << "<RandomScatPos> In this case (split==2), exit position should exist" << MsgDispatch; |
---|
652 | Msg(EsafMsg::Warning) << "pos = "<<pos << MsgDispatch; |
---|
653 | Msg(EsafMsg::Warning) << "direc = "<<direc << MsgDispatch; |
---|
654 | // find local zenith angle |
---|
655 | Double_t anglee; |
---|
656 | EarthVector tempp(pos); |
---|
657 | tempp(2) += EarthRadius(); // pos expressed in earth-centered frame -> gives local vertical direction (expressed in MES frame) |
---|
658 | anglee = direc.Angle(tempp); // angle between dir and local vertical |
---|
659 | Msg(EsafMsg::Warning) << "local zenith angle/halfpi = "<<anglee/PiOver2()<<" rad, angle = "<<anglee*RadToDeg()<<" deg" << MsgDispatch; |
---|
660 | Msg(EsafMsg::Warning) << "<RandomScatPos> Fake aerosol scat position returned" << MsgDispatch; |
---|
661 | scatpos.SetXYZ(0,0,HUGE); |
---|
662 | return rtn; |
---|
663 | } |
---|
664 | altmiddle = exit1.Zv(); |
---|
665 | magmiddle = (exit1 - pos).Mag(); |
---|
666 | } |
---|
667 | } |
---|
668 | |
---|
669 | |
---|
670 | // 3. find other parameters for integration |
---|
671 | // scale height, local zenith angle, pos altitude |
---|
672 | h0 = 2.14378*km; // for high visibility (>= 23km) |
---|
673 | if(split >= 0) h0 = 0.3972*km; // for visibility = 5km |
---|
674 | Double_t angle; |
---|
675 | EarthVector temp(pos); |
---|
676 | temp(2) += EarthRadius(); // pos expressed in earth-centered frame -> gives local vertical direction (expressed in MES frame) |
---|
677 | angle = direc.Angle(temp); // angle between dir and local vertical |
---|
678 | alt1 = pos.Zv(); |
---|
679 | |
---|
680 | |
---|
681 | // 4. generate random number to sample distribution function : Integral(Beta(s).ds) = -ln(1 - RNDM) |
---|
682 | Double_t RNDM = -Log(1 - rndm->Rndm()); |
---|
683 | |
---|
684 | |
---|
685 | // 5. find path length, thus determining position of interaction |
---|
686 | Bool_t upward = kTRUE; |
---|
687 | // 5.1. if horizontal track |
---|
688 | if(fabs(angle - PiOver2()) < kTolerance) { |
---|
689 | if(split == 3) mag = RNDM / (fSz[0] * K); |
---|
690 | else mag = RNDM / (fSz[0] * K * Exp(-alt1/h0)); // should occur rarely |
---|
691 | } |
---|
692 | // 5.2. else deals with zones |
---|
693 | else { |
---|
694 | if(angle > PiOver2()) { |
---|
695 | EarthVector dirinvert = -direc; |
---|
696 | angle = dirinvert.Angle(temp); |
---|
697 | upward = kFALSE; |
---|
698 | } |
---|
699 | if(split == 0) { |
---|
700 | if(upward) { |
---|
701 | if(RNDM > (fSz[1] * K * h0 / Cos(angle) * Exp(1*km/h0) * Exp(-alt1/h0))) { |
---|
702 | // means no valid position found |
---|
703 | scatpos.SetXYZ(0,0,HUGE); |
---|
704 | return rtn; |
---|
705 | } |
---|
706 | mag = -h0 / Cos(angle) * Log(1 - RNDM / (fSz[1] * K * h0 / Cos(angle) * Exp(1*km/h0) * Exp(-alt1/h0))); |
---|
707 | } |
---|
708 | else mag = h0 / Cos(angle) * Log(1 + RNDM / (fSz[1] * K * h0 / Cos(angle) * Exp(1*km/h0) * Exp(-alt1/h0))); |
---|
709 | } |
---|
710 | else if(split == 1) { // upward is false |
---|
711 | // get a position assuming everything occurs in BOUND2 |
---|
712 | mag = h0 / Cos(angle) * Log(1 + RNDM / (fSz[1] * K * h0 / Cos(angle) * Exp(1*km/h0) * Exp(-alt1/h0))); |
---|
713 | // if found position altitude is below altmiddle, needs to consider BOUND1 too |
---|
714 | alt2 = alt1 - mag * Cos(angle); |
---|
715 | if(alt2 < altmiddle) { |
---|
716 | // substract first part of integration until altmiddle (in BOUND2) |
---|
717 | RNDM -= fSz[1] * K * h0 / Cos(angle) * Exp(1*km/h0) * (Exp(-altmiddle/h0) - Exp(-alt1/h0)); |
---|
718 | mag = magmiddle; |
---|
719 | mag += RNDM / (fSz[0] * K); |
---|
720 | } |
---|
721 | } |
---|
722 | else if(split == 2) { // upward is true |
---|
723 | // get a position assuming everything occurs in BOUND1 |
---|
724 | mag = RNDM / (fSz[0] * K); |
---|
725 | // if found position altitude is above altmiddle, needs to consider BOUND2 too |
---|
726 | alt2 = mag * Cos(angle) + alt1; |
---|
727 | if(alt2 > altmiddle) { |
---|
728 | // substract first part of integration (in BOUND1) |
---|
729 | RNDM -= fSz[0] * K * magmiddle; |
---|
730 | mag = magmiddle; |
---|
731 | mag += -h0 / Cos(angle) * Log(1 - RNDM / (fSz[1] * K * h0 / Cos(angle) * Exp(1*km/h0) * Exp(-altmiddle/h0))); |
---|
732 | } |
---|
733 | } |
---|
734 | // for low visibility and both positions inside BOUND1 : aerosol density is constant |
---|
735 | else if(split == 3) mag = RNDM / (fSz[0] * K); |
---|
736 | else { |
---|
737 | if(upward) { |
---|
738 | if(RNDM > (fSz[0] * K * h0 / Cos(angle) * Exp(-alt1/h0))) { |
---|
739 | // means no valid position found |
---|
740 | scatpos.SetXYZ(0,0,HUGE); |
---|
741 | return rtn; |
---|
742 | } |
---|
743 | mag = -h0 / Cos(angle) * Log(1 - RNDM / (fSz[0] * K * h0 / Cos(angle) * Exp(-alt1/h0))); |
---|
744 | } |
---|
745 | else mag = h0 / Cos(angle) * Log(1 + RNDM / (fSz[0] * K * h0 / Cos(angle) * Exp(-alt1/h0))); |
---|
746 | } |
---|
747 | } |
---|
748 | |
---|
749 | // 6. set position of interaction and check if it a valid position |
---|
750 | scatpos = pos + mag*direc; |
---|
751 | if(!IsInAerosol(scatpos)) scatpos.SetXYZ(0,0,HUGE); // handle "under sea level" as well |
---|
752 | |
---|
753 | return rtn; |
---|
754 | } |
---|
755 | |
---|
756 | //______________________________________________________________________________ |
---|
757 | Double_t LowtranAerosol::PhaseFunction(Double_t theta,Double_t wl) const { |
---|
758 | // |
---|
759 | // Mie scattering phase function |
---|
760 | // Normalized when integrated over full solid angle |
---|
761 | // |
---|
762 | |
---|
763 | // check if wl is valid |
---|
764 | CheckWlRange(wl); |
---|
765 | |
---|
766 | // find bounds for interpolation |
---|
767 | Double_t G1(0.), G2(0.), F(0.); |
---|
768 | Int_t i=0; |
---|
769 | for(i=0; i < 6; i++) { |
---|
770 | if(wl >= fWlrange[i] && wl <= fWlrange[i+1]) break; |
---|
771 | } |
---|
772 | G1 = (fG1[i+1] - fG1[i]) / (fWlrange[i+1] - fWlrange[i]) * (wl - fWlrange[i]) + fG1[i]; |
---|
773 | G2 = (fG2[i+1] - fG2[i]) / (fWlrange[i+1] - fWlrange[i]) * (wl - fWlrange[i]) + fG2[i]; |
---|
774 | F = (fF[i+1] - fF[i]) / (fWlrange[i+1] - fWlrange[i]) * (wl - fWlrange[i]) + fF[i]; |
---|
775 | |
---|
776 | // calculate value |
---|
777 | Double_t rtn(0.); |
---|
778 | Double_t HG1 = (1 - G1*G1) / pow(1 - 2*G1*Cos(theta) + G1*G1,1.5); |
---|
779 | Double_t HG2 = (1 - G2*G2) / pow(1 - 2*G2*Cos(theta) + G2*G2,1.5); |
---|
780 | rtn = 1./(4*Pi()) * (F*HG1 + (1 - F)*HG2); |
---|
781 | |
---|
782 | return rtn; |
---|
783 | } |
---|
784 | |
---|
785 | //______________________________________________________________________________ |
---|
786 | Double_t LowtranAerosol::PhaseFunction(const EarthVector& incom_dir,const EarthVector& outgo_dir,Double_t wl) const { |
---|
787 | // |
---|
788 | // Mie scattering phase function |
---|
789 | // Normalized when integrated over full solid angle |
---|
790 | // |
---|
791 | Double_t theta = outgo_dir.Angle(incom_dir); |
---|
792 | |
---|
793 | return PhaseFunction(theta,wl); |
---|
794 | } |
---|
795 | |
---|
796 | //______________________________________________________________________________ |
---|
797 | Double_t LowtranAerosol::GetMaxPhaseFunction(Double_t wlmin,Double_t wlmax) const { |
---|
798 | // |
---|
799 | // returns maximum value of currently used scattering phase function |
---|
800 | // (phase function is normalized when integrated over full solid angle) |
---|
801 | // |
---|
802 | |
---|
803 | Double_t rtn(0.), temp(0.); |
---|
804 | for(Int_t i=0; i < 7; i++) { |
---|
805 | temp = PhaseFunction(0.,fWlrange[i]); |
---|
806 | if(temp > rtn && fWlrange[i] > wlmin && fWlrange[i] < wlmax) rtn = temp; |
---|
807 | } |
---|
808 | temp = PhaseFunction(0.,wlmin); |
---|
809 | if(temp > rtn) rtn = temp; |
---|
810 | temp = PhaseFunction(0.,wlmax); |
---|
811 | if(temp > rtn) rtn = temp; |
---|
812 | |
---|
813 | return rtn; |
---|
814 | } |
---|
815 | |
---|
816 | //______________________________________________________________________________ |
---|
817 | void LowtranAerosol::RandomDir(EarthVector& dir,Double_t wl) const { |
---|
818 | // |
---|
819 | // 'dir' is the incoming direction -> this method set dir to the scattering outgoing direction |
---|
820 | // get a random direction, sampling clouds scattering phase function |
---|
821 | // |
---|
822 | |
---|
823 | Double_t theta(0); |
---|
824 | Double_t u1(0.), u2(0.); |
---|
825 | TRandom* rndm = EsafRandom::Get(); |
---|
826 | |
---|
827 | // check if wl is valid |
---|
828 | CheckWlRange(wl); |
---|
829 | |
---|
830 | // find bounds for interpolation |
---|
831 | Double_t G1(0.), G2(0.), F(0.); |
---|
832 | Int_t i=0; |
---|
833 | for(i=0; i < 6; i++) { |
---|
834 | if(wl >= fWlrange[i] && wl <= fWlrange[i+1]) break; |
---|
835 | } |
---|
836 | G1 = (fG1[i+1] - fG1[i]) / (fWlrange[i+1] - fWlrange[i]) * (wl - fWlrange[i]) + fG1[i]; |
---|
837 | G2 = (fG2[i+1] - fG2[i]) / (fWlrange[i+1] - fWlrange[i]) * (wl - fWlrange[i]) + fG2[i]; |
---|
838 | F = (fF[i+1] - fF[i]) / (fWlrange[i+1] - fWlrange[i]) * (wl - fWlrange[i]) + fF[i]; |
---|
839 | |
---|
840 | |
---|
841 | // get random theta, assuming isotropic distribution in phi |
---|
842 | // theta is the angle between incoming and outgoing directions |
---|
843 | // von neumann sampling -> much faster than using TF1 |
---|
844 | Double_t HG1 = 0.; |
---|
845 | Double_t HG2 = 0.; |
---|
846 | Double_t pfvalue = 0.; |
---|
847 | do { |
---|
848 | u1 = -1 + rndm->Rndm()*2; |
---|
849 | HG1 = (1 + G1) / (1 - G1) / (1 - G1); |
---|
850 | HG2 = (1 + G2) / (1 - G2) / (1 - G2); |
---|
851 | u2 = rndm->Rndm() * 0.5 * (F*HG1 + (1 - F)*HG2); |
---|
852 | HG1 = (1 - G1*G1) / pow(1 - 2*G1*u1 + G1*G1,1.5); |
---|
853 | HG2 = (1 - G2*G2) / pow(1 - 2*G2*u1 + G2*G2,1.5); |
---|
854 | pfvalue = 0.5 * (F*HG1 + (1 - F)*HG2); |
---|
855 | } while(u2 >= pfvalue); |
---|
856 | |
---|
857 | theta = acos(u1); |
---|
858 | |
---|
859 | // then get a random direction |
---|
860 | EarthVector v = dir.Orthogonal(); |
---|
861 | v.Rotate(rndm->Rndm()*2*Pi(),dir); |
---|
862 | EarthVector axis = v.Cross(dir); |
---|
863 | dir.Rotate(theta,axis); |
---|
864 | } |
---|
865 | |
---|
866 | //_____________________________________________________________________________________________________ |
---|
867 | EarthVector LowtranAerosol::GetZoneImpact(const EarthVector& pos, const EarthVector& dir, AerZone zone, string opt) const { |
---|
868 | // |
---|
869 | // Impact on clouds top surface form a given (position,direction) pair in atmosphere |
---|
870 | // if no impact, (0,0,HUGE) is returned |
---|
871 | // upward direction is handled |
---|
872 | // |
---|
873 | // Aerosol considered geometry is defined using "zone" argument |
---|
874 | // |
---|
875 | // if opt == "default" -> incoming impact returned |
---|
876 | // if pos is within clouds, pos is returned |
---|
877 | // if opt == "outgoing" -> outgoing impact returned |
---|
878 | // |
---|
879 | |
---|
880 | // init |
---|
881 | EarthVector rtn(0,0,HUGE); |
---|
882 | Bool_t isinaerosol, downward; |
---|
883 | Double_t Altimpact(0); |
---|
884 | EarthVector direc = dir.Unit(); |
---|
885 | Double_t topaltitude = 2*km; |
---|
886 | Double_t bottomaltitude = 0.; |
---|
887 | if(zone == BOUND1) topaltitude = 1*km; |
---|
888 | if(zone == BOUND2) bottomaltitude = 1*km; |
---|
889 | |
---|
890 | // says if dir is going up/downward |
---|
891 | downward = GoingDownward(pos,direc); |
---|
892 | |
---|
893 | // says if pos is within boundary layers (altitude round-off effects < 1mm are handled) |
---|
894 | isinaerosol = IsInAerosolZone(pos,zone); |
---|
895 | |
---|
896 | // set the impact surface altitude according to photon present situation (up/downward, above/below, enter/exit aerosol) |
---|
897 | if(isinaerosol) { |
---|
898 | if(opt == "default") return pos; |
---|
899 | else if(opt == "outgoing") { |
---|
900 | if(!downward) Altimpact = topaltitude; |
---|
901 | else Altimpact = bottomaltitude; |
---|
902 | } |
---|
903 | else Msg(EsafMsg::Panic) << "<GetZoneImpact()> wrong argument (internal pos)" << MsgDispatch; |
---|
904 | } |
---|
905 | else { |
---|
906 | // if not going toward aerosol layer |
---|
907 | if(pos.Zv() < bottomaltitude && downward) return rtn; |
---|
908 | if(pos.Zv() > topaltitude && !downward) return rtn; |
---|
909 | |
---|
910 | if((opt == "outgoing" && !downward) || (opt == "default" && downward)) |
---|
911 | Altimpact = topaltitude; |
---|
912 | else if((opt == "outgoing" && downward) || (opt == "default" && !downward)) |
---|
913 | Altimpact = bottomaltitude; |
---|
914 | else |
---|
915 | Msg(EsafMsg::Panic) << "<GetZoneImpact()> wrong argument (external pos)" << MsgDispatch; |
---|
916 | } |
---|
917 | |
---|
918 | // now calculate impact |
---|
919 | Double_t mag(0); |
---|
920 | |
---|
921 | /* flat earth |
---|
922 | if(fabs(direc.Z()) < kTolerance) rtn.SetXYZ(0,0,HUGE); |
---|
923 | else { |
---|
924 | mag = (Altimpact - pos.Z()) / direc.Z(); |
---|
925 | if(mag < -kAltitudeTolerance) rtn.SetXYZ(0,0,HUGE); |
---|
926 | else rtn = pos + mag*direc; |
---|
927 | } |
---|
928 | if(mag < -kAltitudeTolerance) Msg(EsafMsg::Warning) << "PB in impact calculation GetCloudImpact(), mag = ["<<mag<< MsgDispatch; |
---|
929 | */ |
---|
930 | |
---|
931 | Double_t b = pos*direc + direc.Z()*EarthRadius(); |
---|
932 | Double_t c = pos.Mag2() + 2*EarthRadius()*(pos.Z() - Altimpact) - pow(Altimpact,2); |
---|
933 | pair<Int_t,Double_t*>& p = findRoots(1.,2*b,c); |
---|
934 | if(p.first == 0) { |
---|
935 | // if direction is nearly horizontal, try the other layer bound |
---|
936 | Double_t anglee; |
---|
937 | EarthVector tempp(pos); |
---|
938 | tempp(2) += EarthRadius(); |
---|
939 | anglee = dir.Angle(tempp); |
---|
940 | if(fabs(anglee - PiOver2()) < 0.1) { |
---|
941 | if(Altimpact == topaltitude) Altimpact = bottomaltitude; |
---|
942 | else Altimpact = topaltitude; |
---|
943 | mag = 0; |
---|
944 | b = pos*direc + direc.Z()*EarthRadius(); |
---|
945 | c = pos.Mag2() + 2*EarthRadius()*(pos.Z() - Altimpact) - pow(Altimpact,2); |
---|
946 | p = findRoots(1.,2*b,c); |
---|
947 | } |
---|
948 | else return rtn; |
---|
949 | } |
---|
950 | if(p.first == 0) return rtn; |
---|
951 | else if(p.first == 1) mag = p.second[0]; |
---|
952 | else if(p.first ==2) { |
---|
953 | if((p.second[0]+kAltitudeTolerance) * (p.second[1]+kAltitudeTolerance) < 0) mag = max(p.second[0],p.second[1]); |
---|
954 | else mag = min(p.second[0],p.second[1]); |
---|
955 | } |
---|
956 | if(mag < -kAltitudeTolerance) Msg(EsafMsg::Warning) << "PB in impact calculation GetZoneImpact(), [min,max] = ["<<mag<<", "<< max(p.second[0],p.second[1])<<"]" << MsgDispatch; |
---|
957 | rtn = pos + mag*direc; |
---|
958 | |
---|
959 | |
---|
960 | return rtn; |
---|
961 | } |
---|
962 | |
---|
963 | //_____________________________________________________________________________________________________ |
---|
964 | EarthVector LowtranAerosol::GetImpact(const EarthVector& pos, const EarthVector& dir, string opt) const { |
---|
965 | // |
---|
966 | // Impact on aerosol top surface form a given (position,direction) pair in atmosphere |
---|
967 | // if no impact, (0,0,HUGE) is returned |
---|
968 | // upward direction is handled |
---|
969 | // |
---|
970 | // if opt == "default" -> incoming impact returned |
---|
971 | // if pos is within clouds, pos is returned |
---|
972 | // if opt == "outgoing" -> outgoing impact returned |
---|
973 | // |
---|
974 | |
---|
975 | return GetZoneImpact(pos,dir,ALL,opt); |
---|
976 | } |
---|
977 | |
---|
978 | //_____________________________________________________________________________ |
---|
979 | Bool_t LowtranAerosol::GoingDownward(const EarthVector& pos,const EarthVector& direc) const { |
---|
980 | // |
---|
981 | // to know if locally track is going up/downward |
---|
982 | // locally means at pos nadir |
---|
983 | // if perfectly horizontal (i.e. local angle of halfpi) returns false (i.e. upward) |
---|
984 | // |
---|
985 | |
---|
986 | Bool_t rtn; |
---|
987 | |
---|
988 | /* flat earth |
---|
989 | if(direc.Z() > 0) return false; |
---|
990 | else return true; |
---|
991 | */ |
---|
992 | |
---|
993 | EarthVector dir = direc.Unit(); |
---|
994 | // find local zenith angle |
---|
995 | Double_t angle; |
---|
996 | EarthVector temp(pos); |
---|
997 | temp(2) += EarthRadius(); // pos expressed in earth-centered frame -> gives local vertical direction (expressed in MES frame) |
---|
998 | angle = dir.Angle(temp); // angle between dir and local vertical |
---|
999 | // returns accordingly |
---|
1000 | if(angle <= PiOver2()) rtn = false; |
---|
1001 | else rtn = true; |
---|
1002 | |
---|
1003 | return rtn; |
---|
1004 | } |
---|
1005 | |
---|
1006 | //_____________________________________________________________________________ |
---|
1007 | void LowtranAerosol::Reset() { |
---|
1008 | // |
---|
1009 | // reset clodus features |
---|
1010 | // |
---|
1011 | |
---|
1012 | // reset current model tables |
---|
1013 | for(Int_t i=0; i < 7; i++) { |
---|
1014 | if(i < 3) fSz[i] = 0; |
---|
1015 | fKwl_ext[i] = 0; |
---|
1016 | fKwl_abs[i] = 0; |
---|
1017 | fG1[i] = 0; |
---|
1018 | fG2[i] = 0; |
---|
1019 | fF[i] = 0; |
---|
1020 | } |
---|
1021 | |
---|
1022 | // build new model tables |
---|
1023 | Build(); |
---|
1024 | } |
---|
1025 | |
---|