ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/gensdaymtx.c
Revision: 1.8
Committed: Sat Jun 7 05:09:45 2025 UTC (2 weeks, 5 days ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +1 -2 lines
Log Message:
refactor: Put some declarations into "paths.h" and included in "platform.h"

File Contents

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