htgop

Optical properties of a gas mixture
git clone git://git.meso-star.fr/htgop.git
Log | Files | Refs | README | LICENSE

commit 4f403b04ecbac79a0fae222e0051812ed0b6ca3b
parent afb9054ca9e495bd48cf8481f3e868ec190f29b7
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 27 Jul 2018 16:51:30 +0200

Compute the SW CDFs wrt the CIE XYZ tristimulus

Diffstat:
Mcmake/CMakeLists.txt | 4++++
Msrc/htgop.c | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/htgop_c.h | 15+++++++++++++++
3 files changed, 229 insertions(+), 3 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -62,6 +62,10 @@ rcmake_prepend_path(HTGOP_FILES_DOC ${PROJECT_SOURCE_DIR}/../) add_library(htgop SHARED ${HTGOP_FILES_SRC} ${HTGOP_FILES_INC} ${HTGOP_FILES_INC_API}) target_link_libraries(htgop RSys) +if(CMAKE_COMPILER_IS_GNUCC) + target_link_libraries(htgop m) +endif() + set_target_properties(htgop PROPERTIES DEFINE_SYMBOL HTGOP_SHARED_BUILD VERSION ${VERSION} diff --git a/src/htgop.c b/src/htgop.c @@ -21,6 +21,16 @@ #include <rsys/logger.h> #include <rsys/mem_allocator.h> +#include <math.h> + +#define WNUM_TO_WLEN(Num) (1.0e7 / ((double)Num)) +#define WLEN_TO_WNUM(Len) WNUM_TO_WLEN(Len) + +#define CIE_XYZ_WAVELENGTH_MIN 380.0 /* In nanometer */ +#define CIE_XYZ_WAVELENGTH_MAX 780.0 /* In nanometer */ +#define CIE_XYZ_WAVENUMBER_MIN WLEN_TO_WNUM(CIE_XYZ_WAVELENGTH_MAX) /*In cm^-1*/ +#define CIE_XYZ_WAVENUMBER_MAX WLEN_TO_WNUM(CIE_XYZ_WAVELENGTH_MIN) /*In cm^-1*/ + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -39,6 +49,178 @@ log_msg } } +static FINLINE double +trapezoidal_integration + (const double lambda_lo, /* Integral lower bound. In nanometer */ + const double lambda_hi, /* Integral upper bound. In nanometer */ + double (*f_bar)(const double lambda)) /* Function to integrate */ +{ + double dlambda; + size_t i, n; + double integral; + ASSERT(lambda_lo <= lambda_hi); + ASSERT(lambda_lo > 0); + + n = (size_t)(lambda_hi - lambda_lo) + 1; + dlambda = (lambda_hi - lambda_lo) / (double)n; + + FOR_EACH(i, 0, n) { + const double lambda1 = lambda_lo + dlambda*(double)(i+0); + const double lambda2 = lambda_hi + dlambda*(double)(i+1); + const double f1 = f_bar(lambda1); + const double f2 = f_bar(lambda2); + integral += (f1 + f2)*dlambda*0.5; + } + return integral; +} + +/* The following 3 functions are used to fit the CIE Xbar, Ybar and Zbar curved + * has defined by the 1931 standard observers. These analytical fits are + * propsed by C. Wyman, P. P. Sloan & P. Shirley in "Simple Analytic + * Approximations to the CIE XYZ Color Matching Functions" - JCGT 2013. */ +static FINLINE double +fit_x_bar_1931(const double lambda) +{ + const double a = (lambda - 442.0) * (lambda < 442.0 ? 0.0624 : 0.0374); + const double b = (lambda - 599.8) * (lambda < 599.8 ? 0.0264 : 0.0323); + const double c = (lambda - 501.1) * (lambda < 501.1 ? 0.0490 : 0.0382); + return 0.362*exp(-0.5*a*a) + 1.056*exp(-0.5f*b*b) - 0.065*exp(-0.5*c*c); +} + +static FINLINE double +fit_y_bar_1931(const double lambda) +{ + const double a = (lambda - 568.8) * (lambda < 568.8 ? 0.0213 : 0.0247); + const double b = (lambda - 530.9) * (lambda < 530.9 ? 0.0613 : 0.0322); + return 0.821*exp(-0.5*a*a) + 0.286*exp(-0.5*b*b); +} + +static FINLINE double +fit_z_bar_1931(const double lambda) +{ + const double a = (lambda - 437.0) * (lambda < 437.0 ? 0.0845 : 0.0278); + const double b = (lambda - 459.0) * (lambda < 459.0 ? 0.0385 : 0.0725); + return 1.217*exp(-0.5*a*a) + 0.681*exp(-0.5*b*b); +} + +static res_T +compute_CIE_XYZ_cdf(struct htgop* htgop) +{ + enum { X, Y, Z }; /* Helper constant */ + size_t iband_min, iband_max; + const double* wnums = NULL; + double sum[3] = {0,0,0}; + double* pdf[3] = {NULL, NULL, NULL}; + double* cdf[3] = {NULL, NULL, NULL}; + size_t i, n; + res_T res = RES_OK; + ASSERT(htgop); + + HTGOP(get_sw_spectral_intervals_wave_numbers(htgop, &wnums)); + HTGOP(get_sw_spectral_intervals_count(htgop, &n)); + ASSERT(n); + + /* Allocate and reset the memory space for the tristimulus CDF */ + #define SETUP(Stimulus) { \ + res = darray_double_resize(&htgop->sw_ ## Stimulus ## _cdf, n); \ + if(res != RES_OK) { \ + log_err(htgop, \ + "Could not reserve the memory space for the CDF " \ + "of the "STR(X)" stimulus.\n"); \ + goto error; \ + } \ + memset(darray_double_data_get(&htgop->sw_ ## Stimulus ## _cdf), 0, \ + n*sizeof(double)); \ + cdf[Stimulus] = darray_double_data_get(&htgop->sw_ ## Stimulus ## _cdf); \ + pdf[Stimulus] = cdf[Stimulus]; \ + } (void)0 + SETUP(X); + SETUP(Y); + SETUP(Z); + #undef RESERVE + + ASSERT(eq_eps(CIE_XYZ_WAVENUMBER_MAX, WLEN_TO_WNUM(CIE_XYZ_WAVELENGTH_MIN), 1.e-6)); + ASSERT(eq_eps(CIE_XYZ_WAVENUMBER_MIN, WLEN_TO_WNUM(CIE_XYZ_WAVELENGTH_MAX), 1.e-6)); + ASSERT(eq_eps(CIE_XYZ_WAVELENGTH_MAX, WNUM_TO_WLEN(CIE_XYZ_WAVENUMBER_MIN), 1.e-6)); + ASSERT(eq_eps(CIE_XYZ_WAVELENGTH_MIN, WNUM_TO_WLEN(CIE_XYZ_WAVENUMBER_MAX), 1.e-6)); + + /* The sw spectral intervals does not intersect the CIE XYZ interval */ + if(wnums[0] > CIE_XYZ_WAVENUMBER_MAX || wnums[n] < CIE_XYZ_WAVENUMBER_MIN) { + log_warn(htgop, + "The loaded SW spectral intervals does not intersect " + "the CIE XYZ interval.\n"); + goto exit; + } + + /* Search the lower and upper bound of the bands that overlap the XYZ + * spectral interval. Use a simple linear search since the overall number of + * bands should be small */ + iband_min = 0, iband_max = n-1; + FOR_EACH(i, 0, n) { + if(wnums[i]<=CIE_XYZ_WAVENUMBER_MIN && CIE_XYZ_WAVENUMBER_MIN<=wnums[i+1]) + iband_min = i; + if(wnums[i]<=CIE_XYZ_WAVENUMBER_MAX && CIE_XYZ_WAVENUMBER_MAX<=wnums[i+1]) + iband_max = i; + } + ASSERT(iband_min <= iband_max); + + /* Compute the *unormalized* pdf of the tristimulus */ + FOR_EACH(i, iband_min, iband_max+1) { + const double lambda_lo = WNUM_TO_WLEN(wnums[i+1]); + const double lambda_hi = WNUM_TO_WLEN(wnums[i+0]); + ASSERT(lambda_lo <= lambda_hi); + pdf[X][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_x_bar_1931); + pdf[Y][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_y_bar_1931); + pdf[Z][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_z_bar_1931); + sum[X] += pdf[X][i]; + sum[Y] += pdf[Y][i]; + sum[Z] += pdf[Z][i]; + } + + /* Compute the cdf of the tristimulus. Note that the upper bound is 'n' and not + * 'iband_max+1' in order to ensure that the CDF is strictly increasing. */ + FOR_EACH(i, iband_min, n) { + /* Normalize the pdf */ + pdf[X][i] /= sum[X]; + pdf[Y][i] /= sum[Y]; + pdf[Z][i] /= sum[Z]; + /* Setup the cumulative */ + if(i == 0) { + cdf[X][i] = pdf[X][i]; + cdf[Y][i] = pdf[Y][i]; + cdf[Z][i] = pdf[Z][i]; + } else { + cdf[X][i] = pdf[X][i] + cdf[X][i-1]; + cdf[Y][i] = pdf[Y][i] + cdf[Y][i-1]; + cdf[Z][i] = pdf[Z][i] + cdf[Z][i-1]; + } + } + ASSERT(eq_eps(cdf[X][n-1], 1, 1.e-6)); + ASSERT(eq_eps(cdf[Y][n-1], 1, 1.e-6)); + ASSERT(eq_eps(cdf[Z][n-1], 1, 1.e-6)); + +#ifndef NDEBUG + FOR_EACH(i, 1, n) { + ASSERT(cdf[X][i] >= cdf[X][i-1]); + ASSERT(cdf[Y][i] >= cdf[Y][i-1]); + ASSERT(cdf[Z][i] >= cdf[Z][i-1]); + } +#endif + + /* Handle numerical issue */ + cdf[X][n-1] = 1.0; + cdf[Y][n-1] = 1.0; + cdf[Z][n-1] = 1.0; + +exit: + return res; +error: + darray_double_clear(&htgop->sw_X_cdf); + darray_double_clear(&htgop->sw_Y_cdf); + darray_double_clear(&htgop->sw_Z_cdf); + goto exit; +} + static res_T parse_spectral_intervals (struct htgop* htgop, @@ -171,7 +353,12 @@ load_stream(struct htgop* htgop, FILE* stream, const char* stream_name) ASSERT(htgop && stream && stream_name); reader_init(&rdr, stream, stream_name); - #define CALL(Func) { if((res = Func) != RES_OK) goto error; } + #define CALL(Func) { \ + if((res = Func) != RES_OK) { \ + log_err(htgop, "%s:%lu: Parsing error.\n", rdr.name, rdr.iline); \ + goto error; \ + } \ + } (void)0 /* Parse the number of levels/layers */ CALL(cstr_to_ulong(read_line(&rdr), &nlvls)); @@ -223,7 +410,7 @@ load_stream(struct htgop* htgop, FILE* stream, const char* stream_name) CALL(cstr_to_double(read_line(&rdr), &htgop->ground.lw_emissivity)); CALL(cstr_to_double(read_line(&rdr), &htgop->ground.sw_emissivity)); - /* Parse the numver of long/short wave spectral intervals */ + /* Parse the number of long/short wave spectral intervals */ CALL(cstr_to_ulong(read_line(&rdr), &nspecints_lw)); CALL(cstr_to_ulong(read_line(&rdr), &nspecints_sw)); @@ -236,11 +423,14 @@ load_stream(struct htgop* htgop, FILE* stream, const char* stream_name) #undef CALL + /* Compute the cdf for the SW CIE XYZ spectral interval */ + res = compute_CIE_XYZ_cdf(htgop); + if(res != RES_OK) goto error; + exit: reader_release(&rdr); return res; error: - log_err(htgop, "%s:%lu: Parsing error.\n", rdr.name, rdr.iline); goto exit; } @@ -252,6 +442,9 @@ release_htgop(ref_T* ref) htgop = CONTAINER_OF(ref, struct htgop, ref); spectral_intervals_release(&htgop->lw_specints); spectral_intervals_release(&htgop->sw_specints); + darray_double_release(&htgop->sw_X_cdf); + darray_double_release(&htgop->sw_Y_cdf); + darray_double_release(&htgop->sw_Z_cdf); darray_level_release(&htgop->levels); darray_layer_release(&htgop->layers); MEM_RM(htgop->allocator, htgop); @@ -296,6 +489,9 @@ htgop_create htgop->verbose = verbose; spectral_intervals_init(allocator, &htgop->lw_specints); spectral_intervals_init(allocator, &htgop->sw_specints); + darray_double_init(allocator, &htgop->sw_X_cdf); + darray_double_init(allocator, &htgop->sw_Y_cdf); + darray_double_init(allocator, &htgop->sw_Z_cdf); darray_level_init(allocator, &htgop->levels); darray_layer_init(allocator, &htgop->layers); @@ -598,3 +794,14 @@ log_err(const struct htgop* htgop, const char* msg, ...) va_end(vargs_list); } +void +log_warn(const struct htgop* htgop, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(htgop && msg); + + va_start(vargs_list, msg); + log_msg(htgop, LOG_WARNING, msg, vargs_list); + va_end(vargs_list); +} + diff --git a/src/htgop_c.h b/src/htgop_c.h @@ -34,6 +34,11 @@ struct htgop { struct spectral_intervals lw_specints; struct spectral_intervals sw_specints; + /* Cumulative distribution function for the Short Wave CIE XYZ tristimulus */ + struct darray_double sw_X_cdf; + struct darray_double sw_Y_cdf; + struct darray_double sw_Z_cdf; + struct darray_layer layers; /* Par layer data */ struct darray_level levels; /* Per level data (#level = #layer + 1 ) */ @@ -53,5 +58,15 @@ log_err #endif ; +extern LOCAL_SYM void +log_warn + (const struct htgop* htgop, + const char* fmt, + ...) +#ifdef COMPILER_GCC + __attribute((format(printf, 2, 3))) +#endif + ; + #endif /* HTGOP_C_H */