| 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 | } | 
|---|