stardis-compute.c (39898B)
1 /* Copyright (C) 2018-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 "stardis-app.h" 17 #include "stardis-args.h" 18 #include "stardis-description.h" 19 #include "stardis-output.h" 20 #include "stardis-compute.h" 21 #include "stardis-parsing.h" 22 #include "stardis-default.h" 23 #include "stardis-fluid.h" 24 #include "stardis-fluid-prog.h" 25 #include "stardis-solid.h" 26 #include "stardis-solid-prog.h" 27 #include "stardis-sfconnect.h" 28 #include "stardis-ssconnect.h" 29 30 #include <sdis.h> 31 #include <sdis_version.h> 32 33 #include <star/s3d.h> 34 #include <star/sg3d_sXd_helper.h> 35 #include <star/ssp.h> 36 37 #include <rsys/double3.h> 38 #include <rsys/double2.h> 39 #include <rsys/logger.h> 40 #include <rsys/image.h> 41 #include <rsys/clock_time.h> 42 43 #ifdef COMPILER_GCC 44 #include <strings.h> /* strcasecmp */ 45 #else 46 #include <string.h> 47 #define strcasecmp(s1, s2) _stricmp((s1), (s2)) 48 #endif 49 50 /******************************************************************************* 51 * Local type; custom data for raytracing callbacks 52 ******************************************************************************/ 53 struct filter_ctx { 54 const struct stardis* stardis; 55 unsigned prim; 56 const struct description* desc; 57 float pos[3]; 58 float dist; 59 int outside; 60 int probe_on_boundary; 61 }; 62 63 #define FILTER_CTX_DEFAULT__ \ 64 { NULL, S3D_INVALID_ID, NULL, { 0, 0, 0 }, FLT_MAX, 0, 0 } 65 66 /******************************************************************************* 67 * Local Functions 68 ******************************************************************************/ 69 70 /* Filter used from a point query to determine not only one of the closest 71 * point, but the better one if there are more than one. In some circumstances 72 * it is not possible to determine the medium we are in using a given hit, but 73 * it is possible using another equidistant hit : 74 * 75 * 76 * P C 77 * +.............+---trg 1--- 78 * | 79 * medium 1 trg 2 medium 2 80 * | 81 * 82 * C is the closest point from P, and 2 different hits at C are possible (one 83 * on each triangle). However, only hit on trg 2 allows to find out that P is 84 * in medium 1 using sign(PC.Ntrg1) as PC.Ntrg2 = 0. 85 * The following filter function aims at selecting the hit on trg2 regardless 86 * of the order in which the 2 triangles are checked. 87 * One unexpected case cannot be decided though, but it implies that the 88 * closest triangle has 2 different media on its sides and that P lies on the 89 * triangle's plane : 90 * 91 * P C medium 1 92 * + +---trg--- 93 * medium 2 94 */ 95 static int 96 hit_filter 97 (const struct s3d_hit* hit, 98 const float ray_org[3], 99 const float ray_dir[3], 100 const float ray_range[2], 101 void* ray_data, 102 void* filter_data) 103 { 104 struct filter_ctx* filter_ctx = ray_data; 105 float s, dir[3]; 106 enum sg3d_property_type prop; 107 struct s3d_attrib interf_pos; 108 const struct description* descriptions; 109 unsigned descr[SG3D_PROP_TYPES_COUNT__]; 110 const struct stardis* stardis; 111 112 (void)ray_org; (void)ray_dir; (void)ray_range; (void)filter_data; 113 ASSERT(hit && filter_ctx); 114 ASSERT(hit->uv[0] == CLAMP(hit->uv[0], 0, 1)); 115 ASSERT(hit->uv[1] == CLAMP(hit->uv[1], 0, 1)); 116 117 if(filter_ctx->dist == hit->distance && filter_ctx->desc) { 118 /* Cannot improve: keep previous! 119 * Or we could end with a NULL desc */ 120 return 1; /* Skip */ 121 } 122 123 stardis = filter_ctx->stardis; 124 descriptions = darray_descriptions_cdata_get(&stardis->descriptions); 125 126 CHK(s3d_primitive_get_attrib(&hit->prim, S3D_POSITION, hit->uv, &interf_pos) 127 == RES_OK); 128 129 if(hit->distance == 0) { 130 filter_ctx->prim = hit->prim.prim_id; 131 filter_ctx->desc = NULL; /* Not apply */ 132 f3_set(filter_ctx->pos, interf_pos.value); 133 filter_ctx->dist = hit->distance; 134 filter_ctx->outside = 0; 135 filter_ctx->probe_on_boundary = 1; 136 return 0; /* Keep */ 137 } 138 139 /* Get the description IDs for this triangle */ 140 CHK(sg3d_geometry_get_unique_triangle_properties(stardis->geometry.sg3d, 141 hit->prim.prim_id, descr) == RES_OK); 142 143 if(descr[SG3D_FRONT] == descr[SG3D_BACK]) { 144 /* Don't need to bother with sides */ 145 filter_ctx->prim = hit->prim.prim_id; 146 filter_ctx->desc = (descr[SG3D_FRONT] == SG3D_UNSPECIFIED_PROPERTY) 147 ? NULL : descriptions + descr[SG3D_FRONT]; 148 f3_set(filter_ctx->pos, interf_pos.value); 149 filter_ctx->dist = hit->distance; 150 filter_ctx->outside = (descr[SG3D_FRONT] == SG3D_UNSPECIFIED_PROPERTY); 151 filter_ctx->probe_on_boundary = 0; 152 return 0; /* Keep */ 153 } 154 155 f3_sub(dir, interf_pos.value, ray_org); 156 s = f3_dot(dir, hit->normal); 157 158 if(s == 0) { 159 filter_ctx->prim = hit->prim.prim_id; 160 filter_ctx->desc = NULL; /* Cannot decide side */ 161 f3_set(filter_ctx->pos, interf_pos.value); 162 filter_ctx->dist = hit->distance; 163 filter_ctx->outside = 0; 164 filter_ctx->probe_on_boundary = 0; 165 } 166 167 /* Determine which side was hit and the property to look at. 168 * Warning: following Embree 2 convention for geometrical normals, 169 * the Star3D hit normals are left-handed */ 170 prop = (s > 0) ? SG3D_BACK : SG3D_FRONT; 171 if(descr[prop] == SG3D_UNSPECIFIED_PROPERTY) { 172 filter_ctx->prim = hit->prim.prim_id; 173 filter_ctx->desc = NULL; 174 f3_set(filter_ctx->pos, interf_pos.value); 175 filter_ctx->dist = hit->distance; 176 filter_ctx->outside = 1; 177 filter_ctx->probe_on_boundary = 0; 178 } else { 179 filter_ctx->prim = hit->prim.prim_id; 180 filter_ctx->desc = descriptions + descr[prop]; 181 f3_set(filter_ctx->pos, interf_pos.value); 182 filter_ctx->dist = hit->distance; 183 filter_ctx->outside = 0; 184 filter_ctx->probe_on_boundary = 0; 185 } 186 187 return 0; /* Keep */ 188 } 189 190 /* Check probe position and move it to the closest point on an interface 191 * if on_interface is set */ 192 static res_T 193 check_probe_conform_to_type 194 (const struct stardis* stardis, 195 double pos[3], 196 double time, 197 unsigned* iprim, 198 double uv[2]) 199 { 200 res_T res = RES_OK; 201 struct s3d_device* s3d = NULL; 202 struct s3d_scene* s3d_scn = NULL; 203 struct s3d_shape* s3d_shp = NULL; 204 struct s3d_scene_view* s3d_view = NULL; 205 struct s3d_hit hit = S3D_HIT_NULL; 206 struct s3d_vertex_data attribs; 207 struct filter_ctx filter_ctx = FILTER_CTX_DEFAULT__; 208 float origin[3]; 209 unsigned vcount, tcount; 210 211 ASSERT(stardis && pos && iprim && uv); 212 213 attribs.type = S3D_FLOAT3; 214 attribs.usage = S3D_POSITION; 215 attribs.get = sg3d_sXd_geometry_get_position; 216 217 ERR(s3d_device_create(stardis->logger, stardis->allocator, 0, &s3d)); 218 ERR(s3d_scene_create(s3d, &s3d_scn)); 219 ERR(s3d_shape_create_mesh(s3d, &s3d_shp)); 220 221 /* Back to s3d API type for ntris and nverts */ 222 ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tcount)); 223 ERR(sg3d_geometry_get_unique_vertices_count(stardis->geometry.sg3d, &vcount)); 224 ERR(s3d_mesh_setup_indexed_vertices(s3d_shp, 225 tcount, sg3d_sXd_geometry_get_indices, 226 vcount, &attribs, 1, stardis->geometry.sg3d)); 227 /* Need a filter to sort out some tricky configurations (see filter comments) */ 228 ERR(s3d_mesh_set_hit_filter_function(s3d_shp, hit_filter, NULL)); 229 ERR(s3d_scene_attach_shape(s3d_scn, s3d_shp)); 230 ERR(s3d_scene_view_create(s3d_scn, S3D_TRACE, &s3d_view)); 231 232 S3D(device_ref_put(s3d)); s3d = NULL; 233 S3D(scene_ref_put(s3d_scn)); s3d_scn = NULL; 234 S3D(shape_ref_put(s3d_shp)); s3d_shp = NULL; 235 236 f3_set_d3(origin, pos); 237 filter_ctx.stardis = stardis; 238 ERR(s3d_scene_view_closest_point(s3d_view, origin, FLT_MAX, &filter_ctx, &hit)); 239 S3D(scene_view_ref_put(s3d_view)); s3d_view = NULL; 240 if(S3D_HIT_NONE(&hit)) { 241 res = RES_BAD_ARG; 242 goto error; 243 } 244 ASSERT(filter_ctx.dist == hit.distance); 245 246 /* Need to check medium */ 247 if(filter_ctx.outside) { 248 logger_print(stardis->logger, LOG_ERROR, 249 "Probe is outside the model.\n"); 250 logger_print(stardis->logger, LOG_ERROR, 251 "Closest geometry is primitive %u, uv = (%g, %g), pos (%g, %g, %g).\n", 252 hit.prim.prim_id, SPLIT2(hit.uv), SPLIT3(filter_ctx.pos)); 253 res = RES_BAD_ARG; 254 goto error; 255 } 256 if(filter_ctx.probe_on_boundary) { 257 logger_print(stardis->logger, LOG_ERROR, 258 "Probe is on primitive %u, uv = (%g, %g). Use -P instead of -p.\n", 259 hit.prim.prim_id, SPLIT2(hit.uv)); 260 res = RES_BAD_ARG; 261 goto error; 262 } 263 if(!filter_ctx.desc) { 264 logger_print(stardis->logger, LOG_ERROR, 265 "Could not determine the medium probe is in. " 266 "Try moving it slightly.\n"); 267 logger_print(stardis->logger, LOG_ERROR, 268 "Closest geometry is primitive %u, uv = (%g, %g), distance %g.\n", 269 hit.prim.prim_id, SPLIT2(hit.uv), filter_ctx.dist); 270 res = RES_BAD_ARG; 271 goto error; 272 } 273 if(DESC_IS_SOLID(filter_ctx.desc)) { 274 double delta; 275 if(filter_ctx.desc->type == DESC_MAT_SOLID) { 276 struct solid* solid = filter_ctx.desc->d.solid; 277 delta = solid->delta; 278 } else { 279 struct solid_prog* solid_prog = filter_ctx.desc->d.solid_prog; 280 struct stardis_vertex vtx; 281 ASSERT(filter_ctx.desc->type == DESC_MAT_SOLID_PROG); 282 d3_set(vtx.P, pos); 283 vtx.time = time; 284 delta = solid_prog->delta(&vtx, solid_prog->prog_data); 285 } 286 if(filter_ctx.dist < 0.25 * delta) { 287 logger_print(stardis->logger, LOG_ERROR, 288 "Probe is %g delta from closest boundary. Use -P instead of -p.\n", 289 filter_ctx.dist / delta); 290 logger_print(stardis->logger, LOG_ERROR, 291 "Closest geometry is primitive %u, uv = (%g, %g).\n", 292 hit.prim.prim_id, SPLIT2(hit.uv)); 293 res = RES_BAD_ARG; 294 goto error; 295 } 296 if(filter_ctx.dist < 0.5 * delta) { 297 logger_print(stardis->logger, LOG_WARNING, 298 "Probe is %g delta from closest boundary. " 299 "Consider using -P instead of -p.\n", 300 filter_ctx.dist / delta); 301 } else { 302 logger_print(stardis->logger, LOG_OUTPUT, 303 "Probe is %g delta from closest boundary.\n", 304 filter_ctx.dist / delta); 305 } 306 } else { 307 ASSERT(DESC_IS_FLUID(filter_ctx.desc)); 308 /* In fluid; TODO: check distance wrt local geometry (use 4V/S?) */ 309 if(filter_ctx.desc->type == DESC_MAT_FLUID) { 310 logger_print(stardis->logger, LOG_WARNING, 311 "Probe is in fluid '%s': computing fluid temperature, " 312 "not using a specific position.\n", 313 str_cget(&filter_ctx.desc->d.fluid->name)); 314 } else { 315 ASSERT(filter_ctx.desc->type == DESC_MAT_FLUID_PROG); 316 logger_print(stardis->logger, LOG_WARNING, 317 "Probe is in fluid_prog '%s': computing fluid temperature, " 318 "not using a specific position.\n", 319 str_cget(&filter_ctx.desc->d.fluid_prog->name)); 320 } 321 } 322 323 *iprim = hit.prim.prim_id; 324 d2_set_f2(uv, hit.uv); 325 end: 326 if(s3d) S3D(device_ref_put(s3d)); 327 if(s3d_scn) S3D(scene_ref_put(s3d_scn)); 328 if(s3d_shp) S3D(shape_ref_put(s3d_shp)); 329 if(s3d_view) S3D(scene_view_ref_put(s3d_view)); 330 331 return res; 332 error: 333 goto end; 334 } 335 336 static res_T 337 compute_probe(struct stardis* stardis, struct time* start) 338 { 339 res_T res = RES_OK; 340 double uv[2] = { 0,0 }; 341 unsigned iprim = UINT_MAX; 342 struct sdis_green_function* green = NULL; 343 struct sdis_estimator* estimator = NULL; 344 struct dump_path_context dump_ctx; 345 struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; 346 FILE* stream_r = NULL; 347 FILE* stream_g = NULL; 348 FILE* stream_p = NULL; 349 struct time compute_start, compute_end; 350 351 ASSERT(stardis && start && (stardis->mode & MODE_COMPUTE_PROBE_TEMP_ON_VOL)); 352 353 ERR(check_probe_conform_to_type(stardis, stardis->probe, 354 stardis->time_range[0], &iprim, uv)); 355 356 args.nrealisations = stardis->samples; 357 d3_set(args.position, stardis->probe); 358 d2_set(args.time_range, stardis->time_range); 359 args.diff_algo = stardis->diff_algo; 360 361 /* Input random state? */ 362 READ_RANDOM_STATE(&stardis->rndgen_state_in_filename); 363 364 if(stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)) { 365 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 366 ERR(sdis_solve_probe_green_function(stardis->sdis_scn, &args, &green)); 367 } else { 368 /* Try to open output files to detect errors early */ 369 if(stardis->mode & MODE_GREEN_BIN) { 370 ASSERT(!str_is_empty(&stardis->bin_green_filename)); 371 stream_g = fopen(str_cget(&stardis->bin_green_filename), "wb"); 372 if(!stream_g) { 373 logger_print(stardis->logger, LOG_ERROR, 374 "cannot open file '%s' for binary writing.\n", 375 str_cget(&stardis->bin_green_filename)); 376 res = RES_IO_ERR; 377 goto error; 378 } 379 if(str_cget(&stardis->end_paths_filename) 380 && strlen(str_cget(&stardis->end_paths_filename))) 381 { 382 stream_p = fopen(str_cget(&stardis->end_paths_filename), "w"); 383 if(!stream_p) { 384 logger_print(stardis->logger, LOG_ERROR, 385 "cannot open file '%s' for writing.\n", 386 str_cget(&stardis->end_paths_filename)); 387 res = RES_IO_ERR; 388 goto error; 389 } 390 } 391 } 392 /* Call solve() */ 393 time_current(&compute_start); 394 ERR(sdis_solve_probe_green_function(stardis->sdis_scn, &args, &green)); 395 time_current(&compute_end); 396 if(stardis->mode & MODE_GREEN_BIN) { 397 struct time output_end; 398 ERR(dump_green_bin(green, stardis, stream_g)); 399 if(stream_p) { 400 ERR(dump_paths_end(green, stardis, stream_p)); 401 } 402 time_current(&output_end); 403 ERR(print_computation_time(NULL, stardis, 404 start, &compute_start, &compute_end, &output_end)); 405 } 406 if(stardis->mode & MODE_GREEN_ASCII) { 407 ERR(dump_green_ascii(green, stardis, stdout)); 408 } 409 } 410 } else { 411 args.register_paths = stardis->dump_paths; 412 args.picard_order = stardis->picard_order; 413 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 414 ERR(sdis_solve_probe(stardis->sdis_scn, &args, &estimator)); 415 } else { 416 struct sdis_mc time = SDIS_MC_NULL; 417 res_T tmp_res1, tmp_res2; 418 time_current(&compute_start); 419 ERR(sdis_solve_probe(stardis->sdis_scn, &args, &estimator)); 420 time_current(&compute_end); 421 ERR(sdis_estimator_get_realisation_time(estimator, &time)); 422 ERR(print_computation_time 423 (&time, stardis, start, &compute_start, &compute_end, NULL)); 424 tmp_res1 = print_single_MC_result(estimator, stardis, stdout); 425 426 /* Dump recorded paths according to user settings */ 427 dump_ctx.stardis = stardis; 428 dump_ctx.rank = 0; 429 tmp_res2 = sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx); 430 if(tmp_res1 != RES_OK) res = tmp_res1; 431 else if(tmp_res2 != RES_OK) res = tmp_res2; 432 } 433 } 434 435 /* Output random state? */ 436 WRITE_RANDOM_STATE(&stardis->rndgen_state_out_filename); 437 438 end: 439 if(stream_r) fclose(stream_r); 440 if(stream_g) fclose(stream_g); 441 if(stream_p) fclose(stream_p); 442 if(estimator) SDIS(estimator_ref_put(estimator)); 443 if(green) SDIS(green_function_ref_put(green)); 444 if(args.rng_state) SSP(rng_ref_put(args.rng_state)); 445 return res; 446 error: 447 goto end; 448 } 449 450 static res_T 451 auto_look_at 452 (struct sdis_scene* scn, 453 const double fov_x, /* Horizontal field of view in radian */ 454 const double proj_ratio, /* Width / height */ 455 const double up[3], /* Up vector */ 456 double position[3], 457 double target[3]) 458 { 459 double lower[3], upper[3]; 460 double up_abs[3]; 461 double axis_min[3]; 462 double axis_x[3]; 463 double axis_z[3]; 464 double tmp[3]; 465 double radius; 466 double depth; 467 res_T res; 468 ASSERT(scn && fov_x!=0 && proj_ratio!=0 && up); 469 470 ERR(sdis_scene_get_aabb(scn, lower, upper)); 471 472 if(lower[0] > upper[0] || lower[1] > upper[1] || lower[2] > upper[2]) { 473 /* Empty scene */ 474 d3(position, STARDIS_DEFAULT_RENDERING_POS); 475 d3(target, STARDIS_DEFAULT_RENDERING_TGT); 476 goto exit; 477 } 478 479 /* The target is the scene centroid */ 480 d3_muld(target, d3_add(target, lower, upper), 0.5); 481 482 /* Define which up dimension is minimal and use its unit vector to compute a 483 * vector orthogonal to `up'. This ensures that the unit vector and `up' are 484 * not collinear so that their cross product is not a zero vector. */ 485 up_abs[0] = fabs(up[0]); 486 up_abs[1] = fabs(up[1]); 487 up_abs[2] = fabs(up[2]); 488 if(up_abs[0] < up_abs[1]) { 489 if(up_abs[0] < up_abs[2]) d3(axis_min, 1, 0, 0); 490 else d3(axis_min, 0, 0, 1); 491 } else { 492 if(up_abs[1] < up_abs[2]) d3(axis_min, 0, 1, 0); 493 else d3(axis_min, 0, 0, 1); 494 } 495 d3_normalize(axis_x, d3_cross(axis_x, up, axis_min)); 496 d3_normalize(axis_z, d3_cross(axis_z, up, axis_x)); 497 498 /* Find whether the XYZ or the ZYX basis maximise the model's visibility */ 499 if(fabs(d3_dot(axis_x, upper)) < fabs(d3_dot(axis_z, upper))) { 500 SWAP(double, axis_x[0], axis_z[0]); 501 SWAP(double, axis_x[1], axis_z[1]); 502 SWAP(double, axis_x[2], axis_z[2]); 503 } 504 505 /* Ensure that the whole model is visible */ 506 radius = d3_len(d3_sub(tmp, upper, lower)) * 0.5; 507 if(proj_ratio < 1) { 508 depth = radius / sin(fov_x / 2.0); 509 } else { 510 depth = radius / sin(fov_x / (2.0 * proj_ratio)); 511 } 512 513 /* Define the camera position */ 514 d3_sub(position, target, d3_muld(tmp, axis_z, depth)); 515 516 /* Empirically move the position to find a better point of view */ 517 d3_add(position, position, d3_muld(tmp, up, radius)); /*Empirical offset*/ 518 d3_add(position, position, d3_muld(tmp, axis_x, radius)); /*Empirical offset*/ 519 d3_normalize(tmp, d3_sub(tmp, target, position)); 520 d3_sub(position, target, d3_muld(tmp, tmp, depth)); 521 522 exit: 523 return res; 524 error: 525 goto exit; 526 } 527 528 static res_T 529 compute_camera(struct stardis* stardis, struct time* start) 530 { 531 res_T res = RES_OK; 532 double proj_ratio; 533 size_t width, height; 534 struct sdis_estimator_buffer* buf = NULL; 535 struct sdis_solve_camera_args args = SDIS_SOLVE_CAMERA_ARGS_DEFAULT; 536 struct sdis_camera* cam = NULL; 537 struct dump_path_context dump_ctx; 538 size_t definition[2]; 539 size_t ix, iy; 540 FILE* stream = NULL; 541 struct time compute_start, compute_end, output_end; 542 543 ASSERT(stardis && start 544 && !(stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)) 545 && (stardis->mode & MODE_COMPUTE_IMAGE_IR)); 546 547 width = (size_t)stardis->camera.img_width; 548 height = (size_t)stardis->camera.img_height; 549 proj_ratio = (double)width / (double)height; 550 /* Setup the camera */ 551 ERR(sdis_camera_create(stardis->dev, &cam)); 552 ERR(sdis_camera_set_proj_ratio(cam, proj_ratio)); 553 ERR(sdis_camera_set_fov(cam, MDEG2RAD(stardis->camera.fov))); 554 if(stardis->camera.auto_look_at) { 555 ERR(auto_look_at(stardis->sdis_scn, MDEG2RAD(stardis->camera.fov), proj_ratio, 556 stardis->camera.up, stardis->camera.pos, stardis->camera.tgt)); 557 logger_print(stardis->logger, LOG_OUTPUT, 558 "Camera auto-look at: position=%g,%g,%g target=%g,%g,%g.\n", 559 SPLIT3(stardis->camera.pos), SPLIT3(stardis->camera.tgt)); 560 } 561 ERR(sdis_camera_look_at(cam, 562 stardis->camera.pos, 563 stardis->camera.tgt, 564 stardis->camera.up)); 565 566 args.cam = cam; 567 d2_set(args.time_range, stardis->camera.time_range); 568 args.image_definition[0] = width; 569 args.image_definition[1] = height; 570 args.spp = (size_t)stardis->camera.spp; 571 args.register_paths = stardis->dump_paths; 572 args.picard_order = stardis->picard_order; 573 args.diff_algo = stardis->diff_algo; 574 575 /* Launch the simulation */ 576 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 577 ERR(sdis_solve_camera(stardis->sdis_scn, &args, &buf)); 578 } else { 579 time_current(&compute_start); 580 ERR(sdis_solve_camera(stardis->sdis_scn, &args, &buf)); 581 time_current(&compute_end); 582 583 /* Write the image */ 584 if(str_is_empty(&stardis->camera.file_name)) 585 stream = stdout; 586 else { 587 stream = fopen(str_cget(&stardis->camera.file_name), "w"); 588 if(!stream) { 589 logger_print(stardis->logger, LOG_ERROR, 590 "cannot open file '%s' for writing.\n", 591 str_cget(&stardis->camera.file_name)); 592 res = RES_IO_ERR; 593 goto error; 594 } 595 } 596 ASSERT(stardis->camera.fmt == STARDIS_RENDERING_OUTPUT_FILE_FMT_VTK 597 || stardis->camera.fmt == STARDIS_RENDERING_OUTPUT_FILE_FMT_HT); 598 if(stardis->camera.fmt == STARDIS_RENDERING_OUTPUT_FILE_FMT_VTK) 599 ERR(dump_vtk_image(buf, stream)); 600 else ERR(dump_ht_image(buf, stream)); 601 if(!str_is_empty(&stardis->camera.file_name)) 602 fclose(stream); 603 604 /* Dump recorded paths according to user settings */ 605 dump_ctx.stardis = stardis; 606 dump_ctx.rank = 0; 607 ERR(sdis_estimator_buffer_get_definition(buf, definition)); 608 FOR_EACH(iy, 0, definition[1]) { 609 FOR_EACH(ix, 0, definition[0]) { 610 const struct sdis_estimator* estimator; 611 ERR(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); 612 ERR(sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx)); 613 } 614 } 615 time_current(&output_end); 616 ERR(print_computation_time(NULL, stardis, 617 start, &compute_start, &compute_end, NULL)); 618 } 619 620 end: 621 if(cam) SDIS(camera_ref_put(cam)); 622 if(buf) SDIS(estimator_buffer_ref_put(buf)); 623 return res; 624 error: 625 goto end; 626 } 627 628 static res_T 629 compute_medium(struct stardis* stardis, struct time* start) 630 { 631 res_T res = RES_OK; 632 struct sdis_medium* medium = NULL; 633 struct sdis_estimator* estimator = NULL; 634 struct sdis_green_function* green = NULL; 635 struct sdis_solve_medium_args args = SDIS_SOLVE_MEDIUM_ARGS_DEFAULT; 636 struct dump_path_context dump_ctx; 637 FILE* stream_r = NULL; 638 FILE* stream_g = NULL; 639 FILE* stream_p = NULL; 640 struct time compute_start, compute_end; 641 642 ASSERT(stardis && start && (stardis->mode & MODE_COMPUTE_TEMP_MEAN_IN_MEDIUM)); 643 644 /* Find medium */ 645 medium = find_medium_by_name(stardis, str_cget(&stardis->solve_name), NULL); 646 if(medium == NULL) { /* Not found */ 647 logger_print(stardis->logger, LOG_ERROR, 648 "Cannot solve medium '%s' (unknown medium)\n", 649 str_cget(&stardis->solve_name)); 650 res = RES_BAD_ARG; 651 goto error; 652 } 653 654 args.nrealisations = stardis->samples; 655 args.medium = medium; 656 d2_set(args.time_range, stardis->time_range); 657 args.diff_algo = stardis->diff_algo; 658 659 /* Input random state? */ 660 READ_RANDOM_STATE(&stardis->rndgen_state_in_filename); 661 662 if(stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)) { 663 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 664 ERR(sdis_solve_medium_green_function(stardis->sdis_scn, &args, &green)); 665 } else { 666 /* Try to open output files to detect errors early */ 667 if(stardis->mode & MODE_GREEN_BIN) { 668 ASSERT(!str_is_empty(&stardis->bin_green_filename)); 669 stream_g = fopen(str_cget(&stardis->bin_green_filename), "wb"); 670 if(!stream_g) { 671 logger_print(stardis->logger, LOG_ERROR, 672 "cannot open file '%s' for binary writing.\n", 673 str_cget(&stardis->bin_green_filename)); 674 res = RES_IO_ERR; 675 goto error; 676 } 677 if(str_cget(&stardis->end_paths_filename) 678 && strlen(str_cget(&stardis->end_paths_filename))) 679 { 680 stream_p = fopen(str_cget(&stardis->end_paths_filename), "w"); 681 if(!stream_p) { 682 logger_print(stardis->logger, LOG_ERROR, 683 "cannot open file '%s' for writing.\n", 684 str_cget(&stardis->end_paths_filename)); 685 res = RES_IO_ERR; 686 goto error; 687 } 688 } 689 } 690 /* Call solve() */ 691 time_current(&compute_start); 692 ERR(sdis_solve_medium_green_function(stardis->sdis_scn, &args, &green)); 693 time_current(&compute_end); 694 if(stardis->mode & MODE_GREEN_BIN) { 695 struct time output_end; 696 ASSERT(!str_is_empty(&stardis->bin_green_filename)); 697 ERR(dump_green_bin(green, stardis, stream_g)); 698 if(stream_p) { 699 ERR(dump_paths_end(green, stardis, stream_p)); 700 } 701 time_current(&output_end); 702 ERR(print_computation_time(NULL, stardis, 703 start, &compute_start, &compute_end, &output_end)); 704 } 705 if(stardis->mode & MODE_GREEN_ASCII) { 706 ERR(dump_green_ascii(green, stardis, stdout)); 707 } 708 } 709 } else { 710 args.register_paths = stardis->dump_paths; 711 args.picard_order = stardis->picard_order; 712 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 713 ERR(sdis_solve_medium(stardis->sdis_scn, &args, &estimator)); 714 } else { 715 struct sdis_mc time = SDIS_MC_NULL; 716 res_T tmp_res1, tmp_res2; 717 time_current(&compute_start); 718 ERR(sdis_solve_medium(stardis->sdis_scn, &args, &estimator)); 719 time_current(&compute_end); 720 721 ERR(sdis_estimator_get_realisation_time(estimator, &time)); 722 ERR(print_computation_time 723 (&time, stardis, start, &compute_start, &compute_end, NULL)); 724 tmp_res1 = print_single_MC_result(estimator, stardis, stdout); 725 /* Dump recorded paths according to user settings */ 726 dump_ctx.stardis = stardis; 727 dump_ctx.rank = 0; 728 tmp_res2 = sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx); 729 if(tmp_res1 != RES_OK) res = tmp_res1; 730 else if(tmp_res2 != RES_OK) res = tmp_res2; 731 } 732 } 733 734 /* Output random state? */ 735 WRITE_RANDOM_STATE(&stardis->rndgen_state_out_filename); 736 737 end: 738 if(stream_r) fclose(stream_r); 739 if(stream_g) fclose(stream_g); 740 if(stream_p) fclose(stream_p); 741 if(estimator) SDIS(estimator_ref_put(estimator)); 742 if(green) SDIS(green_function_ref_put(green)); 743 if(args.rng_state) SSP(rng_ref_put(args.rng_state)); 744 return res; 745 error: 746 goto end; 747 } 748 749 static res_T 750 add_compute_surface_triangle 751 (const unsigned unique_id, const unsigned itri, void* context) 752 { 753 res_T res = RES_OK; 754 const struct add_geom_ctx* ctx = context; 755 struct compute_surface* region; 756 ASSERT(ctx); (void)itri; 757 region = ctx->custom; 758 /* The triangle should be already in the model, but is not 759 * Keep it in err_triangles to allow dumps */ 760 ERR(darray_uint_push_back(®ion->err_triangles, &unique_id)); 761 762 end: 763 return res; 764 error: 765 goto end; 766 } 767 768 static res_T 769 merge_compute_surface_triangle 770 (const unsigned unique_id, 771 const unsigned itri, 772 const int reversed_triangle, 773 unsigned triangle_properties[SG3D_PROP_TYPES_COUNT__], 774 const unsigned merged_properties[SG3D_PROP_TYPES_COUNT__], 775 void* context, 776 int* merge_conflict_status) 777 { 778 res_T res = RES_OK; 779 const struct add_geom_ctx* ctx = context; 780 struct compute_surface* region; 781 size_t uid; 782 enum sdis_side side = reversed_triangle ? SDIS_BACK : SDIS_FRONT; 783 ASSERT(ctx); 784 (void)itri; (void)reversed_triangle; (void)triangle_properties; 785 (void)merged_properties; (void)merge_conflict_status; 786 region = ctx->custom; 787 /* Build the compute region */ 788 uid = unique_id; 789 ERR(darray_size_t_push_back(®ion->primitives, &uid)); 790 ERR(darray_sides_push_back(®ion->sides, &side)); 791 end: 792 return res; 793 error: 794 goto end; 795 } 796 797 static res_T 798 compute_boundary(struct stardis* stardis, struct time* start) 799 { 800 res_T res = RES_OK; 801 struct sdis_green_function* green = NULL; 802 struct sdis_estimator* estimator = NULL; 803 struct dump_path_context dump_ctx; 804 struct sdis_solve_boundary_args args = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; 805 FILE* stream_r = NULL; 806 FILE* stream_g = NULL; 807 FILE* stream_p = NULL; 808 struct time compute_start, compute_end; 809 810 ASSERT(stardis && start && (stardis->mode & MODE_COMPUTE_TEMP_MEAN_ON_SURF)); 811 812 args.nrealisations = stardis->samples; 813 args.primitives 814 = darray_size_t_cdata_get(&stardis->compute_surface.primitives); 815 args.sides = darray_sides_cdata_get(&stardis->compute_surface.sides); 816 args.nprimitives 817 = darray_size_t_size_get(&stardis->compute_surface.primitives); 818 d2_set(args.time_range, stardis->time_range); 819 args.diff_algo = stardis->diff_algo; 820 821 /* Input random state? */ 822 READ_RANDOM_STATE(&stardis->rndgen_state_in_filename); 823 824 if(stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)) { 825 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 826 ERR(sdis_solve_boundary_green_function(stardis->sdis_scn, &args, &green)); 827 } else { 828 /* Try to open output files to detect errors early */ 829 if(stardis->mode & MODE_GREEN_BIN) { 830 ASSERT(!str_is_empty(&stardis->bin_green_filename)); 831 stream_g = fopen(str_cget(&stardis->bin_green_filename), "wb"); 832 if(!stream_g) { 833 logger_print(stardis->logger, LOG_ERROR, 834 "cannot open file '%s' for binary writing.\n", 835 str_cget(&stardis->bin_green_filename)); 836 res = RES_IO_ERR; 837 goto error; 838 } 839 if(str_cget(&stardis->end_paths_filename) 840 && strlen(str_cget(&stardis->end_paths_filename))) 841 { 842 stream_p = fopen(str_cget(&stardis->end_paths_filename), "w"); 843 if(!stream_p) { 844 logger_print(stardis->logger, LOG_ERROR, 845 "cannot open file '%s' for writing.\n", 846 str_cget(&stardis->end_paths_filename)); 847 res = RES_IO_ERR; 848 goto error; 849 } 850 } 851 } 852 /* Call solve() */ 853 time_current(&compute_start); 854 ERR(sdis_solve_boundary_green_function(stardis->sdis_scn, &args, &green)); 855 time_current(&compute_end); 856 if(stardis->mode & MODE_GREEN_BIN) { 857 struct time output_end; 858 ERR(dump_green_bin(green, stardis, stream_g)); 859 if(stream_p) { 860 ERR(dump_paths_end(green, stardis, stream_p)); 861 } 862 time_current(&output_end); 863 ERR(print_computation_time(NULL, stardis, 864 start, &compute_start, &compute_end, &output_end)); 865 } 866 if(stardis->mode & MODE_GREEN_ASCII) { 867 ERR(dump_green_ascii(green, stardis, stdout)); 868 } 869 } 870 } else { 871 args.register_paths = stardis->dump_paths; 872 args.picard_order = stardis->picard_order; 873 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 874 ERR(sdis_solve_boundary(stardis->sdis_scn, &args, &estimator)); 875 } else { 876 struct sdis_mc time = SDIS_MC_NULL; 877 res_T tmp_res1, tmp_res2; 878 time_current(&compute_start); 879 ERR(sdis_solve_boundary(stardis->sdis_scn, &args, &estimator)); 880 time_current(&compute_end); 881 ERR(sdis_estimator_get_realisation_time(estimator, &time)); 882 ERR(print_computation_time 883 (&time, stardis, start, &compute_start, &compute_end, NULL)); 884 tmp_res1 = print_single_MC_result(estimator, stardis, stdout); 885 /* Dump recorded paths according to user settings */ 886 dump_ctx.stardis = stardis; 887 dump_ctx.rank = 0; 888 tmp_res2 = sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx); 889 if(tmp_res1 != RES_OK) res = tmp_res1; 890 else if(tmp_res2 != RES_OK) res = tmp_res2; 891 } 892 } 893 894 /* Output random state? */ 895 WRITE_RANDOM_STATE(&stardis->rndgen_state_out_filename); 896 897 end: 898 if(stream_r) fclose(stream_r); 899 if(stream_g) fclose(stream_g); 900 if(stream_p) fclose(stream_p); 901 if(estimator) SDIS(estimator_ref_put(estimator)); 902 if(green) SDIS(green_function_ref_put(green)); 903 if(args.rng_state) SSP(rng_ref_put(args.rng_state)); 904 return res; 905 error: 906 goto end; 907 } 908 909 static res_T 910 compute_flux_boundary(struct stardis* stardis, struct time* start) 911 { 912 res_T res = RES_OK; 913 struct sdis_green_function* green = NULL; 914 struct sdis_estimator* estimator = NULL; 915 struct sdis_solve_boundary_flux_args args 916 = SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT; 917 struct dump_path_context dump_ctx; 918 FILE* stream_r = NULL; 919 struct time compute_start, compute_end; 920 921 ASSERT(stardis && start && (stardis->mode & MODE_COMPUTE_FLUX_THROUGH_SURF)); 922 923 args.nrealisations = stardis->samples; 924 args.primitives 925 = darray_size_t_cdata_get(&stardis->compute_surface.primitives); 926 args.nprimitives 927 = darray_size_t_size_get(&stardis->compute_surface.primitives); 928 args.picard_order = stardis->picard_order; 929 d2_set(args.time_range, stardis->time_range); 930 args.diff_algo = stardis->diff_algo; 931 932 /* Input random state? */ 933 READ_RANDOM_STATE(&stardis->rndgen_state_in_filename); 934 935 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 936 ERR(sdis_solve_boundary_flux(stardis->sdis_scn, &args, &estimator)); 937 } else { 938 struct sdis_mc time = SDIS_MC_NULL; 939 res_T tmp_res1, tmp_res2; 940 time_current(&compute_start); 941 ERR(sdis_solve_boundary_flux(stardis->sdis_scn, &args, &estimator)); 942 time_current(&compute_end); 943 ERR(sdis_estimator_get_realisation_time(estimator, &time)); 944 ERR(print_computation_time 945 (&time, stardis, start, &compute_start, &compute_end, NULL)); 946 tmp_res1 = print_single_MC_result(estimator, stardis, stdout); 947 948 /* Dump recorded paths according to user settings */ 949 dump_ctx.stardis = stardis; 950 dump_ctx.rank = 0; 951 tmp_res2 = sdis_estimator_for_each_path(estimator, dump_path, &dump_ctx); 952 if(tmp_res1 != RES_OK) res = tmp_res1; 953 else if(tmp_res2 != RES_OK) res = tmp_res2; 954 } 955 956 /* Output random state? */ 957 WRITE_RANDOM_STATE(&stardis->rndgen_state_out_filename); 958 959 end: 960 if(estimator) SDIS(estimator_ref_put(estimator)); 961 if(green) SDIS(green_function_ref_put(green)); 962 if(args.rng_state) SSP(rng_ref_put(args.rng_state)); 963 return res; 964 error: 965 goto end; 966 } 967 968 static res_T 969 compute_map(struct stardis* stardis, struct time* start) 970 { 971 res_T res = RES_OK; 972 struct sdis_green_function* green = NULL; 973 struct sdis_solve_boundary_args args = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; 974 struct darray_estimators estimators; 975 int estimators_initialized = 0; 976 size_t p; 977 978 (void)start; 979 ASSERT(stardis && start 980 && (stardis->mode & MODE_COMPUTE_TEMP_MAP_ON_SURF) 981 && !(stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII))); 982 983 darray_estimators_init(stardis->allocator, &estimators); 984 estimators_initialized = 1; 985 986 ERR(darray_estimators_resize(&estimators, 987 darray_estimators_size_get(&estimators) + 988 darray_size_t_size_get(&stardis->compute_surface.primitives))); 989 990 FOR_EACH(p, 0, darray_size_t_size_get(&stardis->compute_surface.primitives)) { 991 992 args.nrealisations = stardis->samples; 993 args.primitives 994 = darray_size_t_cdata_get(&stardis->compute_surface.primitives); 995 args.sides = darray_sides_cdata_get(&stardis->compute_surface.sides); 996 args.nprimitives 997 = darray_size_t_size_get(&stardis->compute_surface.primitives); 998 d2_set(args.time_range, stardis->time_range); 999 args.register_paths = stardis->dump_paths; 1000 args.picard_order = stardis->picard_order; 1001 args.diff_algo = stardis->diff_algo; 1002 1003 ERR(sdis_solve_boundary(stardis->sdis_scn, &args, 1004 darray_estimators_data_get(&estimators) + p)); 1005 } 1006 if(stardis->mpi_initialized && stardis->mpi_rank != 0) { 1007 ERR(dump_map(stardis, &estimators, stdout)); 1008 } 1009 1010 end: 1011 if(estimators_initialized) { 1012 struct sdis_estimator** est = darray_estimators_data_get(&estimators); 1013 FOR_EACH(p, 0, darray_size_t_size_get(&stardis->compute_surface.primitives)) { 1014 SDIS(estimator_ref_put(est[p])); 1015 } 1016 darray_estimators_release(&estimators); 1017 } 1018 if(green) SDIS(green_function_ref_put(green)); 1019 return res; 1020 error: 1021 goto end; 1022 } 1023 1024 /******************************************************************************* 1025 * Public Functions 1026 ******************************************************************************/ 1027 1028 struct sdis_medium* 1029 find_medium_by_name 1030 (struct stardis* stardis, 1031 const char* name, 1032 unsigned* out_id) 1033 { 1034 struct sdis_medium* medium = NULL; 1035 size_t i; 1036 1037 ASSERT(stardis && name); 1038 1039 FOR_EACH(i, 0, darray_descriptions_size_get(&stardis->descriptions)) { 1040 unsigned id; 1041 struct description* desc = 1042 darray_descriptions_data_get(&stardis->descriptions) + i; 1043 if(strcmp(name, str_cget(get_description_name(desc))) != 0) 1044 continue; 1045 description_get_medium_id(desc, &id); 1046 ASSERT(darray_media_ptr_size_get(&stardis->media) > id); 1047 medium = darray_media_ptr_data_get(&stardis->media)[id]; 1048 if(out_id) *out_id = id; 1049 break; 1050 } 1051 return medium; 1052 } 1053 1054 /* Process an STL file describing a compute region 1055 * Triangles must be members of the geometry already */ 1056 res_T 1057 read_compute_surface 1058 (struct stardis* stardis) 1059 { 1060 res_T res = RES_OK; 1061 struct sstl* sstl = NULL; 1062 struct add_geom_ctx add_geom_ctx; 1063 struct sg3d_geometry_add_callbacks callbacks = SG3D_ADD_CALLBACKS_NULL__; 1064 const char* file; 1065 size_t i; 1066 double _2area = 0; 1067 1068 ASSERT(stardis); 1069 1070 file = str_cget(&stardis->solve_name); 1071 callbacks.get_indices = add_geom_ctx_indices; 1072 callbacks.get_position = add_geom_ctx_position; 1073 callbacks.add_triangle = add_compute_surface_triangle; 1074 callbacks.merge_triangle = merge_compute_surface_triangle; 1075 1076 ERR(sstl_create(stardis->logger, stardis->allocator, 0, &sstl)); 1077 res = sstl_load(sstl, file); 1078 if(res != RES_OK) { 1079 logger_print(stardis->logger, LOG_ERROR, 1080 "Cannot read STL file: '%s'\n", file); 1081 goto error; 1082 } 1083 ERR(sstl_get_desc(sstl, &add_geom_ctx.stl_desc)); 1084 ASSERT(add_geom_ctx.stl_desc.vertices_count <= UINT_MAX 1085 && add_geom_ctx.stl_desc.triangles_count <= UINT_MAX); 1086 1087 add_geom_ctx.custom = &stardis->compute_surface; 1088 1089 res = sg3d_geometry_add( 1090 stardis->geometry.sg3d, 1091 (unsigned)add_geom_ctx.stl_desc.vertices_count, 1092 (unsigned)add_geom_ctx.stl_desc.triangles_count, 1093 &callbacks, 1094 &add_geom_ctx); 1095 if(res != RES_OK) { 1096 logger_print(stardis->logger, LOG_ERROR, 1097 "Cannot add file content: '%s'\n", file); 1098 goto error; 1099 } 1100 1101 /* Compute area of the compute surface */ 1102 FOR_EACH(i, 0, add_geom_ctx.stl_desc.triangles_count) { 1103 const unsigned* trg = add_geom_ctx.stl_desc.indices + (i * 3); 1104 const float* v0f = add_geom_ctx.stl_desc.vertices + (trg[0] * 3); 1105 const float* v1f = add_geom_ctx.stl_desc.vertices + (trg[1] * 3); 1106 const float* v2f = add_geom_ctx.stl_desc.vertices + (trg[2] * 3); 1107 double v0[3], v1[3], v2[3], edge0[3], edge1[3], normal[3], norm; 1108 1109 /* Compute component area and volume */ 1110 d3_sub(edge0, d3_set_f3(v1, v1f), d3_set_f3(v0, v0f)); 1111 d3_sub(edge1, d3_set_f3(v2, v2f), d3_set_f3(v0, v0f)); 1112 d3_cross(normal, edge0, edge1); 1113 norm = d3_normalize(normal, normal); 1114 ASSERT(norm); 1115 _2area += norm; 1116 } 1117 1118 stardis->compute_surface.area = _2area * 0.5 1119 * stardis->scale_factor * stardis->scale_factor; 1120 1121 end: 1122 if(sstl) SSTL(ref_put(sstl)); 1123 return res; 1124 error: 1125 goto end; 1126 } 1127 1128 res_T 1129 stardis_compute 1130 (struct stardis* stardis, 1131 struct time* start) 1132 { 1133 res_T res = RES_OK; 1134 1135 ASSERT(stardis && start && (stardis->mode & COMPUTE_MODES)); 1136 1137 /* Compute */ 1138 if(stardis->mode & MODE_COMPUTE_PROBE_TEMP_ON_VOL) { 1139 ERR(compute_probe(stardis, start)); 1140 } else if(stardis->mode & MODE_COMPUTE_PROBE_TEMP_ON_SURF) { 1141 ERR(compute_probe_on_interface(stardis, start)); 1142 } else if(stardis->mode & MODE_COMPUTE_LIST_PROBE_TEMP_ON_SURF) { 1143 ERR(compute_probe_on_interface(stardis, start)); 1144 } else if(stardis->mode & MODE_COMPUTE_LIST_PROBE_FLUX_DNSTY_ON_SURF) { 1145 ERR(compute_probe_on_interface(stardis, start)); 1146 } else if(stardis->mode & MODE_COMPUTE_PROBE_FLUX_DNSTY_ON_SURF) { 1147 ERR(compute_probe_on_interface(stardis, start)); 1148 } else if(stardis->mode & MODE_COMPUTE_IMAGE_IR) { 1149 ERR(compute_camera(stardis, start)); 1150 } else if(stardis->mode & MODE_COMPUTE_TEMP_MEAN_IN_MEDIUM) { 1151 ERR(compute_medium(stardis, start)); 1152 } else if(stardis->mode & MODE_COMPUTE_TEMP_MEAN_ON_SURF) { 1153 ERR(compute_boundary(stardis, start)); 1154 } else if(stardis->mode & MODE_COMPUTE_FLUX_THROUGH_SURF) { 1155 ERR(compute_flux_boundary(stardis, start)); 1156 } else if(stardis->mode & MODE_COMPUTE_TEMP_MAP_ON_SURF) { 1157 ERR(compute_map(stardis, start)); 1158 } else { 1159 FATAL("Unknown mode.\n"); 1160 } 1161 1162 end: 1163 return res; 1164 error: 1165 logger_print(stardis->logger, LOG_ERROR, 1166 "%s: computation failed!\n", FUNC_NAME); 1167 goto end; 1168 }