stardis

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

stardis-parsing.c (84391B)


      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-parsing.h"
     17 #include "stardis-app.h"
     18 #include "stardis-args.h"
     19 #include "stardis-description.h"
     20 #include "stardis-hbound.h"
     21 #include "stardis-hbound-prog.h"
     22 #include "stardis-hfbound.h"
     23 #include "stardis-hfbound-prog.h"
     24 #include "stardis-tbound.h"
     25 #include "stardis-tbound-prog.h"
     26 #include "stardis-fbound.h"
     27 #include "stardis-fbound-prog.h"
     28 #include "stardis-sfconnect.h"
     29 #include "stardis-sfconnect-prog.h"
     30 #include "stardis-ssconnect.h"
     31 #include "stardis-ssconnect-prog.h"
     32 #include "stardis-fluid-prog.h"
     33 #include "stardis-fluid.h"
     34 #include "stardis-solid-prog.h"
     35 #include "stardis-solid.h"
     36 #include "stardis-program.h"
     37 #include "stardis-default.h"
     38 #include "stardis-green-types.h"
     39 
     40 #include <rsys/rsys.h>
     41 #include <rsys/cstr.h>
     42 #include <rsys/double2.h>
     43 #include <rsys/double3.h>
     44 #include <rsys/logger.h>
     45 #include <rsys/text_reader.h>
     46 #include <rsys/library.h>
     47 
     48 #include <star/sg3d.h>
     49 #include <wordexp.h>
     50 #include <stdlib.h>
     51 #include <stdio.h>
     52 #include <string.h>
     53 #ifdef COMPILER_GCC
     54 #include <strings.h> /* strcasecmp */
     55 #else
     56 #define strcasecmp(s1, s2) _stricmp((s1), (s2))
     57 #endif
     58 
     59 /*******************************************************************************
     60  * Local Functions
     61  ******************************************************************************/
     62 static void
     63 add_geom_ctx_properties
     64   (const unsigned itri,
     65    unsigned prop[3],
     66    void* context)
     67 {
     68   const struct add_geom_ctx* ctx = context;
     69   int i;
     70   ASSERT(prop && ctx); (void)itri;
     71   ASSERT(itri < ctx->stl_desc.triangles_count);
     72   /* Same media for the whole add_geometry set of triangles */
     73   for(i = 0; i < SG3D_PROP_TYPES_COUNT__; i++) prop[i] = ctx->properties[i];
     74 }
     75 
     76 static res_T
     77 add_geom_keep_degenerated
     78   (const unsigned itri,
     79    void* context,
     80    int* abort)
     81 {
     82   const struct add_geom_ctx* ctx = context;
     83   struct darray_uint* degenerated;
     84   ASSERT(abort && ctx && ctx->custom); (void)abort;
     85   ASSERT(itri < ctx->stl_desc.triangles_count);
     86   degenerated = ctx->custom;
     87   return darray_uint_push_back(degenerated, &itri);
     88 }
     89 
     90 static res_T
     91 read_sides_and_files
     92   (struct stardis* stardis,
     93    const int descr_is_intface, /* if 1, don't read side */
     94    const unsigned description_id,
     95    wordexp_t* pwordexp,
     96    size_t* idx)
     97 {
     98   char* arg = NULL;
     99   int file_count = 0;
    100   struct sstl* sstl = NULL;
    101   struct add_geom_ctx add_geom_ctx;
    102   unsigned current_merge_errors;
    103   struct sg3d_geometry_add_callbacks callbacks = SG3D_ADD_CALLBACKS_NULL__;
    104   struct darray_uint degenerated;
    105   struct str str, name;
    106   FILE* f = NULL;
    107   res_T res = RES_OK;
    108 
    109   ASSERT(stardis && pwordexp && idx);
    110 
    111   darray_uint_init(stardis->allocator, &degenerated);
    112   str_init(stardis->allocator, &name);
    113   str_init(stardis->allocator, &str);
    114   callbacks.get_indices = add_geom_ctx_indices;
    115   callbacks.get_properties = add_geom_ctx_properties;
    116   callbacks.get_position = add_geom_ctx_position;
    117   callbacks.degenerated_triangle = add_geom_keep_degenerated;
    118   add_geom_ctx.custom = &degenerated;
    119 
    120   ERR(sg3d_geometry_get_unique_triangles_with_merge_conflict_count(
    121     stardis->geometry.sg3d, &current_merge_errors));
    122 
    123   /* At least one side+name, no side without name */
    124   ERR(sstl_create(stardis->logger, stardis->allocator, 1, &sstl));
    125   for(;;) {
    126     unsigned merge_errors;
    127     if(descr_is_intface) {
    128       add_geom_ctx.properties[SG3D_FRONT] = SG3D_UNSPECIFIED_PROPERTY;
    129       add_geom_ctx.properties[SG3D_BACK] = SG3D_UNSPECIFIED_PROPERTY;
    130       add_geom_ctx.properties[SG3D_INTFACE] = description_id;
    131     } else {
    132       if(pwordexp->we_wordc <= *idx
    133           || 0 == strcasecmp((arg = pwordexp->we_wordv[(*idx)++]), "PROG_PARAMS"))
    134       {
    135         if(file_count == 0) {
    136           /* At least 1 side */
    137           logger_print(stardis->logger, LOG_ERROR,
    138             "Invalid data (missing token 'side')\n");
    139           res = RES_BAD_ARG;
    140           goto error;
    141         }
    142         else break;
    143       }
    144       add_geom_ctx.properties[SG3D_INTFACE] = SG3D_UNSPECIFIED_PROPERTY;
    145       if(0 == strcasecmp(arg, "FRONT")) {
    146         add_geom_ctx.properties[SG3D_FRONT] = description_id;
    147         add_geom_ctx.properties[SG3D_BACK] = SG3D_UNSPECIFIED_PROPERTY;
    148       }
    149       else if(0 == strcasecmp(arg, "BACK")) {
    150         add_geom_ctx.properties[SG3D_FRONT] = SG3D_UNSPECIFIED_PROPERTY;
    151         add_geom_ctx.properties[SG3D_BACK] = description_id;
    152       }
    153       else if(0 == strcasecmp(arg, "BOTH")) {
    154         add_geom_ctx.properties[SG3D_FRONT] = description_id;
    155         add_geom_ctx.properties[SG3D_BACK] = description_id;
    156       }
    157       else {
    158         logger_print(stardis->logger, LOG_ERROR,
    159           "Invalid side specifier: %s\n", arg);
    160         res = RES_BAD_ARG;
    161         goto error;
    162       }
    163     }
    164     if(pwordexp->we_wordc <= *idx
    165         || 0 == strcasecmp((arg = pwordexp->we_wordv[(*idx)++]), "PROG_PARAMS"))
    166     {
    167       if(!descr_is_intface /* Has read a side specifier */
    168         || !file_count) /* Need at least 1 file name */
    169       {
    170         logger_print(stardis->logger, LOG_ERROR,
    171           "Invalid data (missing token 'file name')\n");
    172         res = RES_BAD_ARG;
    173         goto error;
    174       }
    175       else break;
    176     }
    177     file_count++;
    178     res = sstl_load(sstl, arg);
    179     if(res == RES_OK) {
    180       ERR(sstl_get_desc(sstl, &add_geom_ctx.stl_desc));
    181       ASSERT(add_geom_ctx.stl_desc.vertices_count <= UINT_MAX
    182         && add_geom_ctx.stl_desc.triangles_count <= UINT_MAX);
    183       logger_print(stardis->logger, LOG_OUTPUT,
    184         "Read file '%s': %u triangles found.\n",
    185         arg, (unsigned)add_geom_ctx.stl_desc.triangles_count);
    186     } else {
    187       logger_print(stardis->logger, LOG_ERROR,
    188         "Cannot read STL file: '%s'\n", arg);
    189       goto error;
    190     }
    191 
    192     res = sg3d_geometry_add(
    193       stardis->geometry.sg3d,
    194       (unsigned)add_geom_ctx.stl_desc.vertices_count,
    195       (unsigned)add_geom_ctx.stl_desc.triangles_count,
    196       &callbacks,
    197       &add_geom_ctx);
    198     if(darray_uint_size_get(&degenerated)) {
    199       size_t c, n;
    200       const unsigned* ids = darray_uint_cdata_get(&degenerated);
    201       c = darray_uint_size_get(&degenerated);
    202       ASSERT(c <= ULONG_MAX);
    203       logger_print(stardis->logger, LOG_WARNING,
    204         "File '%s' included %lu degenerated triangles (removed)\n",
    205         arg, (unsigned long)c);
    206       ERR(str_printf(&str, "Degenerated triangles IDs: %u", ids[0]));
    207       FOR_EACH(n, 1, c) { ERR(str_append_printf(&str, ", %u", ids[n])); }
    208       logger_print(stardis->logger, LOG_OUTPUT, "%s\n", str_cget(&str));
    209       darray_uint_clear(&degenerated);
    210     }
    211 
    212     if(res != RES_OK) {
    213       logger_print(stardis->logger, LOG_ERROR,
    214         "Cannot add file content: '%s'\n", arg);
    215       goto error;
    216     }
    217     /* Check conflicts */
    218     ERR(sg3d_geometry_get_unique_triangles_with_merge_conflict_count(
    219       stardis->geometry.sg3d, &merge_errors));
    220     if(current_merge_errors != merge_errors) {
    221       int is_for_compute =
    222         (stardis->mode & COMPUTE_MODES) && !(stardis->mode & MODE_DUMP_MODEL);
    223       if(!str_is_empty(&stardis->dump_model_filename)) {
    224         ERR(str_copy(&name, &stardis->dump_model_filename));
    225         ERR(str_append(&name, "_merge_conflits.obj"));
    226         f = fopen(str_cget(&name), "w");
    227         if(!f) {
    228           logger_print(stardis->logger, LOG_ERROR,
    229             "cannot open file '%s' for writing.\n", str_cget(&name));
    230           res = RES_IO_ERR;
    231           goto error;
    232         }
    233         ERR(sg3d_geometry_dump_as_obj(stardis->geometry.sg3d, f,
    234               SG3D_OBJ_DUMP_MERGE_CONFLICTS));
    235         fclose(f); f = NULL;
    236       }
    237       logger_print(stardis->logger, (is_for_compute ? LOG_ERROR : LOG_WARNING),
    238         "Merge conflicts found reading file '%s' (%u triangles).\n",
    239         arg, merge_errors - current_merge_errors);
    240       if(is_for_compute) {
    241         res = RES_BAD_ARG;
    242         goto error;
    243       }
    244     }
    245     current_merge_errors = merge_errors;
    246   }
    247 
    248 end:
    249   if(f) fclose(f);
    250   str_release(&name);
    251   darray_uint_release(&degenerated);
    252   str_release(&str);
    253   if(sstl) SSTL(ref_put(sstl));
    254   return res;
    255 error:
    256   goto end;
    257 }
    258 
    259 /*******************************************************************************
    260  * Public Functions
    261  ******************************************************************************/
    262 void
    263 add_geom_ctx_position
    264   (const unsigned ivert,
    265    double pos[3],
    266    void* context)
    267 {
    268   const struct add_geom_ctx* ctx = context;
    269   const float* v;
    270   ASSERT(pos && ctx);
    271   ASSERT(ivert < ctx->stl_desc.vertices_count);
    272   v = ctx->stl_desc.vertices + 3 * ivert;
    273   d3_set_f3(pos, v);
    274 }
    275 
    276 void
    277 add_geom_ctx_indices
    278   (const unsigned itri,
    279    unsigned ids[3],
    280    void* context)
    281 {
    282   const struct add_geom_ctx* ctx = context;
    283   const unsigned* trg;
    284   int i;
    285   ASSERT(ids && ctx);
    286   ASSERT(itri < ctx->stl_desc.triangles_count);
    287   trg = ctx->stl_desc.indices + 3 * itri;
    288   for(i = 0; i < 3; i++) ids[i] = trg[i];
    289 }
    290 
    291 static res_T
    292 description_set_name
    293   (struct stardis* stardis,
    294    struct str* name,
    295    const char* arg)
    296 {
    297   res_T res = RES_OK;
    298   double foo;
    299   const char* keywords[] = {
    300     "AUTO", "BACK", "BOTH", "FLUID", "FLUID_PROG", "FRONT", "F_BOUNDARY_FOR_SOLID",
    301     "F_BOUNDARY_FOR_SOLID_PROG", "H_BOUNDARY_FOR_FLUID", "H_BOUNDARY_FOR_FLUID_PROG",
    302     "HF_BOUNDARY_FOR_SOLID", "HF_BOUNDARY_FOR_SOLID_PROG",
    303     "H_BOUNDARY_FOR_SOLID", "H_BOUNDARY_FOR_SOLID_PROG", "PROGRAM", "PROG_PARAMS",
    304     "SCALE", "SOLID", "SOLID_PROG", "SOLID_FLUID_CONNECTION",
    305     "SOLID_FLUID_CONNECTION_PROG", "SOLID_SOLID_CONNECTION",
    306     "SOLID_SOLID_CONNECTION_PROG", "SPHERICAL_SOURCE", "SPHERICAL_SOURCE_PROG",
    307     "TRAD", "T_BOUNDARY_FOR_SOLID", "T_BOUNDARY_FOR_SOLID_PROG", "UNKNOWN" };
    308   const char* reason = NULL;
    309   size_t i;
    310   ASSERT(name && arg);
    311 
    312   /* Use name before uppercasing it */
    313   ERR(str_set(name, arg));
    314 
    315   if(RES_OK == cstr_to_double(arg, &foo)) {
    316     /* A number is not a sensible choice for a name! */
    317     res = RES_BAD_ARG;
    318     reason = "number";
    319     goto error;
    320   }
    321   FOR_EACH(i, 0, sizeof(keywords) / sizeof(*keywords)) {
    322     if(0 == strcasecmp(arg, keywords[i])) {
    323       /* A keyword is not a sensible choice for a name! */
    324       res = RES_BAD_ARG;
    325       reason = "reserved keyword";
    326       goto error;
    327     }
    328   }
    329   if(str_len(name) > DESC_NAME_MAX_LEN) {
    330     /* Due to Green export limitations, names are limited in length */
    331     res = RES_BAD_ARG;
    332     reason = "too long";
    333     goto error;
    334   }
    335   /* Name is OK */
    336 
    337 end:
    338   return res;
    339 error:
    340   ASSERT(reason != NULL);
    341   logger_print(stardis->logger, LOG_ERROR, "Invalid name (%s): %s\n",
    342     reason, arg);
    343   goto end;
    344 }
    345 
    346 static struct description*
    347 find_description_by_name
    348   (struct stardis* stardis,
    349    const struct str* name,
    350    const struct description* self)
    351 {
    352   size_t i;
    353   ASSERT(stardis && name);
    354 
    355   FOR_EACH(i, 0, darray_descriptions_size_get(&stardis->descriptions)) {
    356     struct description* desc
    357       = darray_descriptions_data_get(&stardis->descriptions) + i;
    358     if(self == desc) continue;
    359     if(str_eq(name, get_description_name(desc))) {
    360       return desc;
    361     }
    362   }
    363   return NULL;
    364 }
    365 
    366 /* H_BOUNDARY_FOR_SOLID Name ref_temperature emissivity specular_fraction hc T_env STL_filenames
    367  * H_BOUNDARY_FOR_FLUID Name ref_temperature emissivity specular_fraction hc T_env STL_filenames */
    368 static res_T
    369 process_h
    370   (struct stardis* stardis,
    371    const enum description_type type,
    372    wordexp_t* pwordexp)
    373 {
    374   char* arg = NULL;
    375   struct description* desc;
    376   size_t sz;
    377   struct h_boundary* h_boundary;
    378   size_t idx = 1;
    379   res_T res = RES_OK;
    380 
    381   ASSERT(stardis && pwordexp);
    382 
    383   stardis->counts.hbound_count++;
    384 
    385   sz = darray_descriptions_size_get(&stardis->descriptions);
    386   ERR(darray_descriptions_resize(&stardis->descriptions, sz+1));
    387   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
    388   ERR(init_h_boundary(stardis->allocator, &desc->d.h_boundary));
    389   h_boundary = desc->d.h_boundary;
    390   desc->type = type;
    391 
    392   CHK_ARG(idx, "h boundary name");
    393   ERR(description_set_name(stardis, &h_boundary->name, arg));
    394   if(find_description_by_name(stardis, &h_boundary->name, desc)) {
    395     logger_print(stardis->logger, LOG_ERROR,
    396       "Name already used: %s\n", arg);
    397     if(res == RES_OK) res = RES_BAD_ARG;
    398     goto error;
    399   }
    400 
    401   CHK_ARG(idx, "ref_temperature");
    402   res = cstr_to_double(arg, &h_boundary->ref_temperature);
    403   if(res != RES_OK || h_boundary->ref_temperature < 0) {
    404     logger_print(stardis->logger, LOG_ERROR,
    405         "Invalid reference temperature: %s\n", arg);
    406     if(res == RES_OK) res = RES_BAD_ARG;
    407     goto error;
    408   }
    409   stardis->t_range[0] = MMIN(stardis->t_range[0], h_boundary->ref_temperature);
    410   stardis->t_range[1] = MMAX(stardis->t_range[1], h_boundary->ref_temperature);
    411   CHK_ARG(idx, "emissivity");
    412   res = cstr_to_double(arg, &h_boundary->emissivity);
    413   if(res != RES_OK
    414   || h_boundary->emissivity < 0
    415   || h_boundary->emissivity > 1) {
    416     logger_print(stardis->logger, LOG_ERROR, "Invalid emissivity: %s\n", arg);
    417     if(res == RES_OK) res = RES_BAD_ARG;
    418     goto error;
    419   }
    420   CHK_ARG(idx, "specular fraction");
    421   res = cstr_to_double(arg, &h_boundary->specular_fraction);
    422   if(res != RES_OK
    423   || h_boundary->specular_fraction < 0
    424   || h_boundary->specular_fraction > 1) {
    425     logger_print(stardis->logger, LOG_ERROR,
    426       "Invalid specular fraction: %s\n", arg);
    427     if(res == RES_OK) res = RES_BAD_ARG;
    428     goto error;
    429   }
    430   CHK_ARG(idx, "Convection coefficient");
    431   res = cstr_to_double(arg, &h_boundary->hc);
    432   if(res != RES_OK || h_boundary->hc < 0) {
    433     logger_print(stardis->logger, LOG_ERROR,
    434       "Invalid Convection coefficient: %s\n", arg);
    435     if(res == RES_OK) res = RES_BAD_ARG;
    436     goto error;
    437   }
    438   CHK_ARG(idx, "temperature");
    439   res = cstr_to_double(arg, &h_boundary->imposed_temperature);
    440   if(res != RES_OK
    441   || SDIS_TEMPERATURE_IS_UNKNOWN(h_boundary->imposed_temperature)) {
    442     logger_print(stardis->logger, LOG_ERROR, "Invalid temperature: %s\n", arg);
    443     if(res == RES_OK) res = RES_BAD_ARG;
    444     goto error;
    445   }
    446 
    447   if(type == DESC_BOUND_H_FOR_FLUID)
    448     ERR(get_dummy_solid_id(stardis, &h_boundary->mat_id));
    449   else {
    450     struct fluid* fluid = NULL;
    451     ASSERT(type == DESC_BOUND_H_FOR_SOLID);
    452     ERR(init_fluid(stardis->allocator, &fluid));
    453     fluid->fluid_id = allocate_stardis_medium_id(stardis);
    454     h_boundary->mat_id = fluid->fluid_id;
    455     h_boundary->possible_external_fluid = fluid;
    456     ASSERT(sz <= UINT_MAX);
    457     fluid->desc_id = (unsigned)sz;
    458     fluid->imposed_temperature = h_boundary->imposed_temperature;
    459     fluid->t0 = stardis->initial_time;
    460     fluid->is_outside = 1;
    461     fluid->is_green = stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII);
    462     ERR(create_solver_fluid(stardis, fluid));
    463     logger_print(stardis->logger, LOG_OUTPUT,
    464       "External fluid created: T=%g (it is medium %u)\n",
    465       fluid->imposed_temperature,
    466       fluid->fluid_id);
    467   }
    468 
    469   ASSERT(sz <= UINT_MAX);
    470   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
    471 
    472 end:
    473   return res;
    474 error:
    475   goto end;
    476 }
    477 
    478 /* HF_BOUNDARY_FOR_SOLID Name ref_temperature emissivity specular_fraction hc
    479  * T_env flux STL_filenames */
    480 static res_T
    481 process_hf
    482   (struct stardis* stardis,
    483    const enum description_type type,
    484    wordexp_t* pwordexp)
    485 {
    486   char* arg = NULL;
    487   struct description* desc;
    488   size_t sz;
    489   struct hf_boundary* hf_boundary;
    490   size_t idx = 1;
    491   struct fluid* fluid = NULL;
    492   res_T res = RES_OK;
    493 
    494   ASSERT(stardis && pwordexp);
    495 
    496   stardis->counts.hbound_count++;
    497 
    498   sz = darray_descriptions_size_get(&stardis->descriptions);
    499   ERR(darray_descriptions_resize(&stardis->descriptions, sz+1));
    500   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
    501   ERR(init_hf_boundary(stardis->allocator, &desc->d.hf_boundary));
    502   hf_boundary = desc->d.hf_boundary;
    503   desc->type = type;
    504 
    505   CHK_ARG(idx, "hf boundary name");
    506   ERR(description_set_name(stardis, &hf_boundary->name, arg));
    507   if(find_description_by_name(stardis, &hf_boundary->name, desc)) {
    508     logger_print(stardis->logger, LOG_ERROR,
    509       "Name already used: %s\n", arg);
    510     if(res == RES_OK) res = RES_BAD_ARG;
    511     goto error;
    512   }
    513 
    514   CHK_ARG(idx, "ref_temperature");
    515   res = cstr_to_double(arg, &hf_boundary->ref_temperature);
    516   if(res != RES_OK || hf_boundary->ref_temperature < 0) {
    517     logger_print(stardis->logger, LOG_ERROR,
    518         "Invalid reference temperature: %s\n", arg);
    519     if(res == RES_OK) res = RES_BAD_ARG;
    520     goto error;
    521   }
    522   stardis->t_range[0] = MMIN(stardis->t_range[0], hf_boundary->ref_temperature);
    523   stardis->t_range[1] = MMAX(stardis->t_range[1], hf_boundary->ref_temperature);
    524   CHK_ARG(idx, "emissivity");
    525   res = cstr_to_double(arg, &hf_boundary->emissivity);
    526   if(res != RES_OK
    527   || hf_boundary->emissivity < 0
    528   || hf_boundary->emissivity > 1) {
    529     logger_print(stardis->logger, LOG_ERROR, "Invalid emissivity: %s\n", arg);
    530     if(res == RES_OK) res = RES_BAD_ARG;
    531     goto error;
    532   }
    533   CHK_ARG(idx, "specular fraction");
    534   res = cstr_to_double(arg, &hf_boundary->specular_fraction);
    535   if(res != RES_OK
    536   || hf_boundary->specular_fraction < 0
    537   || hf_boundary->specular_fraction > 1) {
    538     logger_print(stardis->logger, LOG_ERROR,
    539       "Invalid specular fraction: %s\n", arg);
    540     if(res == RES_OK) res = RES_BAD_ARG;
    541     goto error;
    542   }
    543   CHK_ARG(idx, "convection coefficient");
    544   res = cstr_to_double(arg, &hf_boundary->hc);
    545   if(res != RES_OK || hf_boundary->hc < 0) {
    546     logger_print(stardis->logger, LOG_ERROR,
    547       "Invalid convection coefficient: %s\n", arg);
    548     if(res == RES_OK) res = RES_BAD_ARG;
    549     goto error;
    550   }
    551   CHK_ARG(idx, "temperature");
    552   res = cstr_to_double(arg, &hf_boundary->imposed_temperature);
    553   if(res != RES_OK
    554   || SDIS_TEMPERATURE_IS_UNKNOWN(hf_boundary->imposed_temperature)) {
    555     logger_print(stardis->logger, LOG_ERROR, "Invalid temperature: %s\n", arg);
    556     if(res == RES_OK) res = RES_BAD_ARG;
    557     goto error;
    558   }
    559   CHK_ARG(idx, "flux");
    560   res = cstr_to_double(arg, &hf_boundary->imposed_flux);
    561   if(res != RES_OK
    562     || hf_boundary->imposed_flux == SDIS_FLUX_NONE) {
    563     /* Flux can be < 0 but not undefined */
    564     logger_print(stardis->logger, LOG_ERROR,
    565       "Invalid flux: %s\n", arg);
    566     if(res == RES_OK) res = RES_BAD_ARG;
    567     goto error;
    568   }
    569 
    570   ASSERT(type == DESC_BOUND_HF_FOR_SOLID);
    571   ERR(init_fluid(stardis->allocator, &fluid));
    572   fluid->fluid_id = allocate_stardis_medium_id(stardis);
    573   hf_boundary->mat_id = fluid->fluid_id;
    574   hf_boundary->possible_external_fluid = fluid;
    575   ASSERT(sz <= UINT_MAX);
    576   fluid->desc_id = (unsigned)sz;
    577   fluid->imposed_temperature = hf_boundary->imposed_temperature;
    578   fluid->t0 = stardis->initial_time;
    579   fluid->is_outside = 1;
    580   fluid->is_green = stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII);
    581   ERR(create_solver_fluid(stardis, fluid));
    582   logger_print(stardis->logger, LOG_OUTPUT,
    583     "External fluid created: T=%g (it is medium %u)\n",
    584     fluid->imposed_temperature,
    585     fluid->fluid_id);
    586 
    587   ASSERT(sz <= UINT_MAX);
    588   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
    589 
    590 end:
    591   return res;
    592 error:
    593   goto end;
    594 }
    595 
    596 static res_T
    597 set_argc_argv
    598   (struct mem_allocator* allocator,
    599    const char* prog_name, /* First argument to copy in argv[0] */
    600    size_t* out_argc,
    601    char** out_argv[],
    602    const wordexp_t* pwordexp,
    603    size_t idx)
    604 {
    605   char** argv = NULL;
    606   size_t argc;
    607   size_t i, n;
    608   res_T res = RES_OK;
    609 
    610   ASSERT(prog_name && out_argc && out_argv && pwordexp);
    611 
    612   if(pwordexp->we_wordc < idx) {
    613     res = RES_BAD_ARG;
    614     goto error;
    615   }
    616 
    617   /* Allocate an additional argument to store the program name as the first
    618    * argument. This is not only useful information, but also respects the C
    619    * convention for declaring a list of arguments. So anyone can use the
    620    * standard getopt function to parse input arguments*/
    621   argc = pwordexp->we_wordc - idx + 1/*program name*/;
    622   argv = MEM_CALLOC(allocator, argc, sizeof(char*));
    623   if(argv == NULL) { res = RES_MEM_ERR; goto error; }
    624 
    625   #define STRDUP(Dst, Src) { \
    626     (Dst) = MEM_CALLOC(allocator, 1, 1 + strlen(Src)); \
    627     if((Dst) == NULL) { res = RES_MEM_ERR; goto error; } \
    628     strcpy((Dst), (Src)); /* size is adequate */ \
    629   } (void)0
    630   STRDUP(argv[0], prog_name);
    631   for(i = idx, n = 1; i < pwordexp->we_wordc; i++, n++) {
    632     STRDUP(argv[n], pwordexp->we_wordv[i]);
    633   }
    634   #undef STRDUP
    635 
    636 end:
    637   *out_argc = argc;
    638   *out_argv = argv;
    639   return res;
    640 error:
    641   if(argv) {
    642     FOR_EACH(i, 0, argc) if(argv[i]) MEM_RM(allocator, argv[i]);
    643     MEM_RM(allocator, argv);
    644   }
    645   argc = 0;
    646   argv = NULL;
    647   goto end;
    648 }
    649 
    650 /* utility macros */
    651 #define GET_LIB_SYMBOL_BASE(DestField, LibHandle, FunName, Optional) \
    652   *(void**)DestField = library_get_symbol((LibHandle), #FunName ); \
    653   if(!*DestField && !(Optional)) { \
    654     logger_print(stardis->logger, LOG_ERROR, \
    655       "Cannot find function '" #FunName "()' in lib %s\n", lib_name); \
    656     res = RES_BAD_ARG; \
    657     goto error; \
    658   }
    659 
    660 #define GET_LIB_SYMBOL(Dest, Field, FunName) \
    661   GET_LIB_SYMBOL_BASE(&((Dest)->Field), (Dest)->program->lib_handle, FunName, 0)
    662 
    663 #define CREATE_DESC_DATA_BASE(Desc, CreateArgs) \
    664   (Desc)->prog_data = (Desc)->create(&ctx, CreateArgs); \
    665   if(!(Desc)->prog_data) { \
    666     logger_print(stardis->logger, LOG_ERROR, \
    667       "Cannot create data for description %s\n", str_cget(&(Desc)->name)); \
    668     res = RES_BAD_ARG; \
    669     goto error; \
    670   }
    671 
    672 #define CREATE_DESC_DATA(Desc) \
    673   CREATE_DESC_DATA_BASE(Desc, \
    674       LIST_ARG3((Desc)->program->prog_data, (Desc)->argc, (Desc)->argv))
    675 
    676 /* The returned program is NULL if no stardis_create_program function is
    677  * defined in the library. */
    678 static res_T
    679 get_prog_common
    680   (const char* lib_name,
    681    struct stardis* stardis,
    682    struct program** program,
    683    void* (**create)
    684      (const struct stardis_description_create_context*, void*, size_t, char**),
    685    void (**release)(void*))
    686 {
    687   res_T res = RES_OK;
    688   struct description* desc;
    689   struct str tmp;
    690 
    691   ASSERT(lib_name && program && create && release && stardis);
    692 
    693   /* get the library handler */
    694   str_init(stardis->allocator, &tmp);
    695   ERR(str_set(&tmp, lib_name));
    696   desc = find_description_by_name(stardis, &tmp, NULL);
    697   if(!desc) {
    698     logger_print(stardis->logger, LOG_ERROR,
    699       "Undefined PROGRAM: %s\n", lib_name);
    700     if(res == RES_OK) res = RES_BAD_ARG;
    701     goto error;
    702   }
    703   else if(desc->type != DESC_PROGRAM) {
    704     logger_print(stardis->logger, LOG_ERROR,
    705       "Is not a PROGRAM: %s\n", lib_name);
    706     if(res == RES_OK) res = RES_BAD_ARG;
    707     goto error;
    708   }
    709   *program = desc->d.program;
    710   /* get the mandatory user-defined functions from the library */
    711   GET_LIB_SYMBOL_BASE(create, (*program)->lib_handle, stardis_create_data, 0);
    712   GET_LIB_SYMBOL_BASE(release, (*program)->lib_handle, stardis_release_data, 0);
    713 
    714 end:
    715   str_release(&tmp);
    716   return res;
    717 error:
    718   goto end;
    719 }
    720 
    721 /* PROGRAM Name library_path [...] */
    722 static res_T
    723 process_program
    724   (struct stardis* stardis,
    725    wordexp_t* pwordexp)
    726 {
    727   char* arg = NULL;
    728   struct description* desc;
    729   size_t sz;
    730   struct program* program;
    731   const char* lib_name;
    732   const char* lic;
    733   const char* _c_;
    734   size_t idx = 1;
    735   res_T res = RES_OK;
    736 
    737   ASSERT(stardis && pwordexp);
    738 
    739   sz = darray_descriptions_size_get(&stardis->descriptions);
    740   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
    741   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
    742   ERR(init_program(stardis->allocator, &desc->d.program));
    743   program = desc->d.program;
    744   desc->type = DESC_PROGRAM;
    745 
    746   CHK_ARG(idx, "program name");
    747   ERR(description_set_name(stardis, &program->name, arg));
    748   if(find_description_by_name(stardis, &program->name, desc)) {
    749     logger_print(stardis->logger, LOG_ERROR,
    750       "Name already used: %s\n", arg);
    751     if(res == RES_OK) res = RES_BAD_ARG;
    752     goto error;
    753   }
    754   lib_name = arg;
    755 
    756   CHK_ARG(idx, "library path");
    757   ERR(str_set(&program->lib_path, arg));
    758 
    759   /* get the library handler */
    760   program->lib_handle = library_open(arg);
    761   if(!program->lib_handle) {
    762     logger_print(stardis->logger, LOG_ERROR,
    763       "Cannot open library: %s (%s)\n", lib_name, arg);
    764     res = RES_BAD_ARG;
    765     goto error;
    766   }
    767 
    768   /* get the mandatory user-defined functions from the library */
    769   GET_LIB_SYMBOL_BASE(&program->get_copyright_notice, program->lib_handle,
    770     get_copyright_notice, 0);
    771   GET_LIB_SYMBOL_BASE(&program->get_license_short, program->lib_handle,
    772     get_license_short, 0);
    773   GET_LIB_SYMBOL_BASE(&program->get_license_text, program->lib_handle,
    774     get_license_text, 0);
    775   /* get the optional user-defined functions from the library */
    776   GET_LIB_SYMBOL_BASE(&program->create,
    777     program->lib_handle,  stardis_create_library_data, 1);
    778   GET_LIB_SYMBOL_BASE(&program->release,
    779     program->lib_handle,  stardis_release_library_data, 1);
    780   GET_LIB_SYMBOL_BASE(&program->finalize,
    781     program->lib_handle,  stardis_finalize_library_data, 1);
    782   if(!(program->create && program->release && program->finalize)
    783     && !(!program->create && !program->release && !program->finalize))
    784   {
    785     logger_print(stardis->logger, LOG_ERROR,
    786       "Inconsistent library data management for library '%s'.\n",
    787       lib_name);
    788     logger_print(stardis->logger, LOG_ERROR,
    789       "Please define all or none of stardis_create_library_data, "
    790       "stardis_finalize_library_data and stardis_release_library_data funcions.\n");
    791     res = RES_BAD_ARG;
    792     goto error;
    793   }
    794 
    795   /* store the end of line as args for custom init */
    796   ERR(set_argc_argv(stardis->allocator, str_cget(&program->name),
    797     &program->argc, &program->argv, pwordexp, idx));
    798   if(program->create) {
    799     /* create and init custom data */
    800     struct stardis_program_context ctx;
    801     ctx.name = lib_name;
    802     switch(stardis->verbose) {
    803       case 0: ctx.verbosity_level = STARDIS_VERBOSE_NONE; break;
    804       case 1: ctx.verbosity_level = STARDIS_VERBOSE_ERROR; break;
    805       case 2: ctx.verbosity_level = STARDIS_VERBOSE_WARNING; break;
    806       case 3: ctx.verbosity_level = STARDIS_VERBOSE_INFO; break;
    807       default:
    808         FATAL("error:" STR(__FILE__) ":" STR(__LINE__)": Invalid type.\n");
    809     }
    810     CREATE_DESC_DATA_BASE(program, LIST_ARG2(program->argc, program->argv));
    811   } else if(program->argc != 1) {
    812     logger_print(stardis->logger, LOG_ERROR,
    813       "Library '%s' has no custom data management functions but has arguments:\n",
    814       lib_name);
    815     for( ; idx < pwordexp->we_wordc; idx++) {
    816       logger_print(stardis->logger, LOG_ERROR, "%s\n", pwordexp->we_wordv[idx]);
    817     }
    818     res = RES_BAD_ARG;
    819     goto error;
    820   }
    821 
    822   lic = program->get_license_short(program->prog_data);
    823   _c_ = program->get_copyright_notice(program->prog_data);
    824   if(!lic) {
    825     res = RES_BAD_ARG;
    826     goto error;
    827   }
    828   if(!_c_) {
    829     res = RES_BAD_ARG;
    830     goto error;
    831   }
    832   logger_print(stardis->logger, LOG_OUTPUT,
    833     "Loading external library '%s': \"%s\"\n",
    834     str_cget(&program->name), str_cget(&program->lib_path));
    835   logger_print(stardis->logger, LOG_OUTPUT, "  %s\n", _c_);
    836   logger_print(stardis->logger, LOG_OUTPUT, "  %s\n", lic);
    837 
    838 end:
    839   return res;
    840 error:
    841   goto end;
    842 }
    843 
    844 /* H_BOUNDARY_FOR_SOLID_PROG Name ProgName STL_filenames [PROG_PARAMS ...]
    845  * H_BOUNDARY_FOR_FLUID_PROG Name ProgName STL_filenames [PROG_PARAMS ...] */
    846 static res_T
    847 process_h_prog
    848   (struct stardis* stardis,
    849    const enum description_type type,
    850    wordexp_t* pwordexp)
    851 {
    852   char* arg = NULL;
    853   struct description* desc;
    854   const char *lib_name, *desc_name;
    855   double h_bound_t_range[2] = {DBL_MAX, -DBL_MAX};
    856   size_t sz;
    857   struct h_boundary_prog* h_boundary_prog;
    858   struct stardis_description_create_context ctx;
    859   size_t idx = 1;
    860   res_T res = RES_OK;
    861 
    862   ASSERT(stardis && pwordexp);
    863 
    864   stardis->counts.fmed_count++;
    865 
    866   sz = darray_descriptions_size_get(&stardis->descriptions);
    867   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
    868   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
    869   ERR(init_h_boundary_prog(stardis->allocator, &desc->d.h_boundary_prog));
    870   h_boundary_prog = desc->d.h_boundary_prog;
    871   desc->type = type;
    872 
    873   CHK_ARG(idx, "programmed h boundary name");
    874   ERR(description_set_name(stardis, &h_boundary_prog->name, arg));
    875   if(find_description_by_name(stardis, &h_boundary_prog->name, desc)) {
    876     logger_print(stardis->logger, LOG_ERROR,
    877       "Name already used: %s\n", arg);
    878     if(res == RES_OK) res = RES_BAD_ARG;
    879     goto error;
    880   }
    881   desc_name = arg;
    882 
    883   CHK_ARG(idx, "program name");
    884   ERR(str_set(&h_boundary_prog->prog_name, arg));
    885   lib_name = arg;
    886 
    887   ASSERT(sz <= UINT_MAX);
    888   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
    889 
    890   /* store the end of line as args for custom init */
    891   ERR(set_argc_argv(stardis->allocator, str_cget(&h_boundary_prog->name),
    892     &h_boundary_prog->argc, &h_boundary_prog->argv, pwordexp, idx));
    893   /* get the user-defined functions from the library */
    894   ERR(get_prog_common(lib_name, stardis,  &h_boundary_prog->program,
    895     &h_boundary_prog->create, &h_boundary_prog->release));
    896   GET_LIB_SYMBOL(h_boundary_prog, ref_temp, stardis_reference_temperature);
    897   GET_LIB_SYMBOL(h_boundary_prog, emissivity, stardis_emissivity);
    898   GET_LIB_SYMBOL(h_boundary_prog, alpha, stardis_specular_fraction);
    899   GET_LIB_SYMBOL(h_boundary_prog, hc, stardis_convection_coefficient);
    900   GET_LIB_SYMBOL(h_boundary_prog, hmax, stardis_max_convection_coefficient);
    901   GET_LIB_SYMBOL(h_boundary_prog, t_range, stardis_t_range);
    902   if(type == DESC_BOUND_H_FOR_FLUID_PROG) {
    903     GET_LIB_SYMBOL(h_boundary_prog, boundary_temp, stardis_boundary_temperature);
    904   } else {
    905     GET_LIB_SYMBOL(h_boundary_prog, fluid_temp, stardis_medium_temperature);
    906   }
    907   /* create and init custom data */
    908   ctx.name = desc_name;
    909   CREATE_DESC_DATA(h_boundary_prog);
    910 
    911   h_boundary_prog->t_range(h_boundary_prog->prog_data, h_bound_t_range);
    912   if(STARDIS_TEMPERATURE_IS_KNOWN(h_bound_t_range[0]))
    913     stardis->t_range[0] = MMIN(stardis->t_range[0], h_bound_t_range[0]);
    914   if(STARDIS_TEMPERATURE_IS_KNOWN(h_bound_t_range[1]))
    915     stardis->t_range[1] = MMAX(stardis->t_range[1], h_bound_t_range[1]);
    916 
    917   /* create the media behind the interface */
    918   if(type == DESC_BOUND_H_FOR_FLUID_PROG) {
    919     ERR(get_dummy_solid_id(stardis, &h_boundary_prog->mat_id));
    920   } else {
    921     struct fluid_prog* fluid_prog = NULL;
    922     ASSERT(type == DESC_BOUND_H_FOR_SOLID_PROG);
    923     ERR(init_fluid_prog(stardis->allocator, &fluid_prog));
    924     fluid_prog->fluid_id = allocate_stardis_medium_id(stardis);
    925     h_boundary_prog->mat_id = fluid_prog->fluid_id;
    926     h_boundary_prog->possible_external_fluid = fluid_prog;
    927     fluid_prog->desc_id = (unsigned)sz;
    928     fluid_prog->temp = h_boundary_prog->fluid_temp;
    929     fluid_prog->is_outside = 1;
    930     fluid_prog->prog_data = h_boundary_prog->prog_data;
    931     /* fluid_prog->release is NULL to avoid deleting shared prog_data */
    932     ERR(create_solver_external_fluid_prog(stardis, fluid_prog));
    933     logger_print(stardis->logger, LOG_OUTPUT,
    934       "External programmed fluid created (it is medium %u)\n",
    935       fluid_prog->fluid_id);
    936   }
    937 
    938 end:
    939   return res;
    940 error:
    941   goto end;
    942 }
    943 
    944 /* HF_BOUNDARY_FOR_SOLID_PROG Name ProgName STL_filenames [PROG_PARAMS ...] */
    945 static res_T
    946 process_hf_prog
    947   (struct stardis* stardis,
    948    const enum description_type type,
    949    wordexp_t* pwordexp)
    950 {
    951   char* arg = NULL;
    952   struct description* desc;
    953   const char *lib_name, *desc_name;
    954   double hf_bound_t_range[2] = {DBL_MAX, -DBL_MAX};
    955   size_t sz;
    956   struct hf_boundary_prog* hf_boundary_prog;
    957   struct stardis_description_create_context ctx;
    958   struct fluid_prog* fluid_prog = NULL;
    959   size_t idx = 1;
    960   res_T res = RES_OK;
    961 
    962   ASSERT(stardis && pwordexp);
    963   ASSERT(type == DESC_BOUND_HF_FOR_SOLID_PROG); /* No HF prog for fluids */
    964 
    965   stardis->counts.fmed_count++;
    966 
    967   sz = darray_descriptions_size_get(&stardis->descriptions);
    968   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
    969   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
    970   ERR(init_hf_boundary_prog(stardis->allocator, &desc->d.hf_boundary_prog));
    971   hf_boundary_prog = desc->d.hf_boundary_prog;
    972   desc->type = type;
    973 
    974   CHK_ARG(idx, "programmed hf boundary name");
    975   ERR(description_set_name(stardis, &hf_boundary_prog->name, arg));
    976   if(find_description_by_name(stardis, &hf_boundary_prog->name, desc)) {
    977     logger_print(stardis->logger, LOG_ERROR,
    978       "Name already used: %s\n", arg);
    979     if(res == RES_OK) res = RES_BAD_ARG;
    980     goto error;
    981   }
    982   desc_name = arg;
    983 
    984   CHK_ARG(idx, "program name");
    985   ERR(str_set(&hf_boundary_prog->prog_name, arg));
    986   lib_name = arg;
    987 
    988   ASSERT(sz <= UINT_MAX);
    989   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
    990 
    991   /* store the end of line as args for custom init */
    992   ERR(set_argc_argv(stardis->allocator, str_cget(&hf_boundary_prog->name),
    993     &hf_boundary_prog->argc, &hf_boundary_prog->argv, pwordexp, idx));
    994   /* get the user-defined functions from the library */
    995   ERR(get_prog_common(lib_name, stardis,  &hf_boundary_prog->program,
    996     &hf_boundary_prog->create, &hf_boundary_prog->release));
    997   GET_LIB_SYMBOL(hf_boundary_prog, ref_temp, stardis_reference_temperature);
    998   GET_LIB_SYMBOL(hf_boundary_prog, emissivity, stardis_emissivity);
    999   GET_LIB_SYMBOL(hf_boundary_prog, alpha, stardis_specular_fraction);
   1000   GET_LIB_SYMBOL(hf_boundary_prog, hc, stardis_convection_coefficient);
   1001   GET_LIB_SYMBOL(hf_boundary_prog, hmax, stardis_max_convection_coefficient);
   1002   GET_LIB_SYMBOL(hf_boundary_prog, flux, stardis_boundary_flux);
   1003   GET_LIB_SYMBOL(hf_boundary_prog, t_range, stardis_t_range);
   1004   GET_LIB_SYMBOL(hf_boundary_prog, fluid_temp, stardis_medium_temperature);
   1005   /* create and init custom data */
   1006   ctx.name = desc_name;
   1007   CREATE_DESC_DATA(hf_boundary_prog);
   1008 
   1009   hf_boundary_prog->t_range(hf_boundary_prog->prog_data, hf_bound_t_range);
   1010   if(STARDIS_TEMPERATURE_IS_KNOWN(hf_bound_t_range[0]))
   1011     stardis->t_range[0] = MMIN(stardis->t_range[0], hf_bound_t_range[0]);
   1012   if(STARDIS_TEMPERATURE_IS_KNOWN(hf_bound_t_range[1]))
   1013     stardis->t_range[1] = MMAX(stardis->t_range[1], hf_bound_t_range[1]);
   1014 
   1015   /* create the media behind the interface */
   1016   ERR(init_fluid_prog(stardis->allocator, &fluid_prog));
   1017   fluid_prog->fluid_id = allocate_stardis_medium_id(stardis);
   1018   hf_boundary_prog->mat_id = fluid_prog->fluid_id;
   1019   hf_boundary_prog->possible_external_fluid = fluid_prog;
   1020   fluid_prog->desc_id = (unsigned)sz;
   1021   fluid_prog->temp = hf_boundary_prog->fluid_temp;
   1022   fluid_prog->is_outside = 1;
   1023   fluid_prog->prog_data = hf_boundary_prog->prog_data;
   1024   /* fluid_prog->release is NULL to avoid deleting shared prog_data */
   1025   ERR(create_solver_external_fluid_prog(stardis, fluid_prog));
   1026   logger_print(stardis->logger, LOG_OUTPUT,
   1027     "External programmed fluid created (it is medium %u)\n",
   1028     fluid_prog->fluid_id);
   1029 
   1030 end:
   1031   return res;
   1032 error:
   1033   goto end;
   1034 }
   1035 
   1036 /* T_BOUNDARY_FOR_SOLID Name T STL_filenames */
   1037 static res_T
   1038 process_t
   1039   (struct stardis* stardis,
   1040    wordexp_t* pwordexp)
   1041 {
   1042   char* arg = NULL;
   1043   struct description* desc;
   1044   size_t sz;
   1045   struct t_boundary* t_boundary;
   1046   size_t idx = 1;
   1047   res_T res = RES_OK;
   1048 
   1049   ASSERT(stardis && pwordexp);
   1050 
   1051   stardis->counts.tbound_count++;
   1052 
   1053   sz = darray_descriptions_size_get(&stardis->descriptions);
   1054   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1055   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1056   ERR(init_t_boundary(stardis->allocator, &desc->d.t_boundary));
   1057   t_boundary = desc->d.t_boundary;
   1058   desc->type = DESC_BOUND_T_FOR_SOLID;
   1059 
   1060   ERR(get_dummy_fluid_id(stardis, &t_boundary->mat_id));
   1061 
   1062 
   1063   CHK_ARG(idx, "temperature boundary name");
   1064   ERR(description_set_name(stardis, &t_boundary->name, arg));
   1065   if(find_description_by_name(stardis, &t_boundary->name, desc)) {
   1066     logger_print(stardis->logger, LOG_ERROR,
   1067       "Name already used: %s\n", arg);
   1068     if(res == RES_OK) res = RES_BAD_ARG;
   1069     goto error;
   1070   }
   1071 
   1072   CHK_ARG(idx, "temperature");
   1073   res = cstr_to_double(arg, &t_boundary->imposed_temperature);
   1074   if(res != RES_OK
   1075   || SDIS_TEMPERATURE_IS_UNKNOWN(t_boundary->imposed_temperature)) {
   1076     logger_print(stardis->logger, LOG_ERROR, "Invalid temperature: %s\n", arg);
   1077     if(res == RES_OK) res = RES_BAD_ARG;
   1078     goto error;
   1079   }
   1080 
   1081   /* Temporarily use the set temperature as a reference temperature.
   1082    * TODO use a different reference temperature when the file format is updated
   1083    * to allow explicit definition by the user. */
   1084   stardis->t_range[0] = MMIN(stardis->t_range[0], t_boundary->imposed_temperature);
   1085   stardis->t_range[1] = MMAX(stardis->t_range[1], t_boundary->imposed_temperature);
   1086 
   1087   ASSERT(sz <= UINT_MAX);
   1088   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
   1089 
   1090 end:
   1091   return res;
   1092 error:
   1093   goto end;
   1094 }
   1095 
   1096 /* T_BOUNDARY_FOR_SOLID_PROG Name ProgName STL_filenames [PROG_PARAMS ...] */
   1097 static res_T
   1098 process_t_prog
   1099   (struct stardis* stardis,
   1100    wordexp_t* pwordexp)
   1101 {
   1102   char* arg = NULL;
   1103   struct description* desc;
   1104   const char *lib_name, *desc_name;
   1105   double t_bound_t_range[2] = {DBL_MAX, -DBL_MAX};
   1106   size_t sz;
   1107   struct t_boundary_prog* t_boundary_prog;
   1108   struct stardis_description_create_context ctx;
   1109   size_t idx = 1;
   1110   res_T res = RES_OK;
   1111 
   1112   ASSERT(stardis && pwordexp);
   1113 
   1114   stardis->counts.fmed_count++;
   1115 
   1116   sz = darray_descriptions_size_get(&stardis->descriptions);
   1117   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1118   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1119   ERR(init_t_boundary_prog(stardis->allocator, &desc->d.t_boundary_prog));
   1120   t_boundary_prog = desc->d.t_boundary_prog;
   1121   desc->type = DESC_BOUND_T_FOR_SOLID_PROG;
   1122 
   1123   ERR(get_dummy_fluid_id(stardis, &t_boundary_prog->mat_id));
   1124 
   1125   CHK_ARG(idx, "programmed t boundary name");
   1126   ERR(description_set_name(stardis, &t_boundary_prog->name, arg));
   1127   if(find_description_by_name(stardis, &t_boundary_prog->name, desc)) {
   1128     logger_print(stardis->logger, LOG_ERROR,
   1129       "Name already used: %s\n", arg);
   1130     if(res == RES_OK) res = RES_BAD_ARG;
   1131     goto error;
   1132   }
   1133   desc_name = arg;
   1134 
   1135   CHK_ARG(idx, "program name");
   1136   ERR(str_set(&t_boundary_prog->prog_name, arg));
   1137   lib_name = arg;
   1138 
   1139   ASSERT(sz <= UINT_MAX);
   1140   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
   1141 
   1142   /* store the end of line as args for custom init */
   1143   ERR(set_argc_argv(stardis->allocator, str_cget(&t_boundary_prog->name),
   1144     &t_boundary_prog->argc, &t_boundary_prog->argv, pwordexp, idx));
   1145   /* get the user-defined functions from the library */
   1146   ERR(get_prog_common(lib_name, stardis, &t_boundary_prog->program,
   1147     &t_boundary_prog->create, &t_boundary_prog->release));
   1148   GET_LIB_SYMBOL(t_boundary_prog, temperature, stardis_boundary_temperature);
   1149   GET_LIB_SYMBOL(t_boundary_prog, t_range, stardis_t_range);
   1150   /* create and init custom data */
   1151   ctx.name = desc_name;
   1152   CREATE_DESC_DATA(t_boundary_prog);
   1153 
   1154   t_boundary_prog->t_range(t_boundary_prog->prog_data, t_bound_t_range);
   1155   if(STARDIS_TEMPERATURE_IS_KNOWN(t_bound_t_range[0]))
   1156     stardis->t_range[0] = MMIN(stardis->t_range[0], t_bound_t_range[0]);
   1157   if(STARDIS_TEMPERATURE_IS_KNOWN(t_bound_t_range[1]))
   1158     stardis->t_range[1] = MMAX(stardis->t_range[1], t_bound_t_range[1]);
   1159 
   1160 end:
   1161   return res;
   1162 error:
   1163   goto end;
   1164 }
   1165 
   1166 /* F_BOUNDARY_FOR_SOLID Name F STL_filenames */
   1167 static res_T
   1168 process_flx
   1169   (struct stardis* stardis,
   1170    wordexp_t* pwordexp)
   1171 {
   1172   char* arg = NULL;
   1173   struct description* desc;
   1174   size_t sz;
   1175   struct f_boundary* f_boundary;
   1176   size_t idx = 1;
   1177   res_T res = RES_OK;
   1178 
   1179   ASSERT(stardis && pwordexp);
   1180 
   1181   stardis->counts.fbound_count++;
   1182 
   1183   sz = darray_descriptions_size_get(&stardis->descriptions);
   1184   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1185   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1186   ERR(init_f_boundary(stardis->allocator, &desc->d.f_boundary));
   1187   f_boundary = desc->d.f_boundary;
   1188   desc->type = DESC_BOUND_F_FOR_SOLID;
   1189 
   1190   ERR(get_dummy_fluid_id(stardis, &f_boundary->mat_id));
   1191 
   1192   CHK_ARG(idx, "flux boundary name");
   1193   ERR(description_set_name(stardis, &f_boundary->name, arg));
   1194   if(find_description_by_name(stardis, &f_boundary->name, desc)) {
   1195     logger_print(stardis->logger, LOG_ERROR,
   1196       "Name already used: %s\n", arg);
   1197     if(res == RES_OK) res = RES_BAD_ARG;
   1198     goto error;
   1199   }
   1200 
   1201   CHK_ARG(idx, "flux");
   1202   res = cstr_to_double(arg, &f_boundary->imposed_flux);
   1203   if(res != RES_OK
   1204   || f_boundary->imposed_flux == SDIS_FLUX_NONE) {
   1205     /* Flux can be < 0 but not undefined */
   1206     if(res == RES_OK) res = RES_BAD_ARG;
   1207     logger_print(stardis->logger, LOG_ERROR, "Invalid flux: %s\n", arg);
   1208     goto error;
   1209   }
   1210   if(f_boundary->imposed_flux != 0 && stardis->picard_order > 1) {
   1211     logger_print(stardis->logger, LOG_ERROR,
   1212       "Cannot have a flux defined at a boundary (here %f) if Picard order "
   1213       "is not 1 (here order is %u)\n",
   1214       f_boundary->imposed_flux, stardis->picard_order);
   1215     res = RES_BAD_ARG;
   1216     goto error;
   1217   }
   1218 
   1219   ASSERT(sz <= UINT_MAX);
   1220   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
   1221 
   1222 end:
   1223   return res;
   1224 error:
   1225   goto end;
   1226 }
   1227 
   1228 /* F_BOUNDARY_FOR_SOLID_PROG Name ProgName STL_filenames [PROG_PARAMS ...] */
   1229 static res_T
   1230 process_flx_prog
   1231   (struct stardis* stardis,
   1232    wordexp_t* pwordexp)
   1233 {
   1234   char* arg = NULL;
   1235   struct description* desc;
   1236   const char *lib_name, *desc_name;
   1237   size_t sz;
   1238   struct f_boundary_prog* f_boundary_prog;
   1239   struct stardis_description_create_context ctx;
   1240   size_t idx = 1;
   1241   res_T res = RES_OK;
   1242 
   1243   ASSERT(stardis && pwordexp);
   1244 
   1245   stardis->counts.fmed_count++;
   1246 
   1247   sz = darray_descriptions_size_get(&stardis->descriptions);
   1248   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1249   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1250   ERR(init_f_boundary_prog(stardis->allocator, &desc->d.f_boundary_prog));
   1251   f_boundary_prog = desc->d.f_boundary_prog;
   1252   desc->type = DESC_BOUND_F_FOR_SOLID_PROG;
   1253 
   1254   ERR(get_dummy_fluid_id(stardis, &f_boundary_prog->mat_id));
   1255 
   1256   CHK_ARG(idx, "programmed t boundary name");
   1257   ERR(description_set_name(stardis, &f_boundary_prog->name, arg));
   1258   if(find_description_by_name(stardis, &f_boundary_prog->name, desc)) {
   1259     logger_print(stardis->logger, LOG_ERROR,
   1260       "Name already used: %s\n", arg);
   1261     if(res == RES_OK) res = RES_BAD_ARG;
   1262     goto error;
   1263   }
   1264 
   1265   CHK_ARG(idx, "program name");
   1266   ERR(str_set(&f_boundary_prog->prog_name, arg));
   1267   desc_name = arg;
   1268   desc_name = arg;
   1269   lib_name = arg;
   1270 
   1271   ASSERT(sz <= UINT_MAX);
   1272   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
   1273 
   1274   /* store the end of line as args for custom init */
   1275   ERR(set_argc_argv(stardis->allocator, str_cget(&f_boundary_prog->name),
   1276     &f_boundary_prog->argc, &f_boundary_prog->argv, pwordexp, idx));
   1277   /* get the user-defined functions from the library */
   1278   ERR(get_prog_common(lib_name, stardis, &f_boundary_prog->program,
   1279     &f_boundary_prog->create, &f_boundary_prog->release));
   1280   GET_LIB_SYMBOL(f_boundary_prog, flux, stardis_boundary_flux);
   1281   /* create and init custom data */
   1282   ctx.name = desc_name;
   1283   CREATE_DESC_DATA(f_boundary_prog);
   1284 
   1285 end:
   1286   return res;
   1287 error:
   1288   goto end;
   1289 }
   1290 
   1291 /* SOLID_FLUID_CONNECTION Name ref_temperature emissivity specular_fraction hc STL_filenames */
   1292 static res_T
   1293 process_sfc
   1294   (struct stardis* stardis,
   1295    const int with_flux,
   1296    wordexp_t* pwordexp)
   1297 {
   1298   char* arg = NULL;
   1299   struct description* desc;
   1300   size_t sz;
   1301   struct solid_fluid_connect* sf_connect;
   1302   size_t idx = 1;
   1303   res_T res = RES_OK;
   1304 
   1305   ASSERT(stardis && pwordexp);
   1306 
   1307   stardis->counts.sfconnect_count++;
   1308 
   1309   sz = darray_descriptions_size_get(&stardis->descriptions);
   1310   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1311   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1312   ERR(init_sf_connect(stardis->allocator, &desc->d.sf_connect));
   1313   sf_connect = desc->d.sf_connect;
   1314   desc->type = DESC_SOLID_FLUID_CONNECT;
   1315 
   1316   /* Use a medium ID even if there is no medium here
   1317    * As other cases use media IDs as unique IDs for read_sides_and_files calls
   1318    * we continue the trend to ensure connection ID is OK */
   1319   sf_connect->connection_id = allocate_stardis_medium_id(stardis);
   1320 
   1321   CHK_ARG(idx, "solid-fluid connection name");
   1322   ERR(description_set_name(stardis, &sf_connect->name, arg));
   1323   if(find_description_by_name(stardis, &sf_connect->name, desc)) {
   1324     logger_print(stardis->logger, LOG_ERROR,
   1325       "Name already used: %s\n", arg);
   1326     if(res == RES_OK) res = RES_BAD_ARG;
   1327     goto error;
   1328   }
   1329 
   1330   CHK_ARG(idx, "ref_temperature");
   1331   res = cstr_to_double(arg, &sf_connect->ref_temperature);
   1332   if(res != RES_OK || sf_connect->ref_temperature < 0) {
   1333     logger_print(stardis->logger, LOG_ERROR,
   1334         "Invalid reference temperature: %s\n", arg);
   1335     if(res == RES_OK) res = RES_BAD_ARG;
   1336     goto error;
   1337   }
   1338   stardis->t_range[0] = MMIN(stardis->t_range[0], sf_connect->ref_temperature);
   1339   stardis->t_range[1] = MMAX(stardis->t_range[1], sf_connect->ref_temperature);
   1340   CHK_ARG(idx, "emissivity");
   1341   res = cstr_to_double(arg, &sf_connect->emissivity);
   1342   if(res != RES_OK
   1343   || sf_connect->emissivity < 0
   1344   || sf_connect->emissivity > 1) {
   1345     logger_print(stardis->logger, LOG_ERROR, "Invalid emissivity: %s\n", arg);
   1346     if(res == RES_OK) res = RES_BAD_ARG;
   1347     goto error;
   1348   }
   1349   CHK_ARG(idx, "specular fraction");
   1350   res = cstr_to_double(arg, &sf_connect->specular_fraction);
   1351   if(res != RES_OK
   1352   || sf_connect->specular_fraction < 0
   1353   || sf_connect->specular_fraction > 1) {
   1354     logger_print(stardis->logger, LOG_ERROR,
   1355       "Invalid specular fraction: %s\n", arg);
   1356     if(res == RES_OK) res = RES_BAD_ARG;
   1357     goto error;
   1358   }
   1359   CHK_ARG(idx, "convection coefficient");
   1360   res = cstr_to_double(arg, &sf_connect->hc);
   1361   if(res != RES_OK || sf_connect->hc < 0) {
   1362     logger_print(stardis->logger, LOG_ERROR,
   1363       "Invalid convection coefficient: %s\n", arg);
   1364     if(res == RES_OK) res = RES_BAD_ARG;
   1365     goto error;
   1366   }
   1367 
   1368   if(with_flux) {
   1369     CHK_ARG(idx, "flux");
   1370     res = cstr_to_double(arg, &sf_connect->flux);
   1371     if(res != RES_OK) {
   1372       logger_print(stardis->logger, LOG_ERROR,
   1373         "Invalid flux: %s\n", arg);
   1374       goto error;
   1375     }
   1376   }
   1377 
   1378   ASSERT(sz <= UINT_MAX);
   1379   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
   1380 
   1381 end:
   1382   return res;
   1383 error:
   1384   goto end;
   1385 }
   1386 
   1387 /* SOLID_FLUID_CONNECTION_PROG Name ProgName STL_filenames [PROG_PARAMS ...] */
   1388 static res_T
   1389 process_sfc_prog
   1390   (struct stardis* stardis,
   1391    const int with_flux,
   1392    wordexp_t* pwordexp)
   1393 {
   1394   char* arg = NULL;
   1395   struct description* desc;
   1396   const char *lib_name, *desc_name;
   1397   double sf_t_range[2] = {DBL_MAX, -DBL_MAX};
   1398   size_t sz;
   1399   struct solid_fluid_connect_prog* sf_connect_prog;
   1400   struct stardis_description_create_context ctx;
   1401   size_t idx = 1;
   1402   res_T res = RES_OK;
   1403 
   1404   ASSERT(stardis && pwordexp);
   1405 
   1406   stardis->counts.sfconnect_count++;
   1407 
   1408   sz = darray_descriptions_size_get(&stardis->descriptions);
   1409   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1410   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1411   ERR(init_sf_connect_prog(stardis->allocator, &desc->d.sf_connect_prog));
   1412   sf_connect_prog = desc->d.sf_connect_prog;
   1413   desc->type = DESC_SOLID_FLUID_CONNECT_PROG;
   1414 
   1415   CHK_ARG(idx, "programmed solid-fluid connection name");
   1416   ERR(description_set_name(stardis, &sf_connect_prog->name, arg));
   1417   if(find_description_by_name(stardis, &sf_connect_prog->name, desc)) {
   1418     logger_print(stardis->logger, LOG_ERROR,
   1419       "Name already used: %s\n", arg);
   1420     if(res == RES_OK) res = RES_BAD_ARG;
   1421     goto error;
   1422   }
   1423   desc_name = arg;
   1424 
   1425   CHK_ARG(idx, "program name");
   1426   ERR(str_set(&sf_connect_prog->prog_name, arg));
   1427   lib_name = arg;
   1428 
   1429   ASSERT(sz <= UINT_MAX);
   1430   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
   1431 
   1432   /* store the end of line as args for custom init */
   1433   ERR(set_argc_argv(stardis->allocator, str_cget(&sf_connect_prog->name),
   1434     &sf_connect_prog->argc, &sf_connect_prog->argv, pwordexp, idx));
   1435   /* get the user-defined functions from the library */
   1436   ERR(get_prog_common(lib_name, stardis, &sf_connect_prog->program,
   1437     &sf_connect_prog->create, &sf_connect_prog->release));
   1438   GET_LIB_SYMBOL(sf_connect_prog, ref_temp, stardis_reference_temperature);
   1439   GET_LIB_SYMBOL(sf_connect_prog, emissivity, stardis_emissivity);
   1440   GET_LIB_SYMBOL(sf_connect_prog, alpha, stardis_specular_fraction);
   1441   GET_LIB_SYMBOL(sf_connect_prog, hc, stardis_convection_coefficient);
   1442   GET_LIB_SYMBOL(sf_connect_prog, hmax, stardis_max_convection_coefficient);
   1443   GET_LIB_SYMBOL(sf_connect_prog, t_range, stardis_t_range);
   1444   if(with_flux) {
   1445     GET_LIB_SYMBOL(sf_connect_prog, flux, stardis_boundary_flux);
   1446   }
   1447   /* create and init custom data */
   1448   ctx.name = desc_name;
   1449   CREATE_DESC_DATA(sf_connect_prog);
   1450 
   1451   sf_connect_prog->t_range(sf_connect_prog->prog_data, sf_t_range);
   1452   if(STARDIS_TEMPERATURE_IS_KNOWN(sf_t_range[0]))
   1453     stardis->t_range[0] = MMIN(stardis->t_range[0], sf_t_range[0]);
   1454   if(STARDIS_TEMPERATURE_IS_KNOWN(sf_t_range[1]))
   1455     stardis->t_range[1] = MMAX(stardis->t_range[1], sf_t_range[1]);
   1456 
   1457 end:
   1458   return res;
   1459 error:
   1460   goto end;
   1461 }
   1462 
   1463 /* SOLID_SOLID_CONNECTION Name contact-resitance STL_filenames */
   1464 static res_T
   1465 process_ssc
   1466   (struct stardis* stardis,
   1467    wordexp_t* pwordexp)
   1468 {
   1469   char* arg = NULL;
   1470   struct description* desc;
   1471   size_t sz;
   1472   struct solid_solid_connect* ss_connect;
   1473   size_t idx = 1;
   1474   res_T res = RES_OK;
   1475 
   1476   ASSERT(stardis && pwordexp);
   1477 
   1478   stardis->counts.ssconnect_count++;
   1479 
   1480   sz = darray_descriptions_size_get(&stardis->descriptions);
   1481   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1482   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1483   ERR(init_ss_connect(stardis->allocator, &desc->d.ss_connect));
   1484   ss_connect = desc->d.ss_connect;
   1485   desc->type = DESC_SOLID_SOLID_CONNECT;
   1486 
   1487   /* Use a medium ID even if there is no medium here
   1488    * As other cases use media IDs as unique IDs for read_sides_and_files calls
   1489    * we continue the trend to ensure connection ID is OK */
   1490   ss_connect->connection_id = allocate_stardis_medium_id(stardis);
   1491 
   1492   CHK_ARG(idx, "solid-solid connection name");
   1493   ERR(description_set_name(stardis, &ss_connect->name, arg));
   1494   if(find_description_by_name(stardis, &ss_connect->name, desc)) {
   1495     logger_print(stardis->logger, LOG_ERROR,
   1496       "Name already used: %s\n", arg);
   1497     if(res == RES_OK) res = RES_BAD_ARG;
   1498     goto error;
   1499   }
   1500 
   1501   CHK_ARG(idx, "contact resistance");
   1502   res = cstr_to_double(arg, &ss_connect->tcr);
   1503   if(res != RES_OK || ss_connect->tcr < 0) {
   1504     logger_print(stardis->logger, LOG_ERROR,
   1505       "Invalid contact resistance: %s\n", arg);
   1506     if(res == RES_OK) res = RES_BAD_ARG;
   1507     goto error;
   1508   }
   1509   else if(ss_connect->tcr == 0) {
   1510     logger_print(stardis->logger, LOG_WARNING,
   1511       "Solid-solid connection %s: defining a contact resistance to 0 has "
   1512       "no effect\n", str_cget(&ss_connect->name));
   1513   }
   1514 
   1515   ASSERT(sz <= UINT_MAX);
   1516   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
   1517 
   1518 end:
   1519   return res;
   1520 error:
   1521   goto end;
   1522 }
   1523 
   1524 /* SOLID_SOLID_CONNECTION_PROG Name ProgName STL_filenames [PROG_PARAMS ...] */
   1525 static res_T
   1526 process_ssc_prog
   1527   (struct stardis* stardis,
   1528    wordexp_t* pwordexp)
   1529 {
   1530   char* arg = NULL;
   1531   struct description* desc;
   1532   const char *lib_name, *desc_name;
   1533   size_t sz;
   1534   struct solid_solid_connect_prog* ss_connect_prog;
   1535   struct stardis_description_create_context ctx;
   1536   size_t idx = 1;
   1537   res_T res = RES_OK;
   1538 
   1539   ASSERT(stardis && pwordexp);
   1540 
   1541   stardis->counts.sfconnect_count++;
   1542 
   1543   sz = darray_descriptions_size_get(&stardis->descriptions);
   1544   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1545   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1546   ERR(init_ss_connect_prog(stardis->allocator, &desc->d.ss_connect_prog));
   1547   ss_connect_prog = desc->d.ss_connect_prog;
   1548   desc->type = DESC_SOLID_SOLID_CONNECT_PROG;
   1549 
   1550   CHK_ARG(idx, "programmed solid-solid connection name");
   1551   ERR(description_set_name(stardis, &ss_connect_prog->name, arg));
   1552   if(find_description_by_name(stardis, &ss_connect_prog->name, desc)) {
   1553     logger_print(stardis->logger, LOG_ERROR,
   1554       "Name already used: %s\n", arg);
   1555     if(res == RES_OK) res = RES_BAD_ARG;
   1556     goto error;
   1557   }
   1558   desc_name = arg;
   1559 
   1560   CHK_ARG(idx, "program name");
   1561   ERR(str_set(&ss_connect_prog->prog_name, arg));
   1562   lib_name = arg;
   1563 
   1564   ASSERT(sz <= UINT_MAX);
   1565   ERR(read_sides_and_files(stardis, 1, (unsigned)sz, pwordexp, &idx));
   1566 
   1567   /* store the end of line as args for custom init */
   1568   ERR(set_argc_argv(stardis->allocator, str_cget(&ss_connect_prog->name),
   1569     &ss_connect_prog->argc, &ss_connect_prog->argv, pwordexp, idx));
   1570   /* get the user-defined functions from the library */
   1571   ERR(get_prog_common(lib_name, stardis, &ss_connect_prog->program,
   1572     &ss_connect_prog->create, &ss_connect_prog->release));
   1573   GET_LIB_SYMBOL(ss_connect_prog, tcr, stardis_thermal_contact_resistance);
   1574   if(!ss_connect_prog->tcr) {
   1575     logger_print(stardis->logger, LOG_ERROR,
   1576       "Cannot find function 'stardis_thermal_contact_resistance()' in lib %s\n",
   1577       lib_name);
   1578     res = RES_BAD_ARG;
   1579     goto error;
   1580   }
   1581   /* create and init custom data */
   1582   ctx.name = desc_name;
   1583   CREATE_DESC_DATA(ss_connect_prog);
   1584 
   1585 end:
   1586   return res;
   1587 error:
   1588   goto end;
   1589 }
   1590 
   1591 static res_T
   1592 read_imposed_temperature
   1593   (struct stardis* stardis,
   1594    double* imposed_temperature,
   1595    wordexp_t* pwordexp,
   1596    size_t* idx)
   1597 {
   1598   char* arg = NULL;
   1599   struct str keep;
   1600   res_T res = RES_OK;
   1601   ASSERT(stardis && imposed_temperature && pwordexp);
   1602 
   1603   str_init(stardis->allocator, &keep);
   1604   CHK_ARG((*idx), "imposed temperature");
   1605   ERR(str_set(&keep, arg));
   1606   if(0 == strcasecmp(arg, "UNKNOWN")) {
   1607     *imposed_temperature = UNKNOWN_MEDIUM_TEMPERATURE;
   1608   } else if((res = cstr_to_double(arg, imposed_temperature)) != RES_OK) {
   1609     goto error;
   1610   }
   1611 
   1612 end:
   1613   str_release(&keep);
   1614   return res;
   1615 error:
   1616   logger_print(stardis->logger, LOG_ERROR, "Invalid imposed temperature: %s\n",
   1617     str_cget(&keep));
   1618   goto end;
   1619 }
   1620 
   1621 static res_T
   1622 read_delta
   1623   (struct stardis* stardis,
   1624    double* delta,
   1625    wordexp_t* pwordexp,
   1626    size_t* idx)
   1627 {
   1628   char* arg = NULL;
   1629   res_T res = RES_OK;
   1630   ASSERT(stardis && delta && pwordexp && idx);
   1631 
   1632   CHK_ARG((*idx), "delta");
   1633   if(RES_OK == cstr_to_double(arg, delta)) {
   1634     /* Was a number */
   1635     if(*delta <= 0) {
   1636       res = RES_BAD_ARG;
   1637       goto error;
   1638     }
   1639   } else {
   1640     /* Could be 'auto' */
   1641     if(0 == strcasecmp(arg, "AUTO")) {
   1642       /* Set to DELTA_AUTO until actual value is substituted */
   1643       *delta = DELTA_AUTO;
   1644     } else {
   1645       res = RES_BAD_ARG;
   1646       goto error;
   1647     }
   1648   }
   1649 end:
   1650   return res;
   1651 error:
   1652   logger_print(stardis->logger, LOG_ERROR, "Invalid delta: %s\n", arg);
   1653   goto end;
   1654 }
   1655 
   1656 /* SOLID Name lambda rho cp delta Tinit Timposed volumic_power STL_filenames */
   1657 static res_T
   1658 process_solid
   1659   (struct stardis* stardis,
   1660    wordexp_t* pwordexp)
   1661 {
   1662   char* arg = NULL;
   1663   struct description* desc;
   1664   size_t sz;
   1665   struct solid* solid;
   1666   size_t idx = 1;
   1667   res_T res = RES_OK;
   1668 
   1669   ASSERT(stardis && pwordexp);
   1670 
   1671   stardis->counts.smed_count++;
   1672 
   1673   sz = darray_descriptions_size_get(&stardis->descriptions);
   1674   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1675   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1676   ERR(init_solid(stardis->allocator, &desc->d.solid));
   1677   solid = desc->d.solid;
   1678   desc->type = DESC_MAT_SOLID;
   1679   solid->solid_id = allocate_stardis_medium_id(stardis);
   1680   solid->is_green = stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII);
   1681   solid->t0 = stardis->initial_time;
   1682   solid->is_outside = 0;
   1683   ASSERT(sz <= UINT_MAX);
   1684   solid->desc_id = (unsigned)sz;
   1685 
   1686   CHK_ARG(idx, "solid name");
   1687   ERR(description_set_name(stardis, &solid->name, arg));
   1688   if(find_description_by_name(stardis, &solid->name, desc)) {
   1689     logger_print(stardis->logger, LOG_ERROR,
   1690       "Name already used: %s\n", arg);
   1691     if(res == RES_OK) res = RES_BAD_ARG;
   1692     goto error;
   1693   }
   1694 
   1695   CHK_ARG(idx, "lambda");
   1696   res = cstr_to_double(arg, &solid->lambda);
   1697   if(res != RES_OK || solid->lambda <= 0) {
   1698     logger_print(stardis->logger, LOG_ERROR, "Invalid lambda: %s\n", arg);
   1699     if(res == RES_OK) res = RES_BAD_ARG;
   1700     goto error;
   1701   }
   1702   CHK_ARG(idx, "rho");
   1703   res = cstr_to_double(arg, &solid->rho);
   1704   if(res != RES_OK || solid->rho <= 0) {
   1705     logger_print(stardis->logger, LOG_ERROR, "Invalid rho: %s\n", arg);
   1706     if(res == RES_OK) res = RES_BAD_ARG;
   1707     goto error;
   1708   }
   1709   CHK_ARG(idx, "cp");
   1710   res = cstr_to_double(arg, &solid->cp);
   1711   if(res != RES_OK || solid->cp <= 0) {
   1712     logger_print(stardis->logger, LOG_ERROR, "Invalid cp: %s\n", arg);
   1713     if(res == RES_OK) res = RES_BAD_ARG;
   1714     goto error;
   1715   }
   1716   ERR(read_delta(stardis, &solid->delta, pwordexp, &idx));
   1717   CHK_ARG(idx, "Tinit");
   1718   res = cstr_to_double(arg, &solid->tinit);
   1719   if(res != RES_OK || SDIS_TEMPERATURE_IS_UNKNOWN(solid->tinit)) {
   1720     logger_print(stardis->logger, LOG_ERROR, "Invalid Tinit: %s\n", arg);
   1721     if(res == RES_OK) res = RES_BAD_ARG;
   1722     goto error;
   1723   }
   1724   ERR(read_imposed_temperature(stardis, &solid->imposed_temperature,
   1725     pwordexp, &idx));
   1726   if(SDIS_TEMPERATURE_IS_KNOWN(solid->imposed_temperature)
   1727   && solid->imposed_temperature != solid->tinit) {
   1728     logger_print(stardis->logger, LOG_ERROR,
   1729       "Imposed temperature, if defined, must match initial temperature "
   1730       "(initial: %g; imposed: %g)\n",
   1731       solid->tinit, solid->imposed_temperature);
   1732     res = RES_BAD_ARG;
   1733     goto error;
   1734   }
   1735 
   1736   CHK_ARG(idx, "volumic power");
   1737   res = cstr_to_double(arg, &solid->vpower);
   1738   if(res != RES_OK) {
   1739     /* VPower can be < 0 */
   1740     logger_print(stardis->logger, LOG_ERROR, "Invalid volumic power: %s\n", arg);
   1741     goto error;
   1742   }
   1743   if(solid->vpower != 0 && stardis->picard_order > 1) {
   1744     logger_print(stardis->logger, LOG_ERROR,
   1745       "Cannot have volumic power (here %f) if Picard order is not 1 "
   1746       "(here order is %u)\n",
   1747       solid->vpower, stardis->picard_order);
   1748     res = RES_BAD_ARG;
   1749     goto error;
   1750   }
   1751 
   1752   /* Actual solid creation is defered until geometry is read to allow
   1753    * enclosure shape VS delta analysis (and auto delta computation) */
   1754 
   1755   ERR(read_sides_and_files(stardis, 0, (unsigned)sz, pwordexp, &idx));
   1756 
   1757 end:
   1758   return res;
   1759 error:
   1760   goto end;
   1761 }
   1762 
   1763 /* SOLID_PROG Name ProgName STL_filenames [PROG_PARAMS ...] */
   1764 static res_T
   1765 process_solid_prog
   1766   (struct stardis* stardis,
   1767    wordexp_t* pwordexp)
   1768 {
   1769   char* arg = NULL;
   1770   struct description* desc;
   1771   const char *lib_name, *desc_name;
   1772   size_t sz;
   1773   struct solid_prog* solid_prog;
   1774   struct stardis_description_create_context ctx;
   1775   size_t idx = 1;
   1776   res_T res = RES_OK;
   1777 
   1778   ASSERT(stardis && pwordexp);
   1779 
   1780   stardis->counts.fmed_count++;
   1781 
   1782   sz = darray_descriptions_size_get(&stardis->descriptions);
   1783   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1784   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1785   ERR(init_solid_prog(stardis->allocator, &desc->d.solid_prog));
   1786   solid_prog = desc->d.solid_prog;
   1787   desc->type = DESC_MAT_SOLID_PROG;
   1788   solid_prog->solid_id = allocate_stardis_medium_id(stardis);
   1789   ASSERT(sz <= UINT_MAX);
   1790   solid_prog->desc_id = (unsigned)sz;
   1791 
   1792   CHK_ARG(idx, "programmed solid name");
   1793   ERR(description_set_name(stardis, &solid_prog->name, arg));
   1794   if(find_description_by_name(stardis, &solid_prog->name, desc)) {
   1795     logger_print(stardis->logger, LOG_ERROR,
   1796       "Name already used: %s\n", arg);
   1797     if(res == RES_OK) res = RES_BAD_ARG;
   1798     goto error;
   1799   }
   1800   desc_name = arg;
   1801 
   1802   CHK_ARG(idx, "program name");
   1803   ERR(str_set(&solid_prog->prog_name, arg));
   1804   lib_name = arg;
   1805 
   1806   ERR(read_sides_and_files(stardis, 0, (unsigned)sz, pwordexp, &idx));
   1807 
   1808   /* store the end of line as args for custom init */
   1809   ERR(set_argc_argv(stardis->allocator, str_cget(&solid_prog->name),
   1810     &solid_prog->argc, &solid_prog->argv, pwordexp, idx));
   1811   /* get the user-defined functions from the library */
   1812   ERR(get_prog_common(lib_name, stardis, &solid_prog->program,
   1813     &solid_prog->create, &solid_prog->release));
   1814   GET_LIB_SYMBOL(solid_prog, lambda, stardis_conductivity);
   1815   GET_LIB_SYMBOL(solid_prog, rho, stardis_volumic_mass);
   1816   GET_LIB_SYMBOL(solid_prog, cp, stardis_calorific_capacity);
   1817   GET_LIB_SYMBOL(solid_prog, delta, stardis_delta_solid);
   1818   GET_LIB_SYMBOL(solid_prog, temp, stardis_medium_temperature);
   1819   GET_LIB_SYMBOL(solid_prog, vpower, stardis_volumic_power);
   1820 
   1821   GET_LIB_SYMBOL_BASE(&solid_prog->sample_path, solid_prog->program->lib_handle,
   1822     stardis_sample_conductive_path, 1);
   1823   GET_LIB_SYMBOL_BASE(&solid_prog->t_range, solid_prog->program->lib_handle,
   1824     stardis_t_range, 1);
   1825 
   1826   /* create and init custom data */
   1827   ctx.name = desc_name;
   1828   CREATE_DESC_DATA(solid_prog);
   1829 
   1830   if(solid_prog->t_range) {
   1831     double t_range[2];
   1832     solid_prog->t_range(solid_prog->prog_data, t_range);
   1833     if(STARDIS_TEMPERATURE_IS_KNOWN(t_range[0]))
   1834       stardis->t_range[0] = MMIN(stardis->t_range[0], t_range[0]);
   1835     if(STARDIS_TEMPERATURE_IS_KNOWN(t_range[1]))
   1836       stardis->t_range[1] = MMAX(stardis->t_range[1], t_range[1]);
   1837   }
   1838 
   1839   ERR(create_solver_solid_prog(stardis, solid_prog));
   1840 
   1841 end:
   1842   return res;
   1843 error:
   1844   goto end;
   1845 }
   1846 
   1847 /* FLUID Name rho cp Tinit Timposed STL_filenames */
   1848 static res_T
   1849 process_fluid
   1850   (struct stardis* stardis,
   1851    wordexp_t* pwordexp)
   1852 {
   1853   char* arg = NULL;
   1854   struct description* desc;
   1855   size_t sz;
   1856   struct fluid* fluid;
   1857   size_t idx = 1;
   1858   res_T res = RES_OK;
   1859 
   1860   ASSERT(stardis && pwordexp);
   1861 
   1862   stardis->counts.fmed_count++;
   1863 
   1864   sz = darray_descriptions_size_get(&stardis->descriptions);
   1865   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1866   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1867   ERR(init_fluid(stardis->allocator, &desc->d.fluid));
   1868   fluid = desc->d.fluid;
   1869   desc->type = DESC_MAT_FLUID;
   1870   fluid->t0 = stardis->initial_time;
   1871   fluid->fluid_id = allocate_stardis_medium_id(stardis);
   1872   fluid->is_green = stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII);
   1873   ASSERT(sz <= UINT_MAX);
   1874   fluid->desc_id = (unsigned)sz;
   1875 
   1876   CHK_ARG(idx, "fluid name");
   1877   ERR(description_set_name(stardis, &fluid->name, arg));
   1878   if(find_description_by_name(stardis, &fluid->name, desc)) {
   1879     logger_print(stardis->logger, LOG_ERROR,
   1880       "Name already used: %s\n", arg);
   1881     if(res == RES_OK) res = RES_BAD_ARG;
   1882     goto error;
   1883   }
   1884 
   1885   CHK_ARG(idx, "rho");
   1886   res = cstr_to_double(arg, &fluid->rho);
   1887   if(res != RES_OK || fluid->rho <= 0) {
   1888     logger_print(stardis->logger, LOG_ERROR, "Invalid rho: %s\n", arg);
   1889     if(res == RES_OK) res = RES_BAD_ARG;
   1890     goto error;
   1891   }
   1892   CHK_ARG(idx, "cp");
   1893   res = cstr_to_double(arg, &fluid->cp);
   1894   if(res != RES_OK || fluid->cp <= 0) {
   1895     logger_print(stardis->logger, LOG_ERROR, "Invalid cp: %s\n", arg);
   1896     if(res == RES_OK) res = RES_BAD_ARG;
   1897     goto error;
   1898   }
   1899   CHK_ARG(idx, "Tinit");
   1900   res = cstr_to_double(arg, &fluid->tinit);
   1901   if(res != RES_OK || SDIS_TEMPERATURE_IS_UNKNOWN(fluid->tinit)) {
   1902     logger_print(stardis->logger, LOG_ERROR, "Invalid Tinit: %s\n", arg);
   1903     if(res == RES_OK) res = RES_BAD_ARG;
   1904     goto error;
   1905   }
   1906   ERR(read_imposed_temperature(stardis, &fluid->imposed_temperature,
   1907     pwordexp, &idx));
   1908   if(SDIS_TEMPERATURE_IS_KNOWN(fluid->imposed_temperature)
   1909   && fluid->imposed_temperature != fluid->tinit) {
   1910     logger_print(stardis->logger, LOG_ERROR,
   1911       "Imposed temperature, if defined, must match initial temperature "
   1912       "(initial: %g; imposed: %g)\n",
   1913       fluid->tinit, fluid->imposed_temperature);
   1914     res = RES_BAD_ARG;
   1915     goto error;
   1916   }
   1917 
   1918   ERR(create_solver_fluid(stardis, fluid));
   1919 
   1920   ERR(read_sides_and_files(stardis, 0, (unsigned)sz, pwordexp, &idx));
   1921 
   1922 end:
   1923   return res;
   1924 error:
   1925   goto end;
   1926 }
   1927 
   1928 /* FLUID_PROG Name ProgName STL_filenames [PROG_PARAMS ...] */
   1929 static res_T
   1930 process_fluid_prog
   1931   (struct stardis* stardis,
   1932    wordexp_t* pwordexp)
   1933 {
   1934   char* arg = NULL;
   1935   struct description* desc;
   1936   const char *lib_name, *desc_name;
   1937   size_t sz;
   1938   struct fluid_prog* fluid_prog;
   1939   struct stardis_description_create_context ctx;
   1940   size_t idx = 1;
   1941   res_T res = RES_OK;
   1942 
   1943   ASSERT(stardis && pwordexp);
   1944 
   1945   stardis->counts.fmed_count++;
   1946 
   1947   sz = darray_descriptions_size_get(&stardis->descriptions);
   1948   ERR(darray_descriptions_resize(&stardis->descriptions, sz + 1));
   1949   desc = darray_descriptions_data_get(&stardis->descriptions) + sz;
   1950   ERR(init_fluid_prog(stardis->allocator, &desc->d.fluid_prog));
   1951   fluid_prog = desc->d.fluid_prog;
   1952   desc->type = DESC_MAT_FLUID_PROG;
   1953   fluid_prog->fluid_id = allocate_stardis_medium_id(stardis);
   1954   ASSERT(sz <= UINT_MAX);
   1955   fluid_prog->desc_id = (unsigned)sz;
   1956 
   1957   CHK_ARG(idx, "programmed fluid name");
   1958   ERR(description_set_name(stardis, &fluid_prog->name, arg));
   1959   if(find_description_by_name(stardis, &fluid_prog->name, desc)) {
   1960     logger_print(stardis->logger, LOG_ERROR,
   1961       "Name already used: %s\n", arg);
   1962     if(res == RES_OK) res = RES_BAD_ARG;
   1963     goto error;
   1964   }
   1965   desc_name = arg;
   1966 
   1967   CHK_ARG(idx, "program name");
   1968   ERR(str_set(&fluid_prog->prog_name, arg));
   1969   lib_name = arg;
   1970 
   1971   ERR(read_sides_and_files(stardis, 0, (unsigned)sz, pwordexp, &idx));
   1972 
   1973   /* store the end of line as args for custom init */
   1974   ERR(set_argc_argv(stardis->allocator, str_cget(&fluid_prog->name),
   1975     &fluid_prog->argc, &fluid_prog->argv, pwordexp, idx));
   1976   /* get the user-defined functions from the library */
   1977   ERR(get_prog_common(lib_name, stardis, &fluid_prog->program,
   1978     &fluid_prog->create, &fluid_prog->release));
   1979   GET_LIB_SYMBOL(fluid_prog, rho, stardis_volumic_mass);
   1980   GET_LIB_SYMBOL(fluid_prog, cp, stardis_calorific_capacity);
   1981   GET_LIB_SYMBOL(fluid_prog, temp, stardis_medium_temperature);
   1982   /* create and init custom data */
   1983   ctx.name = desc_name;
   1984   CREATE_DESC_DATA(fluid_prog);
   1985 
   1986   ERR(create_solver_fluid_prog(stardis, fluid_prog));
   1987 
   1988 end:
   1989   return res;
   1990 error:
   1991   goto end;
   1992 }
   1993 
   1994 /* SCALE scale_factor */
   1995 static res_T
   1996 process_scale
   1997   (struct stardis* stardis,
   1998    wordexp_t* pwordexp)
   1999 {
   2000   char* arg = NULL;
   2001   size_t idx = 1;
   2002   res_T res = RES_OK;
   2003 
   2004   ASSERT(stardis && pwordexp);
   2005 
   2006   if(stardis->scale_factor > 0) {
   2007     logger_print(stardis->logger, LOG_ERROR,
   2008       "SCALE cannot be specified twice\n");
   2009     res = RES_BAD_ARG;
   2010     goto error;
   2011   }
   2012   CHK_ARG(idx, "scale factor");
   2013   res = cstr_to_double(arg, &stardis->scale_factor);
   2014   if(res != RES_OK || stardis->scale_factor <= 0) {
   2015     logger_print(stardis->logger, LOG_ERROR,
   2016       "Invalid scale factor: %s\n", arg);
   2017     if(res == RES_OK) res = RES_BAD_ARG;
   2018     goto error;
   2019   }
   2020 
   2021 end:
   2022   return res;
   2023 error:
   2024   goto end;
   2025 }
   2026 
   2027 /* TRAD Trad Trad_ref */
   2028 static res_T
   2029 process_radiative
   2030   (struct stardis* stardis,
   2031    wordexp_t* pwordexp)
   2032 {
   2033   double trad = 0;
   2034   double tref = 0;
   2035   char* arg = NULL;
   2036   size_t idx = 1;
   2037   res_T res = RES_OK;
   2038 
   2039   ASSERT(stardis && pwordexp);
   2040 
   2041   if(stardis->radenv_def) {
   2042     logger_print(stardis->logger, LOG_ERROR,
   2043       "Radiative environment cannot be specified twice\n");
   2044     res = RES_BAD_ARG;
   2045     goto error;
   2046   }
   2047 
   2048   res = radiative_env_init_const(stardis->allocator, &stardis->radenv);
   2049   if(res != RES_OK) goto error;
   2050 
   2051   CHK_ARG(idx, "Trad");
   2052   res = cstr_to_double(arg, &trad);
   2053   if(res != RES_OK || SDIS_TEMPERATURE_IS_UNKNOWN(trad)) {
   2054     logger_print(stardis->logger, LOG_ERROR,
   2055       "Invalid Trad: %s\n", arg);
   2056     if(res == RES_OK) res = RES_BAD_ARG;
   2057     goto error;
   2058   }
   2059   CHK_ARG(idx, "Trad reference");
   2060   res = cstr_to_double(arg, &tref);
   2061   if(res != RES_OK || tref < 0) {
   2062     logger_print(stardis->logger, LOG_ERROR,
   2063       "Invalid Trad reference: %s\n", arg);
   2064     if(res == RES_OK) res = RES_BAD_ARG;
   2065     goto error;
   2066   }
   2067 
   2068   stardis->radenv.data.cst.temperature = trad;
   2069   stardis->radenv.data.cst.reference_temperature = tref;
   2070   stardis->radenv_def = 1;
   2071 
   2072 end:
   2073   return res;
   2074 error:
   2075   radiative_env_release(&stardis->radenv);
   2076   stardis->radenv = RADIATIVE_ENV_DEFAULT;
   2077   goto end;
   2078 }
   2079 
   2080 static res_T
   2081 process_radiative_prog(struct stardis* stardis, wordexp_t* pwordexp)
   2082 {
   2083   struct stardis_description_create_context ctx;
   2084   struct radiative_env_prog* radenv = NULL;
   2085   double radenv_t_range[2] = {DBL_MAX, -DBL_MAX};
   2086   char* lib_name = NULL;
   2087   char* arg = NULL;
   2088   size_t idx = 1;
   2089   res_T res = RES_OK;
   2090 
   2091   ASSERT(stardis && pwordexp);
   2092 
   2093   radenv = &stardis->radenv.data.prg;
   2094 
   2095   if(stardis->radenv_def) {
   2096     logger_print(stardis->logger, LOG_ERROR,
   2097       "Radiative environment cannot be specified twice\n");
   2098     res = RES_BAD_ARG;
   2099     goto error;
   2100   }
   2101 
   2102   res = radiative_env_init_prog(stardis->allocator, &stardis->radenv);
   2103 
   2104   CHK_ARG(idx, "program name");
   2105   ERR(str_set(&radenv->prog_name, arg));
   2106   lib_name = arg;
   2107 
   2108   if(idx < pwordexp->we_wordc
   2109   && strcasecmp(pwordexp->we_wordv[idx++], "PROG_PARAMS")) {
   2110     logger_print(stardis->logger, LOG_ERROR,
   2111       "Expecting PROG_PARAMS keyword while parsing `%s'.\n",
   2112       pwordexp->we_wordv[idx]);
   2113     res = RES_BAD_ARG;
   2114     goto error;
   2115   }
   2116 
   2117   ERR(set_argc_argv(stardis->allocator, str_cget(&radenv->prog_name),
   2118     &radenv->argc, &radenv->argv, pwordexp, idx));
   2119   ERR(get_prog_common
   2120     (lib_name, stardis, &radenv->program, &radenv->create, &radenv->release));
   2121   GET_LIB_SYMBOL(radenv, temperature,
   2122     stardis_radiative_env_temperature);
   2123   GET_LIB_SYMBOL(radenv, reference_temperature,
   2124     stardis_radiative_env_reference_temperature);
   2125   GET_LIB_SYMBOL(radenv, t_range,
   2126     stardis_t_range);
   2127 
   2128   ctx.name = "Radiative environment";
   2129   radenv->data = radenv->create
   2130     (&ctx, radenv->program->prog_data, radenv->argc, radenv->argv);
   2131   if(!radenv->data) {
   2132     logger_print(stardis->logger, LOG_ERROR,
   2133       "Cannot create data for the radiative environment\n");
   2134     res = RES_UNKNOWN_ERR;
   2135     goto error;
   2136   }
   2137 
   2138   radenv->t_range(radenv->data, radenv_t_range);
   2139   if(STARDIS_TEMPERATURE_IS_KNOWN(radenv_t_range[0]))
   2140     stardis->t_range[0] = MMIN(stardis->t_range[0], radenv_t_range[0]);
   2141   if(STARDIS_TEMPERATURE_IS_KNOWN(radenv_t_range[1]))
   2142     stardis->t_range[1] = MMAX(stardis->t_range[1], radenv_t_range[1]);
   2143 
   2144 exit:
   2145   return res;
   2146 error:
   2147   radiative_env_release(&stardis->radenv);
   2148   stardis->radenv = RADIATIVE_ENV_DEFAULT;
   2149   goto exit;
   2150 }
   2151 
   2152 static res_T
   2153 process_spherical_source
   2154   (struct stardis* stardis,
   2155    wordexp_t* pwordexp)
   2156 {
   2157   struct spherical_source* src = NULL;
   2158   char* arg = NULL;
   2159   size_t idx = 1;
   2160   res_T res = RES_OK;
   2161   ASSERT(stardis && pwordexp);
   2162 
   2163   src = &stardis->extsrc.data.sphere;
   2164 
   2165   if(stardis->extsrc.type != EXTERN_SOURCE_NONE__) {
   2166     logger_print(stardis->logger, LOG_ERROR,
   2167       "Only one external source can be defined\n");
   2168     res = RES_BAD_ARG;
   2169     goto error;
   2170   }
   2171 
   2172   res = extern_source_init_sphere(stardis->allocator, &stardis->extsrc);
   2173   if(res != RES_OK) goto error;
   2174 
   2175   CHK_ARG(idx, "radius");
   2176   res = cstr_to_double(arg, &src->radius);
   2177   if(res == RES_OK && src->radius < 0) res = RES_BAD_ARG;
   2178   if(res != RES_OK) {
   2179     logger_print(stardis->logger, LOG_ERROR,
   2180       "Invalid spherical source radius: %s\n", arg);
   2181     goto error;
   2182   }
   2183 
   2184   #define PARSE_POS(Name, Id) { \
   2185     CHK_ARG(idx, "position "Name); \
   2186     res = cstr_to_double(arg, &src->position[Id]); \
   2187     if(res != RES_OK) { \
   2188       logger_print(stardis->logger, LOG_ERROR, \
   2189         "Invalid spherical source "Name" coordinate: %s\n", arg); \
   2190       goto error; \
   2191     } \
   2192   } (void)0
   2193   PARSE_POS("X", 0);
   2194   PARSE_POS("Y", 1);
   2195   PARSE_POS("Z", 2);
   2196   #undef PARSE_POS
   2197 
   2198   CHK_ARG(idx, "power");
   2199   res = cstr_to_double(arg, &src->power);
   2200   if(res == RES_OK && src->power < 0) res = RES_BAD_ARG;
   2201   if(res != RES_OK) {
   2202     logger_print(stardis->logger, LOG_ERROR,
   2203       "Invalid spherical source power: %s\n", arg);
   2204     goto error;
   2205   }
   2206 
   2207   CHK_ARG(idx, "diffuse radiance");
   2208   res = cstr_to_double(arg, &src->diffuse_radiance);
   2209   if(res == RES_OK && src->diffuse_radiance < 0) res = RES_BAD_ARG;
   2210   if(res != RES_OK) {
   2211     logger_print(stardis->logger, LOG_ERROR,
   2212       "Invalid diffuse radiance for the spherical source: %s\n", arg);
   2213     goto error;
   2214   }
   2215 
   2216   res = extern_source_create_solver_source(&stardis->extsrc, stardis);
   2217   if(res != RES_OK) goto error;
   2218 
   2219 exit:
   2220   return res;
   2221 error:
   2222   extern_source_release(&stardis->extsrc);
   2223   stardis->extsrc = EXTERN_SOURCE_NULL;
   2224   goto exit;
   2225 }
   2226 
   2227 static res_T
   2228 process_spherical_source_prog(struct stardis* stardis, wordexp_t* pwordexp)
   2229 {
   2230   struct stardis_description_create_context ctx;
   2231   struct spherical_source_prog* src = NULL;
   2232   char* lib_name = NULL;
   2233   char* arg = NULL;
   2234   size_t idx = 1;
   2235   res_T res = RES_OK;
   2236   ASSERT(stardis && pwordexp);
   2237 
   2238   src = &stardis->extsrc.data.sphere_prog;
   2239 
   2240   if(stardis->extsrc.type != EXTERN_SOURCE_NONE__) {
   2241       logger_print(stardis->logger, LOG_ERROR,
   2242       "Only one external source can be defined\n");
   2243     res = RES_BAD_ARG;
   2244     goto error;
   2245   }
   2246 
   2247   res = extern_source_init_sphere_prog(stardis->allocator, &stardis->extsrc);
   2248   if(res != RES_OK) goto error;
   2249 
   2250   CHK_ARG(idx, "radius");
   2251   res = cstr_to_double(arg, &src->radius);
   2252   if(res == RES_OK && src->radius < 0) res = RES_BAD_ARG;
   2253   if(res != RES_OK) {
   2254     logger_print(stardis->logger, LOG_ERROR,
   2255       "Invalid spherical source radius: %s\n", arg);
   2256     goto error;
   2257   }
   2258 
   2259   CHK_ARG(idx, "program name");
   2260   ERR(str_set(&src->prog_name, arg));
   2261   lib_name = arg;
   2262 
   2263   if(idx < pwordexp->we_wordc
   2264   && strcasecmp(pwordexp->we_wordv[idx++], "PROG_PARAMS")) {
   2265     logger_print(stardis->logger, LOG_ERROR,
   2266       "Expecting PROG_PARAMS keyword while parsing `%s'.\n",
   2267       pwordexp->we_wordv[idx]);
   2268     res = RES_BAD_ARG;
   2269     goto error;
   2270   }
   2271 
   2272   ERR(set_argc_argv(stardis->allocator, str_cget(&src->prog_name), &src->argc,
   2273     &src->argv, pwordexp, idx));
   2274   ERR(get_prog_common(lib_name, stardis, &src->program, &src->create, &src->release));
   2275   GET_LIB_SYMBOL(src, position, stardis_spherical_source_position);
   2276   GET_LIB_SYMBOL(src, power, stardis_spherical_source_power);
   2277   GET_LIB_SYMBOL(src, diffuse_radiance, stardis_spherical_source_diffuse_radiance);
   2278 
   2279   ctx.name = "External spherical source";
   2280   src->data = src->create(&ctx, src->program->prog_data, src->argc, src->argv);
   2281   if(!src->data) {
   2282     logger_print(stardis->logger, LOG_ERROR,
   2283       "Cannot create data for the external spherical source\n");
   2284     res = RES_UNKNOWN_ERR;
   2285     goto error;
   2286   }
   2287 
   2288   res = extern_source_create_solver_source(&stardis->extsrc, stardis);
   2289   if(res != RES_OK) goto error;
   2290 
   2291 exit:
   2292   return res;
   2293 error:
   2294   extern_source_release(&stardis->extsrc);
   2295   stardis->extsrc = EXTERN_SOURCE_NULL;
   2296   goto exit;
   2297 }
   2298 
   2299 /* Read medium or boundary line; should be one of:
   2300  * SOLID Name lambda rho cp delta Tinit Timposed volumic_power STL_sides_filenames
   2301  * FLUID Name rho cp Tinit Timposed STL_filenames
   2302  * H_BOUNDARY_FOR_SOLID Name ref_temperature emissivity specular_fraction hc T_env STL_sides_filenames
   2303  * H_BOUNDARY_FOR_FLUID Name ref_temperature emissivity specular_fraction hc T_env STL_filenames
   2304  * T_BOUNDARY_FOR_SOLID Name T STL_filenames
   2305  * F_BOUNDARY_FOR_SOLID Name F STL_filenames
   2306  * SOLID_FLUID_CONNECTION Name ref_temperature emissivity specular_fraction hc STL_filenames
   2307  *
   2308  * SOLID_PROG Name Libray STL_sides_filenames [ PROG_PARAMS ... ]
   2309  * SOLID_PROG Name Libray STL_sides_filenames [ PROG_PARAMS ... ]
   2310  * H_BOUNDARY_FOR_SOLID_PROG Name Libray STL_filenames [ PROG_PARAMS ... ]
   2311  * H_BOUNDARY_FOR_FLUID_PROG Name Libray STL_filenames [ PROG_PARAMS ... ]
   2312  * T_BOUNDARY_FOR_SOLID_PROG Name Libray STL_filenames [ PROG_PARAMS ... ]
   2313  * F_BOUNDARY_FOR_SOLID_PROG Name Libray STL_filenames [ PROG_PARAMS ... ]
   2314  * SOLID_FLUID_CONNECTION_PROG Name Libray STL_filenames [ PROG_PARAMS ... ]
   2315  * SOLID_SOLID_CONNECTION_PROG Name Libray STL_filenames [ PROG_PARAMS ... ]
   2316  *
   2317  * SCALE scale_factor
   2318  * TRAD Trad Trad_ref
   2319  *
   2320  * STL_sides_filenames = { { FRONT | BACK | BOTH } STL_filename }+
   2321  * STL_filenames = { STL_filename }+
   2322  */
   2323 static res_T
   2324 process_model_line
   2325   (const char* file_name,
   2326    const char* line,
   2327    wordexp_t *pwordexp,
   2328    struct stardis* stardis)
   2329 {
   2330   res_T res = RES_OK;
   2331   char* arg = NULL;
   2332   size_t idx = 0;
   2333 
   2334   ASSERT(file_name && line && pwordexp && stardis);
   2335 
   2336   CHK_ARG(idx, "model line type");
   2337 
   2338   if(0 == strcasecmp(arg, "H_BOUNDARY_FOR_SOLID"))
   2339     ERR(process_h(stardis, DESC_BOUND_H_FOR_SOLID, pwordexp));
   2340   else if(0 == strcasecmp(arg, "H_BOUNDARY_FOR_SOLID_PROG"))
   2341     ERR(process_h_prog(stardis, DESC_BOUND_H_FOR_SOLID_PROG, pwordexp));
   2342   else if(0 == strcasecmp(arg, "HF_BOUNDARY_FOR_SOLID"))
   2343     ERR(process_hf(stardis, DESC_BOUND_HF_FOR_SOLID, pwordexp));
   2344   else if(0 == strcasecmp(arg, "HF_BOUNDARY_FOR_SOLID_PROG"))
   2345     ERR(process_hf_prog(stardis, DESC_BOUND_HF_FOR_SOLID_PROG, pwordexp));
   2346   else if(0 == strcasecmp(arg, "H_BOUNDARY_FOR_FLUID"))
   2347     ERR(process_h(stardis, DESC_BOUND_H_FOR_FLUID, pwordexp));
   2348   else if(0 == strcasecmp(arg, "H_BOUNDARY_FOR_FLUID_PROG"))
   2349     ERR(process_h_prog(stardis, DESC_BOUND_H_FOR_FLUID_PROG, pwordexp));
   2350   else if(0 == strcasecmp(arg, "T_BOUNDARY_FOR_SOLID"))
   2351     ERR(process_t(stardis, pwordexp));
   2352   else if(0 == strcasecmp(arg, "T_BOUNDARY_FOR_SOLID_PROG"))
   2353     ERR(process_t_prog(stardis, pwordexp));
   2354   else if(0 == strcasecmp(arg, "F_BOUNDARY_FOR_SOLID"))
   2355     ERR(process_flx(stardis, pwordexp));
   2356   else if(0 == strcasecmp(arg, "F_BOUNDARY_FOR_SOLID_PROG"))
   2357     ERR(process_flx_prog(stardis, pwordexp));
   2358   else if(0 == strcasecmp(arg, "SOLID_FLUID_CONNECTION"))
   2359     ERR(process_sfc(stardis, 0/* No flux*/, pwordexp));
   2360   else if(0 == strcasecmp(arg, "F_SOLID_FLUID_CONNECTION"))
   2361     ERR(process_sfc(stardis, 1/* Flux */, pwordexp));
   2362   else if(0 == strcasecmp(arg, "SOLID_FLUID_CONNECTION_PROG"))
   2363     ERR(process_sfc_prog(stardis, 0/* No flux*/, pwordexp));
   2364   else if(0 == strcasecmp(arg, "F_SOLID_FLUID_CONNECTION_PROG"))
   2365     ERR(process_sfc_prog(stardis, 1/* Flux */, pwordexp));
   2366   else if(0 == strcasecmp(arg, "SOLID_SOLID_CONNECTION"))
   2367     ERR(process_ssc(stardis, pwordexp));
   2368   else if(0 == strcasecmp(arg, "SOLID_SOLID_CONNECTION_PROG"))
   2369     ERR(process_ssc_prog(stardis, pwordexp));
   2370   else if(0 == strcasecmp(arg, "SOLID"))
   2371     ERR(process_solid(stardis, pwordexp));
   2372   else if(0 == strcasecmp(arg, "SOLID_PROG"))
   2373     ERR(process_solid_prog(stardis, pwordexp));
   2374   else if(0 == strcasecmp(arg, "FLUID"))
   2375     ERR(process_fluid(stardis, pwordexp));
   2376   else if(0 == strcasecmp(arg, "FLUID_PROG"))
   2377     ERR(process_fluid_prog(stardis, pwordexp));
   2378   else if(0 == strcasecmp(arg, "PROGRAM"))
   2379     ERR(process_program(stardis, pwordexp));
   2380   else if(0 == strcasecmp(arg, "SCALE"))
   2381     ERR(process_scale(stardis, pwordexp));
   2382   else if(0 == strcasecmp(arg, "TRAD"))
   2383     ERR(process_radiative(stardis, pwordexp));
   2384   else if(0 == strcasecmp(arg, "TRAD_PROG"))
   2385     ERR(process_radiative_prog(stardis, pwordexp));
   2386   else if(0 == strcasecmp(arg, "SPHERICAL_SOURCE"))
   2387     ERR(process_spherical_source(stardis, pwordexp));
   2388   else if(0 == strcasecmp(arg, "SPHERICAL_SOURCE_PROG"))
   2389     ERR(process_spherical_source_prog(stardis, pwordexp));
   2390   else {
   2391     logger_print(stardis->logger, LOG_ERROR,
   2392       "Unknown description type: %s\n", arg);
   2393     res = RES_BAD_ARG;
   2394     goto error;
   2395   }
   2396 
   2397 end:
   2398   return res;
   2399 error:
   2400   logger_print(stardis->logger, LOG_ERROR,
   2401     "Invalid description line in model file '%s':\n", file_name);
   2402   logger_print(stardis->logger, LOG_ERROR, "%s\n", line);
   2403   goto end;
   2404 }
   2405 
   2406 /*******************************************************************************
   2407  * Public Functions
   2408  ******************************************************************************/
   2409 
   2410 res_T
   2411 get_dummy_solid_id
   2412   (struct stardis* stardis,
   2413    unsigned* id)
   2414 {
   2415   res_T res = RES_OK;
   2416   struct solid* dummy = NULL;
   2417   struct dummies* dummies;
   2418   ASSERT(stardis && id);
   2419   dummies = &stardis->dummies;
   2420   if(dummies->dummy_solid) {
   2421     *id = dummies->dummy_solid_id;
   2422     goto end;
   2423   }
   2424   ERR(init_solid(stardis->allocator, &dummy));
   2425   dummies->stardis_solid = dummy;
   2426   dummies->dummy_solid_id = allocate_stardis_medium_id(stardis);
   2427   dummy->solid_id = dummies->dummy_solid_id;
   2428   dummy->t0 = stardis->initial_time;
   2429   dummy->is_outside = 1;
   2430   dummy->is_green = stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII);
   2431   create_solver_solid(stardis, dummy);
   2432   dummies->dummy_solid
   2433     = darray_media_ptr_data_get(&stardis->media)[dummies->dummy_solid_id];
   2434   logger_print(stardis->logger, LOG_OUTPUT,
   2435     "Dummy solid created: (it is medium %u)\n",
   2436     dummies->dummy_solid_id);
   2437   *id = dummies->dummy_solid_id;
   2438 end:
   2439   return res;
   2440 error:
   2441   goto end;
   2442 }
   2443 
   2444 res_T
   2445 get_dummy_fluid_id
   2446   (struct stardis* stardis,
   2447    unsigned* id)
   2448 {
   2449   res_T res = RES_OK;
   2450   struct fluid* dummy = NULL;
   2451   struct dummies* dummies;
   2452   ASSERT(stardis && id);
   2453   dummies = &stardis->dummies;
   2454   if(dummies->dummy_fluid) {
   2455     *id = dummies->dummy_fluid_id;
   2456     goto end;
   2457   }
   2458   ERR(init_fluid(stardis->allocator, &dummy));
   2459   dummies->stardis_fluid = dummy;
   2460   dummies->dummy_fluid_id = allocate_stardis_medium_id(stardis);
   2461   dummy->fluid_id = dummies->dummy_fluid_id;
   2462   dummy->t0 = stardis->initial_time;
   2463   dummy->is_outside = 1;
   2464   dummy->is_green = stardis->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII);
   2465   create_solver_fluid(stardis, dummy);
   2466   dummies->dummy_fluid
   2467     = darray_media_ptr_data_get(&stardis->media)[dummies->dummy_fluid_id];
   2468   logger_print(stardis->logger, LOG_OUTPUT,
   2469     "Dummy fluid created: (it is medium %u)\n",
   2470     dummies->dummy_fluid_id);
   2471   *id = dummies->dummy_fluid_id;
   2472 end:
   2473   return res;
   2474 error:
   2475   goto end;
   2476 }
   2477 
   2478 res_T
   2479 read_model
   2480   (const struct darray_str* model_files,
   2481    struct stardis* stardis)
   2482 {
   2483   res_T res = RES_OK;
   2484   const struct str* files = NULL;
   2485   size_t i;
   2486   FILE* f = NULL;
   2487   struct txtrdr* txtrdr = NULL;
   2488   wordexp_t pwordexp;
   2489   int word_initialized = 0;
   2490 
   2491   ASSERT(model_files && stardis);
   2492   files = darray_str_cdata_get(model_files);
   2493   FOR_EACH(i, 0, darray_str_size_get(model_files)) {
   2494     const char* name = str_cget(files + i);
   2495     int fst = 1;
   2496     f = fopen(name, "r");
   2497     if(!f) {
   2498       logger_print(stardis->logger, LOG_ERROR,
   2499         "Cannot open model file '%s'\n", name);
   2500       res = RES_IO_ERR;
   2501       goto error;
   2502     }
   2503     txtrdr_stream(stardis->allocator, f, name, '#', &txtrdr);
   2504     for(;;) {
   2505       char* line;
   2506       int flags = WRDE_NOCMD | WRDE_UNDEF;
   2507       if(!fst) flags |= WRDE_REUSE;
   2508       ERR(txtrdr_read_line(txtrdr));
   2509       line = txtrdr_get_line(txtrdr);
   2510       if(!line) break;
   2511       switch(wordexp(line, &pwordexp, flags)) {
   2512         case 0: /* No error */
   2513           word_initialized = 1;
   2514           break;
   2515         case WRDE_NOSPACE: /* Ran out of memory.  */
   2516           res = RES_MEM_ERR;
   2517           goto error;
   2518         case WRDE_BADCHAR: /* A metachar appears in the wrong place.  */
   2519           logger_print(stardis->logger, LOG_ERROR,
   2520             "%s: word expansion error: invalid character.\n", name);
   2521           goto exp_error;
   2522         case WRDE_BADVAL: /* Undefined var reference with WRDE_UNDEF.  */
   2523           logger_print(stardis->logger, LOG_ERROR,
   2524             "%s: word expansion error: undefined environment variable.\n", name);
   2525           goto exp_error;
   2526         case WRDE_CMDSUB: /* Command substitution with WRDE_NOCMD.  */
   2527           logger_print(stardis->logger, LOG_ERROR,
   2528             "%s: word expansion error: command substitution is not enabled.\n",
   2529             name);
   2530           goto exp_error;
   2531         case WRDE_SYNTAX: /* Shell syntax error.  */
   2532           logger_print(stardis->logger, LOG_ERROR,
   2533             "%s: word expansion error: syntax error.\n", name);
   2534           goto exp_error;
   2535         default:
   2536           FATAL("Unexpected return code.\n");
   2537       }
   2538       ERR(process_model_line(name, line, &pwordexp, stardis));
   2539       fst = 0;
   2540       continue;
   2541 exp_error:
   2542       logger_print(stardis->logger, LOG_ERROR, "%s\n", line);
   2543       res = RES_BAD_ARG;
   2544       goto error;
   2545     }
   2546     txtrdr_ref_put(txtrdr);
   2547     txtrdr = NULL;
   2548     fclose(f);
   2549     f = NULL;
   2550   }
   2551   if(stardis->scale_factor <= 0)
   2552     stardis->scale_factor = STARDIS_DEFAULT_SCALE_FACTOR;
   2553   logger_print(stardis->logger, LOG_OUTPUT,
   2554     "Scaling factor is %g\n", stardis->scale_factor);
   2555   if(stardis->radenv.type == RADIATIVE_ENV_CONST) {
   2556     const double trad = stardis->radenv.data.cst.temperature;
   2557     const double trad_ref = stardis->radenv.data.cst.reference_temperature;
   2558     logger_print(stardis->logger, LOG_OUTPUT,
   2559       "Trad is %g, Trad reference is %g\n", trad, trad_ref);
   2560     stardis->t_range[0] = MMIN(stardis->t_range[0], trad_ref);
   2561     stardis->t_range[1] = MMAX(stardis->t_range[1], trad_ref);
   2562   }
   2563   logger_print(stardis->logger, LOG_OUTPUT,
   2564     "System Tref range is [%g %g]\n", SPLIT2(stardis->t_range));
   2565   logger_print(stardis->logger, LOG_OUTPUT,
   2566     "Picard order is %u\n", stardis->picard_order);
   2567 
   2568   ASSERT(!f && !txtrdr);
   2569 exit:
   2570   if(word_initialized) wordfree(&pwordexp);
   2571   return res;
   2572 error:
   2573   if(f) fclose(f);
   2574   if(txtrdr) txtrdr_ref_put(txtrdr);
   2575   goto exit;
   2576 }
   2577