1 | /* ================================================================== */
|
---|
2 | /**
|
---|
3 | * @file atmo.c
|
---|
4 | * @short Use of tabulated atmospheric profiles and atmospheric refraction.
|
---|
5 | *
|
---|
6 | * @author Konrad Bernloehr
|
---|
7 | * $Date: 2002-07-25 17:46:40 $
|
---|
8 | * $Revision: 1.1 $
|
---|
9 | *
|
---|
10 | * Copyright (C) 1990, 1997, 1998 Konrad Bernloehr. All rights reserved.
|
---|
11 | * Distribution and use of this software with the CORSIKA program is
|
---|
12 | * allowed and free. No redistribution separate of CORSIKA or of
|
---|
13 | * modified versions granted without permission. Modifications may,
|
---|
14 | * however, be distributed as patches to the original version.
|
---|
15 | * This software comes with no warranties.
|
---|
16 | *
|
---|
17 | * --------------------------------------------------------------------
|
---|
18 | *
|
---|
19 | * This file provides code for use of external atmospheric models
|
---|
20 | * (in the form of text-format tables) with the CORSIKA program.
|
---|
21 | * Six atmospheric models as implemented in the MODTRAN program
|
---|
22 | * and as tabulated in MODTRAN documentation (F.X. Kneizys et al. 1996,
|
---|
23 | * 'The MODTRAN 2/3 Report and LOWTRAN 7 Model', Phillips Laboratory,
|
---|
24 | * Hanscom AFB, MA 01731-3010, U.S.A.) are provided as separate files
|
---|
25 | * (atmprof1.dat ... atmprof6.dat). User-provided atmospheric
|
---|
26 | * models should be given model numbers above 6.
|
---|
27 | *
|
---|
28 | * Note that for the Cherenkov part and the hadronic (and muon) part
|
---|
29 | * of CORSIKA the table values are directly interpolated but the
|
---|
30 | * electron/positron/gamma part (derived from EGS) uses special
|
---|
31 | * layers (at present 4 with exponential density decrease and the
|
---|
32 | * most upper layer with constant density). Parameters of these
|
---|
33 | * layers are fitted to tabulated values but not every possible
|
---|
34 | * atmospheric model fits very well with an exponential profile.
|
---|
35 | * You are adviced to check that the fit matches tabulated values to
|
---|
36 | * sufficient precision in the altitude ranges of interest to you.
|
---|
37 | * Try to adjust layer boundary altitudes in case of problems.
|
---|
38 | * The propagation of light without refraction (as implemented in
|
---|
39 | * CORSIKA, unless using the CURVED option) and with refraction (as
|
---|
40 | * implemented by this software) assumes a plane-parallel atmosphere.
|
---|
41 | */
|
---|
42 | /* ==================================================================== */
|
---|
43 |
|
---|
44 | #include <stdio.h>
|
---|
45 | #include <stdlib.h>
|
---|
46 | #include <math.h>
|
---|
47 |
|
---|
48 | #define FAST_INTERPOLATION 1
|
---|
49 |
|
---|
50 | /* The CORSIKA version against which this software should match. */
|
---|
51 | /* If your CORSIKA is somewhat newer than 5.901 there is probably */
|
---|
52 | /* no reason to worry; incompatible changes should not happen */
|
---|
53 | /* all too often. */
|
---|
54 | #ifndef CORSIKA_VERSION
|
---|
55 | # define CORSIKA_VERSION 6000
|
---|
56 | #endif
|
---|
57 |
|
---|
58 | #if (CORSIKA_VERSION < 5901)
|
---|
59 | typedef float cors_real_now_t;
|
---|
60 | #else
|
---|
61 | typedef double cors_real_now_t;
|
---|
62 | #endif
|
---|
63 |
|
---|
64 | /* Function prototypes for functions implemented in this file */
|
---|
65 |
|
---|
66 | /* FORTRAN called functions (beware changes of parameter types !!) */
|
---|
67 | void atmset_(int *iatmo, double *obslev);
|
---|
68 | double rhofx_(double *height);
|
---|
69 | double thickx_(double *height);
|
---|
70 | double refidx_(double *height);
|
---|
71 | double heighx_(double *thick);
|
---|
72 | void raybnd_(double *zem, cors_real_now_t *u, cors_real_now_t *v, double *w,
|
---|
73 | cors_real_now_t *dx, cors_real_now_t *dy, cors_real_now_t *dt);
|
---|
74 | void atmfit_(int *nlp, double *hlay, double *aatm, double *batm, double *catm);
|
---|
75 |
|
---|
76 | /* C called functions (parameter types are always checked) */
|
---|
77 | static void interp(double x, double *v, int n, int *ipl, double *rpl);
|
---|
78 | double rpol(double *x, double *y, int n, double xp);
|
---|
79 | static void init_refraction_tables(void);
|
---|
80 | static void init_corsika_atmosphere(void);
|
---|
81 | static void init_atmosphere(void);
|
---|
82 | static double sum_log_dev_sq(double a, double b, double c, int np,
|
---|
83 | double *h, double *t, double *rho);
|
---|
84 | static double atm_exp_fit(double h1, double h2, double *ap, double *bp,
|
---|
85 | double *cp, double *s0, int *npp);
|
---|
86 |
|
---|
87 | /* Variables used for atmospheric profiles */
|
---|
88 |
|
---|
89 | #define MAX_PROFILE 50
|
---|
90 | int atmosphere;
|
---|
91 | static int num_prof;
|
---|
92 | static double p_alt[MAX_PROFILE], p_log_alt[MAX_PROFILE];
|
---|
93 | static double p_log_rho[MAX_PROFILE], p_rho[MAX_PROFILE];
|
---|
94 | static double p_log_thick[MAX_PROFILE];
|
---|
95 | static double p_log_n1[MAX_PROFILE];
|
---|
96 | static double p_bend_ray_hori_a[MAX_PROFILE];
|
---|
97 | static double p_bend_ray_time0[MAX_PROFILE];
|
---|
98 | static double p_bend_ray_time_a[MAX_PROFILE];
|
---|
99 |
|
---|
100 | static double top_of_atmosphere = 112.8e5;
|
---|
101 | static double bottom_of_atmosphere = 0.;
|
---|
102 |
|
---|
103 | #ifdef FAST_INTERPOLATION
|
---|
104 | #define MAX_FAST_PROFILE 1000
|
---|
105 | static double fast_p_alt[MAX_FAST_PROFILE];
|
---|
106 | static double fast_p_log_rho[MAX_FAST_PROFILE];
|
---|
107 | static double fast_p_log_thick[MAX_FAST_PROFILE];
|
---|
108 | static double fast_p_log_n1[MAX_FAST_PROFILE];
|
---|
109 | static double fast_h_fac;
|
---|
110 | #endif
|
---|
111 |
|
---|
112 | /* ================================================================== */
|
---|
113 | /*
|
---|
114 | Linear interpolation functions from an older program of K.B.
|
---|
115 | A binary search algorithm is used for fast interpolation.
|
---|
116 | */
|
---|
117 |
|
---|
118 | /* --------------------------- interp ------------------------------- */
|
---|
119 | /**
|
---|
120 | * @short Linear interpolation with binary search algorithm.
|
---|
121 | *
|
---|
122 | * Linear interpolation between data point in sorted (i.e. monotonic
|
---|
123 | * ascending or descending) order. This function determines between
|
---|
124 | * which two data points the requested coordinate is and where between
|
---|
125 | * them. If the given coordinate is outside the covered range, the
|
---|
126 | * value for the corresponding edge is returned.
|
---|
127 | *
|
---|
128 | * A binary search algorithm is used for fast interpolation.
|
---|
129 | *
|
---|
130 | * @param x Input: the requested coordinate
|
---|
131 | * @param v Input: tabulated coordinates at data points
|
---|
132 | * @param n Input: number of data points
|
---|
133 | * @param ipl Output: the number of the data point following the requested
|
---|
134 | * coordinate in the given sorting (1 <= ipl <= n-1)
|
---|
135 | * @param rpl Output: the fraction (x-v[ipl-1])/(v[ipl]-v[ipl-1])
|
---|
136 | * with 0 <= rpl <= 1
|
---|
137 | */
|
---|
138 |
|
---|
139 | static void interp ( double x, double *v, int n, int *ipl, double *rpl )
|
---|
140 | {
|
---|
141 | int i, l, m, j, lm;
|
---|
142 |
|
---|
143 | #ifdef DEBUG_TEST_ALL
|
---|
144 | if ( v == NULL || n <= 2 )
|
---|
145 | {
|
---|
146 | fprintf(stderr,"Invalid parameters for interpolation.\n");
|
---|
147 | *ipl = 1;
|
---|
148 | *rpl = 0.;
|
---|
149 | return;
|
---|
150 | }
|
---|
151 | #endif
|
---|
152 |
|
---|
153 | if ( v[0] < v[n-1] )
|
---|
154 | {
|
---|
155 | if (x <= v[0])
|
---|
156 | {
|
---|
157 | *ipl = 1;
|
---|
158 | *rpl = 0.;
|
---|
159 | return;
|
---|
160 | }
|
---|
161 | else if (x >= v[n-1])
|
---|
162 | {
|
---|
163 | *ipl = n-1;
|
---|
164 | *rpl = 1.;
|
---|
165 | return;
|
---|
166 | }
|
---|
167 | lm = 0;
|
---|
168 | }
|
---|
169 | else
|
---|
170 | {
|
---|
171 | if (x >= v[0])
|
---|
172 | {
|
---|
173 | *ipl = 1;
|
---|
174 | *rpl = 0.;
|
---|
175 | return;
|
---|
176 | }
|
---|
177 | else if (x <= v[n-1])
|
---|
178 | {
|
---|
179 | *ipl = n-1;
|
---|
180 | *rpl = 1.;
|
---|
181 | return;
|
---|
182 | }
|
---|
183 | lm = 1;
|
---|
184 | }
|
---|
185 |
|
---|
186 | l = (n+1)/2-1;
|
---|
187 | m = (n+1)/2;
|
---|
188 | for (i=1; i<=30; i++ )
|
---|
189 | {
|
---|
190 | j = l;
|
---|
191 | if (j < 1) j=1;
|
---|
192 | if (j > n-1) j=n-1;
|
---|
193 | if (x >= v[j+lm-1] && x <= v[j-lm])
|
---|
194 | {
|
---|
195 | *ipl = j;
|
---|
196 | if ( v[j] != v[j-1] )
|
---|
197 | *rpl = (x-v[j-1])/(v[j]-v[j-1]);
|
---|
198 | else
|
---|
199 | *rpl = 0.5;
|
---|
200 | return;
|
---|
201 | }
|
---|
202 | m = (m+1)/2;
|
---|
203 | if (x > v[j-1])
|
---|
204 | l = l + (1-2*lm)*m;
|
---|
205 | else
|
---|
206 | l = l - (1-2*lm)*m;
|
---|
207 | }
|
---|
208 | fprintf(stderr,"Interpolation error.\n");
|
---|
209 | }
|
---|
210 |
|
---|
211 | /* ----------------------------- rpol ------------------------------- */
|
---|
212 | /**
|
---|
213 | * @short Linear interpolation with binary search algorithm.
|
---|
214 | *
|
---|
215 | * Linear interpolation between data point in sorted (i.e. monotonic
|
---|
216 | * ascending or descending) order. The resulting interpolated value
|
---|
217 | * is returned as a return value.
|
---|
218 | *
|
---|
219 | * This function calls interp() the find out where to interpolate.
|
---|
220 | *
|
---|
221 | * @param x Input: Coordinates for data table
|
---|
222 | * @param y Input: Corresponding values for data table
|
---|
223 | * @param n Input: Number of data points
|
---|
224 | * @param xp Input: Coordinate of requested value
|
---|
225 | *
|
---|
226 | * @return Interpolated value
|
---|
227 | *
|
---|
228 | */
|
---|
229 |
|
---|
230 | double rpol ( double *x, double *y, int n, double xp )
|
---|
231 | {
|
---|
232 | int ipl;
|
---|
233 | double rpl;
|
---|
234 |
|
---|
235 | interp ( xp, x, n, &ipl, &rpl );
|
---|
236 | return y[ipl-1]*(1.-rpl) + y[ipl]*rpl;
|
---|
237 | }
|
---|
238 |
|
---|
239 | /* ======================================================================= */
|
---|
240 |
|
---|
241 | static double etadsn; /**< About the same as in CORSIKA Cherenkov function */
|
---|
242 | /**< (but doesn't need to be the same). */
|
---|
243 | static double observation_level; /**< Altitude [cm] of observation level */
|
---|
244 | static double obs_level_refidx;
|
---|
245 | static double obs_level_thick;
|
---|
246 |
|
---|
247 | /* ------------------- init_refraction_tables ---------------------- */
|
---|
248 | /**
|
---|
249 | * @short Initialize tables needed for atmospheric refraction.
|
---|
250 | *
|
---|
251 | * Initialize the correction tables used for the refraction bending
|
---|
252 | * of the light paths. It is called once after the atmospheric
|
---|
253 | * profile has been defined.
|
---|
254 | */
|
---|
255 |
|
---|
256 | static void init_refraction_tables()
|
---|
257 | {
|
---|
258 | int ialt;
|
---|
259 |
|
---|
260 | /* Etadsn is the parameter used in CORSIKA for the scaling */
|
---|
261 | /* between density and index of refraction minus one (n-1). */
|
---|
262 | etadsn = 0.000283 * 994186.38 / 1222.656; /* CORSIKA default */
|
---|
263 | /* Look for better approximation above the observation level. */
|
---|
264 | for (ialt=0; ialt<num_prof; ialt++)
|
---|
265 | if ( p_alt[ialt] > observation_level + 1.5e5 )
|
---|
266 | {
|
---|
267 | etadsn = exp(p_log_n1[ialt]) / exp(p_log_rho[ialt]);
|
---|
268 | break;
|
---|
269 | }
|
---|
270 |
|
---|
271 | #ifdef DEBUG_TEST_ALL
|
---|
272 | if ( p_alt[0] != p_alt[4] && p_log_rho[0] != p_log_rho[4] )
|
---|
273 | {
|
---|
274 | double dscl_p, dscl_t;
|
---|
275 | dscl_p = (p_alt[4]-p_alt[0]) / (p_log_rho[0]-p_log_rho[4]);
|
---|
276 | dscl_t = (p_alt[4]-p_alt[0]) / (p_log_thick[0]-p_log_thick[4]);
|
---|
277 | printf(" Pressure scale height near ground: %7.2f m\n",dscl_p*0.01);
|
---|
278 | printf(" Thickness scale height near ground: %7.2f m\n",dscl_t*0.01);
|
---|
279 | printf(" Etadsn=(n-1)/density parameter: %g\n",etadsn);
|
---|
280 | }
|
---|
281 | #endif
|
---|
282 |
|
---|
283 | /* Initialize tables by numerical integration for vertical and slanted */
|
---|
284 | /* (45 degrees) paths, taking into account known angular dependences. */
|
---|
285 |
|
---|
286 | for (ialt=0; ialt<num_prof; ialt++)
|
---|
287 | {
|
---|
288 | double t0_vt, t0_45, x0_45, n_sint0;
|
---|
289 | double t_vt, t_45, x_45, dt_vt, dt_45, dx_45, dz, z, zm, ds;
|
---|
290 | double vc = 29.9792458; /* velocity of light [cm/ns] */
|
---|
291 | double theta0 = 45. * (M_PI/180.), theta1, theta2;
|
---|
292 | int nz, iz;
|
---|
293 | double c, s;
|
---|
294 |
|
---|
295 | x0_45 = (p_alt[ialt]-observation_level) * tan(theta0);
|
---|
296 | t0_vt = 1./vc * (p_alt[ialt] - observation_level +
|
---|
297 | etadsn*(thickx_(&observation_level)-thickx_(&p_alt[ialt])));
|
---|
298 | t0_45 = t0_vt / cos(theta0);
|
---|
299 | nz = 1000; /* Number of steps for numerical ray tracing */
|
---|
300 | dz = (observation_level-p_alt[ialt]) / (double)nz;
|
---|
301 | n_sint0 = refidx_(&p_alt[ialt]) * sin(theta0);
|
---|
302 | for (iz=0, z=p_alt[ialt], theta2=theta0, t_vt=t_45=x_45=0.; iz<nz; iz++)
|
---|
303 | {
|
---|
304 | z += dz;
|
---|
305 | zm = z-0.5*dz;
|
---|
306 | theta1 = theta2;
|
---|
307 | theta2 = asin(n_sint0/refidx_(&z));
|
---|
308 | ds = fabs(dz) / cos(0.5*(theta1+theta2));
|
---|
309 | dt_vt = fabs(dz) * refidx_(&zm) / vc;
|
---|
310 | dt_45 = ds * refidx_(&zm) / vc;
|
---|
311 | dx_45 = fabs(dz) * tan(0.5*(theta1+theta2));
|
---|
312 | t_vt += dt_vt;
|
---|
313 | t_45 += dt_45;
|
---|
314 | x_45 += dx_45;
|
---|
315 | }
|
---|
316 | if ( p_alt[ialt] < observation_level )
|
---|
317 | {
|
---|
318 | t_vt *= -1.;
|
---|
319 | t_45 *= -1.;
|
---|
320 | x_45 *= -1.;
|
---|
321 | }
|
---|
322 | theta1 = asin(n_sint0/refidx_(&observation_level));
|
---|
323 | c = cos(theta0+0.28*(theta1-theta0));
|
---|
324 | s = sin(theta0+0.28*(theta1-theta0));
|
---|
325 | if ( x_45 < x0_45 ) /* Offset is normally less than for straight line */
|
---|
326 | p_bend_ray_hori_a[ialt] = sqrt((x0_45 - x_45) * (c*c*c)/s);
|
---|
327 | else
|
---|
328 | p_bend_ray_hori_a[ialt] = 0.;
|
---|
329 | p_bend_ray_time0[ialt] = t_vt - t0_vt;
|
---|
330 | if ( ((t_45 - t0_45) - (t_vt - t0_vt)) < 0. )
|
---|
331 | p_bend_ray_time_a[ialt] =
|
---|
332 | sqrt(((t0_45 - t_45) - (t0_vt - t_vt)) * (c*c*c)/(s*s));
|
---|
333 | else
|
---|
334 | p_bend_ray_time_a[ialt] = 0.;
|
---|
335 | }
|
---|
336 | }
|
---|
337 |
|
---|
338 | /* ------------------- init_fast_interpolation ---------------------- */
|
---|
339 |
|
---|
340 | #ifdef FAST_INTERPOLATION
|
---|
341 | static void init_fast_interpolation()
|
---|
342 | {
|
---|
343 | int i;
|
---|
344 | for ( i=0; i<MAX_FAST_PROFILE; i++)
|
---|
345 | {
|
---|
346 | if ( i<MAX_FAST_PROFILE-1 )
|
---|
347 | fast_p_alt[i] = bottom_of_atmosphere + (double) i /
|
---|
348 | (double)(MAX_FAST_PROFILE-1) *
|
---|
349 | (top_of_atmosphere - bottom_of_atmosphere);
|
---|
350 | else /* avoid rounding errors */
|
---|
351 | fast_p_alt[i] = top_of_atmosphere;
|
---|
352 | fast_p_log_rho[i] = rpol(p_alt,p_log_rho,num_prof,fast_p_alt[i]);
|
---|
353 | fast_p_log_thick[i] = rpol(p_alt,p_log_thick,num_prof,fast_p_alt[i]);
|
---|
354 | fast_p_log_n1[i] = rpol(p_alt,p_log_n1,num_prof,fast_p_alt[i]);
|
---|
355 | }
|
---|
356 |
|
---|
357 | fast_h_fac = (double)(MAX_FAST_PROFILE-1) /
|
---|
358 | (top_of_atmosphere - bottom_of_atmosphere);
|
---|
359 | }
|
---|
360 | #endif
|
---|
361 |
|
---|
362 | /* ------------------- init_corsika_atmosphere -------------------- */
|
---|
363 | /**
|
---|
364 | * @short Take the atmospheric profile from CORSIKA built-in functions.
|
---|
365 | *
|
---|
366 | * For use of the refraction bending corrections together with the
|
---|
367 | * CORSIKA built-in atmospheres, the atmosphere tables are constructed
|
---|
368 | * from the CORSIKA RHOF and THICK functions.
|
---|
369 | * Note that the refraction index in this case is without taking the
|
---|
370 | * effect of wator vapour into account.
|
---|
371 | */
|
---|
372 |
|
---|
373 | static void init_corsika_atmosphere()
|
---|
374 | {
|
---|
375 | static double alt[50] =
|
---|
376 | { 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.,
|
---|
377 | 10., 11., 12., 13., 14., 15., 16., 17., 18., 19.,
|
---|
378 | 20., 21., 22., 23., 24., 25., 27., 30., 32., 35.,
|
---|
379 | 37., 40., 42., 45., 47., 50., 55., 60., 65., 70.,
|
---|
380 | 75., 80., 85., 90., 95.,100.,105.,110.,120.,150. };
|
---|
381 | int ialt;
|
---|
382 | double rho, thick, n_1;
|
---|
383 | extern double rhof_(double *);
|
---|
384 | extern double thick_(double *);
|
---|
385 | extern double heigh_(double *);
|
---|
386 |
|
---|
387 | atmosphere = 0;
|
---|
388 |
|
---|
389 | thick = 0.;
|
---|
390 | top_of_atmosphere = heigh_(&thick);
|
---|
391 |
|
---|
392 | num_prof = 0;
|
---|
393 | for (ialt=0; ialt<50 && ialt<MAX_PROFILE; ialt++)
|
---|
394 | {
|
---|
395 | if ( alt[ialt] > top_of_atmosphere )
|
---|
396 | {
|
---|
397 | if ( ialt > 0 && ialt+1 < 50 && ialt+1 < MAX_PROFILE )
|
---|
398 | alt[ialt] = 0.5*(alt[ialt-1]+top_of_atmosphere);
|
---|
399 | else
|
---|
400 | alt[ialt] = top_of_atmosphere;
|
---|
401 | }
|
---|
402 | p_alt[num_prof] = alt[ialt]*1e5;
|
---|
403 | p_log_alt[num_prof] = (alt[ialt]>0.)?log(alt[ialt]*1e5):0.;
|
---|
404 | /* Get density from CORSIKA */
|
---|
405 | rho = rhof_(&p_alt[num_prof]);
|
---|
406 | if ( rho <= 0. )
|
---|
407 | rho = 1e-20;
|
---|
408 | p_log_rho[num_prof] = log(rho);
|
---|
409 | p_rho[num_prof] = rho;
|
---|
410 | /* Get atmospheric thickness from CORSIKA */
|
---|
411 | thick = thick_(&p_alt[num_prof]);
|
---|
412 | p_log_thick[num_prof] = (thick>0.)?log(thick):-1000.;
|
---|
413 | /* Index of refraction simply proportional to density, assuming */
|
---|
414 | /* n=1.000283 for CORSIKA default atmosphere at sea level */
|
---|
415 | /* (which is about 0.5% away from the real value of n-1). */
|
---|
416 | n_1 = rho * 0.000283 * 994186.38 / 1222.656;
|
---|
417 | p_log_n1[num_prof] = (n_1>0.)?log(n_1):-1000.;
|
---|
418 | num_prof++;
|
---|
419 | }
|
---|
420 |
|
---|
421 | bottom_of_atmosphere = p_alt[0];
|
---|
422 |
|
---|
423 | #ifdef FAST_INTERPOLATION
|
---|
424 | /* Initialize faster tables for most frequent lookups */
|
---|
425 | init_fast_interpolation();
|
---|
426 | #endif
|
---|
427 |
|
---|
428 | /* Initialize the tables for the refraction bending */
|
---|
429 | init_refraction_tables();
|
---|
430 | }
|
---|
431 |
|
---|
432 | /* ----------------------- init_atmosphere ------------------------ */
|
---|
433 | /**
|
---|
434 | * @short Initialize atmospheric profiles.
|
---|
435 | *
|
---|
436 | * Internal function for initialising both external and CORSIKA
|
---|
437 | * built-in atmospheric profiles. If any CORSIKA built-in profile
|
---|
438 | * should be used, it simply calls init_corsika_atmosphere().
|
---|
439 | *
|
---|
440 | * Otherwise, atmospheric models are read in from text-format tables.
|
---|
441 | * The supplied models 1-6 are based on output of the MODTRAN program.
|
---|
442 | * For the interpolation of relevant parameters (density, thickness,
|
---|
443 | * index of refraction, ...) all parameters are transformed such
|
---|
444 | * that linear interpolation can be easily used.
|
---|
445 | *
|
---|
446 | */
|
---|
447 |
|
---|
448 | static void init_atmosphere ()
|
---|
449 | {
|
---|
450 | char fname[128];
|
---|
451 | FILE *f;
|
---|
452 | char line[1024];
|
---|
453 | int count;
|
---|
454 | double alt,rho,thick,n_1;
|
---|
455 | #ifdef LONG_ATMPROF
|
---|
456 | double p,t,N,O3,H2O;
|
---|
457 | #endif
|
---|
458 |
|
---|
459 | /* CORSIKA built-in atmospheres have atmosphere numbers <= 0 */
|
---|
460 | if ( atmosphere <=0 )
|
---|
461 | {
|
---|
462 | init_corsika_atmosphere();
|
---|
463 | return;
|
---|
464 | }
|
---|
465 |
|
---|
466 | /* There are two different versions of data files. */
|
---|
467 | #ifndef LONG_ATMPROF
|
---|
468 | sprintf(fname,"atmprof%d.dat",atmosphere);
|
---|
469 | #else
|
---|
470 | sprintf(fname,"atm_profile_model_%d.dat",atmosphere);
|
---|
471 | #endif
|
---|
472 | if ( (f=fopen(fname,"r")) == NULL )
|
---|
473 | {
|
---|
474 | perror(fname);
|
---|
475 | exit(1);
|
---|
476 | }
|
---|
477 |
|
---|
478 | count = num_prof = 0;
|
---|
479 | while ( fgets(line,sizeof(line)-1,f) != NULL && num_prof < MAX_PROFILE )
|
---|
480 | {
|
---|
481 | char *s;
|
---|
482 | count++;
|
---|
483 |
|
---|
484 | for (s=line;*s==' ';s++)
|
---|
485 | ;
|
---|
486 | if ( *s=='#' ) /* Comment line */
|
---|
487 | continue;
|
---|
488 | #ifndef LONG_ATMPROF
|
---|
489 | /* The short files contain only data relevant for CORSIKA. */
|
---|
490 | if ( sscanf(s,"%lf %lf %lf %lf",
|
---|
491 | &alt,&rho,&thick,&n_1) != 4 )
|
---|
492 | #else
|
---|
493 | /* The long files contain other data as well. */
|
---|
494 | if ( sscanf(s,"%lf %lf %lf %lf %lf %lf %lf %lf %lf",
|
---|
495 | &alt,&p,&t,&N,&rho,&thick,&O3,&H2O,&n_1) != 9 )
|
---|
496 | #endif
|
---|
497 | {
|
---|
498 | fprintf(stderr,"Syntax error in %s line %d.\n",fname,count);
|
---|
499 | exit(1);
|
---|
500 | }
|
---|
501 |
|
---|
502 | p_alt[num_prof] = alt*1e5; /* Altitude in file was in km */
|
---|
503 | p_log_alt[num_prof] = (alt>0.)?log(alt*1e5):0.;
|
---|
504 | p_log_rho[num_prof] = (rho>0.)?log(rho):-1000.;
|
---|
505 | p_rho[num_prof] = rho;
|
---|
506 | p_log_thick[num_prof] = (thick>0.)?log(thick):-1000.;
|
---|
507 | p_log_n1[num_prof] = (n_1>0.)?log(n_1):-1000.;
|
---|
508 | num_prof++;
|
---|
509 | }
|
---|
510 |
|
---|
511 | fclose(f);
|
---|
512 | fflush(stdout);
|
---|
513 | printf("\n Atmospheric profile %d with %d levels read from file %s\n\n",
|
---|
514 | atmosphere,num_prof,fname);
|
---|
515 |
|
---|
516 | if ( num_prof < 5 )
|
---|
517 | {
|
---|
518 | fprintf(stderr,
|
---|
519 | "There are definitely too few atmospheric levels in this file.\n");
|
---|
520 | fprintf(stderr,
|
---|
521 | "Normally this kind of file should have 50 levels.\n");
|
---|
522 | exit(1);
|
---|
523 | }
|
---|
524 |
|
---|
525 | bottom_of_atmosphere = p_alt[0];
|
---|
526 |
|
---|
527 | #ifdef FAST_INTERPOLATION
|
---|
528 | /* Initialize faster tables for most frequent lookups */
|
---|
529 | init_fast_interpolation();
|
---|
530 | #endif
|
---|
531 |
|
---|
532 | /* Initialize the tables for the refraction bending */
|
---|
533 | init_refraction_tables();
|
---|
534 | }
|
---|
535 |
|
---|
536 | /* -------------------------- atmset_ ---------------------------- */
|
---|
537 | /**
|
---|
538 | * @short Set number of atmospheric model profile to be used.
|
---|
539 | *
|
---|
540 | * The atmospheric model is initialized first before the
|
---|
541 | * interpolating functions can be used. For efficiency reasons,
|
---|
542 | * the functions rhofx_(), thickx_(), ... don't check if the
|
---|
543 | * initialisation was done.
|
---|
544 | *
|
---|
545 | * This function is called if the 'ATMOSPHERE' keyword is
|
---|
546 | * present in the CORSIKA input file.
|
---|
547 | *
|
---|
548 | * The function may be called from CORSIKA to initialize
|
---|
549 | * the atmospheric model via 'CALL ATMSET(IATMO,OBSLEV)' or such.
|
---|
550 | *
|
---|
551 | * @param iatmo (pointer to) atmospheric profile number;
|
---|
552 | * negative for CORSIKA built-in profiles.
|
---|
553 | * @param obslev (pointer to) altitude of observation level [cm]
|
---|
554 | *
|
---|
555 | * @return (none)
|
---|
556 | */
|
---|
557 |
|
---|
558 | void atmset_ (int *iatmo, double *obslev)
|
---|
559 | {
|
---|
560 | atmosphere = *iatmo;
|
---|
561 | observation_level = *obslev;
|
---|
562 | init_atmosphere();
|
---|
563 | obs_level_refidx = refidx_(obslev);
|
---|
564 | obs_level_thick = thickx_(obslev);
|
---|
565 | }
|
---|
566 |
|
---|
567 | /* ---------------------------- rhofx_ ----------------------------- */
|
---|
568 | /**
|
---|
569 | *
|
---|
570 | * @short Density of the atmosphere as a function of altitude.
|
---|
571 | * This function can be called from Fortran code as RHOFX(HEIGHT).
|
---|
572 | *
|
---|
573 | * @param height (pointer to) altitude [cm]
|
---|
574 | *
|
---|
575 | * @return density [g/cm**3]
|
---|
576 | */
|
---|
577 |
|
---|
578 | double rhofx_ (double *height)
|
---|
579 | {
|
---|
580 | #ifdef FAST_INTERPOLATION
|
---|
581 | int i;
|
---|
582 | double r;
|
---|
583 | if ( (*height) < bottom_of_atmosphere )
|
---|
584 | return p_rho[0];
|
---|
585 | else if ( (*height) >= top_of_atmosphere )
|
---|
586 | return 0.;
|
---|
587 | i = (int) ( fast_h_fac * ((*height)-bottom_of_atmosphere) );
|
---|
588 | if ( i >= MAX_FAST_PROFILE-1 )
|
---|
589 | return 0.;
|
---|
590 | r = fast_h_fac * ((*height)-fast_p_alt[i]);
|
---|
591 | return exp((1.-r)*fast_p_log_rho[i] + r*fast_p_log_rho[i+1]);
|
---|
592 | #else
|
---|
593 | return exp(rpol(p_alt,p_log_rho,num_prof,*height));
|
---|
594 | #endif
|
---|
595 | }
|
---|
596 |
|
---|
597 | /* ---------------------------- thickx_ ----------------------------- */
|
---|
598 | /**
|
---|
599 | *
|
---|
600 | * @short Atmospheric thickness [g/cm**2] as a function of altitude.
|
---|
601 | * This function can be called from Fortran code as THICKX(HEIGHT).
|
---|
602 | *
|
---|
603 | * @param height (pointer to) altitude [cm]
|
---|
604 | *
|
---|
605 | * @return thickness [g/cm**2]
|
---|
606 | *
|
---|
607 | */
|
---|
608 |
|
---|
609 | double thickx_ (double *height)
|
---|
610 | {
|
---|
611 | #ifdef FAST_INTERPOLATION
|
---|
612 | int i;
|
---|
613 | double r;
|
---|
614 | if ( (*height) < bottom_of_atmosphere )
|
---|
615 | return exp(fast_p_log_thick[0]);
|
---|
616 | else if ( (*height) >= top_of_atmosphere )
|
---|
617 | return 0.;
|
---|
618 | i = (int) ( fast_h_fac * ((*height)-bottom_of_atmosphere) );
|
---|
619 | if ( i >= MAX_FAST_PROFILE-1 )
|
---|
620 | return 0.;
|
---|
621 | r = fast_h_fac * ((*height)-fast_p_alt[i]);
|
---|
622 | return exp((1.-r)*fast_p_log_thick[i] + r*fast_p_log_thick[i+1]);
|
---|
623 | #else
|
---|
624 | return exp(rpol(p_alt,p_log_thick,num_prof,*height));
|
---|
625 | #endif
|
---|
626 | }
|
---|
627 |
|
---|
628 | /* ---------------------------- refidx_ ----------------------------- */
|
---|
629 | /**
|
---|
630 | *
|
---|
631 | * @short Index of refraction as a function of altitude [cm].
|
---|
632 | * This function can be called from Fortran code as REFIDX(HEIGHT).
|
---|
633 | *
|
---|
634 | * @param height (pointer to) altitude [cm]
|
---|
635 | *
|
---|
636 | * @return index of refraction
|
---|
637 | *
|
---|
638 | */
|
---|
639 |
|
---|
640 | double refidx_ (double *height)
|
---|
641 | {
|
---|
642 | #ifdef FAST_INTERPOLATION
|
---|
643 | int i;
|
---|
644 | double r;
|
---|
645 | if ( (*height) < bottom_of_atmosphere )
|
---|
646 | return 1.+exp(fast_p_log_n1[0]);
|
---|
647 | else if ( (*height) >= top_of_atmosphere )
|
---|
648 | return 1.;
|
---|
649 | i = (int) ( fast_h_fac * ((*height)-bottom_of_atmosphere) );
|
---|
650 | if ( i >= MAX_FAST_PROFILE-1 )
|
---|
651 | return 1.;
|
---|
652 | r = fast_h_fac * ((*height)-fast_p_alt[i]);
|
---|
653 | return 1.+exp((1.-r)*fast_p_log_n1[i] + r*fast_p_log_n1[i+1]);
|
---|
654 | #else
|
---|
655 | return 1.+exp(rpol(p_alt,p_log_n1,num_prof,*height));
|
---|
656 | #endif
|
---|
657 | }
|
---|
658 |
|
---|
659 | /* ---------------------------- heighx_ ----------------------------- */
|
---|
660 | /**
|
---|
661 | *
|
---|
662 | * Altitude [cm] as a function of atmospheric thickness [g/cm**2].
|
---|
663 | * This function can be called from Fortran code as HEIGHX(THICK).
|
---|
664 | *
|
---|
665 | * @param thick (pointer to) atmospheric thickness [g/cm**2]
|
---|
666 | *
|
---|
667 | * @param altitude [cm]
|
---|
668 | */
|
---|
669 |
|
---|
670 | double heighx_ (double *thick)
|
---|
671 | {
|
---|
672 | double h;
|
---|
673 | h = rpol(p_log_thick,p_alt,num_prof,*thick>0.?log(*thick):-1000.);
|
---|
674 | if ( h < top_of_atmosphere )
|
---|
675 | return h;
|
---|
676 | else
|
---|
677 | return top_of_atmosphere;
|
---|
678 | }
|
---|
679 |
|
---|
680 | /* ---------------------------- raybnd_ ---------------------------- */
|
---|
681 | /**
|
---|
682 | * @short Calculate the bending of light due to atmospheric refraction.
|
---|
683 | *
|
---|
684 | * Path of light through the atmosphere including the bending by refraction.
|
---|
685 | * This function assumes a plane-parallel atmosphere.
|
---|
686 | * Coefficients for corrections from straight-line propagation to
|
---|
687 | * refraction-bent path are numerically evaluated when the atmospheric
|
---|
688 | * model is defined.
|
---|
689 | * Note that while the former mix of double/float data types may appear odd,
|
---|
690 | * it was determined by the variables present in older CORSIKA to save
|
---|
691 | * conversions. With CORSIKA 6.0 all parameters are of double type.
|
---|
692 | *
|
---|
693 | * This function may be called from FORTRAN as
|
---|
694 | * CALL RAYBND(ZEM,U,V,W,DX,DY,DT)
|
---|
695 | *
|
---|
696 | * @param zem Altitude of emission above sea level [cm]
|
---|
697 | * @param u Initial/Final direction cosine along X axis (updated)
|
---|
698 | * @param v Initial/Final direction cosine along Y axis (updated)
|
---|
699 | * @param w Initial/Final direction cosine along Z axis (updated)
|
---|
700 | * @param dx Position in CORSIKA detection plane [cm] (updated)
|
---|
701 | * @param dy Position in CORSIKA detection plane [cm] (updated)
|
---|
702 | * @param dt Time of photon [ns]. Input: emission time.
|
---|
703 | * Output: time of arrival in CORSIKA detection plane.
|
---|
704 | */
|
---|
705 |
|
---|
706 | void raybnd_(double *zem, cors_real_now_t *u, cors_real_now_t *v,
|
---|
707 | double *w, cors_real_now_t *dx, cors_real_now_t *dy, cors_real_now_t *dt)
|
---|
708 | {
|
---|
709 | double sin_t_em, sin_t_obs, theta_em, theta_obs;
|
---|
710 | double c, s, h, t, rho;
|
---|
711 | double hori_off, travel_time;
|
---|
712 | double vc = 29.9792458; /* velocity of light [cm/ns] */
|
---|
713 |
|
---|
714 | /* (Sine of) emission zenith angle */
|
---|
715 | sin_t_em = sqrt((double)((*u)*(*u)+(*v)*(*v)));
|
---|
716 | if ( sin_t_em <= 0. )
|
---|
717 | { /* Exactly vertical: no bending; just calulate travel time. */
|
---|
718 | *dt += (((*zem) - observation_level) +
|
---|
719 | etadsn*(obs_level_thick-thickx_(zem))) / (*w) / vc +
|
---|
720 | rpol(p_alt,p_bend_ray_time0,num_prof,*zem);
|
---|
721 | return;
|
---|
722 | }
|
---|
723 | if ( sin_t_em > 1. || (*w) <= 0. )
|
---|
724 | return;
|
---|
725 | theta_em = asin(sin_t_em);
|
---|
726 |
|
---|
727 | /* (Sine of) observed zenith angle */
|
---|
728 | sin_t_obs = sin_t_em*refidx_(zem) / obs_level_refidx;
|
---|
729 | if ( sin_t_obs > 1. )
|
---|
730 | return;
|
---|
731 | theta_obs = asin(sin_t_obs);
|
---|
732 |
|
---|
733 | #ifdef TEST_RAYBND
|
---|
734 | fflush(NULL);
|
---|
735 | printf(" raybnd: theta = %5.3f, %5.3f\n",(double)(theta_em*180./M_PI),
|
---|
736 | (double)(theta_obs*180./M_PI));
|
---|
737 | #endif
|
---|
738 |
|
---|
739 | /* Calculate horizontal displacement with respect to straight line */
|
---|
740 | /* and total light travel time from emission to observation level. */
|
---|
741 |
|
---|
742 | c = cos(theta_em+0.28*(theta_obs-theta_em));
|
---|
743 | s = sin(theta_em+0.28*(theta_obs-theta_em));
|
---|
744 |
|
---|
745 | rho = rhofx_(zem);
|
---|
746 | h = rpol(p_rho,p_bend_ray_hori_a,num_prof,rho);
|
---|
747 | hori_off = -(h*h) * s/(c*c*c);
|
---|
748 | t = rpol(p_rho,p_bend_ray_time_a,num_prof,rho);
|
---|
749 | #ifdef TEST_RAYBND
|
---|
750 | printf(" raybnd: horizontal displacement = %5.2f\n",hori_off);
|
---|
751 | printf(" raybdn: time = %5.3f + %5.3f +%5.3f + %5.3f\n",
|
---|
752 | *dt,(((*zem) - observation_level) +
|
---|
753 | etadsn*(obs_level_thick-thickx_(zem))) / (*w) / vc,
|
---|
754 | rpol(p_alt,p_bend_ray_time0,num_prof,*zem),
|
---|
755 | -(t*t) * (s*s)/(c*c*c));
|
---|
756 | #endif
|
---|
757 | travel_time = rpol(p_alt,p_bend_ray_time0,num_prof,*zem) -
|
---|
758 | (t*t) * (s*s)/(c*c*c);
|
---|
759 | travel_time += (((*zem) - observation_level) +
|
---|
760 | etadsn*(obs_level_thick-thickx_(zem))) / (*w) / vc;
|
---|
761 |
|
---|
762 | /* Update arguments: */
|
---|
763 | /* Emission direction replaced by observed direction. */
|
---|
764 | *u *= sin_t_obs/sin_t_em;
|
---|
765 | *v *= sin_t_obs/sin_t_em;
|
---|
766 | *w = sqrt(1.-sin_t_obs*sin_t_obs);
|
---|
767 |
|
---|
768 | /* Position in observation level corrected for displacement. */
|
---|
769 | *dx += hori_off * (*u)/sin_t_obs;
|
---|
770 | *dy += hori_off * (*v)/sin_t_obs;
|
---|
771 |
|
---|
772 | /* Light travel time added to emission time. */
|
---|
773 | *dt += travel_time;
|
---|
774 | }
|
---|
775 |
|
---|
776 | /* ============================================================== */
|
---|
777 | /*
|
---|
778 | * Functions for fitting the tabulated density profile for CORSIKA EGS part.
|
---|
779 | */
|
---|
780 |
|
---|
781 | /* ------------------------ sum_log_dev_sq ------------------------- */
|
---|
782 | /**
|
---|
783 | * Measure of deviation of model layers from tables.
|
---|
784 | */
|
---|
785 |
|
---|
786 | static double sum_log_dev_sq(double a, double b, double c, int np,
|
---|
787 | double *h, double *t, double *rho)
|
---|
788 | {
|
---|
789 | int ip;
|
---|
790 | double s = 0., al;
|
---|
791 | for (ip=0; ip<np; ip++ )
|
---|
792 | {
|
---|
793 | #if 0
|
---|
794 | /* Fit minimizes relative deviations (better fits at high altitude) */
|
---|
795 | if ( a+b*exp(-h[ip]/c) > 0. && t[ip] > 0. )
|
---|
796 | al = log((a+b*exp(-h[ip]/c)) / t[ip]);
|
---|
797 | else
|
---|
798 | al = 0.;
|
---|
799 | s += al*al;
|
---|
800 | #elif 1
|
---|
801 | /* Compromise between relative and absolute deviations */
|
---|
802 | if ( a+b*exp(-h[ip]/c) > 0. && t[ip] > 0. )
|
---|
803 | al = log((a+b*exp(-h[ip]/c)) / t[ip]);
|
---|
804 | else
|
---|
805 | al = 0.;
|
---|
806 | s += fabs(al)*fabs(a+b*exp(-h[ip]/c) - t[ip]);
|
---|
807 | #elif 0
|
---|
808 | /* Fit minimizes absolute deviations (better fits at low altitude) */
|
---|
809 | al = a+b*exp(-h[ip]/c) - t[ip];
|
---|
810 | s += al*al;
|
---|
811 | #endif
|
---|
812 | }
|
---|
813 | return s;
|
---|
814 | }
|
---|
815 |
|
---|
816 | /* ------------------------ atm_exp_fit ------------------------- */
|
---|
817 | /**
|
---|
818 | * Fit one atmosphere layer by an expontential density model.
|
---|
819 | */
|
---|
820 |
|
---|
821 | static double atm_exp_fit ( double h1, double h2, double *ap,
|
---|
822 | double *bp, double *cp, double *s0, int *npp )
|
---|
823 | {
|
---|
824 | int ip, np, iter;
|
---|
825 | double h[MAX_PROFILE], t[MAX_PROFILE], rho[MAX_PROFILE], t1, t2;
|
---|
826 | double a = *ap, b = *bp, c = *cp;
|
---|
827 | double s, dc;
|
---|
828 |
|
---|
829 | for (ip=np=0; ip<num_prof; ip++)
|
---|
830 | if ( p_alt[ip] >= h1 && p_alt[ip] <= h2 && p_alt[ip] < 86e5 )
|
---|
831 | {
|
---|
832 | h[np] = p_alt[ip];
|
---|
833 | t[np] = exp(p_log_thick[ip]);
|
---|
834 | rho[np] = exp(p_log_rho[ip]);
|
---|
835 | np++;
|
---|
836 | }
|
---|
837 | t1 = thickx_(&h1);
|
---|
838 | t2 = thickx_(&h2);
|
---|
839 |
|
---|
840 | *s0 = sum_log_dev_sq(a,b,c,np,h,t,rho);
|
---|
841 | *npp = np;
|
---|
842 | if ( np <= 0 )
|
---|
843 | return 0.;
|
---|
844 | else if ( h1 == h2 || t1 == t2 )
|
---|
845 | return sum_log_dev_sq(a,b,c,np,h,t,rho);
|
---|
846 | else if ( np <= 2 || ( np == 3 && (h1==h[0] || h2==h[1])) )
|
---|
847 | {
|
---|
848 | *cp = c = *cp;
|
---|
849 | *bp = b = (t2-t1) / (exp(-h2/c)-exp(-h1/c));
|
---|
850 | *ap = a = t1 - b*exp(-h1/c);
|
---|
851 | return sum_log_dev_sq(a,b,c,np,h,t,rho);
|
---|
852 | }
|
---|
853 |
|
---|
854 | c = *cp;
|
---|
855 | b = (t2-t1) / (exp(-h2/c)-exp(-h1/c));
|
---|
856 | a = t1 - b*exp(-h1/c);
|
---|
857 | s = sum_log_dev_sq(a,b,c,np,h,t,rho);
|
---|
858 | for ( iter=0, dc=2000.e2; iter<30; iter++ )
|
---|
859 | {
|
---|
860 | double a1, a2, a3, an, b1, b2, b3, bn, c1, c2, c3, cn, s1, s2, s3, sn;
|
---|
861 | c2 = c;
|
---|
862 | c1 = c - dc;
|
---|
863 | if ( c1 <= 0. )
|
---|
864 | c1 = c / 2.;
|
---|
865 | c3 = c + dc;
|
---|
866 |
|
---|
867 | b1 = (t2-t1) / (exp(-h2/c1)-exp(-h1/c1));
|
---|
868 | a1 = t1 - b1*exp(-h1/c1);
|
---|
869 | b2 = (t2-t1) / (exp(-h2/c2)-exp(-h1/c2));
|
---|
870 | a2 = t1 - b2*exp(-h1/c2);
|
---|
871 | b3 = (t2-t1) / (exp(-h2/c3)-exp(-h1/c3));
|
---|
872 | a3 = t1 - b3*exp(-h1/c3);
|
---|
873 | s1 = sum_log_dev_sq(a1,b1,c1,np,h,t,rho);
|
---|
874 | s2 = sum_log_dev_sq(a2,b2,c2,np,h,t,rho);
|
---|
875 | s3 = sum_log_dev_sq(a3,b3,c3,np,h,t,rho);
|
---|
876 | /* This works only for a roughly parabolic minimum */
|
---|
877 | cn = ((c1*c1-c2*c2)*(s3-s2)-(c3*c3-c2*c2)*(s1-s2)) /
|
---|
878 | (2.*(c1-c2)*(s3-s2)-2.*(c3-c2)*(s1-s2));
|
---|
879 | if ( cn <= 0. )
|
---|
880 | cn = 0.5*c;
|
---|
881 | bn = (t2-t1) / (exp(-h2/cn)-exp(-h1/cn));
|
---|
882 | an = t1 - bn*exp(-h1/cn);
|
---|
883 | sn = sum_log_dev_sq(an,bn,cn,np,h,t,rho);
|
---|
884 | if ( sn > s )
|
---|
885 | break;
|
---|
886 | if ( sn < s*1.00001 )
|
---|
887 | {
|
---|
888 | a = an;
|
---|
889 | b = bn;
|
---|
890 | c = cn;
|
---|
891 | s = sn;
|
---|
892 | break;
|
---|
893 | }
|
---|
894 | if ( cn < 3000.e2 )
|
---|
895 | {
|
---|
896 | cn = 3000.e2;
|
---|
897 | bn = (t2-t1) / (exp(-h2/cn)-exp(-h1/cn));
|
---|
898 | an = t1 - bn*exp(-h1/cn);
|
---|
899 | dc = 1000.e2;
|
---|
900 | }
|
---|
901 | else if ( cn > c + 1.5*dc )
|
---|
902 | {
|
---|
903 | cn = c + 1.5*dc;
|
---|
904 | bn = (t2-t1) / (exp(-h2/cn)-exp(-h1/cn));
|
---|
905 | an = t1 - bn*exp(-h1/cn);
|
---|
906 | dc *= 2.;
|
---|
907 | }
|
---|
908 | else if ( cn < c -1.5*dc )
|
---|
909 | {
|
---|
910 | cn = c -1.5*dc;
|
---|
911 | bn = (t2-t1) / (exp(-h2/cn)-exp(-h1/cn));
|
---|
912 | an = t1 - bn*exp(-h1/cn);
|
---|
913 | dc *= 2.;
|
---|
914 | }
|
---|
915 | else if ( fabs(c-cn) < 0.25*dc )
|
---|
916 | dc = 2.*fabs(c-cn) + 0.2*dc;
|
---|
917 | else
|
---|
918 | dc = 1.5*fabs(c-cn);
|
---|
919 | a = an;
|
---|
920 | b = bn;
|
---|
921 | c = cn;
|
---|
922 | s = sn;
|
---|
923 | }
|
---|
924 | *cp = c;
|
---|
925 | *bp = b;
|
---|
926 | *ap = a;
|
---|
927 | return s;
|
---|
928 | }
|
---|
929 |
|
---|
930 | /* ---------------------------- atmfit_ ------------------------------ */
|
---|
931 | /**
|
---|
932 | * @short Fit the tabulated density profile for CORSIKA EGS part.
|
---|
933 | *
|
---|
934 | * Fitting of the tabulated atmospheric density profile by
|
---|
935 | * piecewise exponential parts as used in CORSIKA.
|
---|
936 | * The fits are constrained by fixing the atmospheric thicknesses
|
---|
937 | * at the boundaries to the values obtained from the table.
|
---|
938 | * Note that not every atmospheric profile can be fitted well
|
---|
939 | * by the CORSIKA piecewise models (4*exponential + 1*constant
|
---|
940 | * density). In particular, the tropical model is known to
|
---|
941 | * be a problem. Setting the boundary heights manually might help.
|
---|
942 | * The user is advised to check at least once that the fitted
|
---|
943 | * layers represent the tabulated atmosphere sufficiently well,
|
---|
944 | * at least at the altitudes most critical for the observations
|
---|
945 | * (usually at observation level and near shower maximum but
|
---|
946 | * depending on the user's emphasis, this may vary).
|
---|
947 | *
|
---|
948 | * Fit all layers (except the uppermost) by exponentials and (if *nlp > 0)
|
---|
949 | * try to improve fits by adjusting layer boundaries.
|
---|
950 | * The uppermost layer has constant density up to the 'edge' of the atmosphere.
|
---|
951 | *
|
---|
952 | * This function may be called from CORSIKA.
|
---|
953 | *
|
---|
954 | * Parameters (all pointers since function is called from Fortran):
|
---|
955 | * @param nlp Number of layers (or negative of that if boundaries set manually)
|
---|
956 | * @param hlay Vector of layer (lower) boundaries.
|
---|
957 | * @param aatm,batm,catm Parameters as used in CORSIKA.
|
---|
958 | */
|
---|
959 |
|
---|
960 | void atmfit_(int *nlp, double *hlay, double *aatm, double *batm, double *catm)
|
---|
961 | {
|
---|
962 | int il, np;
|
---|
963 | double *a, *b, *c, *h, *s, *s0;
|
---|
964 | double atmp[2], btmp[2], ctmp[2], htmp[3];
|
---|
965 | int nl = (*nlp<0) ? -*nlp : *nlp; /* Actual number of layers */
|
---|
966 | double factmx = (*nlp<0) ? 1.0 : 1.4; /* Max. scale for boundary adjustment */
|
---|
967 | int show_fit;
|
---|
968 |
|
---|
969 | if ( nl < 2 || atmosphere == 0 )
|
---|
970 | return;
|
---|
971 |
|
---|
972 | /* The lowest layer boundary must not be below the lowest table entry */
|
---|
973 | /* because values can only be interpolated, not extrapolated. */
|
---|
974 | if ( hlay[0] < bottom_of_atmosphere )
|
---|
975 | hlay[0] = bottom_of_atmosphere;
|
---|
976 | /* The default layers are known to be a rather bad choice for */
|
---|
977 | /* the tropical atmosphere. Replace them with better starting values. */
|
---|
978 | if ( *nlp > 0 && atmosphere == 1 )
|
---|
979 | {
|
---|
980 | hlay[1] = 9.25e5;
|
---|
981 | hlay[2] = 19.0e5;
|
---|
982 | hlay[3] = 37.5e5;
|
---|
983 | hlay[4] = 90.0e5;
|
---|
984 | }
|
---|
985 |
|
---|
986 | if ( (h = calloc((size_t)(nl+1),sizeof(double))) == NULL ||
|
---|
987 | (s = calloc((size_t)(nl),sizeof(double))) == NULL ||
|
---|
988 | (s0 = calloc((size_t)(nl),sizeof(double))) == NULL ||
|
---|
989 | (a = calloc((size_t)(nl),sizeof(double))) == NULL ||
|
---|
990 | (b = calloc((size_t)(nl),sizeof(double))) == NULL ||
|
---|
991 | (c = calloc((size_t)(nl),sizeof(double))) == NULL )
|
---|
992 | {
|
---|
993 | return;
|
---|
994 | }
|
---|
995 |
|
---|
996 | for ( il=0; il<nl; il++ )
|
---|
997 | {
|
---|
998 | a[il] = aatm[il];
|
---|
999 | b[il] = batm[il];
|
---|
1000 | c[il] = catm[il];
|
---|
1001 | h[il] = hlay[il];
|
---|
1002 | }
|
---|
1003 | h[nl] = a[nl-1]*c[nl-1];
|
---|
1004 |
|
---|
1005 | fflush(NULL);
|
---|
1006 |
|
---|
1007 | #ifdef DEBUG_ATM_FIT
|
---|
1008 | printf("\n Parameters of atmospheric layers before the fit:\n");
|
---|
1009 | for ( il=0; il<nl; il++)
|
---|
1010 | printf(" Layer %d: %6.2f km < h < %6.2f km: a = %13.6g, b = %13.6g, c = %13.6g\n",
|
---|
1011 | il+1,h[il]/1e5,h[il+1]/1e5,a[il],b[il],c[il]);
|
---|
1012 | printf("\n");
|
---|
1013 |
|
---|
1014 | /* Functions and variables for cut-and-paste into gnuplot or C: */
|
---|
1015 |
|
---|
1016 | puts("thicka(h)=(h<h2a?a1a+b1a*exp(-h*1e5/c1a):(h<h3a?a2a+b2a*exp(-h*1e5/c2a):(h<h4a?a3a+b3a*exp(-h*1e5/c3a):(h<h5a?a4a+b4a*exp(-h*1e5/c4a):a5a-h*1e5/c5a))))");
|
---|
1017 | puts("rhoa(h)=(h<h2a?b1a/c1a*exp(-h*1e5/c1a):(h<h3a?b2a/c2a*exp(-h*1e5/c2a):(h<h4a?b3a/c3a*exp(-h*1e5/c3a):(h<h5a?b4a/c4a*exp(-h*1e5/c4a):1./c5a))))");
|
---|
1018 | for ( il=0; il<=nl;il++)
|
---|
1019 | printf("h%da=%6.2f;",il+1,h[il]/1.e5);
|
---|
1020 | printf("\n");
|
---|
1021 | for ( il=0; il<nl;il++)
|
---|
1022 | printf("a%da=%13.6g;",il+1,a[il]);
|
---|
1023 | printf("\n");
|
---|
1024 | for ( il=0; il<nl;il++)
|
---|
1025 | printf("b%da=%13.6g;",il+1,b[il]);
|
---|
1026 | printf("\n");
|
---|
1027 | for ( il=0; il<nl;il++)
|
---|
1028 | printf("c%da=%13.6g;",il+1,c[il]);
|
---|
1029 | printf("\n");
|
---|
1030 | #endif
|
---|
1031 |
|
---|
1032 | for ( il=0; il<nl-1; il++ )
|
---|
1033 | s[il] = atm_exp_fit(h[il],h[il+1],&a[il],&b[il],&c[il],&s0[il],&np);
|
---|
1034 |
|
---|
1035 | c[nl-1] = 2./rhofx_(&h[nl-1]);
|
---|
1036 | a[nl-1] = thickx_(&h[nl-1]) + h[nl-1]/c[nl-1];
|
---|
1037 | b[nl-1] = 1.;
|
---|
1038 | h[nl] = a[nl-1]*c[nl-1];
|
---|
1039 |
|
---|
1040 | if ( factmx > 1.0 ) /* Try to improve by adjusting boundaries */
|
---|
1041 | {
|
---|
1042 | #ifdef DEBUG_ATM_FIT
|
---|
1043 | printf("\n Intermediate results of atmosphere fit:\n");
|
---|
1044 | for ( il=0; il<nl; il++)
|
---|
1045 | printf(" Layer %d: %6.2f km < h < %6.2f km: a = %13.6g, b = %13.6g, c = %13.6g, s = %10.4e -> %10.4e\n",
|
---|
1046 | il+1,h[il]/1e5,h[il+1]/1e5,a[il],b[il],c[il],s0[il],s[il]);
|
---|
1047 | printf("\n");
|
---|
1048 | for ( il=0; il<=nl;il++)
|
---|
1049 | printf("h%db=%6.2f;",il+1,h[il]/1.e5);
|
---|
1050 | printf("\n");
|
---|
1051 | for ( il=0; il<nl;il++)
|
---|
1052 | printf("a%db=%13.6g;",il+1,a[il]);
|
---|
1053 | printf("\n");
|
---|
1054 | for ( il=0; il<nl;il++)
|
---|
1055 | printf("b%db=%13.6g;",il+1,b[il]);
|
---|
1056 | printf("\n");
|
---|
1057 | for ( il=0; il<nl;il++)
|
---|
1058 | printf("c%db=%13.6g;",il+1,c[il]);
|
---|
1059 | printf("\n");
|
---|
1060 | #endif
|
---|
1061 |
|
---|
1062 | for ( il=nl-3; il>=0; il-- )
|
---|
1063 | {
|
---|
1064 | double smin[2], stmp[2], s0tmp[2], hvar;
|
---|
1065 | int nptmp[2], npmin[2];
|
---|
1066 | atmp[0] = a[il]; atmp[1] = a[il+1];
|
---|
1067 | btmp[0] = b[il]; btmp[1] = b[il+1];
|
---|
1068 | ctmp[0] = c[il]; ctmp[1] = c[il+1];
|
---|
1069 | htmp[0] = h[il]; htmp[1] = h[il+1]; htmp[2] = h[il+2];
|
---|
1070 | smin[0] = atm_exp_fit(htmp[0],htmp[1],&atmp[0],&btmp[0],&ctmp[0],&s0tmp[0],&npmin[0]);
|
---|
1071 | smin[1] = atm_exp_fit(htmp[1],htmp[2],&atmp[1],&btmp[1],&ctmp[1],&s0tmp[1],&npmin[1]);
|
---|
1072 | if ( npmin[0] <= 3 || npmin[1] <= 3 )
|
---|
1073 | continue;
|
---|
1074 | for ( hvar=htmp[0]+3.e5; hvar<=htmp[2]-3.e5; hvar+=0.25e5 )
|
---|
1075 | {
|
---|
1076 | if ( hvar < h[il+1]/factmx || hvar > h[il+1]*factmx || hvar > h[il+1]+10.e5 )
|
---|
1077 | continue;
|
---|
1078 | atmp[0] = a[il]; atmp[1] = a[il+1];
|
---|
1079 | btmp[0] = b[il]; btmp[1] = b[il+1];
|
---|
1080 | ctmp[0] = c[il]; ctmp[1] = c[il+1];
|
---|
1081 | stmp[0] = atm_exp_fit(htmp[0],hvar,&atmp[0],&btmp[0],&ctmp[0],&s0tmp[0],&nptmp[0]);
|
---|
1082 | stmp[1] = atm_exp_fit(hvar,htmp[2],&atmp[1],&btmp[1],&ctmp[1],&s0tmp[1],&nptmp[1]);
|
---|
1083 | if ( nptmp[0] >= 3 && nptmp[1] >= 3 &&
|
---|
1084 | stmp[0]/(nptmp[0]-3.)+stmp[1]/(nptmp[1]-2.5) <
|
---|
1085 | smin[0]/(npmin[0]-3.)+smin[1]/(npmin[1]-2.5) )
|
---|
1086 | {
|
---|
1087 | htmp[1] = hvar;
|
---|
1088 | smin[0] = stmp[0];
|
---|
1089 | smin[1] = stmp[1];
|
---|
1090 | npmin[0] = nptmp[0];
|
---|
1091 | npmin[1] = nptmp[1];
|
---|
1092 | }
|
---|
1093 | }
|
---|
1094 | h[il+1] = htmp[1];
|
---|
1095 | }
|
---|
1096 |
|
---|
1097 | for ( il=0; il<nl-1; il++ )
|
---|
1098 | s[il] = atm_exp_fit(h[il],h[il+1],&a[il],&b[il],&c[il],&s0[il],&np);
|
---|
1099 |
|
---|
1100 | c[nl-1] = 2./rhofx_(&h[nl-1]);
|
---|
1101 | a[nl-1] = thickx_(&h[nl-1]) + h[nl-1]/c[nl-1];
|
---|
1102 | b[nl-1] = 1.;
|
---|
1103 | h[nl] = a[nl-1]*c[nl-1];
|
---|
1104 | }
|
---|
1105 |
|
---|
1106 | printf("\n Results of the atmosphere fit:\n");
|
---|
1107 | for ( il=0; il<nl; il++)
|
---|
1108 | printf(" Layer %d: %6.2f km < h < %6.2f km: a =%13.6g, b =%13.6g, c =%13.6g\n",
|
---|
1109 | il+1,h[il]/1e5,h[il+1]/1e5,a[il],b[il],c[il]);
|
---|
1110 | printf("\n");
|
---|
1111 | #ifdef DEBUG_ATM_FIT
|
---|
1112 | puts("thickb(h)=(h<h2b?a1b+b1b*exp(-h*1e5/c1b):(h<h3b?a2b+b2b*exp(-h*1e5/c2b):(h<h4b?a3b+b3b*exp(-h*1e5/c3b):(h<h5b?a4b+b4b*exp(-h*1e5/c4b):a5b-h*1e5/c5b))))");
|
---|
1113 | puts("rhob(h)=(h<h2b?b1b/c1b*exp(-h*1e5/c1b):(h<h3b?b2b/c2b*exp(-h*1e5/c2b):(h<h4b?b3b/c3b*exp(-h*1e5/c3b):(h<h5b?b4b/c4b*exp(-h*1e5/c4b):1./c5b))))");
|
---|
1114 | for ( il=0; il<=nl;il++)
|
---|
1115 | printf("h%db=%6.2f;",il+1,h[il]/1.e5);
|
---|
1116 | printf("\n");
|
---|
1117 | for ( il=0; il<nl;il++)
|
---|
1118 | printf("a%db=%13.6g;",il+1,a[il]);
|
---|
1119 | printf("\n");
|
---|
1120 | for ( il=0; il<nl;il++)
|
---|
1121 | printf("b%db=%13.6g;",il+1,b[il]);
|
---|
1122 | printf("\n");
|
---|
1123 | for ( il=0; il<nl;il++)
|
---|
1124 | printf("c%db=%13.6g;",il+1,c[il]);
|
---|
1125 | printf("\n");
|
---|
1126 | #endif
|
---|
1127 |
|
---|
1128 | #ifdef RESPECT_EGS_TOP_OF_ATMOSPHERE
|
---|
1129 | if ( h[nl] > 113e5 ) /* There was a fixed limit of 113 km in EGS part */
|
---|
1130 | {
|
---|
1131 | fflush(NULL);
|
---|
1132 | if ( h[nl-1] >= 112.99e5 )
|
---|
1133 | {
|
---|
1134 | fprintf(stderr,
|
---|
1135 | "\n Atmospheric boundaries cannot satisfy requirements for CORSIKA\n");
|
---|
1136 | fprintf(stderr," EGS part (upper end at %6.2f instead of 113.00 km)\n\n",
|
---|
1137 | h[nl]/1e5);
|
---|
1138 | }
|
---|
1139 | else
|
---|
1140 | {
|
---|
1141 | double rho;
|
---|
1142 | fprintf(stderr,"\n Upper atmospheric boundary forced from %6.2 to 113.00 km\n",
|
---|
1143 | h[nl]/1e5);
|
---|
1144 | fprintf(stderr," to satisfy requirements for CORSIKA EGS part\n");
|
---|
1145 | h[nl] = 113e5;
|
---|
1146 | rho = thickx_(&h[nl-1]) / (h[nl]-h[nl-1]);
|
---|
1147 | c[nl-1] = 1./rho;
|
---|
1148 | a[nl-1] = thickx_(&h[nl-1]) + h[nl-1]/c[nl-1];
|
---|
1149 | }
|
---|
1150 | }
|
---|
1151 | #endif
|
---|
1152 |
|
---|
1153 | for ( il=0; il<nl; il++ )
|
---|
1154 | {
|
---|
1155 | aatm[il] = a[il];
|
---|
1156 | batm[il] = b[il];
|
---|
1157 | catm[il] = c[il];
|
---|
1158 | hlay[il] = h[il];
|
---|
1159 | }
|
---|
1160 | top_of_atmosphere = h[nl] - 0.1;
|
---|
1161 |
|
---|
1162 | free(h); free(s); free(s0); free(a); free(b); free(c);
|
---|
1163 |
|
---|
1164 | /* An environment variable can be used to show fit compared to table */
|
---|
1165 | if ( getenv("SHOWFIT") != NULL )
|
---|
1166 | show_fit = atoi(getenv("SHOWFIT"));
|
---|
1167 | else
|
---|
1168 | show_fit = 1;
|
---|
1169 |
|
---|
1170 | if ( show_fit )
|
---|
1171 | {
|
---|
1172 | int ip;
|
---|
1173 | printf("\n Altitude [km] rho(table) rho(fit) thick(table) thick(fit)\n");
|
---|
1174 | for (ip=0; ip<num_prof; ip++ )
|
---|
1175 | {
|
---|
1176 | if ( p_alt[ip] >= bottom_of_atmosphere &&
|
---|
1177 | p_alt[ip] <= top_of_atmosphere )
|
---|
1178 | {
|
---|
1179 | printf(" %5.1f %12.5e %12.5e %12.5e %12.5e\n",
|
---|
1180 | p_alt[ip]/1e5, p_rho[ip], rhofx_(&p_alt[ip]),
|
---|
1181 | exp(p_log_thick[ip]), thickx_(&p_alt[ip]));
|
---|
1182 | }
|
---|
1183 | }
|
---|
1184 | printf("\n");
|
---|
1185 | if ( show_fit == 999 )
|
---|
1186 | {
|
---|
1187 | printf("Terminating now as requested by the SHOWFIT environment variable.\n");
|
---|
1188 | exit(0);
|
---|
1189 | }
|
---|
1190 | }
|
---|
1191 |
|
---|
1192 | fflush(stdout);
|
---|
1193 |
|
---|
1194 | return;
|
---|
1195 | }
|
---|