ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/genglaze.c
Revision: 2.1
Committed: Mon May 5 19:20:16 2025 UTC (8 days, 19 hours ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Log Message:
feat(genglaze): New tool created by Taoning Wang added to distro

File Contents

# User Rev Content
1 greg 2.1 /*
2     Glazing system multi-layer optics calculation.
3     Source equations: LBNL WINDOW technical documention.
4     System calcuation: Section 7.6.1
5     Anuglar calculation Section 7.7
6     T. Wang
7     */
8    
9     #include "rtmath.h"
10     #include "color.h"
11     #include "data.h"
12    
13     enum {
14     NTHETA = 59,
15     MAX_NAME = 256,
16     };
17    
18     const double DBL_EPSILON = 1E-9;
19     const double FLT_MAX = 3.40282347E+38;
20     const double RAD_PER_DEG = 0.0174532925199;
21     const double COEFFS_TAU_CLEAR[] = {-0.0015, 3.355, -3.84, 1.46, 0.0288};
22     const double COEFFS_TAU_BRONZE[] = {0.002, 2.813, -2.341, -0.05725, 0.599};
23     const double COEFFS_RHO_CLEAR[] = {0.999, -0.563, 2.043, -2.532, 1.054};
24     const double COEFFS_RHO_BRONZE[] = {0.997, -1.868, 6.513, -7.862, 3.225};
25    
26     const double THETAS[NTHETA] = {
27     0., 5., 10., 15., 20., 25., 30., 35., 40.,
28     41., 42., 43., 44., 45., 46., 47., 48., 49, 50.,
29     51., 52., 53., 54., 55., 56., 57., 58., 59, 60.,
30     61., 62., 63., 64., 65., 66., 67., 68., 69, 70.,
31     71., 72., 73., 74., 75., 76., 77., 78., 79, 80.,
32     81., 82., 83., 84., 85., 86., 87., 88., 89, 90.,
33     };
34    
35    
36     char *progname;
37    
38     typedef struct {
39     char *filename;
40     int is_mono;
41     double thickness_m;
42    
43     /* Interpolated data at standard wavelengths */
44     double *t_0;
45     double *rf_0;
46     double *rb_0;
47    
48     /* Calculated angular data */
49     double *t_lambda_theta;
50     double *rf_lambda_theta;
51     double *rb_lambda_theta;
52    
53     } GlazingLayer;
54    
55    
56     inline double polynomial_5(double x, const double coeffs[5]) {
57     return x * ( x * ( x * (coeffs[4] * x + coeffs[3]) + coeffs[2]) + coeffs[1]) + coeffs[0];
58     }
59    
60    
61     /* Calculate rho0 (unpolarized reflectance of a single surface) */
62     static void rho0_calc(const double *t_0, const double *r_0, const int nwvl, double *rho0) {
63     for (int i = 0; i < nwvl; ++i) {
64     const double beta = t_0[i] * t_0[i] - r_0[i] * r_0[i] + 2.0 * r_0[i] + 1.0;
65     double denominator = 2.0 * (2.0 - r_0[i]);
66     if (fabs(denominator) < DBL_EPSILON)
67     denominator = DBL_EPSILON;
68     double discriminant = beta * beta - 2.0 * denominator * r_0[i];
69     if (discriminant < 0.0)
70     discriminant = 0.0;
71     rho0[i] = (beta - sqrt(discriminant)) / denominator;
72     if (rho0[i] < 0.0)
73     rho0[i] = 0.0;
74     if (rho0[i] > 1.0)
75     rho0[i] = 1.0;
76     }
77     }
78    
79     /* refraction index calc */
80     static void n_calc(const double *rho_0, const int nwvl, double *n)
81     {
82     for (int i=0; i < nwvl; ++i) {
83     const double sqrt_rho = sqrt(rho_0[i]);
84     n[i] = ((1.0 - sqrt_rho) < DBL_EPSILON) ? 1.0 : (1.0 + sqrt_rho) / (1.0 - sqrt_rho);
85     }
86     }
87    
88     /* Calculate absoprtion coefficient */
89     static void alpha_calc(const double *t0, const double *r0, const double *rho0,
90     const double thickness_m, const int nwvl, double *abs_coeff)
91     {
92     for (int i = 0; i < nwvl; ++i) {
93     const double numerator = r0[i] - rho0[i];
94     const double denominator = rho0[i] * t0[i];
95     if (denominator > DBL_EPSILON && numerator > DBL_EPSILON) {
96     const double log_val = log(numerator / denominator);
97     abs_coeff[i] = -log_val / thickness_m;
98     } else {
99     abs_coeff[i] = 1.0;
100     }
101     if (abs_coeff[i] < 0.0 || isnan(abs_coeff[i]) || isinf(abs_coeff[i])) {
102     abs_coeff[i] = 0.0;
103     }
104     }
105     }
106    
107    
108     void angular_monolithic(GlazingLayer *layer, const int nwvl) {
109     double *rho_0 = malloc(nwvl * sizeof(double));
110     double *refrac = malloc(nwvl * sizeof(double));
111     double *abs_coeff = malloc(nwvl * sizeof(double));
112    
113     rho0_calc(layer->t_0, layer->rf_0, nwvl, rho_0);
114     n_calc(rho_0, nwvl, refrac);
115     alpha_calc(layer->t_0, layer->rf_0, rho_0, layer->thickness_m, nwvl, abs_coeff);
116    
117     for (int itheta = 0; itheta < NTHETA; ++itheta) {
118     double phi = THETAS[itheta] * RAD_PER_DEG;
119     double cos_phi = cos(phi);
120     double sin_phi = sin(phi);
121    
122     for (int iwvl = 0; iwvl < nwvl; ++iwvl) {
123     double sin_phi_prime_sq = sin_phi * sin_phi / (refrac[iwvl] * refrac[iwvl]);
124    
125     double cos_phi_prime = (sin_phi_prime_sq >= 1.0) ? 0.0 : sqrt(1.0 - sin_phi_prime_sq);
126    
127     double rho_lambda;
128     if (cos_phi_prime <= DBL_EPSILON) {
129     rho_lambda = 1.0;
130     } else {
131     double nf_cos_phi = refrac[iwvl] * cos_phi;
132     double nf_cos_phi_p = refrac[iwvl] * cos_phi_prime;
133     double par_num = nf_cos_phi - cos_phi_prime;
134     double par_den = nf_cos_phi + cos_phi_prime;
135     double per_num = nf_cos_phi_p - cos_phi;
136     double per_den = nf_cos_phi_p + cos_phi;
137     if (fabs(par_den) < DBL_EPSILON || fabs(per_den) < DBL_EPSILON) {
138     rho_lambda = 1.0;
139     } else {
140     double par_comp = pow(par_num / par_den, 2.0);
141     double per_comp = pow(per_num / per_den, 2.0);
142     rho_lambda = 0.5 * (par_comp + per_comp);
143     }
144     }
145     if (rho_lambda < 0.0)
146     rho_lambda = 0.0;
147     if (rho_lambda > 1.0)
148     rho_lambda = 1.0;
149    
150     double exp_arg = (cos_phi_prime < DBL_EPSILON) ? -FLT_MAX : (-abs_coeff[iwvl] * layer->thickness_m / cos_phi_prime);
151     /* Clamp exponent argument to prevent overflow in exp() */
152     if (exp_arg < -700.0)
153     exp_arg = -700.0;
154    
155     double T_internal = exp(exp_arg);
156    
157     double denominator = 1.0 - rho_lambda * rho_lambda * T_internal * T_internal;
158    
159     double t_lambda, r_lambda;
160    
161     if (fabs(denominator) < DBL_EPSILON || rho_lambda >= 1.0) {
162     t_lambda = 0.0;
163     r_lambda = 1.0;
164     } else {
165     double tau_lambda = 1.0 - rho_lambda;
166     t_lambda = tau_lambda * tau_lambda * T_internal / denominator;
167     r_lambda = rho_lambda * (1.0 + t_lambda * T_internal);
168    
169     }
170     if (t_lambda < 0.0)
171     t_lambda = 0.0;
172     if (t_lambda > 1.0)
173     t_lambda = 1.0;
174    
175     const int flat_idx = itheta * nwvl + iwvl;
176     layer->t_lambda_theta[flat_idx] = t_lambda;
177     layer->rf_lambda_theta[flat_idx] = r_lambda;
178     layer->rb_lambda_theta[flat_idx] = r_lambda;
179     }
180     }
181     }
182    
183    
184     void angular_coated(GlazingLayer *layer, const int nwvl) {
185     for (int itheta = 0; itheta < NTHETA; ++itheta) {
186     const double phi = THETAS[itheta] * RAD_PER_DEG;
187     const double cos_phi = cos(phi);
188    
189     for (int iwvl = 0; iwvl < nwvl; ++iwvl) {
190     const double t_0_lambda = layer->t_0[iwvl];
191     const double *coeffs_tau = (t_0_lambda > 0.645) ? COEFFS_TAU_CLEAR : COEFFS_TAU_BRONZE;
192     const double *coeffs_rho = (t_0_lambda > 0.645) ? COEFFS_RHO_CLEAR : COEFFS_RHO_BRONZE;
193    
194     const double tau_bar = polynomial_5(cos_phi, coeffs_tau);
195     const double rho_bar_term = polynomial_5(cos_phi, coeffs_rho);
196     const double rho_bar = rho_bar_term - tau_bar;
197    
198     const int flat_idx = itheta * nwvl + iwvl;
199     layer->t_lambda_theta[flat_idx] = t_0_lambda * tau_bar;
200     layer->rf_lambda_theta[flat_idx] = layer->rf_0[iwvl] * (1.0 - rho_bar) + rho_bar;
201     layer->rb_lambda_theta[flat_idx] = layer->rb_0[iwvl] * (1.0 - rho_bar) + rho_bar;
202     }
203     }
204     }
205    
206    
207     void multi_layer_calc(
208     GlazingLayer *layers,
209     int nlayers,
210     int nwvl,
211     double *total_t,
212     double *total_rf,
213     double *total_rb)
214     {
215     if (nlayers <= 0)
216     return;
217    
218     size_t total_size = (size_t)NTHETA * nwvl * sizeof(double);
219    
220     memcpy(total_t, layers[0].t_lambda_theta, total_size);
221     memcpy(total_rf, layers[0].rf_lambda_theta, total_size);
222     memcpy(total_rb, layers[0].rb_lambda_theta, total_size);
223    
224     if (nlayers == 1)
225     return;
226    
227     double *prev_t = malloc(total_size);
228     double *prev_rf = malloc(total_size);
229     double *prev_rb = malloc(total_size);
230    
231     if (!prev_t || !prev_rf || !prev_rb)
232     perror("Failed to allocate temporary storage in multi_layer");
233    
234     for (int j = 1; j < nlayers; ++j) {
235     memcpy(prev_t, total_t, total_size);
236     memcpy(prev_rf, total_rf, total_size);
237     memcpy(prev_rb, total_rb, total_size);
238    
239     double *t_j = layers[j].t_lambda_theta;
240     double *rf_j = layers[j].rf_lambda_theta;
241     double *rb_j = layers[j].rb_lambda_theta;
242    
243     for (int itheta = 0; itheta < NTHETA; ++itheta) {
244     for (int iwvl = 0; iwvl < nwvl; ++iwvl) {
245     const int idx = itheta * nwvl + iwvl;
246     double denominator = 1.0 - rf_j[idx] * prev_rb[idx];
247     if (fabs(denominator) < DBL_EPSILON) {
248     denominator = DBL_EPSILON;
249     }
250     total_t[idx] = prev_t[idx] * t_j[idx] / denominator;
251     total_rf[idx] = prev_rf[idx] + prev_t[idx] * prev_t[idx] * rf_j[idx] / denominator;
252     total_rb[idx] = rb_j[idx] + t_j[idx] * t_j[idx] * prev_rb[idx] / denominator;
253     }
254     }
255     }
256     free(prev_t);
257     free(prev_rf);
258     free(prev_rb);
259     }
260    
261    
262     int add_layer(GlazingLayer **layers_array, int *count, int *capacity,
263     const char *filename, double thickness_m, int is_monolithic) {
264     if (*count >= *capacity) {
265     *capacity = (*capacity == 0) ? 4 : *capacity * 2;
266     GlazingLayer *new_layers = realloc(*layers_array, *capacity * sizeof(GlazingLayer));
267     if (!new_layers) {
268     perror("Failed to reallocate memory for layers");
269     return 0;
270     }
271     *layers_array = new_layers;
272     }
273    
274     memset(&((*layers_array)[*count]), 0, sizeof(GlazingLayer));
275    
276     #ifdef _WIN32
277     (*layers_array)[*count].filename = _strdup(filename);
278     #else
279     (*layers_array)[*count].filename = strdup(filename);
280     #endif
281    
282     if (!(*layers_array)[*count].filename) {
283     perror("Failed to duplicate filename");
284     return 0;
285     }
286     (*layers_array)[*count].is_mono = is_monolithic;
287     (*layers_array)[*count].thickness_m = thickness_m;
288     (*layers_array)[*count].t_0 = NULL;
289     (*layers_array)[*count].rf_0 = NULL;
290     (*layers_array)[*count].rb_0 = NULL;
291     (*layers_array)[*count].t_lambda_theta = NULL;
292     (*layers_array)[*count].rf_lambda_theta = NULL;
293     (*layers_array)[*count].rb_lambda_theta = NULL;
294    
295     (*count)++;
296     return 1;
297     }
298    
299    
300     int interpolate(GlazingLayer *layer, const double wvl_start, const double wvl_end, const double wvl_interval, const int nwvl) {
301     DATARRAY *dp = getdata(layer->filename);
302     if (!dp) {
303     fprintf(stderr, "Error: Cannot open file '%s'\n", layer->filename);
304     return 0;
305     }
306    
307     layer->t_0 = malloc(nwvl * sizeof(double));
308     layer->rf_0 = malloc(nwvl * sizeof(double));
309     layer->rb_0 = malloc(nwvl * sizeof(double));
310    
311     double wvl = wvl_start;
312     int i = 0;
313     while (wvl <= wvl_end) {
314     double t_pt[2] = {2., wvl};
315     layer->t_0[i] = datavalue(dp, t_pt);
316     double rf_pt[2] = {0., wvl};
317     layer->rf_0[i] = datavalue(dp, rf_pt);
318     double rb_pt[2] = {1., wvl};
319     layer->rb_0[i] = datavalue(dp, rb_pt);
320     wvl = wvl + wvl_interval;
321     i = i + 1;
322    
323     }
324     freedata(dp);
325     return 1;
326     }
327    
328    
329     int write_output_file(
330     const char *tfilename,
331     const char *rfilename,
332     const double *tdata,
333     const double *rfdata,
334     const double *rbdata,
335     const double wvl_start,
336     const double wvl_end,
337     const int nwvl,
338     int argc, char *argv[])
339     {
340     FILE *tfp = fopen(tfilename, "w");
341     FILE *rfp = fopen(rfilename, "w");
342     if (!tfp || !rfp) {
343     fprintf(stderr, "Error: Cannot open output files\n");
344     return(0);
345     }
346    
347     fprintf(tfp, "# ");
348     for (int i = 0; i< argc; ++i) {
349     fprintf(tfp, "%s ", argv[i]);
350     }
351     fprintf(tfp, "\n2\n0 0 %d", NTHETA);
352     for (int i = 0; i < NTHETA; i++) {
353     fprintf(tfp, " %d", (int)THETAS[i]);
354     }
355     fprintf(tfp, "\n%.0f %.0f %d\n", wvl_start, wvl_end, nwvl);
356    
357     for (int itheta = 0; itheta < NTHETA; ++itheta) {
358     for (int iwvl = 0; iwvl < nwvl; ++iwvl) {
359     fprintf(tfp, "%.6f\n", tdata[itheta*nwvl+iwvl]);
360     }
361     }
362     fclose(tfp);
363    
364     fprintf(rfp, "# ");
365     for (int i = 0; i< argc; ++i) {
366     fprintf(rfp, "%s ", argv[i]);
367     }
368     fprintf(rfp, "\n2\n0 0 %d", NTHETA * 2 - 1);
369     for (int i = 0; i < NTHETA; i++) {
370     fprintf(rfp, " %d", (int)THETAS[i]);
371     }
372     for (int i = NTHETA-2; i >= 0; --i) {
373     fprintf(rfp, " %d", (int)(180 - THETAS[i]));
374     }
375     fprintf(rfp, "\n%.0f %.0f %d\n", wvl_start, wvl_end, nwvl);
376    
377     for (int itheta = 0; itheta < NTHETA; ++itheta) {
378     for (int iwvl = 0; iwvl < nwvl; ++iwvl) {
379     fprintf(rfp, "%.6f\n", rfdata[itheta * nwvl + iwvl]);
380     }
381     }
382     for (int itheta = NTHETA - 2; itheta >= 0 ; --itheta) {
383     for (int iwvl = 0; iwvl < nwvl; ++iwvl) {
384     fprintf(rfp, "%.6f\n", rbdata[itheta * nwvl + iwvl]);
385     }
386     }
387    
388     fclose(rfp);
389     return(1);
390     }
391    
392    
393     void print_usage() {
394     fprintf(stderr, "Usage: %s [-m monolithic_layer.dat thickness | -c coated_layer.dat] ...\n", progname);
395     fprintf(stderr, "Calculate multi-layer glazing optics from spectral data files.\n");
396     fprintf(stderr, "Options:\n");
397     fprintf(stderr, " -m <filename> thickness Specify an uncoated (monolithic) glazing layer .dat file and its thickness (meter).\n");
398     fprintf(stderr, " -c <filename> Specify a coated or laminate glazing layer .dat file.\n");
399     fprintf(stderr, " -s start_wvl end_wvl interval Specify wavelength range and interval.\n");
400     fprintf(stderr, " -p prefix Specify prefix name to the output files.\n");
401     fprintf(stderr, " -h, --help Show this help message.\n");
402     fprintf(stderr, "Layer order is determined by the sequence of options on the command line.\n");
403     fprintf(stderr, "Output Files: prefix_t.dat, prefix_r.dat\n");
404     }
405    
406     void cleanup_layers(GlazingLayer *layers, int num_layers) {
407     if (!layers) return;
408     for (int i = 0; i < num_layers; ++i) {
409     if (layers[i].filename)
410     free(layers[i].filename);
411     }
412     free(layers);
413     }
414    
415    
416     int main(int argc, char *argv[])
417     {
418     GlazingLayer *layers = NULL;
419     int num_layers = 0;
420     int layer_capacity = 0;
421     int success = 1;
422     double wvl_start_nm = 380.;
423     double wvl_end_nm = 780.;
424     double wvl_interval_nm = 5.;
425     int nwvl = 81;
426     double thickness_m = 0.003;
427     char *filename;
428     char *prefix = "unnamed";
429     char file_t[MAX_NAME];
430     char file_r[MAX_NAME];
431     progname = argv[0];
432     if (argc <= 1) {
433     print_usage();
434     return EXIT_FAILURE;
435     }
436    
437     for (int i=1; i < argc; ++i) {
438     switch (argv[i][1]) {
439     case 'm':
440     filename = argv[++i];
441     thickness_m = atof(argv[++i]);
442     if (!add_layer(&layers, &num_layers, &layer_capacity, filename, thickness_m, 1)) {
443     cleanup_layers(layers, num_layers);
444     return EXIT_FAILURE;
445     }
446     break;
447     case 'c':
448     filename = argv[++i];
449     if (!add_layer(&layers, &num_layers, &layer_capacity, filename, thickness_m, 0)) {
450     cleanup_layers(layers, num_layers);
451     return EXIT_FAILURE;
452     }
453     break;
454     case 'p':
455     prefix = argv[++i];
456     break;
457     case 's':
458     wvl_start_nm = atof(argv[++i]);
459     wvl_end_nm = atof(argv[++i]);
460     wvl_interval_nm = atof(argv[++i]);
461     if (wvl_start_nm > wvl_end_nm) {
462     fprintf(stderr, "Starting wavelength > End wavelength\n");
463     return EXIT_FAILURE;
464     }
465     if (((int)(wvl_end_nm - wvl_start_nm) % (int)wvl_interval_nm) > 0) {
466     fprintf(stderr,
467     "Error: Wavelength range (%f to %f nm) must be evenly divisible by the interval (%f nm).\n",
468     wvl_start_nm, wvl_end_nm, wvl_interval_nm);
469     return EXIT_FAILURE;
470     }
471     break;
472     case 'h':
473     print_usage();
474     return 1;
475     default:
476     break;
477     }
478     }
479     snprintf(file_t, MAX_NAME, "%s_t.dat", prefix);
480     snprintf(file_r, MAX_NAME, "%s_r.dat", prefix);
481    
482     if (fabs(wvl_start_nm - wvl_end_nm) < DBL_EPSILON && wvl_interval_nm > DBL_EPSILON) {
483     nwvl = 1;
484     } else {
485     nwvl = (int)((wvl_end_nm - wvl_start_nm) / wvl_interval_nm + 1.5);
486     }
487    
488     if (num_layers == 0) {
489     fprintf(stderr, "Error: No layers specified.\n");
490     print_usage();
491     return EXIT_FAILURE;
492     }
493    
494     for (int i = 0; i < num_layers; ++i) {
495     if (!interpolate(&layers[i], wvl_start_nm, wvl_end_nm, wvl_interval_nm, nwvl)) {
496     fprintf(stderr, "Error: Failed to parse or interpolate data for layer %d.\n", i + 1);
497     success = 0;
498     break;
499     }
500     layers[i].t_lambda_theta = malloc(NTHETA * nwvl * sizeof(double));
501     layers[i].rf_lambda_theta = malloc(NTHETA * nwvl * sizeof(double));
502     layers[i].rb_lambda_theta = malloc(NTHETA * nwvl * sizeof(double));
503    
504     if (layers[i].is_mono) {
505     angular_monolithic(&layers[i], nwvl);
506     } else {
507     angular_coated(&layers[i], nwvl);
508     }
509     }
510    
511     if (!success) {
512     cleanup_layers(layers, num_layers);
513     return EXIT_FAILURE;
514     }
515    
516    
517     size_t total_flat_size = (size_t)NTHETA * nwvl * sizeof(double);
518     double *total_t = malloc(total_flat_size);
519     double *total_rf = malloc(total_flat_size);
520     double *total_rb = malloc(total_flat_size);
521    
522     if (!total_t || !total_rf || !total_rb) {
523     perror("Failed to allocate memory for final results");
524     cleanup_layers(layers, num_layers);
525     free(total_t);
526     free(total_rf);
527     free(total_rb);
528     return EXIT_FAILURE;
529     }
530    
531     multi_layer_calc(layers, num_layers, nwvl, total_t, total_rf, total_rb);
532    
533     success &= write_output_file(file_t, file_r, total_t, total_rf, total_rb, wvl_start_nm, wvl_end_nm, nwvl, argc, argv);
534    
535     if (success) {
536     printf("# ");
537     for (int i = 0;i < argc; ++i) {
538     printf("%s ", argv[i]);
539     }
540     printf("\nvoid specdata refl_spec\n");
541     printf("4 noop %s . 'Acos(Rdot)/DEGREE'\n0\n0\n\n", file_r);
542     printf("void specdata trans_spec\n");
543     printf("4 noop %s . 'Acos(abs(Rdot))/DEGREE'\n0\n0\n\n", file_t);
544     printf("void WGMDfunc glaze_mat\n13\n\trefl_spec 1 0 0\n\ttrans_spec 1 0 0\n\tvoid\n\t0 0 1 .\n0\n9 0 0 0 0 0 0 0 0 0\n");
545     } else {
546     fprintf(stderr, "Error: Failed to write one or more output files.\n");
547     }
548    
549     cleanup_layers(layers, num_layers);
550     free(total_t);
551     free(total_rf);
552     free(total_rb);
553    
554     return success ? EXIT_SUCCESS : EXIT_FAILURE;
555     }