ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/gensdaymtx.c
Revision: 1.10
Committed: Fri Jul 11 18:12:25 2025 UTC (2 weeks, 4 days ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad6R0, HEAD
Changes since 1.9: +332 -186 lines
Log Message:
fix(genssky,gensdaymtx): Spectral data ordering was reversed in output HSR's (TW)

File Contents

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