ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/genssky.c
Revision: 2.4
Committed: Thu Aug 8 02:00:48 2024 UTC (8 months, 4 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.3: +16 -11 lines
Log Message:
chore(genssky): Removed unsupported for() construct

File Contents

# User Rev Content
1 greg 2.2 #ifndef lint
2 greg 2.4 static const char RCSid[] = "$Id: genssky.c,v 2.3 2024/08/02 18:47:25 greg Exp $";
3 greg 2.2 #endif
4     /* Main function for generating spectral sky */
5     /* Cloudy sky computed as weight average of clear and cie overcast sky */
6 greg 2.1
7 greg 2.2 #include "atmos.h"
8 greg 2.1 #include "copyright.h"
9     #include "resolu.h"
10 greg 2.2 #include "rtio.h"
11     #include <ctype.h>
12     #ifdef _WIN32
13     #include <windows.h>
14     #else
15     #include <errno.h>
16     #include <sys/stat.h>
17     #include <sys/types.h>
18     #endif
19 greg 2.1
20     char *progname;
21    
22     const double ARCTIC_LAT = 67.;
23     const double TROPIC_LAT = 23.;
24     const int SUMMER_START = 4;
25     const int SUMMER_END = 9;
26     const double GNORM = 0.777778;
27    
28     const double D65EFF = 203.; /* standard illuminant D65 */
29    
30 greg 2.2 /* Mean normalized relative daylight spectra where CCT = 6415K for overcast; */
31 greg 2.1 const double D6415[NSSAMP] = {0.63231, 1.06171, 1.00779, 1.36423, 1.34133,
32     1.27258, 1.26276, 1.26352, 1.22201, 1.13246,
33     1.0434, 1.05547, 0.98212, 0.94445, 0.9722,
34     0.82387, 0.87853, 0.82559, 0.75111, 0.78925};
35    
36 greg 2.2 /* European and North American zones */
37     struct {
38     char zname[8]; /* time zone name (all caps) */
39     float zmer; /* standard meridian */
40     } tzone[] = {{"YST", 135}, {"YDT", 120}, {"PST", 120}, {"PDT", 105},
41     {"MST", 105}, {"MDT", 90}, {"CST", 90}, {"CDT", 75},
42     {"EST", 75}, {"EDT", 60}, {"AST", 60}, {"ADT", 45},
43     {"NST", 52.5}, {"NDT", 37.5}, {"GMT", 0}, {"BST", -15},
44     {"CET", -15}, {"CEST", -30}, {"EET", -30}, {"EEST", -45},
45     {"AST", -45}, {"ADT", -60}, {"GST", -60}, {"GDT", -75},
46     {"IST", -82.5}, {"IDT", -97.5}, {"JST", -135}, {"NDT", -150},
47     {"NZST", -180}, {"NZDT", -195}, {"", 0}};
48    
49     static int make_directory(const char *path) {
50     #ifdef _WIN32
51     if (CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) {
52     return 1;
53     }
54     return 0;
55     #else
56     if (mkdir(path, 0777) == 0 || errno == EEXIST) {
57     return 1;
58     }
59     return 0;
60     #endif
61     }
62    
63 greg 2.3 inline static float deg2rad(float deg) { return deg * (PI / 180.); }
64    
65 greg 2.2 static int cvthour(char *hs, int *tsolar, double *hour) {
66     char *cp = hs;
67     int i, j;
68    
69     if ((*tsolar = *cp == '+'))
70     cp++; /* solar time? */
71     while (isdigit(*cp))
72     cp++;
73     if (*cp == ':')
74     *hour = atoi(hs) + atoi(++cp) / 60.0;
75     else {
76     *hour = atof(hs);
77     if (*cp == '.')
78     cp++;
79     }
80     while (isdigit(*cp))
81     cp++;
82     if (!*cp)
83     return (0);
84     if (*tsolar || !isalpha(*cp)) {
85     fprintf(stderr, "%s: bad time format: %s\n", progname, hs);
86     exit(1);
87     }
88     i = 0;
89     do {
90     for (j = 0; cp[j]; j++)
91     if (toupper(cp[j]) != tzone[i].zname[j])
92     break;
93     if (!cp[j] && !tzone[i].zname[j]) {
94     s_meridian = tzone[i].zmer * (PI / 180);
95     return (1);
96     }
97     } while (tzone[i++].zname[0]);
98    
99     fprintf(stderr, "%s: unknown time zone: %s\n", progname, cp);
100     fprintf(stderr, "Known time zones:\n\t%s", tzone[0].zname);
101     for (i = 1; tzone[i].zname[0]; i++)
102     fprintf(stderr, " %s", tzone[i].zname);
103     putc('\n', stderr);
104     exit(1);
105     }
106    
107     static void basename(const char *path, char *output, size_t outsize) {
108     const char *last_slash = strrchr(path, '/');
109     const char *last_backslash = strrchr(path, '\\');
110     const char *filename = path;
111     const char *last_dot;
112    
113     if (last_slash && last_backslash) {
114     filename =
115     (last_slash > last_backslash) ? last_slash + 1 : last_backslash + 1;
116     } else if (last_slash) {
117     filename = last_slash + 1;
118     } else if (last_backslash) {
119     filename = last_backslash + 1;
120     }
121    
122     last_dot = strrchr(filename, '.');
123     if (last_dot) {
124     size_t length = last_dot - filename;
125     if (length < outsize) {
126     strncpy(output, filename, length);
127     output[length] = '\0';
128     } else {
129     strncpy(output, filename, outsize - 1);
130     output[outsize - 1] = '\0';
131     }
132     }
133     }
134    
135 greg 2.3 static char *join_paths(const char *path1, const char *path2) {
136 greg 2.2 size_t len1 = strlen(path1);
137     size_t len2 = strlen(path2);
138     int need_separator = (path1[len1 - 1] != DIRSEP);
139    
140     char *result = malloc(len1 + len2 + (need_separator ? 2 : 1));
141     if (!result)
142     return NULL;
143    
144     strcpy(result, path1);
145     if (need_separator) {
146     result[len1] = DIRSEP;
147     len1++;
148     }
149     strcpy(result + len1, path2);
150    
151     return result;
152     }
153    
154 greg 2.1 static inline double wmean2(const double a, const double b, const double x) {
155     return a * (1 - x) + b * x;
156     }
157    
158     static inline double wmean(const double a, const double x, const double b,
159     const double y) {
160     return (a * x + b * y) / (a + b);
161     }
162    
163     static double get_zenith_brightness(const double sundir[3]) {
164     double zenithbr;
165     if (sundir[2] < 0) {
166     zenithbr = 0;
167     } else {
168     zenithbr = (8.6 * sundir[2] + .123) * 1000.0 / D65EFF;
169     }
170     return zenithbr;
171     }
172    
173 greg 2.2 /* from gensky.c */
174 greg 2.1 static double get_overcast_brightness(const double dz, const double zenithbr) {
175     double groundbr = zenithbr * GNORM;
176     return wmean(pow(dz + 1.01, 10), zenithbr * (1 + 2 * dz) / 3,
177     pow(dz + 1.01, -10), groundbr);
178     }
179    
180 greg 2.2 static void write_header(const int argc, char **argv, const double cloud_cover,
181     const double grefl, const int res) {
182 greg 2.4 int i;
183 greg 2.2 printf("# ");
184 greg 2.4 for (i = 0; i < argc; i++) {
185 greg 2.2 printf("%s ", argv[i]);
186     }
187     printf("\n");
188 greg 2.3 printf(
189     "#Cloud cover: %g\n#Ground reflectance: %g\n#Sky map resolution: %d\n\n",
190     cloud_cover, grefl, res);
191 greg 2.2 }
192    
193     static void write_rad(const double *sun_radiance, const FVECT sundir,
194 greg 2.3 const char *ddir, const char *skyfile) {
195 greg 2.1 if (sundir[2] > 0) {
196 greg 2.2 printf("void spectrum sunrad\n0\n0\n22 380 780 ");
197     /* Normalize to one */
198     double sum = 0.0;
199 greg 2.4 int i;
200     for (i = 0; i < NSSAMP; ++i) {
201 greg 2.2 sum += sun_radiance[i];
202 greg 2.1 }
203 greg 2.2 double mean = sum / NSSAMP;
204 greg 2.4 for (i = 0; i < NSSAMP; ++i) {
205 greg 2.2 printf("%.3f ", sun_radiance[i] / mean);
206     }
207     double intensity = mean * WVLSPAN;
208     printf("\n\nsunrad light solar\n0\n0\n3 %.1f %.1f %.1f\n\n", intensity,
209     intensity, intensity);
210     printf("solar source sun\n0\n0\n4 %f %f %f 0.533\n\n", sundir[0], sundir[1],
211     sundir[2]);
212     }
213 greg 2.3 printf("void specpict skyfunc\n5 noop %s . 'atan2(Dy,Dx)/PI+1' "
214     "'acos(Dz)/PI'\n0\n0\n\n",
215 greg 2.2 skyfile);
216 greg 2.1 }
217    
218     static void write_hsr_header(FILE *fp, RESOLU *res) {
219 greg 2.3 float wvsplit[4] = {380, 480, 588, 780};
220 greg 2.1 newheader("RADIANCE", fp);
221     fputncomp(NSSAMP, fp);
222     fputwlsplit(wvsplit, fp);
223     fputformat(SPECFMT, fp);
224     fputc('\n', fp);
225     fputsresolu(res, fp);
226     }
227    
228 greg 2.3 static inline float frac(float x) { return x - floor(x); }
229    
230 greg 2.1 int gen_spect_sky(DATARRAY *tau_clear, DATARRAY *scat_clear,
231     DATARRAY *scat1m_clear, DATARRAY *irrad_clear,
232     const double cloud_cover, const FVECT sundir,
233 greg 2.3 const double grefl, const int res, const char *outname,
234     const char *ddir) {
235 greg 2.1 char skyfile[PATH_MAX];
236     char grndfile[PATH_MAX];
237 greg 2.3 if (!snprintf(skyfile, sizeof(skyfile), "%s%c%s_sky.hsr", ddir, DIRSEP,
238     outname)) {
239 greg 2.1 fprintf(stderr, "Error setting sky file name\n");
240     return 0;
241     };
242 greg 2.3 int xres = res;
243     int yres = xres / 2;
244     RESOLU rs = {PIXSTANDARD, xres, yres};
245 greg 2.1 FILE *skyfp = fopen(skyfile, "w");
246     write_hsr_header(skyfp, &rs);
247    
248     CNDX[3] = NSSAMP;
249    
250 greg 2.3 FVECT view_point = {0, 0, ER + 10};
251 greg 2.1 const double radius = VLEN(view_point);
252     const double sun_ct = fdot(view_point, sundir) / radius;
253 greg 2.4 int i, j, k;
254     for (j = 0; j < yres; ++j) {
255     for (i = 0; i < xres; ++i) {
256 greg 2.3 SCOLOR radiance = {0};
257 greg 2.1 SCOLR sky_sclr = {0};
258    
259 greg 2.3 float px = i / (xres - 1.0);
260     float py = j / (yres - 1.0);
261     float lambda = ((1 - py) * PI) - (PI / 2.0);
262     float phi = (px * 2.0 * PI) - PI;
263    
264     FVECT rdir = {cos(lambda) * cos(phi), cos(lambda) * sin(phi),
265     sin(lambda)};
266    
267     const double mu = fdot(view_point, rdir) / radius;
268     const double nu = fdot(rdir, sundir);
269    
270     /* hit ground */
271     if (rdir[2] < 0) {
272     get_ground_radiance(tau_clear, scat_clear, scat1m_clear, irrad_clear,
273     view_point, rdir, radius, mu, sun_ct, nu, grefl,
274     sundir, radiance);
275     } else {
276     get_sky_radiance(scat_clear, scat1m_clear, radius, mu, sun_ct, nu,
277     radiance);
278     }
279 greg 2.1
280 greg 2.4 for (k = 0; k < NSSAMP; ++k) {
281 greg 2.3 radiance[k] *= WVLSPAN;
282 greg 2.1 }
283    
284     if (cloud_cover > 0) {
285     double zenithbr = get_zenith_brightness(sundir);
286     double grndbr = zenithbr * GNORM;
287 greg 2.3 double skybr = get_overcast_brightness(rdir[2], zenithbr);
288     if (rdir[2] < 0) {
289 greg 2.4 for (k = 0; k < NSSAMP; ++k) {
290 greg 2.3 radiance[k] = wmean2(radiance[k], grndbr * D6415[k], cloud_cover);
291     }
292     } else {
293 greg 2.4 for (k = 0; k < NSSAMP; ++k) {
294 greg 2.3 radiance[k] = wmean2(radiance[k], skybr * D6415[k], cloud_cover);
295     }
296 greg 2.1 }
297     }
298    
299 greg 2.3 scolor2scolr(sky_sclr, radiance, 20);
300 greg 2.1 putbinary(sky_sclr, LSCOLR, 1, skyfp);
301     }
302     }
303     fclose(skyfp);
304    
305 greg 2.2 /* Get solar radiance */
306 greg 2.1 double sun_radiance[NSSAMP] = {0};
307     get_solar_radiance(tau_clear, scat_clear, scat1m_clear, sundir, radius,
308     sun_ct, sun_radiance);
309     if (cloud_cover > 0) {
310     double zenithbr = get_zenith_brightness(sundir);
311     double skybr = get_overcast_brightness(sundir[2], zenithbr);
312 greg 2.4 int i;
313     for (i = 0; i < NSSAMP; ++i) {
314 greg 2.1 sun_radiance[i] =
315     wmean2(sun_radiance[i], D6415[i] * skybr / WVLSPAN, cloud_cover);
316     }
317     }
318    
319 greg 2.3 write_rad(sun_radiance, sundir, ddir, skyfile);
320 greg 2.1 return 1;
321     }
322    
323 greg 2.2 static DpPaths get_dppaths(const char *dir, const double aod, const char *mname,
324     const char *tag) {
325 greg 2.1 DpPaths paths;
326    
327 greg 2.2 snprintf(paths.tau, PATH_MAX, "%s%ctau_%s_%s_%.2f.dat", dir, DIRSEP, tag,
328     mname, aod);
329     snprintf(paths.scat, PATH_MAX, "%s%cscat_%s_%s_%.2f.dat", dir, DIRSEP, tag,
330     mname, aod);
331     snprintf(paths.scat1m, PATH_MAX, "%s%cscat1m_%s_%s_%.2f.dat", dir, DIRSEP,
332     tag, mname, aod);
333     snprintf(paths.irrad, PATH_MAX, "%s%cirrad_%s_%s_%.2f.dat", dir, DIRSEP, tag,
334     mname, aod);
335 greg 2.1
336     return paths;
337     }
338    
339 greg 2.2 static void set_rayleigh_density_profile(Atmosphere *atmos, char *tag,
340     const int is_summer,
341 greg 2.1 const double s_latitude) {
342 greg 2.2 if (fabs(s_latitude * 180.0 / PI) > ARCTIC_LAT) {
343 greg 2.1 tag[0] = 's';
344     if (is_summer) {
345     tag[1] = 's';
346     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_SS;
347     atmos->beta_r0 = BR0_SS;
348     } else {
349     tag[1] = 'w';
350     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_SW;
351     atmos->beta_r0 = BR0_SW;
352     }
353 greg 2.2 } else if (fabs(s_latitude * 180.0 / PI) > TROPIC_LAT) {
354 greg 2.1 tag[0] = 'm';
355     if (is_summer) {
356     tag[1] = 's';
357     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_MS;
358     atmos->beta_r0 = BR0_MS;
359     } else {
360     tag[1] = 'w';
361     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_MW;
362     atmos->beta_r0 = BR0_MW;
363     }
364     } else {
365     tag[0] = 't';
366     tag[1] = 'r';
367     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_T;
368     atmos->beta_r0 = BR0_T;
369     }
370     tag[2] = '\0';
371     }
372    
373     static Atmosphere init_atmos(const double aod, const double grefl) {
374 greg 2.2 Atmosphere atmos = {.ozone_density = {.layers =
375     {
376     {.width = 25000.0,
377     .exp_term = 0.0,
378     .exp_scale = 0.0,
379     .linear_term = 1.0 / 15000.0,
380     .constant_term = -2.0 / 3.0},
381     {.width = AH,
382     .exp_term = 0.0,
383     .exp_scale = 0.0,
384     .linear_term = -1.0 / 15000.0,
385     .constant_term = 8.0 / 3.0},
386     }},
387     .rayleigh_density = {.layers =
388     {
389     {.width = AH,
390     .exp_term = 1.0,
391     .exp_scale = -1.0 / HR_MS,
392     .linear_term = 0.0,
393     .constant_term = 0.0},
394     }},
395     .beta_r0 = BR0_MS,
396     .beta_scale = aod / AOD0_CA,
397     .beta_m = NULL,
398     .grefl = grefl};
399 greg 2.1 return atmos;
400     }
401    
402     int main(int argc, char *argv[]) {
403     progname = argv[0];
404     int month, day;
405     double hour;
406     FVECT sundir;
407     int num_threads = 1;
408     int sorder = 4;
409     int year = 0;
410     int tsolar = 0;
411 greg 2.2 int got_meridian = 0;
412 greg 2.1 double grefl = 0.2;
413     double ccover = 0.0;
414 greg 2.3 int res = 64;
415 greg 2.1 double aod = AOD0_CA;
416     char *outname = "out";
417     char *mie_path = getpath("mie_ca.dat", getrlibpath(), R_OK);
418 greg 2.2 char mie_name[20] = "mie_ca";
419 greg 2.1 char lstag[3];
420 greg 2.2 char *ddir = ".";
421 greg 2.4 int i;
422 greg 2.2
423 greg 2.3 if (argc == 2 && !strcmp(argv[1], "-defaults")) {
424 greg 2.2 printf("-i %d\t\t\t\t#scattering order\n", sorder);
425     printf("-g %f\t\t\t#ground reflectance\n", grefl);
426     printf("-c %f\t\t\t#cloud cover\n", ccover);
427     printf("-r %d\t\t\t\t#image resolution\n", res);
428     printf("-d %f\t\t\t#broadband aerosol optical depth\n", AOD0_CA);
429     printf("-f %s\t\t\t\t#output name (-f)\n", outname);
430     printf("-p %s\t\t\t\t#atmos data directory\n", ddir);
431 greg 2.3 exit(0);
432 greg 2.2 }
433 greg 2.1
434     if (argc < 4) {
435 greg 2.2 fprintf(stderr,
436     "Usage: %s month day hour -y year -a lat -o lon -m tz -d aod -r "
437     "res -n nproc -c ccover -l mie -g grefl -f outpath\n",
438 greg 2.1 argv[0]);
439     return 0;
440     }
441    
442     month = atoi(argv[1]);
443 greg 2.2 if (month < 1 || month > 12) {
444     fprintf(stderr, "bad month");
445     exit(1);
446     }
447 greg 2.1 day = atoi(argv[2]);
448 greg 2.2 if (day < 1 || day > 31) {
449     fprintf(stderr, "bad month");
450     exit(1);
451     }
452     got_meridian = cvthour(argv[3], &tsolar, &hour);
453 greg 2.1
454     if (!compute_sundir(year, month, day, hour, tsolar, sundir)) {
455     fprintf(stderr, "Cannot compute solar angle\n");
456     exit(1);
457     }
458    
459 greg 2.4 for (i = 4; i < argc; i++) {
460 greg 2.1 if (argv[i][0] == '-') {
461     switch (argv[i][1]) {
462     case 'a':
463     s_latitude = atof(argv[++i]) * (PI / 180.0);
464     break;
465     case 'c':
466     ccover = atof(argv[++i]);
467     break;
468     case 'd':
469     aod = atof(argv[++i]);
470     break;
471 greg 2.2 case 'f':
472     outname = argv[++i];
473     break;
474     case 'g':
475     grefl = atof(argv[++i]);
476     break;
477 greg 2.1 case 'i':
478     sorder = atoi(argv[++i]);
479     break;
480     case 'l':
481     mie_path = argv[++i];
482 greg 2.2 basename(mie_path, mie_name, sizeof(mie_name));
483 greg 2.1 break;
484     case 'm':
485 greg 2.2 if (got_meridian) {
486     ++i;
487     break;
488     }
489 greg 2.1 s_meridian = atof(argv[++i]) * (PI / 180.0);
490     break;
491     case 'n':
492     num_threads = atoi(argv[++i]);
493     break;
494 greg 2.2 case 'o':
495     s_longitude = atof(argv[++i]) * (PI / 180.0);
496 greg 2.1 break;
497 greg 2.2 case 'p':
498     ddir = argv[++i];
499 greg 2.1 break;
500     case 'r':
501     res = atoi(argv[++i]);
502     break;
503 greg 2.2 case 'y':
504     year = atoi(argv[++i]);
505     break;
506 greg 2.1 default:
507     fprintf(stderr, "Unknown option %s\n", argv[i]);
508     exit(1);
509     }
510     }
511     }
512 greg 2.2 if (year && (year < 1950) | (year > 2050))
513     fprintf(stderr, "%s: warning - year should be in range 1950-2050\n",
514     progname);
515     if (month && !tsolar && fabs(s_meridian - s_longitude) > 45 * PI / 180)
516     fprintf(stderr,
517     "%s: warning - %.1f hours btwn. standard meridian and longitude\n",
518     progname, (s_longitude - s_meridian) * 12 / PI);
519 greg 2.1
520     Atmosphere clear_atmos = init_atmos(aod, grefl);
521    
522     int is_summer = (month >= SUMMER_START && month <= SUMMER_END);
523     if (s_latitude < 0) {
524     is_summer = !is_summer;
525     }
526     set_rayleigh_density_profile(&clear_atmos, lstag, is_summer, s_latitude);
527    
528 greg 2.2 /* Load mie density data */
529 greg 2.1 DATARRAY *mie_dp = getdata(mie_path);
530     if (mie_dp == NULL) {
531     fprintf(stderr, "Error reading mie data\n");
532     return 0;
533     }
534     clear_atmos.beta_m = mie_dp;
535    
536 greg 2.2 char gsdir[PATH_MAX];
537     size_t siz = strlen(ddir);
538 greg 2.3 if (ISDIRSEP(ddir[siz - 1]))
539     ddir[siz - 1] = '\0';
540 greg 2.2 snprintf(gsdir, PATH_MAX, "%s%catmos_data", ddir, DIRSEP);
541     if (!make_directory(gsdir)) {
542     fprintf(stderr, "Failed creating atmos_data directory");
543     exit(1);
544     }
545     DpPaths clear_paths = get_dppaths(gsdir, aod, mie_name, lstag);
546 greg 2.1
547     if (getpath(clear_paths.tau, ".", R_OK) == NULL ||
548     getpath(clear_paths.scat, ".", R_OK) == NULL ||
549     getpath(clear_paths.scat1m, ".", R_OK) == NULL ||
550     getpath(clear_paths.irrad, ".", R_OK) == NULL) {
551 greg 2.2 printf("# Pre-computing...\n");
552 greg 2.1 if (!precompute(sorder, clear_paths, &clear_atmos, num_threads)) {
553 greg 2.2 fprintf(stderr, "Pre-compute failed\n");
554 greg 2.1 return 0;
555     }
556     }
557    
558     DATARRAY *tau_clear_dp = getdata(clear_paths.tau);
559     DATARRAY *irrad_clear_dp = getdata(clear_paths.irrad);
560     DATARRAY *scat_clear_dp = getdata(clear_paths.scat);
561     DATARRAY *scat1m_clear_dp = getdata(clear_paths.scat1m);
562    
563 greg 2.2 write_header(argc, argv, ccover, grefl, res);
564    
565 greg 2.1 if (!gen_spect_sky(tau_clear_dp, scat_clear_dp, scat1m_clear_dp,
566 greg 2.3 irrad_clear_dp, ccover, sundir, grefl, res, outname,
567     ddir)) {
568 greg 2.1 fprintf(stderr, "gen_spect_sky failed\n");
569     exit(1);
570     }
571    
572     freedata(mie_dp);
573     freedata(tau_clear_dp);
574     freedata(scat_clear_dp);
575     freedata(irrad_clear_dp);
576     freedata(scat1m_clear_dp);
577    
578     return 1;
579     }