stardis

Perform coupled heat transfer calculations
git clone git://git.meso-star.fr/stardis.git
Log | Files | Refs | README | LICENSE

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(&region->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(&region->primitives, &uid));
    790   ERR(darray_sides_push_back(&region->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 }