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"