stardis-solver

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

commit 22f2c11ea95188399a306999b699fc448577115f
parent 51284033a44ed852765399fc26eda1efce1bd379
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 11 Sep 2024 11:56:40 +0200

Merge branch 'release_0.16.1'

Diffstat:
MREADME.md | 11+++++++++++
Mconfig.mk | 2+-
Msrc/sdis_heat_path_boundary_Xd.h | 56+-------------------------------------------------------
Msrc/sdis_heat_path_boundary_Xd_c.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_heat_path_boundary_Xd_handle_external_net_flux.h | 11+++++++----
Msrc/sdis_heat_path_boundary_c.h | 18++++++++++++++++++
Msrc/sdis_heat_path_radiative_Xd.h | 142++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/sdis_realisation_Xd.h | 25++++++++++++++++++++++---
Msrc/sdis_scene_Xd.h | 19++++++++++++++++++-
Msrc/sdis_scene_c.h | 8+++++++-
Msrc/test_sdis_solve_boundary_flux.c | 22+++++++++++++++-------
Msrc/test_sdis_unsteady_analytic_profile.c | 2+-
12 files changed, 244 insertions(+), 121 deletions(-)

diff --git a/README.md b/README.md @@ -158,6 +158,17 @@ Edit config.mk as needed, then run: ## Release notes + +### Version 0.16.1 + +- Corrected net flux calculation on surfaces with several Robin boundary + conditions. Depending on the configuration, such a calculation could + be impossible and return an error. +- Improve the performance of external flux calculations by avoiding the + need to calculate the contribution of external flux for surfaces with + zero emissivity. +- Mitigate numerical errors when sampling radiative paths. + ### Version 0.16 #### Add support for custom sampling of solid paths diff --git a/config.mk b/config.mk @@ -1,6 +1,6 @@ VERSION_MAJOR = 0 VERSION_MINOR = 16 -VERSION_PATCH = 0 +VERSION_PATCH = 1 VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) PREFIX = /usr/local diff --git a/src/sdis_heat_path_boundary_Xd.h b/src/sdis_heat_path_boundary_Xd.h @@ -25,58 +25,6 @@ #include "sdis_Xd_begin.h" /******************************************************************************* - * Helper functions - ******************************************************************************/ -/* This function checks whether the random walk is on a boundary and, if so, - * verifies that the temperature of the medium attached to the interface is - * known. This medium can be different from the medium of the enclosure. Indeed, - * the enclosure can contain several media used to set the temperatures of - * several boundary conditions. Hence this function, which queries the medium on - * the trajectory coming from a boundary */ -static res_T -XD(handle_known_medium_temperature) - (struct sdis_scene* scn, - struct rwalk_context* ctx, - struct rwalk* rwalk, - struct temperature* T) -{ - struct sdis_interface* interf = NULL; - struct sdis_medium* mdm = NULL; - double temperature = SDIS_TEMPERATURE_NONE; - res_T res = RES_OK; - ASSERT(scn && ctx && rwalk && T); - - /* Not at an interface */ - if(SXD_HIT_NONE(&rwalk->XD(hit))) return RES_OK; /* Nothing to do */ - - interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); - mdm = rwalk->hit_side==SDIS_FRONT ? interf->medium_front: interf->medium_back; - - temperature = medium_get_temperature(mdm, &rwalk->vtx); - - /* Check if the temperature is known */ - if(SDIS_TEMPERATURE_IS_UNKNOWN(temperature)) goto exit; - - T->value += temperature; - T->done = 1; - - if(ctx->green_path) { - res = green_path_set_limit_vertex - (ctx->green_path, mdm, &rwalk->vtx, rwalk->elapsed_time); - if(res != RES_OK) goto error; - } - - if(ctx->heat_path) { - heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; - } - -exit: - return res; -error: - goto exit; -} - -/******************************************************************************* * Local functions ******************************************************************************/ res_T @@ -135,7 +83,6 @@ XD(boundary_path) } if(res != RES_OK) goto error; -#if 1 if(T->done) goto exit; /* Handling limit boundary condition, i.e. the trajectory originates from a @@ -155,11 +102,10 @@ XD(boundary_path) * computationally, and in fact it's also a handy way of testing * border cases */ if(T->func == XD(convective_path) || T->func == XD(conductive_path)) { - res = XD(handle_known_medium_temperature)(scn, ctx, rwalk, T); + res = XD(query_medium_temperature_from_boundary)(scn, ctx, rwalk, T); if(res != RES_OK) goto error; if(T->done) goto exit; /* That's all folks */ } -#endif exit: return res; diff --git a/src/sdis_heat_path_boundary_Xd_c.h b/src/sdis_heat_path_boundary_Xd_c.h @@ -1138,4 +1138,53 @@ XD(check_Tref) return RES_OK; } +/* This function checks whether the random walk is on a boundary and, if so, + * verifies that the temperature of the medium attached to the interface is + * known. This medium can be different from the medium of the enclosure. Indeed, + * the enclosure can contain several media used to set the temperatures of + * several boundary conditions. Hence this function, which queries the medium on + * the path coming from a boundary */ +res_T +XD(query_medium_temperature_from_boundary) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk* rwalk, + struct temperature* T) +{ + struct sdis_interface* interf = NULL; + struct sdis_medium* mdm = NULL; + double temperature = SDIS_TEMPERATURE_NONE; + res_T res = RES_OK; + ASSERT(scn && ctx && rwalk && T); + + /* Not at an interface */ + if(SXD_HIT_NONE(&rwalk->XD(hit))) return RES_OK; /* Nothing to do */ + + interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); + mdm = rwalk->hit_side==SDIS_FRONT ? interf->medium_front: interf->medium_back; + + temperature = medium_get_temperature(mdm, &rwalk->vtx); + + /* Check if the temperature is known */ + if(SDIS_TEMPERATURE_IS_UNKNOWN(temperature)) goto exit; + + T->value += temperature; + T->done = 1; + + if(ctx->green_path) { + res = green_path_set_limit_vertex + (ctx->green_path, mdm, &rwalk->vtx, rwalk->elapsed_time); + if(res != RES_OK) goto error; + } + + if(ctx->heat_path) { + heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; + } + +exit: + return res; +error: + goto exit; +} + #include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_boundary_Xd_handle_external_net_flux.h b/src/sdis_heat_path_boundary_Xd_handle_external_net_flux.h @@ -503,6 +503,13 @@ XD(handle_external_net_flux) handle_flux = handle_flux && (scn->source != NULL); if(!handle_flux) goto exit; + /* Emissivity is null <=> external flux is null. Nothing to do */ + src_id = sdis_source_get_id(scn->source); + emissivity = interface_side_get_emissivity(args->interf, src_id, &frag); + res = interface_side_check_emissivity(scn->dev, emissivity, frag.P, frag.time); + if(res != RES_OK) goto error; + if(emissivity == 0) goto exit; + /* Sample the external source */ res = source_sample (scn->source, rng, frag.P, frag.time, &src_sample); @@ -536,10 +543,6 @@ XD(handle_external_net_flux) incident_flux_direct + incident_flux_diffuse.reflected; /* Calculate the net flux [W/m^2] */ - src_id = sdis_source_get_id(scn->source); - emissivity = interface_side_get_emissivity(args->interf, src_id, &frag); - res = interface_side_check_emissivity(scn->dev, emissivity, frag.P, frag.time); - if(res != RES_OK) goto error; net_flux = incident_flux * emissivity; /* [W/m^2] */ /* Calculate the net flux from the radiance source scattered at least once by diff --git a/src/sdis_heat_path_boundary_c.h b/src/sdis_heat_path_boundary_c.h @@ -204,6 +204,24 @@ check_Tref_3d const double Tref, const char* call_func_name); +/* Query medium temperature from the boundary. This medium can be different + * from the medium of the enclosure. Hence this function, which queries the + * medium on the path coming from a boundary. If the temperature is known, T is + * set to done. */ +extern LOCAL_SYM res_T +query_medium_temperature_from_boundary_2d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk* rwalk, + struct temperature* T); + +extern LOCAL_SYM res_T +query_medium_temperature_from_boundary_3d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk* rwalk, + struct temperature* T); + /******************************************************************************* * Boundary sub-paths ******************************************************************************/ diff --git a/src/sdis_heat_path_radiative_Xd.h b/src/sdis_heat_path_radiative_Xd.h @@ -28,6 +28,84 @@ #include "sdis_Xd_begin.h" /******************************************************************************* + * Non generic local functions + ******************************************************************************/ +#ifndef SDIS_HEAT_PATH_RADIATIVE_XD_H +#define SDIS_HEAT_PATH_RADIATIVE_XD_H + +static res_T +set_limit_radiative_temperature + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk* rwalk, + /* Direction along which the random walk reached the radiative environment */ + const float dir[3], + const int branch_id, + struct temperature* T) +{ + struct sdis_radiative_ray ray = SDIS_RADIATIVE_RAY_NULL; + double trad = 0; /* [K] */ + res_T res = RES_OK; + + /* Check pre-conditions */ + ASSERT(scn && ctx && rwalk && dir && T); + ASSERT(SXD_HIT_NONE(&rwalk->XD(hit))); + + rwalk->hit_side = SDIS_SIDE_NULL__; + d3_set_f3(rwalk->dir, dir); + d3_normalize(rwalk->dir, rwalk->dir); + d3_set(ray.dir, rwalk->dir); + + trad = radiative_env_get_temperature(scn->radenv, &ray); + if(SDIS_TEMPERATURE_IS_UNKNOWN(trad)) { + log_err(scn->dev, + "%s:%s: the random walk has reached an invalid radiative environment from " + "position (%g, %g, %g) along direction (%g, %g, %g): the temperature is " + "unknown. This may be due to numerical inaccuracies or inconsistencies " + "in the simulated system (e.g. non-closed geometry). For systems where " + "sampled paths can reach such a temperature, we need to define a valid " + "radiative temperature, i.e. one with a known temperature.\n", + __FILE__, FUNC_NAME, SPLIT3(rwalk->vtx.P), SPLIT3(rwalk->dir)); + res = RES_BAD_OP; + goto error; + } + + /* The limit condition is reached */ + T->value += trad; + T->done = 1; + + /* Update green path */ + if(ctx->green_path) { + res = green_path_set_limit_radiative_ray + (ctx->green_path, &ray, rwalk->elapsed_time); + if(res != RES_OK) goto error; + } + + /* Record the limit vertex of the sampled path. Set it arbitrarily at a + * distance of 0.1 meters from the surface along the direction reaching the + * radiative environment */ + if(ctx->heat_path) { + const float empirical_dst = 0.1f * (float)scn->fp_to_meter; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + + vtx = rwalk->vtx; + vtx.P[0] += dir[0] * empirical_dst; + vtx.P[1] += dir[1] * empirical_dst; + vtx.P[2] += dir[2] * empirical_dst; + res = register_heat_vertex(ctx->heat_path, &vtx, T->value, + SDIS_HEAT_VERTEX_RADIATIVE, branch_id); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +#endif /* SDIS_HEAT_PATH_RADIATIVE_XD_H */ + +/******************************************************************************* * Local functions ******************************************************************************/ res_T @@ -72,6 +150,8 @@ XD(trace_radiative_path) /* Trace the radiative ray */ filter_data.XD(hit) = rwalk->XD(hit); filter_data.epsilon = 1.e-6; + filter_data.scn = scn; /* Enable the filtering wrt the enclosure id */ + filter_data.enc_id = rwalk->enc_id; #if (SDIS_XD_DIMENSION == 2) SXD(scene_view_trace_ray_3d (scn->sXd(view), pos, dir, range, &filter_data, &rwalk->XD(hit))); @@ -79,53 +159,14 @@ XD(trace_radiative_path) SXD(scene_view_trace_ray (scn->sXd(view), pos, dir, range, &filter_data, &rwalk->XD(hit))); #endif - if(SXD_HIT_NONE(&rwalk->XD(hit))) { /* Fetch the ambient radiative temperature */ - struct sdis_radiative_ray ray = SDIS_RADIATIVE_RAY_NULL; - double trad = 0; /* [K] */ - - rwalk->hit_side = SDIS_SIDE_NULL__; - d3_set_f3(rwalk->dir, dir); - d3_normalize(rwalk->dir, rwalk->dir); - d3_set(ray.dir, rwalk->dir); - - trad = radiative_env_get_temperature(scn->radenv, &ray); - if(SDIS_TEMPERATURE_IS_UNKNOWN(trad)) { - log_err(scn->dev, - "%s: the random walk has reached an invalid radiative environment from " - "position `%g %g %g' along direction `%g %g %g': the temperature is " - "unknown. This may be due to numerical inaccuracies or inconsistencies " - "in the simulated system (e.g. non-closed geometry). For systems where " - "random walks can reach such a temperature, we need to define a valid " - "radiative temperature, i.e. one with a known temperature.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P), SPLIT3(rwalk->dir)); - res = RES_BAD_OP; - goto error; - } - - T->value += trad; - T->done = 1; - - if(ctx->green_path) { - res = green_path_set_limit_radiative_ray - (ctx->green_path, &ray, rwalk->elapsed_time); - if(res != RES_OK) goto error; - } - - if(ctx->heat_path) { - const float empirical_dst = 0.1f * (float)scn->fp_to_meter; - struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; - - vtx = rwalk->vtx; - vtx.P[0] += dir[0] * empirical_dst; - vtx.P[1] += dir[1] * empirical_dst; - vtx.P[2] += dir[2] * empirical_dst; - res = register_heat_vertex(ctx->heat_path, &vtx, T->value, - SDIS_HEAT_VERTEX_RADIATIVE, branch_id); - if(res != RES_OK) goto error; - } - - /* Stop the radiative path */ - break; + + /* The path reaches the radiative environment */ + if(SXD_HIT_NONE(&rwalk->XD(hit))) { + res = set_limit_radiative_temperature(scn, ctx, rwalk, dir, branch_id, T); + if(res != RES_OK) goto error; + + ASSERT(T->done); + break; /* Stop the radiative path */ } /* Define the hit side */ @@ -167,7 +208,12 @@ XD(trace_radiative_path) fX(normalize)(N, rwalk->XD(hit).normal); if(rwalk->hit_side == SDIS_BACK) fX(minus)(N, N); - /* Check that the radiative path still lies in the same enclosure */ + /* Check that the radiative path is still within the same enclosure. Note + * that this may not be the case, even if the filtering of intersections + * relative to the current enclosure is enabled. This filtering is only + * performed for intersections on a boundary between primitives. As a + * consequence, a threshold effect on how "intersections on a boundary" are + * detected could lead to this situation */ scene_get_enclosure_ids(scn, rwalk->XD(hit).prim.prim_id, enc_ids); chk_enc_id = rwalk->hit_side == SDIS_FRONT ? enc_ids[0] : enc_ids[1]; if(chk_enc_id != rwalk->enc_id) { diff --git a/src/sdis_realisation_Xd.h b/src/sdis_realisation_Xd.h @@ -15,6 +15,7 @@ #include "sdis_device_c.h" #include "sdis_heat_path.h" +#include "sdis_heat_path_boundary_c.h" #include "sdis_interface_c.h" #include "sdis_log.h" #include "sdis_medium_c.h" @@ -449,10 +450,28 @@ XD(boundary_flux_realisation) /* Compute fluid temperature */ if(compute_convective) { RESET_WALK(fluid_side, enc_ids[fluid_side]); - T.func = XD(convective_path); - res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); + + /* Check whether the temperature of the fluid is known by querying it from + * its boundary. This makes it possible to handle situations where fluids + * are used as Robin's boundary condition. In this case, a geometric + * enclosure may have several fluids, each defining the temperature of a + * boundary condition. Sampling of convective paths in such an enclosure is + * forbidden since this enclosure does not exist for this path space: it is + * beyond its boundary */ + res = XD(query_medium_temperature_from_boundary)(scn, &ctx, &rwalk, &T); if(res != RES_OK) return res; - result->Tfluid = T.value; + + /* Robin's boundary condition */ + if(T.done) { + result->Tfluid = T.value; + + /* Sample a convective path */ + } else { + T.func = XD(convective_path); + res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); + if(res != RES_OK) return res; + result->Tfluid = T.value; + } } #undef SET_PARAM diff --git a/src/sdis_scene_Xd.h b/src/sdis_scene_Xd.h @@ -516,7 +516,24 @@ XD(hit_filter_function) reject_hit = hit_shared_edge (&hit_from->prim, &hit->prim, hit_from->uv, hit->uv, org, pos); #endif - return reject_hit; + if(reject_hit) return 1; + } + + /* If the hit to be considered is (approximately) on a boundary between 2 + * primitives, it may belong to the wrong primitive, i.e. the one that doesn't + * "face" the direction of the ray. We therefore check the enclosure towards + * which it is directed, and reject it if it is not the same as the one from + * which the ray originates */ + if(filter_data->scn && HIT_ON_BOUNDARY(hit, org, dir)) { + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + unsigned chk_enc_id = ENCLOSURE_ID_NULL; + + scene_get_enclosure_ids(filter_data->scn, hit->prim.prim_id, enc_ids); + chk_enc_id = fX(dot)(dir, hit->normal) < 0 + ? enc_ids[0] /* Front */ + : enc_ids[1]; /* Back */ + + if(chk_enc_id != filter_data->enc_id) return 1; } return 0; } diff --git a/src/sdis_scene_c.h b/src/sdis_scene_c.h @@ -40,6 +40,11 @@ struct hit_filter_data { struct s3d_hit hit_3d; double epsilon; /* Threshold defining roughly equal intersections */ + /* When a scene is defined, primitives that do not point to the defined + * enclosure are filtered out */ + struct sdis_scene* scn; /* NULL <=> do not filter wrt enc_id */ + unsigned enc_id; + /* Bypass the regular filter function */ s2d_hit_filter_function_T custom_filter_2d; s3d_hit_filter_function_T custom_filter_3d; @@ -47,7 +52,8 @@ struct hit_filter_data { /* Custom filter query data. It is ignored if custom_filter is NULL */ void* custom_filter_data; }; -#define HIT_FILTER_DATA_NULL__ {S2D_HIT_NULL__,S3D_HIT_NULL__,0,NULL,NULL,NULL} +#define HIT_FILTER_DATA_NULL__ \ + {S2D_HIT_NULL__,S3D_HIT_NULL__,0,NULL,ENCLOSURE_ID_NULL,NULL,NULL,NULL} static const struct hit_filter_data HIT_FILTER_DATA_NULL = HIT_FILTER_DATA_NULL__; diff --git a/src/test_sdis_solve_boundary_flux.c b/src/test_sdis_solve_boundary_flux.c @@ -266,7 +266,8 @@ main(int argc, char** argv) { struct sdis_data* data = NULL; struct sdis_device* dev = NULL; - struct sdis_medium* fluid = NULL; + struct sdis_medium* fluid1 = NULL; + struct sdis_medium* fluid2 = NULL; struct sdis_medium* solid = NULL; struct sdis_interface* interf_adiabatic = NULL; struct sdis_interface* interf_Tb = NULL; @@ -299,13 +300,19 @@ main(int argc, char** argv) create_default_device(&argc, &argv, &is_master_process, &dev); radenv = create_radenv(dev); - /* Create the fluid medium */ + /* Create the fluid medium. In fact, create two fluid media, even if they are + * identical, to check that Robin's boundary conditions can be defined with + * several media, without compromising path sampling. Convective paths cannot + * be sampled in enclosures with several media, but radiative paths can be, + * and it should be possible to calculate the boundary flux without any + * problem */ OK(sdis_data_create (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); fluid_args = sdis_data_get(data); fluid_args->temperature = Tf; fluid_shader.temperature = fluid_get_temperature; - OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid)); + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid1)); + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid2)); OK(sdis_data_ref_put(data)); /* Create the solid_medium */ @@ -329,7 +336,7 @@ main(int argc, char** argv) interf_props->temperature = SDIS_TEMPERATURE_NONE; interf_props->emissivity = 0; OK(sdis_interface_create - (dev, solid, fluid, &interf_shader, data, &interf_adiabatic)); + (dev, solid, fluid1, &interf_shader, data, &interf_adiabatic)); OK(sdis_data_ref_put(data)); /* Create the Tb interface */ @@ -342,7 +349,7 @@ main(int argc, char** argv) interf_shader.back.emissivity = interface_get_emissivity; interf_shader.back.reference_temperature = interface_get_reference_temperature; OK(sdis_interface_create - (dev, solid, fluid, &interf_shader, data, &interf_Tb)); + (dev, solid, fluid1, &interf_shader, data, &interf_Tb)); interf_shader.back.emissivity = NULL; OK(sdis_data_ref_put(data)); @@ -356,13 +363,14 @@ main(int argc, char** argv) interf_shader.back.emissivity = interface_get_emissivity; interf_shader.back.reference_temperature = interface_get_reference_temperature; OK(sdis_interface_create - (dev, solid, fluid, &interf_shader, data, &interf_H)); + (dev, solid, fluid2, &interf_shader, data, &interf_H)); interf_shader.back.emissivity = NULL; OK(sdis_data_ref_put(data)); /* Release the media */ OK(sdis_medium_ref_put(solid)); - OK(sdis_medium_ref_put(fluid)); + OK(sdis_medium_ref_put(fluid1)); + OK(sdis_medium_ref_put(fluid2)); /* Map the interfaces to their box triangles */ box_interfaces[0] = box_interfaces[1] = interf_adiabatic; /* Front */ diff --git a/src/test_sdis_unsteady_analytic_profile.c b/src/test_sdis_unsteady_analytic_profile.c @@ -22,7 +22,7 @@ /* * The system is an unsteady-state temperature profile, meaning that at any - * point, at any: time, we can analytically calculate the temperature. We + * point, at any time, we can analytically calculate the temperature. We * immerse in this temperature field a supershape representing a solid in which * we want to evaluate the temperature by Monte Carlo at a given position and * observation time. On the Monte Carlo side, the temperature of the supershape