source: trunk/MagicSoft/Simulation/Corsika/Mmcs614/atmo.c@ 10099

Last change on this file since 10099 was 1444, checked in by blanch, 22 years ago
*** empty log message ***
File size: 38.4 KB
Line 
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)
59typedef float cors_real_now_t;
60#else
61typedef 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 !!) */
67void atmset_(int *iatmo, double *obslev);
68double rhofx_(double *height);
69double thickx_(double *height);
70double refidx_(double *height);
71double heighx_(double *thick);
72void 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);
74void atmfit_(int *nlp, double *hlay, double *aatm, double *batm, double *catm);
75
76/* C called functions (parameter types are always checked) */
77static void interp(double x, double *v, int n, int *ipl, double *rpl);
78double rpol(double *x, double *y, int n, double xp);
79static void init_refraction_tables(void);
80static void init_corsika_atmosphere(void);
81static void init_atmosphere(void);
82static double sum_log_dev_sq(double a, double b, double c, int np,
83 double *h, double *t, double *rho);
84static 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
90int atmosphere;
91static int num_prof;
92static double p_alt[MAX_PROFILE], p_log_alt[MAX_PROFILE];
93static double p_log_rho[MAX_PROFILE], p_rho[MAX_PROFILE];
94static double p_log_thick[MAX_PROFILE];
95static double p_log_n1[MAX_PROFILE];
96static double p_bend_ray_hori_a[MAX_PROFILE];
97static double p_bend_ray_time0[MAX_PROFILE];
98static double p_bend_ray_time_a[MAX_PROFILE];
99
100static double top_of_atmosphere = 112.8e5;
101static double bottom_of_atmosphere = 0.;
102
103#ifdef FAST_INTERPOLATION
104#define MAX_FAST_PROFILE 1000
105static double fast_p_alt[MAX_FAST_PROFILE];
106static double fast_p_log_rho[MAX_FAST_PROFILE];
107static double fast_p_log_thick[MAX_FAST_PROFILE];
108static double fast_p_log_n1[MAX_FAST_PROFILE];
109static 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
139static 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
230double 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
241static double etadsn; /**< About the same as in CORSIKA Cherenkov function */
242 /**< (but doesn't need to be the same). */
243static double observation_level; /**< Altitude [cm] of observation level */
244static double obs_level_refidx;
245static 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
256static 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
341static 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
373static 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
448static 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
558void 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
578double 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
609double 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
640double 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
670double 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
706void 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
734fflush(NULL);
735printf(" 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
750printf(" raybnd: horizontal displacement = %5.2f\n",hori_off);
751printf(" 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
786static 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
821static 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
960void 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}
Note: See TracBrowser for help on using the repository browser.