stardis-solver

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

sdis_heat_path_conductive_wos_Xd.h (24589B)


      1 /* Copyright (C) 2016-2025 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "sdis_device_c.h"
     17 #include "sdis_heat_path_conductive_c.h"
     18 #include "sdis_medium_c.h"
     19 #include "sdis_scene_c.h"
     20 
     21 #include <star/swf.h>
     22 
     23 #include "sdis_Xd_begin.h"
     24 
     25 /* Define epsilon shell from delta */
     26 #define EPSILON_SHELL(Delta) ((Delta)*1e-2)
     27 
     28 /*******************************************************************************
     29  * Non generic helper functions
     30  ******************************************************************************/
     31 #ifndef SDIS_HEAT_PATH_CONDUCTIVE_WOS_XD_H
     32 #define SDIS_HEAT_PATH_CONDUCTIVE_WOS_XD_H
     33 
     34 static res_T
     35 update_green_path
     36   (struct green_path_handle* green_path,
     37    struct rwalk* rwalk,
     38    struct sdis_medium* mdm,
     39    const struct solid_props* props,
     40    const double power_term,
     41    const struct temperature* T)
     42 {
     43   res_T res = RES_OK;
     44   ASSERT(mdm && props && T);
     45 
     46   /* Is the green function estimated? */
     47   if(!green_path) goto exit;
     48 
     49   /* Save power term for green function if any */
     50   if(props->power != SDIS_VOLUMIC_POWER_NONE) {
     51     res = green_path_add_power_term(green_path, mdm, &rwalk->vtx, power_term);
     52     if(res != RES_OK) goto error;
     53   }
     54 
     55   /* Set the green path limit to the current position if the initial condition
     56    * has been reached */
     57   if(T->done) {
     58     res = green_path_set_limit_vertex
     59       (green_path, mdm, &rwalk->vtx, rwalk->elapsed_time);
     60     if(res != RES_OK) goto error;
     61   }
     62 
     63 exit:
     64   return res;
     65 error:
     66   goto exit;
     67 }
     68 
     69 #endif /* SDIS_HEAT_PATH_CONDUCTIVE_WOS_XD_H */
     70 
     71 /*******************************************************************************
     72  * Helper function
     73  ******************************************************************************/
     74 static res_T
     75 XD(check_enclosure_consistency)
     76   (struct sdis_scene* scn,
     77    const struct rwalk* rwalk)
     78 {
     79   unsigned enc_id = ENCLOSURE_ID_NULL;
     80   res_T res = RES_OK;
     81   ASSERT(rwalk);
     82 
     83   res = scene_get_enclosure_id_in_closed_boundaries(scn, rwalk->vtx.P, &enc_id);
     84   if(res != RES_OK) goto error;
     85 
     86   /* Check enclosure consistency */
     87   if(enc_id != rwalk->enc_id) {
     88     log_err(scn->dev,
     89       "%s:%s: invalid solid walk. Unexpected enclosure -- pos=("FORMAT_VECX")\n",
     90       __FILE__, FUNC_NAME, SPLITX(rwalk->vtx.P));
     91     res = RES_BAD_OP_IRRECOVERABLE;
     92     goto error;
     93   }
     94 
     95 exit:
     96   return res;
     97 error:
     98   goto exit;
     99 }
    100 
    101 static res_T
    102 XD(time_travel)
    103   (struct sdis_scene* scn,
    104    struct rwalk* rwalk,
    105    struct ssp_rng* rng,
    106    struct sdis_medium* mdm,
    107    const double alpha, /* Diffusivity, i.e. lambda/(rho*cp) */
    108    const double t0, /* Initial time [s] */
    109    const double pos[3], /* Position before the diffusive step */
    110    double* distance, /* Displacement [m/fp_to_meter] */
    111    struct temperature* T)
    112 {
    113   double dir[DIM] = {0};
    114   double dst = 0; /* Distance [m] */
    115   double tau = 0; /* Time [s] */
    116   double x = 0;
    117   double r = 0;
    118   double temperature = 0; /* [k] */
    119   double time = 0; /* [s] */
    120   res_T res = RES_OK;
    121   ASSERT(scn && rwalk && rng && alpha > 0 && pos && distance && T);
    122 
    123   dst = *distance * scn->fp_to_meter;
    124   ASSERT(dst >= 0);
    125 
    126   /* No displacement => no time travel */
    127   if(dst == 0) goto exit;
    128 
    129   /* Sample x = tau*alpha/distance^2 */
    130   r = ssp_rng_canonical(rng);
    131   x = swf_tabulation_inverse(XD(scn->dev->H), SWF_QUADRATIC, r);
    132 
    133   /* Retrieve the time to travel */
    134   tau = x / alpha * dst * dst;
    135   time = MMIN(tau, rwalk->vtx.time - t0);
    136 
    137   /* Increment the elapsed time */
    138   rwalk->elapsed_time += time;
    139 
    140   if(IS_INF(rwalk->vtx.time)) goto exit; /* Steady computation */
    141 
    142   /* Let's take a trip back in time */
    143   rwalk->vtx.time = MMAX(t0, rwalk->vtx.time - tau);
    144 
    145   /* The path does not reach the initial condition */
    146   if(rwalk->vtx.time > t0) goto exit;
    147 
    148   /* The path reaches the initial condition. Sample a distance corresponding to
    149    * the travel time to the initial condition.
    150    *
    151    * TODO while we use the H function to sample the distance, one should use the
    152    * U function. For the moment, this function is not available, hence the use
    153    * of H. This is not a problem, since we currently assume that the initial
    154    * condition is uniform. Position is only used for path geometry */
    155   r = ssp_rng_canonical(rng);
    156   x = swf_tabulation_inverse(XD(scn->dev->H), SWF_QUADRATIC, r);
    157   dst = sqrt(alpha * time / x);
    158   *distance = dst / scn->fp_to_meter; /* Update travel distance */
    159 
    160   /* Uniformly sample a direction and move along it of the distance that
    161    * separate the path position before diffusion position to its initial
    162    * condition */
    163 #if DIM == 2
    164   ssp_ran_circle_uniform(rng, dir, NULL);
    165 #else
    166   ssp_ran_sphere_uniform(rng, dir, NULL);
    167 #endif
    168   dX(muld)(dir, dir, *distance);
    169   dX(add)(rwalk->vtx.P, pos, dir);
    170 
    171   /* Fetch the initial temperature */
    172   temperature = medium_get_temperature(mdm, &rwalk->vtx);
    173   if(SDIS_TEMPERATURE_IS_UNKNOWN(temperature)) {
    174     log_err(scn->dev,
    175       "%s:%s: the path reaches the initial condition but the "
    176       "%s temperature remains unknown -- pos=("FORMAT_VECX")\n",
    177       __FILE__, FUNC_NAME,
    178       medium_type_to_string(sdis_medium_get_type(mdm)),
    179       SPLITX(rwalk->vtx.P));
    180     res = RES_BAD_ARG;
    181     goto error;
    182   }
    183 
    184   /* Update the temperature */
    185   T->value += temperature;
    186   T->done = 1;
    187 
    188 exit:
    189   return res;
    190 error:
    191   goto exit;
    192 }
    193 
    194 static res_T
    195 XD(handle_volumic_power_wos)
    196   (struct sdis_scene* scn,
    197    const struct solid_props* props,
    198    const double distance, /* [m/fp_to_meter] */
    199    double* power_term,
    200    struct temperature* T)
    201 {
    202   double dst = distance * scn->fp_to_meter; /* [m] */
    203   double term = 0;
    204   res_T res = RES_OK;
    205   ASSERT(scn && props && distance >= 0 && power_term && T);
    206 
    207   if(props->power == SDIS_VOLUMIC_POWER_NONE) goto exit;
    208 
    209   /* No displacement => no power density */
    210   if(distance == 0) goto exit;
    211 
    212   term = dst*dst / (2*DIM*props->lambda);
    213   T->value += props->power * term;
    214 
    215 exit:
    216   *power_term = term;
    217   return res;
    218 }
    219 
    220 #if DIM == 2
    221 static INLINE enum sdis_side
    222 compute_hit_side_2d
    223   (const struct s2d_hit* hit,
    224    const double delta,
    225    const double pos[2]) /* Position from which intersection occurs */
    226 {
    227   struct s2d_attrib p0, p1; /* Segment positions */
    228   double v0[2] = {0}; /* Vector from segment vertex 0 to segment vertex 1 */
    229   double v1[2] = {0}; /* Vector from segment vertex 0 to input position */
    230   double z = 0;
    231 
    232   /* Check pre-conditions */
    233   ASSERT(hit && delta > 0 && pos && !S2D_HIT_NONE(hit));
    234 
    235   /* Delta is not yet used. It could be used to check confidence in the impact
    236    * side calculation. A small value of Z (in relation to epsilon) means that the
    237    * position of the input is close to the boundary and that the calculation of
    238    * the side must be carried out with care. So far, however, no such problems
    239    * have been observed in 2D. */
    240   (void)delta;
    241 
    242   /* Retrieve the positions of the intersected segment */
    243   S2D(segment_get_vertex_attrib(&hit->prim, 0, S2D_POSITION, &p0));
    244   S2D(segment_get_vertex_attrib(&hit->prim, 1, S2D_POSITION, &p1));
    245 
    246   v0[0] = p1.value[0] - p0.value[0];
    247   v0[1] = p1.value[1] - p0.value[1];
    248   v1[0] = pos[0] - p0.value[0];
    249   v1[1] = pos[1] - p0.value[1];
    250 
    251   /* Z coordinate of the cross product between v0 and v1. Its sign indicates on
    252    * which side of the segment the position lies. */
    253   z = d2_cross(v1, v0);
    254   return z > 0 ? SDIS_FRONT : SDIS_BACK;
    255 }
    256 #endif
    257 
    258 #if DIM == 3
    259 /* May return SDIS_SIDE_NULL__ if the side cannot be calculated with confidence,
    260  * i.e. if the input position is too close to the boundary and the calculation
    261  * may therefore present numerical problems */
    262 static INLINE enum sdis_side
    263 compute_hit_side_3d
    264   (const struct s3d_hit* hit,
    265    const double delta,
    266    const double pos[3]) /* Position from which intersection occurs */
    267 {
    268   struct s3d_attrib v0; /* Position of the 1st triangle vertex */
    269   double p[3] = {0}; /* Position of the 1st triangle vertex in double */
    270   double N[3] = {0}; /* Normalized triangle normal */
    271   double D = 0; /* Last parameter of the plane triangle plane equation */
    272   double dst = 0; /* Distance of pos to the plane */
    273 
    274   /* Check pre-conditions */
    275   ASSERT(hit && delta > 0 && pos && !S3D_HIT_NONE(hit));
    276 
    277   /* The distance is close to the border and its calculation can suffer from
    278    * numerical problems. No side can therefore be estimated with confidence */
    279   if(hit->distance < 1e-4 && eq_eps(hit->distance, 0, EPSILON_SHELL(delta))) {
    280     return SDIS_SIDE_NULL__;
    281   }
    282 
    283   /* Retrieve the positions of the intersected triangle */
    284   S3D(triangle_get_vertex_attrib(&hit->prim, 0, S3D_POSITION, &v0));
    285   d3_set_f3(p, v0.value);
    286 
    287   /* Compute the plane equation of the triangle */
    288   d3_set_f3(N, hit->normal);
    289   d3_normalize(N, N);
    290   D = -d3_dot(N, p);
    291 
    292   /* Calculate the distance of the input position from the plane of the triangle
    293    * and use the sign to define which side of the triangle the position is on */
    294   dst = d3_dot(N, pos) + D;
    295   return dst > 0 ? SDIS_FRONT : SDIS_BACK;
    296 }
    297 #endif
    298 
    299 /* Verify that the submitted position is in the expected enclosure */
    300 static res_T
    301 XD(check_diffusion_position)
    302   (struct sdis_scene* scn,
    303    const unsigned expected_enc_id,
    304    const double delta, /* Used to adjust thresholds */
    305    const double pos[DIM])
    306 {
    307   unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL};
    308   enum sdis_side side = SDIS_SIDE_NULL__;
    309 
    310   struct sXd(hit) hit = SXD_HIT_NULL;
    311   struct hit_filter_data filter_data = HIT_FILTER_DATA_NULL;
    312   float wos_pos[DIM] = {0};
    313   float wos_radius = 0;
    314   res_T res = RES_OK;
    315 
    316   /* Check pre-conditions */
    317   ASSERT(scn && pos);
    318   ASSERT(expected_enc_id != ENCLOSURE_ID_NULL);
    319 
    320   /* Filter positions on/near a primitive boundary that don't look towards the
    321    * query position */
    322   filter_data.scn = scn;
    323   filter_data.enc_id = expected_enc_id;
    324 
    325   /* Look for the nearest surface of the position to be checked. By limiting the
    326    * search radius to delta we speed up the closest point query. If no surface
    327    * is found, we assume that the position is in the intended medium.  We rely
    328    * on this assumption because this function is used to verify positions during
    329    * diffusive random walks. Diffusion algorithms ensure that positions are in
    330    * the current medium. This function is only concerned with numerical problems
    331    * which, once the new position has been calculated, position the random walk
    332    * beyond the medium. In other words, the path jumps a boundary that lies
    333    * within the numerical imprecision of the calculation, i.e. very close to the
    334    * position to be verified. So, if no surface is found close to this position,
    335    * it means that there is no nearby boundary and, consequently, no numerical
    336    * problem of this kind could have arisen. */
    337   wos_radius = (float)delta;
    338   fX_set_dX(wos_pos, pos);
    339   SXD(scene_view_closest_point
    340     (scn->sXd(view), wos_pos, wos_radius, &filter_data, &hit));
    341   if(SXD_HIT_NONE(&hit)) goto exit;
    342 
    343   /* Check path consistency */
    344   scene_get_enclosure_ids(scn, hit.prim.prim_id, enc_ids);
    345   side = XD(compute_hit_side)(&hit, delta, pos);
    346   if(side != SDIS_SIDE_NULL__ && enc_ids[side] != expected_enc_id) {
    347     res = RES_BAD_ARG;
    348     goto error;
    349   }
    350 
    351   /* Position is close of the border: check both sides to handle numerical
    352    * problems in calculating hit side */
    353   if(side == SDIS_SIDE_NULL__
    354   && enc_ids[SDIS_FRONT] != expected_enc_id
    355   && enc_ids[SDIS_BACK]  != expected_enc_id) {
    356      res = RES_BAD_ARG;
    357      goto error;
    358   }
    359 
    360 exit:
    361   return res;
    362 error:
    363   goto exit;
    364 }
    365 
    366 static res_T
    367 XD(setup_hit_wos)
    368   (struct sdis_scene* scn,
    369    const struct sXd(hit)* hit,
    370    const double delta,
    371    struct rwalk* rwalk)
    372 {
    373   /* Geometry */
    374   struct sXd(primitive) prim;
    375   struct sXd(attrib) attr;
    376 
    377   /* Properties */
    378   unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL};
    379   enum sdis_side side = SDIS_SIDE_NULL__;
    380 
    381   /* Miscellaneous */
    382   double tgt[DIM] = {0}; /* Target point, i.e. hit position */
    383   res_T res = RES_OK;
    384 
    385   /* Check pre-conditions */
    386   ASSERT(rwalk && hit);
    387 
    388   /* Find intersected position */
    389   SXD(scene_view_get_primitive(scn->sXd(view), hit->prim.prim_id, &prim));
    390 #if DIM == 2
    391   SXD(primitive_get_attrib(&prim, SXD_POSITION, hit->u, &attr));
    392 #else
    393   SXD(primitive_get_attrib(&prim, SXD_POSITION, hit->uv, &attr));
    394 #endif
    395 
    396   scene_get_enclosure_ids(scn, hit->prim.prim_id, enc_ids);
    397 
    398   /* Calculate on which side the intersection occurs */
    399   dX_set_fX(tgt, attr.value);
    400   side = XD(compute_hit_side)(hit, delta, rwalk->vtx.P);
    401 
    402   /* Calculating the side of the intersection can suffer from numerical problems
    403    * when the position of the path is close to the intersected surface (i.e.
    404    * side == SDIS_SIDE_NULL). It is therefore reasonable to assume that there is
    405    * no cause for concern as long as the enclosure identifier on the other side
    406    * of the triangle is the expected one. */
    407   if(side == SDIS_SIDE_NULL__) {
    408     if(rwalk->enc_id == enc_ids[SDIS_FRONT]) side = SDIS_FRONT;
    409     if(rwalk->enc_id == enc_ids[SDIS_BACK]) side = SDIS_BACK;
    410   }
    411 
    412   /* Check path consistency */
    413   if(side == SDIS_SIDE_NULL__ || enc_ids[side] != rwalk->enc_id) {
    414     res = RES_BAD_OP_IRRECOVERABLE;
    415     log_err(scn->dev,
    416       "%s:%s: the conductive path has reached an invalid interface. "
    417       "Unexpected enclosure -- pos=("FORMAT_VECX"), side=%s\n",
    418       __FILE__, FUNC_NAME, SPLITX(tgt),
    419       side == SDIS_FRONT ? "front" : "back");
    420     goto error;
    421   }
    422 
    423   /* Random walk update. Do not set the medium to NULL as the intersection is
    424    * found regardless of time, so the initial condition could be reached before
    425    * the interface. So we can't yet assume that the random walk has left the
    426    * current medium */
    427   dX(set)(rwalk->vtx.P, tgt);
    428   rwalk->XD(hit) = *hit;
    429   rwalk->hit_side = side;
    430 
    431 exit:
    432   return res;
    433 error:
    434   goto exit;
    435 }
    436 
    437 static res_T
    438 XD(setup_hit_rt)
    439   (struct sdis_scene* scn,
    440    const double pos[DIM],
    441    const double dir[DIM],
    442    const struct sXd(hit)* hit,
    443    struct rwalk* rwalk)
    444 {
    445   /* Properties */
    446   unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL};
    447   enum sdis_side side = SDIS_SIDE_NULL__;
    448 
    449   /* Miscellaneous */
    450   double tgt[DIM] = {0}; /* Target point, i.e. hit position */
    451   double N[DIM] = {0};
    452   res_T res = RES_OK;
    453 
    454   /* Check pre-conditions */
    455   ASSERT(pos && dir && rwalk && hit);
    456   ASSERT(dX(is_normalized)(dir));
    457 
    458   /* Calculate on which side the intersection occurs */
    459   dX(muld)(tgt, dir, hit->distance);
    460   dX(add)(tgt, tgt, pos);
    461   dX_set_fX(N, hit->normal);
    462   dX(normalize)(N, N);
    463   side = dX(dot)(N, dir) > 0 ? SDIS_BACK : SDIS_FRONT;
    464 
    465   /* Fetch interface properties and check path consistency */
    466   scene_get_enclosure_ids(scn, hit->prim.prim_id, enc_ids);
    467   if(enc_ids[side] != rwalk->enc_id) {
    468     res = RES_BAD_OP;
    469     goto error;
    470   }
    471 
    472   /* Random walk update. Do not set the medium to NULL as the intersection is
    473    * found regardless of time, so the initial condition could be reached before
    474    * the interface. So we can't yet assume that the random walk has left the
    475    * current medium */
    476   dX(set)(rwalk->vtx.P, tgt);
    477   rwalk->XD(hit) = *hit;
    478   rwalk->hit_side = side;
    479 
    480 exit:
    481   return res;
    482 error:
    483   goto exit;
    484 }
    485 
    486 static res_T
    487 XD(sample_next_position)
    488   (struct sdis_scene* scn,
    489    struct rwalk* rwalk,
    490    struct ssp_rng* rng,
    491    const double delta, /* Used to adjust thresholds */
    492    double* distance) /* Displacement distance */
    493 {
    494   /* Intersection */
    495   struct sXd(hit) hit = SXD_HIT_NULL;
    496 
    497   /* Walk on sphere */
    498   double wos_distance = 0;
    499   double wos_epsilon = 0;
    500   float wos_pos[DIM] = {0};
    501   float wos_radius = 0;
    502 
    503   /* Miscellaneous */
    504   res_T res = RES_OK;
    505   ASSERT(rwalk && rng && distance);
    506 
    507   /* Find the closest distance from the current position to the geometry */
    508   wos_radius = (float)INF;
    509   fX_set_dX(wos_pos, rwalk->vtx.P);
    510   SXD(scene_view_closest_point(scn->sXd(view), wos_pos, wos_radius, NULL, &hit));
    511   CHK(!SXD_HIT_NONE(&hit));
    512   wos_distance = hit.distance;
    513 
    514   /* The current position is in the epsilon shell,
    515    * move it to the nearest interface position */
    516   wos_epsilon = EPSILON_SHELL(delta);
    517   if(wos_distance <= wos_epsilon) {
    518     res = XD(setup_hit_wos)(scn, &hit, delta, rwalk);
    519     if(res != RES_OK) goto error;
    520 
    521   /* Uniformly sample a new position on the surrounding sphere */
    522   } else {
    523     double pos[DIM] = {0};
    524     double dir[DIM] = {0};
    525 
    526 #if DIM == 2
    527     ssp_ran_circle_uniform(rng, dir, NULL);
    528 #else
    529     ssp_ran_sphere_uniform(rng, dir, NULL);
    530 #endif
    531     dX(muld)(pos, dir, (double)hit.distance);
    532     dX(add)(pos, pos, rwalk->vtx.P);
    533 
    534     /* Check that the new position is in the intended medium. Please note that
    535      * we do not use the scene_get_medium_in_closed_boundaries function. It uses
    536      * the ray-tracing operator, which has its own numerical uncertainty that is
    537      * not the same as that of the closest point operator used by this
    538      * scattering algorithm. It can therefore return the expected medium,
    539      * whereas the nearest point operator would return an inconsistent medium.
    540      * The next diffusion step would then detect an error. This is why we use a
    541      * new function based on the same geometric operator used in the present
    542      * algorithm. */
    543     res = XD(check_diffusion_position)(scn, rwalk->enc_id, delta, pos);
    544 
    545     /* Diffusion position is valid => move the path to the new position */
    546     if(res == RES_OK) {
    547       dX(set)(rwalk->vtx.P, pos);
    548 
    549     /* As a result, the new position is detected as being in the wrong medium.
    550      * This means that there has been a numerical problem in moving the
    551      * position, which has therefore jumped the solid boundary. To solve this
    552      * problem, we can move the trajectory on the solid interface along the
    553      * direction of displacement. Indeed, we can assume that the position we
    554      * want to move to is actually inside the epsilon shell. In this case, the
    555      * trajectory will be moved to this interface in the next step anyway. */
    556     } else {
    557       struct sXd(hit) hit_rt = SXD_HIT_NULL;
    558       float rt_pos[DIM] = {0};
    559       float rt_dir[DIM] = {0};
    560       float rt_range[2] = {0, 0};
    561 
    562       fX_set_dX(rt_pos, rwalk->vtx.P);
    563       fX_set_dX(rt_dir, dir);
    564       rt_range[0] = 0;
    565       rt_range[1] = (float)INF;
    566       SXD(scene_view_trace_ray
    567         (scn->sXd(view), rt_pos, rt_dir, rt_range, NULL, &hit_rt));
    568 
    569       if(SXD_HIT_NONE(&hit_rt)) {
    570         /* The lack of intersection is probably due to a current position close
    571          * to the boundary. And although it is detected in the solid by WoS, the
    572          * specific numerical errors of the ray-tracing operator may contradict
    573          * the WoS algorithm, which relies on the closest point operator. But
    574          * since the position is close to the boundary, it can be snaped to it*/
    575         res = XD(setup_hit_wos)(scn, &hit, delta, rwalk);
    576         if(res != RES_OK) goto error;
    577 
    578       } else {
    579         res = XD(setup_hit_rt)(scn, rwalk->vtx.P, dir, &hit_rt, rwalk);
    580         if(res != RES_OK) {
    581           /* An error occurs while handling ray intersection. This means that
    582            * the Ray-Tracing operator find an invalid intersection regarding the
    583            * current enclosure in which the path should be sampled. As in the
    584            * case of the lack of intersection (see above) this means that the
    585            * position is close to the enclosure boundary and that the ray missed
    586            * it. So, As previously, the position can be simply snaped to it
    587            * since one can assumes that the current position is in the right
    588            * enclosure */
    589           res = XD(setup_hit_wos)(scn, &hit, delta, rwalk);
    590           if(res != RES_OK) goto error;
    591         }
    592       }
    593     }
    594   }
    595 
    596 exit:
    597   *distance = hit.distance;
    598   return res;
    599 error:
    600   goto exit;
    601 }
    602 
    603 /*******************************************************************************
    604  * Local function
    605  ******************************************************************************/
    606 res_T
    607 XD(conductive_path_wos)
    608   (struct sdis_scene* scn,
    609    struct rwalk_context* ctx,
    610    struct rwalk* rwalk,
    611    struct ssp_rng* rng,
    612    struct temperature* T)
    613 {
    614   /* Properties */
    615   const struct enclosure* enc = NULL;
    616   struct sdis_medium* mdm = NULL;
    617   struct solid_props props_ref = SOLID_PROPS_NULL;
    618   struct solid_props props = SOLID_PROPS_NULL;
    619   double alpha = 0; /* diffusivity, i.e. lambda/(rho*cp) */
    620 
    621   /* Miscellaneous */
    622   size_t ndiffusion_steps = 0; /* For debug */
    623   double green_power_term = 0;
    624   int green = 0;
    625   const int wos = 1;
    626   res_T res = RES_OK;
    627   (void)ctx; /* Avoid the "unused variable" warning */
    628 
    629   /* Check pre-conditions */
    630   ASSERT(scn && ctx && rwalk && rng && T);
    631 
    632   /* Is green evaluated evaluated */
    633   green = ctx->green_path != NULL;
    634 
    635   res = XD(check_enclosure_consistency)(scn, rwalk);
    636   if(res != RES_OK) goto error;
    637 
    638   /* Get the enclosure medium */
    639   enc = scene_get_enclosure(scn, rwalk->enc_id);
    640   res = scene_get_enclosure_medium(scn, enc, &mdm);
    641   if(res != RES_OK) goto error;
    642   ASSERT(sdis_medium_get_type(mdm) == SDIS_SOLID);
    643 
    644   /* Retrieve the solid properties at the current position. Use them to verify
    645    * that those that are supposed to be constant by the conductive random walk
    646    * remain the same. Note that we take care of the same constraints on the
    647    * solid reinjection since once reinjected, the position of the random walk
    648    * is that at the beginning of the conductive random walk. Thus, after a
    649    * reinjection, the next line retrieves the properties of the reinjection
    650    * position. By comparing them to the properties along the random walk, we
    651    * thus verify that the properties are constant throughout the random walk
    652    * with respect to the properties of the reinjected position. */
    653   solid_get_properties(mdm, &rwalk->vtx, &props_ref);
    654   props = props_ref;
    655 
    656   /* The algorithm assumes that lambda, rho and cp are constants. The
    657    * diffusivity of the material (alpha) can therefore be calculated once */
    658   alpha = props_ref.lambda / (props_ref.rho * props_ref.cp);
    659 
    660   /* Sample a diffusive path */
    661   for(;;) {
    662     double power_term = 0; /* */
    663     double pos[3] = {0,0,0}; /* Position before diffusive step */
    664     double dst = 0; /* [m/fp_to_meter] */
    665 
    666     /* Register the new vertex against the heat path */
    667     #define REGISTER_HEAT_VERTEX {                                             \
    668       res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value,        \
    669         SDIS_HEAT_VERTEX_CONDUCTION, (int)ctx->nbranchings);                   \
    670       if(res != RES_OK) goto error;                                            \
    671     } (void)0
    672 
    673     /* The temperature is known */
    674     if(SDIS_TEMPERATURE_IS_KNOWN(props.temperature)) {
    675       REGISTER_HEAT_VERTEX;
    676       T->value += props.temperature;
    677       T->done = 1;
    678       break;
    679     }
    680 
    681     d3_set(pos, rwalk->vtx.P);
    682 
    683     /* Find the next position of the conductive path */
    684     res = XD(sample_next_position)(scn, rwalk, rng, props.delta, &dst);
    685     if(res != RES_OK) goto error;
    686 
    687     /* Going back in time */
    688     res = XD(time_travel)(scn, rwalk, rng, mdm, alpha, props.t0, pos, &dst, T);
    689     if(res != RES_OK) goto error;
    690 
    691     /* Add the volumic power density */
    692     res = XD(handle_volumic_power_wos)(scn, &props, dst, &power_term, T);
    693     if(res != RES_OK) goto error;
    694 
    695     REGISTER_HEAT_VERTEX;
    696 
    697     /* Accumulate the power term */
    698     if(green) green_power_term += power_term;
    699 
    700     /* The path reaches the initial condition */
    701     if(T->done) {
    702       T->func = NULL;
    703       break;
    704     }
    705 
    706     /* The path reaches a boundary */
    707     if(!SXD_HIT_NONE(&rwalk->XD(hit))) {
    708       T->func = XD(boundary_path);
    709       rwalk->enc_id = ENCLOSURE_ID_NULL;
    710       break;
    711     }
    712 
    713     #undef REGISTER_VERTEX
    714 
    715     /* Retreive and check solid properties at the new position */
    716     res = solid_get_properties(mdm, &rwalk->vtx, &props);
    717     if(res != RES_OK) goto error;
    718     res = check_solid_constant_properties(scn->dev, green, wos, &props_ref, &props);
    719     if(res != RES_OK) goto error;
    720 
    721     ++ndiffusion_steps; /* For debug */
    722   }
    723 
    724   /* Save green function data */
    725   res = update_green_path
    726     (ctx->green_path, rwalk, mdm, &props_ref, green_power_term, T);
    727 
    728 exit:
    729   return res;
    730 error:
    731   goto exit;
    732 }
    733 
    734 #include "sdis_Xd_end.h"