solstice

Compute collected power and efficiencies of a solar plant
git clone git://git.meso-star.com/solstice.git
Log | Files | Refs | README | LICENSE

solstice.c (22729B)


      1 /* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2016-2018 CNRS
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     16 
     17 #define _POSIX_C_SOURCE 200112L /* close support */
     18 
     19 #include "solstice.h"
     20 #include "solstice_c.h"
     21 #include "solstice_args.h"
     22 #include "parser/solparser.h"
     23 
     24 #include <rsys/double2.h>
     25 
     26 #include <sys/stat.h>
     27 #include <sys/types.h>
     28 
     29 #include <errno.h>
     30 #include <stdio.h>
     31 #include <fcntl.h>
     32 
     33 #ifdef COMPILER_CL
     34   /* Wrap POSIX functions and constants */
     35   #include <io.h>
     36   #define open _open
     37   #define close _close
     38   #define fdopen _fdopen
     39   #define S_IRUSR S_IREAD
     40   #define S_IWUSR S_IWRITE
     41 #else
     42   /* open/close functions */
     43   #include <unistd.h>
     44 #endif
     45 
     46 #include <solstice/ssol.h>
     47 
     48 /*******************************************************************************
     49  * Helper functions
     50  ******************************************************************************/
     51 static void
     52 log_err(const char* msg, void* ctx)
     53 {
     54   ASSERT(msg);
     55   (void)ctx;
     56 #ifdef OS_WINDOWS
     57   fprintf(stderr, "error: %s", msg);
     58 #else
     59   fprintf(stderr, "\x1b[31merror:\x1b[0m %s", msg);
     60 #endif
     61 }
     62 
     63 static void
     64 log_warn(const char* msg, void* ctx)
     65 {
     66   ASSERT(msg);
     67   (void)ctx;
     68 #ifdef OS_WINDOWS
     69   fprintf(stderr,"warning: %s", msg);
     70 #else
     71   fprintf(stderr, "\x1b[33mwarning:\x1b[0m %s", msg);
     72 #endif
     73 }
     74 
     75 static void
     76 clear_materials(struct htable_material* materials)
     77 {
     78   struct htable_material_iterator it, end;
     79   ASSERT(materials);
     80 
     81   htable_material_begin(materials, &it);
     82   htable_material_end(materials, &end);
     83   while(!htable_material_iterator_eq(&it, &end)) {
     84     struct ssol_material* mtl = *htable_material_iterator_data_get(&it);
     85     SSOL(material_ref_put(mtl));
     86     htable_material_iterator_next(&it);
     87   }
     88   htable_material_clear(materials);
     89 }
     90 
     91 static void
     92 clear_objects(struct htable_object* objects)
     93 {
     94   struct htable_object_iterator it, end;
     95   ASSERT(objects);
     96 
     97   htable_object_begin(objects, &it);
     98   htable_object_end(objects, &end);
     99   while(!htable_object_iterator_eq(&it, &end)) {
    100     struct ssol_object* obj = *htable_object_iterator_data_get(&it);
    101     SSOL(object_ref_put(obj));
    102     htable_object_iterator_next(&it);
    103   }
    104   htable_object_clear(objects);
    105 }
    106 
    107 static void
    108 clear_nodes(struct darray_nodes* nodes)
    109 {
    110   size_t i, n;
    111   ASSERT(nodes);
    112   n = darray_nodes_size_get(nodes);
    113   FOR_EACH(i, 0, n) {
    114     solstice_node_ref_put(darray_nodes_data_get(nodes)[i]);
    115   }
    116   darray_nodes_clear(nodes);
    117 }
    118 
    119 static void
    120 clear_anchors(struct htable_anchor* anchors)
    121 {
    122   struct htable_anchor_iterator it, end;
    123   ASSERT(anchors);
    124 
    125   htable_anchor_begin(anchors, &it);
    126   htable_anchor_end(anchors, &end);
    127   while(!htable_anchor_iterator_eq(&it, &end)) {
    128     struct solstice_node* node = *htable_anchor_iterator_data_get(&it);
    129     solstice_node_ref_put(node);
    130     htable_anchor_iterator_next(&it);
    131   }
    132   htable_anchor_clear(anchors);
    133 }
    134 
    135 static res_T
    136 auto_look_at
    137   (struct ssol_scene* scn,
    138    const double fov_x, /* Horizontal field of view in radian */
    139    const double proj_ratio, /* Width / height */
    140    const double up[3], /* Up vector */
    141    double position[3],
    142    double target[3])
    143 {
    144   float flower[3], fupper[3];
    145   double lower[3], upper[3];
    146   double up_abs[3];
    147   double axis_min[3];
    148   double axis_x[3];
    149   double axis_z[3];
    150   double tmp[3];
    151   double radius;
    152   double depth;
    153   res_T res;
    154   ASSERT(scn && fov_x && proj_ratio && up);
    155 
    156   res = ssol_scene_compute_aabb(scn, flower, fupper);
    157   if(res != RES_OK) {
    158     fprintf(stderr, "Couldn't compute the scene bounding box.\n");
    159     goto error;
    160   }
    161 
    162   if(flower[0] > fupper[0]
    163   || flower[1] > fupper[1]
    164   || flower[2] > fupper[2]) { /* Empty scene */
    165     d3_set(position, SOLSTICE_ARGS_DEFAULT.camera.pos);
    166     d3_set(target, SOLSTICE_ARGS_DEFAULT.camera.tgt);
    167     goto exit;
    168   }
    169 
    170   d3_set_f3(upper, fupper);
    171   d3_set_f3(lower, flower);
    172 
    173   /* The target is the scene centroid */
    174   d3_muld(target, d3_add(target, lower, upper), 0.5);
    175 
    176   /* Define which up dimension is minimal and use its unit vector to compute a
    177    * vector orthogonal to `up'. This ensures that the unit vector and `up' are
    178    * not collinear and thus that their cross product is not a zero vector. */
    179   up_abs[0] = fabs(up[0]);
    180   up_abs[1] = fabs(up[1]);
    181   up_abs[2] = fabs(up[2]);
    182   if(up_abs[0] < up_abs[1]) {
    183     if(up_abs[0] < up_abs[2]) d3(axis_min, 1, 0, 0);
    184     else d3(axis_min, 0, 0, 1);
    185   } else {
    186     if(up_abs[1] < up_abs[2]) d3(axis_min, 0, 1, 0);
    187     else d3(axis_min, 0, 0, 1);
    188   }
    189   d3_normalize(axis_x, d3_cross(axis_x, up, axis_min));
    190   d3_normalize(axis_z, d3_cross(axis_z, up, axis_x));
    191 
    192   /* Approximate whether on the XYZ or the ZYX basis the visible part of the
    193    * model is maximise */
    194   if(fabs(d3_dot(axis_x, upper)) < fabs(d3_dot(axis_z, upper))) {
    195     SWAP(double, axis_x[0], axis_z[0]);
    196     SWAP(double, axis_x[1], axis_z[1]);
    197     SWAP(double, axis_x[2], axis_z[2]);
    198   }
    199 
    200   /* Ensure that the whole model is visible */
    201   radius = d3_len(d3_sub(tmp, upper, lower)) * 0.5;
    202   if(proj_ratio < 1) {
    203     depth = radius / sin(fov_x/2.0);
    204   } else {
    205     depth = radius / sin(fov_x/(2.0*proj_ratio));
    206   }
    207 
    208   /* Define the camera position */
    209   d3_sub(position, target, d3_muld(tmp, axis_z, depth));
    210 
    211   /* Empirically move the position to find a better point of view */
    212   d3_add(position, position, d3_muld(tmp, up, radius)); /*Empirical offset*/
    213   d3_add(position, position, d3_muld(tmp, axis_x, radius)); /*Empirical offset*/
    214   d3_normalize(tmp, d3_sub(tmp, target, position));
    215   d3_sub(position, target, d3_muld(tmp, tmp, depth));
    216 
    217 exit:
    218   return res;
    219 error:
    220   goto exit;
    221 }
    222 
    223 static res_T
    224 setup_camera(struct solstice* solstice, const struct solstice_args* args)
    225 {
    226   struct ssol_camera* cam = NULL;
    227   double proj_ratio = 0;
    228   double pos[3], tgt[3];
    229   res_T res = RES_OK;
    230   ASSERT(solstice && args);
    231 
    232   res = ssol_camera_create(solstice->ssol, &cam);
    233   if(res != RES_OK) {
    234     fprintf(stderr, "Could not create the rendering camera.\n");
    235     goto error;
    236   }
    237 
    238   proj_ratio = (double)args->img.width / (double)args->img.height;
    239   res = ssol_camera_set_proj_ratio(cam, proj_ratio);
    240   if(res != RES_OK) {
    241     fprintf(stderr, "Invalid image ratio '%g'.\n", proj_ratio);
    242     goto error;
    243   }
    244 
    245   res = ssol_camera_set_fov(cam, MDEG2RAD(args->camera.fov_x));
    246   if(res != RES_OK) {
    247     fprintf(stderr, "Invalid horizontal field of view '%g' degrees.\n",
    248       args->camera.fov_x);
    249     goto error;
    250   }
    251 
    252   if(!args->camera.auto_look_at) {
    253     d3_set(pos, args->camera.pos);
    254     d3_set(tgt, args->camera.tgt);
    255   } else {
    256     res = auto_look_at(solstice->scene, MDEG2RAD(args->camera.fov_x),
    257       proj_ratio, args->camera.up, pos, tgt);
    258     if(res != RES_OK) goto error;
    259   }
    260 
    261   res = ssol_camera_look_at(cam, pos, tgt, args->camera.up);
    262   if(res != RES_OK) {
    263     fprintf(stderr,
    264 "Invalid camera point of view:\n"
    265 "  position = %g %g %g\n"
    266 "  target = %g %g %g\n"
    267 "  up = %g %g %g\n",
    268       SPLIT3(args->camera.pos),
    269       SPLIT3(args->camera.tgt),
    270       SPLIT3(args->camera.up));
    271     goto error;
    272   }
    273 
    274 exit:
    275   solstice->camera = cam;
    276   return res;
    277 error:
    278   if(cam) {
    279     SSOL(camera_ref_put(cam));
    280     cam = NULL;
    281   }
    282   goto exit;
    283 }
    284 
    285 static res_T
    286 setup_framebuffer(struct solstice* solstice, const struct solstice_args* args)
    287 {
    288   struct ssol_image* fbuf = NULL;
    289   res_T res = RES_OK;
    290   ASSERT(solstice && args);
    291 
    292   res = ssol_image_create(solstice->ssol, &fbuf);
    293   if(res != RES_OK) {
    294     fprintf(stderr, "Could not create the rendering framebuffer.\n");
    295     goto error;
    296   }
    297 
    298   res = ssol_image_setup
    299     (fbuf, args->img.width, args->img.height, SSOL_PIXEL_DOUBLE3);
    300   if(res != RES_OK) {
    301     fprintf(stderr,
    302       "Could not set the framebuffer definition to %lux%lu.\n",
    303       args->img.width, args->img.height);
    304     goto error;
    305   }
    306 
    307 exit:
    308   solstice->framebuffer = fbuf;
    309   return res;
    310 
    311 error:
    312   if(fbuf) {
    313     SSOL(image_ref_put(fbuf));
    314     fbuf = NULL;
    315   }
    316   goto exit;
    317 }
    318 
    319 static INLINE void
    320 spherical_to_cartesian_sun_dir
    321   (struct solstice_args_spherical* spherical, double sun_dir[3])
    322 {
    323   double cos_azimuth;
    324   double sin_azimuth;
    325   double cos_elevation;
    326   double sin_elevation;
    327   double azimuth;
    328   double elevation;
    329   ASSERT(spherical && sun_dir);
    330 
    331   azimuth = MDEG2RAD(spherical->azimuth);
    332   elevation = MDEG2RAD(spherical->elevation);
    333 
    334   cos_azimuth = cos(azimuth);
    335   sin_azimuth = sin(azimuth);
    336   cos_elevation = cos(elevation);
    337   sin_elevation = sin(elevation);
    338 
    339   sun_dir[0] = -(cos_elevation * cos_azimuth);
    340   sun_dir[1] = -(cos_elevation * sin_azimuth);
    341   sun_dir[2] = -(sin_elevation);
    342 }
    343 
    344 static res_T
    345 setup_sun_dirs(struct solstice* solstice, const struct solstice_args* args)
    346 {
    347   double* sun_dirs = NULL;
    348   double* sun_angles = NULL;
    349   size_t i;
    350   res_T res = RES_OK;
    351   ASSERT(solstice && args);
    352 
    353   res = darray_double_resize(&solstice->sun_dirs, args->nsun_dirs*3/*#dims*/);
    354   if(res != RES_OK) {
    355     fprintf(stderr,
    356       "Could not reserve the list of %lu sun directions.\n",
    357       (unsigned long)args->nsun_dirs);
    358     goto error;
    359   }
    360   res = darray_double_resize(&solstice->sun_angles, args->nsun_dirs*2/*#dims*/);
    361   if(res != RES_OK) {
    362     fprintf(stderr,
    363       "Could not reserve the list of %lu sun angles.\n",
    364       (unsigned long)args->nsun_dirs);
    365     goto error;
    366   }
    367   sun_dirs = darray_double_data_get(&solstice->sun_dirs);
    368   sun_angles = darray_double_data_get(&solstice->sun_angles);
    369   FOR_EACH(i, 0, args->nsun_dirs) {
    370     spherical_to_cartesian_sun_dir(args->sun_dirs + i, sun_dirs + i*3/*#dims*/);
    371     d2(sun_angles + i*2, args->sun_dirs[i].azimuth, args->sun_dirs[i].elevation);
    372   }
    373 
    374 exit:
    375   return res;
    376 error:
    377   darray_double_clear(&solstice->sun_dirs);
    378   darray_double_clear(&solstice->sun_angles);
    379   goto exit;
    380 }
    381 
    382 static res_T
    383 load_data(struct solstice* solstice, const struct solstice_args* args)
    384 {
    385   struct solparser_entity_iterator it, end;
    386   FILE* file = stdin;
    387   const char* name = "stdin";
    388   res_T res = RES_OK;
    389   ASSERT(solstice && args);
    390 
    391   if(args->input_filename) {
    392     file = fopen(args->input_filename, "r");
    393     if(!file) {
    394       fprintf(stderr, "Could not open the file `%s'.\n", args->input_filename);
    395       res = RES_IO_ERR;
    396       goto error;
    397     }
    398     name = args->input_filename;
    399   } else if(!args->quiet) {
    400 #ifndef OS_WINDOWS
    401     fprintf(stderr,
    402       "Enter the solar facility data. Type ^D (i.e. CTRL+d) to stop:\n");
    403 #else
    404     fprintf(stderr,
    405       "Enter the solar facility data. Type ^Z (i.e. CTRL+z) to stop:\n");
    406 #endif
    407   }
    408 
    409   res = solparser_setup(solstice->parser, name, file);
    410   if(res != RES_OK) goto error;
    411 
    412   res = solparser_load(solstice->parser);
    413   if(res != RES_OK) goto error;
    414 
    415   solparser_entity_iterator_begin(solstice->parser, &it);
    416   solparser_entity_iterator_end(solstice->parser, &end);
    417   if(solparser_entity_iterator_eq(&it, &end)) {
    418     fprintf(stderr, "No entity is defined.\n");
    419     res = RES_BAD_ARG;
    420     goto error;
    421   }
    422 
    423 exit:
    424   if(file && file != stdin) fclose(file);
    425   return res;
    426 error:
    427   goto exit;
    428 }
    429 
    430 static res_T
    431 setup_receivers(struct solstice* solstice, struct srcvl* srcvl)
    432 {
    433   size_t i, n;
    434   res_T res = RES_OK;
    435   ASSERT(solstice && srcvl);
    436 
    437   htable_receiver_clear(&solstice->receivers);
    438   darray_receiver_clear(&solstice->rcvs_list);
    439 
    440   n = srcvl_count(srcvl);
    441 
    442   res = darray_receiver_resize(&solstice->rcvs_list, n);
    443   if(res != RES_OK) {
    444     fprintf(stderr, "Could not reserve memory space for the receivers.\n");
    445     goto error;
    446   }
    447 
    448   FOR_EACH(i, 0, n) {
    449     struct solstice_receiver* receiver;
    450     struct srcvl_receiver rcv;
    451     const struct solparser_entity* entity;
    452     const char* name;
    453 
    454     receiver = darray_receiver_data_get(&solstice->rcvs_list) + i;
    455 
    456     srcvl_get(srcvl, i, &rcv);
    457     entity = solparser_find_entity(solstice->parser, rcv.name);
    458     if(!entity) {
    459       fprintf(stderr, "Invalid entity `%s'.\n", rcv.name);
    460       res = RES_BAD_ARG;
    461       goto error;
    462     }
    463 
    464     if(entity->type != SOLPARSER_ENTITY_GEOMETRY) {
    465       fprintf(stderr,
    466         "The entity `%s' is not a geometry. It cannot be a receiver.\n",
    467         rcv.name);
    468       res = RES_BAD_ARG;
    469       goto error;
    470     }
    471 
    472     res = str_set(&receiver->name, rcv.name);
    473     if(res != RES_OK) {
    474       fprintf(stderr, "Could not copy the receiver name.\n");
    475       goto error;
    476     }
    477 
    478     receiver->node = NULL;
    479     receiver->side = rcv.side;
    480     receiver->per_primitive_output = rcv.per_primitive_output;
    481 
    482     name = str_cget(&receiver->name);
    483     res = htable_receiver_set(&solstice->receivers, &name, &i);
    484     if(res != RES_OK) {
    485       fprintf(stderr,
    486         "Could not register the receiver `%s' against Solstice.\n",
    487         rcv.name);
    488       goto error;
    489     }
    490   }
    491 
    492 exit:
    493   return res;
    494 error:
    495   htable_receiver_clear(&solstice->receivers);
    496   darray_receiver_clear(&solstice->rcvs_list);
    497   goto exit;
    498 }
    499 
    500 static res_T
    501 load_receivers(struct solstice* solstice, const struct solstice_args* args)
    502 {
    503   FILE* file = NULL;
    504   struct srcvl* srcvl = NULL;
    505   res_T res = RES_OK;
    506   ASSERT(solstice && args);
    507 
    508   file = fopen(args->receivers_filename, "r");
    509   if(!file) {
    510     fprintf(stderr, "Could not open the list of receivers `%s'.\n",
    511       args->receivers_filename);
    512     res = RES_IO_ERR;
    513     goto error;
    514   }
    515 
    516   res = srcvl_create(solstice->allocator, &srcvl);
    517   if(res != RES_OK) goto error;
    518   res = srcvl_setup_stream(srcvl, args->receivers_filename, file);
    519   if(res != RES_OK) goto error;
    520   res = srcvl_load(srcvl);
    521   if(res != RES_OK) goto error;
    522   res = setup_receivers(solstice, srcvl);
    523   if(res != RES_OK) goto error;
    524 
    525 exit:
    526   if(file) fclose(file);
    527   if(srcvl) {
    528     srcvl_ref_put(srcvl);
    529     srcvl = NULL;
    530   }
    531   return res;
    532 error:
    533   goto exit;
    534 }
    535 
    536 static res_T
    537 open_output_stream(const char* name, const int force_overwriting, FILE** stream)
    538 {
    539   res_T res = RES_OK;
    540   int fd = -1;
    541   FILE* fp = NULL;
    542   ASSERT(name);
    543 
    544   if(force_overwriting) {
    545     fp = fopen(name, "w");
    546     if(!fp) {
    547       fprintf(stderr, "Could not open the output file `%s'.\n", name);
    548       goto error;
    549     }
    550   } else {
    551     fd = open(name, O_CREAT|O_WRONLY|O_EXCL|O_TRUNC, S_IRUSR|S_IWUSR);
    552     if(fd >= 0) {
    553       fp = fdopen(fd, "w");
    554       if(fp == NULL) {
    555         fprintf(stderr, "Could not open the output file `%s'.\n", name);
    556         goto error;
    557       }
    558     } else if(errno == EEXIST) {
    559       fprintf(stderr,
    560         "The output file `%s' already exists. Use -f to overwrite it.\n", name);
    561       goto error;
    562     } else {
    563       fprintf(stderr,
    564         "Unexpected error while opening the output file `%s'.\n", name);
    565       goto error;
    566     }
    567   }
    568 
    569 exit:
    570   *stream = fp;
    571   return res;
    572 error:
    573   res = RES_IO_ERR;
    574   if(fp) {
    575     CHK(fclose(fp) == 0);
    576     fp = NULL;
    577   } else if(fd >= 0) {
    578     CHK(close(fd) == 0);
    579   }
    580   goto exit;
    581 }
    582 
    583 /*******************************************************************************
    584  * Solstice local functions
    585  ******************************************************************************/
    586 res_T
    587 solstice_init
    588   (struct mem_allocator* allocator,
    589    const struct solstice_args* args,
    590    struct solstice* solstice)
    591 {
    592   res_T res = RES_OK;
    593   ASSERT(solstice && args);
    594 
    595   memset(solstice, 0, sizeof(struct solstice));
    596   htable_material_init(allocator, &solstice->materials);
    597   htable_object_init(allocator, &solstice->objects);
    598   htable_anchor_init(allocator, &solstice->anchors);
    599   htable_receiver_init(allocator, &solstice->receivers);
    600   htable_primary_init(allocator, &solstice->primaries);
    601   darray_nodes_init(allocator, &solstice->roots);
    602   darray_nodes_init(allocator, &solstice->pivots);
    603   darray_receiver_init(allocator, &solstice->rcvs_list);
    604   darray_double_init(allocator, &solstice->sun_dirs);
    605   darray_double_init(allocator, &solstice->sun_angles);
    606 
    607   solstice->allocator = allocator ? allocator : &mem_default_allocator;
    608 
    609   logger_init(solstice->allocator, &solstice->logger);
    610   logger_set_stream(&solstice->logger, LOG_ERROR, log_err, NULL);
    611   logger_set_stream(&solstice->logger, LOG_WARNING, log_warn, NULL);
    612 
    613   res = ssol_device_create(&solstice->logger, allocator, args->nthreads,
    614     args->verbose, &solstice->ssol);
    615   if(res != RES_OK) {
    616     fprintf(stderr, "Could not create the Solstice Solver device.\n");
    617     goto error;
    618   }
    619 
    620   res = ssol_scene_create(solstice->ssol, &solstice->scene);
    621   if(res != RES_OK) {
    622     fprintf(stderr, "Could not create the Solstice Solver scene.\n");
    623     goto error;
    624   }
    625 
    626   res = ssol_material_create_virtual(solstice->ssol, &solstice->mtl_virtual);
    627   if(res != RES_OK) {
    628     fprintf(stderr, "Could not create the global virtual material.\n");
    629     goto error;
    630   }
    631 
    632   res = solparser_create(allocator, &solstice->parser);
    633   if(res != RES_OK) {
    634     fprintf(stderr, "Could not create the Solstice Parser.\n");
    635     goto error;
    636   }
    637 
    638   res = setup_sun_dirs(solstice, args);
    639   if(res != RES_OK) goto error;
    640 
    641   if(args->rng_state_input_filename) {
    642     solstice->rng_state_input = fopen(args->rng_state_input_filename, "r");
    643     if(!solstice->rng_state_input) {
    644       fprintf(stderr, "Could not open the input RNG state file.\n");
    645       res = RES_IO_ERR;
    646       goto error;
    647     }
    648   }
    649 
    650   if(args->rng_state_output_filename) {
    651     res = open_output_stream(args->rng_state_output_filename,
    652       args->force_overwriting, &solstice->rng_state_output);
    653     if(res != RES_OK) goto error;
    654   }
    655 
    656   if(!args->output_filename) {
    657     solstice->output = stdout;
    658   } else {
    659     res = open_output_stream(args->output_filename, args->force_overwriting,
    660       &solstice->output);
    661     if(res != RES_OK) goto error;
    662   }
    663 
    664   res = load_data(solstice, args);
    665   if(res != RES_OK) goto error;
    666 
    667   if(args->receivers_filename) {
    668     res = load_receivers(solstice, args);
    669     if(res != RES_OK) goto error;
    670   }
    671 
    672   res = solstice_setup_entities(solstice);
    673   if(res != RES_OK) {
    674     fprintf(stderr, "Could not setup the Solstice entities.\n");
    675     goto error;
    676   }
    677 
    678   res = solstice_create_sun(solstice);
    679   if(res != RES_OK) {
    680     fprintf(stderr, "Could not setup the Solstice sun.\n");
    681     goto error;
    682   }
    683 
    684   res = solstice_create_atmosphere(solstice);
    685   if(res != RES_OK) {
    686     fprintf(stderr, "Could not setup the Solstice atmosphere.\n");
    687     goto error;
    688   }
    689 
    690   solstice->nexperiments = args->nexperiments;
    691   solstice->dump_format = args->dump_format;
    692   solstice->dump_split_mode = args->dump_split_mode;
    693   solstice->dump_paths = args->dump_paths;
    694 
    695   solstice->path_tracker = SSOL_PATH_TRACKER_DEFAULT;
    696   solstice->path_tracker.infinite_ray_length = args->infinite_ray_length;
    697   solstice->path_tracker.sun_ray_length = args->sun_ray_length;
    698 
    699   if(args->rendering) {
    700     res = setup_camera(solstice, args);
    701     if(res != RES_OK) goto error;
    702     res = setup_framebuffer(solstice, args);
    703     if(res != RES_OK) goto error;
    704     solstice->render_mode = args->render_mode;
    705     solstice->spp = args->img.spp;
    706     d3_set(solstice->up, args->camera.up);
    707   }
    708 
    709 exit:
    710   return res;
    711 error:
    712   solstice_release(solstice);
    713   goto exit;
    714 }
    715 
    716 void
    717 solstice_release(struct solstice* solstice)
    718 {
    719   ASSERT(solstice);
    720   clear_materials(&solstice->materials);
    721   clear_objects(&solstice->objects);
    722   clear_nodes(&solstice->roots);
    723   clear_anchors(&solstice->anchors);
    724   /* Don't clear pivots, as they are cleared from some root */
    725   if(solstice->ssol) SSOL(device_ref_put(solstice->ssol));
    726   if(solstice->scene) SSOL(scene_ref_put(solstice->scene));
    727   if(solstice->sun) SSOL(sun_ref_put(solstice->sun));
    728   if(solstice->atmosphere) SSOL(atmosphere_ref_put(solstice->atmosphere));
    729   if(solstice->parser) solparser_ref_put(solstice->parser);
    730   if(solstice->camera) SSOL(camera_ref_put(solstice->camera));
    731   if(solstice->framebuffer) SSOL(image_ref_put(solstice->framebuffer));
    732   if(solstice->output && solstice->output != stdout) fclose(solstice->output);
    733   if(solstice->mtl_virtual) SSOL(material_ref_put(solstice->mtl_virtual));
    734   if(solstice->rng_state_input) fclose(solstice->rng_state_input);
    735   if(solstice->rng_state_output) fclose(solstice->rng_state_output);
    736   htable_material_release(&solstice->materials);
    737   htable_object_release(&solstice->objects);
    738   htable_anchor_release(&solstice->anchors);
    739   htable_receiver_release(&solstice->receivers);
    740   htable_primary_release(&solstice->primaries);
    741   darray_nodes_release(&solstice->roots);
    742   darray_nodes_release(&solstice->pivots);
    743   darray_receiver_release(&solstice->rcvs_list);
    744   darray_double_release(&solstice->sun_dirs);
    745   darray_double_release(&solstice->sun_angles);
    746   logger_release(&solstice->logger);
    747 }
    748 
    749 res_T
    750 solstice_run(struct solstice* solstice)
    751 {
    752   const double* sun_dirs = NULL;
    753   const double* sun_angles = NULL;
    754   size_t nsun_dirs = 0;
    755   size_t i;
    756   int dump;
    757   int draw;
    758   res_T res = RES_OK;
    759   ASSERT(solstice);
    760 
    761   sun_dirs = darray_double_cdata_get(&solstice->sun_dirs);
    762   sun_angles = darray_double_cdata_get(&solstice->sun_angles);
    763   nsun_dirs = darray_double_size_get(&solstice->sun_dirs);
    764   ASSERT(nsun_dirs%3 == 0);
    765   nsun_dirs /= 3/*#dims*/;
    766 
    767   dump = solstice->dump_format != SOLSTICE_ARGS_DUMP_NONE;
    768   draw = solstice->framebuffer != NULL;
    769 
    770   if(!nsun_dirs) {
    771     const double sun_dir[3] = {0, 0, -1};
    772 
    773     res = ssol_sun_set_direction(solstice->sun, sun_dir);
    774     if(res != RES_OK) {
    775       fprintf(stderr, "Could not update the sun direction.\n");
    776       goto error;
    777     }
    778 
    779     fprintf(solstice->output, "#--- No Sun direction\n");
    780 
    781     if(dump) {
    782       res = solstice_dump(solstice);
    783       if(res != RES_OK) goto error;
    784     } else {
    785       ASSERT(draw);
    786       res = solstice_draw(solstice);
    787       if(res != RES_OK) goto error;
    788     }
    789   } else {
    790     FOR_EACH(i, 0, nsun_dirs) {
    791       const double* sun_dir = sun_dirs + i*3/*#dims*/;
    792       const double* sun_angle = sun_angles + i*2/*#angles*/;
    793       fprintf(solstice->output, "#--- Sun direction: %g %g (%g %g %g)\n",
    794         SPLIT2(sun_angle), SPLIT3(sun_dir));
    795 
    796       res = solstice_update_entities(solstice, sun_dir);
    797       if(res != RES_OK) goto error;
    798 
    799       res = ssol_sun_set_direction(solstice->sun, sun_dir);
    800       if(res != RES_OK) {
    801         fprintf(stderr, "Could not update the sun direction.\n");
    802         goto error;
    803       }
    804 
    805       if(draw) {
    806         res = solstice_draw(solstice);
    807         if(res != RES_OK) goto error;
    808       } else if(dump) {
    809         res = solstice_dump(solstice);
    810         if(res != RES_OK) goto error;
    811       } else {
    812         res = solstice_solve(solstice);
    813         if(res != RES_OK) goto error;
    814       }
    815     }
    816   }
    817 
    818 exit:
    819   return res;
    820 error:
    821   goto exit;
    822 }
    823