stardis-solver

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

commit 7430763d68f3ef2e1254248eedaf77c6d9183c8b
parent b938696bb35be14e90c748a2bb5935c9a62c9d40
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri,  8 Mar 2019 16:09:20 +0100

Clean-up and refactoring of the solvers

Move probe, boundary, and probe_boundary solvers in separate files.

Diffstat:
Mcmake/CMakeLists.txt | 4+++-
Msrc/sdis_Xd_begin.h | 5+++++
Msrc/sdis_Xd_end.h | 6++++++
Msrc/sdis_green.c | 1-
Msrc/sdis_heat_path.c | 8++++----
Msrc/sdis_interface.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_interface_c.h | 16++++++++++++++++
Msrc/sdis_solve.c | 24++++++++++++++++++------
Dsrc/sdis_solve_Xd.h | 1100-------------------------------------------------------------------------------
Asrc/sdis_solve_boundary_Xd.h | 498+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_solve_probe_Xd.h | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_solve_probe_boundary_Xd.h | 378+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
12 files changed, 1240 insertions(+), 1112 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -93,8 +93,10 @@ set(SDIS_FILES_INC sdis_realisation_Xd.h sdis_scene_c.h sdis_scene_Xd.h - sdis_solve_Xd.h + sdis_solve_boundary_Xd.h sdis_solve_medium_Xd.h + sdis_solve_probe_Xd.h + sdis_solve_probe_boundary_Xd.h sdis_Xd_begin.h sdis_Xd_end.h) diff --git a/src/sdis_Xd_begin.h b/src/sdis_Xd_begin.h @@ -33,6 +33,11 @@ static const struct rwalk_context RWALK_CONTEXT_NULL = RWALK_CONTEXT_NULL__; #endif /* SDIS_XD_BEGIN_H */ +#ifdef SDIS_XD_BEGIN_H__ + #error "This header is already included without its associated sdis_Xd_end.h file." +#endif +#define SDIS_XD_BEGIN_H__ + /* Check prerequisite */ #ifndef SDIS_XD_DIMENSION #error "The SDIS_XD_DIMENSION macro must be defined." diff --git a/src/sdis_Xd_end.h b/src/sdis_Xd_end.h @@ -13,6 +13,10 @@ * 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__ + #error "The sdis_Xd_begin.h file must be included priorly to this file." +#endif + #undef SDIS_XD_DIMENSION #undef DIM @@ -33,3 +37,5 @@ #undef fX #undef fX_set_dX #undef dX_set_fX + +#undef SDIS_XD_BEGIN_H__ diff --git a/src/sdis_green.c b/src/sdis_green.c @@ -975,4 +975,3 @@ error: goto exit; } - diff --git a/src/sdis_heat_path.c b/src/sdis_heat_path.c @@ -15,25 +15,25 @@ #include "sdis_heat_path.h" -/* Generate the radiative paths */ +/* Generate the radiative path routines */ #define SDIS_XD_DIMENSION 2 #include "sdis_heat_path_radiative_Xd.h" #define SDIS_XD_DIMENSION 3 #include "sdis_heat_path_radiative_Xd.h" -/* Generate the convective paths */ +/* Generate the convective path routines */ #define SDIS_XD_DIMENSION 2 #include "sdis_heat_path_convective_Xd.h" #define SDIS_XD_DIMENSION 3 #include "sdis_heat_path_convective_Xd.h" -/* Generate the conductive paths */ +/* Generate the conductive path routines */ #define SDIS_XD_DIMENSION 2 #include "sdis_heat_path_conductive_Xd.h" #define SDIS_XD_DIMENSION 3 #include "sdis_heat_path_conductive_Xd.h" -/* Generate the boundary paths */ +/* Generate the boundary path routines */ #define SDIS_XD_DIMENSION 2 #include "sdis_heat_path_boundary_Xd.h" #define SDIS_XD_DIMENSION 3 diff --git a/src/sdis_interface.c b/src/sdis_interface.c @@ -16,6 +16,7 @@ #include "sdis.h" #include "sdis_device_c.h" #include "sdis_interface_c.h" +#include "sdis_scene_c.h" #include <rsys/double2.h> #include <rsys/double3.h> @@ -259,3 +260,92 @@ setup_interface_fragment_3d frag->side = side; } +res_T +build_interface_fragment_2d + (struct sdis_interface_fragment* frag, + const struct sdis_scene* scn, + const unsigned iprim, + const double* uv, + const enum sdis_side side) +{ + struct s2d_attrib attr_P, attr_N; + struct s2d_primitive prim = S2D_PRIMITIVE_NULL; + struct s2d_hit hit = S2D_HIT_NULL; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + float st; + res_T res = RES_OK; + + ASSERT(frag && scn && uv && scene_is_2d(scn)); + ASSERT(side == SDIS_FRONT || side == SDIS_BACK); + + st = (float)uv[0]; + + #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 + CALL(s2d_scene_view_get_primitive(scn->s2d_view, iprim, &prim)); + CALL(s2d_primitive_get_attrib(&prim, S2D_POSITION, st, &attr_P)); + CALL(s2d_primitive_get_attrib(&prim, S2D_GEOMETRY_NORMAL, st, &attr_N)); + #undef CALL + + vtx.P[0] = attr_P.value[0]; + vtx.P[1] = attr_P.value[1]; + vtx.time = NaN; + hit.normal[0] = attr_N.value[0]; + hit.normal[1] = attr_N.value[1]; + hit.distance = 0; + hit.prim = prim; + + setup_interface_fragment_2d(frag, &vtx, &hit, side); + +exit: + return res; +error: + *frag = SDIS_INTERFACE_FRAGMENT_NULL; + goto exit; +} + +res_T +build_interface_fragment_3d + (struct sdis_interface_fragment* frag, + const struct sdis_scene* scn, + const unsigned iprim, + const double* uv, + const enum sdis_side side) +{ + struct s3d_attrib attr_P, attr_N; + struct s3d_primitive prim = S3D_PRIMITIVE_NULL; + struct s3d_hit hit = S3D_HIT_NULL; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + float st[2]; + res_T res = RES_OK; + + ASSERT(frag && scn && uv && !scene_is_2d(scn)); + ASSERT(side == SDIS_FRONT || side == SDIS_BACK); + + st[0] = (float)uv[0]; + st[1] = (float)uv[1]; + + #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 + CALL(s3d_scene_view_get_primitive(scn->s3d_view, iprim, &prim)); + CALL(s3d_primitive_get_attrib(&prim, S3D_POSITION, st, &attr_P)); + CALL(s3d_primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attr_N)); + #undef CALL + + vtx.P[0] = attr_P.value[0]; + vtx.P[1] = attr_P.value[1]; + vtx.P[2] = attr_P.value[2]; + vtx.time = NaN; + hit.normal[0] = attr_N.value[0]; + hit.normal[1] = attr_N.value[1]; + hit.normal[2] = attr_N.value[2]; + hit.distance = 0; + hit.prim = prim; + + setup_interface_fragment_3d(frag, &vtx, &hit, side); + +exit: + return res; +error: + *frag = SDIS_INTERFACE_FRAGMENT_NULL; + goto exit; +} + diff --git a/src/sdis_interface_c.h b/src/sdis_interface_c.h @@ -62,6 +62,22 @@ setup_interface_fragment_3d const struct s3d_hit* hit, const enum sdis_side side); +extern LOCAL_SYM res_T +build_interface_fragment_2d + (struct sdis_interface_fragment* frag, + const struct sdis_scene* scn, + const unsigned iprim, + const double uv[1], + const enum sdis_side side); + +extern LOCAL_SYM res_T +build_interface_fragment_3d + (struct sdis_interface_fragment* frag, + const struct sdis_scene* scn, + const unsigned iprim, + const double uv[2], + const enum sdis_side side); + static INLINE double interface_get_convection_coef (const struct sdis_interface* interf, diff --git a/src/sdis_solve.c b/src/sdis_solve.c @@ -19,11 +19,26 @@ #include "sdis_estimator_c.h" #include "sdis_interface_c.h" -/* Generate the solvers */ +#include <star/ssp.h> +#include <omp.h> + +/* Generate the probe solvers */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_solve_probe_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_solve_probe_Xd.h" + +/* Generate the probe boundary solvers */ #define SDIS_XD_DIMENSION 2 -#include "sdis_solve_Xd.h" +#include "sdis_solve_probe_boundary_Xd.h" #define SDIS_XD_DIMENSION 3 -#include "sdis_solve_Xd.h" +#include "sdis_solve_probe_boundary_Xd.h" + +/* Generate the boundary solvers */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_solve_boundary_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_solve_boundary_Xd.h" /* Generate the medium solvers */ #define SDIS_XD_DIMENSION 2 @@ -31,9 +46,6 @@ #define SDIS_XD_DIMENSION 3 #include "sdis_solve_medium_Xd.h" -#include <star/ssp.h> -#include <omp.h> - /******************************************************************************* * Helper functions ******************************************************************************/ diff --git a/src/sdis_solve_Xd.h b/src/sdis_solve_Xd.h @@ -1,1100 +0,0 @@ -/* 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_estimator_c.h" -#include "sdis_green.h" -#include "sdis_medium_c.h" -#include "sdis_misc.h" -#include "sdis_realisation.h" -#include "sdis_scene_c.h" - -#include <star/ssp.h> -#include <omp.h> - -#include "sdis_Xd_begin.h" - -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 -}; -/******************************************************************************* - * Helper functions - ******************************************************************************/ - -static INLINE void -XD(boundary_get_indices)(const unsigned iprim, unsigned ids[DIM], void* context) -{ - unsigned i; - (void)context; - ASSERT(ids); - FOR_EACH(i, 0, DIM) ids[i] = iprim*DIM + i; -} - -static INLINE void -XD(boundary_get_position)(const unsigned ivert, float pos[DIM], void* context) -{ - struct XD(boundary_context)* ctx = context; - struct sXd(primitive) prim; - struct sXd(attrib) attr; - const unsigned iprim_id = ivert / DIM; - const unsigned iprim_vert = ivert % DIM; - unsigned iprim; - ASSERT(pos && context); - - iprim = (unsigned)ctx->primitives[iprim_id]; - SXD(scene_view_get_primitive(ctx->view, iprim, &prim)); -#if DIM == 2 - s2d_segment_get_vertex_attrib(&prim, iprim_vert, S2D_POSITION, &attr); - ASSERT(attr.type == S2D_FLOAT2); -#else - s3d_triangle_get_vertex_attrib(&prim, iprim_vert, S3D_POSITION, &attr); - ASSERT(attr.type == S3D_FLOAT3); -#endif - fX(set)(pos, attr.value); -} - -static INLINE res_T -XD(interface_prebuild_fragment) - (struct sdis_interface_fragment* frag, - const struct sdis_scene* scn, - const unsigned iprim, - const double* uv, - const enum sdis_side fluid_side) -{ - struct sXd(attrib) attr; - struct sXd(primitive) prim; - struct sXd(hit) hit; - struct sdis_rwalk_vertex vtx; -#if SDIS_XD_DIMENSION == 2 - float st; -#else - float st[2]; -#endif - res_T res = RES_OK; - - ASSERT(frag && scn && uv); - ASSERT(fluid_side == SDIS_FRONT || fluid_side == SDIS_BACK); - - *frag = SDIS_INTERFACE_FRAGMENT_NULL; - -#if SDIS_XD_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 - res = sXd(scene_view_get_primitive(scn->sXd(view), iprim, &prim)); - if(res != RES_OK) return res; - res = sXd(primitive_get_attrib(&prim, SXD_POSITION, st, &attr)); - if(res != RES_OK) return res; - dX_set_fX(vtx.P, attr.value); - res = sXd(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, st, &attr)); - if(res != RES_OK) return res; - fX(set)(hit.normal, attr.value); - - hit.distance = 0; - hit.prim = prim; - vtx.time = NaN; - SET_PARAM(hit, st); - #undef SET_PARAM - XD(setup_interface_fragment)(frag, &vtx, &hit, fluid_side); - - return RES_OK; -} - -static res_T -XD(interface_get_hc_epsilon) - (double *hc, - double* epsilon, - const struct sdis_scene* scn, - const unsigned iprim, - const double* uv, - const double time, - const enum sdis_side fluid_side) -{ - struct sdis_interface_fragment frag; - const struct sdis_interface* interf; - 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; -} - -/******************************************************************************* - * 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 */ - const int register_paths, /* Combination of enum sdis_heat_path_flag */ - struct sdis_green_function** out_green, /* May be NULL <=> No green func */ - struct sdis_estimator** out_estimator) -{ - struct sdis_medium* medium = NULL; - struct sdis_estimator* estimator = NULL; - struct sdis_green_function* green = NULL; - struct sdis_green_function** greens = 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 - || fp_to_meter <= 0 || Tref < 0) { - res = RES_BAD_ARG; - goto error; - } - if(!out_estimator && !out_green) { - res = RES_BAD_ARG; - goto error; - } - if(out_estimator) { - if(!time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1])) { - res = RES_BAD_ARG; - goto error; - } - } - -#if SDIS_XD_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(*rngs)); - 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; - - if(out_green) { - /* Create the per thread green function */ - greens = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*greens)); - if(!greens) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = green_function_create(scn->dev, &greens[i]); - if(res != RES_OK) goto error; - } - - } - - /* Create the estimator */ - if(out_estimator) { - res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &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; - double time; - const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - struct green_path_handle* pgreen_path = NULL; - struct green_path_handle green_path = GREEN_PATH_HANDLE_NULL; - struct sdis_heat_path* pheat_path = NULL; - struct sdis_heat_path heat_path; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - - if(!out_green) { - time = sample_time(rng, time_range); - if(register_paths) { - heat_path_init(scn->dev->allocator, &heat_path); - pheat_path = &heat_path; - } - } else { - /* Do not take care of the submitted time when registering the green - * function. Simply takes 0 as relative time */ - time = 0; - res_local = green_function_create_path(greens[ithread], &green_path); - if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } - - pgreen_path = &green_path; - } - - res_local = XD(probe_realisation)((size_t)irealisation, scn, rng, medium, - position, time, fp_to_meter, Tarad, Tref, pgreen_path, pheat_path, &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(pheat_path) { - pheat_path->status = res_local == RES_OK - ? SDIS_HEAT_PATH_SUCCEED - : SDIS_HEAT_PATH_FAILED; - - /* Check if the path must be saved regarding the register_paths mask */ - if(!(register_paths & (int)pheat_path->status)) { - heat_path_release(pheat_path); - } else { /* Register the sampled path */ - res_local = estimator_add_and_release_heat_path(estimator, pheat_path); - if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } - } - } - } - if(res != RES_OK) goto error; - - /* Setup the estimated temperature */ - if(out_estimator) { - estimator_setup_realisations_count(estimator, nrealisations, N); - estimator_setup_temperature(estimator, weight, sqr_weight); - } - - if(out_green) { - green = greens[0]; /* Return the green of the 1st thread */ - greens[0] = NULL; /* Make invalid the 1st green for 'on exit' clean up*/ - FOR_EACH(i, 1, scn->dev->nthreads) { /* Merge the per thread green */ - res = green_function_merge_and_clear(green, greens[i]); - if(res != RES_OK) goto error; - } - - /* Finalize the estimated green */ - res = green_function_finalize(green, rng_proxy); - if(res != RES_OK) goto error; - } - -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(greens) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(greens[i]) SDIS(green_function_ref_put(greens[i])); - } - MEM_RM(scn->dev->allocator, greens); - } - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_green) *out_green = green; - if(out_estimator) *out_estimator = estimator; - return (res_T)res; -error: - if(green) { - SDIS(green_function_ref_put(green)); - green = NULL; - } - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } - goto exit; -} - -static res_T -XD(solve_probe_boundary) - (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 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 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; - } - -#if SDIS_XD_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; - } - } - - /* 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_ESTIMATOR_TEMPERATURE, &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; - 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; - - /* Setup the estimated temperature */ - estimator_setup_realisations_count(estimator, nrealisations, N); - 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; -} - -static res_T -XD(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) -{ - struct XD(boundary_context) ctx = XD(BOUNDARY_CONTEXT_NULL); - struct sXd(vertex_data) vdata = SXD_VERTEX_DATA_NULL; - struct sXd(scene)* scene = NULL; - struct sXd(shape)* shape = NULL; - struct sXd(scene_view)* view = NULL; - struct sdis_estimator* estimator = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - size_t i; - size_t N = 0; /* #realisations that do not fail */ - size_t view_nprims; - double weight=0, sqr_weight=0; - int64_t irealisation; - 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) { - res = RES_BAD_ARG; - goto error; - } - -#if SDIS_XD_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) { - log_err(scn->dev, - "%s: invalid primitive identifier `%lu'. It must be in the [0 %lu] range.\n", - FUNC_NAME, - (unsigned long)primitives[i], - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } - } - - /* Create the Star-XD shape of the boundary */ -#if SDIS_XD_DIMENSION == 2 - res = s2d_shape_create_line_segments(scn->dev->sXd_dev, &shape); -#else - res = s3d_shape_create_mesh(scn->dev->sXd_dev, &shape); -#endif - if(res != RES_OK) goto error; - - /* Initialise the boundary shape with the triangles/segments of the - * submitted primitives */ - ctx.primitives = primitives; - ctx.view = scn->sXd(view); - vdata.usage = SXD_POSITION; - vdata.get = XD(boundary_get_position); -#if SDIS_XD_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 - vdata.type = S3D_FLOAT3; - res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprimitives, - boundary_get_indices_3d, (unsigned)(nprimitives*3), &vdata, 1, &ctx); -#endif - if(res != RES_OK) goto error; - - /* Create and setup the boundary Star-XD scene */ - res = sXd(scene_create)(scn->dev->sXd_dev, &scene); - if(res != RES_OK) goto error; - res = sXd(scene_attach_shape)(scene, shape); - if(res != RES_OK) goto error; - res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); - if(res != RES_OK) 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(*rngs)); - 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_ESTIMATOR_TEMPERATURE, &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) { - const int ithread = omp_get_thread_num(); - struct sXd(primitive) prim; - struct ssp_rng* rng = rngs[ithread]; - enum sdis_side side; - size_t iprim; - 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 SDIS_XD_DIMENSION == 2 - res_local = s2d_scene_view_sample - (view, - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, st); - uv[0] = (double)st[0]; -#else - res_local = s3d_scene_view_sample - (view, - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, st); - d2_set_f2(uv, st); -#endif - if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } - - /* Map from boundary scene to sdis scene */ - ASSERT(prim.prim_id < nprimitives); - iprim = primitives[prim.prim_id]; - side = sides[prim.prim_id]; - - /* Invoke the boundary realisation */ - res_local = XD(boundary_realisation) - (scn, rng, iprim, uv, time, side, fp_to_meter, Tarad, Tref, &w); - - /* Update the MC accumulators */ - if(res_local == RES_OK) { - weight += w; - sqr_weight += w*w; - ++N; - } else if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } - - /* Setup the estimated temperature */ - estimator_setup_realisations_count(estimator, nrealisations, N); - estimator_setup_temperature(estimator, weight, sqr_weight); - -exit: - if(scene) SXD(scene_ref_put(scene)); - if(shape) SXD(shape_ref_put(shape)); - if(view) SXD(scene_view_ref_put(view)); - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_estimator) *out_estimator = estimator; - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) {if(rngs[i]) SSP(rng_ref_put(rngs[i]));} - MEM_RM(scn->dev->allocator, rngs); - } - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } - goto exit; -} - -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_XD_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); - - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_ESTIMATOR_FLUX, &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; - int flux_mask = 0; - - 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 */ - flux_mask = 0; - if(hr > 0) flux_mask |= FLUX_FLAG_RADIATIVE; - if(hc > 0) flux_mask |= FLUX_FLAG_CONVECTIVE; - res_local = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, - solid_side, fp_to_meter, Tarad, Tref, flux_mask, 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 the estimated values */ - estimator_setup_realisations_count(estimator, nrealisations, N); - 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 */ - 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) -{ - struct XD(boundary_context) ctx = XD(BOUNDARY_CONTEXT_NULL); - struct sXd(vertex_data) vdata = SXD_VERTEX_DATA_NULL; - struct sXd(scene)* scene = NULL; - struct sXd(shape)* shape = NULL; - struct sXd(scene_view)* view = NULL; - struct sdis_estimator* estimator = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - 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; - size_t i; - size_t N = 0; /* #realisations that do not fail */ - size_t view_nprims; - int64_t irealisation; - 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) { - res = RES_BAD_ARG; - goto error; - } - -#if SDIS_XD_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) { - log_err(scn->dev, - "%s: invalid primitive identifier `%lu'. It must be in the [0 %lu] range.\n", - FUNC_NAME, - (unsigned long)primitives[i], - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } - } - - /* Create the Star-XD shape of the boundary */ -#if SDIS_XD_DIMENSION == 2 - res = s2d_shape_create_line_segments(scn->dev->s2d, &shape); -#else - res = s3d_shape_create_mesh(scn->dev->s3d, &shape); -#endif - if(res != RES_OK) goto error; - - /* Initialise the boundary shape with the triangles/segments of the - * submitted primitives */ - ctx.primitives = primitives; - ctx.view = scn->sXd(view); - vdata.get = XD(boundary_get_position); -#if SDIS_XD_DIMENSION == 2 - vdata.usage = S2D_POSITION; - vdata.type = S2D_FLOAT2; - res = s2d_line_segments_setup_indexed_vertices(shape, (unsigned)nprimitives, - 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, - XD(boundary_get_indices), (unsigned)(nprimitives*3), &vdata, 1, &ctx); -#endif - if(res != RES_OK) goto error; - - /* Create and setup the boundary Star-XD scene */ - res = sXd(scene_create)(scn->dev->sXd_dev, &scene); - if(res != RES_OK) goto error; - res = sXd(scene_attach_shape)(scene, shape); - if(res != RES_OK) goto error; - res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); - if(res != RES_OK) 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(*rngs)); - 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_ESTIMATOR_FLUX, &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) - for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { - const int ithread = omp_get_thread_num(); - struct sXd(primitive) prim; - struct ssp_rng* rng = rngs[ithread]; - const struct sdis_interface* interf; - const struct sdis_medium *fmd, *bmd; - enum sdis_side solid_side, fluid_side; - double T_brf[3] = { 0, 0, 0 }; - double epsilon, hc, hr; - size_t iprim; - double uv[DIM - 1]; - float st[DIM - 1]; - double time; - int flux_mask = 0; - 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 SDIS_XD_DIMENSION == 2 - res_local = s2d_scene_view_sample - (view, - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, st); - uv[0] = (double)st[0]; -#else - res_local = s3d_scene_view_sample - (view, - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, st); - d2_set_f2(uv, st); -#endif - if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } - - /* Map from boundary scene to sdis scene */ - ASSERT(prim.prim_id < nprimitives); - iprim = primitives[prim.prim_id]; - - 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))) - { - ATOMIC_SET(&res, RES_BAD_ARG); - continue; - } - solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; - fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; - - res_local = XD(interface_get_hc_epsilon)(&hc, &epsilon, scn, - (unsigned)iprim, uv, time, fluid_side); - if(res_local != RES_OK) { - ATOMIC_SET(&res, res_local); - continue; - } - hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; - - /* Fluid, Radiative and Solid temperatures */ - flux_mask = 0; - if(hr > 0) flux_mask |= FLUX_FLAG_RADIATIVE; - if(hc > 0) flux_mask |= FLUX_FLAG_CONVECTIVE; - res_local = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, - solid_side, fp_to_meter, Tarad, Tref, flux_mask, 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 the estimated values */ - estimator_setup_realisations_count(estimator, nrealisations, N); - 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)); - if(shape) SXD(shape_ref_put(shape)); - if(view) SXD(scene_view_ref_put(view)); - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_estimator) *out_estimator = estimator; - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { if(rngs[i]) SSP(rng_ref_put(rngs[i])); } - MEM_RM(scn->dev->allocator, rngs); - } - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } - goto exit; -} - -#include "sdis_Xd_end.h" diff --git a/src/sdis_solve_boundary_Xd.h b/src/sdis_solve_boundary_Xd.h @@ -0,0 +1,498 @@ +/* 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_estimator_c.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_realisation.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> +#include <omp.h> + +#include "sdis_Xd_begin.h" + +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 +}; + +/******************************************************************************* + * Help functions + ******************************************************************************/ +static INLINE void +XD(boundary_get_indices)(const unsigned iprim, unsigned ids[DIM], void* context) +{ + unsigned i; + (void)context; + ASSERT(ids); + FOR_EACH(i, 0, DIM) ids[i] = iprim*DIM + i; +} + +static INLINE void +XD(boundary_get_position)(const unsigned ivert, float pos[DIM], void* context) +{ + struct XD(boundary_context)* ctx = context; + struct sXd(primitive) prim; + struct sXd(attrib) attr; + const unsigned iprim_id = ivert / DIM; + const unsigned iprim_vert = ivert % DIM; + unsigned iprim; + ASSERT(pos && context); + + iprim = (unsigned)ctx->primitives[iprim_id]; + SXD(scene_view_get_primitive(ctx->view, iprim, &prim)); +#if DIM == 2 + s2d_segment_get_vertex_attrib(&prim, iprim_vert, S2D_POSITION, &attr); + ASSERT(attr.type == S2D_FLOAT2); +#else + s3d_triangle_get_vertex_attrib(&prim, iprim_vert, S3D_POSITION, &attr); + ASSERT(attr.type == S3D_FLOAT3); +#endif + fX(set)(pos, attr.value); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +static res_T +XD(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) +{ + struct XD(boundary_context) ctx = XD(BOUNDARY_CONTEXT_NULL); + struct sXd(vertex_data) vdata = SXD_VERTEX_DATA_NULL; + struct sXd(scene)* scene = NULL; + struct sXd(shape)* shape = NULL; + struct sXd(scene_view)* view = NULL; + struct sdis_estimator* estimator = NULL; + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** rngs = NULL; + size_t i; + size_t N = 0; /* #realisations that do not fail */ + size_t view_nprims; + double weight=0, sqr_weight=0; + int64_t irealisation; + 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) { + res = RES_BAD_ARG; + goto error; + } + +#if SDIS_XD_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) { + log_err(scn->dev, + "%s: invalid primitive identifier `%lu'. It must be in the [0 %lu] range.\n", + FUNC_NAME, + (unsigned long)primitives[i], + (unsigned long)scene_get_primitives_count(scn)-1); + res = RES_BAD_ARG; + goto error; + } + } + + /* Create the Star-XD shape of the boundary */ +#if SDIS_XD_DIMENSION == 2 + res = s2d_shape_create_line_segments(scn->dev->sXd_dev, &shape); +#else + res = s3d_shape_create_mesh(scn->dev->sXd_dev, &shape); +#endif + if(res != RES_OK) goto error; + + /* Initialise the boundary shape with the triangles/segments of the + * submitted primitives */ + ctx.primitives = primitives; + ctx.view = scn->sXd(view); + vdata.usage = SXD_POSITION; + vdata.get = XD(boundary_get_position); +#if SDIS_XD_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 + vdata.type = S3D_FLOAT3; + res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprimitives, + boundary_get_indices_3d, (unsigned)(nprimitives*3), &vdata, 1, &ctx); +#endif + if(res != RES_OK) goto error; + + /* Create and setup the boundary Star-XD scene */ + res = sXd(scene_create)(scn->dev->sXd_dev, &scene); + if(res != RES_OK) goto error; + res = sXd(scene_attach_shape)(scene, shape); + if(res != RES_OK) goto error; + res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); + if(res != RES_OK) 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(*rngs)); + 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_ESTIMATOR_TEMPERATURE, &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) { + const int ithread = omp_get_thread_num(); + struct sXd(primitive) prim; + struct ssp_rng* rng = rngs[ithread]; + enum sdis_side side; + size_t iprim; + 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 SDIS_XD_DIMENSION == 2 + res_local = s2d_scene_view_sample + (view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st); + uv[0] = (double)st[0]; +#else + res_local = s3d_scene_view_sample + (view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st); + d2_set_f2(uv, st); +#endif + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + /* Map from boundary scene to sdis scene */ + ASSERT(prim.prim_id < nprimitives); + iprim = primitives[prim.prim_id]; + side = sides[prim.prim_id]; + + /* Invoke the boundary realisation */ + res_local = XD(boundary_realisation) + (scn, rng, iprim, uv, time, side, fp_to_meter, Tarad, Tref, &w); + + /* Update the MC accumulators */ + if(res_local == RES_OK) { + weight += w; + sqr_weight += w*w; + ++N; + } else if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + } + + /* Setup the estimated temperature */ + estimator_setup_realisations_count(estimator, nrealisations, N); + estimator_setup_temperature(estimator, weight, sqr_weight); + +exit: + if(scene) SXD(scene_ref_put(scene)); + if(shape) SXD(shape_ref_put(shape)); + if(view) SXD(scene_view_ref_put(view)); + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(out_estimator) *out_estimator = estimator; + if(rngs) { + FOR_EACH(i, 0, scn->dev->nthreads) {if(rngs[i]) SSP(rng_ref_put(rngs[i]));} + MEM_RM(scn->dev->allocator, rngs); + } + 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 */ + 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) +{ + struct XD(boundary_context) ctx = XD(BOUNDARY_CONTEXT_NULL); + struct sXd(vertex_data) vdata = SXD_VERTEX_DATA_NULL; + struct sXd(scene)* scene = NULL; + struct sXd(shape)* shape = NULL; + struct sXd(scene_view)* view = NULL; + struct sdis_estimator* estimator = NULL; + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** rngs = NULL; + 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; + size_t i; + size_t N = 0; /* #realisations that do not fail */ + size_t view_nprims; + int64_t irealisation; + 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) { + res = RES_BAD_ARG; + goto error; + } + +#if SDIS_XD_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) { + log_err(scn->dev, + "%s: invalid primitive identifier `%lu'. It must be in the [0 %lu] range.\n", + FUNC_NAME, + (unsigned long)primitives[i], + (unsigned long)scene_get_primitives_count(scn)-1); + res = RES_BAD_ARG; + goto error; + } + } + + /* Create the Star-XD shape of the boundary */ +#if SDIS_XD_DIMENSION == 2 + res = s2d_shape_create_line_segments(scn->dev->s2d, &shape); +#else + res = s3d_shape_create_mesh(scn->dev->s3d, &shape); +#endif + if(res != RES_OK) goto error; + + /* Initialise the boundary shape with the triangles/segments of the + * submitted primitives */ + ctx.primitives = primitives; + ctx.view = scn->sXd(view); + vdata.get = XD(boundary_get_position); +#if SDIS_XD_DIMENSION == 2 + vdata.usage = S2D_POSITION; + vdata.type = S2D_FLOAT2; + res = s2d_line_segments_setup_indexed_vertices(shape, (unsigned)nprimitives, + 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, + XD(boundary_get_indices), (unsigned)(nprimitives*3), &vdata, 1, &ctx); +#endif + if(res != RES_OK) goto error; + + /* Create and setup the boundary Star-XD scene */ + res = sXd(scene_create)(scn->dev->sXd_dev, &scene); + if(res != RES_OK) goto error; + res = sXd(scene_attach_shape)(scene, shape); + if(res != RES_OK) goto error; + res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); + if(res != RES_OK) 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(*rngs)); + 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_ESTIMATOR_FLUX, &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) + for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { + const int ithread = omp_get_thread_num(); + struct sXd(primitive) prim; + struct ssp_rng* rng = rngs[ithread]; + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + const struct sdis_interface* interf; + const struct sdis_medium *fmd, *bmd; + enum sdis_side solid_side, fluid_side; + double T_brf[3] = { 0, 0, 0 }; + double epsilon, hc, hr; + size_t iprim; + double uv[DIM - 1]; + float st[DIM - 1]; + double time; + int flux_mask = 0; + 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 SDIS_XD_DIMENSION == 2 + res_local = s2d_scene_view_sample + (view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st); + uv[0] = (double)st[0]; +#else + res_local = s3d_scene_view_sample + (view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st); + d2_set_f2(uv, st); +#endif + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + /* Map from boundary scene to sdis scene */ + ASSERT(prim.prim_id < nprimitives); + iprim = primitives[prim.prim_id]; + + 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))) + { + ATOMIC_SET(&res, RES_BAD_ARG); + continue; + } + solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; + fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; + + /* Build interface fragment on the fluid side of the primitive */ + res_local = XD(build_interface_fragment) + (&frag, scn, (unsigned)iprim, uv, fluid_side); + if(res_local!= RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + /* Fetch interface parameters */ + 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 */ + flux_mask = 0; + if(hr > 0) flux_mask |= FLUX_FLAG_RADIATIVE; + if(hc > 0) flux_mask |= FLUX_FLAG_CONVECTIVE; + res_local = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, + solid_side, fp_to_meter, Tarad, Tref, flux_mask, 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 the estimated values */ + estimator_setup_realisations_count(estimator, nrealisations, N); + 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)); + if(shape) SXD(shape_ref_put(shape)); + if(view) SXD(scene_view_ref_put(view)); + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(out_estimator) *out_estimator = estimator; + if(rngs) { + FOR_EACH(i, 0, scn->dev->nthreads) { if(rngs[i]) SSP(rng_ref_put(rngs[i])); } + MEM_RM(scn->dev->allocator, rngs); + } + return (res_T)res; +error: + if(estimator) { + SDIS(estimator_ref_put(estimator)); + estimator = NULL; + } + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_solve_probe_Xd.h b/src/sdis_solve_probe_Xd.h @@ -0,0 +1,222 @@ +/* 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_estimator_c.h" +#include "sdis_green.h" +#include "sdis_misc.h" +#include "sdis_realisation.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> +#include <omp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * 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 */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_green_function** out_green, /* May be NULL <=> No green func */ + struct sdis_estimator** out_estimator) +{ + struct sdis_medium* medium = NULL; + struct sdis_estimator* estimator = NULL; + struct sdis_green_function* green = NULL; + struct sdis_green_function** greens = 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 + || fp_to_meter <= 0 || Tref < 0) { + res = RES_BAD_ARG; + goto error; + } + if(!out_estimator && !out_green) { + res = RES_BAD_ARG; + goto error; + } + if(out_estimator) { + if(!time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != time_range[1])) { + res = RES_BAD_ARG; + goto error; + } + } + +#if SDIS_XD_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(*rngs)); + 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; + + if(out_green) { + /* Create the per thread green function */ + greens = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*greens)); + if(!greens) { res = RES_MEM_ERR; goto error; } + FOR_EACH(i, 0, scn->dev->nthreads) { + res = green_function_create(scn->dev, &greens[i]); + if(res != RES_OK) goto error; + } + + } + + /* Create the estimator */ + if(out_estimator) { + res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &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; + double time; + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + struct green_path_handle* pgreen_path = NULL; + struct green_path_handle green_path = GREEN_PATH_HANDLE_NULL; + struct sdis_heat_path* pheat_path = NULL; + struct sdis_heat_path heat_path; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + if(!out_green) { + time = sample_time(rng, time_range); + if(register_paths) { + heat_path_init(scn->dev->allocator, &heat_path); + pheat_path = &heat_path; + } + } else { + /* Do not take care of the submitted time when registering the green + * function. Simply takes 0 as relative time */ + time = 0; + res_local = green_function_create_path(greens[ithread], &green_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + pgreen_path = &green_path; + } + + res_local = XD(probe_realisation)((size_t)irealisation, scn, rng, medium, + position, time, fp_to_meter, Tarad, Tref, pgreen_path, pheat_path, &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(pheat_path) { + pheat_path->status = res_local == RES_OK + ? SDIS_HEAT_PATH_SUCCEED + : SDIS_HEAT_PATH_FAILED; + + /* Check if the path must be saved regarding the register_paths mask */ + if(!(register_paths & (int)pheat_path->status)) { + heat_path_release(pheat_path); + } else { /* Register the sampled path */ + res_local = estimator_add_and_release_heat_path(estimator, pheat_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + } + } + } + if(res != RES_OK) goto error; + + /* Setup the estimated temperature */ + if(out_estimator) { + estimator_setup_realisations_count(estimator, nrealisations, N); + estimator_setup_temperature(estimator, weight, sqr_weight); + } + + if(out_green) { + green = greens[0]; /* Return the green of the 1st thread */ + greens[0] = NULL; /* Make invalid the 1st green for 'on exit' clean up*/ + FOR_EACH(i, 1, scn->dev->nthreads) { /* Merge the per thread green */ + res = green_function_merge_and_clear(green, greens[i]); + if(res != RES_OK) goto error; + } + + /* Finalize the estimated green */ + res = green_function_finalize(green, rng_proxy); + if(res != RES_OK) goto error; + } + +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(greens) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(greens[i]) SDIS(green_function_ref_put(greens[i])); + } + MEM_RM(scn->dev->allocator, greens); + } + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(out_green) *out_green = green; + if(out_estimator) *out_estimator = estimator; + return (res_T)res; +error: + if(green) { + SDIS(green_function_ref_put(green)); + green = NULL; + } + if(estimator) { + SDIS(estimator_ref_put(estimator)); + estimator = NULL; + } + goto exit; +} + +#include "sdis_Xd_end.h" + diff --git a/src/sdis_solve_probe_boundary_Xd.h b/src/sdis_solve_probe_boundary_Xd.h @@ -0,0 +1,378 @@ +/* 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_estimator_c.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_realisation.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> +#include <omp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Local functions + ******************************************************************************/ +static res_T +XD(solve_probe_boundary) + (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 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 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; + } + +#if SDIS_XD_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 SDIS_XD_DIMENSION == 2 + { + 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 /* SDIS_XD_DIMENSION == 3 */ + { + 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; + } + } +#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; + } + + /* Create the estimator */ + res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &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; + 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; + + /* Setup the estimated temperature */ + estimator_setup_realisations_count(estimator, nrealisations, N); + 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; +} + +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_XD_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(build_interface_fragment) + (&frag, scn, (unsigned)iprim, uv, fluid_side); + if(res != RES_OK) goto error; + + /* Create the estimator */ + res = estimator_create(scn->dev, SDIS_ESTIMATOR_FLUX, &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; + int flux_mask = 0; + + 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 */ + flux_mask = 0; + if(hr > 0) flux_mask |= FLUX_FLAG_RADIATIVE; + if(hc > 0) flux_mask |= FLUX_FLAG_CONVECTIVE; + res_local = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, + solid_side, fp_to_meter, Tarad, Tref, flux_mask, 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 the estimated values */ + estimator_setup_realisations_count(estimator, nrealisations, N); + 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; +} + +#include "sdis_Xd_end.h"