ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/genssky.c
Revision: 2.3
Committed: Fri Aug 2 18:47:25 2024 UTC (9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.2: +62 -79 lines
Log Message:
feat(gensdaymtx,epw2wea,genssky): Taoning added new gensdaymtx and updated others

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id$";
3 #endif
4 /* Main function for generating spectral sky */
5 /* Cloudy sky computed as weight average of clear and cie overcast sky */
6
7 #include "atmos.h"
8 #include "copyright.h"
9 #include "resolu.h"
10 #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
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 /* Mean normalized relative daylight spectra where CCT = 6415K for overcast; */
31 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 /* 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 inline static float deg2rad(float deg) { return deg * (PI / 180.); }
64
65 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 static char *join_paths(const char *path1, const char *path2) {
136 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 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 /* from gensky.c */
174 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 static void write_header(const int argc, char **argv, const double cloud_cover,
181 const double grefl, const int res) {
182 printf("# ");
183 for (int i = 0; i < argc; i++) {
184 printf("%s ", argv[i]);
185 }
186 printf("\n");
187 printf(
188 "#Cloud cover: %g\n#Ground reflectance: %g\n#Sky map resolution: %d\n\n",
189 cloud_cover, grefl, res);
190 }
191
192 static void write_rad(const double *sun_radiance, const FVECT sundir,
193 const char *ddir, const char *skyfile) {
194 if (sundir[2] > 0) {
195 printf("void spectrum sunrad\n0\n0\n22 380 780 ");
196 /* Normalize to one */
197 double sum = 0.0;
198 for (int i = 0; i < NSSAMP; ++i) {
199 sum += sun_radiance[i];
200 }
201 double mean = sum / NSSAMP;
202 for (int i = 0; i < NSSAMP; ++i) {
203 printf("%.3f ", sun_radiance[i] / mean);
204 }
205 double intensity = mean * WVLSPAN;
206 printf("\n\nsunrad light solar\n0\n0\n3 %.1f %.1f %.1f\n\n", intensity,
207 intensity, intensity);
208 printf("solar source sun\n0\n0\n4 %f %f %f 0.533\n\n", sundir[0], sundir[1],
209 sundir[2]);
210 }
211 printf("void specpict skyfunc\n5 noop %s . 'atan2(Dy,Dx)/PI+1' "
212 "'acos(Dz)/PI'\n0\n0\n\n",
213 skyfile);
214 }
215
216 static void write_hsr_header(FILE *fp, RESOLU *res) {
217 float wvsplit[4] = {380, 480, 588, 780};
218 newheader("RADIANCE", fp);
219 fputncomp(NSSAMP, fp);
220 fputwlsplit(wvsplit, fp);
221 fputformat(SPECFMT, fp);
222 fputc('\n', fp);
223 fputsresolu(res, fp);
224 }
225
226 static inline float frac(float x) { return x - floor(x); }
227
228 int gen_spect_sky(DATARRAY *tau_clear, DATARRAY *scat_clear,
229 DATARRAY *scat1m_clear, DATARRAY *irrad_clear,
230 const double cloud_cover, const FVECT sundir,
231 const double grefl, const int res, const char *outname,
232 const char *ddir) {
233 char skyfile[PATH_MAX];
234 char grndfile[PATH_MAX];
235 if (!snprintf(skyfile, sizeof(skyfile), "%s%c%s_sky.hsr", ddir, DIRSEP,
236 outname)) {
237 fprintf(stderr, "Error setting sky file name\n");
238 return 0;
239 };
240 int xres = res;
241 int yres = xres / 2;
242 RESOLU rs = {PIXSTANDARD, xres, yres};
243 FILE *skyfp = fopen(skyfile, "w");
244 write_hsr_header(skyfp, &rs);
245
246 CNDX[3] = NSSAMP;
247
248 FVECT view_point = {0, 0, ER + 10};
249 const double radius = VLEN(view_point);
250 const double sun_ct = fdot(view_point, sundir) / radius;
251 for (int j = 0; j < yres; ++j) {
252 for (int i = 0; i < xres; ++i) {
253 SCOLOR radiance = {0};
254 SCOLR sky_sclr = {0};
255
256 float px = i / (xres - 1.0);
257 float py = j / (yres - 1.0);
258 float lambda = ((1 - py) * PI) - (PI / 2.0);
259 float phi = (px * 2.0 * PI) - PI;
260
261 FVECT rdir = {cos(lambda) * cos(phi), cos(lambda) * sin(phi),
262 sin(lambda)};
263
264 const double mu = fdot(view_point, rdir) / radius;
265 const double nu = fdot(rdir, sundir);
266
267 /* hit ground */
268 if (rdir[2] < 0) {
269 get_ground_radiance(tau_clear, scat_clear, scat1m_clear, irrad_clear,
270 view_point, rdir, radius, mu, sun_ct, nu, grefl,
271 sundir, radiance);
272 } else {
273 get_sky_radiance(scat_clear, scat1m_clear, radius, mu, sun_ct, nu,
274 radiance);
275 }
276
277 for (int k = 0; k < NSSAMP; ++k) {
278 radiance[k] *= WVLSPAN;
279 }
280
281 if (cloud_cover > 0) {
282 double zenithbr = get_zenith_brightness(sundir);
283 double grndbr = zenithbr * GNORM;
284 double skybr = get_overcast_brightness(rdir[2], zenithbr);
285 if (rdir[2] < 0) {
286 for (int k = 0; k < NSSAMP; ++k) {
287 radiance[k] = wmean2(radiance[k], grndbr * D6415[k], cloud_cover);
288 }
289 } else {
290 for (int k = 0; k < NSSAMP; ++k) {
291 radiance[k] = wmean2(radiance[k], skybr * D6415[k], cloud_cover);
292 }
293 }
294 }
295
296 scolor2scolr(sky_sclr, radiance, 20);
297 putbinary(sky_sclr, LSCOLR, 1, skyfp);
298 }
299 }
300 fclose(skyfp);
301
302 /* Get solar radiance */
303 double sun_radiance[NSSAMP] = {0};
304 get_solar_radiance(tau_clear, scat_clear, scat1m_clear, sundir, radius,
305 sun_ct, sun_radiance);
306 if (cloud_cover > 0) {
307 double zenithbr = get_zenith_brightness(sundir);
308 double skybr = get_overcast_brightness(sundir[2], zenithbr);
309 for (int i = 0; i < NSSAMP; ++i) {
310 sun_radiance[i] =
311 wmean2(sun_radiance[i], D6415[i] * skybr / WVLSPAN, cloud_cover);
312 }
313 }
314
315 write_rad(sun_radiance, sundir, ddir, skyfile);
316 return 1;
317 }
318
319 static DpPaths get_dppaths(const char *dir, const double aod, const char *mname,
320 const char *tag) {
321 DpPaths paths;
322
323 snprintf(paths.tau, PATH_MAX, "%s%ctau_%s_%s_%.2f.dat", dir, DIRSEP, tag,
324 mname, aod);
325 snprintf(paths.scat, PATH_MAX, "%s%cscat_%s_%s_%.2f.dat", dir, DIRSEP, tag,
326 mname, aod);
327 snprintf(paths.scat1m, PATH_MAX, "%s%cscat1m_%s_%s_%.2f.dat", dir, DIRSEP,
328 tag, mname, aod);
329 snprintf(paths.irrad, PATH_MAX, "%s%cirrad_%s_%s_%.2f.dat", dir, DIRSEP, tag,
330 mname, aod);
331
332 return paths;
333 }
334
335 static void set_rayleigh_density_profile(Atmosphere *atmos, char *tag,
336 const int is_summer,
337 const double s_latitude) {
338 if (fabs(s_latitude * 180.0 / PI) > ARCTIC_LAT) {
339 tag[0] = 's';
340 if (is_summer) {
341 tag[1] = 's';
342 atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_SS;
343 atmos->beta_r0 = BR0_SS;
344 } else {
345 tag[1] = 'w';
346 atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_SW;
347 atmos->beta_r0 = BR0_SW;
348 }
349 } else if (fabs(s_latitude * 180.0 / PI) > TROPIC_LAT) {
350 tag[0] = 'm';
351 if (is_summer) {
352 tag[1] = 's';
353 atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_MS;
354 atmos->beta_r0 = BR0_MS;
355 } else {
356 tag[1] = 'w';
357 atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_MW;
358 atmos->beta_r0 = BR0_MW;
359 }
360 } else {
361 tag[0] = 't';
362 tag[1] = 'r';
363 atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_T;
364 atmos->beta_r0 = BR0_T;
365 }
366 tag[2] = '\0';
367 }
368
369 static Atmosphere init_atmos(const double aod, const double grefl) {
370 Atmosphere atmos = {.ozone_density = {.layers =
371 {
372 {.width = 25000.0,
373 .exp_term = 0.0,
374 .exp_scale = 0.0,
375 .linear_term = 1.0 / 15000.0,
376 .constant_term = -2.0 / 3.0},
377 {.width = AH,
378 .exp_term = 0.0,
379 .exp_scale = 0.0,
380 .linear_term = -1.0 / 15000.0,
381 .constant_term = 8.0 / 3.0},
382 }},
383 .rayleigh_density = {.layers =
384 {
385 {.width = AH,
386 .exp_term = 1.0,
387 .exp_scale = -1.0 / HR_MS,
388 .linear_term = 0.0,
389 .constant_term = 0.0},
390 }},
391 .beta_r0 = BR0_MS,
392 .beta_scale = aod / AOD0_CA,
393 .beta_m = NULL,
394 .grefl = grefl};
395 return atmos;
396 }
397
398 int main(int argc, char *argv[]) {
399 progname = argv[0];
400 int month, day;
401 double hour;
402 FVECT sundir;
403 int num_threads = 1;
404 int sorder = 4;
405 int year = 0;
406 int tsolar = 0;
407 int got_meridian = 0;
408 double grefl = 0.2;
409 double ccover = 0.0;
410 int res = 64;
411 double aod = AOD0_CA;
412 char *outname = "out";
413 char *mie_path = getpath("mie_ca.dat", getrlibpath(), R_OK);
414 char mie_name[20] = "mie_ca";
415 char lstag[3];
416 char *ddir = ".";
417
418 if (argc == 2 && !strcmp(argv[1], "-defaults")) {
419 printf("-i %d\t\t\t\t#scattering order\n", sorder);
420 printf("-g %f\t\t\t#ground reflectance\n", grefl);
421 printf("-c %f\t\t\t#cloud cover\n", ccover);
422 printf("-r %d\t\t\t\t#image resolution\n", res);
423 printf("-d %f\t\t\t#broadband aerosol optical depth\n", AOD0_CA);
424 printf("-f %s\t\t\t\t#output name (-f)\n", outname);
425 printf("-p %s\t\t\t\t#atmos data directory\n", ddir);
426 exit(0);
427 }
428
429 if (argc < 4) {
430 fprintf(stderr,
431 "Usage: %s month day hour -y year -a lat -o lon -m tz -d aod -r "
432 "res -n nproc -c ccover -l mie -g grefl -f outpath\n",
433 argv[0]);
434 return 0;
435 }
436
437 month = atoi(argv[1]);
438 if (month < 1 || month > 12) {
439 fprintf(stderr, "bad month");
440 exit(1);
441 }
442 day = atoi(argv[2]);
443 if (day < 1 || day > 31) {
444 fprintf(stderr, "bad month");
445 exit(1);
446 }
447 got_meridian = cvthour(argv[3], &tsolar, &hour);
448
449 if (!compute_sundir(year, month, day, hour, tsolar, sundir)) {
450 fprintf(stderr, "Cannot compute solar angle\n");
451 exit(1);
452 }
453
454 for (int i = 4; i < argc; i++) {
455 if (argv[i][0] == '-') {
456 switch (argv[i][1]) {
457 case 'a':
458 s_latitude = atof(argv[++i]) * (PI / 180.0);
459 break;
460 case 'c':
461 ccover = atof(argv[++i]);
462 break;
463 case 'd':
464 aod = atof(argv[++i]);
465 break;
466 case 'f':
467 outname = argv[++i];
468 break;
469 case 'g':
470 grefl = atof(argv[++i]);
471 break;
472 case 'i':
473 sorder = atoi(argv[++i]);
474 break;
475 case 'l':
476 mie_path = argv[++i];
477 basename(mie_path, mie_name, sizeof(mie_name));
478 break;
479 case 'm':
480 if (got_meridian) {
481 ++i;
482 break;
483 }
484 s_meridian = atof(argv[++i]) * (PI / 180.0);
485 break;
486 case 'n':
487 num_threads = atoi(argv[++i]);
488 break;
489 case 'o':
490 s_longitude = atof(argv[++i]) * (PI / 180.0);
491 break;
492 case 'p':
493 ddir = argv[++i];
494 break;
495 case 'r':
496 res = atoi(argv[++i]);
497 break;
498 case 'y':
499 year = atoi(argv[++i]);
500 break;
501 default:
502 fprintf(stderr, "Unknown option %s\n", argv[i]);
503 exit(1);
504 }
505 }
506 }
507 if (year && (year < 1950) | (year > 2050))
508 fprintf(stderr, "%s: warning - year should be in range 1950-2050\n",
509 progname);
510 if (month && !tsolar && fabs(s_meridian - s_longitude) > 45 * PI / 180)
511 fprintf(stderr,
512 "%s: warning - %.1f hours btwn. standard meridian and longitude\n",
513 progname, (s_longitude - s_meridian) * 12 / PI);
514
515 Atmosphere clear_atmos = init_atmos(aod, grefl);
516
517 int is_summer = (month >= SUMMER_START && month <= SUMMER_END);
518 if (s_latitude < 0) {
519 is_summer = !is_summer;
520 }
521 set_rayleigh_density_profile(&clear_atmos, lstag, is_summer, s_latitude);
522
523 /* Load mie density data */
524 DATARRAY *mie_dp = getdata(mie_path);
525 if (mie_dp == NULL) {
526 fprintf(stderr, "Error reading mie data\n");
527 return 0;
528 }
529 clear_atmos.beta_m = mie_dp;
530
531 char gsdir[PATH_MAX];
532 size_t siz = strlen(ddir);
533 if (ISDIRSEP(ddir[siz - 1]))
534 ddir[siz - 1] = '\0';
535 snprintf(gsdir, PATH_MAX, "%s%catmos_data", ddir, DIRSEP);
536 if (!make_directory(gsdir)) {
537 fprintf(stderr, "Failed creating atmos_data directory");
538 exit(1);
539 }
540 DpPaths clear_paths = get_dppaths(gsdir, aod, mie_name, lstag);
541
542 if (getpath(clear_paths.tau, ".", R_OK) == NULL ||
543 getpath(clear_paths.scat, ".", R_OK) == NULL ||
544 getpath(clear_paths.scat1m, ".", R_OK) == NULL ||
545 getpath(clear_paths.irrad, ".", R_OK) == NULL) {
546 printf("# Pre-computing...\n");
547 if (!precompute(sorder, clear_paths, &clear_atmos, num_threads)) {
548 fprintf(stderr, "Pre-compute failed\n");
549 return 0;
550 }
551 }
552
553 DATARRAY *tau_clear_dp = getdata(clear_paths.tau);
554 DATARRAY *irrad_clear_dp = getdata(clear_paths.irrad);
555 DATARRAY *scat_clear_dp = getdata(clear_paths.scat);
556 DATARRAY *scat1m_clear_dp = getdata(clear_paths.scat1m);
557
558 write_header(argc, argv, ccover, grefl, res);
559
560 if (!gen_spect_sky(tau_clear_dp, scat_clear_dp, scat1m_clear_dp,
561 irrad_clear_dp, ccover, sundir, grefl, res, outname,
562 ddir)) {
563 fprintf(stderr, "gen_spect_sky failed\n");
564 exit(1);
565 }
566
567 freedata(mie_dp);
568 freedata(tau_clear_dp);
569 freedata(scat_clear_dp);
570 freedata(irrad_clear_dp);
571 freedata(scat1m_clear_dp);
572
573 return 1;
574 }