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 (3 weeks 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

# User Rev Content
1 greg 1.2 #ifndef lint
2 greg 1.9 static const char RCSid[] = "$Id: gensdaymtx.c,v 1.8 2025/06/07 05:09:45 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.10 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 greg 1.1
33     /* Mean normalized relative daylight spectra where CCT = 6415K for overcast */
34 greg 1.10 const double D6415[NSSAMP] = {
35 greg 1.6 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 greg 1.10 0.82387, 0.87853, 0.82559, 0.75111, 0.78925
39     };
40 greg 1.6
41     enum {
42 greg 1.10 NSUNPATCH = 4 /* max. # patches to spread sun into */
43 greg 1.6 };
44    
45 greg 1.10 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 greg 1.6 {
74     return deg * (PI / 180.);
75     }
76 greg 1.1
77 greg 1.10 static inline double
78     rad_to_deg
79     (
80     double rad
81     )
82 greg 1.6 {
83     return rad * (180. / PI);
84     }
85 greg 1.1
86 greg 1.10 static inline void
87     vectorize
88     (
89     double altitude,
90     double azimuth,
91     FVECT v
92     )
93 greg 1.6 {
94     v[1] = cos(altitude);
95     v[0] = (v)[1] * sin(azimuth);
96     v[1] *= cos(azimuth);
97     v[2] = sin(altitude);
98     }
99 greg 1.1
100 greg 1.10 static inline double
101     wmean2
102     (
103     const double a,
104     const double b,
105     const double x
106     )
107 greg 1.6 {
108     return a * (1 - x) + b * x;
109     }
110 greg 1.1
111 greg 1.10 static inline double
112     wmean
113     (
114     const double a,
115     const double x,
116     const double b,
117     const double y
118     )
119 greg 1.6 {
120     return (a * x + b * y) / (a + b);
121 greg 1.1 }
122    
123 greg 1.10 static int
124     make_directory
125     (
126     const char *path
127     )
128 greg 1.6 {
129 greg 1.1 #ifdef _WIN32
130 greg 1.6 if (CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) {
131     return 1;
132     }
133     return 0;
134 greg 1.10
135 greg 1.1 #else
136 greg 1.6 if (mkdir(path, 0777) == 0 || errno == EEXIST) {
137     return 1;
138     }
139     return 0;
140 greg 1.10
141 greg 1.1 #endif
142     }
143    
144 greg 1.10 static const char *
145     getfmtname
146     (
147     int fmt
148     )
149 greg 1.6 {
150     switch (fmt) {
151     case 'a':
152     return ("ascii");
153 greg 1.10
154 greg 1.6 case 'f':
155     return ("float");
156 greg 1.10
157 greg 1.6 case 'd':
158     return ("double");
159     }
160     return ("unknown");
161 greg 1.1 }
162    
163    
164 greg 1.10 static double
165     get_overcast_zenith_brightness
166     (
167     const double sundir[3]
168     )
169 greg 1.6 {
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 greg 1.1 }
178    
179 greg 1.5
180 greg 1.1 /* from gensky.c */
181 greg 1.10 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 greg 1.5 {
201     float W;
202     W = -1 * (tan(s_latitude) * tan(sdec(jdate(month, day))));
203 greg 1.6 return 12 + (PI / 2 - atan2(W, sqrt(1 - W * W))) * 180 / (PI * 15);
204 greg 1.5 }
205    
206    
207 greg 1.10 double
208     solar_sunrise
209     (
210     int month,
211     int day
212     )
213 greg 1.5 {
214     float W;
215     W = -1 * (tan(s_latitude) * tan(sdec(jdate(month, day))));
216 greg 1.6 return 12 - (PI / 2 - atan2(W, sqrt(1 - W * W))) * 180 / (PI * 15);
217 greg 1.5 }
218    
219 greg 1.10 int
220     rh_init
221     (
222     void
223     )
224 greg 1.6 {
225 greg 1.1 #define NROW 7
226 greg 1.6 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 greg 1.10 for (p = 0; p < NROW; p++) {
232 greg 1.6 nskypatch += tnaz[p];
233 greg 1.10 }
234 greg 1.6 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 greg 1.10 rh_palt[0] = -PI / 2.; /* ground & zenith patches */
244 greg 1.6 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 greg 1.10 p = 1; /* "normal" patches */
250 greg 1.6 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 greg 1.10 2. * PI * (sin(alpha * (i + 1)) - sin(alpha * i)) / (double)ninrow;
255 greg 1.6 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 greg 1.10
263 greg 1.1 #undef NROW
264     }
265    
266     /* Resize daylight matrix (GW) */
267 greg 1.10 float *
268     resize_dmatrix
269     (
270     float *mtx_data,
271     int nsteps,
272     int npatch
273     )
274 greg 1.6 {
275 greg 1.10 if (mtx_data == NULL) {
276 greg 1.6 mtx_data = (float * ) malloc(sizeof(float) * NSSAMP * nsteps * npatch);
277 greg 1.10 }else{
278 greg 1.6 mtx_data = (float * ) realloc(mtx_data,
279 greg 1.10 sizeof(float) * NSSAMP * nsteps * npatch);
280     }
281 greg 1.6 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 greg 1.10 }
287     return mtx_data;
288 greg 1.6 }
289    
290 greg 1.10 static Atmosphere
291     init_atmos
292     (
293     const double aod,
294     const double grefl
295     )
296 greg 1.6 {
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 greg 1.10 static DpPaths
336     get_dppaths
337     (
338     const char *dir,
339     const double aod,
340     const char *mname,
341     const char *tag
342     )
343 greg 1.6 {
344     DpPaths paths;
345    
346     snprintf(paths.tau, PATH_MAX, "%s%ctau_%s_%s_%.2f.dat",
347 greg 1.10 dir, DIRSEP, tag, mname, aod);
348 greg 1.6 snprintf(paths.scat, PATH_MAX, "%s%cscat_%s_%s_%.2f.dat",
349 greg 1.10 dir, DIRSEP, tag, mname, aod);
350 greg 1.6 snprintf(paths.scat1m, PATH_MAX, "%s%cscat1m_%s_%s_%.2f.dat",
351 greg 1.10 dir, DIRSEP, tag, mname, aod);
352 greg 1.6 snprintf(paths.irrad, PATH_MAX, "%s%cirrad_%s_%s_%.2f.dat",
353 greg 1.10 dir, DIRSEP, tag, mname, aod);
354 greg 1.6
355     return paths;
356 greg 1.1 }
357    
358 greg 1.6
359 greg 1.10 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 greg 1.6 {
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 greg 1.1 }
399 greg 1.5
400 greg 1.6
401 greg 1.1 /* Add in solar direct to nearest sky patches (GW) */
402 greg 1.10 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 greg 1.6 {
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 greg 1.10 for (i = nsuns; i--;) {
422 greg 1.6 near_dprod[i] = -1.;
423 greg 1.10 }
424 greg 1.6 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 greg 1.10 for (i = 0; i < nsuns; i++) {
431 greg 1.6 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 greg 1.10 }
441 greg 1.6 }
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 greg 1.10 if (mean > 0) {
466 greg 1.6 dir_ratio = intensity / mean;
467 greg 1.10 }
468 greg 1.6 for (i = 0; i < NSSAMP; ++i) {
469     sun_radiance[i] *= dir_ratio;
470     }
471    
472     /* weight by proximity */
473     wtot = 0;
474 greg 1.10 for (i = nsuns; i--;) {
475 greg 1.6 wtot += wta[i] = 1. / (1.002 - near_dprod[i]);
476 greg 1.10 }
477 greg 1.6 /* 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 greg 1.10 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 greg 1.6 for (i = 1; i < nskypatch; i++) {
505 greg 1.10 FVECT rdir_sky;
506 greg 1.6 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 greg 1.10 SCOLOR sky_radiance = {0};
511 greg 1.6 get_sky_radiance(scat, scat1m, ER, mu_sky, sun_ct, sspa, sky_radiance);
512 greg 1.10 int k;
513 greg 1.6 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 greg 1.1 }
534    
535    
536     /* Compute sky patch radiance values (modified by GW) */
537 greg 1.10 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 greg 1.6 {
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 greg 1.10 if (altitude <= 0.0) {
564 greg 1.6 sun_zenith = deg_to_rad(90.0);
565 greg 1.10 }else if (altitude >= deg_to_rad(87.0)) {
566 greg 1.6 sun_zenith = deg_to_rad(3.0);
567 greg 1.10 }else{
568 greg 1.6 sun_zenith = deg_to_rad(90.0) - altitude;
569 greg 1.10 }
570 greg 1.6
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 greg 1.10 diffuse_irradiance += indirect_irradiance_clear->arr.d[l] * 20; /* 20nm interval */
582 greg 1.6 }
583     /* free(indirect_irradiance_clear); */
584     diffuse_irradiance = wmean2(diffuse_irradiance, overcast_ghi, ccover);
585     if (diffuse_irradiance > 0) {
586 greg 1.10 dif_ratio = difhor / WHTEFFICACY / diffuse_irradiance / 1.15; /* fudge */
587 greg 1.6 }
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 greg 1.10 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 greg 1.6
636 greg 1.7 fixargv0(argv[0]);
637 greg 1.6
638     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
639     switch (argv[i][1]) {
640 greg 1.10 case 'd': /* solar (direct) only */
641 greg 1.6 solar_only = 1;
642     break;
643 greg 1.10 case 's': /* sky only (no direct) */
644 greg 1.6 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 greg 1.10 case 'r': /* rotate distribution */
656     if (argv[i][2] && argv[i][2] != 'z') {
657 greg 1.6 goto userr;
658 greg 1.10 }
659 greg 1.6 rotation = atof(argv[++i]);
660     break;
661 greg 1.10 case 'u': /* solar hours only */
662 greg 1.6 sun_hours_only = 1;
663     break;
664     case 'p':
665     ddir = argv[++i];
666     break;
667 greg 1.10 case 'v': /* verbose progress reports */
668 greg 1.6 verbose++;
669     break;
670 greg 1.10 case 'h': /* turn off header */
671 greg 1.6 doheader = 0;
672     break;
673 greg 1.10 case '5': /* 5-phase calculation */
674 greg 1.6 nsuns = 1;
675     fixed_sun_sa = PI / 360. * atof(argv[++i]);
676     if (fixed_sun_sa <= 0) {
677     fprintf(
678 greg 1.10 stderr,
679 greg 1.6 "%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 greg 1.10 case 'o': /* output format */
689 greg 1.6 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 greg 1.10 if (i < argc - 1) {
704 greg 1.6 goto userr;
705 greg 1.10 }
706 greg 1.6
707     epw = EPWopen(argv[i]);
708 greg 1.10 if (epw == NULL) {
709 greg 1.6 exit(1);
710 greg 1.10 }
711 greg 1.6 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 greg 1.10 if (i == argc - 1) {
717 greg 1.6 fprintf(stderr, "%s: reading weather tape '%s'\n", progname, argv[i]);
718 greg 1.10 }else{
719 greg 1.6 fprintf(stderr, "%s: reading weather tape from <stdin>\n", progname);
720 greg 1.10 }
721 greg 1.6 }
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 greg 1.10 switch (epw->isWEA) { /* translate units */
727 greg 1.6 case WEAnot:
728     case WEAradnorm:
729 greg 1.10 input = 1; /* radiometric quantities */
730     dir_is_horiz = 0; /* direct is perpendicular meas. */
731 greg 1.6 break;
732     case WEAradhoriz:
733 greg 1.10 input = 1; /* radiometric quantities */
734     dir_is_horiz = 1; /* solar measured horizontally */
735 greg 1.6 break;
736     case WEAphotnorm:
737 greg 1.10 input = 2; /* photometric quantities */
738     dir_is_horiz = 0; /* direct is perpendicular meas. */
739 greg 1.6 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 greg 1.10 stderr,
750 greg 1.6 "%s: (lat,long)=(%.1f,%.1f) degrees north, west\n",
751     progname, s_latitude, s_longitude);
752 greg 1.10 if (rotation != 0) {
753 greg 1.6 fprintf(stderr, "%s: rotating output %.0f degrees\n", progname, rotation);
754 greg 1.10 }
755 greg 1.6 }
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 greg 1.10 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 greg 1.9 if (aod >= 999.0) {
783     aod = AOD0_CA;
784 greg 1.10 fprintf(stderr, "aod is not set, using default value %.3f\n", AOD0_CA);
785 greg 1.9 }
786 greg 1.6 double cc = erec.skycover;
787 greg 1.9 if (cc >= 99.0) {
788     cc = 0.0;
789 greg 1.10 fprintf(stderr, "skycover is not set, using default value %.3f\n", 0.0);
790 greg 1.9 }
791 greg 1.10 double sda, sta, st;
792     int sun_in_sky;
793 greg 1.6
794     /* compute solar position */
795     if ((mo == 2) & (da == 29)) {
796     julian_date = 60;
797     leap_day = 1;
798 greg 1.10 } else{
799 greg 1.6 julian_date = jdate(mo, da) + leap_day;
800 greg 1.10 }
801 greg 1.6 sda = sdec(julian_date);
802     sta = stadj(julian_date);
803     st = hr + sta;
804 greg 1.5 if (timeinterval > 0) {
805 greg 1.10 if (fabs(solar_sunrise(mo, da) - st) <= timeinterval/120) {
806 greg 1.5 st = (st + timeinterval/120 + solar_sunrise(mo, da))/2;
807 greg 1.10 }else if (fabs(solar_sunset(mo, da) - st) < timeinterval/120) {
808 greg 1.5 st = (st - timeinterval/120 + solar_sunset(mo, da))/2;
809 greg 1.10 }
810 greg 1.5 }
811 greg 1.6 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 greg 1.10 continue; /* skipping nighttime points */
819 greg 1.6 }
820     sun_ct = fdot(view_point, sundir) / ER;
821 greg 1.5
822 greg 1.6 dni = erec.dirillum;
823     dhi = erec.diffillum;
824 greg 1.5
825 greg 1.6 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 greg 1.10 ntsteps++; /* keep count of time steps */
834 greg 1.6
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 greg 1.10 if (ISDIRSEP(ddir[siz - 1])) {
848 greg 1.6 ddir[siz - 1] = '\0';
849 greg 1.10 }
850     snprintf(gsdir, PATH_MAX, "%s%catmos_data", ddir, DIRSEP);
851 greg 1.6 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 greg 1.10 if (!solar_only) {
874 greg 1.6 compute_sky(tau_clear_dp, scat_clear_dp, scat1m_clear_dp, irrad_clear_dp,
875 greg 1.10 cc, dhi, view_point, mtx_data + mtx_offset);
876     }
877     if (!sky_only) {
878 greg 1.6 add_direct(tau_clear_dp, scat_clear_dp, scat1m_clear_dp, irrad_clear_dp,
879 greg 1.10 cc, dni, mtx_data + mtx_offset);
880     }
881 greg 1.6 /* monthly reporting */
882 greg 1.10 if (verbose && mo != last_monthly) {
883 greg 1.6 fprintf(stderr, "%s: stepping through month %d...\n", progname,
884 greg 1.10 last_monthly = mo);
885     }
886 greg 1.6 }
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 greg 1.10 if (outfmt != 'a') {
899 greg 1.6 SET_FILE_BINARY(stdout);
900 greg 1.10 }
901 greg 1.1 #ifdef getc_unlocked
902 greg 1.6 flockfile(stdout);
903 greg 1.1 #endif
904 greg 1.10 if (verbose) {
905 greg 1.6 fprintf(stderr, "%s: writing %smatrix with %d time steps...\n", progname,
906 greg 1.10 outfmt == 'a' ? "" : "binary ", nstored);
907     }
908 greg 1.6 if (doheader) {
909     newheader("RADIANCE", stdout);
910     printargs(argc, argv, stdout);
911     printf("LATLONG= %.8f %.8f\n", rad_to_deg(s_latitude),
912 greg 1.10 -rad_to_deg(s_longitude));
913 greg 1.6 printf("NROWS=%d\n", nskypatch);
914     printf("NCOLS=%d\n", nstored);
915     printf("NCOMP=%d\n", NSSAMP);
916 greg 1.10 fputwlsplit(WLPART, stdout);
917     if ((outfmt == 'f') | (outfmt == 'd')) {
918 greg 1.6 fputendian(stdout);
919 greg 1.10 }
920 greg 1.6 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 greg 1.10 for (k = NSSAMP - 1; k >= 0; k--) {
930 greg 1.6 printf("%.3g ", mtx_data[mtx_offset + k]);
931     }
932     printf("\n");
933     mtx_offset += NSSAMP * nskypatch;
934     }
935 greg 1.10 if (nstored > 1) {
936 greg 1.6 fputc('\n', stdout);
937 greg 1.10 }
938 greg 1.6 break;
939     case 'f':
940     for (j = 0; j < nstored; j++) {
941 greg 1.10 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 greg 1.6 mtx_offset += NSSAMP * nskypatch;
947     }
948     break;
949     case 'd':
950     for (j = 0; j < nstored; j++) {
951     double ment[NSSAMP];
952 greg 1.10 for (k = NSSAMP - 1; k >= 0; k--) {
953     ment[j] = mtx_data[mtx_offset + k];
954     }
955 greg 1.6 putbinary(ment, sizeof(double), NSSAMP, stdout);
956     mtx_offset += NSSAMP * nskypatch;
957     }
958     break;
959     }
960 greg 1.10 if (ferror(stdout)) {
961 greg 1.6 goto writerr;
962 greg 1.10 }
963 greg 1.6 }
964     return 0;
965 greg 1.10
966 greg 1.1 userr:
967 greg 1.6 fprintf(stderr,
968 greg 1.10 "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 greg 1.6 exit(1);
972 greg 1.1 fmterr:
973 greg 1.6 fprintf(stderr, "%s: weather tape format error in header\n", progname);
974     exit(1);
975 greg 1.1 writerr:
976 greg 1.6 fprintf(stderr, "%s: write error on output\n", progname);
977     exit(1);
978 greg 1.1 }