stardis

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

stardis-app.c (32294B)


      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 #ifdef STARDIS_ENABLE_MPI
     17   #define _POSIX_C_SOURCE 200112L
     18 #endif
     19 
     20 #include <star/sg3d.h>
     21 
     22 #include "stardis-app.h"
     23 #include "stardis-args.h"
     24 #include "stardis-description.h"
     25 #include "stardis-default.h"
     26 #include "stardis-parsing.h"
     27 #include "stardis-compute.h"
     28 #include "stardis-intface.h"
     29 #include "stardis-solid.h"
     30 #include "stardis-fluid.h"
     31 #include "stardis-program.h"
     32 
     33 #include <star/senc3d.h>
     34 #include <star/sg3d_sencXd_helper.h>
     35 #include <star/sg3d_sdisXd_helper.h>
     36 
     37 #include <rsys/str.h>
     38 #include <rsys/library.h>
     39 #include <rsys/logger.h>
     40 #include <rsys/double2.h>
     41 #include <rsys/double3.h>
     42 #include <rsys/dynamic_array_double.h>
     43 
     44 #include <string.h>
     45 
     46 #ifdef STARDIS_ENABLE_MPI
     47   #include <stdio.h>
     48   #include <mpi.h>
     49 #endif
     50 
     51 static const struct dummies DUMMIES_NULL = DUMMIES_NULL__;
     52 static const struct counts COUNTS_NULL = COUNTS_NULL__;
     53 
     54 /*******************************************************************************
     55  * Local Functions
     56  ******************************************************************************/
     57 static struct sdis_interface*
     58 geometry_get_interface
     59   (const size_t itri, void* ctx)
     60 {
     61   struct darray_interface_ptrs* app_interface_data = ctx;
     62   ASSERT(app_interface_data
     63     && itri < darray_interface_ptrs_size_get(app_interface_data));
     64   return darray_interface_ptrs_cdata_get(app_interface_data)[itri];
     65 }
     66 
     67 static res_T
     68 check_delta_and_create_solid
     69   (struct stardis* stardis,
     70    struct description* description)
     71 {
     72   res_T res = RES_OK;
     73   struct senc3d_enclosure* enc = NULL;
     74   double ratio, delta_range[2] = { DBL_MAX, -DBL_MAX };
     75   const double acceptance_ratio = 3;
     76   struct senc3d_enclosure_header header;
     77   struct solid* solid = description->d.solid;
     78 
     79   ASSERT(stardis && description && description->type == DESC_MAT_SOLID);
     80 
     81   /* Create solid to have ID informed */
     82   /* Check if delta can fit possible multiple enclosures */
     83   if(stardis->senc3d_scn) {
     84     /* Due to previous errors, senc3d_scn can be unavailable */
     85     unsigned e, ecount = 0;
     86     const unsigned desc_id = solid->desc_id;
     87     double solid_volume = 0;
     88 
     89     /* The enclosures where created using description ids */
     90     ERR(senc3d_scene_get_enclosure_count_by_medium(stardis->senc3d_scn,
     91       desc_id, &ecount));
     92     if(ecount == 0) {
     93       unsigned ccount;
     94       ERR(sg3d_geometry_get_unique_triangles_with_properties_conflict_count(
     95         stardis->geometry.sg3d, &ccount));
     96       CHK(ccount == 0); /* This solid can only be unused if in conflict*/
     97     } else {
     98       int external = 0;
     99       FOR_EACH(e, 0, ecount) {
    100         ERR(senc3d_scene_get_enclosure_by_medium(stardis->senc3d_scn, desc_id,
    101           e, &enc));
    102         ERR(senc3d_enclosure_get_header(enc, &header));
    103         solid_volume += header.volume;
    104         if(header.is_infinite) {
    105           /* External solid, volume is negative and no delta walk expected */
    106           external = 1;
    107         } else {
    108           double d = header.volume / (header.area * 6);
    109           if(d <= 0) {
    110             /* Should not */
    111             res = RES_BAD_OP;
    112             goto error;
    113           }
    114           delta_range[0] = MMIN(delta_range[0], d);
    115           delta_range[1] = MMAX(delta_range[1], d);
    116         }
    117         ERR(senc3d_enclosure_ref_put(enc));
    118         enc = NULL;
    119       }
    120       if(ecount > 1 || !external) {
    121         ASSERT(0 < delta_range[0] && delta_range[0] <= delta_range[1]);
    122         ratio = delta_range[1] / delta_range[0];
    123         if(ratio > acceptance_ratio)
    124           logger_print(stardis->logger, LOG_WARNING,
    125             "Solid '%s' is used in %u different enclosures that have different "
    126             "delta requirements.\n",
    127             str_cget(&solid->name), ecount);
    128         /* Delta needs to be substituted with actual value */
    129         if(solid->delta == DELTA_AUTO) {
    130           solid->delta = delta_range[0];
    131           logger_print(stardis->logger, LOG_OUTPUT,
    132             "Auto delta for solid '%s' set to %g\n",
    133             str_cget(&solid->name), solid->delta);
    134         } else {
    135           int too_small
    136             = (delta_range[0] > solid->delta * acceptance_ratio);
    137           int too_big
    138             = (delta_range[0] * acceptance_ratio < solid->delta);
    139           /* Check if user delta is OK */
    140           if(too_small || too_big) {
    141             logger_print(stardis->logger, LOG_WARNING,
    142               "User delta for solid '%s' seems too %s: %g; "
    143               "auto delta would have set it to %g.\n",
    144               str_cget(&solid->name), (too_big ? "big" : "small"),
    145               solid->delta, delta_range[0]);
    146           }
    147         }
    148         /* Print power */
    149         if(solid->vpower != SDIS_VOLUMIC_POWER_NONE && solid->vpower != 0) {
    150           logger_print(stardis->logger, LOG_OUTPUT,
    151             "Power of the Solid '%s': %g W\n",
    152             str_cget(&solid->name), solid_volume * solid->vpower);
    153         }
    154       }
    155     }
    156   }
    157   ERR(create_solver_solid(stardis, solid));
    158 
    159 end:
    160   if(enc) SENC3D(enclosure_ref_put(enc));
    161   return res;
    162 error:
    163   goto end;
    164 }
    165 
    166 /*******************************************************************************
    167  * Public Functions
    168  ******************************************************************************/
    169 
    170 #ifdef STARDIS_ENABLE_MPI
    171 /* To be called after logger has been initialized
    172  * and before stardis is initialized */
    173 res_T
    174 init_mpi
    175   (int* pargc,
    176    char** pargv[],
    177    void (*prt_err_fn)(const char* msg, void* ctx),
    178    void (*prt_warn_fn)(const char* msg, void* ctx))
    179 {
    180   res_T res = RES_OK;
    181   char buf[64];
    182   int mpi_provided;
    183 
    184   ASSERT(pargc && pargv && prt_err_fn && prt_warn_fn);
    185 
    186   if(MPI_Init_thread(pargc, pargv, MPI_THREAD_MULTIPLE, &mpi_provided)
    187     != MPI_SUCCESS)
    188   {
    189     prt_err_fn("Cannot init MPI\n", NULL);
    190     res = RES_BAD_ARG;
    191     goto error;
    192   }
    193   else if(mpi_provided != MPI_THREAD_MULTIPLE) {
    194     const char* lvl;
    195     switch(mpi_provided) {
    196       case MPI_THREAD_SINGLE: lvl = "MPI_THREAD_SINGLE"; break;
    197       case MPI_THREAD_FUNNELED: lvl = "MPI_THREAD_FUNNELED"; break;
    198       case MPI_THREAD_SERIALIZED: lvl = "MPI_THREAD_SERIALIZED"; break;
    199       default: FATAL("Unreachable code.\n"); break;
    200     }
    201     snprintf(buf, sizeof(buf)-1, "MPI support restricted to %s\n", lvl);
    202     prt_warn_fn(buf, NULL);
    203   }
    204 
    205 end:
    206   return res;
    207 error:
    208   goto end;
    209 }
    210 
    211 void
    212 finalize_mpi(void)
    213 {
    214   int initialized;
    215 
    216   CHK(MPI_Initialized(&initialized) == MPI_SUCCESS);
    217   if(initialized)
    218     CHK(MPI_Finalize() == MPI_SUCCESS);
    219 }
    220 #endif
    221 
    222 res_T
    223 stardis_init
    224   (const struct args* args,
    225    struct logger* logger,
    226    struct mem_allocator* allocator,
    227    struct stardis* stardis)
    228 {
    229   res_T tmp_res, res = RES_OK;
    230   struct sg3d_sdisXd_scene_create_context create_context;
    231   struct htable_intface htable_interfaces;
    232   struct str str, name;
    233   FILE* f = NULL;
    234   unsigned i, vcount, tcount, ocount, count;
    235   int is_for_compute;
    236   struct sdis_device_create_args dev_args = SDIS_DEVICE_CREATE_ARGS_DEFAULT;
    237 
    238   ASSERT(args && logger && allocator && stardis);
    239 
    240   str_init(allocator, &str);
    241   str_init(allocator, &name);
    242   /* Init everything that cannot fail */
    243   stardis->dummies = DUMMIES_NULL;
    244   stardis->logger = logger;
    245   stardis->allocator = allocator;
    246   htable_intface_init(stardis->allocator, &htable_interfaces);
    247   darray_descriptions_init(stardis->allocator, &stardis->descriptions);
    248   d3_set(stardis->probe, args->pos_and_time);
    249   d2_set(stardis->time_range, args->pos_and_time + 3);
    250   stardis->dev = NULL;
    251   stardis->sdis_scn = NULL;
    252   stardis->senc3d_scn = NULL;
    253   stardis->mode = args->mode;
    254   stardis->counts = COUNTS_NULL;
    255   init_camera(stardis->allocator, &stardis->camera);
    256   str_init(stardis->allocator, &stardis->solve_name);
    257   str_init(stardis->allocator, &stardis->dump_model_filename);
    258   str_init(stardis->allocator, &stardis->paths_filename);
    259   str_init(stardis->allocator, &stardis->bin_green_filename);
    260   str_init(stardis->allocator, &stardis->end_paths_filename);
    261   str_init(stardis->allocator, &stardis->rndgen_state_in_filename);
    262   str_init(stardis->allocator, &stardis->rndgen_state_out_filename);
    263   str_init(stardis->allocator, &stardis->chunks_prefix);
    264   darray_size_t_init(stardis->allocator, &stardis->compute_surface.primitives);
    265   darray_sides_init(stardis->allocator, &stardis->compute_surface.sides);
    266   darray_uint_init(stardis->allocator, &stardis->compute_surface.err_triangles);
    267   darray_probe_boundary_init(stardis->allocator, &stardis->probe_boundary_list);
    268   stardis->compute_surface.area = 0;
    269   stardis->samples = args->samples;
    270   stardis->nthreads = args->nthreads;
    271   stardis->picard_order = args->picard_order;
    272   stardis->scale_factor = -1; /* invalid value */
    273   stardis->radenv = RADIATIVE_ENV_DEFAULT;
    274   stardis->radenv_def = 0;
    275   stardis->initial_time = args->initial_time;
    276   stardis->geometry_initialized = 0;
    277   d2(stardis->t_range, INF, -INF);
    278   stardis->dump_paths = SDIS_HEAT_PATH_NONE;
    279   if(args->dump_paths & DUMP_ERROR)
    280     stardis->dump_paths |= SDIS_HEAT_PATH_FAILURE;
    281   if(args->dump_paths & DUMP_SUCCESS)
    282     stardis->dump_paths |= SDIS_HEAT_PATH_SUCCESS;
    283   stardis->diff_algo = args->diff_algo;
    284   stardis->next_medium_id = 0;
    285   stardis->undefined_medium_behind_boundary_id = SENC3D_UNSPECIFIED_MEDIUM;
    286   stardis->verbose = args->verbose;
    287   stardis->extsrc = EXTERN_SOURCE_NULL;
    288   stardis->disable_intrad = args->disable_intrad;
    289   darray_media_ptr_init(stardis->allocator, &stardis->media);
    290 
    291   /* If a dump is expected, we won't process any computation */
    292   is_for_compute =
    293     (stardis->mode & COMPUTE_MODES) && !(stardis->mode & MODE_DUMP_MODEL);
    294 
    295   dev_args.logger = stardis->logger;
    296   dev_args.allocator = stardis->allocator;
    297   dev_args.nthreads_hint = stardis->nthreads;
    298   dev_args.verbosity = stardis->verbose;
    299 
    300 #ifdef STARDIS_ENABLE_MPI
    301   logger_print(stardis->logger, LOG_OUTPUT, "MPI is enabled.\n");
    302   /* Open MPI accepts the C/C++ argc and argv arguments to main,
    303    * but neither modifies, interprets, nor distributes them: use NULL */
    304   CHK(MPI_Initialized(&stardis->mpi_initialized) == MPI_SUCCESS);
    305   if(stardis->mpi_initialized)
    306     CHK(MPI_Comm_rank(MPI_COMM_WORLD, &stardis->mpi_rank) == MPI_SUCCESS);
    307 #else
    308   logger_print(stardis->logger, LOG_OUTPUT, "MPI is disabled.\n");
    309   stardis->mpi_initialized = 0;
    310 #endif
    311 
    312   dev_args.use_mpi = stardis->mpi_initialized;
    313 
    314   ERR(sdis_device_create(&dev_args, &stardis->dev));
    315 
    316   ERR(init_geometry(stardis->logger, stardis->allocator, stardis->verbose,
    317     &stardis->geometry));
    318   stardis->geometry_initialized = 1;
    319 
    320   if(args->dump_model_filename) {
    321     ERR(str_set(&stardis->dump_model_filename, args->dump_model_filename));
    322   }
    323 
    324   if(args->mode & MODE_COMPUTE_IMAGE_IR) {
    325     ERR(parse_camera(stardis->logger, args->camera, stardis));
    326   }
    327   else if(args->mode & MODE_COMPUTE_TEMP_MEAN_IN_MEDIUM) {
    328     ERR(str_set(&stardis->solve_name, args->medium_name));
    329   }
    330   else if((args->mode & MODE_COMPUTE_PROBE_TEMP_ON_SURF)
    331        || (args->mode & MODE_COMPUTE_PROBE_FLUX_DNSTY_ON_SURF)
    332        || (args->mode & MODE_COMPUTE_LIST_PROBE_TEMP_ON_SURF)
    333        || (args->mode & MODE_COMPUTE_LIST_PROBE_FLUX_DNSTY_ON_SURF)) {
    334     ERR(darray_probe_boundary_copy
    335       (&stardis->probe_boundary_list, &args->probe_boundary_list));
    336   }
    337   else if(args->mode & SURFACE_COMPUTE_MODES) {
    338     ERR(str_set(&stardis->solve_name, args->solve_filename));
    339   }
    340   else if(args->mode & MODE_DUMP_C_CHUNKS) {
    341     ERR(str_set(&stardis->chunks_prefix, args->chunks_prefix));
    342   }
    343   ERR(read_model(&args->model_files, stardis));
    344 
    345   create_context.geometry = stardis->geometry.sg3d;
    346   create_context.app_interface_getter = geometry_get_interface;
    347   create_context.app_interface_data = &stardis->geometry.interf_bytrg;
    348   ERR(sg3d_geometry_get_unique_vertices_count(stardis->geometry.sg3d, &vcount));
    349   ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tcount));
    350 
    351   logger_print(stardis->logger, LOG_OUTPUT,
    352     "Read %u unique triangles.\n", tcount);
    353 
    354   ERR(sg3d_geometry_validate_properties(stardis->geometry.sg3d,
    355      validate_properties, stardis));
    356   ERR(sg3d_geometry_get_unique_triangles_with_properties_conflict_count(
    357     stardis->geometry.sg3d, &count));
    358   if(count) {
    359     if(!str_is_empty(&stardis->dump_model_filename)) {
    360       ERR(str_copy(&name, &stardis->dump_model_filename));
    361       ERR(str_append(&name, "_property_conflits.obj"));
    362       f = fopen(str_cget(&name), "w");
    363       if(!f) {
    364         logger_print(stardis->logger, LOG_ERROR,
    365           "cannot open file '%s' for writing.\n", str_cget(&name));
    366         res = RES_IO_ERR;
    367         goto error;
    368       }
    369       ERR(sg3d_geometry_dump_as_obj(stardis->geometry.sg3d, f,
    370             SG3D_OBJ_DUMP_PROPERTY_CONFLICTS));
    371       fclose(f); f = NULL;
    372     }
    373     logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING),
    374       "Property conflicts found in the model (%u triangles).\n", count);
    375     if(is_for_compute) {
    376       res = RES_BAD_ARG;
    377       goto error;
    378     }
    379   }
    380 
    381   /* If computation is on a compute surface, read it */
    382   if(args->mode & SURFACE_COMPUTE_MODES) {
    383     unsigned save_count = count;
    384     ASSERT(!str_is_empty(&stardis->solve_name));
    385     ERR(read_compute_surface(stardis));
    386     /* Check compute surface  */
    387     ERR(sg3d_geometry_validate_properties(stardis->geometry.sg3d,
    388       validate_properties, stardis));
    389     ERR(sg3d_geometry_get_unique_triangles_with_properties_conflict_count(
    390       stardis->geometry.sg3d, &count));
    391     ASSERT(count >= save_count);
    392     if(save_count != count) {
    393       logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING),
    394         "Invalid compute region defined by file '%s'.\n",
    395         str_cget(&stardis->solve_name));
    396       logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING),
    397         "The file contains %u triangles not in the model.\n", count - save_count);
    398       if(is_for_compute) {
    399         res = RES_BAD_ARG;
    400         goto error;
    401       }
    402     }
    403     logger_print(stardis->logger, LOG_OUTPUT, "Compute surface area is %g m2\n",
    404       stardis->compute_surface.area);
    405   }
    406 
    407   /* Create enclosures */
    408   tmp_res = init_enclosures(stardis);
    409   if(tmp_res != RES_OK && is_for_compute) {
    410     res = tmp_res;
    411     goto error;
    412   }
    413   ERR(senc3d_scene_get_overlapping_triangles_count(stardis->senc3d_scn, &ocount));
    414   if(ocount) {
    415     if(!str_is_empty(&stardis->dump_model_filename)) {
    416       ERR(str_copy(&name, &stardis->dump_model_filename));
    417       ERR(str_append(&name, "_overlapping_triangles.obj"));
    418       f = fopen(str_cget(&name), "w");
    419       if(!f) {
    420         logger_print(stardis->logger, LOG_ERROR,
    421           "cannot open file '%s' for writing.\n", str_cget(&name));
    422         res = RES_IO_ERR;
    423         goto error;
    424       }
    425       /* Dump vertices */
    426       for(i = 0; i < vcount; i++) {
    427         double coord[3];
    428         ERR(senc3d_scene_get_vertex(stardis->senc3d_scn, i, coord));
    429         fprintf(f, "v %.16g %.16g %.16g\n", SPLIT3(coord));
    430       }
    431       /* Dump triangles */
    432       for(i = 0; i < ocount; i++) {
    433         unsigned id, trg[3];
    434         ERR(senc3d_scene_get_overlapping_triangle(stardis->senc3d_scn, i, &id));
    435         ERR(senc3d_scene_get_triangle(stardis->senc3d_scn, id, trg));
    436         fprintf(f, "f %u %u %u\n",
    437             1 + trg[0], 1 + trg[1], 1 + trg[2]); /* OBJ indexing starts at 1 */
    438       }
    439       fclose(f); f = NULL;
    440     }
    441     logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING),
    442       "Scene contains %u overlapping triangles.\n",
    443       ocount);
    444     if(is_for_compute) {
    445       res = RES_BAD_ARG;
    446       goto error;
    447     }
    448   }
    449 
    450   /* Create solids, finalize program data, and log model information */
    451   for(i = 0; i < darray_descriptions_size_get(&stardis->descriptions); i++) {
    452     struct description* desc =
    453       darray_descriptions_data_get(&stardis->descriptions) + i;
    454     if(desc->type == DESC_MAT_SOLID) {
    455       tmp_res = check_delta_and_create_solid(stardis, desc);
    456     } else if(desc->type == DESC_PROGRAM && desc->d.program->finalize) {
    457       enum stardis_return_status rs;
    458       rs = desc->d.program->finalize(desc->d.program->prog_data);
    459       switch(rs) {
    460         case STARDIS_SUCCESS: tmp_res = RES_OK; break;
    461         case STARDIS_FAILURE: tmp_res = RES_BAD_ARG; break;
    462         default:
    463           FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n");
    464       }
    465     }
    466     if(tmp_res != RES_OK && is_for_compute) {
    467       res = tmp_res;
    468       goto error;
    469     }
    470     ERR(str_print_description(&str, i, desc));
    471     logger_print(stardis->logger, LOG_OUTPUT, "%s\n", str_cget(&str));
    472   }
    473 
    474   if(is_for_compute) {
    475     for(i = 0; i < tcount; ++i) {
    476       ERR(create_intface(stardis, i, &htable_interfaces));
    477     }
    478     if(args->paths_filename) {
    479       ERR(str_set(&stardis->paths_filename, args->paths_filename));
    480     }
    481     if(args->bin_green_filename) {
    482       ERR(str_set(&stardis->bin_green_filename, args->bin_green_filename));
    483     }
    484     if(args->end_paths_filename) {
    485       ERR(str_set(&stardis->end_paths_filename, args->end_paths_filename));
    486     }
    487     if(args->rndgen_state_in_filename) {
    488       ERR(str_set(&stardis->rndgen_state_in_filename,
    489         args->rndgen_state_in_filename));
    490     }
    491     if(args->rndgen_state_out_filename) {
    492       ERR(str_set(&stardis->rndgen_state_out_filename,
    493         args->rndgen_state_out_filename));
    494     }
    495   }
    496 
    497   /* If computation is on a volume, check medium is known */
    498   if(args->mode & MODE_COMPUTE_TEMP_MEAN_IN_MEDIUM) {
    499     if(!find_medium_by_name(stardis, str_cget(&stardis->solve_name), NULL)) {
    500       logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING),
    501         "Cannot find medium '%s'\n", str_cget(&stardis->solve_name));
    502       res = RES_BAD_ARG;
    503       goto error;
    504     }
    505   }
    506 
    507   if(is_for_compute) {
    508     struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT;
    509     ASSERT(darray_interface_ptrs_size_get(&stardis->geometry.interf_bytrg)
    510       == tcount);
    511 
    512     res = radiative_env_create_solver_radiative_env(&stardis->radenv, stardis);
    513     if(res != RES_OK) goto error;
    514 
    515     scn_args.get_indices = sg3d_sdisXd_geometry_get_indices;
    516     scn_args.get_interface = sg3d_sdisXd_geometry_get_interface;
    517     scn_args.get_position = sg3d_sdisXd_geometry_get_position;
    518     scn_args.nprimitives = tcount;
    519     scn_args.nvertices = vcount;
    520     scn_args.fp_to_meter = stardis->scale_factor;
    521     scn_args.t_range[0] = stardis->t_range[0];
    522     scn_args.t_range[1] = stardis->t_range[1];
    523     scn_args.source = stardis->extsrc.sdis_src;
    524     scn_args.radenv = stardis->radenv.sdis_radenv;
    525     scn_args.context = &create_context;
    526 
    527     /* Setting Tmax to 0 is a way of setting the radiative coefficient to 0, and
    528      * thus setting the probability of evolving in a radiative random walk to
    529      * zero. */
    530     if(stardis->disable_intrad) {
    531       scn_args.t_range[0] = 0;
    532       scn_args.t_range[1] = 0;
    533     }
    534 
    535     res = sdis_scene_create(stardis->dev, &scn_args, &stardis->sdis_scn);
    536     if(res != RES_OK) {
    537       logger_print(stardis->logger, LOG_ERROR,
    538         "Cannot create the stardis solver scene.\n");
    539       goto error;
    540     }
    541   }
    542 
    543 exit:
    544   if(f) fclose(f);
    545   str_release(&str);
    546   str_release(&name);
    547   htable_intface_release(&htable_interfaces);
    548   return res;
    549 error:
    550   stardis_release(stardis);
    551   goto exit;
    552 }
    553 
    554 void
    555 stardis_release
    556   (struct stardis* stardis)
    557 {
    558   size_t i;
    559 
    560   ASSERT(stardis);
    561 
    562   if(stardis->dev) SDIS(device_ref_put(stardis->dev));
    563   if(stardis->sdis_scn) SDIS(scene_ref_put(stardis->sdis_scn));
    564   if(stardis->senc3d_scn) SENC3D(scene_ref_put(stardis->senc3d_scn));
    565   str_release(&stardis->solve_name);
    566   str_release(&stardis->dump_model_filename);
    567   str_release(&stardis->paths_filename);
    568   str_release(&stardis->bin_green_filename);
    569   str_release(&stardis->end_paths_filename);
    570   str_release(&stardis->chunks_prefix);
    571 
    572   extern_source_release(&stardis->extsrc);
    573   radiative_env_release(&stardis->radenv);
    574 
    575   /* release non-PROGRAM descritions first */
    576   FOR_EACH(i, 0, darray_descriptions_size_get(&stardis->descriptions)) {
    577     struct description* d = darray_descriptions_data_get(&stardis->descriptions) +i;
    578     if(d->type == DESC_PROGRAM) continue;
    579     release_description(d, stardis->allocator);
    580   }
    581   /* release PROGRAM descritions */
    582   FOR_EACH(i, 0, darray_descriptions_size_get(&stardis->descriptions)) {
    583     struct description* d = darray_descriptions_data_get(&stardis->descriptions) +i;
    584     if(d->type != DESC_PROGRAM) continue;
    585     release_description(d, stardis->allocator);
    586   }
    587   darray_descriptions_release(&stardis->descriptions);
    588   if(stardis->geometry_initialized)
    589     release_geometry(&stardis->geometry);
    590   darray_size_t_release(&stardis->compute_surface.primitives);
    591   darray_sides_release(&stardis->compute_surface.sides);
    592   darray_uint_release(&stardis->compute_surface.err_triangles);
    593   FOR_EACH(i, 0, darray_media_ptr_size_get(&stardis->media)) {
    594     if(darray_media_ptr_data_get(&stardis->media)[i])
    595       SDIS(medium_ref_put(darray_media_ptr_data_get(&stardis->media)[i]));
    596   }
    597   darray_media_ptr_release(&stardis->media);
    598   release_camera(&stardis->camera);
    599   if(stardis->dummies.stardis_fluid) {
    600     release_fluid(stardis->dummies.stardis_fluid, stardis->allocator);
    601   }
    602   if(stardis->dummies.stardis_solid) {
    603     release_solid(stardis->dummies.stardis_solid, stardis->allocator);
    604   }
    605   darray_probe_boundary_release(&stardis->probe_boundary_list);
    606 }
    607 
    608 unsigned
    609 allocate_stardis_medium_id
    610   (struct stardis* stardis)
    611 {
    612   ASSERT(stardis);
    613   return stardis->next_medium_id++;
    614 }
    615 
    616 res_T
    617 init_enclosures
    618   (struct stardis* stardis)
    619 {
    620   res_T res = RES_OK;
    621   unsigned tsz, vsz;
    622   struct senc3d_device* senc_dev = NULL;
    623 
    624   ERR(sg3d_geometry_get_unique_triangles_count(stardis->geometry.sg3d, &tsz));
    625 
    626   ERR(sg3d_geometry_get_unique_vertices_count(stardis->geometry.sg3d, &vsz));
    627   ERR(senc3d_device_create(stardis->logger, stardis->allocator,
    628     stardis->nthreads, stardis->verbose, &senc_dev));
    629   stardis->undefined_medium_behind_boundary_id
    630     = allocate_stardis_medium_id(stardis);
    631   ERR(senc3d_scene_create(senc_dev,
    632     SENC3D_CONVENTION_NORMAL_BACK | SENC3D_CONVENTION_NORMAL_OUTSIDE,
    633     tsz, sg3d_sencXd_geometry_get_indices, sg3d_sencXd_geometry_get_media,
    634     vsz, sg3d_sencXd_geometry_get_position, stardis->geometry.sg3d,
    635     &stardis->senc3d_scn));
    636 exit:
    637   if(senc_dev) senc3d_device_ref_put(senc_dev);
    638   return res;
    639 error:
    640   goto exit;
    641 }
    642 
    643 #define COUNT_SIDE(Rank) {\
    644   if(properties[(Rank)] == SG3D_UNSPECIFIED_PROPERTY) undef_count++;\
    645   else {\
    646     ASSERT(properties[(Rank)] < darray_descriptions_size_get(&stardis->descriptions));\
    647     ASSERT(DESC_IS_MEDIUM(descs+properties[(Rank)]));\
    648     if(DESC_IS_SOLID(descs+properties[(Rank)])) { solid_count++; }\
    649     else { ASSERT(DESC_IS_FLUID(descs+properties[(Rank)])); fluid_count++; }\
    650   }\
    651 }
    652 
    653 res_T
    654 validate_properties
    655   (const unsigned itri,
    656    const unsigned properties[SG3D_PROP_TYPES_COUNT__],
    657    void* context,
    658    int* properties_conflict_status)
    659 {
    660   res_T res = RES_OK;
    661   struct stardis* stardis = context;
    662   unsigned undef_count, solid_count, fluid_count, intface_count;
    663   const struct description* descs;
    664   const struct description* intface = NULL;
    665 
    666   (void)itri;
    667   ASSERT(stardis && properties_conflict_status);
    668   descs = darray_descriptions_cdata_get(&stardis->descriptions);
    669   *properties_conflict_status = NO_PROPERTY_CONFLICT;
    670   undef_count = solid_count = fluid_count = intface_count = 0;
    671 
    672   COUNT_SIDE(SG3D_FRONT);
    673   COUNT_SIDE(SG3D_BACK);
    674   if(properties[SG3D_INTFACE] == SG3D_UNSPECIFIED_PROPERTY)
    675     undef_count++;
    676   else intface_count++;
    677 
    678   ASSERT(solid_count <= 2 && fluid_count <= 2 && intface_count <= 1);
    679   ASSERT(undef_count + solid_count + fluid_count + intface_count == 3);
    680   if(intface_count) {
    681     ASSERT(properties[SG3D_INTFACE]
    682       < darray_descriptions_size_get(&stardis->descriptions));
    683     intface = descs + properties[SG3D_INTFACE];
    684     switch (intface->type) {
    685     case DESC_BOUND_H_FOR_FLUID:
    686     case DESC_BOUND_H_FOR_FLUID_PROG:
    687       if(!(solid_count == 0 && fluid_count == 1)) {
    688         if(solid_count + fluid_count == 2)
    689           *properties_conflict_status = BOUND_H_FOR_FLUID_BETWEEN_2_DEFS;
    690         else if(solid_count + fluid_count == 0)
    691           *properties_conflict_status = BOUND_H_FOR_FLUID_BETWEEN_2_UNDEFS;
    692         else if(solid_count == 1)
    693           *properties_conflict_status = BOUND_H_FOR_FLUID_ENCLOSING_SOLID;
    694         else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n");
    695         goto end;
    696       }
    697       break;
    698     case DESC_BOUND_H_FOR_SOLID:
    699     case DESC_BOUND_H_FOR_SOLID_PROG:
    700       if(!(solid_count == 1 && fluid_count == 0)) {
    701         if(solid_count + fluid_count == 2)
    702           *properties_conflict_status = BOUND_H_FOR_SOLID_BETWEEN_2_DEFS;
    703         else if(solid_count + fluid_count == 0)
    704           *properties_conflict_status = BOUND_H_FOR_SOLID_BETWEEN_2_UNDEFS;
    705         else if(fluid_count == 1)
    706           *properties_conflict_status = BOUND_H_FOR_SOLID_ENCLOSING_FLUID;
    707         else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n");
    708         goto end;
    709       }
    710       break;
    711     case DESC_BOUND_HF_FOR_SOLID:
    712     case DESC_BOUND_HF_FOR_SOLID_PROG:
    713       if(!(solid_count == 1 && fluid_count == 0)) {
    714         if(solid_count + fluid_count == 2)
    715           *properties_conflict_status = BOUND_HF_FOR_SOLID_BETWEEN_2_DEFS;
    716         else if(solid_count + fluid_count == 0)
    717           *properties_conflict_status = BOUND_HF_FOR_SOLID_BETWEEN_2_UNDEFS;
    718         else if(fluid_count == 1)
    719           *properties_conflict_status = BOUND_HF_FOR_SOLID_ENCLOSING_FLUID;
    720         else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n");
    721         goto end;
    722       }
    723       break;
    724     case DESC_BOUND_T_FOR_SOLID:
    725     case DESC_BOUND_T_FOR_SOLID_PROG:
    726       if(!(solid_count == 1 && fluid_count == 0)) {
    727         if(solid_count + fluid_count == 2)
    728           *properties_conflict_status = BOUND_T_FOR_SOLID_BETWEEN_2_DEFS;
    729         else if(solid_count + fluid_count == 0)
    730           *properties_conflict_status = BOUND_T_FOR_SOLID_BETWEEN_2_UNDEFS;
    731         else if(fluid_count == 1)
    732           *properties_conflict_status = BOUND_T_FOR_SOLID_ENCLOSING_FLUID;
    733         else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n");
    734         goto end;
    735       }
    736       break;
    737     case DESC_BOUND_F_FOR_SOLID:
    738     case DESC_BOUND_F_FOR_SOLID_PROG:
    739       if(!(solid_count == 1 && fluid_count == 0)) {
    740         if(solid_count + fluid_count == 2)
    741           *properties_conflict_status = BOUND_F_FOR_SOLID_BETWEEN_2_DEFS;
    742         else if(solid_count + fluid_count == 0)
    743           *properties_conflict_status = BOUND_F_FOR_SOLID_BETWEEN_2_UNDEFS;
    744         else if(fluid_count == 1)
    745           *properties_conflict_status = BOUND_F_FOR_SOLID_ENCLOSING_FLUID;
    746         else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n");
    747         goto end;
    748       }
    749       break;
    750     case DESC_SOLID_FLUID_CONNECT:
    751     case DESC_SOLID_FLUID_CONNECT_PROG:
    752       if(solid_count != 1 || fluid_count != 1) {
    753         if(solid_count == 2)
    754           *properties_conflict_status = SFCONNECT_BETWEEN_2_SOLIDS;
    755         else if(fluid_count == 2)
    756           *properties_conflict_status = SFCONNECT_BETWEEN_2_FLUIDS;
    757         else if(solid_count + fluid_count == 1)
    758           *properties_conflict_status = SFCONNECT_USED_AS_BOUNDARY;
    759         else if(solid_count + fluid_count == 0)
    760           *properties_conflict_status = SFCONNECT_BETWEEN_2_UNDEFS;
    761         else FATAL("error:" STR(__FILE__) ":" STR(__LINE__)"\n");
    762         goto end;
    763       }
    764       break;
    765     case DESC_SOLID_SOLID_CONNECT:
    766     case DESC_SOLID_SOLID_CONNECT_PROG:
    767       if(solid_count != 2) {
    768         /*if(soli_count == 1 && fluid_count == 1)*/
    769           /**properties_conflict_status = SSCONNECT_BETWEEN_SOLID_AND_FLUID;*/
    770         goto end;
    771       }
    772       break;
    773     default:
    774       FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n");
    775     }
    776   } else {
    777     /* No interface defined */
    778     ASSERT(intface_count == 0 && undef_count >= 1);
    779     if(undef_count == 3) {
    780       *properties_conflict_status = TRG_WITH_NO_PROPERTY;
    781       goto end;
    782     }
    783     if(fluid_count == 2) {
    784       *properties_conflict_status = NO_CONNECTION_BETWEEN_2_FLUIDS;
    785       goto end;
    786     }
    787     if(undef_count == 2) {
    788       ASSERT(fluid_count + solid_count == 1);
    789       if(fluid_count)
    790         *properties_conflict_status = NO_BOUND_BETWEEN_FLUID_AND_UNDEF;
    791       else *properties_conflict_status = NO_BOUND_BETWEEN_SOLID_AND_UNDEF;
    792       goto end;
    793     }
    794     if(undef_count == 1 && solid_count == 1 && fluid_count == 1) {
    795       *properties_conflict_status = NO_CONNECTION_BETWEEN_SOLID_AND_FLUID;
    796       goto end;
    797     }
    798     /* Undef interface between solids is OK */
    799     CHK(solid_count == 2);
    800   }
    801 
    802 end:
    803   return res;
    804 }
    805 
    806 #undef COUNT_SIDE
    807 
    808 res_T
    809 init_geometry
    810   (struct logger* logger,
    811    struct mem_allocator* allocator,
    812    const int verbose,
    813    struct geometry* geom)
    814 {
    815   res_T res = RES_OK;
    816   struct sg3d_device* sg3d_dev = NULL;
    817 
    818   ASSERT(allocator && geom);
    819 
    820   geom->sg3d = NULL;
    821   darray_interface_ptrs_init(allocator, &geom->interfaces);
    822   darray_interface_ptrs_init(allocator, &geom->interf_bytrg);
    823   ERR(sg3d_device_create(logger, allocator, verbose, &sg3d_dev));
    824   ERR(sg3d_geometry_create(sg3d_dev, &geom->sg3d));
    825 
    826 exit:
    827   if(sg3d_dev) SG3D(device_ref_put(sg3d_dev));
    828   return res;
    829 error:
    830   release_geometry(geom);
    831   goto exit;
    832 }
    833 
    834 void
    835 release_geometry
    836   (struct geometry* geom)
    837 {
    838   size_t i;
    839   struct sdis_interface
    840     ** intf = darray_interface_ptrs_data_get(&geom->interfaces);
    841   if(geom->sg3d) SG3D(geometry_ref_put(geom->sg3d));
    842   for(i = 0; i < darray_interface_ptrs_size_get(&geom->interfaces); ++i)
    843     SDIS(interface_ref_put(intf[i]));
    844   darray_interface_ptrs_release(&geom->interfaces);
    845   darray_interface_ptrs_release(&geom->interf_bytrg);
    846 }
    847 
    848 void
    849 init_camera
    850   (struct mem_allocator* alloc, struct camera* cam)
    851 {
    852   ASSERT(alloc && cam);
    853   d3(cam->pos, STARDIS_DEFAULT_RENDERING_POS);
    854   d3(cam->tgt, STARDIS_DEFAULT_RENDERING_TGT);
    855   d3(cam->up, STARDIS_DEFAULT_RENDERING_UP);
    856   cam->fmt = STARDIS_DEFAULT_RENDERING_OUTPUT_FILE_FMT;
    857   cam->fov = STARDIS_DEFAULT_RENDERING_FOV;
    858   cam->spp = STARDIS_DEFAULT_RENDERING_SPP;
    859   cam->img_width = STARDIS_DEFAULT_RENDERING_IMG_WIDTH;
    860   cam->img_height = STARDIS_DEFAULT_RENDERING_IMG_HEIGHT;
    861   d2(cam->time_range, STARDIS_DEFAULT_RENDERING_TIME);
    862   cam->auto_look_at = 1;
    863   str_init(alloc, &cam->file_name);
    864 }
    865 
    866 void
    867 release_camera
    868   (struct camera* cam)
    869 {
    870   ASSERT(cam);
    871   str_release(&cam->file_name);
    872 }
    873 
    874 void
    875 log_err_fn
    876   (const char* msg, void* ctx)
    877 {
    878 #ifdef STARDIS_ENABLE_MPI
    879   int initialized, rank = 0;
    880 #endif
    881 
    882   ASSERT(msg);
    883   (void)ctx;
    884 
    885 #ifdef STARDIS_ENABLE_MPI
    886   CHK(MPI_Initialized(&initialized) == MPI_SUCCESS);
    887   if(initialized) CHK(MPI_Comm_rank(MPI_COMM_WORLD, &rank) == MPI_SUCCESS);
    888   /* Only master prints */
    889   if(rank != 0) return;
    890 #endif
    891 
    892 #ifdef OS_WINDOWS
    893   fprintf(stderr, "error: %s", msg);
    894 #else
    895   fprintf(stderr, "\x1b[31merror:\x1b[0m %s", msg);
    896 #endif
    897 }
    898 
    899 void
    900 log_warn_fn
    901   (const char* msg, void* ctx)
    902 {
    903 #ifdef STARDIS_ENABLE_MPI
    904   int initialized, rank = 0;
    905 #endif
    906 
    907   ASSERT(msg);
    908   (void)ctx;
    909 
    910 #ifdef STARDIS_ENABLE_MPI
    911   CHK(MPI_Initialized(&initialized) == MPI_SUCCESS);
    912   if(initialized) CHK(MPI_Comm_rank(MPI_COMM_WORLD, &rank) == MPI_SUCCESS);
    913   /* Only master prints */
    914   if(rank != 0) return;
    915 #endif
    916 
    917 #ifdef OS_WINDOWS
    918   fprintf(stderr, "warning: %s", msg);
    919 #else
    920   fprintf(stderr, "\x1b[33mwarning:\x1b[0m %s", msg);
    921 #endif
    922 }
    923 
    924 void
    925 log_prt_fn
    926   (const char* msg, void* ctx)
    927 {
    928 #ifdef STARDIS_ENABLE_MPI
    929   int initialized, rank = 0;
    930 #endif
    931 
    932   ASSERT(msg);
    933   (void)ctx;
    934 
    935 #ifdef STARDIS_ENABLE_MPI
    936   CHK(MPI_Initialized(&initialized) == MPI_SUCCESS);
    937   if(initialized) CHK(MPI_Comm_rank(MPI_COMM_WORLD, &rank) == MPI_SUCCESS);
    938   /* only master prints */
    939   if(rank != 0) return;
    940 #endif
    941 
    942 #ifdef OS_WINDOWS
    943   fprintf(stderr, "message: %s", msg);
    944 #else
    945   fprintf(stderr, "\x1b[32moutput:\x1b[0m %s", msg);
    946 #endif
    947 }
    948