stardis-solver

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

commit 3b446ef71622fcdeb051067cb8273b126dd4f9f9
parent ce13a66818d03c0d0a147fea23a6644e5f731cbd
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 10 May 2019 12:04:35 +0200

Still improve the reinjection robustness

Reject the reinjection directions if the reinjected position is
inconsistent wrt to the scene materials.

Update the displacement onto the triangle when a numerical issue is
detected.

Diffstat:
Msrc/sdis_heat_path_boundary_Xd.h | 277+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 216 insertions(+), 61 deletions(-)

diff --git a/src/sdis_heat_path_boundary_Xd.h b/src/sdis_heat_path_boundary_Xd.h @@ -33,7 +33,7 @@ /******************************************************************************* * Helper functions ******************************************************************************/ -static FINLINE void +static void XD(sample_reinjection_dir) (const struct XD(rwalk)* rwalk, struct ssp_rng* rng, float dir[DIM]) { @@ -76,7 +76,7 @@ XD(sample_reinjection_dir) } #if DIM == 2 -static FINLINE void +static void XD(move_away_primitive_boundaries) (struct XD(rwalk)* rwalk, const double delta) @@ -100,7 +100,7 @@ XD(move_away_primitive_boundaries) #else /* Move the random walk away from the primitive boundaries to avoid numerical * issues leading to inconsistent random walks. */ -static FINLINE void +static void XD(move_away_primitive_boundaries) (struct XD(rwalk)* rwalk, const double delta) @@ -110,8 +110,13 @@ XD(move_away_primitive_boundaries) float dst[3]; /* Distance from current position to edge equation */ float N[3]; /* Triangle normal */ float P[3]; /* Random walk position */ + float tmp[3]; float min_dst, max_dst; + float cos_a1, cos_a2; float len; + int imax; + int imin; + int imid; int i; ASSERT(rwalk && delta > 0 && !S3D_HIT_NONE(&rwalk->hit)); @@ -147,23 +152,50 @@ XD(move_away_primitive_boundaries) min_dst = MMIN(MMIN(dst[0], dst[1]), dst[2]); max_dst = MMAX(MMAX(dst[0], dst[1]), dst[2]); - /* Search for the edge index that is neither the nearest nor the farthest - * from the current random walk position. This will be the edge toward which - * we will move the position */ + /* Sort the edge with respect to their distance to the random walk position */ FOR_EACH(i, 0, 3) { - if(dst[i] != min_dst && dst[i] != max_dst) break; + if(dst[i] == min_dst) { + imin = i; + } else if(dst[i] == max_dst) { + imax = i; + } else { + imid = i; + } } - ASSERT(i < 3); + (void)imax; - /* TODO if the current position is near a vertex, one should move toward - * the farthest edge instead to avoid too small displacement */ + /* TODO if the current position is near a vertex, one should move toward the + * farthest edge along its normal to avoid too small displacement */ - len = -MMIN(dst[i]*0.5f, (float)(delta*0.1)); - XD(move_pos)(rwalk->vtx.P, E[i], len); + /* Compute the distance `len' from the current position to the edges to move + * to, along the normal of the edge from which the random walk is the nearest + * + * +. cos(a) = dst[imid] / len + * / `*. len = dst[imid] / cos(a) + * / | `*. + * / len | a /`*. + * / | / `*. + * / | /dst[imid]*. + * / |/ `*. + * +---------o----------------+ */ + cos_a1 = f3_dot(E[imin], f3_minus(tmp, E[imid])); + cos_a2 = f3_dot(E[imin], f3_minus(tmp, E[imax])); + + /* Compute the maximum displacement distance into the triangle along the the + * normal of the edge from which the random walk is the nearest */ + dst[imid] = cos_a1 > 0 ? dst[imid] / cos_a1 : FLT_MAX; + dst[imax] = cos_a2 > 0 ? dst[imax] / cos_a2 : FLT_MAX; + len = MMIN(dst[imid], dst[imax]); + ASSERT(len != FLT_MAX); + + /* Define the displacement distance as the minimum between 10 percent of + * delta and len / 2. */ + len = MMIN(len*0.5f, (float)(delta*0.1)); + XD(move_pos)(rwalk->vtx.P, E[imin], len); } #endif -static FINLINE res_T +static res_T XD(select_reinjection_dir) (const struct sdis_scene* scn, const struct sdis_medium* mdm, /* Medium into which the reinjection occurs */ @@ -255,9 +287,9 @@ XD(select_reinjection_dir) * is that the random walk lies roughly on an edge. In this case, sampled * reinjecton dirs can intersect the primitive on the other side of the * edge. Normally, this primitive should be filtered by the "hit_filter" - * function but this may be not the case due to "threshold effect". In both - * situations, try to slightly move away from the primitive boundaries and - * retry to find a valid reinjection. */ + * function but this may be not the case due to a "threshold effect". In + * both situations, try to slightly move away from the primitive boundaries + * and retry to find a valid reinjection. */ if(dst0 == -1 && dst1 == -1) { XD(move_away_primitive_boundaries)(rwalk, delta); if(move_pos) *move_pos = 1; @@ -339,6 +371,57 @@ error: goto exit; } +static res_T +XD(select_reinjection_dir_and_check_validity) + (const struct sdis_scene* scn, + const struct sdis_medium* mdm, /* Medium into which the reinjection occurs */ + struct XD(rwalk)* rwalk, /* Current random walk state */ + const float dir0[DIM], /* Challenged direction */ + const float dir1[DIM], /* Challanged direction */ + const double delta, /* Max reinjection distance */ + float out_reinject_dir[DIM], /* Selected direction */ + float* out_reinject_dst, /* Effective reinjection distance */ + int can_move, /* Define of the random wal pos can be moved or not */ + int* move_pos, /* Define if the current random walk was moved. May be NULL */ + int* is_valid, /* Define if the reinjection defines a valid pos */ + struct sXd(hit)* out_reinject_hit) /* Hit along the reinjection dir */ +{ + double pos[DIM]; + struct sdis_medium* reinject_mdm; + struct sXd(hit) reinject_hit; + float reinject_dir[DIM]; + float reinject_dst; + res_T res = RES_OK; + ASSERT(is_valid && out_reinject_dir && out_reinject_dst && out_reinject_hit); + + /* Select a reinjection direction */ + res = XD(select_reinjection_dir)(scn, mdm, rwalk, dir0, dir1, delta, + reinject_dir, &reinject_dst, can_move, move_pos, &reinject_hit); + if(res != RES_OK) goto error; + + if(!SXD_HIT_NONE(&reinject_hit)) { + *is_valid = 1; + } else { + /* Check medium consistency at the reinjection position */ + XD(move_pos)(dX(set)(pos, rwalk->vtx.P), reinject_dir, reinject_dst); + res = scene_get_medium_in_closed_boundaries + (scn, pos, &reinject_mdm); + if(res != RES_OK) goto error; + + *is_valid = reinject_mdm == mdm; + } + + if(*is_valid) { + fX(set)(out_reinject_dir, reinject_dir); + *out_reinject_dst = reinject_dst; + *out_reinject_hit = reinject_hit; + } + +exit: + return res; +error: + goto exit; +} /* Check that the interface fragment is consistent with the current state of * the random walk */ @@ -378,6 +461,7 @@ XD(solid_solid_boundary_path) { struct sXd(hit) hit0, hit1; struct sXd(hit)* hit; + struct XD(rwalk) rwalk_saved; struct sdis_interface* interf = NULL; struct sdis_medium* solid_front = NULL; struct sdis_medium* solid_back = NULL; @@ -394,7 +478,10 @@ XD(solid_solid_boundary_path) float* dir; float reinject_dst_front, reinject_dst_back; float reinject_dst; + const int MAX_ATTEMPTS = 10; + int iattempt; int move; + int reinjection_is_valid; res_T res = RES_OK; ASSERT(scn && fp_to_meter > 0 && ctx && frag && rwalk && rng && T); ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); @@ -418,29 +505,52 @@ XD(solid_solid_boundary_path) delta_boundary_front = delta_front*sqrt(DIM); delta_boundary_back = delta_back *sqrt(DIM); - /* Sample a reinjection direction and reflect it around the normal. Then - * reflect them on the back side of the interface. */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); - XD(reflect)(dir2, dir0, rwalk->hit.normal); - fX(minus)(dir1, dir0); - fX(minus)(dir3, dir2); - - /* Select the reinjection direction and distance for the front side */ - res = XD(select_reinjection_dir)(scn, solid_front, rwalk, dir0, dir2, - delta_boundary_front, dir_front, &reinject_dst_front, 1, NULL, &hit0); - if(res != RES_OK) goto error; - - /* Select the reinjection direction and distance for the back side */ - res = XD(select_reinjection_dir)(scn, solid_back, rwalk, dir1, dir3, - delta_boundary_back, dir_back, &reinject_dst_back, 1, &move, &hit1); - if(res != RES_OK) goto error; + rwalk_saved = *rwalk; + reinjection_is_valid = 0; + iattempt = 0; + do { + if(iattempt != 0) *rwalk = rwalk_saved; + + /* Sample a reinjection direction and reflect it around the normal. Then + * reflect them on the back side of the interface. */ + XD(sample_reinjection_dir)(rwalk, rng, dir0); + XD(reflect)(dir2, dir0, rwalk->hit.normal); + fX(minus)(dir1, dir0); + fX(minus)(dir3, dir2); + + /* Select the reinjection direction and distance for the front side */ + res = XD(select_reinjection_dir_and_check_validity)(scn, solid_front, rwalk, + dir0, dir2, delta_boundary_front, dir_front, &reinject_dst_front, 1, &move, + &reinjection_is_valid, &hit0); + if(res != RES_OK) goto error; + if(!reinjection_is_valid) continue; - /* If random walk was moved by the select_reinjection_dir on back side, one - * has to rerun the select_reinjection_dir on front side at the new pos */ - if(move) { - res = XD(select_reinjection_dir)(scn, solid_front, rwalk, dir0, dir2, - delta_boundary_front, dir_front, &reinject_dst_front, 0, NULL, &hit0); + /* Select the reinjection direction and distance for the back side */ + res = XD(select_reinjection_dir_and_check_validity)(scn, solid_back, rwalk, + dir1, dir3, delta_boundary_back, dir_back, &reinject_dst_back, 1, &move, + &reinjection_is_valid, &hit1); if(res != RES_OK) goto error; + if(!reinjection_is_valid) continue; + + /* If random walk was moved by the select_reinjection_dir on back side, one + * has to rerun the select_reinjection_dir on front side at the new pos */ + if(move) { + res = XD(select_reinjection_dir_and_check_validity)(scn, solid_front, + rwalk, dir0, dir2, delta_boundary_front, dir_front, &reinject_dst_front, + 0, NULL, &reinjection_is_valid, &hit0); + if(res != RES_OK) goto error; + if(!reinjection_is_valid) continue; + } + } while(!reinjection_is_valid && ++iattempt < MAX_ATTEMPTS); + + /* Could not find a valid reinjection */ + if(iattempt >= MAX_ATTEMPTS) { + *rwalk = rwalk_saved; + log_err(scn->dev, + "%s: could not find a valid soid/solid reinjection at {%g, %g, %g}.\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; } /* Define the reinjection side. Note that the proba should be : @@ -522,6 +632,7 @@ XD(solid_fluid_boundary_path) struct sdis_medium* mdm_back = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; + struct XD(rwalk) rwalk_saved; struct sXd(hit) hit = SXD_HIT_NULL; struct sdis_interface_fragment frag_fluid; double hc; @@ -536,6 +647,9 @@ XD(solid_fluid_boundary_path) double tmp; float dir0[DIM], dir1[DIM]; float reinject_dst; + const int MAX_ATTEMPTS = 10; + int iattempt; + int reinjection_is_valid = 0; res_T res = RES_OK; ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T && ctx); ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); @@ -566,21 +680,40 @@ XD(solid_fluid_boundary_path) * delta. */ delta_boundary = sqrt(DIM) * delta; - /* Sample a reinjection direction */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); + rwalk_saved = *rwalk; + reinjection_is_valid = 0; + iattempt = 0; + do { + if(iattempt != 0) *rwalk = rwalk_saved; - /* Reflect the sampled direction around the normal */ - XD(reflect)(dir1, dir0, rwalk->hit.normal); + /* Sample a reinjection direction */ + XD(sample_reinjection_dir)(rwalk, rng, dir0); - if(solid == mdm_back) { - fX(minus)(dir0, dir0); - fX(minus)(dir1, dir1); - } + /* Reflect the sampled direction around the normal */ + XD(reflect)(dir1, dir0, rwalk->hit.normal); - /* Select the solid reinjection direction and distance */ - res = XD(select_reinjection_dir)(scn, solid, rwalk, dir0, dir1, - delta_boundary, dir0, &reinject_dst, 1, NULL, &hit); - if(res != RES_OK) goto error; + if(solid == mdm_back) { + fX(minus)(dir0, dir0); + fX(minus)(dir1, dir1); + } + + /* Select the solid reinjection direction and distance */ + res = XD(select_reinjection_dir_and_check_validity)(scn, solid, rwalk, + dir0, dir1, delta_boundary, dir0, &reinject_dst, 1, NULL, + &reinjection_is_valid, &hit); + if(res != RES_OK) goto error; + + } while(!reinjection_is_valid && ++iattempt < MAX_ATTEMPTS); + + /* Could not find a valid reinjecton */ + if(iattempt >= MAX_ATTEMPTS) { + *rwalk = rwalk_saved; + log_err(scn->dev, + "%s: could not find a valid solid/fluid reinjection at {%g, %g %g}.\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } /* Define the orthogonal dst from the reinjection pos to the interface */ delta = reinject_dst / sqrt(DIM); @@ -658,6 +791,7 @@ XD(solid_boundary_with_flux_path) struct ssp_rng* rng, struct XD(temperature)* T) { + struct XD(rwalk) rwalk_saved; struct sdis_interface* interf = NULL; struct sdis_medium* mdm = NULL; double lambda; @@ -670,6 +804,9 @@ XD(solid_boundary_with_flux_path) float dir0[DIM]; float dir1[DIM]; float reinject_dst; + const int MAX_ATTEMPTS = 10; + int iattempt = 0; + int reinjection_is_valid = 0; res_T res = RES_OK; ASSERT(frag && phi != SDIS_FLUX_NONE); ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); @@ -691,21 +828,39 @@ XD(solid_boundary_with_flux_path) * distance from the boundary to the point to chalenge is equal to delta. */ delta_boundary = delta * sqrt(DIM); - /* Sample a reinjection direction */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); + rwalk_saved = *rwalk; + reinjection_is_valid = 0; + iattempt = 0; + do { + if(iattempt != 0) *rwalk = rwalk_saved; + /* Sample a reinjection direction */ + XD(sample_reinjection_dir)(rwalk, rng, dir0); - /* Reflect the sampled direction around the normal */ - XD(reflect)(dir1, dir0, rwalk->hit.normal); + /* Reflect the sampled direction around the normal */ + XD(reflect)(dir1, dir0, rwalk->hit.normal); - if(frag->side == SDIS_BACK) { - fX(minus)(dir0, dir0); - fX(minus)(dir1, dir1); - } + if(frag->side == SDIS_BACK) { + fX(minus)(dir0, dir0); + fX(minus)(dir1, dir1); + } - /* Select the reinjection direction and distance */ - res = XD(select_reinjection_dir)(scn, mdm, rwalk, dir0, dir1, - delta_boundary, dir0, &reinject_dst, 1, NULL, &hit); - if(res != RES_OK) goto error; + /* Select the reinjection direction and distance */ + res = XD(select_reinjection_dir_and_check_validity)(scn, mdm, rwalk, dir0, + dir1, delta_boundary, dir0, &reinject_dst, 1, NULL, + &reinjection_is_valid, &hit); + if(res != RES_OK) goto error; + + } while(!reinjection_is_valid && ++iattempt < MAX_ATTEMPTS); + + /* Could not find a valid reinjecton */ + if(iattempt >= MAX_ATTEMPTS) { + *rwalk = rwalk_saved; + log_err(scn->dev, + "%s: could not find a valid solid/fluid with flux reinjection " + "at {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } /* Define the orthogonal dst from the reinjection pos to the interface */ delta = reinject_dst / sqrt(DIM);