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, 1 day 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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: gensdaymtx.c,v 1.7 2025/06/06 19:11:21 greg Exp $";
3 #endif
4
5 #include <stdlib.h>
6 #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 #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 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
32 /* Mean normalized relative daylight spectra where CCT = 6415K for overcast */
33 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
65
66 static inline double deg_to_rad(double deg)
67 {
68 return deg * (PI / 180.);
69 }
70
71 static inline double rad_to_deg(double rad)
72 {
73 return rad * (180. / PI);
74 }
75
76 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
84 static inline double wmean2(const double a, const double b, const double x)
85 {
86 return a * (1 - x) + b * x;
87 }
88
89 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 }
95
96 static int make_directory(const char *path)
97 {
98 #ifdef _WIN32
99 if (CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) {
100 return 1;
101 }
102 return 0;
103 #else
104 if (mkdir(path, 0777) == 0 || errno == EEXIST) {
105 return 1;
106 }
107 return 0;
108 #endif
109 }
110
111 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 }
123
124
125 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 }
135
136
137 /* from gensky.c */
138 static double get_overcast_brightness(const double dz, const double zenithbr)
139 {
140 double groundbr = zenithbr * GNORM;
141 return wmean(pow(dz + 1.01, 10),
142 zenithbr * (1 + 2 * dz) / 3,
143 pow(dz + 1.01, -10), groundbr);
144 }
145
146 double solar_sunset(int month, int day)
147 {
148 float W;
149 W = -1 * (tan(s_latitude) * tan(sdec(jdate(month, day))));
150 return 12 + (PI / 2 - atan2(W, sqrt(1 - W * W))) * 180 / (PI * 15);
151 }
152
153
154 double solar_sunrise(int month, int day)
155 {
156 float W;
157 W = -1 * (tan(s_latitude) * tan(sdec(jdate(month, day))));
158 return 12 - (PI / 2 - atan2(W, sqrt(1 - W * W))) * 180 / (PI * 15);
159 }
160
161 int rh_init(void)
162 {
163 #define NROW 7
164 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 #undef NROW
200 }
201
202 /* Resize daylight matrix (GW) */
203 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 }
216 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 }
275
276
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 }
311
312
313 /* Add in solar direct to nearest sky patches (GW) */
314 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 }
424
425
426 /* Compute sky patch radiance values (modified by GW) */
427 void compute_sky(DATARRAY *tau, DATARRAY *scat, DATARRAY *scat1m,
428 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 fixargv0(argv[0]);
511
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 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 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
685 dni = erec.dirillum;
686 dhi = erec.diffillum;
687
688 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 #ifdef getc_unlocked
760 flockfile(stdout);
761 #endif
762 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 userr:
817 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 fmterr:
823 fprintf(stderr, "%s: weather tape format error in header\n", progname);
824 exit(1);
825 writerr:
826 fprintf(stderr, "%s: write error on output\n", progname);
827 exit(1);
828 }