atrstm

Load and structure a combustion gas mixture
git clone git://git.meso-star.fr/atrstm.git
Log | Files | Refs | README | LICENSE

commit cc23f79b77a6aec67d501013396c3ce941d6c975
parent 6afe4cf69bc00ebea1c7ed2a10dbca428964752d
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 21 Jan 2021 11:44:47 +0100

Add the atrstm_fetch_radcoefs function

Rename property/optprop in radcoef

Diffstat:
Mcmake/CMakeLists.txt | 6+++---
Msrc/atrstm.c | 23++++++++++++++++++++++-
Msrc/atrstm.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/atrstm_c.h | 11++++++++---
Dsrc/atrstm_optprops.c | 159-------------------------------------------------------------------------------
Dsrc/atrstm_optprops.h | 165-------------------------------------------------------------------------------
Asrc/atrstm_radcoefs.c | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/atrstm_radcoefs.h | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/atrstm_radprops.c | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/atrstm_radprops.h | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/atrstm_setup_octrees.c | 96++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/atrstm_svx.h | 18+++++++++---------
Dsrc/test_atrstm_optprops.c | 43-------------------------------------------
Asrc/test_atrstm_radcoefs.c | 43+++++++++++++++++++++++++++++++++++++++++++
14 files changed, 1068 insertions(+), 437 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -56,16 +56,16 @@ set(ATRSTM_FILES_SRC atrstm.c atrstm_cache.c atrstm_log.c - atrstm_optprops.c atrstm_partition.c + atrstm_radcoefs.c atrstm_setup_octrees.c atrstm_setup_uvm.c) set(ATRSTM_FILES_INC atrstm_c.h atrstm_cache.h atrstm_log.h - atrstm_optprops.h atrstm_partition.h + atrstm_radcoefs.h atrstm_setup_octrees.h) set(ATRSTM_FILES_INC_API @@ -110,7 +110,7 @@ if(NOT NO_TEST) endfunction() build_test(test_atrstm) - new_test(test_atrstm_optprops) + new_test(test_atrstm_radcoefs) endif() ################################################################################ diff --git a/src/atrstm.c b/src/atrstm.c @@ -21,7 +21,6 @@ #include "atrstm_log.h" #include "atrstm_setup_octrees.h" -#include <astoria/atrri.h> #include <astoria/atrtp.h> #include <star/suvm.h> @@ -166,6 +165,21 @@ atrstm_create res = atrri_load(atrstm->atrri, args->atrri_filename); if(res != RES_OK) goto error; + /* In shortwave, pre-fetched the refractive index */ + if(atrstm->spectral_type != ATRSTM_SPECTRAL_SW) { + atrstm->refract_id = ATRRI_REFRACTIVE_INDEX_NULL; + } else { + const double wlen = atrstm->wlen_range[0]; + ASSERT(atrstm->wlen_range[0] == atrstm->wlen_range[1]); + res = atrri_fetch_refractive_index(atrstm->atrri, wlen, &atrstm->refract_id); + if(res != RES_OK) { + log_err(atrstm, + "Could not fetch the refractive index of the shortwave wavelength %g " + "-- %s.\n", wlen, res_to_cstr(res)); + goto error; + } + } + /* Create the Star-UnstructuredVolumetricMesh device */ res = suvm_device_create (atrstm->logger, atrstm->allocator, atrstm->verbose, &atrstm->suvm); @@ -222,6 +236,13 @@ atrstm_ref_put(struct atrstm* atrstm) return RES_OK; } +const char* +atrstm_get_name(const struct atrstm* atrstm) +{ + ASSERT(atrstm); + return str_cget(&atrstm->name); +} + /******************************************************************************* * Local functions ******************************************************************************/ diff --git a/src/atrstm.h b/src/atrstm.h @@ -38,11 +38,22 @@ #define ATRSTM(Func) atrstm_ ## Func #endif -enum atrstm_property { - ATRSTM_Ks, /* Scattering coefficient */ - ATRSTM_Ka, /* Absorption coefficient */ - ATRSTM_Kext, /* Extinction coefficient = Ks + Ka */ - ATRSTM_PROPS_COUNT__ +/* Optical properties */ +enum atrstm_radcoef { + ATRSTM_RADCOEF_Ks, /* Scattering coefficient */ + ATRSTM_RADCOEF_Ka, /* Absorption coefficient */ + ATRSTM_RADCOEF_Kext, /* Extinction coefficient = Ks + Ka */ + ATRSTM_RADCOEFS_COUNT__ +}; + +enum atrstm_radcoef_flag { + ATRSTM_RADCOEF_FLAG_Ks = BIT(ATRSTM_RADCOEF_Ks), + ATRSTM_RADCOEF_FLAG_Ka = BIT(ATRSTM_RADCOEF_Ka), + ATRSTM_RADCOEF_FLAG_Kext = BIT(ATRSTM_RADCOEF_Kext), + ATRSTM_RADCOEFS_MASK_ALL = + ATRSTM_RADCOEF_FLAG_Ks + | ATRSTM_RADCOEF_FLAG_Ka + | ATRSTM_RADCOEF_FLAG_Kext }; /* List of medium components */ @@ -52,6 +63,12 @@ enum atrstm_component { ATRSTM_CPNTS_COUNT__ }; +enum atrstm_component_flag { + ATRSTM_CPNT_FLAG_SOOT = BIT(ATRSTM_CPNT_SOOT), + ATRSTM_CPNT_FLAG_GAS = BIT(ATRSTM_CPNT_GAS), + ATRSTM_CPNTS_MASK_ALL = ATRSTM_CPNT_FLAG_SOOT | ATRSTM_CPNT_FLAG_GAS +}; + enum atrstm_svx_op { ATRSTM_SVX_MIN, ATRSTM_SVX_MAX, @@ -106,6 +123,35 @@ struct atrstm_args { } static const struct atrstm_args ATRSTM_ARGS_DEFAULT = ATRSTM_ARGS_DEFAULT__; +struct atrstm_fetch_radcoefs_args { + double pos[3]; /* Position to query */ + size_t iband; /* Spectral band index. Not use in shortwave */ + size_t iquad; /* Quadrature point index. Not use in shortwave */ + double wavelength; /* In nm */ + + int radcoefs_mask; /* Combination of atrstm_radcoef_flag */ + int components_mask; /* Combination of atrstm_component_flag */ + + /* For debug: assert if the fetched property is not in [k_min, k_max] */ + double k_min[ATRSTM_RADCOEFS_COUNT__]; + double k_max[ATRSTM_RADCOEFS_COUNT__]; +}; +#define ATRSTM_FETCH_RADCOEFS_ARGS_DEFAULT__ { \ + {0, 0, 0}, /* Position */ \ + SIZE_MAX, /* Index of the spectral band */ \ + SIZE_MAX, /* Index of the quadrature point */ \ + DBL_MAX, /* Wavelength */ \ + \ + ATRSTM_RADCOEFS_MASK_ALL, /* Mask of radiative properties to fetch */ \ + ATRSTM_CPNTS_MASK_ALL, /* Mask of comonent to handle */ \ + \ + /* For debug */ \ + {-DBL_MAX,-DBL_MAX,-DBL_MAX}, /* Kmin */ \ + { DBL_MAX, DBL_MAX, DBL_MAX}, /* Kmax */ \ +} +static const struct atrstm_fetch_radcoefs_args +ATRSTM_FETCH_RADCOEFS_ARGS_DEFAULT = ATRSTM_FETCH_RADCOEFS_ARGS_DEFAULT__; + /* Forward declaration of extern data types */ struct logger; struct mem_allocator; @@ -129,11 +175,20 @@ ATRSTM_API res_T atrstm_ref_get (struct atrstm* atrstm); - ATRSTM_API res_T atrstm_ref_put (struct atrstm* atrstm); +ATRSTM_API const char* +atrstm_get_name + (const struct atrstm* atrstm); + +ATRSTM_API res_T +atrstm_fetch_radcoefs + (const struct atrstm* atrstm, + const struct atrstm_fetch_radcoefs_args* args, + double radcoefs[ATRSTM_RADCOEFS_COUNT__]); + END_DECLS #endif /* ATRSTM_H */ diff --git a/src/atrstm_c.h b/src/atrstm_c.h @@ -18,6 +18,8 @@ #include "atrstm.h" +#include <astoria/atrri.h> + #include <rsys/logger.h> #include <rsys/mem_allocator.h> #include <rsys/ref_count.h> @@ -34,7 +36,7 @@ struct atrstm { struct atrri* atrri; /* Handle toward the Astoria Refractive Indices */ /* Unstructured volumetric mesh */ - struct suvm_device* suvm; + struct suvm_device* suvm; struct suvm_volume* volume; struct svx_device* svx; @@ -42,7 +44,7 @@ struct atrstm { unsigned nthreads; /* #nthreads */ - struct str name; /* Name of the gas mixture */ + struct str name; /* Name of the semi-transparent medium */ double gyration_radius_prefactor; double fractal_dimension; @@ -51,7 +53,10 @@ struct atrstm { double wlen_range[2]; /* Spectral range in nm */ unsigned grid_max_definition[3]; double optical_thickness; - + + /* Pre-fetched refractive id in shortwave */ + struct atrri_refractive_index refract_id; + struct cache* cache; /* Cache of the SVX data structures */ struct mem_allocator* allocator; diff --git a/src/atrstm_optprops.c b/src/atrstm_optprops.c @@ -1,159 +0,0 @@ -/* Copyright (C) 2020 CNRS - * - * 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 "atrstm_c.h" -#include "atrstm_log.h" -#include "atrstm_optprops.h" - -#include <astoria/atrri.h> -#include <astoria/atrtp.h> - -#include <star/suvm.h> - -/******************************************************************************* - * Local functions - ******************************************************************************/ -res_T -primitive_compute_optprops - (const struct atrstm* atrstm, - const struct atrri_refractive_index* refract_id, - const struct suvm_primitive* prim, - struct optprops optprops[]) -{ - struct optprops_compute_args args = OPTPROPS_COMPUTE_ARGS_NULL; - struct atrtp_desc desc = ATRTP_DESC_NULL; - size_t inode; - res_T res = RES_OK; - ASSERT(atrstm && prim && prim->nvertices == 4 && refract_id && optprops); - - res = atrtp_get_desc(atrstm->atrtp, &desc); - if(res != RES_OK) goto error; - - /* Setup the constant input arguments of the optical properties computation - * routine */ - args.lambda = refract_id->wavelength; - args.n = refract_id->n; - args.kappa = refract_id->kappa; - args.gyration_radius_prefactor = atrstm->gyration_radius_prefactor; - args.fractal_dimension = atrstm->fractal_dimension; - - FOR_EACH(inode, 0, 4) { - const double* node; - - /* Fetch the thermodynamic properties of the node */ - node = atrtp_desc_get_node_properties(&desc, prim->indices[inode]); - - /* Setup the per node input arguments of the optical properties computation - * routine */ - args.soot_volumic_fraction = - node[ATRTP_SOOT_VOLFRAC]; - args.soot_primary_particles_count = - node[ATRTP_SOOT_PRIMARY_PARTICLES_COUNT]; - args.soot_primary_particles_diameter = - node[ATRTP_SOOT_PRIMARY_PARTICLES_DIAMETER]; - - /* Compute the node optical properties */ - optprops_compute(&optprops[inode], &args); - } - -exit: - return res; -error: - goto exit; -} - -res_T -dump_optprops - (const struct atrstm* atrstm, - const struct atrri_refractive_index* refract_id, - FILE* stream) -{ - struct optprops_compute_args args = OPTPROPS_COMPUTE_ARGS_NULL; - struct atrtp_desc desc = ATRTP_DESC_NULL; - struct optprops props_min; - struct optprops props_max; - size_t inode; - - res_T res = RES_OK; - ASSERT(atrstm && stream); - - res = atrtp_get_desc(atrstm->atrtp, &desc); - if(res != RES_OK) goto error; - - /* Setup the constant input params of the optical properties computation */ - args.lambda = refract_id->wavelength; - args.n = refract_id->n; - args.kappa = refract_id->kappa; - args.gyration_radius_prefactor = atrstm->gyration_radius_prefactor; - args.fractal_dimension = atrstm->fractal_dimension; - - props_min.ka = DBL_MAX; - props_max.ka =-DBL_MAX; - props_min.ks = DBL_MAX; - props_max.ks =-DBL_MAX; - props_min.kext = DBL_MAX; - props_max.kext =-DBL_MAX; - - #define FPRINTF(Text, Args) { \ - const int n = fprintf(stream, Text COMMA_##Args LIST_##Args); \ - if(n < 0) { \ - log_err(atrstm, "Error writing optical properties.\n"); \ - res = RES_IO_ERR; \ - goto error; \ - } \ - } (void)0 - - FPRINTF("# Ka Ks Kext\n", ARG0()); - - FOR_EACH(inode, 0, desc.nnodes) { - struct optprops props; - const double* node; - - /* Fetch the thermodynamic properties of the node */ - node = atrtp_desc_get_node_properties(&desc, inode); - - /* Setup the per node input args of the optical properties computation */ - args.soot_volumic_fraction = - node[ATRTP_SOOT_VOLFRAC]; - args.soot_primary_particles_count = - node[ATRTP_SOOT_PRIMARY_PARTICLES_COUNT]; - args.soot_primary_particles_diameter = - node[ATRTP_SOOT_PRIMARY_PARTICLES_DIAMETER]; - - /* Compute the node optical properties */ - optprops_compute(&props, &args); - - FPRINTF("%g %g %g\n", ARG3(props.ka, props.ks, props.kext)); - - props_min.ka = MMIN(props_min.ka, props.ka); - props_max.ka = MMAX(props_max.ka, props.ka); - props_min.ks = MMIN(props_min.ks, props.ks); - props_max.ks = MMAX(props_max.ks, props.ks); - props_min.kext = MMIN(props_min.kext, props.kext); - props_max.kext = MMAX(props_max.kext, props.kext); - - } - - FPRINTF("# Bounds = [%g %g %g], [%g, %g, %g]\n", ARG6 - (props_min.ka, props_min.ks, props_min.kext, - props_max.ka, props_max.ks, props_max.kext)); - - #undef FPRINTF - -exit: - return res; -error: - goto exit; -} diff --git a/src/atrstm_optprops.h b/src/atrstm_optprops.h @@ -1,165 +0,0 @@ -/* Copyright (C) 2020 CNRS - * - * 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 ATRSTM_OPTPROPS_H -#define ATRSTM_OPTPROPS_H - -#include <rsys/math.h> -#include <rsys/rsys.h> - -/* Optical properties */ -struct optprops { - double ka; /* Absorption coefficient */ - double ks; /* Scattering coefficient */ - double kext; /* Extinction coefficient */ -}; -#define OPTPROPS_NULL__ {0} -static const struct optprops OPTPROPS_NULL = OPTPROPS_NULL__; - -struct optprops_compute_args { - double lambda; /* wavelength [nm] */ - double n; /* Refractive index (real component) */ - double kappa; /* Refractive index (imaginary component) */ - double gyration_radius_prefactor; - double fractal_dimension; - double soot_volumic_fraction; /* In m^3(soot) / m^3 */ - double soot_primary_particles_count; - double soot_primary_particles_diameter; /* In nm */ -}; -#define OPTPROPS_COMPUTE_ARGS_NULL__ {0} -static const struct optprops_compute_args OPTPROPS_COMPUTE_ARGS_NULL = - OPTPROPS_COMPUTE_ARGS_NULL__; - -/* Forward declarations */ -struct atrri_refractive_index; -struct atrstm; -struct suvm_primitive; - -extern LOCAL_SYM res_T -primitive_compute_optprops - (const struct atrstm* atrstm, - const struct atrri_refractive_index* refract_id, - const struct suvm_primitive* prim, - struct optprops optprops[]); /* Per primitive node optical properties */ - -/* Write per node optical properties regarding the submitted refracted index */ -extern LOCAL_SYM res_T -dump_optprops - (const struct atrstm* atrstm, - const struct atrri_refractive_index* refract_id, - FILE* stream); - -static INLINE res_T -primitive_compute_optprops_range - (const struct atrstm* atrstm, - const struct atrri_refractive_index* refract_id, - const struct suvm_primitive* prim, - struct optprops* optprops_min, - struct optprops* optprops_max) -{ - struct optprops props[4]; - res_T res = RES_OK; - ASSERT(optprops_min && optprops_max); - - res = primitive_compute_optprops(atrstm, refract_id, prim, props); - if(res != RES_OK) return res; - - optprops_min->ka = MMIN - (MMIN(props[0].ka, props[1].ka), - MMIN(props[2].ka, props[3].ka)); - optprops_min->ks = MMIN - (MMIN(props[0].ks, props[1].ks), - MMIN(props[2].ks, props[3].ks)); - optprops_min->kext = MMIN - (MMIN(props[0].kext, props[1].kext), - MMIN(props[2].kext, props[3].kext)); - - optprops_max->ka = MMAX - (MMAX(props[0].ka, props[1].ka), - MMAX(props[2].ka, props[3].ka)); - optprops_max->ks = MMAX - (MMAX(props[0].ks, props[1].ks), - MMAX(props[2].ks, props[3].ks)); - optprops_max->kext = MMAX - (MMAX(props[0].kext, props[1].kext), - MMAX(props[2].kext, props[3].kext)); - - return RES_OK; -} - -/* Compute the optical properties of the semi transparent medium as described - * in "Effects of multiple scattering on radiative properties of soot fractal - * aggregates" J. Yon et al., Journal of Quantitative Spectroscopy and - * Radiative Transfer Vol 133, pp. 374-381, 2014 */ -static INLINE void -optprops_compute - (struct optprops* optprops, - const struct optprops_compute_args* args) -{ - /* Input variables */ - const double lambda = args->lambda; /* [nm] */ - const double n = args->n; - const double kappa = args->kappa; - const double Fv = args->soot_volumic_fraction; /* [nm^3(soot)/nm] */ - const double Np = args->soot_primary_particles_count; - const double Dp = args->soot_primary_particles_diameter; /* [nm] */ - const double kf = args->gyration_radius_prefactor; - const double Df = args->fractal_dimension; - - if(Np == 0 || Fv == 0) { - optprops->ka = 0; - optprops->ks = 0; - optprops->kext = 0; - } else { - /* Precomputed values */ - const double n2 = n*n; - const double kappa2 = kappa*kappa; - const double four_n2_kappa2 = 4*n2*kappa2; - const double xp = (PI*Dp) / lambda; - const double xp3 = xp*xp*xp; - const double k = (2*PI) / lambda; - const double k2 = k*k; - - /* E(m), m = n + i*kappa */ - const double tmp0 = (n2 - kappa2 + 2); - const double denom = tmp0*tmp0 + four_n2_kappa2; - const double Em = (6*n*kappa) / denom; - - /* F(m), m = n + i*kappa */ - const double tmp1 = ((n2-kappa2 - 1)*(n2-kappa2+2) + four_n2_kappa2)/denom; - const double Fm = tmp1*tmp1 + Em*Em; - - /* Gyration radius */ - const double Rg = 0.5 * Dp * pow(Np/kf, 1.0/Df); /* [nm] */ - const double Rg2 = Rg*Rg; - const double g = pow(1 + 4*k2*Rg2/(3*Df), -Df*0.5); - - /* Cross sections, [nm^3/aggrefate] */ - const double Cabs = Np * (4*PI*xp3)/(k*k) * Em; - const double Csca = Np*Np * (8*PI*xp3*xp3) / (3*k2) * Fm * g; - - /* Optical properties */ - const double Va = (Np*PI*Dp*Dp*Dp) / 6; /* [nm^3] */ - const double rho = Fv / Va; /* [#aggregates / nm^3] */ - const double tmp2 = 1.e-9 * rho; - - ASSERT(denom != 0 && Df != 0 && Dp != 0); - optprops->ka = tmp2 * Cabs; - optprops->ks = tmp2 * Csca; - optprops->kext = optprops->ka + optprops->ks; - } -} - -#endif /* ATRSTM_OPTPROPS_H */ diff --git a/src/atrstm_radcoefs.c b/src/atrstm_radcoefs.c @@ -0,0 +1,271 @@ +/* Copyright (C) 2020 CNRS + * + * 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 "atrstm_c.h" +#include "atrstm_log.h" +#include "atrstm_radcoefs.h" + +#include <astoria/atrri.h> +#include <astoria/atrtp.h> + +#include <star/suvm.h> + +#include <rsys/cstr.h> + +#include <string.h> + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static INLINE int +check_fetch_radcoefs_args + (const struct atrstm* atrstm, + const struct atrstm_fetch_radcoefs_args* args) +{ + int i; + ASSERT(atrstm); + + if(!args + || args->wavelength < atrstm->wlen_range[0] + || args->wavelength > atrstm->wlen_range[1] + || !(args->radcoefs_mask & ATRSTM_RADCOEFS_MASK_ALL) + || !(args->components_mask & ATRSTM_CPNTS_MASK_ALL)) + return 0; + + FOR_EACH(i, 0, ATRSTM_RADCOEFS_COUNT__) { + if(args->k_min[i] > args->k_max[i]) return 0; + } + + return 1; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +atrstm_fetch_radcoefs + (const struct atrstm* atrstm, + const struct atrstm_fetch_radcoefs_args* args, + double radcoefs[ATRSTM_RADCOEFS_COUNT__]) +{ + struct radcoefs prim_radcoefs[4]; + double bcoords[4]; + struct suvm_primitive prim = SUVM_PRIMITIVE_NULL; + res_T res = RES_OK; + + if(!atrstm || !check_fetch_radcoefs_args(atrstm, args) || !radcoefs) { + res = RES_BAD_ARG; + goto error; + } + memset(radcoefs, 0, sizeof(double[ATRSTM_RADCOEFS_COUNT__])); + + /* Find the primitive into which pos lies */ + res = suvm_volume_at(atrstm->volume, args->pos, &prim, bcoords); + if(res != RES_OK) { + log_err(atrstm, + "Error accessing the volumetric mesh at {%g, %g, %g} -- %s.\n", + SPLIT3(args->pos), res_to_cstr(res)); + goto error; + } + + /* No primitive found */ + if(SUVM_PRIMITIVE_NONE(&prim)) { + log_warn(atrstm, + "%s: the position {%g, %g, %g} does not belongs to the semi-transparent " + "medium.\n", FUNC_NAME, SPLIT3(args->pos)); + goto exit; + } + + /* Compute the radiative properties of the primitive nodes */ + res = primitive_compute_radcoefs + (atrstm, &atrstm->refract_id, &prim, prim_radcoefs); + if(res != RES_OK) { + log_err(atrstm, + "Error computing the radiative properties for the primitive %lu -- %s.\n", + (unsigned long)prim.iprim, + res_to_cstr(res)); + goto error; + } + + /* Linearly interpolate the node radiative properties */ + if(args->radcoefs_mask & ATRSTM_RADCOEF_FLAG_Ka) { + radcoefs[ATRSTM_RADCOEF_Ka] = + prim_radcoefs[0].ka * bcoords[0] + + prim_radcoefs[1].ka * bcoords[1] + + prim_radcoefs[2].ka * bcoords[2] + + prim_radcoefs[3].ka * bcoords[3]; + ASSERT(radcoefs[ATRSTM_RADCOEF_Ka] >= args->k_min[ATRSTM_RADCOEF_Ka]); + ASSERT(radcoefs[ATRSTM_RADCOEF_Ka] <= args->k_max[ATRSTM_RADCOEF_Ka]); + } + if(args->radcoefs_mask & ATRSTM_RADCOEF_FLAG_Ks) { + radcoefs[ATRSTM_RADCOEF_Ks] = + prim_radcoefs[0].ks * bcoords[0] + + prim_radcoefs[1].ks * bcoords[1] + + prim_radcoefs[2].ks * bcoords[2] + + prim_radcoefs[3].ks * bcoords[3]; + ASSERT(radcoefs[ATRSTM_RADCOEF_Ks] >= args->k_min[ATRSTM_RADCOEF_Ks]); + ASSERT(radcoefs[ATRSTM_RADCOEF_Ks] <= args->k_max[ATRSTM_RADCOEF_Ks]); + } + if(args->radcoefs_mask & ATRSTM_RADCOEF_FLAG_Kext) { + radcoefs[ATRSTM_RADCOEF_Kext] = + prim_radcoefs[0].kext * bcoords[0] + + prim_radcoefs[1].kext * bcoords[1] + + prim_radcoefs[2].kext * bcoords[2] + + prim_radcoefs[3].kext * bcoords[3]; + ASSERT(radcoefs[ATRSTM_RADCOEF_Kext] >= args->k_min[ATRSTM_RADCOEF_Kext]); + ASSERT(radcoefs[ATRSTM_RADCOEF_Kext] <= args->k_max[ATRSTM_RADCOEF_Kext]); + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +primitive_compute_radcoefs + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + const struct suvm_primitive* prim, + struct radcoefs radcoefs[]) +{ + struct radcoefs_compute_args args = RADCOEFS_COMPUTE_ARGS_NULL; + struct atrtp_desc desc = ATRTP_DESC_NULL; + size_t inode; + res_T res = RES_OK; + ASSERT(atrstm && prim && prim->nvertices == 4 && refract_id && radcoefs); + + res = atrtp_get_desc(atrstm->atrtp, &desc); + if(res != RES_OK) goto error; + + /* Setup the constant input arguments of the optical properties computation + * routine */ + args.lambda = refract_id->wavelength; + args.n = refract_id->n; + args.kappa = refract_id->kappa; + args.gyration_radius_prefactor = atrstm->gyration_radius_prefactor; + args.fractal_dimension = atrstm->fractal_dimension; + + FOR_EACH(inode, 0, 4) { + const double* node; + + /* Fetch the thermodynamic properties of the node */ + node = atrtp_desc_get_node_properties(&desc, prim->indices[inode]); + + /* Setup the per node input arguments of the optical properties computation + * routine */ + args.soot_volumic_fraction = + node[ATRTP_SOOT_VOLFRAC]; + args.soot_primary_particles_count = + node[ATRTP_SOOT_PRIMARY_PARTICLES_COUNT]; + args.soot_primary_particles_diameter = + node[ATRTP_SOOT_PRIMARY_PARTICLES_DIAMETER]; + + /* Compute the node optical properties */ + radcoefs_compute(&radcoefs[inode], &args); + } + +exit: + return res; +error: + goto exit; +} + +res_T +dump_radcoefs + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + FILE* stream) +{ + struct radcoefs_compute_args args = RADCOEFS_COMPUTE_ARGS_NULL; + struct atrtp_desc desc = ATRTP_DESC_NULL; + struct radcoefs props_min; + struct radcoefs props_max; + size_t inode; + + res_T res = RES_OK; + ASSERT(atrstm && stream); + + res = atrtp_get_desc(atrstm->atrtp, &desc); + if(res != RES_OK) goto error; + + /* Setup the constant input params of the optical properties computation */ + args.lambda = refract_id->wavelength; + args.n = refract_id->n; + args.kappa = refract_id->kappa; + args.gyration_radius_prefactor = atrstm->gyration_radius_prefactor; + args.fractal_dimension = atrstm->fractal_dimension; + + props_min.ka = DBL_MAX; + props_max.ka =-DBL_MAX; + props_min.ks = DBL_MAX; + props_max.ks =-DBL_MAX; + props_min.kext = DBL_MAX; + props_max.kext =-DBL_MAX; + + #define FPRINTF(Text, Args) { \ + const int n = fprintf(stream, Text COMMA_##Args LIST_##Args); \ + if(n < 0) { \ + log_err(atrstm, "Error writing optical properties.\n"); \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + + FPRINTF("# Ka Ks Kext\n", ARG0()); + + FOR_EACH(inode, 0, desc.nnodes) { + struct radcoefs props; + const double* node; + + /* Fetch the thermodynamic properties of the node */ + node = atrtp_desc_get_node_properties(&desc, inode); + + /* Setup the per node input args of the optical properties computation */ + args.soot_volumic_fraction = + node[ATRTP_SOOT_VOLFRAC]; + args.soot_primary_particles_count = + node[ATRTP_SOOT_PRIMARY_PARTICLES_COUNT]; + args.soot_primary_particles_diameter = + node[ATRTP_SOOT_PRIMARY_PARTICLES_DIAMETER]; + + /* Compute the node optical properties */ + radcoefs_compute(&props, &args); + + FPRINTF("%g %g %g\n", ARG3(props.ka, props.ks, props.kext)); + + props_min.ka = MMIN(props_min.ka, props.ka); + props_max.ka = MMAX(props_max.ka, props.ka); + props_min.ks = MMIN(props_min.ks, props.ks); + props_max.ks = MMAX(props_max.ks, props.ks); + props_min.kext = MMIN(props_min.kext, props.kext); + props_max.kext = MMAX(props_max.kext, props.kext); + + } + + FPRINTF("# Bounds = [%g %g %g], [%g, %g, %g]\n", ARG6 + (props_min.ka, props_min.ks, props_min.kext, + props_max.ka, props_max.ks, props_max.kext)); + + #undef FPRINTF + +exit: + return res; +error: + goto exit; +} diff --git a/src/atrstm_radcoefs.h b/src/atrstm_radcoefs.h @@ -0,0 +1,166 @@ +/* Copyright (C) 2020 CNRS + * + * 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 ATRSTM_RADCOEFS_H +#define ATRSTM_RADCOEFS_H + +#include <rsys/math.h> +#include <rsys/rsys.h> + +/* Radiative coefficients */ +struct radcoefs { + double ka; /* Absorption coefficient */ + double ks; /* Scattering coefficient */ + double kext; /* Extinction coefficient */ +}; +#define RADCOEFS_NULL__ {0} +static const struct radcoefs RADCOEFS_NULL = RADCOEFS_NULL__; + +struct radcoefs_compute_args { + double lambda; /* wavelength [nm] */ + double n; /* Refractive index (real component) */ + double kappa; /* Refractive index (imaginary component) */ + double gyration_radius_prefactor; + double fractal_dimension; + double soot_volumic_fraction; /* In m^3(soot) / m^3 */ + double soot_primary_particles_count; + double soot_primary_particles_diameter; /* In nm */ +}; +#define RADCOEFS_COMPUTE_ARGS_NULL__ {0} +static const struct radcoefs_compute_args RADCOEFS_COMPUTE_ARGS_NULL = + RADCOEFS_COMPUTE_ARGS_NULL__; + +/* Forward declarations */ +struct atrri_refractive_index; +struct atrstm; +struct suvm_primitive; + +extern LOCAL_SYM res_T +primitive_compute_radcoefs + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + const struct suvm_primitive* prim, + struct radcoefs radcoefs[]); /* Per primitive node radiative coefficients */ + +/* Write per node radiative coefficients regarding the submitted refracted + * index */ +extern LOCAL_SYM res_T +dump_radcoefs + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + FILE* stream); + +static INLINE res_T +primitive_compute_radcoefs_range + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + const struct suvm_primitive* prim, + struct radcoefs* radcoefs_min, + struct radcoefs* radcoefs_max) +{ + struct radcoefs props[4]; + res_T res = RES_OK; + ASSERT(radcoefs_min && radcoefs_max); + + res = primitive_compute_radcoefs(atrstm, refract_id, prim, props); + if(res != RES_OK) return res; + + radcoefs_min->ka = MMIN + (MMIN(props[0].ka, props[1].ka), + MMIN(props[2].ka, props[3].ka)); + radcoefs_min->ks = MMIN + (MMIN(props[0].ks, props[1].ks), + MMIN(props[2].ks, props[3].ks)); + radcoefs_min->kext = MMIN + (MMIN(props[0].kext, props[1].kext), + MMIN(props[2].kext, props[3].kext)); + + radcoefs_max->ka = MMAX + (MMAX(props[0].ka, props[1].ka), + MMAX(props[2].ka, props[3].ka)); + radcoefs_max->ks = MMAX + (MMAX(props[0].ks, props[1].ks), + MMAX(props[2].ks, props[3].ks)); + radcoefs_max->kext = MMAX + (MMAX(props[0].kext, props[1].kext), + MMAX(props[2].kext, props[3].kext)); + + return RES_OK; +} + +/* Compute the radiative coefficients of the semi transparent medium as described + * in "Effects of multiple scattering on radiative properties of soot fractal + * aggregates" J. Yon et al., Journal of Quantitative Spectroscopy and + * Radiative Transfer Vol 133, pp. 374-381, 2014 */ +static INLINE void +radcoefs_compute + (struct radcoefs* radcoefs, + const struct radcoefs_compute_args* args) +{ + /* Input variables */ + const double lambda = args->lambda; /* [nm] */ + const double n = args->n; + const double kappa = args->kappa; + const double Fv = args->soot_volumic_fraction; /* [nm^3(soot)/nm] */ + const double Np = args->soot_primary_particles_count; + const double Dp = args->soot_primary_particles_diameter; /* [nm] */ + const double kf = args->gyration_radius_prefactor; + const double Df = args->fractal_dimension; + + if(Np == 0 || Fv == 0) { + radcoefs->ka = 0; + radcoefs->ks = 0; + radcoefs->kext = 0; + } else { + /* Precomputed values */ + const double n2 = n*n; + const double kappa2 = kappa*kappa; + const double four_n2_kappa2 = 4*n2*kappa2; + const double xp = (PI*Dp) / lambda; + const double xp3 = xp*xp*xp; + const double k = (2*PI) / lambda; + const double k2 = k*k; + + /* E(m), m = n + i*kappa */ + const double tmp0 = (n2 - kappa2 + 2); + const double denom = tmp0*tmp0 + four_n2_kappa2; + const double Em = (6*n*kappa) / denom; + + /* F(m), m = n + i*kappa */ + const double tmp1 = ((n2-kappa2 - 1)*(n2-kappa2+2) + four_n2_kappa2)/denom; + const double Fm = tmp1*tmp1 + Em*Em; + + /* Gyration radius */ + const double Rg = 0.5 * Dp * pow(Np/kf, 1.0/Df); /* [nm] */ + const double Rg2 = Rg*Rg; + const double g = pow(1 + 4*k2*Rg2/(3*Df), -Df*0.5); + + /* Cross sections, [nm^3/aggrefate] */ + const double Cabs = Np * (4*PI*xp3)/(k*k) * Em; + const double Csca = Np*Np * (8*PI*xp3*xp3) / (3*k2) * Fm * g; + + /* Radiative coefficients */ + const double Va = (Np*PI*Dp*Dp*Dp) / 6; /* [nm^3] */ + const double rho = Fv / Va; /* [#aggregates / nm^3] */ + const double tmp2 = 1.e-9 * rho; + + ASSERT(denom != 0 && Df != 0 && Dp != 0); + radcoefs->ka = tmp2 * Cabs; + radcoefs->ks = tmp2 * Csca; + radcoefs->kext = radcoefs->ka + radcoefs->ks; + } +} + +#endif /* ATRSTM_RADCOEFS_H */ diff --git a/src/atrstm_radprops.c b/src/atrstm_radprops.c @@ -0,0 +1,271 @@ +/* Copyright (C) 2020 CNRS + * + * 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 "atrstm_c.h" +#include "atrstm_log.h" +#include "atrstm_radcoefs.h" + +#include <astoria/atrri.h> +#include <astoria/atrtp.h> + +#include <star/suvm.h> + +#include <rsys/cstr.h> + +#include <string.h> + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static INLINE int +check_fetch_radcoefs_args + (const struct atrstm* atrstm, + const struct atrstm_fetch_radcoefs_args* args) +{ + int i; + ASSERT(atrstm); + + if(!args + || args->wavelength < atrstm->wlen_range[0] + || args->wavelength > atrstm->wlen_range[1] + || !(args->radcoefs_mask & ATRSTM_RADCOEFS_MASK_ALL) + || !(args->components_mask & ATRSTM_CPNTS_MASK_ALL)) + return 0; + + FOR_EACH(i, 0, ATRSTM_RADCOEFS_COUNT__) { + if(args->k_min[i] > args->k_max[i]) return 0; + } + + return 1; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +atrstm_fetch_radcoefs + (const struct atrstm* atrstm, + const struct atrstm_fetch_radcoefs_args* args, + double radcoefs[ATRSTM_RADCOEFS_COUNT__]) +{ + struct radcoefs prim_radcoefs[4]; + double bcoords[4]; + struct suvm_primitive prim = SUVM_PRIMITIVE_NULL; + res_T res = RES_OK; + + if(!atrstm || !check_fetch_radcoefs_args(atrstm, args) || !radcoefs) { + res = RES_BAD_ARG; + goto error; + } + memset(radcoefs, 0, sizeof(double[ATRSTM_RADCOEFS_COUNT__])); + + /* Find the primitive into which pos lies */ + res = suvm_volume_at(atrstm->volume, args->pos, &prim, bcoords); + if(res != RES_OK) { + log_err(atrstm, + "Error accessing the volumetric mesh at {%g, %g, %g} -- %s.\n", + SPLIT3(args->pos), res_to_cstr(res)); + goto error; + } + + /* No primitive found */ + if(SUVM_PRIMITIVE_NONE(&prim)) { + log_warn(atrstm, + "%s: the position {%g, %g, %g} does not belongs to the semi-transparent " + "medium.\n", FUNC_NAME, SPLIT3(args->pos)); + goto exit; + } + + /* Compute the radiative properties of the primitive nodes */ + res = primitive_compute_radcoefs + (atrstm, &atrstm->refract_id, &prim, prim_radcoefs); + if(res != RES_OK) { + log_err(atrstm, + "Error computing the radiative properties for the primitive %lu -- %s.\n", + (unsigned long)prim.iprim, + res_to_cstr(res)); + goto error; + } + + /* Linearly interpolate the node radiative properties */ + if(args->radcoefs_mask & ATRSTM_RADCOEF_FLAG_Ka) { + radcoefs[ATRSTM_RADCOEF_Ka] = + prim_radcoefs[0].ka * bcoords[0] + + prim_radcoefs[1].ka * bcoords[1] + + prim_radcoefs[2].ka * bcoords[2] + + prim_radcoefs[3].ka * bcoords[3]; + ASSERT(radcoefs[ATRSTM_RADCOEF_Ka] >= args->k_min[ATRSTM_RADCOEF_Ka]); + ASSERT(radcoefs[ATRSTM_RADCOEF_Ka] <= args->k_max[ATRSTM_RADCOEF_Ka]); + } + if(args->radcoefs_mask & ATRSTM_RADCOEF_FLAG_Ks) { + radcoefs[ATRSTM_RADCOEF_Ks] = + prim_radcoefs[0].ks * bcoords[0] + + prim_radcoefs[1].ks * bcoords[1] + + prim_radcoefs[2].ks * bcoords[2] + + prim_radcoefs[3].ks * bcoords[3]; + ASSERT(radcoefs[ATRSTM_RADCOEF_Ks] >= args->k_min[ATRSTM_RADCOEF_Ks]); + ASSERT(radcoefs[ATRSTM_RADCOEF_Ks] <= args->k_max[ATRSTM_RADCOEF_Ks]); + } + if(args->radcoefs_mask & ATRSTM_RADCOEF_FLAG_Kext) { + radcoefs[ATRSTM_RADCOEF_Kext] = + prim_radcoefs[0].kext * bcoords[0] + + prim_radcoefs[1].kext * bcoords[1] + + prim_radcoefs[2].kext * bcoords[2] + + prim_radcoefs[3].kext * bcoords[3]; + ASSERT(radcoefs[ATRSTM_RADCOEF_Kext] >= args->k_min[ATRSTM_RADCOEF_Kext]); + ASSERT(radcoefs[ATRSTM_RADCOEF_Kext] <= args->k_max[ATRSTM_RADCOEF_Kext]); + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +primitive_compute_radcoefs + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + const struct suvm_primitive* prim, + struct radcoefs radcoefs[]) +{ + struct radcoefs_compute_args args = RADCOEFS_COMPUTE_ARGS_NULL; + struct atrtp_desc desc = ATRTP_DESC_NULL; + size_t inode; + res_T res = RES_OK; + ASSERT(atrstm && prim && prim->nvertices == 4 && refract_id && radcoefs); + + res = atrtp_get_desc(atrstm->atrtp, &desc); + if(res != RES_OK) goto error; + + /* Setup the constant input arguments of the optical properties computation + * routine */ + args.lambda = refract_id->wavelength; + args.n = refract_id->n; + args.kappa = refract_id->kappa; + args.gyration_radius_prefactor = atrstm->gyration_radius_prefactor; + args.fractal_dimension = atrstm->fractal_dimension; + + FOR_EACH(inode, 0, 4) { + const double* node; + + /* Fetch the thermodynamic properties of the node */ + node = atrtp_desc_get_node_properties(&desc, prim->indices[inode]); + + /* Setup the per node input arguments of the optical properties computation + * routine */ + args.soot_volumic_fraction = + node[ATRTP_SOOT_VOLFRAC]; + args.soot_primary_particles_count = + node[ATRTP_SOOT_PRIMARY_PARTICLES_COUNT]; + args.soot_primary_particles_diameter = + node[ATRTP_SOOT_PRIMARY_PARTICLES_DIAMETER]; + + /* Compute the node optical properties */ + radcoefs_compute(&radcoefs[inode], &args); + } + +exit: + return res; +error: + goto exit; +} + +res_T +dump_radcoefs + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + FILE* stream) +{ + struct radcoefs_compute_args args = RADCOEFS_COMPUTE_ARGS_NULL; + struct atrtp_desc desc = ATRTP_DESC_NULL; + struct radcoefs props_min; + struct radcoefs props_max; + size_t inode; + + res_T res = RES_OK; + ASSERT(atrstm && stream); + + res = atrtp_get_desc(atrstm->atrtp, &desc); + if(res != RES_OK) goto error; + + /* Setup the constant input params of the optical properties computation */ + args.lambda = refract_id->wavelength; + args.n = refract_id->n; + args.kappa = refract_id->kappa; + args.gyration_radius_prefactor = atrstm->gyration_radius_prefactor; + args.fractal_dimension = atrstm->fractal_dimension; + + props_min.ka = DBL_MAX; + props_max.ka =-DBL_MAX; + props_min.ks = DBL_MAX; + props_max.ks =-DBL_MAX; + props_min.kext = DBL_MAX; + props_max.kext =-DBL_MAX; + + #define FPRINTF(Text, Args) { \ + const int n = fprintf(stream, Text COMMA_##Args LIST_##Args); \ + if(n < 0) { \ + log_err(atrstm, "Error writing optical properties.\n"); \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + + FPRINTF("# Ka Ks Kext\n", ARG0()); + + FOR_EACH(inode, 0, desc.nnodes) { + struct radcoefs props; + const double* node; + + /* Fetch the thermodynamic properties of the node */ + node = atrtp_desc_get_node_properties(&desc, inode); + + /* Setup the per node input args of the optical properties computation */ + args.soot_volumic_fraction = + node[ATRTP_SOOT_VOLFRAC]; + args.soot_primary_particles_count = + node[ATRTP_SOOT_PRIMARY_PARTICLES_COUNT]; + args.soot_primary_particles_diameter = + node[ATRTP_SOOT_PRIMARY_PARTICLES_DIAMETER]; + + /* Compute the node optical properties */ + radcoefs_compute(&props, &args); + + FPRINTF("%g %g %g\n", ARG3(props.ka, props.ks, props.kext)); + + props_min.ka = MMIN(props_min.ka, props.ka); + props_max.ka = MMAX(props_max.ka, props.ka); + props_min.ks = MMIN(props_min.ks, props.ks); + props_max.ks = MMAX(props_max.ks, props.ks); + props_min.kext = MMIN(props_min.kext, props.kext); + props_max.kext = MMAX(props_max.kext, props.kext); + + } + + FPRINTF("# Bounds = [%g %g %g], [%g, %g, %g]\n", ARG6 + (props_min.ka, props_min.ks, props_min.kext, + props_max.ka, props_max.ks, props_max.kext)); + + #undef FPRINTF + +exit: + return res; +error: + goto exit; +} diff --git a/src/atrstm_radprops.h b/src/atrstm_radprops.h @@ -0,0 +1,166 @@ +/* Copyright (C) 2020 CNRS + * + * 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 ATRSTM_RADCOEFS_H +#define ATRSTM_RADCOEFS_H + +#include <rsys/math.h> +#include <rsys/rsys.h> + +/* Radiative coefficients */ +struct radcoefs { + double ka; /* Absorption coefficient */ + double ks; /* Scattering coefficient */ + double kext; /* Extinction coefficient */ +}; +#define RADCOEFS_NULL__ {0} +static const struct radcoefs RADCOEFS_NULL = RADCOEFS_NULL__; + +struct radcoefs_compute_args { + double lambda; /* wavelength [nm] */ + double n; /* Refractive index (real component) */ + double kappa; /* Refractive index (imaginary component) */ + double gyration_radius_prefactor; + double fractal_dimension; + double soot_volumic_fraction; /* In m^3(soot) / m^3 */ + double soot_primary_particles_count; + double soot_primary_particles_diameter; /* In nm */ +}; +#define RADCOEFS_COMPUTE_ARGS_NULL__ {0} +static const struct radcoefs_compute_args RADCOEFS_COMPUTE_ARGS_NULL = + RADCOEFS_COMPUTE_ARGS_NULL__; + +/* Forward declarations */ +struct atrri_refractive_index; +struct atrstm; +struct suvm_primitive; + +extern LOCAL_SYM res_T +primitive_compute_radcoefs + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + const struct suvm_primitive* prim, + struct radcoefs radcoefs[]); /* Per primitive node radiative coefficients */ + +/* Write per node radiative coefficients regarding the submitted refracted + * index */ +extern LOCAL_SYM res_T +dump_radcoefs + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + FILE* stream); + +static INLINE res_T +primitive_compute_radcoefs_range + (const struct atrstm* atrstm, + const struct atrri_refractive_index* refract_id, + const struct suvm_primitive* prim, + struct radcoefs* radcoefs_min, + struct radcoefs* radcoefs_max) +{ + struct radcoefs props[4]; + res_T res = RES_OK; + ASSERT(radcoefs_min && radcoefs_max); + + res = primitive_compute_radcoefs(atrstm, refract_id, prim, props); + if(res != RES_OK) return res; + + radcoefs_min->ka = MMIN + (MMIN(props[0].ka, props[1].ka), + MMIN(props[2].ka, props[3].ka)); + radcoefs_min->ks = MMIN + (MMIN(props[0].ks, props[1].ks), + MMIN(props[2].ks, props[3].ks)); + radcoefs_min->kext = MMIN + (MMIN(props[0].kext, props[1].kext), + MMIN(props[2].kext, props[3].kext)); + + radcoefs_max->ka = MMAX + (MMAX(props[0].ka, props[1].ka), + MMAX(props[2].ka, props[3].ka)); + radcoefs_max->ks = MMAX + (MMAX(props[0].ks, props[1].ks), + MMAX(props[2].ks, props[3].ks)); + radcoefs_max->kext = MMAX + (MMAX(props[0].kext, props[1].kext), + MMAX(props[2].kext, props[3].kext)); + + return RES_OK; +} + +/* Compute the radiative coefficients of the semi transparent medium as described + * in "Effects of multiple scattering on radiative properties of soot fractal + * aggregates" J. Yon et al., Journal of Quantitative Spectroscopy and + * Radiative Transfer Vol 133, pp. 374-381, 2014 */ +static INLINE void +radcoefs_compute + (struct radcoefs* radcoefs, + const struct radcoefs_compute_args* args) +{ + /* Input variables */ + const double lambda = args->lambda; /* [nm] */ + const double n = args->n; + const double kappa = args->kappa; + const double Fv = args->soot_volumic_fraction; /* [nm^3(soot)/nm] */ + const double Np = args->soot_primary_particles_count; + const double Dp = args->soot_primary_particles_diameter; /* [nm] */ + const double kf = args->gyration_radius_prefactor; + const double Df = args->fractal_dimension; + + if(Np == 0 || Fv == 0) { + radcoefs->ka = 0; + radcoefs->ks = 0; + radcoefs->kext = 0; + } else { + /* Precomputed values */ + const double n2 = n*n; + const double kappa2 = kappa*kappa; + const double four_n2_kappa2 = 4*n2*kappa2; + const double xp = (PI*Dp) / lambda; + const double xp3 = xp*xp*xp; + const double k = (2*PI) / lambda; + const double k2 = k*k; + + /* E(m), m = n + i*kappa */ + const double tmp0 = (n2 - kappa2 + 2); + const double denom = tmp0*tmp0 + four_n2_kappa2; + const double Em = (6*n*kappa) / denom; + + /* F(m), m = n + i*kappa */ + const double tmp1 = ((n2-kappa2 - 1)*(n2-kappa2+2) + four_n2_kappa2)/denom; + const double Fm = tmp1*tmp1 + Em*Em; + + /* Gyration radius */ + const double Rg = 0.5 * Dp * pow(Np/kf, 1.0/Df); /* [nm] */ + const double Rg2 = Rg*Rg; + const double g = pow(1 + 4*k2*Rg2/(3*Df), -Df*0.5); + + /* Cross sections, [nm^3/aggrefate] */ + const double Cabs = Np * (4*PI*xp3)/(k*k) * Em; + const double Csca = Np*Np * (8*PI*xp3*xp3) / (3*k2) * Fm * g; + + /* Radiative coefficients */ + const double Va = (Np*PI*Dp*Dp*Dp) / 6; /* [nm^3] */ + const double rho = Fv / Va; /* [#aggregates / nm^3] */ + const double tmp2 = 1.e-9 * rho; + + ASSERT(denom != 0 && Df != 0 && Dp != 0); + radcoefs->ka = tmp2 * Cabs; + radcoefs->ks = tmp2 * Csca; + radcoefs->kext = radcoefs->ka + radcoefs->ks; + } +} + +#endif /* ATRSTM_RADCOEFS_H */ diff --git a/src/atrstm_setup_octrees.c b/src/atrstm_setup_octrees.c @@ -18,7 +18,7 @@ #include "atrstm_c.h" #include "atrstm_cache.h" #include "atrstm_log.h" -#include "atrstm_optprops.h" +#include "atrstm_radcoefs.h" #include "atrstm_partition.h" #include "atrstm_setup_octrees.h" #include "atrstm_svx.h" @@ -86,24 +86,24 @@ register_primitive } static void -voxel_commit_optprops_range +voxel_commit_radcoefs_range (float* vox, const enum atrstm_component cpnt, - const struct optprops* optprops_min, - const struct optprops* optprops_max) + const struct radcoefs* radcoefs_min, + const struct radcoefs* radcoefs_max) { double ka[2], ks[2], kext[2]; float vox_ka[2], vox_ks[2], vox_kext[2]; ASSERT(vox); - ASSERT(optprops_min); - ASSERT(optprops_max); + ASSERT(radcoefs_min); + ASSERT(radcoefs_max); - ka[0] = optprops_min->ka; - ka[1] = optprops_max->ka; - ks[0] = optprops_min->ks; - ks[1] = optprops_max->ks; - kext[0] = optprops_min->kext; - kext[1] = optprops_max->kext; + ka[0] = radcoefs_min->ka; + ka[1] = radcoefs_max->ka; + ks[0] = radcoefs_min->ks; + ks[1] = radcoefs_max->ks; + kext[0] = radcoefs_min->kext; + kext[1] = radcoefs_max->kext; /* Ensure that the single precision bounds include their double precision * version. */ @@ -115,12 +115,12 @@ voxel_commit_optprops_range if(kext[1] != (float)kext[1]) kext[1] = nextafterf((float)kext[1], FLT_MAX); /* Fetch the range of the voxel optical properties */ - vox_ka[0] = vox_get(vox, cpnt, ATRSTM_Ka, ATRSTM_SVX_MIN); - vox_ka[1] = vox_get(vox, cpnt, ATRSTM_Ka, ATRSTM_SVX_MAX); - vox_ks[0] = vox_get(vox, cpnt, ATRSTM_Ks, ATRSTM_SVX_MIN); - vox_ks[1] = vox_get(vox, cpnt, ATRSTM_Ks, ATRSTM_SVX_MAX); - vox_kext[0] = vox_get(vox, cpnt, ATRSTM_Kext, ATRSTM_SVX_MIN); - vox_kext[1] = vox_get(vox, cpnt, ATRSTM_Kext, ATRSTM_SVX_MAX); + vox_ka[0] = vox_get(vox, cpnt, ATRSTM_RADCOEF_Ka, ATRSTM_SVX_MIN); + vox_ka[1] = vox_get(vox, cpnt, ATRSTM_RADCOEF_Ka, ATRSTM_SVX_MAX); + vox_ks[0] = vox_get(vox, cpnt, ATRSTM_RADCOEF_Ks, ATRSTM_SVX_MIN); + vox_ks[1] = vox_get(vox, cpnt, ATRSTM_RADCOEF_Ks, ATRSTM_SVX_MAX); + vox_kext[0] = vox_get(vox, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MIN); + vox_kext[1] = vox_get(vox, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MAX); /* Update the range of the voxel optical properties */ vox_ka[0] = MMIN(vox_ka[0], (float)ka[0]); @@ -131,12 +131,12 @@ voxel_commit_optprops_range vox_kext[1] = MMAX(vox_kext[1], (float)kext[1]); /* Commit the upd to the voxel */ - vox_set(vox, cpnt, ATRSTM_Ka, ATRSTM_SVX_MIN, vox_ka[0]); - vox_set(vox, cpnt, ATRSTM_Ka, ATRSTM_SVX_MAX, vox_ka[1]); - vox_set(vox, cpnt, ATRSTM_Ks, ATRSTM_SVX_MIN, vox_ks[0]); - vox_set(vox, cpnt, ATRSTM_Ks, ATRSTM_SVX_MAX, vox_ks[1]); - vox_set(vox, cpnt, ATRSTM_Kext, ATRSTM_SVX_MIN, vox_kext[0]); - vox_set(vox, cpnt, ATRSTM_Kext, ATRSTM_SVX_MAX, vox_kext[1]); + vox_set(vox, cpnt, ATRSTM_RADCOEF_Ka, ATRSTM_SVX_MIN, vox_ka[0]); + vox_set(vox, cpnt, ATRSTM_RADCOEF_Ka, ATRSTM_SVX_MAX, vox_ka[1]); + vox_set(vox, cpnt, ATRSTM_RADCOEF_Ks, ATRSTM_SVX_MIN, vox_ks[0]); + vox_set(vox, cpnt, ATRSTM_RADCOEF_Ks, ATRSTM_SVX_MAX, vox_ks[1]); + vox_set(vox, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MIN, vox_kext[0]); + vox_set(vox, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MAX, vox_kext[1]); } static res_T @@ -223,21 +223,21 @@ voxelize_partition intersect = suvm_polyhedron_intersect_aabb(&poly, vxl_low, vxl_upp); if(intersect != SUVM_INTERSECT_NONE) { - struct optprops optprops_min; - struct optprops optprops_max; + struct radcoefs radcoefs_min; + struct radcoefs radcoefs_max; float* vox = part_get_voxel(part, mcode[2]); /* Currently, gas is not handled */ - optprops_min = OPTPROPS_NULL; - optprops_max = OPTPROPS_NULL; - voxel_commit_optprops_range - (vox, ATRSTM_CPNT_GAS, &optprops_min, &optprops_max); + radcoefs_min = RADCOEFS_NULL; + radcoefs_max = RADCOEFS_NULL; + voxel_commit_radcoefs_range + (vox, ATRSTM_CPNT_GAS, &radcoefs_min, &radcoefs_max); - res = primitive_compute_optprops_range - (atrstm, refract_id, &prim, &optprops_min, &optprops_max); + res = primitive_compute_radcoefs_range + (atrstm, refract_id, &prim, &radcoefs_min, &radcoefs_max); if(UNLIKELY(res != RES_OK)) goto error; - voxel_commit_optprops_range - (vox, ATRSTM_CPNT_SOOT, &optprops_min, &optprops_max); + voxel_commit_radcoefs_range + (vox, ATRSTM_CPNT_SOOT, &radcoefs_min, &radcoefs_max); } } } @@ -429,19 +429,19 @@ voxels_merge_component FOR_EACH(ivox, 0, nvoxs) { const float* vox = voxels[ivox]; - ka[0] = MMIN(ka[0], vox_get(vox, cpnt, ATRSTM_Ka, ATRSTM_SVX_MIN)); - ka[1] = MMAX(ka[1], vox_get(vox, cpnt, ATRSTM_Ka, ATRSTM_SVX_MAX)); - ks[0] = MMIN(ks[0], vox_get(vox, cpnt, ATRSTM_Ks, ATRSTM_SVX_MIN)); - ks[1] = MMAX(ks[1], vox_get(vox, cpnt, ATRSTM_Ks, ATRSTM_SVX_MAX)); - kext[0] = MMIN(kext[0], vox_get(vox, cpnt, ATRSTM_Kext, ATRSTM_SVX_MIN)); - kext[1] = MMAX(kext[1], vox_get(vox, cpnt, ATRSTM_Kext, ATRSTM_SVX_MAX)); + ka[0] = MMIN(ka[0], vox_get(vox, cpnt, ATRSTM_RADCOEF_Ka, ATRSTM_SVX_MIN)); + ka[1] = MMAX(ka[1], vox_get(vox, cpnt, ATRSTM_RADCOEF_Ka, ATRSTM_SVX_MAX)); + ks[0] = MMIN(ks[0], vox_get(vox, cpnt, ATRSTM_RADCOEF_Ks, ATRSTM_SVX_MIN)); + ks[1] = MMAX(ks[1], vox_get(vox, cpnt, ATRSTM_RADCOEF_Ks, ATRSTM_SVX_MAX)); + kext[0] = MMIN(kext[0], vox_get(vox, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MIN)); + kext[1] = MMAX(kext[1], vox_get(vox, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MAX)); } - vox_set(dst, cpnt, ATRSTM_Ka, ATRSTM_SVX_MIN, ka[0]); - vox_set(dst, cpnt, ATRSTM_Ka, ATRSTM_SVX_MAX, ka[1]); - vox_set(dst, cpnt, ATRSTM_Ks, ATRSTM_SVX_MIN, ks[0]); - vox_set(dst, cpnt, ATRSTM_Ks, ATRSTM_SVX_MAX, ks[1]); - vox_set(dst, cpnt, ATRSTM_Kext, ATRSTM_SVX_MIN, kext[0]); - vox_set(dst, cpnt, ATRSTM_Kext, ATRSTM_SVX_MAX, kext[1]); + vox_set(dst, cpnt, ATRSTM_RADCOEF_Ka, ATRSTM_SVX_MIN, ka[0]); + vox_set(dst, cpnt, ATRSTM_RADCOEF_Ka, ATRSTM_SVX_MAX, ka[1]); + vox_set(dst, cpnt, ATRSTM_RADCOEF_Ks, ATRSTM_SVX_MIN, ks[0]); + vox_set(dst, cpnt, ATRSTM_RADCOEF_Ks, ATRSTM_SVX_MAX, ks[1]); + vox_set(dst, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MIN, kext[0]); + vox_set(dst, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MAX, kext[1]); } static void @@ -472,8 +472,8 @@ voxels_component_compute_kext_range /* Compute the Kext range of the submitted voxels */ FOR_EACH(ivox, 0, nvoxs) { const float* vox = voxels[ivox].data; - kext[0] = MMIN(kext[0], vox_get(vox, cpnt, ATRSTM_Kext, ATRSTM_SVX_MIN)); - kext[1] = MMAX(kext[1], vox_get(vox, cpnt, ATRSTM_Kext, ATRSTM_SVX_MAX)); + kext[0] = MMIN(kext[0], vox_get(vox, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MIN)); + kext[1] = MMAX(kext[1], vox_get(vox, cpnt, ATRSTM_RADCOEF_Kext, ATRSTM_SVX_MAX)); } } diff --git a/src/atrstm_svx.h b/src/atrstm_svx.h @@ -25,21 +25,21 @@ * linearly as N single precision floating point data, with N computed as * bellow: * - * N = ATRSTM_PROPS_COUNT__ #optical properties per voxel + * N = ATRSTM_RADCOEFS_COUNT__ #optical properties per voxel * * ATRSTM_SVX_OPS_COUNT__ #supported operations on each properties * * ATRSTM_CPNTS_COUNT__; #components on which properties are defined * * In a given voxel, the index `id' in [0, N-1] corresponding to the optical - * property `enum atrstm_property P' of the component `enum atrstm_component + * property `enum atrstm_radcoef P' of the component `enum atrstm_component * C' according to the operation `enum atrstm_svx_op O' is * then computed as bellow: * * id = C * NFLOATS_PER_CPNT + P * HTSKY_SVX_OPS_COUNT__ + O; - * NFLOATS_PER_CPNT = ATRSTM_SVX_OPS_COUNT__ * ATRSTM_PROPS_COUNT__; + * NFLOATS_PER_CPNT = ATRSTM_SVX_OPS_COUNT__ * ATRSTM_RADCOEFS_COUNT__; */ /* Constant defining the number of floating point data per component */ -#define NFLOATS_PER_CPNT (ATRSTM_SVX_OPS_COUNT__ * ATRSTM_PROPS_COUNT__) +#define NFLOATS_PER_CPNT (ATRSTM_SVX_OPS_COUNT__ * ATRSTM_RADCOEFS_COUNT__) /* Constant defining the overall number of floating point data of a voxel */ #define NFLOATS_PER_VOXEL (NFLOATS_PER_CPNT * ATRSTM_CPNTS_COUNT__) @@ -48,12 +48,12 @@ static FINLINE float vox_get (const float* vox, const enum atrstm_component cpnt, - const enum atrstm_property prop, + const enum atrstm_radcoef prop, const enum atrstm_svx_op op) { ASSERT(vox); ASSERT((unsigned)cpnt < ATRSTM_CPNTS_COUNT__); - ASSERT((unsigned)prop < ATRSTM_PROPS_COUNT__); + ASSERT((unsigned)prop < ATRSTM_RADCOEFS_COUNT__); ASSERT((unsigned)op < ATRSTM_SVX_OPS_COUNT__); return vox[cpnt*NFLOATS_PER_CPNT + prop*ATRSTM_SVX_OPS_COUNT__ + op]; } @@ -62,13 +62,13 @@ static FINLINE void vox_set (float* vox, const enum atrstm_component cpnt, - const enum atrstm_property prop, + const enum atrstm_radcoef prop, const enum atrstm_svx_op op, const float val) { ASSERT(vox); ASSERT((unsigned)cpnt < ATRSTM_CPNTS_COUNT__); - ASSERT((unsigned)prop < ATRSTM_PROPS_COUNT__); + ASSERT((unsigned)prop < ATRSTM_RADCOEFS_COUNT__); ASSERT((unsigned)op < ATRSTM_SVX_OPS_COUNT__); vox[cpnt*NFLOATS_PER_CPNT + prop*ATRSTM_SVX_OPS_COUNT__ + op] = val; } @@ -81,7 +81,7 @@ vox_clear(float* vox) FOR_EACH(cpnt, 0, ATRSTM_CPNTS_COUNT__) { int prop; - FOR_EACH(prop, 0, ATRSTM_PROPS_COUNT__) { + FOR_EACH(prop, 0, ATRSTM_RADCOEFS_COUNT__) { vox_set(vox, cpnt, prop, ATRSTM_SVX_MIN, FLT_MAX); vox_set(vox, cpnt, prop, ATRSTM_SVX_MAX,-FLT_MAX); } diff --git a/src/test_atrstm_optprops.c b/src/test_atrstm_optprops.c @@ -1,43 +0,0 @@ -/* Copyright (C) 2020 CNRS - * - * 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 "atrstm_optprops.h" - -int -main(int argc, char** argv) -{ - const double ka_ref = 5.7382401729092799E-019; - const double ks_ref = 7.2169062018378995E-024; - - struct optprops optprops; - struct optprops_compute_args args = OPTPROPS_COMPUTE_ARGS_NULL; - (void)argc, (void)argv; - - args.lambda = 633; - args.n = 1.90; - args.kappa = 0.55; - args.gyration_radius_prefactor = 1.70; - args.fractal_dimension = 1.75; - args.soot_volumic_fraction = 1.e-7; - args.soot_primary_particles_count = 100; - args.soot_primary_particles_diameter = 1; - - optprops_compute(&optprops, &args); - - printf("ka = %g; ks = %g\n", optprops.ka, optprops.ks); - CHK(eq_eps(optprops.ka, ka_ref, ka_ref*1.e-6)); - CHK(eq_eps(optprops.ks, ks_ref, ks_ref*1.e-6)); - return 0; -} diff --git a/src/test_atrstm_radcoefs.c b/src/test_atrstm_radcoefs.c @@ -0,0 +1,43 @@ +/* Copyright (C) 2020 CNRS + * + * 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 "atrstm_radcoefs.h" + +int +main(int argc, char** argv) +{ + const double ka_ref = 5.7382401729092799E-019; + const double ks_ref = 7.2169062018378995E-024; + + struct radcoefs radcoefs; + struct radcoefs_compute_args args = RADCOEFS_COMPUTE_ARGS_NULL; + (void)argc, (void)argv; + + args.lambda = 633; + args.n = 1.90; + args.kappa = 0.55; + args.gyration_radius_prefactor = 1.70; + args.fractal_dimension = 1.75; + args.soot_volumic_fraction = 1.e-7; + args.soot_primary_particles_count = 100; + args.soot_primary_particles_diameter = 1; + + radcoefs_compute(&radcoefs, &args); + + printf("ka = %g; ks = %g\n", radcoefs.ka, radcoefs.ks); + CHK(eq_eps(radcoefs.ka, ka_ref, ka_ref*1.e-6)); + CHK(eq_eps(radcoefs.ks, ks_ref, ks_ref*1.e-6)); + return 0; +}