stardis-solver

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

commit db9607f292dcbc9562a5b95c16b30165f64e1ba2
parent 47d1debedbcdff637fde53217199f06e493a0af7
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  6 Mar 2019 15:28:33 +0100

Merge branch 'feature_medium_temperature' into develop

Diffstat:
Mcmake/CMakeLists.txt | 4++++
Msrc/sdis.h | 23++++++++++++++++++++++-
Msrc/sdis_scene.c | 34+++++++++++++++++++++++++++++++++-
Msrc/sdis_scene_Xd.h | 18+++++++++---------
Msrc/sdis_scene_c.h | 6++++++
Msrc/sdis_solve.c | 30++++++++++++++++++++++++++++++
Msrc/sdis_solve_Xd.h | 1-
Asrc/sdis_solve_medium_Xd.h | 353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_solve_medium.c | 423+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_solve_medium_2d.c | 397+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 1277 insertions(+), 12 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -93,6 +93,7 @@ set(SDIS_FILES_INC sdis_scene_c.h sdis_scene_Xd.h sdis_solve_Xd.h + sdis_solve_medium_Xd.h sdis_Xd_begin.h sdis_Xd_end.h) @@ -184,6 +185,8 @@ if(NOT NO_TEST) new_test(test_sdis_solve_probe3_2d) new_test(test_sdis_solve_boundary) new_test(test_sdis_solve_boundary_flux) + new_test(test_sdis_solve_medium) + new_test(test_sdis_solve_medium_2d) new_test(test_sdis_volumic_power) new_test(test_sdis_volumic_power4_2d) @@ -198,6 +201,7 @@ if(NOT NO_TEST) add_test(test_sdis_volumic_power3_2d test_sdis_volumic_power3_2d) endif() + target_link_libraries(test_sdis_solve_medium Star3DUT) target_link_libraries(test_sdis_solve_probe3 Star3DUT) target_link_libraries(test_sdis_solve_probe3_2d ${MATH_LIB}) target_link_libraries(test_sdis_solve_camera Star3DUT) diff --git a/src/sdis.h b/src/sdis.h @@ -681,6 +681,15 @@ sdis_scene_get_dimension (const struct sdis_scene* scn, enum sdis_scene_dimension* dim); +/* Return the area/volume of occupied by a medium in a 2D/3D scene. Only + * enclosed media are handled, i.e. media whose border are explicitly defined + * by a geometry. */ +SDIS_API res_T +sdis_scene_get_medium_spread + (struct sdis_scene* scn, + const struct sdis_medium* mdm, + double* spread); + /******************************************************************************* * An estimator stores the state of a simulation ******************************************************************************/ @@ -913,6 +922,18 @@ sdis_solve_camera sdis_write_accums_T writer, void* writer_data); +SDIS_API res_T +sdis_solve_medium + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + struct sdis_medium* medium, /* Medium to solve */ + const double time_range[2], /* 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 int register_path, /* Combination of enum sdis_heat_path_flag */ + struct sdis_estimator** estimator); + /******************************************************************************* * Green solvers. * @@ -927,7 +948,7 @@ sdis_solve_camera * media must be constant in time and space too. Furthermore, note that only * the interfaces/media that had a flux/volumic power during green estimation * can update their flux/volumic power value for subsequent - * sdis_green_function_solve invocations : other interfaces/media are + * sdis_green_function_solve invocations : others interfaces/media are * definitely registered against the green function as interfaces/media with no * flux/volumic power. * diff --git a/src/sdis_scene.c b/src/sdis_scene.c @@ -312,13 +312,45 @@ sdis_scene_release_analysis(struct sdis_scene* scn) res_T sdis_scene_get_dimension - (const struct sdis_scene* scn, enum sdis_scene_dimension* dim) + (const struct sdis_scene* scn, enum sdis_scene_dimension* dim) { if(!scn || !dim) return RES_BAD_ARG; *dim = scene_is_2d(scn) ? SDIS_SCENE_2D : SDIS_SCENE_3D; return RES_OK; } +res_T +sdis_scene_get_medium_spread + (struct sdis_scene* scn, + const struct sdis_medium* mdm, + double* out_spread) +{ + struct htable_enclosure_iterator it, end; + double spread = 0; + res_T res = RES_OK; + + if(!scn || !mdm || !out_spread) { + res = RES_BAD_ARG; + goto error; + } + + htable_enclosure_begin(&scn->enclosures, &it); + htable_enclosure_end(&scn->enclosures, &end); + while(!htable_enclosure_iterator_eq(&it, &end)) { + const struct enclosure* enc = htable_enclosure_iterator_data_get(&it); + htable_enclosure_iterator_next(&it); + if(sdis_medium_get_id(mdm) == enc->medium_id) { + spread += enc->V; + } + } + *out_spread = spread; + +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Local miscellaneous function ******************************************************************************/ diff --git a/src/sdis_scene_Xd.h b/src/sdis_scene_Xd.h @@ -558,7 +558,7 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e enc_data = htable_enclosure_find(&scn->enclosures, &header.enclosure_id); ASSERT(enc_data != NULL); - /* Setup the vertex data */ + /* Setup the vertex data */ vdata.usage = SXD_POSITION; #if DIM == 2 vdata.type = S2D_FLOAT2; @@ -580,7 +580,7 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e #endif CALL(sXd(scene_create)(sXd_dev, &sXd_scn)); CALL(sXd(scene_attach_shape)(sXd_scn, sXd_shape)); - CALL(sXd(scene_view_create)(sXd_scn, SXD_SAMPLE, &enc_data->sXd(view))); + CALL(sXd(scene_view_create)(sXd_scn, SXD_SAMPLE|SXD_TRACE, &enc_data->sXd(view))); /* Compute the S/V ratio */ #if DIM == 2 @@ -590,11 +590,12 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e CALL(s3d_scene_view_compute_area(enc_data->s3d_view, &S)); CALL(s3d_scene_view_compute_volume(enc_data->s3d_view, &V)); #endif + enc_data->V = V; enc_data->S_over_V = S / V; ASSERT(enc_data->S_over_V >= 0); #undef CALL - /* Set enclosure hc upper bound regardless of its media being a fluid */ + /* Set enclosure hc upper bound regardless of its media being a fluid */ p_ub = htable_d_find(&scn->tmp_hc_ub, &header.enclosure_id); ASSERT(p_ub); enc_data->hc_upper_bound = *p_ub; @@ -612,6 +613,9 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e #endif } + /* Setup the medium id of the enclosure */ + SENCXD(enclosure_get_medium(enc, 0, &enc_data->medium_id)); + exit: enclosure_release(&enc_dummy); if(sXd_shape) SXD(shape_ref_put(sXd_shape)); @@ -642,7 +646,6 @@ XD(setup_enclosures)(struct sdis_scene* scn, struct sencXd(descriptor)* desc) #else struct senc_enclosure_header header; #endif - const struct sdis_medium* mdm; SENCXD(descriptor_get_enclosure(desc, ienc, &enc)); SENCXD(enclosure_get_header(enc, &header)); @@ -687,13 +690,10 @@ XD(setup_enclosures)(struct sdis_scene* scn, struct sencXd(descriptor)* desc) } SENCXD(enclosure_get_medium(enc, 0, &enclosed_medium)); - if(res != RES_OK) goto error; ASSERT(enclosed_medium < darray_medium_size_get(&scn->media)); - mdm = darray_medium_cdata_get(&scn->media)[enclosed_medium]; - ASSERT(mdm); - /* Silently discard the solid and infinite enclosures */ - if(mdm->type == SDIS_FLUID && !header.is_infinite) { + /* Silently discard infinite enclosures */ + if(!header.is_infinite) { res = XD(setup_enclosure_geometry)(scn, enc); if(res != RES_OK) goto error; } diff --git a/src/sdis_scene_c.h b/src/sdis_scene_c.h @@ -78,6 +78,9 @@ struct enclosure { double hc_upper_bound; double S_over_V; /* in 3D = surface/volume; in 2D = perimeter/area */ + double V; /* 3D = volume; 2D = area; */ + + unsigned medium_id; }; static INLINE void @@ -88,6 +91,7 @@ enclosure_init(struct mem_allocator* allocator, struct enclosure* enc) enc->s3d_view = NULL; darray_uint_init(allocator, &enc->local2global); enc->S_over_V = 0; + enc->V = 0; enc->hc_upper_bound = 0; } @@ -111,6 +115,7 @@ enclosure_copy(struct enclosure* dst, const struct enclosure* src) dst->s2d_view = src->s2d_view; } dst->S_over_V = src->S_over_V; + dst->V = src->V; dst->hc_upper_bound = src->hc_upper_bound; return darray_uint_copy(&dst->local2global, &src->local2global); } @@ -132,6 +137,7 @@ enclosure_copy_and_release(struct enclosure* dst, struct enclosure* src) src->s2d_view = NULL; } dst->S_over_V = src->S_over_V; + dst->V = src->V; dst->hc_upper_bound = src->hc_upper_bound; return RES_OK; } diff --git a/src/sdis_solve.c b/src/sdis_solve.c @@ -25,6 +25,12 @@ #define SDIS_XD_DIMENSION 3 #include "sdis_solve_Xd.h" +/* Generate the medium solvers */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_solve_medium_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_solve_medium_Xd.h" + #include <star/ssp.h> #include <omp.h> @@ -430,3 +436,27 @@ error: goto exit; } +res_T +sdis_solve_medium + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + struct sdis_medium* medium, /* Medium to solve */ + 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 */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_estimator** estimator) +{ + res_T res = RES_OK; + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + res = solve_medium_2d(scn, nrealisations, medium, time_range, fp_to_meter, Tarad, + Tref, register_paths, estimator); + } else { + res = solve_medium_3d(scn, nrealisations, medium, time_range, fp_to_meter, Tarad, + Tref, register_paths, estimator); + } + return res; +} + diff --git a/src/sdis_solve_Xd.h b/src/sdis_solve_Xd.h @@ -33,7 +33,6 @@ struct XD(boundary_context) { static const struct XD(boundary_context) XD(BOUNDARY_CONTEXT_NULL) = { NULL, NULL }; - /******************************************************************************* * Helper functions ******************************************************************************/ diff --git a/src/sdis_solve_medium_Xd.h b/src/sdis_solve_medium_Xd.h @@ -0,0 +1,353 @@ +/* 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_realisation.h" +#include "sdis_scene_c.h" + +#include <rsys/algorithm.h> +#include <rsys/dynamic_array.h> + +#include "sdis_Xd_begin.h" + +#ifndef SDIS_SOLVE_MEDIUM_XD_H +#define SDIS_SOLVE_MEDIUM_XD_H + +/* + * Define the data structures and functions that are not generic to the + * SDIS_XD_DIMENSION parameter + */ + +struct enclosure_cumul { + const struct enclosure* enc; + double cumul; +}; + +/* Define the darray_enclosure_cumul dynamic array */ +#define DARRAY_NAME enclosure_cumul +#define DARRAY_DATA struct enclosure_cumul +#include <rsys/dynamic_array.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE int +cmp_double_to_enc_cumuls(const void* a, const void* b) +{ + const double key = *(const double*)a; + const struct enclosure_cumul* enc_cumul = (const struct enclosure_cumul*)b; + if(key < enc_cumul->cumul) return -1; + if(key > enc_cumul->cumul) return +1; + return 0; +} + +static res_T +compute_medium_enclosure_cumulative + (struct sdis_scene* scn, + const struct sdis_medium* mdm, + struct darray_enclosure_cumul* cumul) +{ + struct htable_enclosure_iterator it, end; + double accum = 0; + res_T res = RES_OK; + ASSERT(scn && mdm && cumul); + + darray_enclosure_cumul_clear(cumul); + + htable_enclosure_begin(&scn->enclosures, &it); + htable_enclosure_end(&scn->enclosures, &end); + while(!htable_enclosure_iterator_eq(&it, &end)) { + struct enclosure_cumul enc_cumul; + const struct enclosure* enc = htable_enclosure_iterator_data_get(&it); + htable_enclosure_iterator_next(&it); + + if(sdis_medium_get_id(mdm) != enc->medium_id) continue; + + accum += enc->V; + enc_cumul.enc = enc; + enc_cumul.cumul = accum; + res = darray_enclosure_cumul_push_back(cumul, &enc_cumul); + if(res != RES_OK) goto error; + } + + if(darray_enclosure_cumul_size_get(cumul) == 0) { + log_err(scn->dev, + "%s: there is no enclosure that encompasses the submitted medium.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + darray_enclosure_cumul_clear(cumul); + goto exit; +} + +static const struct enclosure* +sample_medium_enclosure + (const struct darray_enclosure_cumul* cumul, struct ssp_rng* rng) +{ + const struct enclosure_cumul* enc_cumuls = NULL; + const struct enclosure_cumul* enc_cumul_found = NULL; + double r; + size_t i; + size_t sz; + ASSERT(cumul && rng && darray_enclosure_cumul_size_get(cumul)); + + sz = darray_enclosure_cumul_size_get(cumul); + enc_cumuls = darray_enclosure_cumul_cdata_get(cumul); + if(sz == 1) { + enc_cumul_found = enc_cumuls; + } else { + /* Generate an uniform random number in [0, cumul[ */ + r = ssp_rng_canonical(rng); + r = r * enc_cumuls[sz-1].cumul; + + enc_cumul_found = search_lower_bound + (&r, enc_cumuls, sz, sizeof(*enc_cumuls), cmp_double_to_enc_cumuls); + ASSERT(enc_cumul_found); + + /* search_lower_bound returns the first entry that is not less than `r'. + * The following code discards entries that are also equal to `r'. */ + i = (size_t)(enc_cumul_found - enc_cumuls); + while(enc_cumuls[i].cumul == r && i < sz) ++i; + ASSERT(i < sz); + + enc_cumul_found = enc_cumuls + i; + } + return enc_cumul_found->enc; +} + +#endif /* !SDIS_SOLVE_MEDIUM_XD_H */ + +/******************************************************************************* + * Helper functions generic to the SDIS_XD_DIMENSION parameter + ******************************************************************************/ +static res_T +XD(sample_enclosure_position) + (const struct enclosure* enc, + struct ssp_rng* rng, + double pos[DIM]) +{ + const size_t MAX_NCHALLENGES = 1000; + float lower[DIM], upper[DIM]; + size_t ichallenge; + size_t i; + res_T res = RES_OK; + ASSERT(enc && rng && pos); + + SXD(scene_view_get_aabb(enc->sXd(view), lower, upper)); + + FOR_EACH(i, 0, DIM) { + if(lower[i] > upper[i] || eq_epsf(lower[i], upper[i], 1.e-6f)) { + res = RES_BAD_ARG; /* Degenerated enclosure */ + goto error; + } + } + + FOR_EACH(ichallenge, 0, MAX_NCHALLENGES) { + struct sXd(hit) hit = SXD_HIT_NULL; + const float dir[3] = {1,0,0}; + const float range[2] = {0, FLT_MAX}; + float org[DIM]; + + /* Generate an uniform position into the enclosure AABB */ + FOR_EACH(i, 0, DIM) { + org[i] = ssp_rng_uniform_float(rng, lower[i], upper[i]); + } + + /* Check that pos lies into the enclosure; trace a ray and check that it + * hits something and that the normal points towards the traced ray + * direction (enclosure normals point inword the enclosure) */ + SXD(scene_view_trace_ray(enc->sXd(view), org, dir, range, NULL, &hit)); + if(!SXD_HIT_NONE(&hit) && fX(dot)(dir, hit.normal) < 0) { + dX_set_fX(pos, org); + break; + } + } + + if(ichallenge >= MAX_NCHALLENGES) { + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local function + ******************************************************************************/ +static res_T +XD(solve_medium) + (struct sdis_scene* scn, + const size_t nrealisations, + struct sdis_medium* mdm, + 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_estimator** out_estimator) +{ + struct darray_enclosure_cumul cumul; + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** rngs = NULL; + struct sdis_estimator* estimator = NULL; + struct accum { double sum, sum2; size_t naccums; }* accums = NULL; + int64_t irealisation; + int cumul_is_init = 0; + size_t i; + ATOMIC res = RES_OK; + + if(!scn || !mdm || !nrealisations || nrealisations > INT64_MAX + || !time_range || 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 + + /* 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 per thread accumulator */ + accums = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*accums)); + if(!accums) { res = RES_MEM_ERR; goto error; } + + /* Create the estimator */ + res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); + if(res != RES_OK) goto error; + + /* Compute the enclosure cumulative */ + darray_enclosure_cumul_init(scn->dev->allocator, &cumul); + cumul_is_init = 1; + res = compute_medium_enclosure_cumulative(scn, mdm, &cumul); + if(res != RES_OK) goto error; + + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) + for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { + const int ithread = omp_get_thread_num(); + const struct enclosure* enc = NULL; + struct accum* accum = accums + ithread; + struct ssp_rng* rng = rngs[ithread]; + struct sdis_heat_path* pheat_path = NULL; + struct sdis_heat_path heat_path; + double weight; + double time; + double pos[DIM]; + res_T res_local = RES_OK; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + /* Sample the time */ + time = sample_time(rng, time_range); + + /* Prepare path registration if necessary */ + if(register_paths) { + heat_path_init(scn->dev->allocator, &heat_path); + pheat_path = &heat_path; + } + + /* Uniformly Sample an enclosure that surround the submitted medium and + * uniformly sample a position into it */ + enc = sample_medium_enclosure(&cumul, rng); + res_local = XD(sample_enclosure_position)(enc, rng, pos); + if(res_local != RES_OK) { + log_err(scn->dev, "%s: could not sample a medium position.\n", FUNC_NAME); + ATOMIC_SET(&res, res_local); + continue; + } + + /* Run a probe realisation */ + res_local = XD(probe_realisation)((size_t)irealisation, scn, rng, mdm, pos, + time, fp_to_meter, Tarad, Tref, NULL, pheat_path, &weight); + if(res_local != RES_OK) { + if(res_local != RES_BAD_OP) { ATOMIC_SET(&res, res_local); continue; } + } else { + accum->sum += weight; + accum->sum2 += weight*weight; + ++accum->naccums; + } + + /* Finalize the registered path */ + 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; + + /* Merge the per thread accumulators into the accumulator of the thread 0 */ + FOR_EACH(i, 1, scn->dev->nthreads) { + accums[0].sum += accums[i].sum; + accums[0].sum2 += accums[i].sum2; + accums[0].naccums += accums[i].naccums; + } + ASSERT(accums[0].naccums <= nrealisations); + + /* Setup the estimated temperature */ + estimator_setup_realisations_count(estimator, nrealisations, accums[0].naccums); + estimator_setup_temperature(estimator, accums[0].sum, accums[0].sum2); + +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(accums) MEM_RM(scn->dev->allocator, accums); + if(cumul_is_init) darray_enclosure_cumul_release(&cumul); + 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" diff --git a/src/test_sdis_solve_medium.c b/src/test_sdis_solve_medium.c @@ -0,0 +1,423 @@ +/* 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.h" +#include "test_sdis_utils.h" + +#include <rsys/math.h> +#include <rsys/stretchy_array.h> +#include <star/s3dut.h> + +#include <string.h> + +#define Tf0 300.0 +#define Tf1 330.0 +#define N 1000ul /* #realisations */ + +/* + * The scene is composed of 2 super shapes whose temperature is unknown. The + * first super shape is surrounded by a fluid whose temperature is Tf0 while + * the second one is in fluid whose temperature is Tf1. The temperatures of the + * super shape 0 and 1 are thus uniform and equal to Tf0 and Tf1, respectively. + * + * This program performs 2 tests. In the first one, the super shapes 0 and 1 + * have different media; the medium solver thus estimates the + * temperature of one super shape. In the second test, the scene is updated to + * use the same medium for the 2 super shapes. In this case, when invoked on + * the right medium, the estimated temperature T is equal to : + * + * T = Tf0 * V0/(V0 + V1) + Tf1 * V1/(V0 + V1) + * + * with V0 and V1 the volume of the super shapes 0 and 1, respectively. + */ + +/******************************************************************************* + * Geometry + ******************************************************************************/ +struct context { + struct s3dut_mesh_data msh0; + struct s3dut_mesh_data msh1; + struct sdis_interface* interf0; + struct sdis_interface* interf1; +}; + +static void +get_indices(const size_t itri, size_t ids[3], void* context) +{ + const struct context* ctx = context; + /* Note that we swap the indices to ensure that the triangle normals point + * inward the super shape */ + if(itri < ctx->msh0.nprimitives) { + ids[0] = ctx->msh0.indices[itri*3+0]; + ids[2] = ctx->msh0.indices[itri*3+1]; + ids[1] = ctx->msh0.indices[itri*3+2]; + } else { + const size_t itri2 = itri - ctx->msh0.nprimitives; + ids[0] = ctx->msh1.indices[itri2*3+0] + ctx->msh0.nvertices; + ids[2] = ctx->msh1.indices[itri2*3+1] + ctx->msh0.nvertices; + ids[1] = ctx->msh1.indices[itri2*3+2] + ctx->msh0.nvertices; + } +} + +static void +get_position(const size_t ivert, double pos[3], void* context) +{ + const struct context* ctx = context; + if(ivert < ctx->msh0.nvertices) { + pos[0] = ctx->msh0.positions[ivert*3+0] - 2.0; + pos[1] = ctx->msh0.positions[ivert*3+1]; + pos[2] = ctx->msh0.positions[ivert*3+2]; + } else { + const size_t ivert2 = ivert - ctx->msh0.nvertices; + pos[0] = ctx->msh1.positions[ivert2*3+0] + 2.0; + pos[1] = ctx->msh1.positions[ivert2*3+1]; + pos[2] = ctx->msh1.positions[ivert2*3+2]; + } +} + +static void +get_interface(const size_t itri, struct sdis_interface** bound, void* context) +{ + const struct context* ctx = context; + *bound = itri < ctx->msh0.nprimitives ? ctx->interf0 : ctx->interf1; +} + +/******************************************************************************* + * Fluid medium + ******************************************************************************/ +struct fluid { + double temperature; +}; + +static double +fluid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct fluid*)sdis_data_cget(data))->temperature; +} + +/******************************************************************************* + * Solid medium + ******************************************************************************/ +struct solid { + double cp; + double lambda; + double rho; + double delta; + double temperature; +}; + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->cp; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->lambda; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->rho; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->delta; +} + +static double +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->temperature; +} + +struct interf { + double hc; + double epsilon; + double specular_fraction; +}; + +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->hc; +} + +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->epsilon; +} + +static double +interface_get_specular_fraction + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->specular_fraction; +} + +/******************************************************************************* + * Test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct s3dut_super_formula f0 = S3DUT_SUPER_FORMULA_NULL; + struct s3dut_super_formula f1 = S3DUT_SUPER_FORMULA_NULL; + struct s3dut_mesh* msh0 = NULL; + struct s3dut_mesh* msh1 = NULL; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_device* dev = NULL; + struct sdis_medium* solid0 = NULL; + struct sdis_medium* solid1 = NULL; + struct sdis_medium* fluid0 = NULL; + struct sdis_medium* fluid1 = NULL; + struct sdis_interface* solid0_fluid0 = NULL; + struct sdis_interface* solid0_fluid1 = NULL; + struct sdis_interface* solid1_fluid1 = NULL; + struct sdis_scene* scn = NULL; + struct sdis_data* data = NULL; + struct sdis_estimator* estimator = NULL; + struct fluid* fluid_param = NULL; + struct solid* solid_param = NULL; + struct interf* interface_param = NULL; + struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; + struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; + struct context ctx; + const double trange[2] = {0, INF}; + double ref; + double v, v0, v1; + size_t nreals; + size_t nfails; + size_t ntris; + size_t nverts; + (void)argc, (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + + fluid_shader.temperature = fluid_get_temperature; + + /* Create the fluid0 medium */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + fluid_param = sdis_data_get(data); + fluid_param->temperature = Tf0; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid0)); + OK(sdis_data_ref_put(data)); + + /* Create the fluid1 medium */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + fluid_param = sdis_data_get(data); + fluid_param->temperature = Tf1; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid1)); + OK(sdis_data_ref_put(data)); + + /* Setup the solid shader */ + solid_shader.calorific_capacity = solid_get_calorific_capacity; + solid_shader.thermal_conductivity = solid_get_thermal_conductivity; + solid_shader.volumic_mass = solid_get_volumic_mass; + solid_shader.delta_solid = solid_get_delta; + solid_shader.temperature = solid_get_temperature; + + /* Create the solid0 medium */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + solid_param = sdis_data_get(data); + solid_param->cp = 1.0; + solid_param->lambda = 0.1; + solid_param->rho = 1.0; + solid_param->delta = 1.0/20.0; + solid_param->temperature = -1; /* Unknown temperature */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid0)); + OK(sdis_data_ref_put(data)); + + /* Create the solid1 medium */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + solid_param = sdis_data_get(data); + solid_param->cp = 1.0; + solid_param->lambda = 1.0; + solid_param->rho = 1.0; + solid_param->delta = 1.0/20.0; + solid_param->temperature = -1; /* Unknown temperature */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); + OK(sdis_data_ref_put(data)); + + /* Create the interfaces */ + OK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data)); + interface_param = sdis_data_get(data); + interface_param->hc = 0.5; + interface_param->epsilon = 0; + interface_param->specular_fraction = 0; + interface_shader.convection_coef = interface_get_convection_coef; + interface_shader.front = SDIS_INTERFACE_SIDE_SHADER_NULL; + interface_shader.back.temperature = NULL; + interface_shader.back.emissivity = interface_get_emissivity; + interface_shader.back.specular_fraction = interface_get_specular_fraction; + OK(sdis_interface_create + (dev, solid0, fluid0, &interface_shader, data, &solid0_fluid0)); + OK(sdis_interface_create + (dev, solid0, fluid1, &interface_shader, data, &solid0_fluid1)); + OK(sdis_interface_create + (dev, solid1, fluid1, &interface_shader, data, &solid1_fluid1)); + OK(sdis_data_ref_put(data)); + + /* Create the mesh0 */ + f0.A = 1; f0.B = 1; f0.M = 3; f0.N0 = 1; f0.N1 = 1; f0.N2 = 2; + f1.A = 1; f1.B = 1; f1.M = 10; f1.N0 = 1; f1.N1 = 1; f1.N2 = 3; + OK(s3dut_create_super_shape(&allocator, &f0, &f1, 1, 64, 32, &msh0)); + OK(s3dut_mesh_get_data(msh0, &ctx.msh0)); + + /* Create the mesh1 */ + f0.A = 1; f0.B = 1; f0.M = 10; f0.N0 = 1; f0.N1 = 1; f0.N2 = 5; + f1.A = 1; f1.B = 1; f1.M = 1; f1.N0 = 1; f1.N1 = 1; f1.N2 = 1; + OK(s3dut_create_super_shape(&allocator, &f0, &f1, 1, 64, 32, &msh1)); + OK(s3dut_mesh_get_data(msh1, &ctx.msh1)); + + /* Create the scene */ + ctx.interf0 = solid0_fluid0; + ctx.interf1 = solid1_fluid1; + ntris = ctx.msh0.nprimitives + ctx.msh1.nprimitives; + nverts = ctx.msh0.nvertices + ctx.msh1.nvertices; +#if 0 + { + double* vertices = NULL; + size_t* indices = NULL; + size_t i; + CHK(vertices = MEM_CALLOC(&allocator, nverts*3, sizeof(*vertices))); + CHK(indices = MEM_CALLOC(&allocator, ntris*3, sizeof(*indices))); + FOR_EACH(i, 0, ntris) get_indices(i, indices + i*3, &ctx); + FOR_EACH(i, 0, nverts) get_position(i, vertices + i*3, &ctx); + dump_mesh(stdout, vertices, nverts, indices, ntris); + MEM_RM(&allocator, vertices); + MEM_RM(&allocator, indices); + } +#endif + + OK(sdis_scene_create(dev, ntris, get_indices, get_interface, nverts, + get_position, &ctx, &scn)); + + BA(sdis_scene_get_medium_spread(NULL, solid0, &v0)); + BA(sdis_scene_get_medium_spread(scn, NULL, &v0)); + BA(sdis_scene_get_medium_spread(scn, solid0, NULL)); + OK(sdis_scene_get_medium_spread(scn, solid0, &v0)); + CHK(v0 > 0); + OK(sdis_scene_get_medium_spread(scn, solid1, &v1)); + CHK(v1 > 0); + OK(sdis_scene_get_medium_spread(scn, fluid0, &v)); + CHK(v == 0); + OK(sdis_scene_get_medium_spread(scn, fluid1, &v)); + CHK(v == 0); + + BA(sdis_solve_medium(NULL, N, solid0, trange, 1.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, 0, solid0, trange, 1.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, N, NULL, trange, 1.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, N, solid0, NULL, 1.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, N, solid0, trange, 0.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, N, solid0, trange, 1.f, -1, 0, 0, NULL)); + OK(sdis_solve_medium(scn, N, solid0, trange, 1.f, -1, 0, 0, &estimator)); + + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + printf("Shape0 temperature = "STR(Tf0)" ~ %g +/- %g\n", T.E, T.SE); + printf("#failures = %lu/%lu\n", nfails, N); + CHK(eq_eps(T.E, Tf0, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + + OK(sdis_solve_medium(scn, N, solid1, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + printf("Shape1 temperature = "STR(Tf1)" ~ %g +/- %g\n", T.E, T.SE); + printf("#failures = %lu/%lu\n", nfails, N); + CHK(eq_eps(T.E, Tf1, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + +#if 0 + OK(sdis_solve_medium(scn, 1, solid1, trange, 1.f, -1, 0, + SDIS_HEAT_PATH_ALL, &estimator)); + dump_heat_paths(stderr, estimator); + OK(sdis_estimator_ref_put(estimator)); +#endif + + /* Create a new scene with the same medium in the 2 super shapes */ + OK(sdis_scene_ref_put(scn)); + ctx.interf0 = solid0_fluid0; + ctx.interf1 = solid0_fluid1; + OK(sdis_scene_create(dev, ntris, get_indices, get_interface, nverts, + get_position, &ctx, &scn)); + + OK(sdis_scene_get_medium_spread(scn, solid0, &v)); + CHK(eq_eps(v, v0+v1, 1.e-6)); + + BA(sdis_solve_medium(scn, N, solid1, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_solve_medium(scn, 10000, solid0, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + ref = Tf0 * v0/v + Tf1 * v1/v; + printf("Shape0 + Shape1 temperature = %g ~ %g +/- %g\n", ref, T.E, T.SE); + printf("#failures = %lu/10000\n", nfails); + CHK(eq_eps(T.E, ref, T.SE*3)); + OK(sdis_estimator_ref_put(estimator)); + + /* Release */ + OK(s3dut_mesh_ref_put(msh0)); + OK(s3dut_mesh_ref_put(msh1)); + OK(sdis_device_ref_put(dev)); + OK(sdis_medium_ref_put(fluid0)); + OK(sdis_medium_ref_put(fluid1)); + OK(sdis_medium_ref_put(solid0)); + OK(sdis_medium_ref_put(solid1)); + OK(sdis_interface_ref_put(solid0_fluid0)); + OK(sdis_interface_ref_put(solid0_fluid1)); + OK(sdis_interface_ref_put(solid1_fluid1)); + OK(sdis_scene_ref_put(scn)); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_solve_medium_2d.c b/src/test_sdis_solve_medium_2d.c @@ -0,0 +1,397 @@ +/* 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.h" +#include "test_sdis_utils.h" + +#include <rsys/stretchy_array.h> +#include <rsys/math.h> + +#include <string.h> + +#define Tf0 300.0 +#define Tf1 330.0 +#define N 1000ul /* #realisations */ + +/* + * The scene is composed of a square and a disk whose temperature is unknown. + * The square is surrounded by a fluid whose temperature is Tf0 while the disk + * is in a fluid whose temperature is Tf1. The temperature of the square + * and the disk are thus uniform and equal to Tf0 and Tf1, respectively. + * + * # # Tf1 +---------+ + * # # _\ | | _\ Tf0 + * # # / / | | / / + * # # \__/ | | \__/ + * # # | | + * # # +---------+ + * + * This program performs 2 tests. In the first one, the square and the disk + * have different media; the medium solver estimates the temperature of + * the square or the one of the disk. In the second test, the scene is updated + * to use the same medium for the 2 shapes. When invoked on + * the right medium, the estimated temperature T is equal to : + * + * T = Tf0 * A0/(A0 + A1) + Tf1 * A1/(A0 + A1) + * + * with A0 and A1 the area of the shape and the area of the disk, respectively. + */ + +/******************************************************************************* + * Geometry + ******************************************************************************/ +struct context { + const double* positions; + const size_t* indices; + size_t nsegments_interf0; /* #segments of the interface 0 */ + struct sdis_interface* interf0; + struct sdis_interface* interf1; +}; + +static void +get_indices(const size_t iseg, size_t ids[2], void* context) +{ + const struct context* ctx = context; + ids[0] = ctx->indices[iseg*2+0]; + ids[1] = ctx->indices[iseg*2+1]; +} + +static void +get_position(const size_t ivert, double pos[2], void* context) +{ + const struct context* ctx = context; + pos[0] = ctx->positions[ivert*2+0]; + pos[1] = ctx->positions[ivert*2+1]; +} + +static void +get_interface(const size_t iseg, struct sdis_interface** bound, void* context) +{ + const struct context* ctx = context; + *bound = iseg < ctx->nsegments_interf0 ? ctx->interf0 : ctx->interf1; +} + +/******************************************************************************* + * Fluid medium + ******************************************************************************/ +struct fluid { + double temperature; +}; + +static double +fluid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct fluid*)sdis_data_cget(data))->temperature; +} + +/******************************************************************************* + * Solid medium + ******************************************************************************/ +struct solid { + double cp; + double lambda; + double rho; + double delta; + double temperature; +}; + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->cp; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->lambda; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->rho; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->delta; +} + +static double +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->temperature; +} + +struct interf { + double hc; + double epsilon; + double specular_fraction; +}; + +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->hc; +} + +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->epsilon; +} + +static double +interface_get_specular_fraction + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->specular_fraction; +} + +/******************************************************************************* + * Test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_device* dev = NULL; + struct sdis_medium* solid0 = NULL; + struct sdis_medium* solid1 = NULL; + struct sdis_medium* fluid0 = NULL; + struct sdis_medium* fluid1 = NULL; + struct sdis_interface* solid0_fluid0 = NULL; + struct sdis_interface* solid0_fluid1 = NULL; + struct sdis_interface* solid1_fluid1 = NULL; + struct sdis_scene* scn = NULL; + struct sdis_data* data = NULL; + struct sdis_estimator* estimator = NULL; + struct fluid* fluid_param = NULL; + struct solid* solid_param = NULL; + struct interf* interface_param = NULL; + struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; + struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; + struct context ctx; + const double trange[2] = {0, INF}; + double a, a0, a1; + double ref; + double* positions = NULL; + size_t* indices = NULL; + size_t nverts; + size_t nreals; + size_t nfails; + size_t i; + (void)argc, (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + + fluid_shader.temperature = fluid_get_temperature; + + /* Create the fluid0 medium */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + fluid_param = sdis_data_get(data); + fluid_param->temperature = Tf0; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid0)); + OK(sdis_data_ref_put(data)); + + /* Create the fluid1 medium */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + fluid_param = sdis_data_get(data); + fluid_param->temperature = Tf1; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid1)); + OK(sdis_data_ref_put(data)); + + /* Setup the solid shader */ + solid_shader.calorific_capacity = solid_get_calorific_capacity; + solid_shader.thermal_conductivity = solid_get_thermal_conductivity; + solid_shader.volumic_mass = solid_get_volumic_mass; + solid_shader.delta_solid = solid_get_delta; + solid_shader.temperature = solid_get_temperature; + + /* Create the solid0 medium */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + solid_param = sdis_data_get(data); + solid_param->cp = 1.0; + solid_param->lambda = 0.1; + solid_param->rho = 1.0; + solid_param->delta = 1.0/20.0; + solid_param->temperature = -1; /* Unknown temperature */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid0)); + OK(sdis_data_ref_put(data)); + + /* Create the solid1 medium */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + solid_param = sdis_data_get(data); + solid_param->cp = 1.0; + solid_param->lambda = 1.0; + solid_param->rho = 1.0; + solid_param->delta = 1.0/20.0; + solid_param->temperature = -1; /* Unknown temperature */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); + OK(sdis_data_ref_put(data)); + + /* Create the interfaces */ + OK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data)); + interface_param = sdis_data_get(data); + interface_param->hc = 0.5; + interface_param->epsilon = 0; + interface_param->specular_fraction = 0; + interface_shader.convection_coef = interface_get_convection_coef; + interface_shader.front = SDIS_INTERFACE_SIDE_SHADER_NULL; + interface_shader.back.temperature = NULL; + interface_shader.back.emissivity = interface_get_emissivity; + interface_shader.back.specular_fraction = interface_get_specular_fraction; + OK(sdis_interface_create + (dev, solid0, fluid0, &interface_shader, data, &solid0_fluid0)); + OK(sdis_interface_create + (dev, solid0, fluid1, &interface_shader, data, &solid0_fluid1)); + OK(sdis_interface_create + (dev, solid1, fluid1, &interface_shader, data, &solid1_fluid1)); + OK(sdis_data_ref_put(data)); + + /* Setup the square geometry */ + sa_add(positions, square_nvertices*2); + sa_add(indices, square_nsegments*2); + memcpy(positions, square_vertices, square_nvertices*sizeof(double[2])); + memcpy(indices, square_indices, square_nsegments*sizeof(size_t[2])); + + /* Transate the square in X */ + FOR_EACH(i, 0, square_nvertices) positions[i*2] += 2; + + /* Setup a disk */ + nverts = 64; + FOR_EACH(i, 0, nverts) { + const double theta = (double)i * (2*PI)/(double)nverts; + const double r = 1; /* Radius */ + const double x = cos(theta) * r - 2/* X translation */; + const double y = sin(theta) * r + 0.5/* Y translation */; + sa_push(positions, x); + sa_push(positions, y); + } + FOR_EACH(i, 0, nverts) { + const size_t i0 = i + square_nvertices; + const size_t i1 = (i+1) % nverts + square_nvertices; + /* Flip the ids to ensure that the normals point inward the disk */ + sa_push(indices, i1); + sa_push(indices, i0); + } + + /* Create the scene */ + ctx.positions = positions; + ctx.indices = indices; + ctx.nsegments_interf0 = square_nsegments; + ctx.interf0 = solid0_fluid0; + ctx.interf1 = solid1_fluid1; + OK(sdis_scene_2d_create(dev, sa_size(indices)/2, get_indices, get_interface, + sa_size(positions)/2, get_position, &ctx, &scn)); + + OK(sdis_scene_get_medium_spread(scn, solid0, &a0)); + CHK(eq_eps(a0, 1.0, 1.e-6)); + OK(sdis_scene_get_medium_spread(scn, solid1, &a1)); + /* Rough estimation since the disk is coarsely discretized */ + CHK(eq_eps(a1, PI, 1.e-1)); + + /* Estimate the temperature of the square */ + OK(sdis_solve_medium(scn, N, solid0, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + printf("Square temperature = "STR(Tf0)" ~ %g +/- %g\n", T.E, T.SE); + printf("#failures = %lu / %lu\n", nfails, N); + CHK(eq_eps(T.E, Tf0, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + + /* Estimate the temperature of the disk */ + OK(sdis_solve_medium(scn, N, solid1, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + printf("Disk temperature = "STR(Tf1)" ~ %g +/- %g\n", T.E, T.SE); + printf("#failures = %lu / %lu\n", nfails, N); + CHK(eq_eps(T.E, Tf1, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + + /* Create a new scene with the same medium for the disk and the square */ + OK(sdis_scene_ref_put(scn)); + ctx.interf0 = solid0_fluid0; + ctx.interf1 = solid0_fluid1; + OK(sdis_scene_2d_create(dev, sa_size(indices)/2, get_indices, get_interface, + sa_size(positions)/2, get_position, &ctx, &scn)); + + OK(sdis_scene_get_medium_spread(scn, solid0, &a)); + CHK(eq_eps(a, a0+a1, 1.e-6)); + + /* Estimate the temperature of the square and disk shapes */ + BA(sdis_solve_medium(scn, N, solid1, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_solve_medium(scn, 10000, solid0, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + ref = Tf0 * a0/a + Tf1 * a1/a; + printf("Square + Disk temperature = %g ~ %g +/- %g\n", ref, T.E, T.SE); + printf("#failures = %lu / 10000\n", nfails); + CHK(eq_eps(T.E, ref, 3*T.SE)); + CHK(nreals + nfails == 10000); + OK(sdis_estimator_ref_put(estimator)); + + /* Release */ + OK(sdis_device_ref_put(dev)); + OK(sdis_medium_ref_put(solid0)); + OK(sdis_medium_ref_put(solid1)); + OK(sdis_medium_ref_put(fluid0)); + OK(sdis_medium_ref_put(fluid1)); + OK(sdis_interface_ref_put(solid0_fluid0)); + OK(sdis_interface_ref_put(solid0_fluid1)); + OK(sdis_interface_ref_put(solid1_fluid1)); + OK(sdis_scene_ref_put(scn)); + + sa_release(positions); + sa_release(indices); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} +