ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/gensdaymtx.c
Revision: 1.6
Committed: Tue Apr 15 20:15:50 2025 UTC (2 weeks, 3 days ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.5: +739 -727 lines
Log Message:
fix(gensdaymtx): Added WAVELENGTH_SPLITS= to header output and code cleanup by TW

File Contents

# User Rev Content
1 greg 1.2 #ifndef lint
2 greg 1.5 static const char RCSid[] = "$Id: gensdaymtx.c,v 1.4 2024/08/08 02:00:20 greg Exp $";
3 greg 1.2 #endif
4 greg 1.5
5     #include <stdlib.h>
6 greg 1.1 #include <ctype.h>
7     #ifdef _WIN32
8     #include <windows.h>
9     #else
10     #include <errno.h>
11     #include <sys/stat.h>
12     #include <sys/types.h>
13     #endif
14    
15 greg 1.5 #include "atmos.h"
16     #include "copyright.h"
17     #include "data.h"
18     #include "platform.h"
19     #include "rtio.h"
20     #include "rtmath.h"
21     #include "sun.h"
22     #include "loadEPW.h"
23    
24    
25 greg 1.6 const double SUN_ANG_DEG = 0.533; /* sun full-angle in degrees */
26     const double ARCTIC_LAT = 67.;
27     const double TROPIC_LAT = 23.;
28     const int SUMMER_START = 4;
29     const int SUMMER_END = 9;
30     const double GNORM = 0.777778;
31 greg 1.1
32     /* Mean normalized relative daylight spectra where CCT = 6415K for overcast */
33 greg 1.6 const double D6415[NSSAMP] = {
34     0.63231, 1.06171, 1.00779, 1.36423, 1.34133,
35     1.27258, 1.26276, 1.26352, 1.22201, 1.13246,
36     1.0434, 1.05547, 0.98212, 0.94445, 0.9722,
37     0.82387, 0.87853, 0.82559, 0.75111, 0.78925};
38    
39     enum {
40     NSUNPATCH = 4 /* max. # patches to spread sun into */
41     };
42    
43     char *progname;
44     double altitude; /* Solar altitude (radians) */
45     double azimuth; /* Solar azimuth (radians) */
46     int julian_date; /* Julian date */
47     double sun_zenith; /* Sun zenith angle (radians) */
48     int nskypatch; /* number of Reinhart patches */
49     float *rh_palt; /* sky patch altitudes (radians) */
50     float *rh_pazi; /* sky patch azimuths (radians) */
51     float *rh_dom; /* sky patch solid angle (sr) */
52     FVECT sundir;
53     double sun_ct; /* cos(theta) of sun altitude angle */
54    
55     int input = 0; /* Input type */
56     int output = 0; /* Output type */
57     int nsuns = NSUNPATCH; /* number of sun patches to use */
58     double fixed_sun_sa = -1; /* fixed solid angle per sun? */
59     int verbose = 0; /* progress reports to stderr? */
60     int outfmt = 'a'; /* output format */
61     int rhsubdiv = 1; /* Reinhart sky subdivisions */
62     COLOR skycolor = {.96, 1.004, 1.118}; /* sky coloration */
63     COLOR suncolor = {1., 1., 1.}; /* sun color */
64     double grefl = .2; /* ground reflectance */
65 greg 1.1
66    
67 greg 1.6 static inline double deg_to_rad(double deg)
68     {
69     return deg * (PI / 180.);
70     }
71 greg 1.1
72 greg 1.6 static inline double rad_to_deg(double rad)
73     {
74     return rad * (180. / PI);
75     }
76 greg 1.1
77 greg 1.6 static inline void vectorize(double altitude, double azimuth, FVECT v)
78     {
79     v[1] = cos(altitude);
80     v[0] = (v)[1] * sin(azimuth);
81     v[1] *= cos(azimuth);
82     v[2] = sin(altitude);
83     }
84 greg 1.1
85 greg 1.6 static inline double wmean2(const double a, const double b, const double x)
86     {
87     return a * (1 - x) + b * x;
88     }
89 greg 1.1
90 greg 1.6 static inline double wmean(
91     const double a, const double x,
92     const double b, const double y)
93     {
94     return (a * x + b * y) / (a + b);
95 greg 1.1 }
96    
97 greg 1.6 static int make_directory(const char *path)
98     {
99 greg 1.1 #ifdef _WIN32
100 greg 1.6 if (CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) {
101     return 1;
102     }
103     return 0;
104 greg 1.1 #else
105 greg 1.6 if (mkdir(path, 0777) == 0 || errno == EEXIST) {
106     return 1;
107     }
108     return 0;
109 greg 1.1 #endif
110     }
111    
112 greg 1.6 static const char *getfmtname(int fmt)
113     {
114     switch (fmt) {
115     case 'a':
116     return ("ascii");
117     case 'f':
118     return ("float");
119     case 'd':
120     return ("double");
121     }
122     return ("unknown");
123 greg 1.1 }
124    
125    
126 greg 1.6 static double get_overcast_zenith_brightness(const double sundir[3])
127     {
128     double zenithbr;
129     if (sundir[2] < 0) {
130     zenithbr = 0;
131     } else {
132     zenithbr = (8.6 * sundir[2] + .123) * 1000.0 / D65EFFICACY;
133     }
134     return zenithbr;
135 greg 1.1 }
136    
137 greg 1.5
138 greg 1.1 /* from gensky.c */
139 greg 1.6 static double get_overcast_brightness(const double dz, const double zenithbr)
140     {
141 greg 1.1 double groundbr = zenithbr * GNORM;
142 greg 1.6 return wmean(pow(dz + 1.01, 10),
143     zenithbr * (1 + 2 * dz) / 3,
144     pow(dz + 1.01, -10), groundbr);
145 greg 1.1 }
146    
147 greg 1.6 double solar_sunset(int month, int day)
148 greg 1.5 {
149     float W;
150     W = -1 * (tan(s_latitude) * tan(sdec(jdate(month, day))));
151 greg 1.6 return 12 + (PI / 2 - atan2(W, sqrt(1 - W * W))) * 180 / (PI * 15);
152 greg 1.5 }
153    
154    
155 greg 1.6 double solar_sunrise(int month, int day)
156 greg 1.5 {
157     float W;
158     W = -1 * (tan(s_latitude) * tan(sdec(jdate(month, day))));
159 greg 1.6 return 12 - (PI / 2 - atan2(W, sqrt(1 - W * W))) * 180 / (PI * 15);
160 greg 1.5 }
161    
162 greg 1.6 int rh_init(void)
163     {
164 greg 1.1 #define NROW 7
165 greg 1.6 static const int tnaz[NROW] = {30, 30, 24, 24, 18, 12, 6};
166     const double alpha = (PI / 2.) / (NROW * rhsubdiv + .5);
167     int p, i, j;
168     /* allocate patch angle arrays */
169     nskypatch = 0;
170     for (p = 0; p < NROW; p++)
171     nskypatch += tnaz[p];
172     nskypatch *= rhsubdiv * rhsubdiv;
173     nskypatch += 2;
174     rh_palt = (float *)malloc(sizeof(float) * nskypatch);
175     rh_pazi = (float *)malloc(sizeof(float) * nskypatch);
176     rh_dom = (float *)malloc(sizeof(float) * nskypatch);
177     if ((rh_palt == NULL) | (rh_pazi == NULL) | (rh_dom == NULL)) {
178     fprintf(stderr, "%s: out of memory in rh_init()\n", progname);
179     exit(1);
180     }
181     rh_palt[0] = -PI / 2.; /* ground & zenith patches */
182     rh_pazi[0] = 0.;
183     rh_dom[0] = 2. * PI;
184     rh_palt[nskypatch - 1] = PI / 2.;
185     rh_pazi[nskypatch - 1] = 0.;
186     rh_dom[nskypatch - 1] = 2. * PI * (1. - cos(alpha * .5));
187     p = 1; /* "normal" patches */
188     for (i = 0; i < NROW * rhsubdiv; i++) {
189     const float ralt = alpha * (i + .5);
190     const int ninrow = tnaz[i / rhsubdiv] * rhsubdiv;
191     const float dom =
192     2. * PI * (sin(alpha * (i + 1)) - sin(alpha * i)) / (double)ninrow;
193     for (j = 0; j < ninrow; j++) {
194     rh_palt[p] = ralt;
195     rh_pazi[p] = 2. * PI * j / (double)ninrow;
196     rh_dom[p++] = dom;
197     }
198     }
199     return nskypatch;
200 greg 1.1 #undef NROW
201     }
202    
203     /* Resize daylight matrix (GW) */
204 greg 1.6 float *resize_dmatrix(float *mtx_data, int nsteps, int npatch)
205     {
206     if (mtx_data == NULL)
207     mtx_data = (float * ) malloc(sizeof(float) * NSSAMP * nsteps * npatch);
208     else
209     mtx_data = (float * ) realloc(mtx_data,
210     sizeof(float) * NSSAMP * nsteps * npatch);
211     if (mtx_data == NULL) {
212     fprintf(stderr,
213     "%s: out of memory in resize_dmatrix(%d,%d)\n",
214     progname, nsteps, npatch);
215     exit(1);
216 greg 1.1 }
217 greg 1.6 return mtx_data;
218     }
219    
220     static Atmosphere init_atmos(const double aod, const double grefl)
221     {
222     Atmosphere atmos = {
223     .ozone_density = {
224     .layers = {
225     {
226     .width = 25000.0,
227     .exp_term = 0.0,
228     .exp_scale = 0.0,
229     .linear_term = 1.0 / 15000.0,
230     .constant_term = -2.0 / 3.0
231     },
232     {
233     .width = AH,
234     .exp_term = 0.0,
235     .exp_scale = 0.0,
236     .linear_term = -1.0 / 15000.0,
237     .constant_term = 8.0 / 3.0
238     },
239     }
240     },
241     .rayleigh_density = {
242     .layers = {
243     {
244     .width = AH,
245     .exp_term = 1.0,
246     .exp_scale = -1.0 / HR_MS,
247     .linear_term = 0.0,
248     .constant_term = 0.0
249     },
250     }
251     },
252     .beta_r0 = BR0_MS,
253     .beta_scale = aod / AOD0_CA,
254     .beta_m = NULL,
255     .grefl = grefl
256     };
257     return atmos;
258     }
259    
260     static DpPaths get_dppaths(const char *dir, const double aod,
261     const char *mname, const char *tag)
262     {
263     DpPaths paths;
264    
265     snprintf(paths.tau, PATH_MAX, "%s%ctau_%s_%s_%.2f.dat",
266     dir, DIRSEP, tag, mname, aod);
267     snprintf(paths.scat, PATH_MAX, "%s%cscat_%s_%s_%.2f.dat",
268     dir, DIRSEP, tag, mname, aod);
269     snprintf(paths.scat1m, PATH_MAX, "%s%cscat1m_%s_%s_%.2f.dat",
270     dir, DIRSEP, tag, mname, aod);
271     snprintf(paths.irrad, PATH_MAX, "%s%cirrad_%s_%s_%.2f.dat",
272     dir, DIRSEP, tag, mname, aod);
273    
274     return paths;
275 greg 1.1 }
276    
277 greg 1.6
278     static void set_rayleigh_density_profile(Atmosphere *atmos,
279     char *tag, const int is_summer, const double s_latitude)
280     {
281     /* Set rayleigh density profile */
282     if (fabs(s_latitude * 180.0 / PI) > ARCTIC_LAT) {
283     tag[0] = 's';
284     if (is_summer) {
285     tag[1] = 's';
286     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_SS;
287     atmos->beta_r0 = BR0_SS;
288     } else {
289     tag[1] = 'w';
290     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_SW;
291     atmos->beta_r0 = BR0_SW;
292     }
293     } else if (fabs(s_latitude * 180.0 / PI) > TROPIC_LAT) {
294     tag[0] = 'm';
295     if (is_summer) {
296     tag[1] = 's';
297     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_MS;
298     atmos->beta_r0 = BR0_MS;
299     } else {
300     tag[1] = 'w';
301     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_MW;
302     atmos->beta_r0 = BR0_MW;
303     }
304     } else {
305     tag[0] = 't';
306     tag[1] = 'r';
307     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_T;
308     atmos->beta_r0 = BR0_T;
309     }
310     tag[2] = '\0';
311 greg 1.1 }
312 greg 1.5
313 greg 1.6
314 greg 1.1 /* Add in solar direct to nearest sky patches (GW) */
315 greg 1.6 void add_direct(DATARRAY *tau, DATARRAY *scat, DATARRAY *scat1m, DATARRAY *irrad,
316     double ccover, double dirnorm, float *parr)
317     {
318     FVECT svec;
319     double near_dprod[NSUNPATCH];
320     int near_patch[NSUNPATCH];
321     double wta[NSUNPATCH], wtot;
322     int i, j, p;
323    
324     /* identify nsuns closest patches */
325     for (i = nsuns; i--;)
326     near_dprod[i] = -1.;
327     vectorize(altitude, azimuth, svec);
328     for (p = 1; p < nskypatch; p++) {
329     FVECT pvec;
330     double dprod;
331     vectorize(rh_palt[p], rh_pazi[p], pvec);
332     dprod = DOT(pvec, svec);
333     for (i = 0; i < nsuns; i++)
334     if (dprod > near_dprod[i]) {
335     for (j = nsuns; --j > i;) {
336     near_dprod[j] = near_dprod[j - 1];
337     near_patch[j] = near_patch[j - 1];
338     }
339     near_dprod[i] = dprod;
340     near_patch[i] = p;
341     break;
342     }
343     }
344     /* Get solar radiance */
345     double sun_radiance[NSSAMP] = {0};
346     get_solar_radiance(tau, scat, scat1m, sundir, ER, sun_ct, sun_radiance);
347     if (ccover > 0) {
348     double zenithbr = get_overcast_zenith_brightness(sundir);
349     double skybr = get_overcast_brightness(sundir[2], zenithbr);
350     int l;
351     for (l = 0; l < NSSAMP; ++l) {
352     sun_radiance[l] = wmean2(sun_radiance[l], D6415[l] * skybr / WVLSPAN, ccover);
353     }
354     }
355     /* Normalize */
356     double sum = 0.0;
357     for (i = 0; i < NSSAMP; ++i) {
358     sum += sun_radiance[i];
359     }
360     double mean = sum / NSSAMP;
361    
362     double intensity = mean * WVLSPAN;
363     if (dirnorm > 0) {
364     intensity = dirnorm / SOLOMG / WHTEFFICACY;
365     }
366     double dir_ratio = 1.;
367     if (mean > 0)
368     dir_ratio = intensity / mean;
369     for (i = 0; i < NSSAMP; ++i) {
370     sun_radiance[i] *= dir_ratio;
371     }
372    
373     /* weight by proximity */
374     wtot = 0;
375     for (i = nsuns; i--;)
376     wtot += wta[i] = 1. / (1.002 - near_dprod[i]);
377     /* add to nearest patch radiances */
378     for (i = nsuns; i--;) {
379     float *pdest = parr + NSSAMP * near_patch[i];
380     int k;
381     for (k = 0; k < NSSAMP; k++) {
382     *pdest++ = sun_radiance[k] * wta[i] / wtot;
383     }
384     }
385     }
386    
387    
388     void calc_sky_patch_radiance(DATARRAY *scat, DATARRAY *scat1m,
389     DATARRAY *irrad_clear, double ccover, double dif_ratio,
390     double overcast_zenithbr, FVECT view_point, float *parr)
391     {
392     double mu_sky; /* Sun-sky point azimuthal angle */
393     double sspa; /* Sun-sky point angle */
394     int i;
395     for (i = 1; i < nskypatch; i++) {
396     FVECT rdir_sky;
397     vectorize(rh_palt[i], rh_pazi[i], rdir_sky);
398     mu_sky = fdot(view_point, rdir_sky) / ER;
399     sspa = fdot(rdir_sky, sundir);
400    
401     SCOLOR sky_radiance = {0};
402     get_sky_radiance(scat, scat1m, ER, mu_sky, sun_ct, sspa, sky_radiance);
403     int k;
404     for (k = 0; k < NSSAMP; ++k) {
405     sky_radiance[k] *= WVLSPAN;
406     }
407    
408     if (ccover > 0) {
409     double skybr = get_overcast_brightness(rdir_sky[2], overcast_zenithbr);
410     for (k = 0; k < NSSAMP; ++k) {
411     sky_radiance[k] = wmean2(sky_radiance[k], skybr * D6415[k], ccover);
412     }
413     }
414    
415     /* calibration */
416     for (k = 0; k < NSSAMP; ++k) {
417     sky_radiance[k] *= dif_ratio;
418     }
419    
420     for (k = 0; k < NSSAMP; ++k) {
421     parr[NSSAMP * i + k] = sky_radiance[k];
422     }
423     }
424 greg 1.1 }
425    
426    
427     /* Compute sky patch radiance values (modified by GW) */
428     void compute_sky(DATARRAY *tau, DATARRAY *scat, DATARRAY *scat1m,
429 greg 1.6 DATARRAY *irrad, double ccover, double difhor, FVECT view_point, float *parr)
430     {
431     float sun_zenith;
432     SCOLOR sky_radiance = {0};
433     SCOLOR ground_radiance = {0};
434     SCOLR sky_sclr = {0};
435     SCOLR ground_sclr = {0};
436     const double radius = VLEN(view_point);
437     const double sun_ct = fdot(view_point, sundir) / radius;
438     const FVECT rdir_grnd = {0, 0, -1};
439     const double mu_grnd = fdot(view_point, rdir_grnd) / radius;
440     const double nu_grnd = fdot(rdir_grnd, sundir);
441    
442     /* Calculate sun zenith angle (don't let it dip below horizon) */
443     /* Also limit minimum angle to keep circumsolar off zenith */
444     if (altitude <= 0.0)
445     sun_zenith = deg_to_rad(90.0);
446     else if (altitude >= deg_to_rad(87.0))
447     sun_zenith = deg_to_rad(3.0);
448     else
449     sun_zenith = deg_to_rad(90.0) - altitude;
450    
451     double overcast_zenithbr = get_overcast_zenith_brightness(sundir);
452    
453     /* diffuse calibration factor */
454     double dif_ratio = 1;
455     if (difhor > 0) {
456     DATARRAY *indirect_irradiance_clear = get_indirect_irradiance(irrad, radius, sun_ct);
457     double overcast_ghi = overcast_zenithbr * 7.0 * PI / 9.0;
458     double diffuse_irradiance = 0;
459     int l;
460     for (l = 0; l < NSSAMP; ++l) {
461     diffuse_irradiance += indirect_irradiance_clear->arr.d[l] * 20; /* 20nm interval */
462     }
463     /* free(indirect_irradiance_clear); */
464     diffuse_irradiance = wmean2(diffuse_irradiance, overcast_ghi, ccover);
465     if (diffuse_irradiance > 0) {
466     dif_ratio = difhor / WHTEFFICACY / diffuse_irradiance / 1.15; /* fudge */
467     }
468     }
469    
470     /* Compute ground radiance (include solar contribution if any) */
471     get_ground_radiance(tau, scat, scat1m, irrad, view_point, rdir_grnd, radius,
472     mu_grnd, sun_ct, nu_grnd, grefl, sundir, parr);
473     int j;
474     for (j = 0; j < NSSAMP; j++) {
475     parr[j] *= WVLSPAN;
476     }
477     calc_sky_patch_radiance(scat, scat1m, irrad, ccover, dif_ratio, overcast_zenithbr, view_point, parr);
478     }
479    
480     int main(int argc, char *argv[])
481     {
482     EPWheader *epw = NULL; /* EPW/WEA input file */
483     EPWrecord erec; /* current EPW/WEA input record */
484     int doheader = 1; /* output header? */
485     double rotation = 0.0; /* site rotation (degrees) */
486     double elevation = 0; /* site elevation (meters) */
487     int leap_day = 0; /* add leap day? */
488     int sun_hours_only = 0; /* only output sun hours? */
489     int dir_is_horiz; /* direct is meas. on horizontal? */
490     int ntsteps = 0; /* number of time steps */
491     int tstorage = 0; /* number of allocated time steps */
492     int nstored = 0; /* number of time steps in matrix */
493     int last_monthly = 0; /* month of last report */
494     double dni; /* direct normal illuminance */
495     double dhi; /* diffuse horizontal illuminance */
496    
497     float *mtx_data = NULL;
498     int mtx_offset = 0;
499     double timeinterval = 0;
500     char lstag[3];
501     char *mie_path = getpath("mie_ca.dat", getrlibpath(), R_OK);
502     char *ddir = ".";
503     char mie_name[20] = "mie_ca";
504     int num_threads = 1;
505     int sorder = 4;
506     int solar_only = 0;
507     int sky_only = 0;
508     int i, j;
509     FVECT view_point = {0, 0, ER};
510    
511     progname = argv[0];
512    
513     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
514     switch (argv[i][1]) {
515     case 'd': /* solar (direct) only */
516     solar_only = 1;
517     break;
518     case 's': /* sky only (no direct) */
519     sky_only = 1;
520     break;
521     case 'g':
522     grefl = atof(argv[++i]);
523     break;
524     case 'm':
525     rhsubdiv = atoi(argv[++i]);
526     break;
527     case 'n':
528     num_threads = atoi(argv[++i]);
529     break;
530     case 'r': /* rotate distribution */
531     if (argv[i][2] && argv[i][2] != 'z')
532     goto userr;
533     rotation = atof(argv[++i]);
534     break;
535     case 'u': /* solar hours only */
536     sun_hours_only = 1;
537     break;
538     case 'p':
539     ddir = argv[++i];
540     break;
541     case 'v': /* verbose progress reports */
542     verbose++;
543     break;
544     case 'h': /* turn off header */
545     doheader = 0;
546     break;
547     case '5': /* 5-phase calculation */
548     nsuns = 1;
549     fixed_sun_sa = PI / 360. * atof(argv[++i]);
550     if (fixed_sun_sa <= 0) {
551     fprintf(
552     stderr,
553     "%s: missing solar disk size argument for '-5' option\n",
554     progname);
555     exit(1);
556     }
557     fixed_sun_sa *= fixed_sun_sa * PI;
558     break;
559     case 'i':
560     timeinterval = atof(argv[++i]);
561     break;
562     case 'o': /* output format */
563     switch (argv[i][2]) {
564     case 'f':
565     case 'd':
566     case 'a':
567     outfmt = argv[i][2];
568     break;
569     default:
570     goto userr;
571     }
572     break;
573     default:
574     goto userr;
575     }
576     }
577     if (i < argc - 1)
578     goto userr;
579    
580     epw = EPWopen(argv[i]);
581     if (epw == NULL)
582     exit(1);
583     if (i == argc - 1 && freopen(argv[i], "r", stdin) == NULL) {
584     fprintf(stderr, "%s: cannot open '%s' for input\n", progname, argv[i]);
585     exit(1);
586     }
587     if (verbose) {
588     if (i == argc - 1)
589     fprintf(stderr, "%s: reading weather tape '%s'\n", progname, argv[i]);
590     else
591     fprintf(stderr, "%s: reading weather tape from <stdin>\n", progname);
592     }
593     s_latitude = epw->loc.latitude;
594     s_longitude = -epw->loc.longitude;
595     s_meridian = -15.*epw->loc.timezone;
596     elevation = epw->loc.elevation;
597     switch (epw->isWEA) { /* translate units */
598     case WEAnot:
599     case WEAradnorm:
600     input = 1; /* radiometric quantities */
601     dir_is_horiz = 0; /* direct is perpendicular meas. */
602     break;
603     case WEAradhoriz:
604     input = 1; /* radiometric quantities */
605     dir_is_horiz = 1; /* solar measured horizontally */
606     break;
607     case WEAphotnorm:
608     input = 2; /* photometric quantities */
609     dir_is_horiz = 0; /* direct is perpendicular meas. */
610     break;
611     default:
612     goto fmterr;
613     }
614    
615     rh_init();
616    
617     if (verbose) {
618     fprintf(stderr, "%s: location '%s %s'\n", progname, epw->loc.city, epw->loc.country);
619     fprintf(
620     stderr,
621     "%s: (lat,long)=(%.1f,%.1f) degrees north, west\n",
622     progname, s_latitude, s_longitude);
623     if (rotation != 0)
624     fprintf(stderr, "%s: rotating output %.0f degrees\n", progname, rotation);
625     }
626    
627     s_latitude = deg_to_rad(s_latitude);
628     s_longitude = deg_to_rad(s_longitude);
629     s_meridian = deg_to_rad(s_meridian);
630     /* initial allocation */
631     mtx_data = resize_dmatrix(mtx_data, tstorage = 2, nskypatch);
632    
633     /* Load mie density data */
634     DATARRAY *mie_dp = getdata(mie_path);
635     if (mie_dp == NULL) {
636     fprintf(stderr, "Error reading mie data\n");
637     return 0;
638     }
639    
640     if (epw->isWEA == WEAnot) {
641     fprintf(stderr, "EPW input\n");
642     } else if (epw->isWEA != WEAphotnorm) {
643     fprintf(stderr, "need WEA in photopic unit\n");
644     exit(1);
645     }
646    
647     while ((j = EPWread(epw, &erec)) > 0) {
648     const int mo = erec.date.month+1;
649     const int da = erec.date.day;
650     const double hr = erec.date.hour;
651     double aod = erec.optdepth;
652     double cc = erec.skycover;
653     double sda, sta, st;
654     int sun_in_sky;
655    
656     if (aod == 0.0) {
657     aod = AOD0_CA;
658     fprintf(stderr, "aod is zero, using default value %.3f\n", AOD0_CA);
659     }
660     /* compute solar position */
661     if ((mo == 2) & (da == 29)) {
662     julian_date = 60;
663     leap_day = 1;
664     } else
665     julian_date = jdate(mo, da) + leap_day;
666     sda = sdec(julian_date);
667     sta = stadj(julian_date);
668     st = hr + sta;
669 greg 1.5 if (timeinterval > 0) {
670     if (fabs(solar_sunrise(mo, da) - st) <= timeinterval/120)
671     st = (st + timeinterval/120 + solar_sunrise(mo, da))/2;
672     else if (fabs(solar_sunset(mo, da) - st) < timeinterval/120)
673     st = (st - timeinterval/120 + solar_sunset(mo, da))/2;
674     }
675 greg 1.6 altitude = salt(sda, st);
676     sun_in_sky = (altitude > -deg_to_rad(SUN_ANG_DEG / 2.));
677    
678     azimuth = sazi(sda, st) + PI - deg_to_rad(rotation);
679    
680     vectorize(altitude, azimuth, sundir);
681     if (sun_hours_only && !sun_in_sky) {
682     continue; /* skipping nighttime points */
683     }
684     sun_ct = fdot(view_point, sundir) / ER;
685 greg 1.5
686 greg 1.6 dni = erec.dirillum;
687     dhi = erec.diffillum;
688 greg 1.5
689 greg 1.6 mtx_offset = NSSAMP * nskypatch * nstored;
690     nstored += 1;
691    
692     /* make space for next row */
693     if (nstored > tstorage) {
694     tstorage += (tstorage >> 1) + nstored + 7;
695     mtx_data = resize_dmatrix(mtx_data, tstorage, nskypatch);
696     }
697     ntsteps++; /* keep count of time steps */
698    
699     /* compute sky patch values */
700     Atmosphere clear_atmos = init_atmos(aod, grefl);
701     int is_summer = (mo >= SUMMER_START && mo <= SUMMER_END);
702     if (s_latitude < 0) {
703     is_summer = !is_summer;
704     }
705     set_rayleigh_density_profile(&clear_atmos, lstag, is_summer, s_latitude);
706    
707     clear_atmos.beta_m = mie_dp;
708    
709     char gsdir[PATH_MAX];
710     size_t siz = strlen(ddir);
711     if (ISDIRSEP(ddir[siz - 1]))
712     ddir[siz - 1] = '\0';
713     snprintf(gsdir, PATH_MAX, "%s%catmos_data", ddir, DIRSEP);
714     if (!make_directory(gsdir)) {
715     fprintf(stderr, "Failed creating atmos_data directory");
716     exit(1);
717     }
718     DpPaths clear_paths = get_dppaths(gsdir, aod, mie_name, lstag);
719    
720     if (getpath(clear_paths.tau, ".", R_OK) == NULL ||
721     getpath(clear_paths.scat, ".", R_OK) == NULL ||
722     getpath(clear_paths.scat1m, ".", R_OK) == NULL ||
723     getpath(clear_paths.irrad, ".", R_OK) == NULL) {
724     fprintf(stderr, "# Pre-computing...\n");
725     if (!precompute(sorder, clear_paths, &clear_atmos, num_threads)) {
726     fprintf(stderr, "Pre-compute failed\n");
727     return 0;
728     }
729     }
730    
731     DATARRAY *tau_clear_dp = getdata(clear_paths.tau);
732     DATARRAY *irrad_clear_dp = getdata(clear_paths.irrad);
733     DATARRAY *scat_clear_dp = getdata(clear_paths.scat);
734     DATARRAY *scat1m_clear_dp = getdata(clear_paths.scat1m);
735    
736     if (!solar_only)
737     compute_sky(tau_clear_dp, scat_clear_dp, scat1m_clear_dp, irrad_clear_dp,
738     cc, dhi, view_point, mtx_data + mtx_offset);
739     if (!sky_only)
740     add_direct(tau_clear_dp, scat_clear_dp, scat1m_clear_dp, irrad_clear_dp,
741     cc, dni, mtx_data + mtx_offset);
742     /* monthly reporting */
743     if (verbose && mo != last_monthly)
744     fprintf(stderr, "%s: stepping through month %d...\n", progname,
745     last_monthly = mo);
746     }
747     if (j != EOF) {
748     fprintf(stderr, "%s: error on input\n", progname);
749     exit(1);
750     }
751     EPWclose(epw); epw = NULL;
752     freedata(mie_dp);
753     if (!ntsteps) {
754     fprintf(stderr, "%s: no valid time steps on input\n", progname);
755     exit(1);
756     }
757     /* write out matrix */
758     if (outfmt != 'a')
759     SET_FILE_BINARY(stdout);
760 greg 1.1 #ifdef getc_unlocked
761 greg 1.6 flockfile(stdout);
762 greg 1.1 #endif
763 greg 1.6 if (verbose)
764     fprintf(stderr, "%s: writing %smatrix with %d time steps...\n", progname,
765     outfmt == 'a' ? "" : "binary ", nstored);
766     if (doheader) {
767     newheader("RADIANCE", stdout);
768     printargs(argc, argv, stdout);
769     printf("LATLONG= %.8f %.8f\n", rad_to_deg(s_latitude),
770     -rad_to_deg(s_longitude));
771     printf("NROWS=%d\n", nskypatch);
772     printf("NCOLS=%d\n", nstored);
773     printf("NCOMP=%d\n", NSSAMP);
774     float wvsplit[4] = {380, 480, 588, 780};
775     fputwlsplit(wvsplit, stdout);
776     if ((outfmt == 'f') | (outfmt == 'd'))
777     fputendian(stdout);
778     fputformat((char *)getfmtname(outfmt), stdout);
779     putchar('\n');
780     }
781     /* patches are rows (outer sort) */
782     for (i = 0; i < nskypatch; i++) {
783     mtx_offset = NSSAMP * i;
784     switch (outfmt) {
785     case 'a':
786     for (j = 0; j < nstored; j++) {
787     int k;
788     for (k = 0; k < NSSAMP; k++) {
789     printf("%.3g ", mtx_data[mtx_offset + k]);
790     }
791     printf("\n");
792     mtx_offset += NSSAMP * nskypatch;
793     }
794     if (nstored > 1)
795     fputc('\n', stdout);
796     break;
797     case 'f':
798     for (j = 0; j < nstored; j++) {
799     putbinary(mtx_data + mtx_offset, sizeof(float), NSSAMP, stdout);
800     mtx_offset += NSSAMP * nskypatch;
801     }
802     break;
803     case 'd':
804     for (j = 0; j < nstored; j++) {
805     double ment[NSSAMP];
806     for (j = 0; j < NSSAMP; j++)
807     ment[j] = mtx_data[mtx_offset + j];
808     putbinary(ment, sizeof(double), NSSAMP, stdout);
809     mtx_offset += NSSAMP * nskypatch;
810     }
811     break;
812     }
813     if (ferror(stdout))
814     goto writerr;
815     }
816     return 0;
817 greg 1.1 userr:
818 greg 1.6 fprintf(stderr,
819     "Usage: %s [-v][-h][-A][-d|-s|-n][-u][-D file [-M modfile]][-r "
820     "deg][-m N][-g r g b][-c r g b][-o{f|d}][-O{0|1}] [tape.wea]\n",
821     progname);
822     exit(1);
823 greg 1.1 fmterr:
824 greg 1.6 fprintf(stderr, "%s: weather tape format error in header\n", progname);
825     exit(1);
826 greg 1.1 writerr:
827 greg 1.6 fprintf(stderr, "%s: write error on output\n", progname);
828     exit(1);
829 greg 1.1 }