rnatm

Load and structure data describing an atmosphere
git clone git://git.meso-star.fr/rnatm.git
Log | Files | Refs | README | LICENSE

commit ca72a7c2c598c9abbb2221d7be00e036529a575a
parent 1903657bfc8786ea103eb99303b9c6e690bc520d
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 26 Sep 2022 16:43:55 +0200

Add the rnatm_cell_get_radcoef function

Internal refactoring to use the same code to get the radiative
coefficients whether it was when building octree or when calling the
public API.

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/rnatm.h | 31+++++++++++++++++++++++++++++++
Msrc/rnatm_c.h | 17++++++++++++++++-
Msrc/rnatm_octree.c | 158++++++++-----------------------------------------------------------------------
Msrc/rnatm_properties.c | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/rnatm_radcoef.c | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 393 insertions(+), 143 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -70,6 +70,7 @@ set(RNATM_FILES_SRC rnatm_octree.c rnatm_octrees_storage.c rnatm_properties.c + rnatm_radcoef.c rnatm_voxel_partition.c rnatm_write_vtk.c) set(RNATM_FILES_INC diff --git a/src/rnatm.h b/src/rnatm.h @@ -91,6 +91,31 @@ struct rnatm_cell { * the RNATM_GAS constant */ size_t component; }; +#define RNATM_CELL_NULL__ {SUVM_PRIMITIVE_NULL__, RNATM_GAS} +static const struct rnatm_cell RNATM_CELL_NULL = RNATM_CELL_NULL__; + +struct rnatm_cell_get_radcoef_args { + struct rnatm_cell cell; /* Cell to query */ + double barycentric_coords[4]; /* Position into and relative to the cell */ + size_t iband; /* Index of the spectral band to query */ + size_t iquad; /* Index of the quadrature point to query in the band */ + double wavelength; /* Wavenlength in the queried band (in nanometers) */ + + enum rnatm_radcoef radcoef; + + /* For debug: check that the retrieved radcoef is in [k_min, k_max] */ + double k_min; + double k_max; +}; +#define RNATM_CELL_GET_RADCOEF_ARGS_NULL__ { \ + RNATM_CELL_NULL__, \ + {0, 0, 0, 0}, /* Barycentric coordinates */ \ + 0, 0, 0, /* Spectral data (band, quadrature point, wavelength) */ \ + RNATM_RADCOEFS_COUNT__, \ + -DBL_MAX, DBL_MAX /* For debug: Radcoef range */ \ +} +static const struct rnatm_cell_get_radcoef_args +RNATM_CELL_GET_RADCOEF_ARGS_NULL = RNATM_CELL_GET_RADCOEF_ARGS_NULL__; struct rnatm_create_args { struct rnatm_gas_args gas; @@ -189,6 +214,12 @@ rnatm_fetch_cell struct rnatm_cell* cell, double barycentric_coords[4]); /* `pos' relative to the returned `cell' */ +RNATM_API res_T +rnatm_cell_get_radcoef + (const struct rnatm* atm, + const struct rnatm_cell_get_radcoef_args* args, + double* k); + RNATM_API size_t rnatm_get_aerosols_count (const struct rnatm* atm); diff --git a/src/rnatm_c.h b/src/rnatm_c.h @@ -21,12 +21,13 @@ #ifndef RNATM_C_H #define RNATM_C_H +#include "rnatm.h" + #include <rsys/dynamic_array_size_t.h> #include <rsys/logger.h> #include <rsys/ref_count.h> #include <rsys/str.h> -struct rnatm_create_args; struct rnsf; /* Generate the dynamic array of dynamic array of size_t */ @@ -251,4 +252,18 @@ unload_octree (struct rnatm* atm, const size_t iaccel_struct); +/* Get the radiative coefficient of the input tetrahedron. The output radiative + * coefficients per vertex are defined even if the tetrahedron is invalid or if + * no spectral data is defined for this component for the considered band. In + * such cases, the k returned are zero and the function returns 0 instead of 1*/ +extern LOCAL_SYM int +tetra_get_radcoef + (const struct rnatm* atm, + const struct suvm_primitive* tetra, + const size_t component, + const size_t iband, + const size_t iquad_pt, + const enum rnatm_radcoef radcoef, + float k[4]); + #endif /* RNATM_C_H */ diff --git a/src/rnatm_octree.c b/src/rnatm_octree.c @@ -18,8 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* lround, nextafter */ -#define _POSIX_C_SOURCE 200112L +#define _POSIX_C_SOURCE 200112L /* lround */ #include "rnatm_c.h" #include "rnatm_log.h" @@ -386,160 +385,35 @@ compute_grid_definition(struct rnatm* atm, const struct rnatm_create_args* args) return RES_OK; } +/* Compute the radiative coefficients range of the tetrahedron */ static INLINE void -setup_tetra_radcoefs_gas +setup_tetra_radcoefs (struct rnatm* atm, const struct suvm_primitive* tetra, + const size_t cpnt, const size_t iband, - const size_t iquad_pt, + const size_t iquad, struct radcoefs* radcoefs) { float ka[4]; float ks[4]; - struct sck_band band; - struct sck_quad_pt quad_pt; ASSERT(atm && tetra && radcoefs); - ASSERT(tetra->nvertices == 4); - - /* Compute the scattering coefficient range of the tetrahedron */ - SCK(get_band(atm->gas.ck, iband, &band)); - ks[0] = band.ks_list[tetra->indices[0]]; - ks[1] = band.ks_list[tetra->indices[1]]; - ks[2] = band.ks_list[tetra->indices[2]]; - ks[3] = band.ks_list[tetra->indices[3]]; - radcoefs->ks_min = MMIN(MMIN(ks[0], ks[1]), MMIN(ks[2], ks[3])); - radcoefs->ks_max = MMAX(MMAX(ks[0], ks[1]), MMAX(ks[2], ks[3])); - - /* Compute the absorption coefficient range of the tetrahedron */ - SCK(band_get_quad_pt(&band, iquad_pt, &quad_pt)); - ka[0] = quad_pt.ka_list[tetra->indices[0]]; - ka[1] = quad_pt.ka_list[tetra->indices[1]]; - ka[2] = quad_pt.ka_list[tetra->indices[2]]; - ka[3] = quad_pt.ka_list[tetra->indices[3]]; - radcoefs->ka_min = MMIN(MMIN(ka[0], ka[1]), MMIN(ka[2], ka[3])); - radcoefs->ka_max = MMAX(MMAX(ka[0], ka[1]), MMAX(ka[2], ka[3])); -} - -static void -setup_tetra_radcoefs_aerosol - (struct rnatm* atm, - const struct suvm_primitive* tetra, - const size_t iband, - const struct aerosol* aerosol, - struct radcoefs* radcoefs) -{ - struct sck_band gas_band; - struct sars_band ars_band; - double gas_spectral_range[2]; /* In nm */ - size_t ars_ibands[2]; - float ka[4], ks[4]; - ASSERT(atm && aerosol && tetra && radcoefs); - - /* Look for the aerosol bands covered by the gas band */ - SCK(get_band(atm->gas.ck, iband, &gas_band)); - gas_spectral_range[0] = gas_band.lower; - gas_spectral_range[1] = nextafter(gas_band.upper, 0); /* inclusive */ - SARS(find_bands(aerosol->sars, gas_spectral_range, ars_ibands)); - - /* No aerosol band is overlaid by the gas band: ignore the aerosol */ - if(ars_ibands[0] > ars_ibands[1]) { - radcoefs->ks_min = FLT_MAX; - radcoefs->ks_max =-FLT_MAX; - radcoefs->ka_min = FLT_MAX; - radcoefs->ka_max =-FLT_MAX; - return; - } - - SARS(get_band(aerosol->sars, ars_ibands[0], &ars_band)); - - /* The gas band is included in an aerosol band: use the aerosol radiative - * coefficients */ - if(ars_ibands[0] == ars_ibands[1] - && ars_band.lower <= gas_band.lower - && ars_band.upper >= gas_band.upper) { + ASSERT(cpnt == RNATM_GAS || cpnt < darray_aerosol_size_get(&atm->aerosols)); - /* Compute the absorption coefficient range of the tetrahedron */ - ka[0] = sars_band_get_ka(&ars_band, tetra->indices[0]); - ka[1] = sars_band_get_ka(&ars_band, tetra->indices[1]); - ka[2] = sars_band_get_ka(&ars_band, tetra->indices[2]); - ka[3] = sars_band_get_ka(&ars_band, tetra->indices[3]); + if(tetra_get_radcoef(atm, tetra, cpnt, iband, iquad, RNATM_RADCOEF_Ka, ka) + && tetra_get_radcoef(atm, tetra, cpnt, iband, iquad, RNATM_RADCOEF_Ks, ks)) { + /* Radiative coefficients are defined for this component at the considered + * spectral band */ radcoefs->ka_min = MMIN(MMIN(ka[0], ka[1]), MMIN(ka[2], ka[3])); radcoefs->ka_max = MMAX(MMAX(ka[0], ka[1]), MMAX(ka[2], ka[3])); - - /* Compute the scattering coefficient range of the tetrahedron */ - ks[0] = sars_band_get_ks(&ars_band, tetra->indices[0]); - ks[1] = sars_band_get_ks(&ars_band, tetra->indices[1]); - ks[2] = sars_band_get_ks(&ars_band, tetra->indices[2]); - ks[3] = sars_band_get_ks(&ars_band, tetra->indices[3]); radcoefs->ks_min = MMIN(MMIN(ks[0], ks[1]), MMIN(ks[2], ks[3])); radcoefs->ks_max = MMAX(MMAX(ks[0], ks[1]), MMAX(ks[2], ks[3])); - - /* The gas band overlaid N aerosol bands (N >= 1) */ } else { - float tau_ka[4] = {0, 0, 0, 0}; - float tau_ks[4] = {0, 0, 0, 0}; - double lambda_min; - double lambda_max; - float rcp_gas_band_len; - size_t iars_band; - - FOR_EACH(iars_band, ars_ibands[0], ars_ibands[1]+1) { - float lambda_len; - - SARS(get_band(aerosol->sars, iars_band, &ars_band)); - lambda_min = MMAX(gas_band.lower, ars_band.lower); - lambda_max = MMIN(gas_band.upper, ars_band.upper); /* exclusive */ - lambda_max = nextafter(lambda_max, 0); /* inclusive */ - - /* Shrink lambda_<min|max> to the spectral range */ - lambda_min = MMAX(lambda_min, atm->spectral_range[0]); - lambda_max = MMIN(lambda_max, atm->spectral_range[1]); - lambda_len = (float)(lambda_max - lambda_min); - ASSERT(lambda_len > 0); - - tau_ka[0] += sars_band_get_ka(&ars_band, tetra->indices[0]) * lambda_len; - tau_ka[1] += sars_band_get_ka(&ars_band, tetra->indices[1]) * lambda_len; - tau_ka[2] += sars_band_get_ka(&ars_band, tetra->indices[2]) * lambda_len; - tau_ka[3] += sars_band_get_ka(&ars_band, tetra->indices[3]) * lambda_len; - - tau_ks[0] += sars_band_get_ks(&ars_band, tetra->indices[0]) * lambda_len; - tau_ks[1] += sars_band_get_ks(&ars_band, tetra->indices[1]) * lambda_len; - tau_ks[2] += sars_band_get_ks(&ars_band, tetra->indices[2]) * lambda_len; - tau_ks[3] += sars_band_get_ks(&ars_band, tetra->indices[3]) * lambda_len; - } - - /* Compute the radiative coefficients of the tetrahedron */ - lambda_min = gas_band.lower; - lambda_max = nextafter(gas_band.upper, 0); /* inclusive */ - rcp_gas_band_len = 1.f/(float)(lambda_max - lambda_min); - f4_mulf(ks, tau_ks, rcp_gas_band_len); - f4_mulf(ka, tau_ka, rcp_gas_band_len); - - /* Compute the radiative coefficients range of the tetrahedron */ - radcoefs->ks_min = MMIN(MMIN(ks[0], ks[1]), MMIN(ks[2], ks[3])); - radcoefs->ks_max = MMAX(MMAX(ks[0], ks[1]), MMAX(ks[2], ks[3])); - radcoefs->ka_min = MMIN(MMIN(ka[0], ka[1]), MMIN(ka[2], ka[3])); - radcoefs->ka_max = MMAX(MMAX(ka[0], ka[1]), MMAX(ka[2], ka[3])); - } -} - -static INLINE void -setup_tetra_radcoefs - (struct rnatm* atm, - const struct suvm_primitive* tetra, - const size_t iband, - const size_t iquad_pt, - const size_t cpnt, - struct radcoefs* radcoefs) -{ - ASSERT(atm && tetra && radcoefs); - ASSERT(cpnt == RNATM_GAS || cpnt < darray_aerosol_size_get(&atm->aerosols)); - - if(cpnt == RNATM_GAS) { - setup_tetra_radcoefs_gas(atm, tetra, iband, iquad_pt, radcoefs); - } else { - const struct aerosol* aerosol = darray_aerosol_cdata_get(&atm->aerosols)+cpnt; - setup_tetra_radcoefs_aerosol(atm, tetra, iband, aerosol, radcoefs); + /* No valid radiative coefficients are set: ignore the tetrahedron */ + radcoefs->ka_min = FLT_MAX; + radcoefs->ka_max =-FLT_MAX; + radcoefs->ks_min = FLT_MAX; + radcoefs->ks_max =-FLT_MAX; } } @@ -642,7 +516,7 @@ voxelize_tetra const size_t iband = args->accel_structs[iitem].iband; const size_t iquad_pt = args->accel_structs[iitem].iquad_pt; struct radcoefs* radcoefs = args->per_item_radcoefs + iitem; - setup_tetra_radcoefs(atm, &tetra, iband, iquad_pt, cpnt, radcoefs); + setup_tetra_radcoefs(atm, &tetra, cpnt, iband, iquad_pt, radcoefs); } /* Iterate voxels intersected by the AABB of the polyedron */ diff --git a/src/rnatm_properties.c b/src/rnatm_properties.c @@ -35,6 +35,61 @@ /******************************************************************************* * Helper functions ******************************************************************************/ +static res_T +check_rnatm_cell_get_radcoef_args + (const struct rnatm* atm, + const struct rnatm_cell_get_radcoef_args* args) +{ + struct sck_band band; + double sum_bcoords; + size_t i, n; + ASSERT(darray_band_size_get(&atm->bands)); + + if(!args) return RES_BAD_ARG; + + /* Invalid geometric primitive */ + if(SUVM_PRIMITIVE_NONE(&args->cell.prim) + || args->cell.prim.nvertices != 4) /* Expect a tetraheron */ + return RES_BAD_ARG; + + /* Invalid component */ + if(args->cell.component != RNATM_GAS + && args->cell.component >= darray_aerosol_size_get(&atm->aerosols)) + return RES_BAD_ARG; + + /* Invalid barycentric coordinates */ + sum_bcoords = + args->barycentric_coords[0] + + args->barycentric_coords[1] + + args->barycentric_coords[2] + + args->barycentric_coords[3]; + if(!eq_eps(sum_bcoords, 1, 1.e-6)) + return RES_BAD_ARG; + + /* Invalid band index */ + n = darray_band_size_get(&atm->bands) - 1; + if(args->iband < darray_band_cdata_get(&atm->bands)[0].index + || args->iband > darray_band_cdata_get(&atm->bands)[n].index) + return RES_BAD_ARG; + + /* Invalid quadrature point index */ + i = args->iband - darray_band_cdata_get(&atm->bands)[0].index; + ASSERT(i <= n && args->iband == darray_band_cdata_get(&atm->bands)[i].index); + if(args->iquad >= darray_band_cdata_get(&atm->bands)[i].nquad_pts) + return RES_BAD_ARG; + + /* Invalid wavelength */ + SCK(get_band(atm->gas.ck, args->iband, &band)); + if(args->wavelength < band.lower || args->wavelength > band.upper) + return RES_BAD_ARG; + + /* Invalid K range */ + if(args->k_min > args->k_max) + return RES_BAD_ARG; + + return RES_OK; +} + static INLINE void reset_gas_properties(struct gas* gas) { @@ -498,6 +553,42 @@ error: } /******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +rnatm_cell_get_radcoef + (const struct rnatm* atm, + const struct rnatm_cell_get_radcoef_args* args, + double* out_k) +{ + float per_vertex_k[4]; + double k = NaN; + res_T res = RES_OK; + + if(!atm || !out_k) { res = RES_BAD_ARG; goto error; } + res = check_rnatm_cell_get_radcoef_args(atm, args); + if(res != RES_OK) goto error; + + /* Get the radiative coefficients of the tetrahedron */ + tetra_get_radcoef(atm, &args->cell.prim, args->cell.component, args->iband, + args->iquad, args->radcoef, per_vertex_k); + + /* Calculate the radiative coefficient by linearly interpolating the + * coefficients defined by tetrahedron vertex */ + k = per_vertex_k[0] * args->barycentric_coords[0] + + per_vertex_k[1] * args->barycentric_coords[1] + + per_vertex_k[2] * args->barycentric_coords[2] + + per_vertex_k[3] * args->barycentric_coords[3]; + ASSERT(args->k_min <= k && k <= args->k_max); + +exit: + *out_k = k; + return res; +error: + goto exit; +} + +/******************************************************************************* * Local functions ******************************************************************************/ res_T diff --git a/src/rnatm_radcoef.c b/src/rnatm_radcoef.c @@ -0,0 +1,238 @@ +/* Copyright (C) 2022 Centre National de la Recherche Scientifique + * Copyright (C) 2022 Institut de Physique du Globe de Paris + * Copyright (C) 2022 |Méso|Star> (contact@meso-star.com) + * Copyright (C) 2022 Université de Reims Champagne-Ardenne + * Copyright (C) 2022 Université de Versaille Saint-Quentin + * Copyright (C) 2022 Université Paul Sabatier (contact@laplace.univ-tlse.fr) + * + * 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 "rnatm_c.h" + +#include <star/sars.h> +#include <star/sck.h> + +#include <rsys/float4.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static int +tetra_get_radcoef_gas + (const struct rnatm* atm, + const struct suvm_primitive* tetra, + const size_t iband, + const size_t iquad_pt, + const enum rnatm_radcoef radcoef, + float k[4]) +{ + struct sck_band band; + float ka[4]; + float ks[4]; + ASSERT(atm && tetra && k); + ASSERT(iband < sck_get_bands_count(atm->gas.ck)); + ASSERT(!SUVM_PRIMITIVE_NONE(tetra)); + ASSERT(tetra->nvertices == 4); + ASSERT(tetra->indices[0] < sck_get_nodes_count(atm->gas.ck)); + ASSERT(tetra->indices[1] < sck_get_nodes_count(atm->gas.ck)); + ASSERT(tetra->indices[2] < sck_get_nodes_count(atm->gas.ck)); + ASSERT(tetra->indices[3] < sck_get_nodes_count(atm->gas.ck)); + + /* Retrieve the band */ + SCK(get_band(atm->gas.ck, iband, &band)); + ASSERT(iquad_pt < band.quad_pts_count); + + /* Get the absorption coefficients */ + if(radcoef == RNATM_RADCOEF_Ka || radcoef == RNATM_RADCOEF_Kext) { + /* Retrieve the quadrature point */ + struct sck_quad_pt quad; + SCK(band_get_quad_pt(&band, iquad_pt, &quad)); + ka[0] = quad.ka_list[tetra->indices[0]]; + ka[1] = quad.ka_list[tetra->indices[1]]; + ka[2] = quad.ka_list[tetra->indices[2]]; + ka[3] = quad.ka_list[tetra->indices[3]]; + } + + /* Get the scattering coefficients */ + if(radcoef == RNATM_RADCOEF_Ks || radcoef == RNATM_RADCOEF_Kext) { + ks[0] = band.ks_list[tetra->indices[0]]; + ks[1] = band.ks_list[tetra->indices[1]]; + ks[2] = band.ks_list[tetra->indices[2]]; + ks[3] = band.ks_list[tetra->indices[3]]; + } + + switch(radcoef) { + case RNATM_RADCOEF_Ka: f4_set(k, ka); break; + case RNATM_RADCOEF_Ks: f4_set(k, ks); break; + case RNATM_RADCOEF_Kext: f4_add(k, ka, ks); break; + default: FATAL("Unreachable code\n"); + } + + return 1; +} + +static int +tetra_get_radcoef_aerosol + (const struct rnatm* atm, + const struct suvm_primitive* tetra, + const size_t iband, + const struct aerosol* aerosol, + const enum rnatm_radcoef radcoef, + float k[4]) +{ + struct sck_band gas_band; + struct sars_band ars_band; + double gas_spectral_range[2]; /* In nm */ + size_t ars_ibands[2]; + float ka[4], ks[4]; + ASSERT(atm && aerosol && tetra && k); + + ASSERT(!SUVM_PRIMITIVE_NONE(tetra)); + ASSERT(tetra->nvertices == 4); + ASSERT(tetra->indices[0] < sars_get_nodes_count(aerosol->sars)); + ASSERT(tetra->indices[1] < sars_get_nodes_count(aerosol->sars)); + ASSERT(tetra->indices[2] < sars_get_nodes_count(aerosol->sars)); + ASSERT(tetra->indices[3] < sars_get_nodes_count(aerosol->sars)); + + /* Look for the aerosol bands covered by the gas band */ + SCK(get_band(atm->gas.ck, iband, &gas_band)); + gas_spectral_range[0] = gas_band.lower; + gas_spectral_range[1] = nextafter(gas_band.upper, 0); /* inclusive */ + SARS(find_bands(aerosol->sars, gas_spectral_range, ars_ibands)); + + /* No aerosol band is overlaid by the gas band */ + if(ars_ibands[0] > ars_ibands[1]) { + k[0] = 0; + k[1] = 0; + k[2] = 0; + k[3] = 0; + return 0; + } + + SARS(get_band(aerosol->sars, ars_ibands[0], &ars_band)); + + /* The gas band is included in an aerosol band: use the aerosol radiative + * coefficients */ + if(ars_ibands[0] == ars_ibands[1] + && ars_band.lower <= gas_band.lower + && ars_band.upper >= gas_band.upper) { + + /* Get the absorption coefficient */ + if(radcoef == RNATM_RADCOEF_Ka || radcoef == RNATM_RADCOEF_Kext) { + ka[0] = sars_band_get_ka(&ars_band, tetra->indices[0]); + ka[1] = sars_band_get_ka(&ars_band, tetra->indices[1]); + ka[2] = sars_band_get_ka(&ars_band, tetra->indices[2]); + ka[3] = sars_band_get_ka(&ars_band, tetra->indices[3]); + } + + /* Get the scattering coefficient */ + if(radcoef == RNATM_RADCOEF_Ks || radcoef == RNATM_RADCOEF_Kext) { + ks[0] = sars_band_get_ks(&ars_band, tetra->indices[0]); + ks[1] = sars_band_get_ks(&ars_band, tetra->indices[1]); + ks[2] = sars_band_get_ks(&ars_band, tetra->indices[2]); + ks[3] = sars_band_get_ks(&ars_band, tetra->indices[3]); + } + + /* The gas band overlaid N aerosol bands (N >= 1) */ + } else { + float tau_ka[4] = {0, 0, 0, 0}; + float tau_ks[4] = {0, 0, 0, 0}; + double lambda_min; + double lambda_max; + float rcp_gas_band_len; + size_t iars_band; + + FOR_EACH(iars_band, ars_ibands[0], ars_ibands[1]+1) { + float lambda_len; + + SARS(get_band(aerosol->sars, iars_band, &ars_band)); + lambda_min = MMAX(gas_band.lower, ars_band.lower); + lambda_max = MMIN(gas_band.upper, ars_band.upper); /* exclusive */ + lambda_max = nextafter(lambda_max, 0); /* inclusive */ + + /* Shrink lambda_<min|max> to the spectral range */ + lambda_min = MMAX(lambda_min, atm->spectral_range[0]); + lambda_max = MMIN(lambda_max, atm->spectral_range[1]); + lambda_len = (float)(lambda_max - lambda_min); + ASSERT(lambda_len > 0); + + /* Get the absorption coefficient */ + if(radcoef == RNATM_RADCOEF_Ka || radcoef == RNATM_RADCOEF_Kext) { + tau_ka[0] += sars_band_get_ka(&ars_band, tetra->indices[0]) * lambda_len; + tau_ka[1] += sars_band_get_ka(&ars_band, tetra->indices[1]) * lambda_len; + tau_ka[2] += sars_band_get_ka(&ars_band, tetra->indices[2]) * lambda_len; + tau_ka[3] += sars_band_get_ka(&ars_band, tetra->indices[3]) * lambda_len; + } + + /* Get the scattering coefficient */ + if(radcoef == RNATM_RADCOEF_Ks || radcoef == RNATM_RADCOEF_Kext) { + tau_ks[0] += sars_band_get_ks(&ars_band, tetra->indices[0]) * lambda_len; + tau_ks[1] += sars_band_get_ks(&ars_band, tetra->indices[1]) * lambda_len; + tau_ks[2] += sars_band_get_ks(&ars_band, tetra->indices[2]) * lambda_len; + tau_ks[3] += sars_band_get_ks(&ars_band, tetra->indices[3]) * lambda_len; + } + } + + /* Compute the radiative coefficients of the tetrahedron */ + lambda_min = gas_band.lower; + lambda_max = nextafter(gas_band.upper, 0); /* inclusive */ + rcp_gas_band_len = 1.f/(float)(lambda_max - lambda_min); + f4_mulf(ks, tau_ks, rcp_gas_band_len); + f4_mulf(ka, tau_ka, rcp_gas_band_len); + } + + switch(radcoef) { + case RNATM_RADCOEF_Ka: f4_set(k, ka); break; + case RNATM_RADCOEF_Ks: f4_set(k, ks); break; + case RNATM_RADCOEF_Kext: f4_add(k, ka, ks); break; + default: FATAL("Unreachable code\n"); + } + + return 1; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +int +tetra_get_radcoef + (const struct rnatm* atm, + const struct suvm_primitive* tetra, + const size_t component, + const size_t iband, + const size_t iquad_pt, + const enum rnatm_radcoef radcoef, + float k[4]) +{ + ASSERT(atm); + + if(SUVM_PRIMITIVE_NONE(tetra)) { + k[0] = 0; + k[1] = 0; + k[2] = 0; + k[3] = 0; + return 0; + } + + if(component == RNATM_GAS) { + return tetra_get_radcoef_gas(atm, tetra, iband, iquad_pt, radcoef, k); + } else { + const struct aerosol* aerosol = NULL; + ASSERT(component < darray_aerosol_size_get(&atm->aerosols)); + aerosol = darray_aerosol_cdata_get(&atm->aerosols) + component; + return tetra_get_radcoef_aerosol(atm, tetra, iband, aerosol, radcoef, k); + } +}