htrdr

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

commit 14f76f7e274d6364db4fb5daeb5c72ff36522f35
parent eb83e35c47e11f837b72594f6033fc208c195882
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu,  4 Jun 2020 15:21:43 +0200

Add the Planck reference temperature as a command line option

Diffstat:
Mcmake/CMakeLists.txt | 2++
Msrc/htrdr.c | 73+++++++------------------------------------------------------------------
Msrc/htrdr.h | 9+++------
Msrc/htrdr_args.c | 23+++++++++++++++++++++++
Msrc/htrdr_args.h.in | 6++++--
Msrc/htrdr_c.h | 109-------------------------------------------------------------------------------
Msrc/htrdr_ran_wlen.h | 2--
Asrc/htrdr_spectral.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/htrdr_spectral.h | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 255 insertions(+), 185 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -103,6 +103,7 @@ set(HTRDR_FILES_SRC htrdr_ran_wlen.c htrdr_rectangle.c htrdr_slab.c + htrdr_spectral.c htrdr_sun.c) set(HTRDR_FILES_INC htrdr.h @@ -117,6 +118,7 @@ set(HTRDR_FILES_INC htrdr_ran_wlen.h htrdr_rectangle.h htrdr_slab.h + htrdr_spectral.h htrdr_sun.h htrdr_solve.h) set(HTRDR_FILES_INC2 # Generated files diff --git a/src/htrdr.c b/src/htrdr.c @@ -428,6 +428,7 @@ htrdr_init htrdr->grid_max_definition[1] = args->grid_max_definition[1]; htrdr->grid_max_definition[2] = args->grid_max_definition[2]; htrdr->spectral_type = args->spectral_type; + htrdr->ref_temperature = args->ref_temperature; res = init_mpi(htrdr); if(res != RES_OK) goto error; @@ -524,20 +525,20 @@ htrdr_init if(res != RES_OK) goto error; } else { - double Tref; /* In Kelvin */ size_t n; - switch(htrdr->spectral_type) { - case HTRDR_SPECTRAL_LW: Tref = 290; break; - case HTRDR_SPECTRAL_SW: Tref = 5778; /* Tsun */ break; - default: FATAL("Unreachable code.\n"); break; + if(htrdr->ref_temperature <= 0) { + htrdr_log_err(htrdr, "%s: invalid reference temperature %g K.\n", + FUNC_NAME, htrdr->ref_temperature); + res = RES_BAD_ARG; + goto error; } ASSERT(htrdr->wlen_range_m[0] <= htrdr->wlen_range_m[1]); n = (size_t)(spectral_range[1] - spectral_range[0]); res = htrdr_ran_wlen_create - (htrdr, spectral_range, n, Tref, &htrdr->ran_wlen); + (htrdr, spectral_range, n, htrdr->ref_temperature, &htrdr->ran_wlen); if(res != RES_OK) goto error; } @@ -766,66 +767,6 @@ compute_sky_min_band_len } res_T -brightness_temperature - (struct htrdr* htrdr, - const double lambda_min, - const double lambda_max, - const double radiance, /* In W/m2/sr/m */ - double* temperature) -{ - const size_t MAX_ITER = 100; - const double epsilon_T = 1e-4; /* In K */ - const double epsilon_B = radiance * 1e-8; - double T, T0, T1, T2; - double B, B0; - size_t i; - res_T res = RES_OK; - ASSERT(temperature && lambda_min <= lambda_max); - - /* Search for a brightness temperature whose radiance is greater than or - * equal to the estimated radiance */ - T2 = 200; - FOR_EACH(i, 0, MAX_ITER) { - const double B2 = planck(lambda_min, lambda_max, T2); - if(B2 >= radiance) break; - T2 *= 2; - } - if(i >= MAX_ITER) { res = RES_BAD_OP; goto error; } - - B0 = T0 = T1 = 0; - FOR_EACH(i, 0, MAX_ITER) { - T = (T1+T2)*0.5; - B = planck(lambda_min, lambda_max, T); - - if(B < radiance) { - T1 = T; - } else { - T2 = T; - } - - if(fabs(T-T0) < epsilon_T || fabs(B-B0) < epsilon_B) - break; - - T0 = T; - B0 = B; - } - if(i >= MAX_ITER) { res = RES_BAD_OP; goto error; } - - *temperature = T; - -exit: - return res; -error: - htrdr_log_err(htrdr, - "Could not compute the brightness temperature for the estimated radiance %g " - "averaged over [%g, %g] nanometers.\n", - radiance, - lambda_min*1e9, - lambda_max*1e9); - goto exit; -} - -res_T open_output_stream (struct htrdr* htrdr, const char* filename, diff --git a/src/htrdr.h b/src/htrdr.h @@ -17,6 +17,8 @@ #ifndef HTRDR_H #define HTRDR_H +#include "htrdr_spectral.h" + #include <rsys/logger.h> #include <rsys/ref_count.h> #include <rsys/str.h> @@ -30,12 +32,6 @@ #define HTRDR(Func) htrdr_ ## Func #endif -enum htrdr_spectral_type { - HTRDR_SPECTRAL_LW, /* Longwave */ - HTRDR_SPECTRAL_SW, /* Shortwave */ - HTRDR_SPECTRAL_SW_CIE_XYZ /* Shortwave wrt the CIE XYZ tristimulus */ -}; - /* Forward declarations */ struct htsky; struct htrdr_args; @@ -64,6 +60,7 @@ struct htrdr { struct htsky* sky; enum htrdr_spectral_type spectral_type; double wlen_range_m[2]; /* Integration range in *meters* */ + double ref_temperature; /* Reference temperature in Kelvin */ size_t spp; /* #samples per pixel */ size_t width; /* Image width */ diff --git a/src/htrdr_args.c b/src/htrdr_args.c @@ -323,6 +323,13 @@ parse_spectral_parameter(struct htrdr_args* args, const char* str) args->spectral_type = HTRDR_SPECTRAL_LW; res = parse_spectral_range(val, args->wlen_range); if(res != RES_OK) goto error; + } else if(!strcmp(key, "Tref")) { + res = cstr_to_double(val, &args->ref_temperature); + if(res == RES_OK && args->ref_temperature < 0) res = RES_BAD_ARG; + if(res != RES_OK) { + fprintf(stderr, "Invalid reference temperature Tref=%s.\n", val); + goto error; + } } else { fprintf(stderr, "Invalid spectral parameter `%s'.\n", key); res = RES_BAD_ARG; @@ -534,6 +541,22 @@ htrdr_args_init(struct htrdr_args* args, int argc, char** argv) goto error; } + /* Setup default ref temperature if necessary */ + if(args->ref_temperature <= 0) { + switch(args->spectral_type) { + case HTRDR_SPECTRAL_LW: + args->ref_temperature = HTRDR_DEFAULT_LW_REF_TEMPERATURE; + break; + case HTRDR_SPECTRAL_SW: + args->ref_temperature = HTRDR_SUN_TEMPERATURE; + break; + case HTRDR_SPECTRAL_SW_CIE_XYZ: + args->ref_temperature = -1; /* Unused */ + break; + default: FATAL("Unreachable code.\n"); break; + } + } + exit: return res; error: diff --git a/src/htrdr_args.h.in b/src/htrdr_args.h.in @@ -16,7 +16,7 @@ #ifndef HTRDR_ARGS_H #define HTRDR_ARGS_H -#include "htrdr.h" +#include "htrdr_spectral.h" #include "htrdr_cie_xyz.h" #include <float.h> @@ -58,6 +58,7 @@ struct htrdr_args { enum htrdr_spectral_type spectral_type; double wlen_range[2]; /* Spectral range of integration in nm */ + double ref_temperature; /* Planck reference temperature in Kelvin */ unsigned nthreads; /* Hint on the number of threads to use */ int force_overwriting; @@ -94,8 +95,9 @@ struct htrdr_args { 90, /* Sun elevation */ \ @HTRDR_ARGS_DEFAULT_OPTICAL_THICKNESS_THRESHOLD@, /* Optical thickness */ \ {UINT_MAX, UINT_MAX, UINT_MAX}, /* Maximum definition of the grid */ \ - HTRDR_SPECTRAL_SW_CIE_XYZ, \ + HTRDR_SPECTRAL_SW_CIE_XYZ, /* Spectral type */ \ HTRDR_CIE_XYZ_RANGE_DEFAULT__, /* Spectral range */ \ + -1, /* Reference temperature */ \ (unsigned)~0, /* #threads */ \ 0, /* Force overwriting */ \ 0, /* dump VTK */ \ diff --git a/src/htrdr_c.h b/src/htrdr_c.h @@ -18,7 +18,6 @@ #define HTRDR_C_H #include <rsys/rsys.h> -#include <rsys/math.h> /* PI support */ #ifndef NDEBUG #define MPI(Func) ASSERT(MPI_##Func == MPI_SUCCESS) @@ -36,9 +35,6 @@ enum htrdr_mpi_message { struct htrdr; -#define SW_WAVELENGTH_MIN 380 /* In nanometer */ -#define SW_WAVELENGTH_MAX 780 /* In nanometer */ - /* In nanometer */ static FINLINE double wavenumber_to_wavelength(const double nu/*In cm^-1*/) @@ -96,102 +92,6 @@ morton_xyz_decode_u21(const uint64_t code, uint32_t xyz[3]) xyz[2] = (uint32_t)morton3D_decode_u21(code >> 0); } -static INLINE double -wiebelt(const double v) -{ - int m; - double w, v2, v4; - /*.153989717364e+00;*/ - const double fifteen_over_pi_power_4 = 15.0/(PI*PI*PI*PI); - const double z0 = 1.0/3.0; - const double z1 = 1.0/8.0; - const double z2 = 1.0/60.0; - const double z4 = 1.0/5040.0; - const double z6 = 1.0/272160.0; - const double z8 = 1.0/13305600.0; - - if(v >= 2.) { - w = 0; - for(m=1; m<6 ;m++) - w+=exp(-m*v)/(m*m*m*m) * (((m*v+3)*m*v+6)*m*v+6); - w = w * fifteen_over_pi_power_4; - } else { - v2 = v*v; - v4 = v2*v2; - w = z0 - z1*v + z2*v2 - z4*v2*v2 + z6*v4*v2 - z8*v4*v4; - w = 1. - fifteen_over_pi_power_4*v2*v*w; - } - ASSERT(w >= 0.0 && w <= 1.0); - return w; -} - -static INLINE double -blackbody_fraction - (const double lambda0, /* In meters */ - const double lambda1, /* In meters */ - const double temperature) /* In Kelvin */ -{ - const double C2 = 1.43877735e-2; /* m.K */ - double x0 = C2 / lambda0; - double x1 = C2 / lambda1; - double v0 = x0 / temperature; - double v1 = x1 / temperature; - double w0 = wiebelt(v0); - double w1 = wiebelt(v1); - return w1 - w0; -} - - - -/* Return the Planck value in W/m^2/sr/m at a given wavelength */ -static INLINE double -planck_monochromatic - (const double lambda, /* In meters */ - const double temperature) /* In Kelvin */ -{ - const double c = 299792458; /* m/s */ - const double h = 6.62607015e-34; /* J.s */ - const double k = 1.380649e-23; /* J/K */ - const double lambda2 = lambda*lambda; - const double lambda5 = lambda2*lambda2*lambda; - const double B = ((2.0 * h * c*c) / lambda5) /* W/m^2/sr/m */ - / (exp(h*c/(lambda*k*temperature))-1.0); - ASSERT(temperature > 0); - return B; -} - -/* Return the average Planck value in W/m^2/sr/m over the - * [lambda_min, lambda_max] interval. */ -static INLINE double -planck_interval - (const double lambda_min, /* In meters */ - const double lambda_max, /* In meters */ - const double temperature) /* In Kelvin */ -{ - const double T2 = temperature*temperature; - const double T4 = T2*T2; - const double BOLTZMANN_CONSTANT = 5.6696e-8; /* W/m^2/K^4 */ - ASSERT(lambda_min < lambda_max && temperature > 0); - return blackbody_fraction(lambda_min, lambda_max, temperature) - * BOLTZMANN_CONSTANT * T4 / (PI * (lambda_max-lambda_min)); /* In W/m^2/sr/m */ -} - -/* Invoke planck_monochromatic or planck_interval whether the submitted - * interval is null or not, respectively. The returned value is in W/m^2/sr/m */ -static FINLINE double -planck - (const double lambda_min, /* In meters */ - const double lambda_max, /* In meters */ - const double temperature) /* In Kelvin */ -{ - ASSERT(lambda_min <= lambda_max && temperature > 0); - if(lambda_min == lambda_max) { - return planck_monochromatic(lambda_min, temperature); - } else { - return planck_interval(lambda_min, lambda_max, temperature); - } -} - /* Return the minimum length in nanometer of the sky spectral bands * clamped to in [range[0], range[1]]. */ extern LOCAL_SYM double @@ -199,15 +99,6 @@ compute_sky_min_band_len (struct htsky* sky, const double range[2]); -extern LOCAL_SYM res_T -brightness_temperature - (struct htrdr* htrdr, - const double lambda_min, /* In meters */ - const double lambda_max, /* In meters */ - /* Averaged over [lambda_min, lambda_max]. In W/m^2/sr/m */ - const double radiance, - double* temperature); - extern LOCAL_SYM res_T open_output_stream (struct htrdr* htrdr, diff --git a/src/htrdr_ran_wlen.h b/src/htrdr_ran_wlen.h @@ -27,8 +27,6 @@ struct htrdr_ran_wlen; extern LOCAL_SYM res_T htrdr_ran_wlen_create (struct htrdr* htrdr, - /* range must be included in [200,1000] nm for solar or in [1000, 100000] - * nanometers for longwave (thermal)*/ const double range[2], /* # bands used to discretisze the spectral domain. HTRDR_WLEN_RAN_CONTINUE * <=> no discretisation */ diff --git a/src/htrdr_spectral.c b/src/htrdr_spectral.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, 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/>. */ + +#include "htrdr.h" +#include "htrdr_spectral.h" + +res_T +brightness_temperature + (struct htrdr* htrdr, + const double lambda_min, + const double lambda_max, + const double radiance, /* In W/m2/sr/m */ + double* temperature) +{ + const size_t MAX_ITER = 100; + const double epsilon_T = 1e-4; /* In K */ + const double epsilon_B = radiance * 1e-8; + double T, T0, T1, T2; + double B, B0; + size_t i; + res_T res = RES_OK; + ASSERT(temperature && lambda_min <= lambda_max); + + /* Search for a brightness temperature whose radiance is greater than or + * equal to the estimated radiance */ + T2 = 200; + FOR_EACH(i, 0, MAX_ITER) { + const double B2 = planck(lambda_min, lambda_max, T2); + if(B2 >= radiance) break; + T2 *= 2; + } + if(i >= MAX_ITER) { res = RES_BAD_OP; goto error; } + + B0 = T0 = T1 = 0; + FOR_EACH(i, 0, MAX_ITER) { + T = (T1+T2)*0.5; + B = planck(lambda_min, lambda_max, T); + + if(B < radiance) { + T1 = T; + } else { + T2 = T; + } + + if(fabs(T-T0) < epsilon_T || fabs(B-B0) < epsilon_B) + break; + + T0 = T; + B0 = B; + } + if(i >= MAX_ITER) { res = RES_BAD_OP; goto error; } + + *temperature = T; + +exit: + return res; +error: + htrdr_log_err(htrdr, + "Could not compute the brightness temperature for the estimated radiance %g " + "averaged over [%g, %g] nanometers.\n", + radiance, + lambda_min*1e9, + lambda_max*1e9); + goto exit; +} + diff --git a/src/htrdr_spectral.h b/src/htrdr_spectral.h @@ -0,0 +1,137 @@ +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, 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_SPECTRAL_H +#define HTRDR_SPECTRAL_H + +#include <rsys/rsys.h> +#include <rsys/math.h> /* PI support */ + +#define HTRDR_SUN_TEMPERATURE 5778 /* In K */ +#define HTRDR_DEFAULT_LW_REF_TEMPERATURE 290 /* Default longwave temperature in K */ + +enum htrdr_spectral_type { + HTRDR_SPECTRAL_LW, /* Longwave */ + HTRDR_SPECTRAL_SW, /* Shortwave */ + HTRDR_SPECTRAL_SW_CIE_XYZ /* Shortwave wrt the CIE XYZ tristimulus */ +}; + +struct htrdr; + +static INLINE double +wiebelt(const double v) +{ + int m; + double w, v2, v4; + /*.153989717364e+00;*/ + const double fifteen_over_pi_power_4 = 15.0/(PI*PI*PI*PI); + const double z0 = 1.0/3.0; + const double z1 = 1.0/8.0; + const double z2 = 1.0/60.0; + const double z4 = 1.0/5040.0; + const double z6 = 1.0/272160.0; + const double z8 = 1.0/13305600.0; + + if(v >= 2.) { + w = 0; + for(m=1; m<6 ;m++) + w+=exp(-m*v)/(m*m*m*m) * (((m*v+3)*m*v+6)*m*v+6); + w = w * fifteen_over_pi_power_4; + } else { + v2 = v*v; + v4 = v2*v2; + w = z0 - z1*v + z2*v2 - z4*v2*v2 + z6*v4*v2 - z8*v4*v4; + w = 1. - fifteen_over_pi_power_4*v2*v*w; + } + ASSERT(w >= 0.0 && w <= 1.0); + return w; +} + +static INLINE double +blackbody_fraction + (const double lambda0, /* In meters */ + const double lambda1, /* In meters */ + const double temperature) /* In Kelvin */ +{ + const double C2 = 1.43877735e-2; /* m.K */ + double x0 = C2 / lambda0; + double x1 = C2 / lambda1; + double v0 = x0 / temperature; + double v1 = x1 / temperature; + double w0 = wiebelt(v0); + double w1 = wiebelt(v1); + return w1 - w0; +} + +/* Return the Planck value in W/m^2/sr/m at a given wavelength */ +static INLINE double +planck_monochromatic + (const double lambda, /* In meters */ + const double temperature) /* In Kelvin */ +{ + const double c = 299792458; /* m/s */ + const double h = 6.62607015e-34; /* J.s */ + const double k = 1.380649e-23; /* J/K */ + const double lambda2 = lambda*lambda; + const double lambda5 = lambda2*lambda2*lambda; + const double B = ((2.0 * h * c*c) / lambda5) /* W/m^2/sr/m */ + / (exp(h*c/(lambda*k*temperature))-1.0); + ASSERT(temperature > 0); + return B; +} + +/* Return the average Planck value in W/m^2/sr/m over the + * [lambda_min, lambda_max] interval. */ +static INLINE double +planck_interval + (const double lambda_min, /* In meters */ + const double lambda_max, /* In meters */ + const double temperature) /* In Kelvin */ +{ + const double T2 = temperature*temperature; + const double T4 = T2*T2; + const double BOLTZMANN_CONSTANT = 5.6696e-8; /* W/m^2/K^4 */ + ASSERT(lambda_min < lambda_max && temperature > 0); + return blackbody_fraction(lambda_min, lambda_max, temperature) + * BOLTZMANN_CONSTANT * T4 / (PI * (lambda_max-lambda_min)); /* In W/m^2/sr/m */ +} + +/* Invoke planck_monochromatic or planck_interval whether the submitted + * interval is null or not, respectively. The returned value is in W/m^2/sr/m */ +static FINLINE double +planck + (const double lambda_min, /* In meters */ + const double lambda_max, /* In meters */ + const double temperature) /* In Kelvin */ +{ + ASSERT(lambda_min <= lambda_max && temperature > 0); + if(lambda_min == lambda_max) { + return planck_monochromatic(lambda_min, temperature); + } else { + return planck_interval(lambda_min, lambda_max, temperature); + } +} + +extern LOCAL_SYM res_T +brightness_temperature + (struct htrdr* htrdr, + const double lambda_min, /* In meters */ + const double lambda_max, /* In meters */ + /* Averaged over [lambda_min, lambda_max]. In W/m^2/sr/m */ + const double radiance, + double* temperature); + +#endif /* HTRDR_SPECTRAL_H */