stardis-solver

Solve coupled heat transfers
git clone git://git.meso-star.fr/stardis-solver.git
Log | Files | Refs | README | LICENSE

commit ce4dffb0cf39bf7c20bd48a4c70b8d04a2ad725f
parent 3434fe78d33de5b010b800d47550cd5b744ce1ce
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  6 Feb 2019 11:10:49 +0100

Major refactoring of generic solvers

Move the realisation code in specific files. Move the
radiative/fluid/solid/boundary temperature routines in specific files
and rename them in radiative/fluid/solid/boundary path.

Update estimator internal API. It is now created with a given number of
overall realisations and number of successes. The temperature/flux
estimations are setuped from the MC accumulators and the number of
sucesses of the estimator.

Rename the sdis_estimator_type constants.

Diffstat:
Mcmake/CMakeLists.txt | 13++++++++++++-
Msrc/sdis.h | 35+++++++++++++++++------------------
Asrc/sdis_Xd_begin.h | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_Xd_end.h | 35+++++++++++++++++++++++++++++++++++
Msrc/sdis_estimator.c | 33+++++++++++++++++----------------
Msrc/sdis_estimator_c.h | 46++++++++++++++++++++++++++++++++++++++++------
Asrc/sdis_heat_path.c | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path.h | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_boundary_Xd.h | 578++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_conductive_Xd.h | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_convective_Xd.h | 228+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_radiative_Xd.h | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_misc.h | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_realisation.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_realisation.h | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_realisation_Xd.h | 327+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_scene.c | 4+---
Msrc/sdis_scene_c.h | 10+++++++---
Msrc/sdis_solve.c | 531++++++++++++-------------------------------------------------------------------
Msrc/sdis_solve_Xd.h | 2250++++++++++++++++++-------------------------------------------------------------
Asrc/sdis_solve_radiative.c | 20++++++++++++++++++++
Msrc/test_sdis_solve_boundary_flux.c | 4++--
Msrc/test_sdis_solve_probe.c | 2+-
23 files changed, 2865 insertions(+), 2248 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -57,8 +57,10 @@ set(SDIS_FILES_SRC sdis_data.c sdis_device.c sdis_estimator.c + sdis_heat_path.c sdis_interface.c sdis_medium.c + sdis_realisation.c sdis_scene.c sdis_solve.c) @@ -69,11 +71,20 @@ set(SDIS_FILES_INC sdis_camera.h sdis_device_c.h sdis_estimator_c.h + sdis_heat_path.h + sdis_heat_path_boundary_Xd.h + sdis_heat_path_conductive_Xd.h + sdis_heat_path_convective_Xd.h + sdis_heat_path_radiative_Xd.h sdis_interface_c.h sdis_medium_c.h + sdis_realisation.h + sdis_realisation_Xd.h sdis_scene_c.h sdis_scene_Xd.h - sdis_solve_Xd.h) + sdis_solve_Xd.h + sdis_Xd_begin.h + sdis_Xd_end.h) set(SDIS_FILES_DOC COPYING README.md) diff --git a/src/sdis.h b/src/sdis.h @@ -77,9 +77,9 @@ enum sdis_medium_type { }; enum sdis_estimator_type { - SDIS_TEMPERATURE_ESTIMATOR, - SDIS_FLUX_ESTIMATOR, - SDIS_EST_TYPES_COUNT__ + SDIS_ESTIMATOR_TEMPERATURE, + SDIS_ESTIMATOR_FLUX, + SDIS_ESTIMATOR_TYPES_COUNT__ }; /* Random walk vertex, i.e. a spatiotemporal position at a given step of the @@ -604,20 +604,6 @@ sdis_solve_probe_boundary struct sdis_estimator** estimator); SDIS_API res_T -sdis_solve_camera - (struct sdis_scene* scn, - const struct sdis_camera* cam, /* Point of view */ - const double time, /* Observation time */ - const double fp_to_meter, /* Scale from floating point units to meters */ - const double ambient_radiative_temperature, /* In Kelvin */ - const double reference_temperature, /* In Kelvin */ - const size_t width, /* Image definition in in X */ - const size_t height, /* Image definition in Y */ - const size_t spp, /* #samples per pixel */ - sdis_write_accums_T writer, - void* writer_data); - -SDIS_API res_T sdis_solve_boundary (struct sdis_scene* scn, const size_t nrealisations, /* #realisations */ @@ -630,7 +616,6 @@ sdis_solve_boundary const double reference_temperature, /* In Kelvin */ struct sdis_estimator** estimator); -/* Flux solver */ SDIS_API res_T sdis_solve_probe_boundary_flux (struct sdis_scene* scn, @@ -655,6 +640,20 @@ sdis_solve_boundary_flux const double reference_temperature, /* In Kelvin */ struct sdis_estimator** estimator); +SDIS_API res_T +sdis_solve_camera + (struct sdis_scene* scn, + const struct sdis_camera* cam, /* Point of view */ + const double time, /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double ambient_radiative_temperature, /* In Kelvin */ + const double reference_temperature, /* In Kelvin */ + const size_t width, /* Image definition in in X */ + const size_t height, /* Image definition in Y */ + const size_t spp, /* #samples per pixel */ + sdis_write_accums_T writer, + void* writer_data); + END_DECLS #endif /* SDIS_H */ diff --git a/src/sdis_Xd_begin.h b/src/sdis_Xd_begin.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 SDIS_XD_BEGIN_H +#define SDIS_XD_BEGIN_H + +#include <rsys/rsys.h> + +struct rwalk_context { + double Tarad; /* Ambient radiative temperature */ + double Tref3; /* Reference temperature ^ 3 */ +}; + +#endif /* SDIS_XD_BEGIN_H */ + +/* Check prerequisite */ +#ifndef SDIS_SOLVE_DIMENSION + #error "The SDIS_SOLVE_DIMENSION macro must be defined." +#endif + +#if SDIS_SOLVE_DIMENSION == 2 + #include <rsys/double2.h> + #include <rsys/float2.h> + #include <star/s2d.h> +#elif SDIS_SOLVE_DIMENSION == 3 + #include <rsys/double3.h> + #include <rsys/float3.h> + #include <star/s3d.h> +#else + #error "Invalid dimension." +#endif + +/* Syntactic sugar */ +#define DIM SDIS_SOLVE_DIMENSION + +/* Star-XD macros generic to SDIS_SOLVE_DIMENSION */ +#define sXd(Name) CONCAT(CONCAT(CONCAT(s, DIM), d_), Name) +#define sXd_dev CONCAT(CONCAT(s, DIM), d) +#define SXD_HIT_NONE CONCAT(CONCAT(S,DIM), D_HIT_NONE) +#define SXD_HIT_NULL CONCAT(CONCAT(S,DIM), D_HIT_NULL) +#define SXD_HIT_NULL__ CONCAT(CONCAT(S, DIM), D_HIT_NULL__) +#define SXD_POSITION CONCAT(CONCAT(S, DIM), D_POSITION) +#define SXD_GEOMETRY_NORMAL CONCAT(CONCAT(S, DIM), D_GEOMETRY_NORMAL) +#define SXD_VERTEX_DATA_NULL CONCAT(CONCAT(S, DIM), D_VERTEX_DATA_NULL) +#define SXD CONCAT(CONCAT(S, DIM), D) +#define SXD_FLOAT2 CONCAT(CONCAT(S, DIM), D_FLOAT2) +#define SXD_FLOAT3 CONCAT(CONCAT(S, DIM), D_FLOAT3) +#define SXD_SAMPLE CONCAT(CONCAT(S, DIM), D_SAMPLE) + +/* Vector macros generic to SDIS_SOLVE_DIMENSION */ +#define dX(Func) CONCAT(CONCAT(CONCAT(d, DIM), _), Func) +#define fX(Func) CONCAT(CONCAT(CONCAT(f, DIM), _), Func) +#define fX_set_dX CONCAT(CONCAT(CONCAT(f, DIM), _set_d), DIM) +#define dX_set_fX CONCAT(CONCAT(CONCAT(d, DIM), _set_f), DIM) + +/* Macro making generic its submitted nae to SDIS_SOLVE_DIMENSION */ +#define XD(Name) CONCAT(CONCAT(CONCAT(Name, _), DIM), d) + +/* Generate the generic data structures and constants */ +#if (SDIS_SOLVE_DIMENSION == 2 && !defined(SDIS_2D_H)) \ +|| (SDIS_SOLVE_DIMENSION == 3 && !defined(SDIS_3D_H)) + #if SDIS_SOLVE_DIMENSION == 2 + #define SDIS_2D_H + #else + #define SDIS_3D_H + #endif + +/* Current state of the random walk */ +struct XD(rwalk) { + struct sdis_rwalk_vertex vtx; /* Position and time of the Random walk */ + const struct sdis_medium* mdm; /* Medium in which the random walk lies */ + struct sXd(hit) hit; /* Hit of the random walk */ + enum sdis_side hit_side; +}; +static const struct XD(rwalk) XD(RWALK_NULL) = { + SDIS_RWALK_VERTEX_NULL__, NULL, SXD_HIT_NULL__, SDIS_SIDE_NULL__ +}; + +struct XD(temperature) { + res_T (*func)/* Next function to invoke in order to compute the temperature */ + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* temp); + double value; /* Current value of the temperature */ + int done; +}; +static const struct XD(temperature) XD(TEMPERATURE_NULL) = { NULL, 0, 0 }; + +#endif /* SDIX_XD_H */ + diff --git a/src/sdis_Xd_end.h b/src/sdis_Xd_end.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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/>. */ + +#undef SDIS_SOLVE_DIMENSION +#undef DIM + +#undef sXd +#undef sXd_dev +#undef SXD_HIT_NONE +#undef SXD_HIT_NULL +#undef SXD_HIT_NULL__ +#undef SXD_POSITION +#undef SXD_GEOMETRY_NORMAL +#undef SXD_VERTEX_DATA_NULL +#undef SXD +#undef SXD_FLOAT2 +#undef SXD_FLOAT3 +#undef SXD_SAMPLE + +#undef dX +#undef fX +#undef fX_set_dX +#undef dX_set_fX diff --git a/src/sdis_estimator.c b/src/sdis_estimator.c @@ -28,8 +28,6 @@ estimator_release(ref_T* ref) ASSERT(ref); estimator = CONTAINER_OF(ref, struct sdis_estimator, ref); dev = estimator->dev; - ASSERT((estimator->fluxes!=NULL) == (estimator->type==SDIS_FLUX_ESTIMATOR)); - MEM_RM(dev->allocator, estimator->fluxes); MEM_RM(dev->allocator, estimator); SDIS(device_ref_put(dev)); } @@ -93,10 +91,10 @@ res_T sdis_estimator_get_convective_flux (const struct sdis_estimator* estimator, struct sdis_mc* flux) { - if(!estimator || !flux ||estimator->type != SDIS_FLUX_ESTIMATOR) + if(!estimator || !flux ||estimator->type != SDIS_ESTIMATOR_FLUX) return RES_BAD_ARG; ASSERT(estimator->fluxes); - *flux = estimator->fluxes[FLUX_CONVECTIVE__]; + *flux = estimator->fluxes[FLUX_CONVECTIVE]; return RES_OK; } @@ -104,10 +102,10 @@ res_T sdis_estimator_get_radiative_flux (const struct sdis_estimator* estimator, struct sdis_mc* flux) { - if(!estimator || !flux || estimator->type != SDIS_FLUX_ESTIMATOR) + if(!estimator || !flux || estimator->type != SDIS_ESTIMATOR_FLUX) return RES_BAD_ARG; ASSERT(estimator->fluxes); - *flux = estimator->fluxes[FLUX_RADIATIVE__]; + *flux = estimator->fluxes[FLUX_RADIATIVE]; return RES_OK; } @@ -115,10 +113,10 @@ res_T sdis_estimator_get_total_flux (const struct sdis_estimator* estimator, struct sdis_mc* flux) { - if(!estimator || !flux || estimator->type != SDIS_FLUX_ESTIMATOR) + if(!estimator || !flux || estimator->type != SDIS_ESTIMATOR_FLUX) return RES_BAD_ARG; ASSERT(estimator->fluxes); - *flux = estimator->fluxes[FLUX_TOTAL__]; + *flux = estimator->fluxes[FLUX_TOTAL]; return RES_OK; } @@ -129,14 +127,19 @@ res_T estimator_create (struct sdis_device* dev, const enum sdis_estimator_type type, + const size_t nrealisations, + const size_t nsuccesses, struct sdis_estimator** out_estimator) { struct sdis_estimator* estimator = NULL; res_T res = RES_OK; - if(!dev || !out_estimator - || (type != SDIS_TEMPERATURE_ESTIMATOR && type != SDIS_FLUX_ESTIMATOR)) - { + if(!dev + || (unsigned)type >= SDIS_ESTIMATOR_TYPES_COUNT__ + || !nrealisations + || !nsuccesses + || nsuccesses > nrealisations + || !out_estimator) { res = RES_BAD_ARG; goto error; } @@ -146,14 +149,12 @@ estimator_create res = RES_MEM_ERR; goto error; } - estimator->type = type; - estimator->fluxes = (type != SDIS_FLUX_ESTIMATOR) ? NULL - : MEM_CALLOC(dev->allocator, FLUX_NAMES_COUNT__, sizeof(struct sdis_mc)); ref_init(&estimator->ref); SDIS(device_ref_get(dev)); + estimator->nrealisations = nsuccesses; + estimator->nfailures = nrealisations - nsuccesses; estimator->dev = dev; - if(type == SDIS_FLUX_ESTIMATOR && !estimator->fluxes) goto error; - + estimator->type = type; exit: if(out_estimator) *out_estimator = estimator; return res; diff --git a/src/sdis_estimator_c.h b/src/sdis_estimator_c.h @@ -16,6 +16,7 @@ #ifndef SDIS_ESTIMATOR_C_H #define SDIS_ESTIMATOR_C_H +#include <rsys/math.h> #include <rsys/ref_count.h> /* Forward declarations */ @@ -23,16 +24,16 @@ struct sdis_device; struct sdis_estimator; enum sdis_estimator_type; -enum flux_names { - FLUX_CONVECTIVE__, - FLUX_RADIATIVE__, - FLUX_TOTAL__, +enum flux_name { + FLUX_CONVECTIVE, + FLUX_RADIATIVE, + FLUX_TOTAL, FLUX_NAMES_COUNT__ }; struct sdis_estimator { struct sdis_mc temperature; - struct sdis_mc* fluxes; + struct sdis_mc fluxes[FLUX_NAMES_COUNT__]; size_t nrealisations; size_t nfailures; @@ -42,13 +43,46 @@ struct sdis_estimator { }; /******************************************************************************* - * Estmator data structure + * Estimator local API ******************************************************************************/ extern LOCAL_SYM res_T estimator_create (struct sdis_device* dev, const enum sdis_estimator_type type, + const size_t nrealisations, + const size_t nsuccesses, struct sdis_estimator** estimator); +static INLINE void +estimator_setup_temperature + (struct sdis_estimator* estim, + const double sum, + const double sum2) +{ + double N; + ASSERT(estim && estim->nrealisations); + N = (double)estim->nrealisations; + estim->temperature.E = sum/N; + estim->temperature.V = sum2/N - estim->temperature.E*estim->temperature.E; + estim->temperature.V = MMAX(estim->temperature.V, 0); + estim->temperature.SE = sqrt(estim->temperature.V/N); +} + +static INLINE void +estimator_setup_flux + (struct sdis_estimator* estim, + const enum flux_name name, + const double sum, + const double sum2) +{ + double N; + ASSERT(estim && (unsigned)name < FLUX_NAMES_COUNT__ && estim->nrealisations); + N = (double)estim->nrealisations; + estim->fluxes[name].E = sum/N; + estim->fluxes[name].V = sum2/N - estim->fluxes[name].E*estim->fluxes[name].E; + estim->fluxes[name].V = MMAX(estim->fluxes[name].V, 0); + estim->fluxes[name].SE = sqrt(estim->fluxes[name].V/N); +} + #endif /* SDIS_PROBE_ESTIMATOR_C_H */ diff --git a/src/sdis_heat_path.c b/src/sdis_heat_path.c @@ -0,0 +1,41 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 "sdis_heat_path.h" + +/* Generate the radiative paths */ +#define SDIS_SOLVE_DIMENSION 2 +#include "sdis_heat_path_radiative_Xd.h" +#define SDIS_SOLVE_DIMENSION 3 +#include "sdis_heat_path_radiative_Xd.h" + +/* Generate the convective paths */ +#define SDIS_SOLVE_DIMENSION 2 +#include "sdis_heat_path_convective_Xd.h" +#define SDIS_SOLVE_DIMENSION 3 +#include "sdis_heat_path_convective_Xd.h" + +/* Generate the conductive paths */ +#define SDIS_SOLVE_DIMENSION 2 +#include "sdis_heat_path_conductive_Xd.h" +#define SDIS_SOLVE_DIMENSION 3 +#include "sdis_heat_path_conductive_Xd.h" + +/* Generate the boundary paths */ +#define SDIS_SOLVE_DIMENSION 2 +#include "sdis_heat_path_boundary_Xd.h" +#define SDIS_SOLVE_DIMENSION 3 +#include "sdis_heat_path_boundary_Xd.h" + diff --git a/src/sdis_heat_path.h b/src/sdis_heat_path.h @@ -0,0 +1,134 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 SDIS_HEAT_PATH_H +#define SDIS_HEAT_PATH_H + +#include <rsys/rsys.h> + +struct rwalk_2d; +struct rwalk_3d; +struct rwalk_context; +struct sdis_scene; +struct ssp_rng; +struct temperature_2d; +struct temperature_3d; + +/******************************************************************************* + * Trace or pursue a radiative path + ******************************************************************************/ +extern LOCAL_SYM res_T +trace_radiative_path_2d + (struct sdis_scene* scn, + const float ray_dir[3], + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +trace_radiative_path_3d + (struct sdis_scene* scn, + const float ray_dir[3], + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +extern LOCAL_SYM res_T +radiative_path_2d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +radiative_path_3d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +/******************************************************************************* + * Convective path + ******************************************************************************/ +extern LOCAL_SYM res_T +convective_path_2d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +convective_path_3d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +/******************************************************************************* + * Conductive path + ******************************************************************************/ +extern LOCAL_SYM res_T +conductive_path_2d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +conductive_path_3d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +/******************************************************************************* + * Boundary sub-path + ******************************************************************************/ +extern LOCAL_SYM res_T +boundary_path_2d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +boundary_path_3d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +#endif /* SDIS_HEAT_PATH_H */ + diff --git a/src/sdis_heat_path_boundary_Xd.h b/src/sdis_heat_path_boundary_Xd.h @@ -0,0 +1,578 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 "sdis_device_c.h" +#include "sdis_heat_path.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/* Emperical scale factor applied to the challenged reinjection distance. If + * the distance to reinject is less than this adjusted value, the solver + * switches from 2D reinjection scheme to the 1D reinjection scheme in order to + * avoid numerical issues. */ +#define REINJECT_DST_MIN_SCALE 0.125f + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static FINLINE void +XD(sample_reinjection_dir) + (const struct XD(rwalk)* rwalk, struct ssp_rng* rng, float dir[DIM]) +{ +#if DIM == 2 + /* The sampled directions is defined by rotating the normal around the Z axis + * of an angle of PI/4 or -PI/4. Let the rotation matrix defined as + * | cos(a) -sin(a) | + * | sin(a) cos(a) | + * with a = PI/4, dir = sqrt(2)/2 * | 1 -1 | . N + * | 1 1 | + * with a =-PI/4, dir = sqrt(2)/2 * | 1 1 | . N + * |-1 1 | + * Note that since the sampled direction is finally normalized, we can + * discard the sqrt(2)/2 constant. */ + const uint64_t r = ssp_rng_uniform_uint64(rng, 0, 1); + ASSERT(rwalk && dir); + if(r) { + dir[0] = rwalk->hit.normal[0] - rwalk->hit.normal[1]; + dir[1] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; + } else { + dir[0] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; + dir[1] =-rwalk->hit.normal[0] + rwalk->hit.normal[1]; + } + f2_normalize(dir, dir); +#else + /* Sample a random direction around the normal whose cosine is 1/sqrt(3). To + * do so we sample a position onto a cone whose height is 1/sqrt(2) and the + * radius of its base is 1. */ + float frame[9]; + ASSERT(fX(is_normalized)(rwalk->hit.normal)); + + ssp_ran_circle_uniform_float(rng, dir, NULL); + dir[2] = (float)(1.0/sqrt(2)); + + f33_basis(frame, rwalk->hit.normal); + f33_mulf3(dir, frame, dir); + f3_normalize(dir, dir); + ASSERT(eq_epsf(f3_dot(dir, rwalk->hit.normal), (float)(1.0/sqrt(3)), 1.e-4f)); +#endif +} + +/* Check that the interface fragment is consistent with the current state of + * the random walk */ +static INLINE int +XD(check_rwalk_fragment_consistency) + (const struct XD(rwalk)* rwalk, + const struct sdis_interface_fragment* frag) +{ + double N[DIM]; + double uv[2] = {0, 0}; + ASSERT(rwalk && frag); + dX(normalize)(N, dX_set_fX(N, rwalk->hit.normal)); + if( SXD_HIT_NONE(&rwalk->hit) + || !dX(eq_eps)(rwalk->vtx.P, frag->P, 1.e-6) + || !dX(eq_eps)(N, frag->Ng, 1.e-6) + || !( (IS_INF(rwalk->vtx.time) && IS_INF(frag->time)) + || eq_eps(rwalk->vtx.time, frag->time, 1.e-6))) { + return 0; + } +#if (SDIS_SOLVE_DIMENSION == 2) + uv[0] = rwalk->hit.u; +#else + d2_set_f2(uv, rwalk->hit.uv); +#endif + return d2_eq_eps(uv, frag->uv, 1.e-6); +} + +static void +XD(solid_solid_boundary_path) + (const struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + struct sXd(hit) hit0, hit1, hit2, hit3; + struct sXd(hit)* hit; + const struct sdis_interface* interf = NULL; + const struct sdis_medium* solid_front = NULL; + const struct sdis_medium* solid_back = NULL; + const struct sdis_medium* mdm; + double lambda_front, lambda_back; + double delta_front, delta_back; + double delta_boundary_front, delta_boundary_back; + double delta_boundary; + double reinject_dst_front, reinject_dst_back; + double reinject_dst; + double proba; + double tmp; + double r; + double power; + float range0[2], range1[2]; + float dir0[DIM], dir1[DIM], dir2[DIM], dir3[DIM]; + float* dir; + float pos[DIM]; + int dim = DIM; + ASSERT(scn && fp_to_meter > 0 && ctx && frag && rwalk && rng && T); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + (void)frag, (void)ctx; + + /* Retrieve the current boundary media */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + solid_front = interface_get_medium(interf, SDIS_FRONT); + solid_back = interface_get_medium(interf, SDIS_BACK); + ASSERT(solid_front->type == SDIS_SOLID); + ASSERT(solid_back->type == SDIS_SOLID); + + /* Fetch the properties of the media */ + lambda_front = solid_get_thermal_conductivity(solid_front, &rwalk->vtx); + lambda_back = solid_get_thermal_conductivity(solid_back, &rwalk->vtx); + + /* Note that reinjection distance is *FIXED*. It MUST ensure that the orthogonal + * distance from the boundary to the point to challenge is equal to delta. */ + delta_front = solid_get_delta(solid_front, &rwalk->vtx); + delta_back = solid_get_delta(solid_back, &rwalk->vtx); + delta_boundary_front = delta_front*sqrt(DIM); + delta_boundary_back = delta_back *sqrt(DIM); + + /* Sample a reinjection direction and reflect it around the normal. Then + * reflect them on the back side of the interface. */ + XD(sample_reinjection_dir)(rwalk, rng, dir0); + XD(reflect)(dir2, dir0, rwalk->hit.normal); + fX(minus)(dir1, dir0); + fX(minus)(dir3, dir2); + + /* Trace the sampled directions on both sides of the interface to adjust the + * reinjection distance of the random walk . */ + fX_set_dX(pos, rwalk->vtx.P); + f2(range0, 0, (float)delta_boundary_front*RAY_RANGE_MAX_SCALE); + f2(range1, 0, (float)delta_boundary_back *RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range0, &rwalk->hit, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range1, &rwalk->hit, &hit1)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir2, range0, &rwalk->hit, &hit2)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir3, range1, &rwalk->hit, &hit3)); + + /* Adjust the reinjection distance */ + reinject_dst_front = MMIN(MMIN(delta_boundary_front, hit0.distance), hit2.distance); + reinject_dst_back = MMIN(MMIN(delta_boundary_back, hit1.distance), hit3.distance); + + /* Define the reinjection side. Note that the proba should be : + * Lf/Df' / (Lf/Df' + Lb/Db') + * + * with L<f|b> the lambda of the <front|back> side and D<f|b>' the adjusted + * delta of the <front|back> side, i.e. : + * D<f|b>' = reinject_dst_<front|back> / sqrt(DIM) + * + * Anyway, one can avoid to compute the adjusted delta by directly using the + * adjusted reinjection distance since the resulting proba is strictly the + * same; sqrt(DIM) can be simplified. */ + r = ssp_rng_canonical(rng); + proba = (lambda_front/reinject_dst_front) + / (lambda_front/reinject_dst_front + lambda_back/reinject_dst_back); + if(r < proba) { /* Reinject in front */ + dir = dir0; + hit = &hit0; + mdm = solid_front; + reinject_dst = reinject_dst_front; + delta_boundary = delta_boundary_front; + } else { /* Reinject in back */ + dir = dir1; + hit = &hit1; + mdm = solid_back; + reinject_dst = reinject_dst_back; + delta_boundary = delta_boundary_back; + } + + /* Switch in 1D reinjection scheme */ + if(reinject_dst < delta_boundary * REINJECT_DST_MIN_SCALE) { + if(dir == dir0) { + fX(set)(dir, rwalk->hit.normal); + } else { + fX(minus)(dir, rwalk->hit.normal); + } + + f2(range0, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir, range0, &rwalk->hit, hit)); + reinject_dst = MMIN(delta_boundary, hit->distance), + dim = 1; + + /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit + * distance */ + if(!SXD_HIT_NONE(hit)) { + reinject_dst *= 0.5; + *hit = SXD_HIT_NULL; + } + } + + /* Handle the volumic power */ + power = solid_get_volumic_power(mdm, &rwalk->vtx); + if(power != SDIS_VOLUMIC_POWER_NONE) { + const double delta_in_meter = reinject_dst * fp_to_meter; + const double lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); + tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); + T->value += tmp; + } + + /* Reinject */ + XD(move_pos)(rwalk->vtx.P, dir, (float)reinject_dst); + if(eq_epsf(hit->distance, (float)reinject_dst, 1.e-4f)) { + T->func = XD(boundary_path); + rwalk->mdm = NULL; + rwalk->hit = *hit; + rwalk->hit_side = fX(dot)(hit->normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; + } else { + T->func = XD(conductive_path); + rwalk->mdm = mdm; + rwalk->hit = SXD_HIT_NULL; + rwalk->hit_side = SDIS_SIDE_NULL__; + } +} + +static void +XD(solid_fluid_boundary_path) + (const struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + const struct sdis_interface* interf = NULL; + const struct sdis_medium* mdm_front = NULL; + const struct sdis_medium* mdm_back = NULL; + const struct sdis_medium* solid = NULL; + const struct sdis_medium* fluid = NULL; + struct sXd(hit) hit0 = SXD_HIT_NULL; + struct sXd(hit) hit1 = SXD_HIT_NULL; + struct sdis_interface_fragment frag_fluid; + double hc; + double hr; + double epsilon; /* Interface emissivity */ + double lambda; + double fluid_proba; + double radia_proba; + double delta; + double delta_boundary; + double r; + double tmp; + float pos[DIM]; + float dir0[DIM], dir1[DIM]; + float range[2]; + int dim = DIM; + + ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T && ctx); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + + /* Retrieve the solid and the fluid split by the boundary */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + mdm_front = interface_get_medium(interf, SDIS_FRONT); + mdm_back = interface_get_medium(interf, SDIS_BACK); + ASSERT(mdm_front->type != mdm_back->type); + + frag_fluid = *frag; + if(mdm_front->type == SDIS_SOLID) { + solid = mdm_front; + fluid = mdm_back; + frag_fluid.side = SDIS_BACK; + } else { + solid = mdm_back; + fluid = mdm_front; + frag_fluid.side = SDIS_FRONT; + } + + /* Fetch the solid properties */ + lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); + delta = solid_get_delta(solid, &rwalk->vtx); + + /* Note that the reinjection distance is *FIXED*. It MUST ensure that the + * orthogonal distance from the boundary to the point to chalenge is equal to + * delta. */ + delta_boundary = sqrt(DIM) * delta; + + /* Sample a reinjection direction */ + XD(sample_reinjection_dir)(rwalk, rng, dir0); + + /* Reflect the sampled direction around the normal */ + XD(reflect)(dir1, dir0, rwalk->hit.normal); + + if(solid == mdm_back) { + fX(minus)(dir0, dir0); + fX(minus)(dir1, dir1); + } + + /* Trace dir0/dir1 to adjust the reinjection distance */ + fX_set_dX(pos, rwalk->vtx.P); + f2(range, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range, &rwalk->hit, &hit1)); + + /* Adjust the delta boundary to the hit distance */ + tmp = MMIN(MMIN(delta_boundary, hit0.distance), hit1.distance); + + if(tmp >= delta_boundary * REINJECT_DST_MIN_SCALE) { + delta_boundary = tmp; + /* Define the orthogonal dst from the reinjection pos to the interface */ + delta = delta_boundary / sqrt(DIM); + } else { /* Switch in 1D reinjection scheme. */ + fX(set)(dir0, rwalk->hit.normal); + if(solid == mdm_back) fX(minus)(dir0, dir0); + f2(range, 0, (float)delta*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); + delta_boundary = MMIN(hit0.distance, delta); + + /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit + * distance in order to avoid infinite bounces for parallel plane */ + if(!SXD_HIT_NONE(&hit0)) { + delta_boundary *= 0.5; + hit0 = SXD_HIT_NULL; + } + + delta = delta_boundary; + dim = 1; + } + + /* Fetch the boundary properties */ + epsilon = interface_side_get_emissivity(interf, &frag_fluid); + hc = interface_get_convection_coef(interf, frag); + + /* Compute the radiative coefficient */ + hr = 4.0 * BOLTZMANN_CONSTANT * ctx->Tref3 * epsilon; + + /* Compute the probas to switch in solid, fluid or radiative random walk */ + tmp = lambda / (delta*fp_to_meter); + fluid_proba = hc / (tmp + hr + hc); + radia_proba = hr / (tmp + hr + hc); + /*solid_proba = tmp / (tmp + hr + hc);*/ + + r = ssp_rng_canonical(rng); + if(r < radia_proba) { /* Switch in radiative random walk */ + T->func = XD(radiative_path); + rwalk->mdm = fluid; + rwalk->hit_side = rwalk->mdm == mdm_front ? SDIS_FRONT : SDIS_BACK; + } else if(r < fluid_proba + radia_proba) { /* Switch to convective random walk */ + T->func = XD(convective_path); + rwalk->mdm = fluid; + rwalk->hit_side = rwalk->mdm == mdm_front ? SDIS_FRONT : SDIS_BACK; + } else { /* Solid random walk */ + /* Handle the volumic power */ + const double power = solid_get_volumic_power(solid, &rwalk->vtx); + if(power != SDIS_VOLUMIC_POWER_NONE) { + const double delta_in_meter = delta_boundary * fp_to_meter; + tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); + T->value += tmp; + } + + /* Reinject */ + XD(move_pos)(rwalk->vtx.P, dir0, (float)delta_boundary); + if(eq_epsf(hit0.distance, (float)delta_boundary, 1.e-4f)) { + T->func = XD(boundary_path); + rwalk->mdm = NULL; + rwalk->hit = hit0; + rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; + } else { + T->func = XD(conductive_path); + rwalk->mdm = solid; + rwalk->hit = SXD_HIT_NULL; + rwalk->hit_side = SDIS_SIDE_NULL__; + } + } +} + +static void +XD(solid_boundary_with_flux_path) + (const struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + const double phi, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + const struct sdis_interface* interf = NULL; + const struct sdis_medium* mdm = NULL; + double lambda; + double delta; + double delta_boundary; + double delta_in_meter; + double power; + double tmp; + struct sXd(hit) hit0; + struct sXd(hit) hit1; + float pos[DIM]; + float dir0[DIM]; + float dir1[DIM]; + float range[2]; + int dim = DIM; + ASSERT(frag && phi != SDIS_FLUX_NONE); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + (void)ctx; + + /* Fetch current interface */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + ASSERT(phi == interface_side_get_flux(interf, frag)); + + /* Fetch incoming solid */ + mdm = interface_get_medium(interf, frag->side); + ASSERT(mdm->type == SDIS_SOLID); + + /* Fetch medium properties */ + lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); + delta = solid_get_delta(mdm, &rwalk->vtx); + + /* Compute the reinjection distance. It MUST ensure that the orthogonal + * distance from the boundary to the point to chalenge is equal to delta. */ + delta_boundary = delta * sqrt(DIM); + + /* Sample a reinjection direction */ + XD(sample_reinjection_dir)(rwalk, rng, dir0); + + /* Reflect the sampled direction around the normal */ + XD(reflect)(dir1, dir0, rwalk->hit.normal); + + if(frag->side == SDIS_BACK) { + fX(minus)(dir0, dir0); + fX(minus)(dir1, dir1); + } + + /* Trace dir0/dir1 to adjust the reinjection distance wrt the geometry */ + fX_set_dX(pos, rwalk->vtx.P); + f2(range, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range, &rwalk->hit, &hit1)); + + /* Adjust the delta boundary to the hit distance */ + tmp = MMIN(MMIN(delta_boundary, hit0.distance), hit1.distance); + + if(tmp >= delta_boundary * REINJECT_DST_MIN_SCALE) { + delta_boundary = tmp; + /* Define the orthogonal dst from the reinjection pos to the interface */ + delta = delta_boundary / sqrt(DIM); + } else { /* Switch in 1D reinjection scheme. */ + fX(set)(dir0, rwalk->hit.normal); + if(frag->side == SDIS_BACK) fX(minus)(dir0, dir0); + f2(range, 0, (float)delta*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); + delta_boundary = MMIN(hit0.distance, delta_boundary); + + /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit + * distance in order to avoid infinite bounces for parallel plane */ + if(!SXD_HIT_NONE(&hit0)) { + delta_boundary *= 0.5; + hit0 = SXD_HIT_NULL; + } + + delta = delta_boundary; + dim = 1; + } + + /* Handle the flux */ + delta_in_meter = delta*fp_to_meter; + T->value += phi * delta_in_meter / lambda; + + /* Handle the volumic power */ + power = solid_get_volumic_power(mdm, &rwalk->vtx); + if(power != SDIS_VOLUMIC_POWER_NONE) { + delta_in_meter = delta_boundary * fp_to_meter; + tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); + T->value += tmp; + } + + /* Reinject into the solid */ + XD(move_pos)(rwalk->vtx.P, dir0, (float)delta_boundary); + if(eq_epsf(hit0.distance, (float)delta_boundary, 1.e-4f)) { + T->func = XD(boundary_path); + rwalk->mdm = NULL; + rwalk->hit = hit0; + rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; + } else { + T->func = XD(conductive_path); + rwalk->mdm = mdm; + rwalk->hit = SXD_HIT_NULL; + rwalk->hit_side = SDIS_SIDE_NULL__; + } +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +XD(boundary_path) + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + const struct sdis_interface* interf = NULL; + const struct sdis_medium* mdm_front = NULL; + const struct sdis_medium* mdm_back = NULL; + const struct sdis_medium* mdm = NULL; + double tmp; + ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); + ASSERT(rwalk->mdm == NULL); + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + + XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + + fX(normalize)(rwalk->hit.normal, rwalk->hit.normal); + + /* Retrieve the current interface */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + + /* Check if the boundary temperature is known */ + tmp = interface_side_get_temperature(interf, &frag); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + return RES_OK; + } + + /* Check if the boundary flux is known. Note that currently, only solid media + * can have a flux as limit condition */ + mdm = interface_get_medium(interf, frag.side); + if(sdis_medium_get_type(mdm) == SDIS_SOLID ) { + const double phi = interface_side_get_flux(interf, &frag); + if(phi != SDIS_FLUX_NONE) { + XD(solid_boundary_with_flux_path) + (scn, fp_to_meter, ctx, &frag, phi, rwalk, rng, T); + return RES_OK; + } + } + + mdm_front = interface_get_medium(interf, SDIS_FRONT); + mdm_back = interface_get_medium(interf, SDIS_BACK); + + if(mdm_front->type == mdm_back->type) { + XD(solid_solid_boundary_path) + (scn, fp_to_meter, ctx, &frag, rwalk, rng, T); + } else { + XD(solid_fluid_boundary_path) + (scn, fp_to_meter, ctx, &frag, rwalk, rng, T); + } + return RES_OK; +} + +#include "sdis_Xd_end.h" + diff --git a/src/sdis_heat_path_conductive_Xd.h b/src/sdis_heat_path_conductive_Xd.h @@ -0,0 +1,234 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 "sdis_device_c.h" +#include "sdis_heat_path.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +res_T +XD(conductive_path) + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + double position_start[DIM]; + const struct sdis_medium* mdm; + ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); + ASSERT(rwalk->mdm->type == SDIS_SOLID); + (void)ctx; + + /* Check the random walk consistency */ + CHK(scene_get_medium(scn, rwalk->vtx.P, NULL, &mdm) == RES_OK); + if(mdm != rwalk->mdm) { + log_err(scn->dev, "%s: invalid solid random walk. " + "Unexpected medium at {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); + return RES_BAD_OP_IRRECOVERABLE; + } + /* Save the submitted position */ + dX(set)(position_start, rwalk->vtx.P); + + do { /* Solid random walk */ + struct get_medium_info info = GET_MEDIUM_INFO_NULL; + struct sXd(hit) hit0, hit1; + double lambda; /* Thermal conductivity */ + double rho; /* Volumic mass */ + double cp; /* Calorific capacity */ + double tmp; + double power; + float delta, delta_solid; /* Random walk numerical parameter */ + float range[2]; + float dir0[DIM], dir1[DIM]; + float org[DIM]; + + /* Check the limit condition */ + tmp = solid_get_temperature(mdm, &rwalk->vtx); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + return RES_OK; + } + + /* Fetch solid properties */ + delta_solid = (float)solid_get_delta(mdm, &rwalk->vtx); + lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); + rho = solid_get_volumic_mass(mdm, &rwalk->vtx); + cp = solid_get_calorific_capacity(mdm, &rwalk->vtx); + power = solid_get_volumic_power(mdm, &rwalk->vtx); + +#if DIM == 2 + /* Sample a direction around 2PI */ + ssp_ran_circle_uniform_float(rng, dir0, NULL); +#else + /* Sample a direction around 4PI */ + ssp_ran_sphere_uniform_float(rng, dir0, NULL); +#endif + + /* Trace a ray along the sampled direction and its opposite to check if a + * surface is hit in [0, delta_solid]. */ + fX_set_dX(org, rwalk->vtx.P); + fX(minus)(dir1, dir0); + hit0 = hit1 = SXD_HIT_NULL; + range[0] = 0.f, range[1] = delta_solid*RAY_RANGE_MAX_SCALE; + SXD(scene_view_trace_ray(scn->sXd(view), org, dir0, range, NULL, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), org, dir1, range, NULL, &hit1)); + + if(SXD_HIT_NONE(&hit0) && SXD_HIT_NONE(&hit1)) { + /* Hit nothing: move along dir0 of the original delta */ + delta = delta_solid; + + /* Add the volumic power density to the measured temperature */ + if(power != SDIS_VOLUMIC_POWER_NONE) { + const double delta_in_meter = delta * fp_to_meter; + tmp = power * delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); + T->value += tmp; + } + } else { + /* Hit something: move along dir0 of the minimum hit distance */ + delta = MMIN(hit0.distance, hit1.distance); + + /* Add the volumic power density to the measured temperature */ + if(power != SDIS_VOLUMIC_POWER_NONE) { + const double delta_s_in_meter = delta_solid * fp_to_meter; + double h; + double h_in_meter; + double cos_U_N; + float N[DIM]; + + if(delta == hit0.distance) { + fX(normalize)(N, hit0.normal); + cos_U_N = fX(dot)(dir0, N); + } else { + ASSERT(delta == hit1.distance); + fX(normalize)(N, hit1.normal); + cos_U_N = fX(dot)(dir1, N); + } + + h = delta * fabs(cos_U_N); + h_in_meter = h * fp_to_meter; + + /* The regular power term at wall */ + tmp = power * h_in_meter * h_in_meter / (2.0 * lambda); + + /* Add the power corrective term */ + if(h < delta_solid) { + const double sin_a = h / delta_solid; +#if DIM==2 + /* tmp1 = sin(2a) / (PI - 2*a) */ + const double tmp1 = sin_a * sqrt(1 - sin_a*sin_a)/acos(sin_a); + tmp += -(power*delta_s_in_meter*delta_s_in_meter)/(4.0*lambda) * tmp1; +#else + const double tmp1 = (sin_a*sin_a*sin_a - sin_a)/ (1-sin_a); + tmp += (power*delta_s_in_meter*delta_s_in_meter)/(6*lambda) * tmp1; +#endif + + } else if(h == delta_solid) { + tmp += -(delta_s_in_meter*delta_s_in_meter*power)/(2.0*DIM*lambda); + } + T->value += tmp; + } + } + + /* Sample the time */ + if(rwalk->vtx.time != INF) { + double tau, mu, t0; + mu = (2*DIM*lambda) / (rho*cp*delta*fp_to_meter*delta*fp_to_meter); + tau = ssp_ran_exp(rng, mu); + t0 = solid_get_t0(rwalk->mdm); + rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); + if(rwalk->vtx.time == t0) { + /* Check the initial condition */ + tmp = solid_get_temperature(mdm, &rwalk->vtx); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + return RES_OK; + } + /* The initial condition should have been reached */ + log_err(scn->dev, + "%s: undefined initial condition. " + "The time is %f but the temperature remains unknown.\n", + FUNC_NAME, t0); + return RES_BAD_OP; + } + } + + /* Define if the random walk hits something along dir0 */ + if(hit0.distance > delta) { + rwalk->hit = SXD_HIT_NULL; + rwalk->hit_side = SDIS_SIDE_NULL__; + } else { + rwalk->hit = hit0; + rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; + } + + /* Update the random walk position */ + XD(move_pos)(rwalk->vtx.P, dir0, delta); + + /* Fetch the current medium */ + if(SXD_HIT_NONE(&rwalk->hit)) { + CHK(scene_get_medium(scn, rwalk->vtx.P, &info, &mdm) == RES_OK); + } else { + const struct sdis_interface* interf; + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + mdm = interface_get_medium(interf, rwalk->hit_side); + } + + /* Check random walk consistency */ + if(mdm != rwalk->mdm) { + log_err(scn->dev, + "%s: inconsistent medium during the solid random walk.\n", FUNC_NAME); +#if DIM == 2 + #define VEC_STR "%g %g" + #define VEC_SPLIT SPLIT2 +#else + #define VEC_STR "%g %g %g" + #define VEC_SPLIT SPLIT3 +#endif + log_err(scn->dev, + " start position: " VEC_STR "; current position: " VEC_STR "\n", + VEC_SPLIT(position_start), VEC_SPLIT(rwalk->vtx.P)); + if(SXD_HIT_NONE(&rwalk->hit)) { + float hit_pos[DIM]; + fX(mulf)(hit_pos, info.ray_dir, info.XD(hit).distance); + fX(add)(hit_pos, info.ray_org, hit_pos); + log_err(scn->dev, " ray org: " VEC_STR "; ray dir: " VEC_STR "\n", + VEC_SPLIT(info.ray_org), VEC_SPLIT(info.ray_dir)); + log_err(scn->dev, " targeted point: " VEC_STR "\n", + VEC_SPLIT(info.pos_tgt)); + log_err(scn->dev, " hit pos: " VEC_STR "\n", VEC_SPLIT(hit_pos)); + } +#undef VEC_STR +#undef VEC_SPLIT + return RES_BAD_OP; + } + + /* Keep going while the solid random walk does not hit an interface */ + } while(SXD_HIT_NONE(&rwalk->hit)); + + T->func = XD(boundary_path); + rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ + return RES_OK; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_convective_Xd.h b/src/sdis_heat_path_convective_Xd.h @@ -0,0 +1,228 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 "sdis_device_c.h" +#include "sdis_heat_path.h" +#include "sdis_medium_c.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +res_T +XD(convective_path) + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + struct sXd(attrib) attr_P, attr_N; + struct sdis_interface_fragment frag; + const struct sdis_interface* interf; + const struct enclosure* enc; + unsigned enc_ids[2]; + unsigned enc_id; + double rho; /* Volumic mass */ + double hc; /* Convection coef */ + double cp; /* Calorific capacity */ + double tmp; + double r; +#if SDIS_SOLVE_DIMENSION == 2 + float st; +#else + float st[2]; +#endif + (void)rng, (void)fp_to_meter, (void)ctx; + ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); + ASSERT(rwalk->mdm->type == SDIS_FLUID); + + tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); + if(tmp >= 0) { /* T is known. */ + T->value += tmp; + T->done = 1; + return RES_OK; + } + + if(SXD_HIT_NONE(&rwalk->hit)) { /* The path begins in the fluid */ + const float range[2] = {0, FLT_MAX}; + float dir[DIM] = {0}; + float org[DIM]; + + dir[DIM-1] = 1; + fX_set_dX(org, rwalk->vtx.P); + + /* Init the path hit field required to define the current enclosure and + * fetch the interface data */ + SXD(scene_view_trace_ray(scn->sXd(view), org, dir, range, NULL, &rwalk->hit)); + rwalk->hit_side = fX(dot)(rwalk->hit.normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; + + if(SXD_HIT_NONE(&rwalk->hit)) { + log_err(scn->dev, +"%s: the position %g %g %g lies in the surrounding fluid whose temperature must \n" +"be known.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); + return RES_BAD_OP; + } + } + + /* Fetch the current interface and its associated enclosures */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + scene_get_enclosure_ids(scn, rwalk->hit.prim.prim_id, enc_ids); + + /* Define the enclosure identifier of the current medium */ + ASSERT(interf->medium_front != interf->medium_back); + if(rwalk->mdm == interf->medium_front) { + enc_id = enc_ids[0]; + ASSERT(rwalk->hit_side == SDIS_FRONT); + } else { + ASSERT(rwalk->mdm == interf->medium_back); + enc_id = enc_ids[1]; + ASSERT(rwalk->hit_side == SDIS_BACK); + } + + /* Fetch the enclosure data */ + enc = scene_get_enclosure(scn, enc_id); + if(!enc) { + /* The possibility for a fluid enclosure to be unregistred is that it is + * the external enclosure. In this situation unknown temperature is + * forbidden. */ + log_err(scn->dev, +"%s: invalid enclosure. The surrounding fluid has an unset temperature.\n", + FUNC_NAME); + return RES_BAD_ARG; + } + + /* The hc upper bound can be 0 if h is uniformly 0. In that case the result + * is the initial condition. */ + if(enc->hc_upper_bound == 0) { + /* Cannot be in the fluid without starting there. */ + ASSERT(SXD_HIT_NONE(&rwalk->hit)); + rwalk->vtx.time = fluid_get_t0(rwalk->mdm); + tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + return RES_OK; + } + + /* At t=0, the initial condition should have been reached. */ + log_err(scn->dev, + "%s: undefined initial condition. " + "Time is 0 but the temperature remains unknown.\n", + FUNC_NAME); + return RES_BAD_OP; + } + + /* A trick to force first r test result. */ + r = 1; + + /* Sample time until init condition is reached or a true convection occurs. */ + for(;;) { + struct sXd(primitive) prim; + /* Setup the fragment of the interface. */ + XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + + /* Fetch hc. */ + hc = interface_get_convection_coef(interf, &frag); + if(hc > enc->hc_upper_bound) { + log_err(scn->dev, + "%s: hc (%g) exceeds its provided upper bound (%g) at %g %g %g.\n", + FUNC_NAME, hc, enc->hc_upper_bound, SPLIT3(rwalk->vtx.P)); + return RES_BAD_OP; + } + + if(r < hc / enc->hc_upper_bound) { + /* True convection. Always true if hc == bound. */ + break; + } + + /* Fetch other physical properties. */ + cp = fluid_get_calorific_capacity(rwalk->mdm, &rwalk->vtx); + rho = fluid_get_volumic_mass(rwalk->mdm, &rwalk->vtx); + + /* Sample the time using the upper bound. */ + if(rwalk->vtx.time != INF) { + double mu, tau, t0; + mu = enc->hc_upper_bound / (rho * cp) * enc->S_over_V; + tau = ssp_ran_exp(rng, mu); + t0 = fluid_get_t0(rwalk->mdm); + rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); + if(rwalk->vtx.time == t0) { + /* Check the initial condition. */ + tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + return RES_OK; + } + /* The initial condition should have been reached. */ + log_err(scn->dev, + "%s: undefined initial condition. " + "Time is %g but the temperature remains unknown.\n", + FUNC_NAME, t0); + return RES_BAD_OP; + } + } + + /* Uniformly sample the enclosure. */ +#if DIM == 2 + SXD(scene_view_sample + (enc->sXd(view), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, &rwalk->hit.u)); + st = rwalk->hit.u; +#else + SXD(scene_view_sample + (enc->sXd(view), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, rwalk->hit.uv)); + f2_set(st, rwalk->hit.uv); +#endif + /* Map the sampled primitive id from the enclosure space to the scene + * space. Note that the overall scene has only one shape. As a consequence + * neither the geom_id nor the inst_id needs to be updated */ + rwalk->hit.prim.prim_id = enclosure_local2global_prim_id(enc, prim.prim_id); + + SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_POSITION, st, &attr_P)); + SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_GEOMETRY_NORMAL, st, &attr_N)); + dX_set_fX(rwalk->vtx.P, attr_P.value); + fX(set)(rwalk->hit.normal, attr_N.value); + + /* Fetch the interface of the sampled point. */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + if(rwalk->mdm == interf->medium_front) { + rwalk->hit_side = SDIS_FRONT; + } else if(rwalk->mdm == interf->medium_back) { + rwalk->hit_side = SDIS_BACK; + } else { + FATAL("Unexpected fluid interface.\n"); + } + + /* Renew r for next loop. */ + r = ssp_rng_canonical_float(rng); + } + + rwalk->hit.distance = 0; + T->func = XD(boundary_path); + rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ + return RES_OK; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_radiative_Xd.h b/src/sdis_heat_path_radiative_Xd.h @@ -0,0 +1,202 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 "sdis_device_c.h" +#include "sdis_heat_path.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +XD(trace_radiative_path) + (struct sdis_scene* scn, + const float ray_dir[3], + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + /* The radiative random walk is always performed in 3D. In 2D, the geometry + * are assumed to be extruded to the infinty along the Z dimension. */ + float N[3] = {0, 0, 0}; + float dir[3] = {0, 0, 0}; + res_T res = RES_OK; + + ASSERT(scn && ray_dir && fp_to_meter > 0 && ctx && rwalk && rng && T); + (void)fp_to_meter; + + f3_set(dir, ray_dir); + + /* Launch the radiative random walk */ + for(;;) { + const struct sdis_interface* interf = NULL; + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + const struct sdis_medium* chk_mdm = NULL; + double alpha; + double epsilon; + double r; + float pos[DIM]; + const float range[2] = { 0, FLT_MAX }; + + fX_set_dX(pos, rwalk->vtx.P); + + /* Trace the radiative ray */ +#if (SDIS_SOLVE_DIMENSION == 2) + SXD(scene_view_trace_ray_3d + (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); +#else + SXD(scene_view_trace_ray + (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); +#endif + if(SXD_HIT_NONE(&rwalk->hit)) { /* Fetch the ambient radiative temperature */ + rwalk->hit_side = SDIS_SIDE_NULL__; + if(ctx->Tarad >= 0) { + T->value += ctx->Tarad; + T->done = 1; + break; + } else { + log_err(scn->dev, + "%s: the random walk reaches an invalid ambient radiative temperature " + "of `%gK' at position `%g %g %g'. This may be due to numerical " + "inaccuracies or to inconsistency in the simulated system (eg: " + "unclosed geometry). For systems where the random walks can reach " + "such temperature, one has to setup a valid ambient radiative " + "temperature, i.e. it must be greater or equal to 0.\n", + FUNC_NAME, + ctx->Tarad, + SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; + } + } + + /* Define the hit side */ + rwalk->hit_side = fX(dot)(dir, rwalk->hit.normal) < 0 + ? SDIS_FRONT : SDIS_BACK; + + /* Move the random walk to the hit position */ + XD(move_pos)(rwalk->vtx.P, dir, rwalk->hit.distance); + + /* Fetch the new interface and setup the hit fragment */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + + /* Fetch the interface emissivity */ + epsilon = interface_side_get_emissivity(interf, &frag); + if(epsilon > 1 || epsilon < 0) { + log_err(scn->dev, + "%s: invalid overall emissivity `%g' at position `%g %g %g'.\n", + FUNC_NAME, epsilon, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; + } + + /* Switch in boundary temperature ? */ + r = ssp_rng_canonical(rng); + if(r < epsilon) { + T->func = XD(boundary_path); + rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ + break; + } + + /* Normalize the normal of the interface and ensure that it points toward the + * current medium */ + fX(normalize)(N, rwalk->hit.normal); + if(rwalk->hit_side == SDIS_BACK){ + chk_mdm = interf->medium_back; + fX(minus)(N, N); + } else { + chk_mdm = interf->medium_front; + } + + if(chk_mdm != rwalk->mdm) { + /* To ease the setting of models, the external enclosure is allowed to be + * incoherent regarding media. Here a radiative path is allowed to join + * 2 different fluids. */ + const int outside = scene_is_outside + (scn, rwalk->hit_side, rwalk->hit.prim.prim_id); + if(outside && chk_mdm->type == SDIS_FLUID) { + rwalk->mdm = chk_mdm; + } else { + log_err(scn->dev, "%s: inconsistent medium definition at `%g %g %g'.\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; + } + } + alpha = interface_side_get_specular_fraction(interf, &frag); + r = ssp_rng_canonical(rng); + if(r < alpha) { /* Sample specular part */ + reflect_3d(dir, f3_minus(dir, dir), N); + } else { /* Sample diffuse part */ + ssp_ran_hemisphere_cos_float(rng, N, dir, NULL); + } + } + +exit: + return res; +error: + goto exit; +} + +res_T +XD(radiative_path) + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + /* The radiative random walk is always performed in 3D. In 2D, the geometry + * are assumed to be extruded to the infinty along the Z dimension. */ + float N[3] = {0, 0, 0}; + float dir[3] = {0, 0, 0}; + res_T res = RES_OK; + + ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + (void)fp_to_meter; + + /* Normalize the normal of the interface and ensure that it points toward the + * current medium */ + fX(normalize(N, rwalk->hit.normal)); + if(rwalk->hit_side == SDIS_BACK) { + fX(minus(N, N)); + } + + /* Cosine weighted sampling of a direction around the surface normal */ + ssp_ran_hemisphere_cos_float(rng, N, dir, NULL); + + /* Launch the radiative random walk */ + res = XD(trace_radiative_path)(scn, dir, fp_to_meter, ctx, rwalk, rng, T); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_misc.h b/src/sdis_misc.h @@ -0,0 +1,80 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 SDIS_MISC_H +#define SDIS_MISC_H + +#include <rsys/float2.h> +#include <rsys/float3.h> + +/* Empirical scale factor to apply to the upper bound of the ray range in order + * to handle numerical imprecisions */ +#define RAY_RANGE_MAX_SCALE 1.001f + +/* Define a new result code from RES_BAD_OP saying that the bad operation is + * definitive, i.e. in the current state, the realisation will inevitably fail. + * It is thus unecessary to retry a specific section of the random walk */ +#define RES_BAD_OP_IRRECOVERABLE (-RES_BAD_OP) + +#define BOLTZMANN_CONSTANT 5.6696e-8 /* W/m^2/K^4 */ + +/* Reflect the V wrt the normal N. By convention V points outward the surface */ +static FINLINE float* +reflect_2d(float res[2], const float V[2], const float N[2]) +{ + float tmp[2]; + float cos_V_N; + ASSERT(res && V && N); + ASSERT(f2_is_normalized(V) && f2_is_normalized(N)); + cos_V_N = f2_dot(V, N); + f2_mulf(tmp, N, 2*cos_V_N); + f2_sub(res, tmp, V); + return res; +} + +/* Reflect the V wrt the normal N. By convention V points outward the surface */ +static FINLINE float* +reflect_3d(float res[3], const float V[3], const float N[3]) +{ + float tmp[3]; + float cos_V_N; + ASSERT(res && V && N); + ASSERT(f3_is_normalized(V) && f3_is_normalized(N)); + cos_V_N = f3_dot(V, N); + f3_mulf(tmp, N, 2*cos_V_N); + f3_sub(res, tmp, V); + return res; +} + +static FINLINE double* +move_pos_2d(double pos[2], const float dir[2], const float delta) +{ + ASSERT(pos && dir); + pos[0] += dir[0] * delta; + pos[1] += dir[1] * delta; + return pos; +} + +static FINLINE double* +move_pos_3d(double pos[3], const float dir[3], const float delta) +{ + ASSERT(pos && dir); + pos[0] += dir[0] * delta; + pos[1] += dir[1] * delta; + pos[2] += dir[2] * delta; + return pos; +} + +#endif /* SDIS_MISC_H */ diff --git a/src/sdis_realisation.c b/src/sdis_realisation.c @@ -0,0 +1,71 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 "sdis_realisation.h" + +/* Generate the generic realisations */ +#define SDIS_SOLVE_DIMENSION 2 +#include "sdis_realisation_Xd.h" +#define SDIS_SOLVE_DIMENSION 3 +#include "sdis_realisation_Xd.h" + +res_T +ray_realisation_3d + (struct sdis_scene* scn, + struct ssp_rng* rng, + const struct sdis_medium* medium, + const double position[], + const double direction[], + const double time, + const double fp_to_meter, + const double Tarad, + const double Tref, + double* weight) +{ + struct rwalk_context ctx; + struct rwalk_3d rwalk = RWALK_NULL_3d; + struct temperature_3d T = TEMPERATURE_NULL_3d; + float dir[3]; + res_T res = RES_OK; + ASSERT(scn && position && direction && time>=0 && fp_to_meter>0 && weight); + ASSERT(Tref >= 0 && medium && medium->type == SDIS_FLUID); + + d3_set(rwalk.vtx.P, position); + rwalk.vtx.time = time; + rwalk.hit = S3D_HIT_NULL; + rwalk.hit_side = SDIS_SIDE_NULL__; + rwalk.mdm = medium; + + ctx.Tarad = Tarad; + ctx.Tref3 = Tref*Tref*Tref; + + f3_set_d3(dir, direction); + + res = trace_radiative_path_3d(scn, dir, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) goto error; + + if(!T.done) { + res = compute_temperature_3d(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) goto error; + } + + *weight = T.value; + +exit: + return res; +error: + goto exit; +} + diff --git a/src/sdis_realisation.h b/src/sdis_realisation.h @@ -0,0 +1,130 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 SDIS_REALISATION_H +#define SDIS_REALISATION_H + +#include "sdis.h" +#include "sdis_estimator_c.h" + +#include <rsys/rsys.h> + +struct sdis_scene; +struct ssp_rng; + +/******************************************************************************* + * Realisation at a given position and time IN a medium + ******************************************************************************/ +extern LOCAL_SYM res_T +probe_realisation_2d + (struct sdis_scene* scn, + struct ssp_rng* rng, + const struct sdis_medium* medium, + const double position[2], + const double time, + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double ambient_radiative_temperature, + const double reference_temperature, + double* weight); + +extern LOCAL_SYM res_T +probe_realisation_3d + (struct sdis_scene* scn, + struct ssp_rng* rng, + const struct sdis_medium* medium, + const double position[3], + const double time, + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double ambient_radiative_temperature, + const double reference_temperature, + double* weight); + +/******************************************************************************* + * Realisation at a given position and time ON a given side of a boundary + ******************************************************************************/ +extern LOCAL_SYM res_T +boundary_realisation_2d + (struct sdis_scene* scn, + struct ssp_rng* rng, + const size_t iprim, + const double u[1], + const double time, + const enum sdis_side side, + const double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + double* weight); + +extern LOCAL_SYM res_T +boundary_realisation_3d + (struct sdis_scene* scn, + struct ssp_rng* rng, + const size_t iprim, + const double uv[2], + const double time, + const enum sdis_side side, + const double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + double* weight); + +extern LOCAL_SYM res_T +boundary_flux_realisation_2d + (struct sdis_scene* scn, + struct ssp_rng* rng, + const size_t iprim, + const double uv[1], + const double time, + const enum sdis_side solid_side, + const double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + const char compute_radiative, + const char compute_convective, + double weight[FLUX_NAMES_COUNT__]); + +extern LOCAL_SYM res_T +boundary_flux_realisation_3d + (struct sdis_scene* scn, + struct ssp_rng* rng, + const size_t iprim, + const double uv[2], + const double time, + const enum sdis_side solid_side, + const double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + const char compute_radiative, + const char compute_convective, + double weight[FLUX_NAMES_COUNT__]); + +/******************************************************************************* + * Realisation along a given ray at a given time. Available only in 3D. + ******************************************************************************/ +extern LOCAL_SYM res_T +ray_realisation_3d + (struct sdis_scene* scn, + struct ssp_rng* rng, + const struct sdis_medium* medium, + const double position[3], + const double direction[3], + const double time, + const double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + double* weight); + +#endif /* SDIS_REALISATION_H */ + diff --git a/src/sdis_realisation_Xd.h b/src/sdis_realisation_Xd.h @@ -0,0 +1,327 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 "sdis_device_c.h" +#include "sdis_heat_path.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <rsys/stretchy_array.h> +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +XD(compute_temperature) + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ +#ifndef NDEBUG + /* Stack that saves the state of each recursion steps. */ + struct entry { + struct XD(temperature) temperature; + struct XD(rwalk) rwalk; + }* stack = NULL; + size_t istack = 0; +#endif + /* Maximum accepted #failures before stopping the realisation */ + const size_t MAX_FAILS = 10; + res_T res = RES_OK; + ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); + + do { + /* Save the current random walk state */ + const struct XD(rwalk) rwalk_bkp = *rwalk; + const struct XD(temperature) T_bkp = *T; + size_t nfails = 0; /* #failures */ + +#ifndef NDEBUG + struct entry e; + e.temperature = *T; + e.rwalk = *rwalk; + sa_push(stack, e); + ++istack; +#endif + + /* Reject the step if a BAD_OP occurs and retry up to MAX_FAILS times */ + do { + res = T->func(scn, fp_to_meter, ctx, rwalk, rng, T); + if(res == RES_BAD_OP) { *rwalk = rwalk_bkp; *T = T_bkp; } + } while(res == RES_BAD_OP && ++nfails < MAX_FAILS); + if(res != RES_OK) goto error; + + } while(!T->done); + +exit: +#ifndef NDEBUG + sa_release(stack); +#endif + return res == RES_BAD_OP_IRRECOVERABLE ? RES_BAD_OP : res; +error: + goto exit; +} + + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +XD(probe_realisation) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const struct sdis_medium* medium, + const double position[], + const double time, + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double ambient_radiative_temperature, + const double reference_temperature, + double* weight) +{ + struct rwalk_context ctx; + struct XD(rwalk) rwalk = XD(RWALK_NULL); + struct XD(temperature) T = XD(TEMPERATURE_NULL); + double t0; + double (*get_initial_temperature) + (const struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vtx); + res_T res = RES_OK; + ASSERT(medium && position && fp_to_meter > 0 && weight && time >= 0); + + switch(medium->type) { + case SDIS_FLUID: + T.func = XD(convective_path); + get_initial_temperature = fluid_get_temperature; + t0 = fluid_get_t0(medium); + break; + case SDIS_SOLID: + T.func = XD(conductive_path); + get_initial_temperature = solid_get_temperature; + t0 = solid_get_t0(medium); + break; + default: FATAL("Unreachable code\n"); break; + } + + dX(set)(rwalk.vtx.P, position); + rwalk.vtx.time = time; + if(t0 >= rwalk.vtx.time) { + double tmp; + /* Check the initial condition. */ + rwalk.vtx.time = t0; + tmp = get_initial_temperature(medium, &rwalk.vtx); + if(tmp >= 0) { + *weight = tmp; + return RES_OK; + } + /* The initial condition should have been reached */ + log_err(scn->dev, + "%s: undefined initial condition. " + "The time is %f but the temperature remains unknown.\n", + FUNC_NAME, t0); + return RES_BAD_OP; + } + + rwalk.hit = SXD_HIT_NULL; + rwalk.mdm = medium; + + ctx.Tarad = ambient_radiative_temperature; + ctx.Tref3 = + reference_temperature + * reference_temperature + * reference_temperature; + + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) return res; + + *weight = T.value; + return RES_OK; +} + +res_T +XD(boundary_realisation) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const size_t iprim, + const double uv[2], + const double time, + const enum sdis_side side, + const double fp_to_meter, + const double Tarad, + const double Tref, + double* weight) +{ + struct rwalk_context ctx; + struct XD(rwalk) rwalk = XD(RWALK_NULL); + struct XD(temperature) T = XD(TEMPERATURE_NULL); + struct sXd(attrib) attr; +#if SDIS_SOLVE_DIMENSION == 2 + float st; +#else + float st[2]; +#endif + res_T res = RES_OK; + ASSERT(uv && fp_to_meter > 0 && weight && Tref >= 0 && time >= 0); + + T.func = XD(boundary_path); + rwalk.hit_side = side; + rwalk.hit.distance = 0; + rwalk.vtx.time = time; + rwalk.mdm = NULL; /* The random walk is at an interface between 2 media */ + +#if SDIS_SOLVE_DIMENSION == 2 + st = (float)uv[0]; +#else + f2_set_d2(st, uv); +#endif + + /* Fetch the primitive */ + SXD(scene_view_get_primitive + (scn->sXd(view), (unsigned int)iprim, &rwalk.hit.prim)); + + /* Retrieve the world space position of the probe onto the primitive */ + SXD(primitive_get_attrib(&rwalk.hit.prim, SXD_POSITION, st, &attr)); + dX_set_fX(rwalk.vtx.P, attr.value); + + /* Retrieve the primitive normal */ + SXD(primitive_get_attrib(&rwalk.hit.prim, SXD_GEOMETRY_NORMAL, st, &attr)); + fX(set)(rwalk.hit.normal, attr.value); + +#if SDIS_SOLVE_DIMENSION==2 + rwalk.hit.u = st; +#else + f2_set(rwalk.hit.uv, st); +#endif + + ctx.Tarad = Tarad; + ctx.Tref3 = Tref*Tref*Tref; + + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) return res; + + *weight = T.value; + return RES_OK; +} + +res_T +XD(boundary_flux_realisation) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const size_t iprim, + const double uv[DIM], + const double time, + const enum sdis_side solid_side, + const double fp_to_meter, + const double Tarad, + const double Tref, + const char compute_radiative, + const char compute_convective, + double weight[3]) +{ + struct rwalk_context ctx; + struct XD(rwalk) rwalk; + struct XD(temperature) T; + struct sXd(attrib) attr; + struct sXd(primitive) prim; +#if SDIS_SOLVE_DIMENSION == 2 + float st; +#else + float st[2]; +#endif + double P[SDIS_SOLVE_DIMENSION]; + float N[SDIS_SOLVE_DIMENSION]; + const double Tr3 = Tref * Tref * Tref; + const enum sdis_side fluid_side = + (solid_side == SDIS_FRONT) ? SDIS_BACK : SDIS_FRONT; + res_T res = RES_OK; + ASSERT(uv && fp_to_meter > 0 && weight && time >= 0 && Tref >= 0); + +#if SDIS_SOLVE_DIMENSION == 2 + #define SET_PARAM(Dest, Src) (Dest).u = (Src); + st = (float)uv[0]; +#else + #define SET_PARAM(Dest, Src) f2_set((Dest).uv, (Src)); + f2_set_d2(st, uv); +#endif + + /* Fetch the primitive */ + SXD(scene_view_get_primitive(scn->sXd(view), (unsigned int)iprim, &prim)); + + /* Retrieve the world space position of the probe onto the primitive */ + SXD(primitive_get_attrib(&prim, SXD_POSITION, st, &attr)); + dX_set_fX(P, attr.value); + + /* Retrieve the primitive normal */ + SXD(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, st, &attr)); + fX(set)(N, attr.value); + + #define RESET_WALK(Side, Mdm) { \ + rwalk = XD(RWALK_NULL); \ + rwalk.hit_side = (Side); \ + rwalk.hit.distance = 0; \ + rwalk.vtx.time = time; \ + rwalk.mdm = (Mdm); \ + rwalk.hit.prim = prim; \ + SET_PARAM(rwalk.hit, st); \ + ctx.Tarad = Tarad; \ + ctx.Tref3 = Tr3; \ + dX(set)(rwalk.vtx.P, P); \ + fX(set)(rwalk.hit.normal, N); \ + T = XD(TEMPERATURE_NULL); \ + } (void)0 + + /* Compute boundary temperature */ + RESET_WALK(solid_side, NULL); + T.func = XD(boundary_path); + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) return res; + weight[0] = T.value; + + /* Compute radiative temperature */ + if(compute_radiative) { + RESET_WALK(fluid_side, NULL); + T.func = XD(radiative_path); + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) return res; + weight[1] = T.value; + } + + /* Compute fluid temperature */ + if(compute_convective) { + const struct sdis_interface* interf = + scene_get_interface(scn, (unsigned)iprim); + const struct sdis_medium* mdm = interface_get_medium(interf, fluid_side); + + RESET_WALK(fluid_side, mdm); + T.func = XD(convective_path); + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) return res; + weight[2] = T.value; + } + + #undef SET_PARAM + #undef RESET_WALK + + return RES_OK; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_scene.c b/src/sdis_scene.c @@ -15,11 +15,9 @@ #include "sdis_scene_Xd.h" -/* Generate the 2D functions of the scene */ +/* Generate the Generic functions of the scene */ #define SDIS_SCENE_DIMENSION 2 #include "sdis_scene_Xd.h" - -/* Generate the 3D functions of the scene */ #define SDIS_SCENE_DIMENSION 3 #include "sdis_scene_Xd.h" diff --git a/src/sdis_scene_c.h b/src/sdis_scene_c.h @@ -41,6 +41,10 @@ struct get_medium_info { struct s2d_hit hit_2d; struct s3d_hit hit_3d; }; +#define GET_MEDIUM_INFO_NULL__ \ + {{0,0,0}, {0,0,0}, {0,0,0}, S2D_HIT_NULL__, S3D_HIT_NULL__} +static const struct get_medium_info GET_MEDIUM_INFO_NULL = + GET_MEDIUM_INFO_NULL__; static INLINE void prim_prop_init(struct mem_allocator* allocator, struct prim_prop* prim) @@ -147,13 +151,13 @@ enclosure_local2global_prim_id #define DARRAY_FUNCTOR_INIT interface_init #include <rsys/dynamic_array.h> -/* Declare the array of medium */ +/* Declare the array of media */ #define DARRAY_NAME medium #define DARRAY_DATA struct sdis_medium* #define DARRAY_FUNCTOR_INIT medium_init #include <rsys/dynamic_array.h> -/* Declare the array of primitive */ +/* Declare the array of primitives */ #define DARRAY_NAME prim_prop #define DARRAY_DATA struct prim_prop #define DARRAY_FUNCTOR_INIT prim_prop_init @@ -169,7 +173,7 @@ enclosure_local2global_prim_id #define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE enclosure_copy_and_release #include <rsys/hash_table.h> -/* Declare the hash table that maps an enclosure id to its data */ +/* Declare the hash table that maps an enclosure id to hc upper bound */ #define HTABLE_NAME d #define HTABLE_KEY unsigned #define HTABLE_DATA double diff --git a/src/sdis_solve.c b/src/sdis_solve.c @@ -18,13 +18,10 @@ #include "sdis_device_c.h" #include "sdis_estimator_c.h" #include "sdis_interface_c.h" -#include "sdis_solve_Xd.h" -/* Generate the 2D solver */ +/* Generate the solvers */ #define SDIS_SOLVE_DIMENSION 2 #include "sdis_solve_Xd.h" - -/* Generate the 3D solver */ #define SDIS_SOLVE_DIMENSION 3 #include "sdis_solve_Xd.h" @@ -172,100 +169,14 @@ sdis_solve_probe const double Tref, /* Reference temperature */ struct sdis_estimator** out_estimator) { - const struct sdis_medium* medium = NULL; - struct sdis_estimator* estimator = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - double weight = 0; - double sqr_weight = 0; - const int64_t rcount = (int64_t)nrealisations; - int64_t irealisation = 0; - size_t N = 0; /* #realisations that do not fail */ - size_t i; - ATOMIC res = RES_OK; - - if(!scn || !nrealisations || nrealisations > INT64_MAX || !position - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || fp_to_meter <= 0 || Tref < 0 || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } - - /* Create the proxy RNG */ - res = ssp_rng_proxy_create(scn->dev->allocator, &ssp_rng_mt19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - - /* Create the per thread RNG */ - rngs = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); - if(!rngs) { - res = RES_MEM_ERR; - goto error; - } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); - if(res != RES_OK) goto error; - } - - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_TEMPERATURE_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - - /* Retrieve the medium in which the submitted position lies */ - res = scene_get_medium(scn, position, NULL, &medium); - if(res != RES_OK) goto error; - - /* Here we go! Launch the Monte Carlo estimation */ - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) - for(irealisation = 0; irealisation < rcount; ++irealisation) { - res_T res_local; - double w = NaN; - const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occured */ - - if(scene_is_2d(scn)) { - res_local = probe_realisation_2d - (scn, rng, medium, position, time_range, fp_to_meter, Tarad, Tref, &w); - } else { - res_local = probe_realisation_3d - (scn, rng, medium, position, time_range, fp_to_meter, Tarad, Tref, &w); - } - if(res_local != RES_OK) { - if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } else { - weight += w; - sqr_weight += w*w; - ++N; - } - } - if(res != RES_OK) goto error; - - setup_estimator(estimator, nrealisations, N, weight, sqr_weight); - -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(rngs[i]) SSP(rng_ref_put(rngs[i])); - } - MEM_RM(scn->dev->allocator, rngs); - } - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_estimator) *out_estimator = estimator; - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_2d(scn, nrealisations, position, time_range, + fp_to_meter, Tarad, Tref, out_estimator); + } else { + return solve_probe_3d(scn, nrealisations, position, time_range, + fp_to_meter, Tarad, Tref, out_estimator); } - goto exit; } res_T @@ -281,131 +192,83 @@ sdis_solve_probe_boundary const double Tref, /* In Kelvin */ struct sdis_estimator** out_estimator) { - struct sdis_estimator* estimator = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - double weight = 0; - double sqr_weight = 0; - const int64_t rcount = (int64_t)nrealisations; - int64_t irealisation = 0; - size_t N = 0; /* #realisations that do not fail */ - size_t i; - ATOMIC res = RES_OK; - - if(!scn || !nrealisations || nrealisations > INT64_MAX || !uv - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || fp_to_meter <= 0 || Tref < 0 || (side != SDIS_FRONT && side != SDIS_BACK) - || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } - - /* Check the primitive identifier */ - if(iprim >= scene_get_primitives_count(scn)) { - log_err(scn->dev, -"%s: invalid primitive identifier `%lu'. It must be in the [0 %lu] range.\n", - FUNC_NAME, - (unsigned long)iprim, - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } - - /* Check parametric coordinates */ + if(!scn) return RES_BAD_ARG; if(scene_is_2d(scn)) { - const double v = CLAMP(1.0 - uv[0], 0, 1); - if(uv[0] < 0 || uv[0] > 1 || !eq_eps(uv[0] + v, 1, 1.e-6)) { - log_err(scn->dev, -"%s: invalid parametric coordinates %g.\n" -"u + (1-u) must be equal to 1 with u [0, 1].\n", - FUNC_NAME, uv[0]); - res = RES_BAD_ARG; - goto error; - } + return solve_probe_boundary_2d(scn, nrealisations, iprim, uv, time_range, + side, fp_to_meter, Tarad, Tref, out_estimator); } else { - const double w = CLAMP(1 - uv[0] - uv[1], 0, 1); - if(uv[0] < 0 || uv[1] < 0 || uv[0] > 1 || uv[1] > 1 - || !eq_eps(w + uv[0] + uv[1], 1, 1.e-6)) { - log_err(scn->dev, -"%s: invalid parametric coordinates [%g, %g].\n" -"u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", - FUNC_NAME, uv[0], uv[1]); - res = RES_BAD_ARG; - goto error; - } + return solve_probe_boundary_3d(scn, nrealisations, iprim, uv, time_range, + side, fp_to_meter, Tarad, Tref, out_estimator); } +} - /* Create the proxy RNG */ - res = ssp_rng_proxy_create(scn->dev->allocator, &ssp_rng_mt19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - - /* Create the per thread RNG */ - rngs = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); - if(!rngs) { - res = RES_MEM_ERR; - goto error; - } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); - if(res != RES_OK) goto error; +res_T +sdis_solve_boundary + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const enum sdis_side sides[], /* Per primitive side to consider */ + const size_t nprimitives, /* #primitives */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_boundary_2d(scn, nrealisations, primitives, sides, nprimitives, + time_range, fp_to_meter, Tarad, Tref, out_estimator); + } else { + return solve_boundary_3d(scn, nrealisations, primitives, sides, nprimitives, + time_range, fp_to_meter, Tarad, Tref, out_estimator); } +} - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_TEMPERATURE_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - - /* Here we go! Launch the Monte Carlo estimation */ - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) - for(irealisation = 0; irealisation < rcount; ++irealisation) { - res_T res_local; - double w = NaN; - const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - - if(scene_is_2d(scn)) { - res_local = boundary_realisation_2d - (scn, rng, iprim, uv, time_range, side, fp_to_meter, Tarad, Tref, &w); - } else { - res_local = boundary_realisation_3d - (scn, rng, iprim, uv, time_range, side, fp_to_meter, Tarad, Tref, &w); - } - if(res_local != RES_OK) { - if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } else { - weight += w; - sqr_weight += w*w; - ++N; - } +res_T +sdis_solve_probe_boundary_flux + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t iprim, /* Identifier of the primitive on which the probe lies */ + const double uv[2], /* Parametric coordinates of the probe onto the primitve */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_boundary_flux_2d(scn, nrealisations, iprim, uv, + time_range, fp_to_meter, Tarad, Tref, out_estimator); + } else { + return solve_probe_boundary_flux_3d(scn, nrealisations, iprim, uv, + time_range, fp_to_meter, Tarad, Tref, out_estimator); } - if(res != RES_OK) goto error; - - setup_estimator(estimator, nrealisations, N, weight, sqr_weight); +} -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(rngs[i]) SSP(rng_ref_put(rngs[i])); - } - MEM_RM(scn->dev->allocator, rngs); - } - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_estimator) *out_estimator = estimator; - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; +res_T +sdis_solve_boundary_flux + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const size_t nprimitives, /* #primitives */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) +{ + res_T res = RES_OK; + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + res = solve_boundary_flux_2d(scn, nrealisations, primitives, nprimitives, + time_range, fp_to_meter, Tarad, Tref, out_estimator); + } else { + res = solve_boundary_flux_3d(scn, nrealisations, primitives, nprimitives, + time_range, fp_to_meter, Tarad, Tref, out_estimator); } - goto exit; + return res; } res_T @@ -546,245 +409,3 @@ error: goto exit; } -res_T -sdis_solve_boundary - (struct sdis_scene* scn, - const size_t nrealisations, /* #realisations */ - const size_t primitives[], /* List of boundary primitives to handle */ - const enum sdis_side sides[], /* Per primitive side to consider */ - const size_t nprimitives, /* #primitives */ - const double time_range[2], /* Observation time */ - const double fp_to_meter, /* Scale from floating point units to meters */ - const double Tarad, /* In Kelvin */ - const double Tref, /* In Kelvin */ - struct sdis_estimator** out_estimator) -{ - res_T res = RES_OK; - if(!scn) return RES_BAD_ARG; - if(scene_is_2d(scn)) { - res = solve_boundary_2d(scn, nrealisations, primitives, sides, nprimitives, - time_range, fp_to_meter, Tarad, Tref, out_estimator); - } else { - res = solve_boundary_3d(scn, nrealisations, primitives, sides, nprimitives, - time_range, fp_to_meter, Tarad, Tref, out_estimator); - } - return res; -} - -res_T -sdis_solve_probe_boundary_flux - (struct sdis_scene* scn, - const size_t nrealisations, /* #realisations */ - const size_t iprim, /* Identifier of the primitive on which the probe lies */ - const double uv[2], /* Parametric coordinates of the probe onto the primitve */ - const double time_range[2], /* Observation time */ - const double fp_to_meter, /* Scale from floating point units to meters */ - const double Tarad, /* In Kelvin */ - const double Tref, /* In Kelvin */ - struct sdis_estimator** out_estimator) -{ - struct sdis_estimator* estimator = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - const struct sdis_interface* interf; - const struct sdis_medium *fmd, *bmd; - enum sdis_side solid_side, fluid_side; - struct sdis_interface_fragment frag; - double weight_t = 0, sqr_weight_t = 0; - double weight_fc = 0, sqr_weight_fc = 0; - double weight_fr = 0, sqr_weight_fr = 0; - double weight_f= 0, sqr_weight_f = 0; - const int64_t rcount = (int64_t)nrealisations; - int64_t irealisation = 0; - size_t N = 0; /* #realisations that do not fail */ - size_t i; - ATOMIC res = RES_OK; - - if(!scn || !nrealisations || nrealisations > INT64_MAX || !uv - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || fp_to_meter <= 0 || Tref < 0 - || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } - - /* Check the primitive identifier */ - if(iprim >= scene_get_primitives_count(scn)) { - log_err(scn->dev, - "%s: invalid primitive identifier `%lu'. It must be in the [0 %lu] range.\n", - FUNC_NAME, - (unsigned long)iprim, - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } - - /* Check parametric coordinates */ - if(scene_is_2d(scn)) { - const double v = CLAMP(1.0 - uv[0], 0, 1); - if(uv[0] < 0 || uv[0] > 1 || !eq_eps(uv[0] + v, 1, 1.e-6)) { - log_err(scn->dev, - "%s: invalid parametric coordinates %g.\n" - "u + (1-u) must be equal to 1 with u [0, 1].\n", - FUNC_NAME, uv[0]); - res = RES_BAD_ARG; - goto error; - } - } else { - const double w = CLAMP(1 - uv[0] - uv[1], 0, 1); - if(uv[0] < 0 || uv[1] < 0 || uv[0] > 1 || uv[1] > 1 - || !eq_eps(w + uv[0] + uv[1], 1, 1.e-6)) { - log_err(scn->dev, - "%s: invalid parametric coordinates [%g, %g].\n" - "u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", - FUNC_NAME, uv[0], uv[1]); - res = RES_BAD_ARG; - goto error; - } - } - /* Check medium is fluid on one side and solid on the other */ - interf = scene_get_interface(scn, (unsigned)iprim); - fmd = interface_get_medium(interf, SDIS_FRONT); - bmd = interface_get_medium(interf, SDIS_BACK); - if(!fmd || !bmd - || (!(fmd->type == SDIS_FLUID && bmd->type == SDIS_SOLID) - && !(fmd->type == SDIS_SOLID && bmd->type == SDIS_FLUID))) - { - res = RES_BAD_ARG; - goto error; - } - solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; - fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; - - /* Create the proxy RNG */ - res = ssp_rng_proxy_create(scn->dev->allocator, &ssp_rng_mt19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - - /* Create the per thread RNG */ - rngs = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); - if(!rngs) { - res = RES_MEM_ERR; - goto error; - } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs + i); - if(res != RES_OK) goto error; - } - - /* Prebuild the interface fragment */ - if(scene_is_2d(scn)) { - res = interface_prebuild_fragment_2d(&frag, scn, (unsigned)iprim, - uv, fluid_side); - } else { - res = interface_prebuild_fragment_3d(&frag, scn, (unsigned)iprim, - uv, fluid_side); - } - - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_FLUX_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - - /* Here we go! Launch the Monte Carlo estimation */ - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static) reduction(+:weight_t,sqr_weight_t,\ - weight_fc,sqr_weight_fc,weight_fr,sqr_weight_fr,weight_f,sqr_weight_f,N) - for(irealisation = 0; irealisation < rcount; ++irealisation) { - res_T res_local; - double T_brf[3] = { 0, 0, 0 }; - const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - double time, epsilon, hc, hr; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - - /* Sample a time */ - time = sample_time(time_range, rng); - - /* Compute hr and hc */ - frag.time = time; - epsilon = interface_side_get_emissivity(interf, &frag); - hc = interface_get_convection_coef(interf, &frag); - hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; - - /* Fluid, Radiative and Solid temperatures */ - if(scene_is_2d(scn)) { - res_local = probe_flux_realisation_2d(scn, rng, iprim, uv, time, - solid_side, fp_to_meter, Tarad, Tref, hr>0, hc>0, T_brf); - } else { - res_local = probe_flux_realisation_3d(scn, rng, iprim, uv, time, - solid_side, fp_to_meter, Tarad, Tref, hr>0, hc>0, T_brf); - } - if(res_local != RES_OK) { - if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } else { - const double Tboundary = T_brf[0]; - const double Tradiative = T_brf[1]; - const double Tfluid = T_brf[2]; - const double w_conv = hc * (Tboundary - Tfluid); - const double w_rad = hr * (Tboundary - Tradiative); - const double w_total = w_conv + w_rad; - weight_t += Tboundary; - sqr_weight_t += Tboundary * Tboundary; - weight_fc += w_conv; - sqr_weight_fc += w_conv * w_conv; - weight_fr += w_rad; - sqr_weight_fr += w_rad * w_rad; - weight_f += w_total; - sqr_weight_f += w_total * w_total; - ++N; - } - } - if(res != RES_OK) goto error; - - setup_estimator(estimator, nrealisations, N, weight_t, sqr_weight_t); - setup_estimator_flux(estimator, FLUX_CONVECTIVE__, weight_fc, sqr_weight_fc); - setup_estimator_flux(estimator, FLUX_RADIATIVE__, weight_fr, sqr_weight_fr); - setup_estimator_flux(estimator, FLUX_TOTAL__, weight_f, sqr_weight_f); - -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(rngs[i]) SSP(rng_ref_put(rngs[i])); - } - MEM_RM(scn->dev->allocator, rngs); - } - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_estimator) *out_estimator = estimator; - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } - goto exit; -} - -res_T -sdis_solve_boundary_flux - (struct sdis_scene* scn, - const size_t nrealisations, /* #realisations */ - const size_t primitives[], /* List of boundary primitives to handle */ - const size_t nprimitives, /* #primitives */ - const double time_range[2], /* Observation time */ - const double fp_to_meter, /* Scale from floating point units to meters */ - const double Tarad, /* In Kelvin */ - const double Tref, /* In Kelvin */ - struct sdis_estimator** out_estimator) -{ - res_T res = RES_OK; - if(!scn) return RES_BAD_ARG; - if(scene_is_2d(scn)) { - res = solve_boundary_flux_2d(scn, nrealisations, primitives, nprimitives, - time_range, fp_to_meter, Tarad, Tref, out_estimator); - } else { - res = solve_boundary_flux_3d(scn, nrealisations, primitives, nprimitives, - time_range, fp_to_meter, Tarad, Tref, out_estimator); - } - return res; -} diff --git a/src/sdis_solve_Xd.h b/src/sdis_solve_Xd.h @@ -13,116 +13,31 @@ * 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 SDIS_SOLVE_DIMENSION -#ifndef SDIS_SOLVE_XD_H -#define SDIS_SOLVE_XD_H - #include "sdis_device_c.h" -#include "sdis_interface_c.h" +#include "sdis_estimator_c.h" #include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_realisation.h" #include "sdis_scene_c.h" -#include <rsys/float2.h> -#include <rsys/float3.h> -#include <rsys/stretchy_array.h> - #include <star/ssp.h> #include <omp.h> -/* Define a new result code from RES_BAD_OP saying that the bad operation is - * definitive, i.e. in the current state, the realisation will inevitably fail. - * It is thus unecessary to retry a specific section of the random walk */ -#define RES_BAD_OP_IRRECOVERABLE (-RES_BAD_OP) +#include "sdis_Xd_begin.h" -/* Empirical scale factor to apply to the upper bound of the ray range in order - * to handle numerical imprecisions */ -#define RAY_RANGE_MAX_SCALE 1.001f - -/* Emperical scale factor applied to the challenged reinjection distance. If - * the distance to reinject is less than this adjusted value, the solver - * switches from 2D reinjection scheme to the 1D reinjection scheme in order to - * avoid numerical issues. */ -#define REINJECT_DST_MIN_SCALE 0.125f - -#define BOLTZMANN_CONSTANT 5.6696e-8 /* W/m^2/K^4 */ - -struct rwalk_context { - double Tarad; /* Ambient radiative temperature */ - double Tref3; /* Reference temperature ^ 3 */ +struct XD(boundary_context) { + struct sXd(scene_view)* view; + const size_t* primitives; +}; +static const struct XD(boundary_context) XD(BOUNDARY_CONTEXT_NULL) = { + NULL, NULL }; -/* Reflect the vector V wrt the normal N. By convention V points outward the - * surface. */ -static INLINE float* -reflect_2d(float res[2], const float V[2], const float N[2]) -{ - float tmp[2]; - float cos_V_N; - ASSERT(res && V && N); - ASSERT(f2_is_normalized(V) && f2_is_normalized(N)); - cos_V_N = f2_dot(V, N); - f2_mulf(tmp, N, 2*cos_V_N); - f2_sub(res, tmp, V); - return res; -} - -/* Reflect the vector V wrt the normal N. By convention V points outward the - * surface. */ -static INLINE float* -reflect_3d(float res[3], const float V[3], const float N[3]) -{ - float tmp[3]; - float cos_V_N; - ASSERT(res && V && N); - ASSERT(f3_is_normalized(V) && f3_is_normalized(N)); - cos_V_N = f3_dot(V, N); - f3_mulf(tmp, N, 2*cos_V_N); - f3_sub(res, tmp, V); - return res; -} - -static INLINE void -setup_estimator - (struct sdis_estimator* estimator, - const size_t nrealisations, - const size_t nsuccesses, - const double accum_weights, - const double accum_sqr_weights) -{ - ASSERT(estimator && nrealisations && nsuccesses); - estimator->nrealisations = nsuccesses; - estimator->nfailures = nrealisations - nsuccesses; - estimator->temperature.E = accum_weights / (double)nsuccesses; - estimator->temperature.V = - accum_sqr_weights / (double)nsuccesses - - estimator->temperature.E * estimator->temperature.E; - estimator->temperature.V = MMAX(estimator->temperature.V, 0); - estimator->temperature.SE = - sqrt(estimator->temperature.V / (double)nsuccesses); -} - -static INLINE void -setup_estimator_flux - (struct sdis_estimator* estimator, - const enum flux_names name, - const double accum_weights, - const double accum_sqr_weights) -{ - ASSERT(estimator && (unsigned)name < FLUX_NAMES_COUNT__ && estimator->fluxes); - ASSERT(estimator->nrealisations); - estimator->fluxes[name].E = accum_weights / (double)estimator->nrealisations; - estimator->fluxes[name].V = - accum_sqr_weights / (double)estimator->nrealisations - - estimator->fluxes[name].E * estimator->fluxes[name].E; - estimator->fluxes[name].V = MMAX(estimator->fluxes[name].V, 0); - estimator->fluxes[name].SE = - sqrt(estimator->fluxes[name].V / (double)estimator->nrealisations); -} +#ifndef SDIS_SOLVE_XD_H +#define SDIS_SOLVE_XD_H static INLINE double -sample_time - (const double time_range[2], - struct ssp_rng* rng) +sample_time(struct ssp_rng* rng, const double time_range[2]) { ASSERT(time_range && time_range[0] >= 0 && time_range[1] >= time_range[0]); ASSERT(rng); @@ -131,119 +46,11 @@ sample_time } #endif /* SDIS_SOLVE_XD_H */ -#else - -#if (SDIS_SOLVE_DIMENSION == 2) - #include <rsys/double2.h> - #include <rsys/float2.h> - #include <star/s2d.h> -#elif (SDIS_SOLVE_DIMENSION == 3) - #include <rsys/double2.h> - #include <rsys/double3.h> - #include <rsys/float3.h> - #include <star/s3d.h> -#else - #error "Invalid SDIS_SOLVE_DIMENSION value." -#endif - -/* Syntactic sugar */ -#define DIM SDIS_SOLVE_DIMENSION - -/* Star-XD macros generic to SDIS_SOLVE_DIMENSION */ -#define sXd(Name) CONCAT(CONCAT(CONCAT(s, DIM), d_), Name) -#define SXD_HIT_NONE CONCAT(CONCAT(S,DIM), D_HIT_NONE) -#define SXD_HIT_NULL CONCAT(CONCAT(S,DIM), D_HIT_NULL) -#define SXD_HIT_NULL__ CONCAT(CONCAT(S, DIM), D_HIT_NULL__) -#define SXD_POSITION CONCAT(CONCAT(S, DIM), D_POSITION) -#define SXD_GEOMETRY_NORMAL CONCAT(CONCAT(S, DIM), D_GEOMETRY_NORMAL) -#define SXD_GEOMETRY_NORMAL CONCAT(CONCAT(S, DIM), D_GEOMETRY_NORMAL) -#define SXD_VERTEX_DATA_NULL CONCAT(CONCAT(S, DIM), D_VERTEX_DATA_NULL) -#define SXD CONCAT(CONCAT(S, DIM), D) -#define SXD_FLOAT2 CONCAT(CONCAT(S, DIM), D_FLOAT2) -#define SXD_FLOAT3 CONCAT(CONCAT(S, DIM), D_FLOAT3) -#define SXD_SAMPLE CONCAT(CONCAT(S, DIM), D_SAMPLE) -#define sXd_dev CONCAT(CONCAT(s, DIM), d) - -/* Vector macros generic to SDIS_SOLVE_DIMENSION */ -#define dX(Func) CONCAT(CONCAT(CONCAT(d, DIM), _), Func) -#define fX(Func) CONCAT(CONCAT(CONCAT(f, DIM), _), Func) -#define fX_set_dX CONCAT(CONCAT(CONCAT(f, DIM), _set_d), DIM) -#define dX_set_fX CONCAT(CONCAT(CONCAT(d, DIM), _set_f), DIM) - -/* Macro making generic its subimitted name to SDIS_SOLVE_DIMENSION */ -#define XD(Name) CONCAT(CONCAT(CONCAT(Name, _), DIM), d) - -/* Current state of the random walk */ -struct XD(rwalk) { - struct sdis_rwalk_vertex vtx; /* Position and time of the Random walk */ - const struct sdis_medium* mdm; /* Medium in which the random walk lies */ - struct sXd(hit) hit; /* Hit of the random walk */ - enum sdis_side hit_side; -}; -static const struct XD(rwalk) XD(RWALK_NULL) = { - SDIS_RWALK_VERTEX_NULL__, NULL, SXD_HIT_NULL__, SDIS_SIDE_NULL__ -}; - -struct XD(boundary_context) { - struct sXd(scene_view)* view; - const size_t* primitives; -}; -static const struct XD(boundary_context) XD(BOUNDARY_CONTEXT_NULL) = { - NULL, NULL -}; - -struct XD(temperature) { - res_T (*func)/* Next function to invoke in order to compute the temperature */ - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* temp); - double value; /* Current value of the temperature */ - int done; -}; -static const struct XD(temperature) XD(TEMPERATURE_NULL) = { NULL, 0, 0 }; - -static res_T -XD(boundary_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T); - -static res_T -XD(solid_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T); - -static res_T -XD(fluid_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T); - -static res_T -XD(radiative_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T); /******************************************************************************* * Helper functions ******************************************************************************/ + static INLINE void XD(boundary_get_indices)(const unsigned iprim, unsigned ids[DIM], void* context) { @@ -276,1451 +83,6 @@ XD(boundary_get_position)(const unsigned ivert, float pos[DIM], void* context) fX(set)(pos, attr.value); } -static FINLINE void -XD(move_pos)(double pos[DIM], const float dir[DIM], const float delta) -{ - ASSERT(pos && dir); - pos[0] += dir[0] * delta; - pos[1] += dir[1] * delta; -#if(SDIS_SOLVE_DIMENSION == 3) - pos[2] += dir[2] * delta; -#endif -} - -static FINLINE void -XD(sample_reinjection_dir) - (const struct XD(rwalk)* rwalk, struct ssp_rng* rng, float dir[DIM]) -{ -#if DIM == 2 - /* The sampled directions is defined by rotating the normal around the Z axis - * of an angle of PI/4 or -PI/4. Let the rotation matrix defined as - * | cos(a) -sin(a) | - * | sin(a) cos(a) | - * with a = PI/4, dir = sqrt(2)/2 * | 1 -1 | . N - * | 1 1 | - * with a =-PI/4, dir = sqrt(2)/2 * | 1 1 | . N - * |-1 1 | - * Note that since the sampled direction is finally normalized, we can - * discard the sqrt(2)/2 constant. */ - const uint64_t r = ssp_rng_uniform_uint64(rng, 0, 1); - ASSERT(rwalk && dir); - if(r) { - dir[0] = rwalk->hit.normal[0] - rwalk->hit.normal[1]; - dir[1] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; - } else { - dir[0] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; - dir[1] =-rwalk->hit.normal[0] + rwalk->hit.normal[1]; - } - f2_normalize(dir, dir); -#else - /* Sample a random direction around the normal whose cosine is 1/sqrt(3). To - * do so we sample a position onto a cone whose height is 1/sqrt(2) and the - * radius of its base is 1. */ - float frame[9]; - ASSERT(fX(is_normalized)(rwalk->hit.normal)); - - ssp_ran_circle_uniform_float(rng, dir, NULL); - dir[2] = (float)(1.0/sqrt(2)); - - f33_basis(frame, rwalk->hit.normal); - f33_mulf3(dir, frame, dir); - f3_normalize(dir, dir); - ASSERT(eq_epsf(f3_dot(dir, rwalk->hit.normal), (float)(1.0/sqrt(3)), 1.e-4f)); -#endif -} - -/* Check that the interface fragment is consistent with the current state of - * the random walk */ -static INLINE int -XD(check_rwalk_fragment_consistency) - (const struct XD(rwalk)* rwalk, - const struct sdis_interface_fragment* frag) -{ - double N[DIM]; - double uv[2] = {0, 0}; - ASSERT(rwalk && frag); - dX(normalize)(N, dX_set_fX(N, rwalk->hit.normal)); - if( SXD_HIT_NONE(&rwalk->hit) - || !dX(eq_eps)(rwalk->vtx.P, frag->P, 1.e-6) - || !dX(eq_eps)(N, frag->Ng, 1.e-6) - || !( (IS_INF(rwalk->vtx.time) && IS_INF(frag->time)) - || eq_eps(rwalk->vtx.time, frag->time, 1.e-6))) { - return 0; - } -#if (SDIS_SOLVE_DIMENSION == 2) - uv[0] = rwalk->hit.u; -#else - d2_set_f2(uv, rwalk->hit.uv); -#endif - return d2_eq_eps(uv, frag->uv, 1.e-6); -} - -static res_T -XD(trace_radiative_path) - (struct sdis_scene* scn, - const float ray_dir[3], - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - /* The radiative random walk is always perform in 3D. In 2D, the geometry are - * assumed to be extruded to the infinty along the Z dimension. */ - float N[3] = {0, 0, 0}; - float dir[3] = {0, 0, 0}; - res_T res = RES_OK; - - ASSERT(scn && ray_dir && fp_to_meter > 0 && ctx && rwalk && rng && T); - (void)fp_to_meter; - - f3_set(dir, ray_dir); - - /* Launch the radiative random walk */ - for(;;) { - const struct sdis_interface* interf = NULL; - struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; - const struct sdis_medium* chk_mdm = NULL; - double alpha; - double epsilon; - double r; - float pos[DIM]; - const float range[2] = { 0, FLT_MAX }; - - fX_set_dX(pos, rwalk->vtx.P); - - /* Trace the radiative ray */ -#if (SDIS_SOLVE_DIMENSION == 2) - SXD(scene_view_trace_ray_3d - (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); -#else - SXD(scene_view_trace_ray - (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); -#endif - if(SXD_HIT_NONE(&rwalk->hit)) { /* Fetch the ambient radiative temperature */ - rwalk->hit_side = SDIS_SIDE_NULL__; - if(ctx->Tarad >= 0) { - T->value += ctx->Tarad; - T->done = 1; - break; - } else { - log_err(scn->dev, - "%s: the random walk reaches an invalid ambient radiative temperature " - "of `%gK' at position `%g %g %g'. This may be due to numerical " - "inaccuracies or to inconsistency in the simulated system (eg: " - "unclosed geometry). For systems where the random walks can reach " - "such temperature, one has to setup a valid ambient radiative " - "temperature, i.e. it must be greater or equal to 0.\n", - FUNC_NAME, - ctx->Tarad, - SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP; - goto error; - } - } - - /* Define the hit side */ - rwalk->hit_side = fX(dot)(dir, rwalk->hit.normal) < 0 - ? SDIS_FRONT : SDIS_BACK; - - /* Move the random walk to the hit position */ - XD(move_pos)(rwalk->vtx.P, dir, rwalk->hit.distance); - - /* Fetch the new interface and setup the hit fragment */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); - - /* Fetch the interface emissivity */ - epsilon = interface_side_get_emissivity(interf, &frag); - if(epsilon > 1 || epsilon < 0) { - log_err(scn->dev, - "%s: invalid overall emissivity `%g' at position `%g %g %g'.\n", - FUNC_NAME, epsilon, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP; - goto error; - } - - /* Switch in boundary temperature ? */ - r = ssp_rng_canonical(rng); - if(r < epsilon) { - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ - break; - } - - /* Normalize the normal of the interface and ensure that it points toward the - * current medium */ - fX(normalize)(N, rwalk->hit.normal); - if(rwalk->hit_side == SDIS_BACK){ - chk_mdm = interf->medium_back; - fX(minus)(N, N); - } else { - chk_mdm = interf->medium_front; - } - - if(chk_mdm != rwalk->mdm) { - /* To ease the setting of models, the external enclosure is allowed to be - * incoherent regarding media. - * Here a radiative path is allowed to join 2 different fluids. */ - const int outside = scene_is_outside - (scn, rwalk->hit_side, rwalk->hit.prim.prim_id); - if(outside && chk_mdm->type == SDIS_FLUID) { - rwalk->mdm = chk_mdm; - } else { - log_err(scn->dev, "%s: inconsistent medium definition at `%g %g %g'.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP; - goto error; - } - } - alpha = interface_side_get_specular_fraction(interf, &frag); - r = ssp_rng_canonical(rng); - if(r < alpha) { /* Sample specular part */ - reflect_3d(dir, f3_minus(dir, dir), N); - } else { /* Sample diffuse part */ - ssp_ran_hemisphere_cos_float(rng, N, dir, NULL); - } - } - -exit: - return res; -error: - goto exit; -} - -res_T -XD(radiative_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - /* The radiative random walk is always perform in 3D. In 2D, the geometry are - * assumed to be extruded to the infinty along the Z dimension. */ - float N[3] = {0, 0, 0}; - float dir[3] = {0, 0, 0}; - res_T res = RES_OK; - - ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); - (void)fp_to_meter; - - /* Normalize the normal of the interface and ensure that it points toward the - * current medium */ - fX(normalize(N, rwalk->hit.normal)); - if(rwalk->hit_side == SDIS_BACK) { - fX(minus(N, N)); - } - - /* Cosine weighted sampling of a direction around the surface normal */ - ssp_ran_hemisphere_cos_float(rng, N, dir, NULL); - - /* Launch the radiative random walk */ - res = XD(trace_radiative_path)(scn, dir, fp_to_meter, ctx, rwalk, rng, T); - if(res != RES_OK) goto error; - -exit: - return res; -error: - goto exit; -} - -res_T -XD(fluid_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - struct sXd(attrib) attr_P, attr_N; - struct sdis_interface_fragment frag; - const struct sdis_interface* interf; - const struct enclosure* enc; - unsigned enc_ids[2]; - unsigned enc_id; - double rho; /* Volumic mass */ - double hc; /* Convection coef */ - double cp; /* Calorific capacity */ - double tmp; - double r; -#if DIM == 2 - float st; -#else - float st[2]; -#endif - (void)rng, (void)fp_to_meter, (void)ctx; - ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_FLUID); - - tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(tmp >= 0) { /* T is known. */ - T->value += tmp; - T->done = 1; - return RES_OK; - } - - if(SXD_HIT_NONE(&rwalk->hit)) { /* The path begins in the fluid */ - const float range[2] = {0, FLT_MAX}; - float dir[DIM] = {0}; - float org[DIM]; - - dir[DIM-1] = 1; - fX_set_dX(org, rwalk->vtx.P); - - /* Init the path hit field required to define the current enclosure and - * fetch the interface data */ - SXD(scene_view_trace_ray(scn->sXd(view), org, dir, range, NULL, &rwalk->hit)); - rwalk->hit_side = fX(dot)(rwalk->hit.normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; - - if(SXD_HIT_NONE(&rwalk->hit)) { - log_err(scn->dev, -"%s: the position %g %g %g lies in the surrounding fluid whose temperature must \n" -"be known.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - return RES_BAD_OP; - } - } - - /* Fetch the current interface and its associated enclosures */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - scene_get_enclosure_ids(scn, rwalk->hit.prim.prim_id, enc_ids); - - /* Define the enclosure identifier of the current medium */ - ASSERT(interf->medium_front != interf->medium_back); - if(rwalk->mdm == interf->medium_front) { - enc_id = enc_ids[0]; - ASSERT(rwalk->hit_side == SDIS_FRONT); - } else { - ASSERT(rwalk->mdm == interf->medium_back); - enc_id = enc_ids[1]; - ASSERT(rwalk->hit_side == SDIS_BACK); - } - - /* Fetch the enclosure data */ - enc = scene_get_enclosure(scn, enc_id); - if(!enc) { - /* The possibility for a fluid enclosure to be unregistred is that it is - * the external enclosure. In this situation unknown temperature is - * forbidden. */ - log_err(scn->dev, -"%s: invalid enclosure. The surrounding fluid has an unset temperature.\n", - FUNC_NAME); - return RES_BAD_ARG; - } - - /* The hc upper bound can be 0 if h is uniformly 0. In that case the result - * is the initial condition. */ - if(enc->hc_upper_bound == 0) { - /* Cannot be in the fluid without starting there. */ - ASSERT(SXD_HIT_NONE(&rwalk->hit)); - rwalk->vtx.time = fluid_get_t0(rwalk->mdm); - tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - - /* At t=0, the initial condition should have been reached. */ - log_err(scn->dev, - "%s: undefined initial condition. " - "Time is 0 but the temperature remains unknown.\n", - FUNC_NAME); - return RES_BAD_OP; - } - - /* A trick to force first r test result. */ - r = 1; - - /* Sample time until init condition is reached or a true convection occurs. */ - for(;;) { - struct sXd(primitive) prim; - /* Setup the fragment of the interface. */ - XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); - - /* Fetch hc. */ - hc = interface_get_convection_coef(interf, &frag); - if(hc > enc->hc_upper_bound) { - log_err(scn->dev, - "%s: hc (%g) exceeds its provided upper bound (%g) at %g %g %g.\n", - FUNC_NAME, hc, enc->hc_upper_bound, SPLIT3(rwalk->vtx.P)); - return RES_BAD_OP; - } - - if(r < hc / enc->hc_upper_bound) { - /* True convection. Always true if hc == bound. */ - break; - } - - /* Fetch other physical properties. */ - cp = fluid_get_calorific_capacity(rwalk->mdm, &rwalk->vtx); - rho = fluid_get_volumic_mass(rwalk->mdm, &rwalk->vtx); - - /* Sample the time using the upper bound. */ - if(rwalk->vtx.time != INF) { - double mu, tau, t0; - mu = enc->hc_upper_bound / (rho * cp) * enc->S_over_V; - tau = ssp_ran_exp(rng, mu); - t0 = fluid_get_t0(rwalk->mdm); - rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); - if(rwalk->vtx.time == t0) { - /* Check the initial condition. */ - tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - /* The initial condition should have been reached. */ - log_err(scn->dev, - "%s: undefined initial condition. " - "Time is %g but the temperature remains unknown.\n", - FUNC_NAME, t0); - return RES_BAD_OP; - } - } - - /* Uniformly sample the enclosure. */ -#if DIM == 2 - SXD(scene_view_sample - (enc->sXd(view), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, &rwalk->hit.u)); - st = rwalk->hit.u; -#else - SXD(scene_view_sample - (enc->sXd(view), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, rwalk->hit.uv)); - f2_set(st, rwalk->hit.uv); -#endif - /* Map the sampled primitive id from the enclosure space to the scene - * space. Note that the overall scene has only one shape. As a consequence - * neither the geom_id nor the inst_id needs to be updated */ - rwalk->hit.prim.prim_id = enclosure_local2global_prim_id(enc, prim.prim_id); - - SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_POSITION, st, &attr_P)); - SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_GEOMETRY_NORMAL, st, &attr_N)); - dX_set_fX(rwalk->vtx.P, attr_P.value); - fX(set)(rwalk->hit.normal, attr_N.value); - - /* Fetch the interface of the sampled point. */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - if(rwalk->mdm == interf->medium_front) { - rwalk->hit_side = SDIS_FRONT; - } else if(rwalk->mdm == interf->medium_back) { - rwalk->hit_side = SDIS_BACK; - } else { - FATAL("Unexpected fluid interface.\n"); - } - - /* Renew r for next loop. */ - r = ssp_rng_canonical_float(rng); - } - - rwalk->hit.distance = 0; - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ - return RES_OK; -} - -static void -XD(solid_solid_boundary_temperature) - (const struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - const struct sdis_interface_fragment* frag, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - struct sXd(hit) hit0, hit1, hit2, hit3; - struct sXd(hit)* hit; - const struct sdis_interface* interf = NULL; - const struct sdis_medium* solid_front = NULL; - const struct sdis_medium* solid_back = NULL; - const struct sdis_medium* mdm; - double lambda_front, lambda_back; - double delta_front, delta_back; - double delta_boundary_front, delta_boundary_back; - double delta_boundary; - double reinject_dst_front, reinject_dst_back; - double reinject_dst; - double proba; - double tmp; - double r; - double power; - float range0[2], range1[2]; - float dir0[DIM], dir1[DIM], dir2[DIM], dir3[DIM]; - float* dir; - float pos[DIM]; - int dim = DIM; - ASSERT(scn && fp_to_meter > 0 && ctx && frag && rwalk && rng && T); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); - (void)frag, (void)ctx; - - /* Retrieve the current boundary media */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - solid_front = interface_get_medium(interf, SDIS_FRONT); - solid_back = interface_get_medium(interf, SDIS_BACK); - ASSERT(solid_front->type == SDIS_SOLID); - ASSERT(solid_back->type == SDIS_SOLID); - - /* Fetch the properties of the media */ - lambda_front = solid_get_thermal_conductivity(solid_front, &rwalk->vtx); - lambda_back = solid_get_thermal_conductivity(solid_back, &rwalk->vtx); - - /* Note that reinjection distance is *FIXED*. It MUST ensure that the orthogonal - * distance from the boundary to the point to challenge is equal to delta. */ - delta_front = solid_get_delta(solid_front, &rwalk->vtx); - delta_back = solid_get_delta(solid_back, &rwalk->vtx); - delta_boundary_front = delta_front*sqrt(DIM); - delta_boundary_back = delta_back *sqrt(DIM); - - /* Sample a reinjection direction and reflect it around the normal. Then - * reflect them on the back side of the interface. */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); - XD(reflect)(dir2, dir0, rwalk->hit.normal); - fX(minus)(dir1, dir0); - fX(minus)(dir3, dir2); - - /* Trace the sampled directions on both sides of the interface to adjust the - * reinjection distance of the random walk . */ - fX_set_dX(pos, rwalk->vtx.P); - f2(range0, 0, (float)delta_boundary_front*RAY_RANGE_MAX_SCALE); - f2(range1, 0, (float)delta_boundary_back *RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range0, &rwalk->hit, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range1, &rwalk->hit, &hit1)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir2, range0, &rwalk->hit, &hit2)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir3, range1, &rwalk->hit, &hit3)); - - /* Adjust the reinjection distance */ - reinject_dst_front = MMIN(MMIN(delta_boundary_front, hit0.distance), hit2.distance); - reinject_dst_back = MMIN(MMIN(delta_boundary_back, hit1.distance), hit3.distance); - - /* Define the reinjection side. Note that the proba should be : - * Lf/Df' / (Lf/Df' + Lb/Db') - * - * with L<f|b> the lambda of the <front|back> side and D<f|b>' the adjusted - * delta of the <front|back> side, i.e. : - * D<f|b>' = reinject_dst_<front|back> / sqrt(DIM) - * - * Anyway, one can avoid to compute the adjusted delta by directly using the - * adjusted reinjection distance since the resulting proba is strictly the - * same; sqrt(DIM) can be simplified. */ - r = ssp_rng_canonical(rng); - proba = (lambda_front/reinject_dst_front) - / (lambda_front/reinject_dst_front + lambda_back/reinject_dst_back); - if(r < proba) { /* Reinject in front */ - dir = dir0; - hit = &hit0; - mdm = solid_front; - reinject_dst = reinject_dst_front; - delta_boundary = delta_boundary_front; - } else { /* Reinject in back */ - dir = dir1; - hit = &hit1; - mdm = solid_back; - reinject_dst = reinject_dst_back; - delta_boundary = delta_boundary_back; - } - - /* Switch in 1D reinjection scheme */ - if(reinject_dst < delta_boundary * REINJECT_DST_MIN_SCALE) { - if(dir == dir0) { - fX(set)(dir, rwalk->hit.normal); - } else { - fX(minus)(dir, rwalk->hit.normal); - } - - f2(range0, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir, range0, &rwalk->hit, hit)); - reinject_dst = MMIN(delta_boundary, hit->distance), - dim = 1; - - /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit - * distance */ - if(!SXD_HIT_NONE(hit)) { - reinject_dst *= 0.5; - *hit = SXD_HIT_NULL; - } - } - - /* Handle the volumic power */ - power = solid_get_volumic_power(mdm, &rwalk->vtx); - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_in_meter = reinject_dst * fp_to_meter; - const double lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); - T->value += tmp; - } - - /* Reinject */ - XD(move_pos)(rwalk->vtx.P, dir, (float)reinject_dst); - if(eq_epsf(hit->distance, (float)reinject_dst, 1.e-4f)) { - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; - rwalk->hit = *hit; - rwalk->hit_side = fX(dot)(hit->normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(solid_temperature); - rwalk->mdm = mdm; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } -} - -static void -XD(solid_fluid_boundary_temperature) - (const struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - const struct sdis_interface_fragment* frag, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - const struct sdis_interface* interf = NULL; - const struct sdis_medium* mdm_front = NULL; - const struct sdis_medium* mdm_back = NULL; - const struct sdis_medium* solid = NULL; - const struct sdis_medium* fluid = NULL; - struct sXd(hit) hit0 = SXD_HIT_NULL; - struct sXd(hit) hit1 = SXD_HIT_NULL; - struct sdis_interface_fragment frag_fluid; - double hc; - double hr; - double epsilon; /* Interface emissivity */ - double lambda; - double fluid_proba; - double radia_proba; - double delta; - double delta_boundary; - double r; - double tmp; - float pos[DIM]; - float dir0[DIM], dir1[DIM]; - float range[2]; - int dim = DIM; - - ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T && ctx); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); - - /* Retrieve the solid and the fluid split by the boundary */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - mdm_front = interface_get_medium(interf, SDIS_FRONT); - mdm_back = interface_get_medium(interf, SDIS_BACK); - ASSERT(mdm_front->type != mdm_back->type); - - frag_fluid = *frag; - if(mdm_front->type == SDIS_SOLID) { - solid = mdm_front; - fluid = mdm_back; - frag_fluid.side = SDIS_BACK; - } else { - solid = mdm_back; - fluid = mdm_front; - frag_fluid.side = SDIS_FRONT; - } - - /* Fetch the solid properties */ - lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); - delta = solid_get_delta(solid, &rwalk->vtx); - - /* Note that the reinjection distance is *FIXED*. It MUST ensure that the - * orthogonal distance from the boundary to the point to chalenge is equal to - * delta. */ - delta_boundary = sqrt(DIM) * delta; - - /* Sample a reinjection direction */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); - - /* Reflect the sampled direction around the normal */ - XD(reflect)(dir1, dir0, rwalk->hit.normal); - - if(solid == mdm_back) { - fX(minus)(dir0, dir0); - fX(minus)(dir1, dir1); - } - - /* Trace dir0/dir1 to adjust the reinjection distance */ - fX_set_dX(pos, rwalk->vtx.P); - f2(range, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range, &rwalk->hit, &hit1)); - - /* Adjust the delta boundary to the hit distance */ - tmp = MMIN(MMIN(delta_boundary, hit0.distance), hit1.distance); - - if(tmp >= delta_boundary * REINJECT_DST_MIN_SCALE) { - delta_boundary = tmp; - /* Define the orthogonal dst from the reinjection pos to the interface */ - delta = delta_boundary / sqrt(DIM); - } else { /* Switch in 1D reinjection scheme. */ - fX(set)(dir0, rwalk->hit.normal); - if(solid == mdm_back) fX(minus)(dir0, dir0); - f2(range, 0, (float)delta*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); - delta_boundary = MMIN(hit0.distance, delta); - - /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit - * distance in order to avoid infinite bounces for parallel plane */ - if(!SXD_HIT_NONE(&hit0)) { - delta_boundary *= 0.5; - hit0 = SXD_HIT_NULL; - } - - delta = delta_boundary; - dim = 1; - } - - /* Fetch the boundary properties */ - epsilon = interface_side_get_emissivity(interf, &frag_fluid); - hc = interface_get_convection_coef(interf, frag); - - /* Compute the radiative coefficient */ - hr = 4.0 * BOLTZMANN_CONSTANT * ctx->Tref3 * epsilon; - - /* Compute the probas to switch in solid, fluid or radiative random walk */ - tmp = lambda / (delta*fp_to_meter); - fluid_proba = hc / (tmp + hr + hc); - radia_proba = hr / (tmp + hr + hc); - /*solid_proba = tmp / (tmp + hr + hc);*/ - - r = ssp_rng_canonical(rng); - if(r < radia_proba) { /* Switch in radiative random walk */ - T->func = XD(radiative_temperature); - rwalk->mdm = fluid; - rwalk->hit_side = rwalk->mdm == mdm_front ? SDIS_FRONT : SDIS_BACK; - } else if(r < fluid_proba + radia_proba) { /* Switch to fluid random walk */ - T->func = XD(fluid_temperature); - rwalk->mdm = fluid; - rwalk->hit_side = rwalk->mdm == mdm_front ? SDIS_FRONT : SDIS_BACK; - } else { /* Solid random walk */ - /* Handle the volumic power */ - const double power = solid_get_volumic_power(solid, &rwalk->vtx); - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_in_meter = delta_boundary * fp_to_meter; - tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); - T->value += tmp; - } - - /* Reinject */ - XD(move_pos)(rwalk->vtx.P, dir0, (float)delta_boundary); - if(eq_epsf(hit0.distance, (float)delta_boundary, 1.e-4f)) { - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; - rwalk->hit = hit0; - rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(solid_temperature); - rwalk->mdm = solid; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } - } -} - -static void -XD(solid_boundary_with_flux_temperature) - (const struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - const struct sdis_interface_fragment* frag, - const double phi, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - const struct sdis_interface* interf = NULL; - const struct sdis_medium* mdm = NULL; - double lambda; - double delta; - double delta_boundary; - double delta_in_meter; - double power; - double tmp; - struct sXd(hit) hit0; - struct sXd(hit) hit1; - float pos[DIM]; - float dir0[DIM]; - float dir1[DIM]; - float range[2]; - int dim = DIM; - ASSERT(frag && phi != SDIS_FLUX_NONE); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); - (void)ctx; - - /* Fetch current interface */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - ASSERT(phi == interface_side_get_flux(interf, frag)); - - /* Fetch incoming solid */ - mdm = interface_get_medium(interf, frag->side); - ASSERT(mdm->type == SDIS_SOLID); - - /* Fetch medium properties */ - lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - delta = solid_get_delta(mdm, &rwalk->vtx); - - /* Compute the reinjection distance. It MUST ensure that the orthogonal - * distance from the boundary to the point to chalenge is equal to delta. */ - delta_boundary = delta * sqrt(DIM); - - /* Sample a reinjection direction */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); - - /* Reflect the sampled direction around the normal */ - XD(reflect)(dir1, dir0, rwalk->hit.normal); - - if(frag->side == SDIS_BACK) { - fX(minus)(dir0, dir0); - fX(minus)(dir1, dir1); - } - - /* Trace dir0/dir1 to adjust the reinjection distance wrt the geometry */ - fX_set_dX(pos, rwalk->vtx.P); - f2(range, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range, &rwalk->hit, &hit1)); - - /* Adjust the delta boundary to the hit distance */ - tmp = MMIN(MMIN(delta_boundary, hit0.distance), hit1.distance); - - if(tmp >= delta_boundary * REINJECT_DST_MIN_SCALE) { - delta_boundary = tmp; - /* Define the orthogonal dst from the reinjection pos to the interface */ - delta = delta_boundary / sqrt(DIM); - } else { /* Switch in 1D reinjection scheme. */ - fX(set)(dir0, rwalk->hit.normal); - if(frag->side == SDIS_BACK) fX(minus)(dir0, dir0); - f2(range, 0, (float)delta*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); - delta_boundary = MMIN(hit0.distance, delta_boundary); - - /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit - * distance in order to avoid infinite bounces for parallel plane */ - if(!SXD_HIT_NONE(&hit0)) { - delta_boundary *= 0.5; - hit0 = SXD_HIT_NULL; - } - - delta = delta_boundary; - dim = 1; - } - - /* Handle the flux */ - delta_in_meter = delta*fp_to_meter; - T->value += phi * delta_in_meter / lambda; - - /* Handle the volumic power */ - power = solid_get_volumic_power(mdm, &rwalk->vtx); - if(power != SDIS_VOLUMIC_POWER_NONE) { - delta_in_meter = delta_boundary * fp_to_meter; - tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); - T->value += tmp; - } - - /* Reinject into the solid */ - XD(move_pos)(rwalk->vtx.P, dir0, (float)delta_boundary); - if(eq_epsf(hit0.distance, (float)delta_boundary, 1.e-4f)) { - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; - rwalk->hit = hit0; - rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(solid_temperature); - rwalk->mdm = mdm; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } -} - -res_T -XD(boundary_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; - const struct sdis_interface* interf = NULL; - const struct sdis_medium* mdm_front = NULL; - const struct sdis_medium* mdm_back = NULL; - const struct sdis_medium* mdm = NULL; - double tmp; - ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); - ASSERT(rwalk->mdm == NULL); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); - - XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); - - fX(normalize)(rwalk->hit.normal, rwalk->hit.normal); - - /* Retrieve the current interface */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - - /* Check if the boundary temperature is known */ - tmp = interface_side_get_temperature(interf, &frag); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - - /* Check if the boundary flux is known. Note that currently, only solid media - * can have a flux as limit condition */ - mdm = interface_get_medium(interf, frag.side); - if(sdis_medium_get_type(mdm) == SDIS_SOLID ) { - const double phi = interface_side_get_flux(interf, &frag); - if(phi != SDIS_FLUX_NONE) { - XD(solid_boundary_with_flux_temperature) - (scn, fp_to_meter, ctx, &frag, phi, rwalk, rng, T); - return RES_OK; - } - } - - mdm_front = interface_get_medium(interf, SDIS_FRONT); - mdm_back = interface_get_medium(interf, SDIS_BACK); - - if(mdm_front->type == mdm_back->type) { - XD(solid_solid_boundary_temperature) - (scn, fp_to_meter, ctx, &frag, rwalk, rng, T); - } else { - XD(solid_fluid_boundary_temperature) - (scn, fp_to_meter, ctx, &frag, rwalk, rng, T); - } - return RES_OK; -} - -#ifdef COMPILER_CL -#pragma warning(push) -#pragma warning(disable : 4701) -/* potentially uninitialized local variable 'info' used - * - * For warning numbers in the range 4700-4999, which are the ones associated - * with code generation, the state of the warning in effect when the compiler - * encounters the open curly brace of a function will be in effect for the rest - * of the function. Using the warning pragma in the function to change the - * state of a warning that has a number larger than 4699 will only take effect - * after the end of the function. The following example shows the correct - * placement of warning pragmas to disable a code-generation warning message, - * and then to restore it. */ -#endif -res_T -XD(solid_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - double position_start[DIM]; - const struct sdis_medium* mdm; - ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_SOLID); - (void)ctx; - - /* Check the random walk consistency */ - CHK(scene_get_medium(scn, rwalk->vtx.P, NULL, &mdm) == RES_OK); - if(mdm != rwalk->mdm) { - log_err(scn->dev, "%s: invalid solid random walk. " - "Unexpected medium at {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - return RES_BAD_OP_IRRECOVERABLE; - } - /* Save the submitted position */ - dX(set)(position_start, rwalk->vtx.P); - - do { /* Solid random walk */ - struct get_medium_info info; - struct sXd(hit) hit0, hit1; - double lambda; /* Thermal conductivity */ - double rho; /* Volumic mass */ - double cp; /* Calorific capacity */ - double tmp; - double power; - float delta, delta_solid; /* Random walk numerical parameter */ - float range[2]; - float dir0[DIM], dir1[DIM]; - float org[DIM]; - - /* Check the limit condition */ - tmp = solid_get_temperature(mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - - /* Fetch solid properties */ - delta_solid = (float)solid_get_delta(mdm, &rwalk->vtx); - lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - rho = solid_get_volumic_mass(mdm, &rwalk->vtx); - cp = solid_get_calorific_capacity(mdm, &rwalk->vtx); - power = solid_get_volumic_power(mdm, &rwalk->vtx); - -#if (SDIS_SOLVE_DIMENSION == 2) - /* Sample a direction around 2PI */ - ssp_ran_circle_uniform_float(rng, dir0, NULL); -#else - /* Sample a direction around 4PI */ - ssp_ran_sphere_uniform_float(rng, dir0, NULL); -#endif - - /* Trace a ray along the sampled direction and its opposite to check if a - * surface is hit in [0, delta_solid]. */ - fX_set_dX(org, rwalk->vtx.P); - fX(minus)(dir1, dir0); - hit0 = hit1 = SXD_HIT_NULL; - range[0] = 0.f, range[1] = delta_solid*RAY_RANGE_MAX_SCALE; - SXD(scene_view_trace_ray(scn->sXd(view), org, dir0, range, NULL, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), org, dir1, range, NULL, &hit1)); - - if(SXD_HIT_NONE(&hit0) && SXD_HIT_NONE(&hit1)) { - /* Hit nothing: move along dir0 of the original delta */ - delta = delta_solid; - - /* Add the volumic power density to the measured temperature */ - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_in_meter = delta * fp_to_meter; - tmp = power * delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); - T->value += tmp; - } - } else { - /* Hit something: move along dir0 of the minimum hit distance */ - delta = MMIN(hit0.distance, hit1.distance); - - /* Add the volumic power density to the measured temperature */ - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_s_in_meter = delta_solid * fp_to_meter; - double h; - double h_in_meter; - double cos_U_N; - float N[DIM]; - - if(delta == hit0.distance) { - fX(normalize)(N, hit0.normal); - cos_U_N = fX(dot)(dir0, N); - } else { - ASSERT(delta == hit1.distance); - fX(normalize)(N, hit1.normal); - cos_U_N = fX(dot)(dir1, N); - } - - h = delta * fabs(cos_U_N); - h_in_meter = h * fp_to_meter; - - /* The regular power term at wall */ - tmp = power * h_in_meter * h_in_meter / (2.0 * lambda); - - /* Add the power corrective term */ - if(h < delta_solid) { - const double sin_a = h / delta_solid; -#if DIM==2 - /* tmp1 = sin(2a) / (PI - 2*a) */ - const double tmp1 = sin_a * sqrt(1 - sin_a*sin_a)/acos(sin_a); - tmp += -(power*delta_s_in_meter*delta_s_in_meter)/(4.0*lambda) * tmp1; -#else - const double tmp1 = (sin_a*sin_a*sin_a - sin_a)/ (1-sin_a); - tmp += (power*delta_s_in_meter*delta_s_in_meter)/(6*lambda) * tmp1; -#endif - - } else if(h == delta_solid) { - tmp += -(delta_s_in_meter*delta_s_in_meter*power)/(2.0*DIM*lambda); - } - T->value += tmp; - } - } - - /* Sample the time */ - if(rwalk->vtx.time != INF) { - double tau, mu, t0; - mu = (2*DIM*lambda) / (rho*cp*delta*fp_to_meter*delta*fp_to_meter); - tau = ssp_ran_exp(rng, mu); - t0 = solid_get_t0(rwalk->mdm); - rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); - if(rwalk->vtx.time == t0) { - /* Check the initial condition */ - tmp = solid_get_temperature(mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - /* The initial condition should have been reached */ - log_err(scn->dev, - "%s: undefined initial condition. " - "The time is %f but the temperature remains unknown.\n", - FUNC_NAME, t0); - return RES_BAD_OP; - } - } - - /* Define if the random walk hits something along dir0 */ - if(hit0.distance > delta) { - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } else { - rwalk->hit = hit0; - rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } - - /* Update the random walk position */ - XD(move_pos)(rwalk->vtx.P, dir0, delta); - - /* Fetch the current medium */ - if(SXD_HIT_NONE(&rwalk->hit)) { - CHK(scene_get_medium(scn, rwalk->vtx.P, &info, &mdm) == RES_OK); - } else { - const struct sdis_interface* interf; - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - mdm = interface_get_medium(interf, rwalk->hit_side); - } - - /* Check random walk consistency */ - if(mdm != rwalk->mdm) { - log_err(scn->dev, - "%s: inconsistent medium during the solid random walk.\n", FUNC_NAME); -#if DIM == 2 - #define VEC_STR "%g %g" - #define VEC_SPLIT SPLIT2 -#else - #define VEC_STR "%g %g %g" - #define VEC_SPLIT SPLIT3 -#endif - log_err(scn->dev, - " start position: " VEC_STR "; current position: " VEC_STR "\n", - VEC_SPLIT(position_start), VEC_SPLIT(rwalk->vtx.P)); - if(SXD_HIT_NONE(&rwalk->hit)) { - float hit_pos[DIM]; - fX(mulf)(hit_pos, info.ray_dir, info.XD(hit).distance); - fX(add)(hit_pos, info.ray_org, hit_pos); - log_err(scn->dev, " ray org: " VEC_STR "; ray dir: " VEC_STR "\n", - VEC_SPLIT(info.ray_org), VEC_SPLIT(info.ray_dir)); - log_err(scn->dev, " targeted point: " VEC_STR "\n", - VEC_SPLIT(info.pos_tgt)); - log_err(scn->dev, " hit pos: " VEC_STR "\n", VEC_SPLIT(hit_pos)); - } -#undef VEC_STR -#undef VEC_SPLIT - return RES_BAD_OP; - } - - /* Keep going while the solid random walk does not hit an interface */ - } while(SXD_HIT_NONE(&rwalk->hit)); - - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ - return RES_OK; -} -#ifdef COMPILER_CL -#pragma warning(pop) -#endif - -static res_T -XD(compute_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ -#ifndef NDEBUG - struct entry { - struct XD(temperature) temperature; - struct XD(rwalk) rwalk; - }* stack = NULL; - size_t istack = 0; -#endif - const size_t max_fails = 10; - res_T res = RES_OK; - ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); - - do { - /* Save the current random walk state */ - const struct XD(rwalk) rwalk_bkp = *rwalk; - const struct XD(temperature) T_bkp = *T; - - size_t nfails = 0; - -#ifndef NDEBUG - struct entry e; - e.temperature = *T; - e.rwalk = *rwalk; - sa_push(stack, e); - ++istack; -#endif - - /* Reject the current step if a BAD_OP occurs and retry up to "max_fails" - * times */ - do { - res = T->func(scn, fp_to_meter, ctx, rwalk, rng, T); - if(res == RES_BAD_OP) { *rwalk = rwalk_bkp; *T = T_bkp; } - } while(res == RES_BAD_OP && ++nfails < max_fails); - if(res != RES_OK) goto error; - - } while(!T->done); - -exit: -#ifndef NDEBUG - sa_release(stack); -#endif - return res == RES_BAD_OP_IRRECOVERABLE ? RES_BAD_OP : res; -error: - goto exit; -} - -static res_T -XD(probe_realisation) - (struct sdis_scene* scn, - struct ssp_rng* rng, - const struct sdis_medium* medium, - const double position[], - const double time_range[2], - const double fp_to_meter,/* Scale factor from floating point unit to meter */ - const double ambient_radiative_temperature, - const double reference_temperature, - double* weight) -{ - struct rwalk_context ctx; - struct XD(rwalk) rwalk = XD(RWALK_NULL); - struct XD(temperature) T = XD(TEMPERATURE_NULL); - double (*get_initial_temperature) - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx); - double t0; - res_T res = RES_OK; - ASSERT(medium && position && fp_to_meter > 0 && weight); - - switch(medium->type) { - case SDIS_FLUID: - T.func = XD(fluid_temperature); - get_initial_temperature = fluid_get_temperature; - t0 = fluid_get_t0(medium); - break; - case SDIS_SOLID: - T.func = XD(solid_temperature); - get_initial_temperature = solid_get_temperature; - t0 = solid_get_t0(medium); - break; - default: FATAL("Unreachable code\n"); break; - } - - dX(set)(rwalk.vtx.P, position); - /* Sample a time */ - rwalk.vtx.time = sample_time(time_range, rng); - if(t0 >= rwalk.vtx.time) { - double tmp; - /* Check the initial condition. */ - rwalk.vtx.time = t0; - tmp = get_initial_temperature(medium, &rwalk.vtx); - if(tmp >= 0) { - *weight = tmp; - return RES_OK; - } - /* The initial condition should have been reached */ - log_err(scn->dev, - "%s: undefined initial condition. " - "The time is %f but the temperature remains unknown.\n", - FUNC_NAME, t0); - return RES_BAD_OP; - } - - rwalk.hit = SXD_HIT_NULL; - rwalk.mdm = medium; - - ctx.Tarad = ambient_radiative_temperature; - ctx.Tref3 = - reference_temperature - * reference_temperature - * reference_temperature; - - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - - *weight = T.value; - return RES_OK; -} - -static res_T -XD(boundary_realisation) - (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[2], - const double time_range[2], - const enum sdis_side side, - const double fp_to_meter, - const double Tarad, - const double Tref, - double* weight) -{ - struct rwalk_context ctx; - struct XD(rwalk) rwalk = XD(RWALK_NULL); - struct XD(temperature) T = XD(TEMPERATURE_NULL); - struct sXd(attrib) attr; -#if SDIS_SOLVE_DIMENSION == 2 - float st; -#else - float st[2]; -#endif - res_T res = RES_OK; - ASSERT(uv && fp_to_meter > 0 && weight && Tref >= 0 - && time_range && time_range[0] >= 0 && time_range[1] >= time_range[0]); - - T.func = XD(boundary_temperature); - rwalk.hit_side = side; - rwalk.hit.distance = 0; - /* Sample a time */ - rwalk.vtx.time = sample_time(time_range, rng); - rwalk.mdm = NULL; /* The random walk is at an interface between 2 media */ - -#if SDIS_SOLVE_DIMENSION == 2 - st = (float)uv[0]; -#else - f2_set_d2(st, uv); -#endif - - /* Fetch the primitive */ - SXD(scene_view_get_primitive - (scn->sXd(view), (unsigned int)iprim, &rwalk.hit.prim)); - - /* Retrieve the world space position of the probe onto the primitive */ - SXD(primitive_get_attrib(&rwalk.hit.prim, SXD_POSITION, st, &attr)); - dX_set_fX(rwalk.vtx.P, attr.value); - - /* Retrieve the primitive normal */ - SXD(primitive_get_attrib(&rwalk.hit.prim, SXD_GEOMETRY_NORMAL, st, &attr)); - fX(set)(rwalk.hit.normal, attr.value); - -#if SDIS_SOLVE_DIMENSION==2 - rwalk.hit.u = st; -#else - f2_set(rwalk.hit.uv, st); -#endif - - ctx.Tarad = Tarad; - ctx.Tref3 = Tref*Tref*Tref; - - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - - *weight = T.value; - return RES_OK; -} - -static res_T -XD(probe_flux_realisation) - (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[DIM], - const double time, - const enum sdis_side solid_side, - const double fp_to_meter, - const double Tarad, - const double Tref, - const char compute_radiative, - const char compute_convective, - double weight[3]) -{ - struct rwalk_context ctx; - struct XD(rwalk) rwalk; - struct XD(temperature) T; - struct sXd(attrib) attr; - struct sXd(primitive) prim; -#if SDIS_SOLVE_DIMENSION == 2 - float st; -#else - float st[2]; -#endif - double P[SDIS_SOLVE_DIMENSION]; - float N[SDIS_SOLVE_DIMENSION]; - const double Tr3 = Tref * Tref * Tref; - const enum sdis_side fluid_side = - (solid_side == SDIS_FRONT) ? SDIS_BACK : SDIS_FRONT; - res_T res = RES_OK; - ASSERT(uv && fp_to_meter > 0 && weight && time >= 0 && Tref >= 0); - -#if SDIS_SOLVE_DIMENSION == 2 - #define SET_PARAM(Dest, Src) (Dest).u = (Src); - st = (float)uv[0]; -#else - #define SET_PARAM(Dest, Src) f2_set((Dest).uv, (Src)); - f2_set_d2(st, uv); -#endif - - /* Fetch the primitive */ - SXD(scene_view_get_primitive(scn->sXd(view), (unsigned int)iprim, &prim)); - - /* Retrieve the world space position of the probe onto the primitive */ - SXD(primitive_get_attrib(&prim, SXD_POSITION, st, &attr)); - dX_set_fX(P, attr.value); - - /* Retrieve the primitive normal */ - SXD(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, st, &attr)); - fX(set)(N, attr.value); - - #define RESET_WALK(Side, Mdm) \ - rwalk = XD(RWALK_NULL); \ - rwalk.hit_side = (Side); \ - rwalk.hit.distance = 0; \ - rwalk.vtx.time = time; \ - rwalk.mdm = (Mdm); \ - SET_PARAM(rwalk.hit, st); \ - ctx.Tarad = Tarad; \ - ctx.Tref3 = Tr3; \ - rwalk.hit.prim = prim; \ - dX(set)(rwalk.vtx.P, P); \ - fX(set)(rwalk.hit.normal, N); \ - T = XD(TEMPERATURE_NULL); - - /* Compute boundary temperature */ - RESET_WALK(solid_side, NULL); - T.func = XD(boundary_temperature); - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - weight[0] = T.value; - - /* Compute radiative temperature */ - if(compute_radiative) { - RESET_WALK(fluid_side, NULL); - T.func = XD(radiative_temperature); - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - weight[1] = T.value; - } - - /* Compute fluid temperature */ - if(compute_convective) { - const struct sdis_interface* interf = - scene_get_interface(scn, (unsigned)iprim); - const struct sdis_medium* mdm = interface_get_medium(interf, fluid_side); - - RESET_WALK(fluid_side, mdm); - T.func = XD(fluid_temperature); - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - weight[2] = T.value; - } - - #undef SET_PARAM - #undef RESET_WALK - - return RES_OK; -} - static INLINE res_T XD(interface_prebuild_fragment) (struct sdis_interface_fragment* frag, @@ -1728,7 +90,8 @@ XD(interface_prebuild_fragment) const unsigned iprim, const double* uv, const enum sdis_side fluid_side) -{ struct sXd(attrib) attr; +{ + struct sXd(attrib) attr; struct sXd(primitive) prim; struct sXd(hit) hit; struct sdis_rwalk_vertex vtx; @@ -1767,7 +130,7 @@ XD(interface_prebuild_fragment) #undef SET_PARAM XD(setup_interface_fragment)(frag, &vtx, &hit, fluid_side); - return res; + return RES_OK; } static res_T @@ -1785,65 +148,278 @@ XD(interface_get_hc_epsilon) res_T res = RES_OK; res = XD(interface_prebuild_fragment)(&frag, scn, iprim, uv, fluid_side); + if(res != RES_OK) return res; + frag.time = time; interf = scene_get_interface(scn, iprim); ASSERT(interf); *epsilon = interface_side_get_emissivity(interf, &frag); *hc = interface_get_convection_coef(interf, &frag); + return RES_OK; +} - return res; +/******************************************************************************* + * Generic solve function + ******************************************************************************/ +static res_T +XD(solve_probe) + (struct sdis_scene* scn, + const size_t nrealisations, + const double position[3], + const double time_range[2], + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double Tarad, /* Ambient radiative temperature */ + const double Tref, /* Reference temperature */ + struct sdis_estimator** out_estimator) +{ + const struct sdis_medium* medium = NULL; + struct sdis_estimator* estimator = NULL; + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** rngs = NULL; + double weight = 0; + double sqr_weight = 0; + const int64_t rcount = (int64_t)nrealisations; + int64_t irealisation = 0; + size_t N = 0; /* #realisations that do not fail */ + size_t i; + ATOMIC res = RES_OK; + + if(!scn || !nrealisations || nrealisations > INT64_MAX || !position + || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) + || fp_to_meter <= 0 || Tref < 0 || !out_estimator) { + res = RES_BAD_ARG; + goto error; + } + +#if SDIS_SOLVE_DIMENSION == 2 + if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } +#else + if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } +#endif + + /* Create the proxy RNG */ + res = ssp_rng_proxy_create(scn->dev->allocator, &ssp_rng_mt19937_64, + scn->dev->nthreads, &rng_proxy); + if(res != RES_OK) goto error; + + /* Create the per thread RNG */ + rngs = MEM_CALLOC + (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); + if(!rngs) { + res = RES_MEM_ERR; + goto error; + } + FOR_EACH(i, 0, scn->dev->nthreads) { + res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); + if(res != RES_OK) goto error; + } + + /* Retrieve the medium in which the submitted position lies */ + res = scene_get_medium(scn, position, NULL, &medium); + if(res != RES_OK) goto error; + + /* Here we go! Launch the Monte Carlo estimation */ + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) + for(irealisation = 0; irealisation < rcount; ++irealisation) { + res_T res_local; + double w = NaN; + double time; + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occured */ + + time = sample_time(rng, time_range); + + res_local = XD(probe_realisation) + (scn, rng, medium, position, time, fp_to_meter, Tarad, Tref, &w); + if(res_local != RES_OK) { + if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + } else { + weight += w; + sqr_weight += w*w; + ++N; + } + } + if(res != RES_OK) goto error; + + /* Create the estimator */ + res = estimator_create + (scn->dev, SDIS_ESTIMATOR_TEMPERATURE, nrealisations, N, &estimator); + if(res != RES_OK) goto error; + + /* Setup the estimated temperature */ + estimator_setup_temperature(estimator, weight, sqr_weight); + +exit: + if(rngs) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(rngs[i]) SSP(rng_ref_put(rngs[i])); + } + MEM_RM(scn->dev->allocator, rngs); + } + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(out_estimator) *out_estimator = estimator; + return (res_T)res; +error: + if(estimator) { + SDIS(estimator_ref_put(estimator)); + estimator = NULL; + } + goto exit; } -#if SDIS_SOLVE_DIMENSION == 3 static res_T -XD(ray_realisation) +XD(solve_probe_boundary) (struct sdis_scene* scn, - struct ssp_rng* rng, - const struct sdis_medium* medium, - const double position[], - const double direction[], - const double time, - const double fp_to_meter, - const double Tarad, - const double Tref, - double* weight) + const size_t nrealisations, /* #realisations */ + const size_t iprim, /* Identifier of the primitive on which the probe lies */ + const double uv[2], /* Parametric coordinates of the probe onto the primitve */ + const double time_range[2], /* Observation time */ + const enum sdis_side side, /* Side of iprim on which the probe lies */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) { - struct rwalk_context ctx; - struct XD(rwalk) rwalk = XD(RWALK_NULL); - struct XD(temperature) T = XD(TEMPERATURE_NULL); - float dir[3]; - res_T res = RES_OK; - ASSERT(scn && position && direction && time>=0 && fp_to_meter>0 && weight - && Tref >= 0); - ASSERT(medium && medium->type == SDIS_FLUID); + struct sdis_estimator* estimator = NULL; + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** rngs = NULL; + double weight = 0; + double sqr_weight = 0; + const int64_t rcount = (int64_t)nrealisations; + int64_t irealisation = 0; + size_t N = 0; /* #realisations that do not fail */ + size_t i; + ATOMIC res = RES_OK; + + if(!scn || !nrealisations || nrealisations > INT64_MAX || !uv + || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) + || fp_to_meter <= 0 || Tref < 0 || (side != SDIS_FRONT && side != SDIS_BACK) + || !out_estimator) { + res = RES_BAD_ARG; + goto error; + } - dX(set)(rwalk.vtx.P, position); - rwalk.vtx.time = time; - rwalk.hit = SXD_HIT_NULL; - rwalk.hit_side = SDIS_SIDE_NULL__; - rwalk.mdm = medium; +#if SDIS_SOLVE_DIMENSION == 2 + if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } +#else + if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } +#endif - ctx.Tarad = Tarad; - ctx.Tref3 = Tref*Tref*Tref; + /* Check the primitive identifier */ + if(iprim >= scene_get_primitives_count(scn)) { + log_err(scn->dev, + "%s: invalid primitive identifier `%lu'. " + "It must be in the [0 %lu] range.\n", + FUNC_NAME, + (unsigned long)iprim, + (unsigned long)scene_get_primitives_count(scn)-1); + res = RES_BAD_ARG; + goto error; + } - f3_set_d3(dir, direction); + /* Check parametric coordinates */ + if(scene_is_2d(scn)) { + const double v = CLAMP(1.0 - uv[0], 0, 1); + if(uv[0] < 0 || uv[0] > 1 || !eq_eps(uv[0] + v, 1, 1.e-6)) { + log_err(scn->dev, + "%s: invalid parametric coordinates %g." + "u + (1-u) must be equal to 1 with u [0, 1].\n", + FUNC_NAME, uv[0]); + res = RES_BAD_ARG; + goto error; + } + } else { + const double w = CLAMP(1 - uv[0] - uv[1], 0, 1); + if(uv[0] < 0 || uv[1] < 0 || uv[0] > 1 || uv[1] > 1 + || !eq_eps(w + uv[0] + uv[1], 1, 1.e-6)) { + log_err(scn->dev, + "%s: invalid parametric coordinates [%g, %g]. " + "u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", + FUNC_NAME, uv[0], uv[1]); + res = RES_BAD_ARG; + goto error; + } + } - res = XD(trace_radiative_path)(scn, dir, fp_to_meter, &ctx, &rwalk, rng, &T); + /* Create the proxy RNG */ + res = ssp_rng_proxy_create(scn->dev->allocator, &ssp_rng_mt19937_64, + scn->dev->nthreads, &rng_proxy); if(res != RES_OK) goto error; - if(!T.done) { - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + /* Create the per thread RNG */ + rngs = MEM_CALLOC + (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); + if(!rngs) { + res = RES_MEM_ERR; + goto error; + } + FOR_EACH(i, 0, scn->dev->nthreads) { + res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); if(res != RES_OK) goto error; } - *weight = T.value; + /* Here we go! Launch the Monte Carlo estimation */ + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) + for(irealisation = 0; irealisation < rcount; ++irealisation) { + res_T res_local; + double w = NaN; + double time; + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + time = sample_time(rng, time_range); + + res_local = XD(boundary_realisation) + (scn, rng, iprim, uv, time, side, fp_to_meter, Tarad, Tref, &w); + if(res_local != RES_OK) { + if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + } else { + weight += w; + sqr_weight += w*w; + ++N; + } + } + if(res != RES_OK) goto error; + + /* Create the estimator */ + res = estimator_create + (scn->dev, SDIS_ESTIMATOR_TEMPERATURE, nrealisations, N, &estimator); + if(res != RES_OK) goto error; + + /* Setup the estimated temperature */ + estimator_setup_temperature(estimator, weight, sqr_weight); exit: - return res; + if(rngs) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(rngs[i]) SSP(rng_ref_put(rngs[i])); + } + MEM_RM(scn->dev->allocator, rngs); + } + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(out_estimator) *out_estimator = estimator; + return (res_T)res; error: + if(estimator) { + SDIS(estimator_ref_put(estimator)); + estimator = NULL; + } goto exit; } -#endif /* SDIS_SOLVE_DIMENSION == 3 */ static res_T XD(solve_boundary) @@ -1874,14 +450,20 @@ XD(solve_boundary) ATOMIC res = RES_OK; if(!scn || !nrealisations || nrealisations > INT64_MAX || !primitives - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || !sides || !nprimitives || fp_to_meter < 0 || Tref < 0 - || !out_estimator) { + || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) + || !sides || !nprimitives || fp_to_meter < 0 || Tref < 0 + || !out_estimator) { res = RES_BAD_ARG; goto error; } +#if SDIS_SOLVE_DIMENSION == 2 + if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } +#else + if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } +#endif + SXD(scene_view_primitives_count(scn->sXd(view), &view_nprims)); FOR_EACH(i, 0, nprimitives) { if(primitives[i] >= view_nprims) { @@ -1896,7 +478,7 @@ XD(solve_boundary) } /* Create the Star-XD shape of the boundary */ -#if DIM == 2 +#if SDIS_SOLVE_DIMENSION == 2 res = s2d_shape_create_line_segments(scn->dev->sXd_dev, &shape); #else res = s3d_shape_create_mesh(scn->dev->sXd_dev, &shape); @@ -1909,11 +491,11 @@ XD(solve_boundary) ctx.view = scn->sXd(view); vdata.usage = SXD_POSITION; vdata.get = XD(boundary_get_position); -#if DIM == 2 +#if SDIS_SOLVE_DIMENSION == 2 vdata.type = S2D_FLOAT2; res = s2d_line_segments_setup_indexed_vertices(shape, (unsigned)nprimitives, boundary_get_indices_2d, (unsigned)(nprimitives*2), &vdata, 1, &ctx); -#else /* DIM == 3 */ +#else vdata.type = S3D_FLOAT3; res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprimitives, boundary_get_indices_3d, (unsigned)(nprimitives*3), &vdata, 1, &ctx); @@ -1941,10 +523,6 @@ XD(solve_boundary) if(res != RES_OK) goto error; } - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_TEMPERATURE_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) for(irealisation=0; irealisation<(int64_t)nrealisations; ++irealisation) { @@ -1956,12 +534,15 @@ XD(solve_boundary) double w = NaN; double uv[DIM-1]; float st[DIM-1]; + double time; res_T res_local = RES_OK; if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + time = sample_time(rng, time_range); + /* Sample a position onto the boundary */ -#if DIM == 2 +#if SDIS_SOLVE_DIMENSION == 2 res_local = s2d_scene_view_sample (view, ssp_rng_canonical_float(rng), @@ -1986,7 +567,7 @@ XD(solve_boundary) /* Invoke the boundary realisation */ res_local = XD(boundary_realisation) - (scn, rng, iprim, uv, time_range, side, fp_to_meter, Tarad, Tref, &w); + (scn, rng, iprim, uv, time, side, fp_to_meter, Tarad, Tref, &w); /* Update the MC accumulators */ if(res_local == RES_OK) { @@ -1999,7 +580,13 @@ XD(solve_boundary) } } - setup_estimator(estimator, nrealisations, N, weight, sqr_weight); + /* Create the estimator */ + res = estimator_create + (scn->dev, SDIS_ESTIMATOR_TEMPERATURE, nrealisations, N, &estimator); + if(res != RES_OK) goto error; + + /* Setup the estimated temperature */ + estimator_setup_temperature(estimator, weight, sqr_weight); exit: if(scene) SXD(scene_ref_put(scene)); @@ -2021,6 +608,196 @@ error: } static res_T +XD(solve_probe_boundary_flux) + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t iprim, /* Identifier of the primitive on which the probe lies */ + const double uv[2], /* Parametric coordinates of the probe onto the primitve */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) +{ + struct sdis_estimator* estimator = NULL; + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** rngs = NULL; + const struct sdis_interface* interf; + const struct sdis_medium *fmd, *bmd; + enum sdis_side solid_side, fluid_side; + struct sdis_interface_fragment frag; + double weight_t = 0, sqr_weight_t = 0; + double weight_fc = 0, sqr_weight_fc = 0; + double weight_fr = 0, sqr_weight_fr = 0; + double weight_f= 0, sqr_weight_f = 0; + const int64_t rcount = (int64_t)nrealisations; + int64_t irealisation = 0; + size_t N = 0; /* #realisations that do not fail */ + size_t i; + ATOMIC res = RES_OK; + + if(!scn || !nrealisations || nrealisations > INT64_MAX || !uv + || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) + || fp_to_meter <= 0 || Tref < 0 + || !out_estimator) { + res = RES_BAD_ARG; + goto error; + } + +#if SDIS_SOLVE_DIMENSION == 2 + if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } +#else + if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } +#endif + + /* Check the primitive identifier */ + if(iprim >= scene_get_primitives_count(scn)) { + log_err(scn->dev, + "%s: invalid primitive identifier `%lu'. " + "It must be in the [0 %lu] range.\n", + FUNC_NAME, + (unsigned long)iprim, + (unsigned long)scene_get_primitives_count(scn)-1); + res = RES_BAD_ARG; + goto error; + } + + /* Check parametric coordinates */ + if(scene_is_2d(scn)) { + const double v = CLAMP(1.0 - uv[0], 0, 1); + if(uv[0] < 0 || uv[0] > 1 || !eq_eps(uv[0] + v, 1, 1.e-6)) { + log_err(scn->dev, + "%s: invalid parametric coordinates %g. " + "u + (1-u) must be equal to 1 with u [0, 1].\n", + FUNC_NAME, uv[0]); + res = RES_BAD_ARG; + goto error; + } + } else { + const double w = CLAMP(1 - uv[0] - uv[1], 0, 1); + if(uv[0] < 0 || uv[1] < 0 || uv[0] > 1 || uv[1] > 1 + || !eq_eps(w + uv[0] + uv[1], 1, 1.e-6)) { + log_err(scn->dev, + "%s: invalid parametric coordinates [%g, %g]. " + "u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", + FUNC_NAME, uv[0], uv[1]); + res = RES_BAD_ARG; + goto error; + } + } + /* Check medium is fluid on one side and solid on the other */ + interf = scene_get_interface(scn, (unsigned)iprim); + fmd = interface_get_medium(interf, SDIS_FRONT); + bmd = interface_get_medium(interf, SDIS_BACK); + if(!fmd || !bmd + || ( !(fmd->type == SDIS_FLUID && bmd->type == SDIS_SOLID) + && !(fmd->type == SDIS_SOLID && bmd->type == SDIS_FLUID))) { + res = RES_BAD_ARG; + goto error; + } + solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; + fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; + + /* Create the proxy RNG */ + res = ssp_rng_proxy_create(scn->dev->allocator, &ssp_rng_mt19937_64, + scn->dev->nthreads, &rng_proxy); + if(res != RES_OK) goto error; + + /* Create the per thread RNG */ + rngs = MEM_CALLOC + (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); + if(!rngs) { + res = RES_MEM_ERR; + goto error; + } + FOR_EACH(i, 0, scn->dev->nthreads) { + res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs + i); + if(res != RES_OK) goto error; + } + + /* Prebuild the interface fragment */ + res = XD(interface_prebuild_fragment) + (&frag, scn, (unsigned)iprim, uv, fluid_side); + + /* Here we go! Launch the Monte Carlo estimation */ + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) reduction(+:weight_t,sqr_weight_t,\ + weight_fc,sqr_weight_fc,weight_fr,sqr_weight_fr,weight_f,sqr_weight_f,N) + for(irealisation = 0; irealisation < rcount; ++irealisation) { + res_T res_local; + double T_brf[3] = { 0, 0, 0 }; + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + double time, epsilon, hc, hr; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + time = sample_time(rng, time_range); + + /* Compute hr and hc */ + frag.time = time; + epsilon = interface_side_get_emissivity(interf, &frag); + hc = interface_get_convection_coef(interf, &frag); + hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; + + /* Fluid, Radiative and Solid temperatures */ + res_local = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, + solid_side, fp_to_meter, Tarad, Tref, hr>0, hc>0, T_brf); + if(res_local != RES_OK) { + if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + } else { + const double Tboundary = T_brf[0]; + const double Tradiative = T_brf[1]; + const double Tfluid = T_brf[2]; + const double w_conv = hc * (Tboundary - Tfluid); + const double w_rad = hr * (Tboundary - Tradiative); + const double w_total = w_conv + w_rad; + weight_t += Tboundary; + sqr_weight_t += Tboundary * Tboundary; + weight_fc += w_conv; + sqr_weight_fc += w_conv * w_conv; + weight_fr += w_rad; + sqr_weight_fr += w_rad * w_rad; + weight_f += w_total; + sqr_weight_f += w_total * w_total; + ++N; + } + } + if(res != RES_OK) goto error; + + /* Create the estimator */ + res = estimator_create + (scn->dev, SDIS_ESTIMATOR_FLUX, nrealisations, N, &estimator); + if(res != RES_OK) goto error; + + /* Setup the estimated values */ + estimator_setup_temperature(estimator, weight_t, sqr_weight_t); + estimator_setup_flux(estimator, FLUX_CONVECTIVE, weight_fc, sqr_weight_fc); + estimator_setup_flux(estimator, FLUX_RADIATIVE, weight_fr, sqr_weight_fr); + estimator_setup_flux(estimator, FLUX_TOTAL, weight_f, sqr_weight_f); + +exit: + if(rngs) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(rngs[i]) SSP(rng_ref_put(rngs[i])); + } + MEM_RM(scn->dev->allocator, rngs); + } + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(out_estimator) *out_estimator = estimator; + return (res_T)res; +error: + if(estimator) { + SDIS(estimator_ref_put(estimator)); + estimator = NULL; + } + goto exit; +} +static res_T XD(solve_boundary_flux) (struct sdis_scene* scn, const size_t nrealisations, /* #realisations */ @@ -2051,14 +828,20 @@ XD(solve_boundary_flux) ATOMIC res = RES_OK; if(!scn || !nrealisations || nrealisations > INT64_MAX || !primitives - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || !nprimitives || fp_to_meter < 0 || Tref < 0 - || !out_estimator) { + || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) + || !nprimitives || fp_to_meter < 0 || Tref < 0 + || !out_estimator) { res = RES_BAD_ARG; goto error; } +#if SDIS_SOLVE_DIMENSION == 2 + if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } +#else + if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } +#endif + SXD(scene_view_primitives_count(scn->sXd(view), &view_nprims)); FOR_EACH(i, 0, nprimitives) { if(primitives[i] >= view_nprims) { @@ -2073,7 +856,7 @@ XD(solve_boundary_flux) } /* Create the Star-XD shape of the boundary */ -#if DIM == 2 +#if SDIS_SOLVE_DIMENSION == 2 res = s2d_shape_create_line_segments(scn->dev->s2d, &shape); #else res = s3d_shape_create_mesh(scn->dev->s3d, &shape); @@ -2085,16 +868,16 @@ XD(solve_boundary_flux) ctx.primitives = primitives; ctx.view = scn->sXd(view); vdata.get = XD(boundary_get_position); -#if DIM == 2 +#if SDIS_SOLVE_DIMENSION == 2 vdata.usage = S2D_POSITION; vdata.type = S2D_FLOAT2; res = s2d_line_segments_setup_indexed_vertices(shape, (unsigned)nprimitives, - boundary_get_indices_2d, (unsigned)(nprimitives*2), &vdata, 1, &ctx); + XD(boundary_get_indices), (unsigned)(nprimitives*2), &vdata, 1, &ctx); #else /* DIM == 3 */ vdata.usage = S3D_POSITION; vdata.type = S3D_FLOAT3; res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprimitives, - boundary_get_indices_3d, (unsigned)(nprimitives*3), &vdata, 1, &ctx); + XD(boundary_get_indices), (unsigned)(nprimitives*3), &vdata, 1, &ctx); #endif if(res != RES_OK) goto error; @@ -2119,10 +902,6 @@ XD(solve_boundary_flux) if(res != RES_OK) goto error; } - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_FLUX_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) reduction(+:weight_t,sqr_weight_t,\ weight_fc,sqr_weight_fc,weight_fr,sqr_weight_fr,weight_f,sqr_weight_f,N) @@ -2143,11 +922,10 @@ XD(solve_boundary_flux) if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - /* Sample a time */ - time = sample_time(time_range, rng); + time = sample_time(rng, time_range); /* Sample a position onto the boundary */ -#if DIM == 2 +#if SDIS_SOLVE_DIMENSION == 2 res_local = s2d_scene_view_sample (view, ssp_rng_canonical_float(rng), @@ -2191,7 +969,7 @@ XD(solve_boundary_flux) hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; /* Fluid, Radiative and Solid temperatures */ - res_local = XD(probe_flux_realisation)(scn, rng, iprim, uv, time, + res_local = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, solid_side, fp_to_meter, Tarad, Tref, hr > 0, hc > 0, T_brf); if(res_local != RES_OK) { if(res_local != RES_BAD_OP) { @@ -2218,10 +996,16 @@ XD(solve_boundary_flux) } if(res != RES_OK) goto error; - setup_estimator(estimator, nrealisations, N, weight_t, sqr_weight_t); - setup_estimator_flux(estimator, FLUX_CONVECTIVE__, weight_fc, sqr_weight_fc); - setup_estimator_flux(estimator, FLUX_RADIATIVE__, weight_fr, sqr_weight_fr); - setup_estimator_flux(estimator, FLUX_TOTAL__, weight_f, sqr_weight_f); + /* Create the estimator */ + res = estimator_create + (scn->dev, SDIS_ESTIMATOR_FLUX, nrealisations, N, &estimator); + if(res != RES_OK) goto error; + + /* Setup the estimated values */ + estimator_setup_temperature(estimator, weight_t, sqr_weight_t); + estimator_setup_flux(estimator, FLUX_CONVECTIVE, weight_fc, sqr_weight_fc); + estimator_setup_flux(estimator, FLUX_RADIATIVE, weight_fr, sqr_weight_fr); + estimator_setup_flux(estimator, FLUX_TOTAL, weight_f, sqr_weight_f); exit: if(scene) SXD(scene_ref_put(scene)); @@ -2242,24 +1026,4 @@ error: goto exit; } -#undef SDIS_SOLVE_DIMENSION -#undef DIM -#undef sXd -#undef sXd_dev -#undef SXD_HIT_NONE -#undef SXD_HIT_NULL -#undef SXD_HIT_NULL__ -#undef SXD_POSITION -#undef SXD_GEOMETRY_NORMAL -#undef SXD_VERTEX_DATA_NULL -#undef SXD_FLOAT2 -#undef SXD_FLOAT3 -#undef SXD_SAMPLE -#undef SXD -#undef dX -#undef fX -#undef fX_set_dX -#undef XD - -#endif /* !SDIS_SOLVE_DIMENSION */ - +#include "sdis_Xd_end.h" diff --git a/src/sdis_solve_radiative.c b/src/sdis_solve_radiative.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * 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 SDIS_SOLVE_DIMENSION 2 +#include "sdis_solve_Xd_radiative.h" + +#define SDIS_SOLVE_DIMENSION 3 +#include "sdis_solve_Xd_radiative.h" diff --git a/src/test_sdis_solve_boundary_flux.c b/src/test_sdis_solve_boundary_flux.c @@ -195,7 +195,7 @@ check_estimator printf("T = %g ~ %g +/- %g\n", T, V.E, V.SE); CHK(eq_eps(V.E, T, 3 * (V.SE ? V.SE : FLT_EPSILON))); OK(sdis_estimator_get_type(estimator, &type)); - if(type == SDIS_FLUX_ESTIMATOR) { + if(type == SDIS_ESTIMATOR_FLUX) { OK(sdis_estimator_get_convective_flux(estimator, &V)); printf("Convective flux = %g ~ %g +/- %g\n", CF, V.E, V.SE); CHK(eq_eps(V.E, CF, 3 * (V.SE ? V.SE : FLT_EPSILON))); @@ -366,7 +366,7 @@ main(int argc, char** argv) OK(SOLVE(box_scn, N, iprim, uv, time_range, 1.0, Trad, Tref, &estimator)); OK(sdis_estimator_get_type(estimator, &type)); - CHK(type == SDIS_FLUX_ESTIMATOR); + CHK(type == SDIS_ESTIMATOR_FLUX); OK(sdis_scene_get_boundary_position(box_scn, iprim, uv, pos)); printf("Boundary values of the box at (%g %g %g) = ", SPLIT3(pos)); diff --git a/src/test_sdis_solve_probe.c b/src/test_sdis_solve_probe.c @@ -272,7 +272,7 @@ main(int argc, char** argv) BA(sdis_estimator_get_type(estimator, NULL)); BA(sdis_estimator_get_type(NULL, &type)); OK(sdis_estimator_get_type(estimator, &type)); - CHK(type == SDIS_TEMPERATURE_ESTIMATOR); + CHK(type == SDIS_ESTIMATOR_TEMPERATURE); /* Fluxes aren't available after sdis_solve_probe */ BA(sdis_estimator_get_convective_flux(estimator, NULL));