stardis-solver

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

commit 8d0142a2d1b3ced8ac9eb8e167e8702456b600b2
parent 1bcdc87731beaa41af785e8ea7ac68157a13d597
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 15 Apr 2025 15:26:44 +0200

Improve the robustness of external net flux processing

Mitigate numerical problems when calculating incident diffuse flux. A
corner position can lead to the unexpected entry of a radiative path
into a solid. This commit mitigates this problem by moving the position
slightly away from the corner when this unexpected behavior occurs.

Diffstat:
Msrc/sdis_heat_path_boundary_Xd_c.h | 19++++++++++---------
Msrc/sdis_heat_path_boundary_Xd_handle_external_net_flux.h | 149++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 131 insertions(+), 37 deletions(-)

diff --git a/src/sdis_heat_path_boundary_Xd_c.h b/src/sdis_heat_path_boundary_Xd_c.h @@ -250,7 +250,7 @@ XD(sample_reinjection_dir) #if DIM == 2 static void XD(move_away_primitive_boundaries) - (const struct rwalk* rwalk, + (const struct sXd(hit)* hit, const double delta, double position[DIM]) /* Position to move */ { @@ -259,9 +259,9 @@ XD(move_away_primitive_boundaries) float dir[DIM]; float len; const float st = 0.5f; - ASSERT(rwalk && !SXD_HIT_NONE(&rwalk->XD(hit)) && delta > 0); + ASSERT(!SXD_HIT_NONE(hit) && delta > 0); - SXD(primitive_get_attrib(&rwalk->XD(hit).prim, SXD_POSITION, st, &attr)); + SXD(primitive_get_attrib(&hit->prim, SXD_POSITION, st, &attr)); fX_set_dX(pos, position); fX(sub)(dir, attr.value, pos); @@ -275,7 +275,7 @@ XD(move_away_primitive_boundaries) * numerical issues leading to inconsistent random walks. */ static void XD(move_away_primitive_boundaries) - (const struct rwalk* rwalk, + (const struct sXd(hit)* hit, const double delta, double position[DIM]) { @@ -292,14 +292,14 @@ XD(move_away_primitive_boundaries) int imin = 0; int imid = 0; int i; - ASSERT(rwalk && delta > 0 && !S3D_HIT_NONE(&rwalk->XD(hit))); + ASSERT(delta > 0 && !S3D_HIT_NONE(hit)); fX_set_dX(P, position); /* Fetch triangle vertices */ - S3D(triangle_get_vertex_attrib(&rwalk->XD(hit).prim, 0, S3D_POSITION, &v0)); - S3D(triangle_get_vertex_attrib(&rwalk->XD(hit).prim, 1, S3D_POSITION, &v1)); - S3D(triangle_get_vertex_attrib(&rwalk->XD(hit).prim, 2, S3D_POSITION, &v2)); + S3D(triangle_get_vertex_attrib(&hit->prim, 0, S3D_POSITION, &v0)); + S3D(triangle_get_vertex_attrib(&hit->prim, 1, S3D_POSITION, &v1)); + S3D(triangle_get_vertex_attrib(&hit->prim, 2, S3D_POSITION, &v2)); /* Compute the edge vector */ f3_sub(E[0], v1.value, v0.value); @@ -479,7 +479,8 @@ XD(find_reinjection_ray) * and retry to find a valid reinjection. */ if(dst0 == -1 && dst1 == -1 && iattempt < MAX_ATTEMPTS - 1) { /* Is there still a trial to be done? */ - XD(move_away_primitive_boundaries)(args->rwalk, args->distance, ray->org); + XD(move_away_primitive_boundaries) + (&args->rwalk->XD(hit), args->distance, ray->org); ray->position_was_moved = 1; } } while(dst0 == -1 && dst1 == -1 && ++iattempt < MAX_ATTEMPTS); 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 @@ -115,7 +115,8 @@ sample_brdf static res_T check_interface (const struct sdis_interface* interf, - const struct sdis_interface_fragment* frag) + const struct sdis_interface_fragment* frag, + const int verbose) /* Control the verbosity of the function */ { enum sdis_medium_type mdm_frt_type = SDIS_MEDIUM_TYPES_COUNT__; enum sdis_medium_type mdm_bck_type = SDIS_MEDIUM_TYPES_COUNT__; @@ -128,11 +129,13 @@ check_interface /* Semi-transparent materials are not supported. This means that a solid/solid * interface must not be intersected when tracing radiative paths */ if(mdm_frt_type == SDIS_SOLID && mdm_bck_type == SDIS_SOLID) { - log_err(interf->dev, - "Error when sampling the trajectory to calculate the incident diffuse " - "flux. The trajectory reaches a solid/solid interface, whereas this is " - "supposed to be impossible (path position: %g, %g, %g).\n", + if(verbose) { + log_err(interf->dev, + "Error when sampling the trajectory to calculate the incident diffuse " + "flux. The trajectory reaches a solid/solid interface, whereas this is " + "supposed to be impossible (path position: %g, %g, %g).\n", SPLIT3(frag->P)); + } res = RES_BAD_OP; goto error; } @@ -148,12 +151,14 @@ check_interface /* Check that the current position is on the correct side of the interface */ if(frag->side != fluid_side) { - log_err(interf->dev, - "Inconsistent intersection when sampling the trajectory to calculate the " - "incident diffuse flux. The radiative path reaches an interface on " - "its solid side, whereas this is supposed to be impossible " - "(path position: %g, %g, %g).\n", - SPLIT3(frag->P)); + if(verbose) { + log_err(interf->dev, + "Inconsistent intersection when sampling the trajectory to calculate " + "the incident diffuse flux. The radiative path reaches an interface on " + "its solid side, whereas this is supposed to be impossible " + "(path position: %g, %g, %g).\n", + SPLIT3(frag->P)); + } res = RES_BAD_OP; goto error; } @@ -283,6 +288,98 @@ XD(setup_fragment) } static INLINE res_T +XD(find_next_fragment) + (const struct sdis_scene* scn, + const double in_pos[3], + const double in_dir[3], /* Always in 3D */ + const struct sXd(hit)* in_hit, + const double time, + struct sXd(hit)* out_hit, + struct sdis_interface** out_interf, + struct sdis_interface_fragment* out_frag) +{ + const int NATTEMPTS_MAX = 10; + int nattempts = 0; + + /* Stardis */ + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + struct sdis_interface* interf = NULL; + + struct sXd(hit) hit = SXD_HIT_NULL; + double rt_pos[DIM] = {0}; + res_T res = RES_OK; + + ASSERT(scn && in_pos && in_dir && in_hit && !SXD_HIT_NONE(in_hit)); + ASSERT(out_hit && out_interf && out_frag); + + dX(set)(rt_pos, in_pos); + + do { + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + struct sdis_medium* solid = NULL; + double pos[3] = {0}; + double vec[3] = {0}; + double N[3] = {0}; + double delta = 0; + + /* Reset result code. It may have been modified during a previous attempt */ + res = RES_OK; + + /* Find the following surface along the direction of propagation */ + XD(trace_ray)(scn, rt_pos, in_dir, INF, in_hit, &hit); + if(SXD_HIT_NONE(&hit)) break; + + /* Retrieve the current position and normal */ + dX(add)(pos, rt_pos, dX(muld)(vec, in_dir, hit.distance)); + dX_set_fX(N, hit.normal); + dX(normalize(N, N)); + + /* Retrieve the current interface properties */ + interf = scene_get_interface(scn, hit.prim.prim_id); + XD(setup_fragment)(&frag, pos, in_dir, time, N, &hit); + + /* Check that the path reaches a valid interface. + * An invalid fragment may mean that the ray position is in a corner and the + * traced ray has missed the surface of that corner. To correct this, the + * ray position is moved slightly away from the corner before a ray is drawn + * in the same direction. This fallback solution is executed a number of + * times, after which, if the fragment is still invalid, it is considered + * that the numerical error cannot be mitigated. */ + res = check_interface(interf, &frag, nattempts == NATTEMPTS_MAX); + if(res != RES_OK && nattempts == NATTEMPTS_MAX) goto error; + ++nattempts; + + if(res != RES_OK) { /* Mitigate numerical error (see above) */ + if(sdis_medium_get_type(interf->medium_front) == SDIS_SOLID) { + solid = interf->medium_front; + } else { + ASSERT(sdis_medium_get_type(interf->medium_back) == SDIS_SOLID); + solid = interf->medium_back; + } + + /* Retrieves the delta of the solid that surrounds the boundary, as it is + * actually the only numerical parameter that says something about the + * system. */ + vtx.P[0] = pos[0]; + vtx.P[1] = pos[1]; + vtx.P[2] = pos[2]; + vtx.time = time; + delta = solid_get_delta(solid, &vtx); + + XD(move_away_primitive_boundaries)(in_hit, delta, rt_pos); + } + } while(res != RES_OK); + +exit: + *out_hit = hit; + *out_interf = interf; + *out_frag = frag; + return res; +error: + goto exit; +} + +static INLINE res_T XD(setup_brdf) (struct sdis_device* dev, const struct sdis_source* src, @@ -333,6 +430,7 @@ XD(compute_incident_diffuse_flux) double pos[3] = {0}; /* In 3D for ray tracing ray to the source */ double dir[3] = {0}; /* Incident direction (toward the surface). Always 3D.*/ double N[3] = {0}; /* Surface normal. Always 3D */ + size_t nbounces = 0; /* For debug */ res_T res = RES_OK; ASSERT(in_pos && in_N && in_hit && diffuse_flux); @@ -361,12 +459,13 @@ XD(compute_incident_diffuse_flux) /* Miscellaneous */ double L = 0; /* incident flux to bounce position */ double wi[3] = {0}; /* Incident direction (outward the surface). Always 3D */ - double vec[DIM] = {0}; /* Temporary variable */ d3_minus(wi, dir); /* Always in 3D */ - /* Find the following surface along the direction of propagation */ - XD(trace_ray)(scn, pos, dir, INF, &hit, &hit); + res = XD(find_next_fragment) + (scn, pos, dir, &hit, time, &hit, &interf, &frag); + if(res != RES_OK) goto error; + if(SXD_HIT_NONE(&hit)) { /* No surface. Handle the radiance emitted by the source and scattered at * least once in the environment. Note that the value returned is not the @@ -382,26 +481,18 @@ XD(compute_incident_diffuse_flux) break; } - /* Retrieve the current position and normal */ - dX(add)(pos, pos, dX(muld)(vec, dir, hit.distance)); - dX_set_fX(N, hit.normal); - dX(normalize(N, N)); - - /* Retrieve the current interface properties */ - interf = scene_get_interface(scn, hit.prim.prim_id); - XD(setup_fragment)(&frag, pos, dir, time, N, &hit); - - /* Check that the path reaches a valid interface */ - res = check_interface(interf, &frag); - if(res != RES_OK) goto error; - + d3_set(pos, frag.P); XD(setup_brdf)(scn->dev, scn->source, &brdf, interf, &frag); /* Check if path is absorbed */ if(ssp_rng_canonical(rng) < brdf.emissivity) break; /* Sample rebound direction */ - if(frag.side == SDIS_BACK) dX(minus)(N, N); /* Revert normal if necessary */ + switch(frag.side) { + case SDIS_FRONT: dX(set)(N, frag.Ng); break; + case SDIS_BACK: dX(minus)(N, frag.Ng); break; + default: FATAL("Unreachable code\n"); + } sample_brdf(&brdf, rng, wi, N, &brdf_sample); d3_set(dir, brdf_sample.dir); /* Always in 3D */ @@ -437,6 +528,7 @@ XD(compute_incident_diffuse_flux) } } diffuse_flux->reflected += L; /* [W/m^2/sr] */ + ++nbounces; } diffuse_flux->reflected *= PI; /* [W/m^2] */ @@ -508,6 +600,7 @@ XD(handle_external_net_flux) 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 */