htrdr

Solving radiative transfer in heterogeneous media
git clone git://git.meso-star.fr/htrdr.git
Log | Files | Refs | README | LICENSE

commit 0a245b626cec7aa9277617b88af35b536a1d9aa1
parent 2aea236e5872a1db38342b48be307e03289681f5
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  7 Dec 2022 19:02:24 +0100

core: add the discrete wavelength distribution

Diffstat:
Mcmake/core/CMakeLists.txt | 2++
Asrc/core/htrdr_ran_wlen_discrete.c | 334+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/core/htrdr_ran_wlen_discrete.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 398 insertions(+), 0 deletions(-)

diff --git a/cmake/core/CMakeLists.txt b/cmake/core/CMakeLists.txt @@ -89,6 +89,7 @@ set(HTRDR_CORE_FILES_SRC htrdr_log.c htrdr_materials.c htrdr_ran_wlen_cie_xyz.c + htrdr_ran_wlen_discrete.c htrdr_ran_wlen_planck.c htrdr_rectangle.c htrdr_slab.c @@ -104,6 +105,7 @@ set(HTRDR_CORE_FILES_INC htrdr_log.h htrdr_materials.h htrdr_ran_wlen_cie_xyz.h + htrdr_ran_wlen_discrete.h htrdr_ran_wlen_planck.h htrdr_rectangle.h htrdr_sensor.h diff --git a/src/core/htrdr_ran_wlen_discrete.c b/src/core/htrdr_ran_wlen_discrete.c @@ -0,0 +1,334 @@ +/* Copyright (C) 2018, 2019, 2020, 2021 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019, 2021 CNRS + * Copyright (C) 2018, 2019, Université Paul Sabatier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* nextafter */ + +#include "core/htrdr_c.h" +#include "core/htrdr_log.h" +#include "core/htrdr_ran_wlen_discrete.h" + +#include <rsys/algorithm.h> +#include <rsys/dynamic_array_double.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +#include <math.h> + +struct htrdr_ran_wlen_discrete { + struct darray_double cumul; + struct darray_double proba; + struct darray_double radia; /* In W/m²/sr/m */ + struct darray_double wlens; /* In nm */ + double range[2]; /* Boundaries of the spectral integration interval in nm */ + size_t nbands; /* #bands */ + + struct htrdr* htrdr; + ref_T ref; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE res_T +check_htrdr_ran_wlen_discrete_create_args + (const struct htrdr_ran_wlen_discrete_create_args* args) +{ + if(!args) return RES_BAD_ARG; + + /* Invalid number of wavelength */ + if(!args->nwavelengths) + return RES_BAD_ARG; + + /* Invalid functor */ + if(!args->get) + return RES_BAD_ARG; + + return RES_OK; +} + +static res_T +setup_per_wlen_radiance + (struct htrdr_ran_wlen_discrete* ran, + const struct htrdr_ran_wlen_discrete_create_args* args) +{ + double* wlens = NULL; + double* radia = NULL; + size_t iwlen = 0; + res_T res = RES_OK; + ASSERT(ran && args); + + res = darray_double_resize(&ran->wlens, args->nwavelengths); + if(res != RES_OK) { + htrdr_log_err(ran->htrdr, + "Error allocating discrete wavelength distribution wavelength list.\n"); + goto error; + } + res = darray_double_resize(&ran->radia, args->nwavelengths); + if(res != RES_OK) { + htrdr_log_err(ran->htrdr, + "Error allocating discrete wavelength distribution radiance list.\n"); + goto error; + } + + wlens = darray_double_data_get(&ran->wlens); + radia = darray_double_data_get(&ran->radia); + + /* Store the discrete values */ + FOR_EACH(iwlen, 0, args->nwavelengths) { + args->get(iwlen, wlens+iwlen, radia+iwlen, args->context); + + if(iwlen > 0 && wlens[iwlen] <= wlens[iwlen-1]) { + htrdr_log_err(ran->htrdr, + "Failed to calculate discrete luminance distribution probabilities. " + "Wavelengths are not sorted in ascending order.\n"); + res = RES_BAD_ARG; + goto error; + } + } + + /* Setup the spectral range */ + ran->range[0] = wlens[0]; + ran->range[1] = wlens[args->nwavelengths-1]; + +exit: + return res; +error: + darray_double_clear(&ran->wlens); + darray_double_clear(&ran->radia); + goto exit; +} + +static res_T +setup_distribution + (struct htrdr_ran_wlen_discrete* ran, + const struct htrdr_ran_wlen_discrete_create_args* args) +{ + double* cumul = NULL; + double* proba = NULL; + double sum = 0; + size_t iband; + res_T res = RES_OK; + ASSERT(ran && check_htrdr_ran_wlen_discrete_create_args(args)); + ASSERT(ran->nbands >= 1); /* At least one band */ + + res = darray_double_resize(&ran->cumul, ran->nbands); + if(res != RES_OK) { + htrdr_log_err(ran->htrdr, + "Error allocating the cumulative discrete wavelength distribution.\n"); + goto error; + } + res = darray_double_resize(&ran->proba, ran->nbands); + if(res != RES_OK) { + htrdr_log_err(ran->htrdr, + "Error allocating the discrete wavelength distribution probabilities.\n"); + goto error; + } + + cumul = darray_double_data_get(&ran->cumul); + proba = darray_double_data_get(&ran->proba); + + /* Compute the unormalized probabilities to sample a band */ + FOR_EACH(iband, 0, ran->nbands) { + const size_t iw0 = iband+0; + const size_t iw1 = iband+1; + double w0, L0; + double w1, L1; + double area; + + args->get(iw0, &w0, &L0, args->context); + args->get(iw1, &w1, &L1, args->context); + ASSERT(w0 < w1); + + area = (L0 + L1) * (w1-w0) * 0.5; + + proba[iband] = area; + sum += area; + } + + /* Normalize the probabilities and setup the cumulative */ + FOR_EACH(iband, 0, ran->nbands) { + proba[iband] /= sum; + if(iband == 0) { + cumul[iband] = proba[iband]; + } else { + cumul[iband] = proba[iband] + cumul[iband-1]; + ASSERT(cumul[iband] >= cumul[iband-1]); + } + } + ASSERT(eq_eps(cumul[ran->nbands-1], 1, 1e-6)); + cumul[ran->nbands-1] = 1.0; /* Fix numerical imprecision */ + +exit: + return res; +error: + darray_double_clear(&ran->proba); + darray_double_clear(&ran->cumul); + goto exit; +} + +static void +release_discrete(ref_T* ref) +{ + struct htrdr_ran_wlen_discrete* ran = NULL; + struct htrdr* htrdr = NULL; + ASSERT(ref); + ran = CONTAINER_OF(ref, struct htrdr_ran_wlen_discrete, ref); + darray_double_release(&ran->cumul); + darray_double_release(&ran->proba); + darray_double_release(&ran->radia); + darray_double_release(&ran->wlens); + htrdr = ran->htrdr; + MEM_RM(htrdr_get_allocator(htrdr), ran); + htrdr_ref_put(htrdr); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +htrdr_ran_wlen_discrete_create + (struct htrdr* htrdr, + const struct htrdr_ran_wlen_discrete_create_args* args, + struct htrdr_ran_wlen_discrete** out_ran) +{ + struct htrdr_ran_wlen_discrete* ran = NULL; + res_T res = RES_OK; + ASSERT(htrdr && args && out_ran); + ASSERT(check_htrdr_ran_wlen_discrete_create_args(args) == RES_OK); + + ran = MEM_CALLOC(htrdr_get_allocator(htrdr), 1, sizeof(*ran)); + if(!ran) { + res = RES_MEM_ERR; + htrdr_log_err(htrdr, + "%s: error allocating discrete wavelength distribution\n", + FUNC_NAME); + goto error; + } + + ref_init(&ran->ref); + darray_double_init(htrdr_get_allocator(htrdr), &ran->cumul); + darray_double_init(htrdr_get_allocator(htrdr), &ran->proba); + darray_double_init(htrdr_get_allocator(htrdr), &ran->radia); + darray_double_init(htrdr_get_allocator(htrdr), &ran->wlens); + htrdr_ref_get(htrdr); + ran->htrdr = htrdr; + + ran->nbands = args->nwavelengths - 1; + + res = setup_per_wlen_radiance(ran, args); + if(res != RES_OK) goto error; + res = setup_distribution(ran, args); + if(res != RES_OK) goto error; + +exit: + *out_ran = ran; + return res; +error: + if(ran) { htrdr_ran_wlen_discrete_ref_put(ran); ran = NULL; } + goto exit; +} + +void +htrdr_ran_wlen_discrete_ref_get(struct htrdr_ran_wlen_discrete* ran) +{ + ASSERT(ran); + ref_get(&ran->ref); +} + +void +htrdr_ran_wlen_discrete_ref_put(struct htrdr_ran_wlen_discrete* ran) +{ + ASSERT(ran); + ref_put(&ran->ref, release_discrete); +} + +double +htrdr_ran_wlen_discrete_sample + (struct htrdr_ran_wlen_discrete* ran, + const double r0, + const double r1, + double* pdf) /* In nm⁻¹ */ +{ + double* find = NULL; + const double* proba = NULL; + const double* cumul = NULL; + const double* radia = NULL; + const double* wlens = NULL; + double w0, w1; /* Wavelengths of the sampled band in nm */ + double L0, L1; /* Radiance of the sampled band in W/m²/sr/m */ + double a, b, c, tmp; + double lambda; /* Sampled wavelength in nm */ + size_t iband = 0; + double r0_next = nextafter(r0, DBL_MAX); + ASSERT(0 <= r0 && r0 < 1); + ASSERT(0 <= r1 && r1 < 1); + + cumul = darray_double_cdata_get(&ran->cumul); + proba = darray_double_cdata_get(&ran->proba); + radia = darray_double_cdata_get(&ran->radia); + wlens = darray_double_cdata_get(&ran->wlens); + + /* Sample a band. Use r0_next rather than r0 to find the first entry that is + * not less than *or equal* to r0 */ + find = search_lower_bound + (&r0_next, cumul, ran->nbands, sizeof(double), cmp_dbl); + ASSERT(find); + + iband = (size_t)(find - cumul); + ASSERT(iband < ran->nbands); + ASSERT(cumul[iband] > r0 && (!iband || cumul[iband-1] <= r0)); + + /* Retrieve the boundaries of the sampled band */ + w0 = wlens[iband+0]; + w1 = wlens[iband+1]; + L0 = radia[iband+0]; + L1 = radia[iband+1]; + + /* Compute the parameters of the quadratic equation */ + tmp = (L1-L0) / (w1-w0); + a = 0.5 * tmp; + b = L0 - w0 * tmp; + c = 0.5 * tmp * w0*w0 - L0*w0 - r1 * 0.5 * (L0+L1)*(w1-w0); + + /* Sample lambda in the sampled band */ + if(a == 0) { + lambda = -c/b; + ASSERT(w0 <= lambda && lambda <= w1); + } else { + const double delta = b*b - 4*a*c; + const double sqrt_delta = sqrt(delta); + const double root0 = (-b + sqrt_delta) / (2*a); + const double root1 = (-b - sqrt_delta) / (2*a); + + if(w0 <= root0 && root0 <= w1) { + ASSERT(root1 < w0 || w1 < root1); + lambda = root0; + } else if(w0 <= root1 && root1 <= w1) { + ASSERT(root0 < w0 || w1 < root1); + lambda = root1; + } else { + FATAL("Unreachable code\n"); + } + } + + if(*pdf) { + const double proba_wlen = L0+(L1-L0)/(w1-w0)*(lambda-w0); + *pdf = proba[iband] * proba_wlen; + } + + return lambda; +} diff --git a/src/core/htrdr_ran_wlen_discrete.h b/src/core/htrdr_ran_wlen_discrete.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2018, 2019, 2020, 2021 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019, 2021 CNRS + * Copyright (C) 2018, 2019, Université Paul Sabatier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef HTRDR_RAN_WLEN_DISCRETE_H +#define HTRDR_RAN_WLEN_DISCRETE_H + +#include "core/htrdr.h" +#include <rsys/rsys.h> + +struct htrdr_ran_wlen_discrete_create_args { + void (*get) + (const size_t i, + double* wlen, /* In nanometer */ + double* radiance, /* In W/m²/sr/m */ + void* ctx); + size_t nwavelengths; + void* context; /* User defined data */ +}; + +/* Forware declarations */ +struct htrdr; +struct htrdr_ran_wlen_discrete; + +BEGIN_DECLS + +HTRDR_CORE_API res_T +htrdr_ran_wlen_discrete_create + (struct htrdr* htrdr, + const struct htrdr_ran_wlen_discrete_create_args* args, + struct htrdr_ran_wlen_discrete** ran); + +HTRDR_CORE_API void +htrdr_ran_wlen_discrete_ref_get + (struct htrdr_ran_wlen_discrete* ran); + +HTRDR_CORE_API void +htrdr_ran_wlen_discrete_ref_put + (struct htrdr_ran_wlen_discrete* ran); + +HTRDR_CORE_API double /* wavelength in nanometer */ +htrdr_ran_wlen_discrete_sample + (struct htrdr_ran_wlen_discrete* ran, + const double r0, const double r1, /* Canonical number in [0, 1[ */ + double* pdf); /* In nm⁻¹ */ + +END_DECLS + +#endif /* HTRDR_RAN_WLEN_DISCRETE_H */