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_args.c (18109B)


      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 2
     18 
     19 #include "solstice_args.h"
     20 #include "solstice_version.h"
     21 
     22 #include <rsys/cstr.h>
     23 #include <rsys/double3.h>
     24 #include <rsys/stretchy_array.h>
     25 
     26 #ifdef COMPILER_CL
     27   #include <getopt.h>
     28   #define strtok_r strtok_s
     29 #else
     30   #include <unistd.h>
     31 #endif
     32 
     33 #include <string.h>
     34 
     35 /*******************************************************************************
     36  * Helper functions
     37  ******************************************************************************/
     38 static void
     39 print_help(const char* program)
     40 {
     41   printf(
     42 "Usage: %s [OPTIONS] [FILE]\n"
     43 "Integrate the solar flux in a complex solar facility described in FILE. If not\n"
     44 "define, the solar facility is read from standard input. Refer to solstice(1)\n"
     45 "man page for more informations.\n\n",
     46     program);
     47   printf(
     48 "  -D <dirs>        list of sun directions.\n");
     49   printf(
     50 "  -f               overwrite the output files if they already exist, i.e. the\n"
     51 "                   OUTPUT file and the output RNG state.\n");
     52   printf(
     53 "  -G <rng>         save and restore the state of the random number generator.\n");
     54   printf(
     55 "  -g <dump>        switch in dump geometry mode and configure it.\n");
     56   printf(
     57 "  -h               display this help and exit.\n");
     58   printf(
     59 "  -n SAMPLES       number of Monte Carlo samples. Default is %lu.\n",
     60     SOLSTICE_ARGS_DEFAULT.nexperiments);
     61   printf(
     62 "  -o OUTPUT        write results to OUTPUT. If not defined, write results to\n"
     63 "                   standard output.\n");
     64   printf(
     65 "  -p <dump-paths>  switch in dump radiative paths mode and configure it.\n");
     66   printf(
     67 "  -q               do not print the helper message when no FILE is submitted.\n");
     68   printf(
     69 "  -R RECEIVERS     define the file from which the list of receivers are read.\n");
     70   printf(
     71 "  -r <rendering>   switch in rendering mode and configure it.\n");
     72   printf(
     73 "  -t THREADS       hint on the number of threads to use. By default use as\n"
     74 "                   many threads as CPU cores.\n");
     75   printf(
     76 "  -v               make the program more verbose.\n");
     77   printf(
     78 "  --version        display version information and exit.\n");
     79   printf("\n");
     80   printf(
     81 "Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com).\n"
     82 "Copyright (C) 2016-2018 CNRS.\n"
     83 "Solstice is a free software released under the GNU GPL license, version 3 or\n"
     84 "later. You are free to change or redistribute it under certain conditions\n"
     85 "<http://gnu.org/licenses/gpl.html>.\n");
     86 }
     87 
     88 static res_T
     89 parse_fov(const char* str, double* out_fov)
     90 {
     91   double fov;
     92   res_T res = RES_OK;
     93   ASSERT(str && out_fov);
     94 
     95   res = cstr_to_double(str, &fov);
     96   if(res != RES_OK) {
     97     fprintf(stderr, "Invalid field of view `%s'.\n", str);
     98     return RES_BAD_ARG;
     99   }
    100 
    101   if(fov < 30 || fov > 120) {
    102     fprintf(stderr, "The field of view %g is not in [30, 120].\n", fov);
    103     return RES_BAD_ARG;
    104   }
    105   *out_fov = fov;
    106   return RES_OK;
    107 }
    108 
    109 static res_T
    110 parse_multiple_options
    111   (const char* str,
    112    struct solstice_args* args,
    113    res_T (*parse_option)(const char* str, struct solstice_args* args))
    114 {
    115   char buf[512];
    116   char* tk;
    117   char* ctx;
    118   res_T res = RES_OK;
    119   ASSERT(args && str);
    120 
    121   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    122     fprintf(stderr, "Could not duplicate the option string `%s'.\n", str);
    123     res = RES_MEM_ERR;
    124     goto error;
    125   }
    126   strncpy(buf, str, sizeof(buf));
    127 
    128   tk = strtok_r(buf, ":", &ctx);
    129   do {
    130     res = parse_option(tk, args);
    131     if(res != RES_OK) goto error;
    132     tk = strtok_r(NULL, ":", &ctx);
    133   } while(tk);
    134 
    135 exit:
    136   return res;
    137 error:
    138   goto exit;
    139 }
    140 
    141 static res_T
    142 parse_sun_dir_list(const char* str, struct solstice_args* args)
    143 {
    144   char buf[512];
    145   char* tk;
    146   char* ctx;
    147   size_t len;
    148   res_T res = RES_OK;
    149   ASSERT(str && args);
    150 
    151   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    152     fprintf(stderr,
    153       "Could not duplicate the list of sun directions `%s'.\n", str);
    154     res = RES_MEM_ERR;
    155     goto error;
    156   }
    157   strncpy(buf, str, sizeof(buf));
    158 
    159   tk = strtok_r(buf, ":", &ctx);
    160   while(tk) {
    161     struct solstice_args_spherical spherical;
    162     double tmp[2];
    163 
    164     res = cstr_to_list_double(tk, ',', tmp, &len, 2);
    165     if(res == RES_OK && len != 2) res = RES_BAD_ARG;
    166     if(res != RES_OK) {
    167       fprintf(stderr, "Invalid sun direction `%s'.\n", tk);
    168       goto error;
    169     }
    170 
    171     if(tmp[0] < 0 || tmp[0] >= 360) {
    172       fprintf(stderr,
    173         "Invalid azimuth angle `%g'. Azimuth must be in [0, 360[ degrees.\n",
    174         tmp[0]);
    175       res = RES_BAD_ARG;
    176       goto error;
    177     }
    178     if(tmp[1] < 0 || tmp[1] > 90) {
    179       fprintf(stderr,
    180         "Invalid elevation angle `%g'. Elevation must be in [0, 90] degrees.\n",
    181         tmp[1]);
    182       res = RES_BAD_ARG;
    183       goto error;
    184     }
    185 
    186     spherical.azimuth = tmp[0];
    187     spherical.elevation = tmp[1];
    188     sa_push(args->sun_dirs, spherical);
    189 
    190     tk = strtok_r(NULL, ":", &ctx);
    191   }
    192 
    193   args->nsun_dirs += sa_size(args->sun_dirs);
    194 
    195 exit:
    196   return res;
    197 error:
    198   if(args->sun_dirs) {
    199     sa_release(args->sun_dirs);
    200     args->sun_dirs = NULL;
    201     args->nsun_dirs = 0;
    202   }
    203   goto exit;
    204 }
    205 
    206 static res_T
    207 parse_image_definition
    208   (const char* str,
    209    unsigned long* width,
    210    unsigned long* height)
    211 {
    212   char buf[64];
    213   char* tk;
    214   char* ctx;
    215   res_T res = RES_OK;
    216   ASSERT(str && width && height);
    217 
    218   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    219     fprintf(stderr,
    220       "Could not duplicate the image definition string `%s'.\n", str);
    221     return RES_MEM_ERR;
    222   }
    223   strncpy(buf, str, sizeof(buf));
    224 
    225   tk = strtok_r(buf, "x", &ctx);
    226   res = cstr_to_ulong(tk, width);
    227   if(res == RES_OK && !*width) res = RES_BAD_ARG;
    228   if(res != RES_OK) {
    229     fprintf(stderr, "Invalid image width `%s'\n", tk);
    230     return res;
    231   }
    232 
    233   tk = strtok_r(NULL, "", &ctx);
    234   res = cstr_to_ulong(tk, height);
    235   if(res == RES_OK && !*height) res = RES_BAD_ARG;
    236   if(res != RES_OK) {
    237     fprintf(stderr, "Invalid image height `%s'\n", tk);
    238     return res;
    239   }
    240 
    241   return res;
    242 }
    243 
    244 static res_T
    245 parse_render_mode(const char* str, enum solstice_args_render_mode* mode)
    246 {
    247   res_T res = RES_OK;
    248   ASSERT(str && mode);
    249 
    250   if(!strcmp(str, "draft")) {
    251     *mode = SOLSTICE_ARGS_RENDER_DRAFT;
    252   } else if(!strcmp(str, "pt")) {
    253     *mode = SOLSTICE_ARGS_RENDER_PATH_TRACING;
    254   } else {
    255     fprintf(stderr, "Invalid render mode `%s'.\n", str);
    256     res = RES_BAD_ARG;
    257     goto error;
    258   }
    259 exit:
    260   return res;
    261 error:
    262   goto exit;
    263 }
    264 
    265 static res_T
    266 parse_rendering_option(const char* str, struct solstice_args* args)
    267 {
    268   char buf[128];
    269   char* key;
    270   char* val;
    271   char* ctx;
    272   size_t len;
    273   res_T res = RES_OK;
    274   ASSERT(str && args);
    275 
    276   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    277     fprintf(stderr,
    278       "Could not duplicate the rendering option string `%s'\n", str);
    279     res = RES_MEM_ERR;
    280     goto error;
    281   }
    282   strncpy(buf, str, sizeof(buf));
    283 
    284   key = strtok_r(buf, "=", &ctx);
    285   val = strtok_r(NULL, "", &ctx);
    286 
    287   if(!val) {
    288     fprintf(stderr, "Missing a value to the rendering option `%s'.\n", key);
    289     res = RES_BAD_ARG;
    290     goto error;
    291   }
    292 
    293   if(!strcmp(key, "fov")) { /* Camera horizontal field of view in degrees */
    294     res = parse_fov(val, &args->camera.fov_x);
    295     if(res != RES_OK) goto error;
    296   } else if(!strcmp(key, "img")) { /* Image definition */
    297     res = parse_image_definition(val, &args->img.width, &args->img.height);
    298     if(res != RES_OK) goto error;
    299   } else if(!strcmp(key, "pos")) { /* Camera position */
    300     res = cstr_to_list_double(val, ',', args->camera.pos, &len, 3);
    301     if(res == RES_OK && len != 3) res = RES_BAD_ARG;
    302     if(res != RES_OK ) {
    303       fprintf(stderr, "Invalid camera position `%s'.\n", val);
    304       goto error;
    305     }
    306     args->camera.auto_look_at = 0; /* Disable auto look at */
    307   } else if(!strcmp(key, "rmode")) { /* Render mode */
    308     res = parse_render_mode(val, &args->render_mode);
    309     if(res != RES_OK) goto error;
    310   } else if(!strcmp(key, "spp")) { /*# Samples per pixel */
    311     res = cstr_to_uint(val, &args->img.spp);
    312     if(res == RES_OK && !args->img.spp) res = RES_BAD_ARG;
    313     if(res != RES_OK) {
    314       fprintf(stderr, "Invalid number of samples per pixel `%s'.\n", val);
    315       goto error;
    316     }
    317   } else if(!strcmp(key, "tgt")) { /* Camera target */
    318     res = cstr_to_list_double(val, ',', args->camera.tgt, &len, 3);
    319     if(res == RES_OK && len != 3) res = RES_BAD_ARG;
    320     if(res != RES_OK) {
    321       fprintf(stderr, "Invalid camera target `%s'.\n", val);
    322       goto error;
    323     }
    324     args->camera.auto_look_at = 0; /* Disable auto look at */
    325   } else if(!strcmp(key, "up")) { /* Camera up vector */
    326     res = cstr_to_list_double(val, ',', args->camera.up, &len, 3);
    327     if(res == RES_OK && len != 3) res = RES_BAD_ARG;
    328     if(res != RES_OK) {
    329       fprintf(stderr, "Invalid camera up vector `%s'.\n", val);
    330       goto error;
    331     }
    332   } else {
    333     fprintf(stderr, "Invalid rendering option `%s'.\n", key);
    334     res = RES_BAD_ARG;
    335     goto error;
    336   }
    337 
    338 exit:
    339   return res;
    340 error:
    341   goto exit;
    342 }
    343 
    344 static res_T
    345 parse_dump_format(const char* str, enum solstice_args_dump_format* fmt)
    346 {
    347   res_T res = RES_OK;
    348   ASSERT(str && fmt);
    349 
    350   if(!strcmp(str, "obj")) {
    351     *fmt = SOLSTICE_ARGS_DUMP_OBJ;
    352   } else {
    353     fprintf(stderr, "Invalid dump format `%s'.\n", str);
    354     res = RES_BAD_ARG;
    355     goto error;
    356   }
    357 
    358 exit:
    359   return res;
    360 error:
    361   goto exit;
    362 }
    363 
    364 static res_T
    365 parse_dump_split_mode(const char* str, enum solstice_args_dump_split_mode* mode)
    366 {
    367   res_T res = RES_OK;
    368   ASSERT(str && mode);
    369 
    370   if(!strcmp(str, "geometry")) {
    371     *mode = SOLSTICE_ARGS_DUMP_SPLIT_GEOMETRY;
    372   } else if(!strcmp(str, "none")) {
    373     *mode = SOLSTICE_ARGS_DUMP_SPLIT_NONE;
    374   } else if(!strcmp(str, "object")) {
    375     *mode = SOLSTICE_ARGS_DUMP_SPLIT_OBJECT;
    376   } else {
    377     fprintf(stderr, "Invalid dump split mode `%s'.\n", str);
    378     res = RES_BAD_ARG;
    379     goto error;
    380   }
    381 exit:
    382   return res;
    383 error:
    384   goto exit;
    385 }
    386 
    387 static res_T
    388 parse_dump_option(const char* str, struct solstice_args* args)
    389 {
    390   char buf[128];
    391   char* key;
    392   char* val;
    393   char* ctx;
    394   res_T res = RES_OK;
    395   ASSERT(str && args);
    396 
    397   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    398     fprintf(stderr,
    399       "Could not duplicate the dump geometry option string `%s'\n", str);
    400     res = RES_MEM_ERR;
    401     goto error;
    402   }
    403   strncpy(buf, str, sizeof(buf));
    404 
    405   key = strtok_r(buf, "=", &ctx);
    406   val = strtok_r(NULL, "", &ctx);
    407 
    408   if(!val) {
    409     fprintf(stderr, "Missing a value to the dump option `%s'.\n", key);
    410     res = RES_BAD_ARG;
    411     goto error;
    412   }
    413 
    414   if(!strcmp(key, "format")) {
    415     res = parse_dump_format(val, &args->dump_format);
    416   } else if(!strcmp(key, "split")) {
    417     res = parse_dump_split_mode(val, &args->dump_split_mode);
    418   } else {
    419     fprintf(stderr, "Invalid dump option `%s'.\n", val);
    420     res = RES_BAD_ARG;
    421     goto error;
    422   }
    423   if(res != RES_OK) goto error;
    424 
    425 exit:
    426   return res;
    427 error:
    428   goto exit;
    429 }
    430 
    431 static res_T
    432 parse_dump_paths_option(const char* str, struct solstice_args* args)
    433 {
    434   char buf[128];
    435   char* key;
    436   char* val;
    437   char* ctx;
    438   res_T res = RES_OK;
    439   ASSERT(str && args);
    440 
    441   if(!strcmp(str, "default")) {
    442     args->infinite_ray_length = SOLSTICE_ARGS_DEFAULT.infinite_ray_length;
    443     args->sun_ray_length = SOLSTICE_ARGS_DEFAULT.sun_ray_length;
    444     goto exit;
    445   }
    446 
    447   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    448     fprintf(stderr,
    449 "Could not duplicate the dump radiative paths option string `%s'.\n", str);
    450     res = RES_MEM_ERR;
    451     goto error;
    452   }
    453 
    454   strncpy(buf, str, sizeof(buf));
    455 
    456   key = strtok_r(buf, "=", &ctx);
    457   val = strtok_r(NULL, "", &ctx);
    458 
    459   if(!val) {
    460     fprintf(stderr,
    461       "Missing a value to the dump radiative paths option `%s'.\n", key);
    462     res = RES_BAD_ARG;
    463     goto error;
    464   }
    465 
    466   if(!strcmp(key, "irlen")) {
    467     res = cstr_to_double(val, &args->infinite_ray_length);
    468     if(res != RES_OK) {
    469       fprintf(stderr, "Invalid infinite ray length `%s'.\n", val);
    470       goto error;
    471     }
    472   } else if(!strcmp(key, "srlen")) {
    473     res = cstr_to_double(val, &args->sun_ray_length);
    474     if(res != RES_OK) {
    475       fprintf(stderr, "Invalid sun ray length `%s'.\n", val);
    476       goto error;
    477     }
    478   } else {
    479     fprintf(stderr, "Invalid dump radiative paths option `%s'.\n", val);
    480     res = RES_BAD_ARG;
    481     goto error;
    482   }
    483   if(res != RES_OK) goto error;
    484 
    485 exit:
    486   return res;
    487 error:
    488   goto exit;
    489 }
    490 
    491 static res_T
    492 parse_rng_option(const char* str, struct solstice_args* args)
    493 {
    494   char buf[128];
    495   char* key;
    496   char* val;
    497   char* ctx;
    498   size_t len;
    499   res_T res = RES_OK;
    500   ASSERT(str && args);
    501 
    502   if(strlen(str) >= sizeof(buf)-1/*NULL char*/) {
    503     fprintf(stderr,
    504       "Could not duplicate the RNG option string `%s'\n", str);
    505     res = RES_MEM_ERR;
    506     goto error;
    507   }
    508   strncpy(buf, str, sizeof(buf));
    509 
    510   key = strtok_r(buf, "=", &ctx);
    511   val = strtok_r(NULL, "", &ctx);
    512 
    513   if(!val) {
    514     fprintf(stderr, "Missing a value to the RNG option `%s'.\n", key);
    515     res = RES_BAD_ARG;
    516     goto error;
    517   }
    518 
    519   if(!strcmp(key, "istate")) { /* Input state */
    520     len = strlen(val);
    521     args->rng_state_input_filename = mem_calloc(len+1, sizeof(char));
    522     if(!args->rng_state_input_filename) { res = RES_MEM_ERR; goto error; }
    523     strcpy(args->rng_state_input_filename, val);
    524   } else if(!strcmp(key, "ostate")) { /* Output state */
    525     len = strlen(val);
    526     args->rng_state_output_filename = mem_calloc(len+1, sizeof(char));
    527     if(!args->rng_state_output_filename) { res = RES_MEM_ERR; goto error; }
    528     strcpy(args->rng_state_output_filename, val);
    529   } else {
    530     fprintf(stderr, "Invalid RNG option `%s'.\n", key);
    531     res = RES_BAD_ARG;
    532     goto error;
    533   }
    534 
    535 exit:
    536   return res;
    537 error:
    538   goto exit;
    539 }
    540 
    541 /*******************************************************************************
    542  * Local function
    543  ******************************************************************************/
    544 res_T
    545 solstice_args_init(struct solstice_args* args, const int argc, char** argv)
    546 {
    547   int opt;
    548   int i;
    549   res_T res = RES_OK;
    550   ASSERT(args && argc && argv);
    551 
    552   *args = SOLSTICE_ARGS_DEFAULT;
    553 
    554   FOR_EACH(i, 1, argc) {
    555     if(!strcmp(argv[i], "--version")) {
    556       printf("Solstice %d.%d.%d\n",
    557         SOLSTICE_VERSION_MAJOR,
    558         SOLSTICE_VERSION_MINOR,
    559         SOLSTICE_VERSION_PATCH);
    560       args->quit = 1;
    561       goto exit;
    562     }
    563   }
    564 
    565   optind = 0;
    566   while((opt = getopt(argc, argv, "D:fG:g:hn:o:p:qR:r:t:v")) != -1) {
    567     switch(opt) {
    568       case 'D': /* Sun directions */
    569         res = parse_sun_dir_list(optarg, args);
    570         break;
    571       case 'f': args->force_overwriting = 1; break;
    572       case 'h': /* Print short help and exit */
    573         print_help(argv[0]);
    574         solstice_args_release(args);
    575         args->quit = 1;
    576         goto exit;
    577       case 'n': /* Define the number of experiments */
    578         res = cstr_to_ulong(optarg, &args->nexperiments);
    579         if(res == RES_OK && !args->nexperiments) res = RES_BAD_ARG;
    580         break;
    581       case 'G': /* Setup the random number generator */
    582         res = parse_multiple_options(optarg, args, parse_rng_option);
    583         break;
    584       case 'g': /* Switch in dump geometry mode and configure it */
    585         res = parse_multiple_options(optarg, args, parse_dump_option);
    586         if(res == RES_OK && args->dump_format == SOLSTICE_ARGS_DUMP_NONE) {
    587           fprintf(stderr, "%s: missing a dump format -- `%s'.\n",
    588             argv[0], optarg);
    589           res = RES_BAD_ARG;
    590         }
    591         break;
    592       case 'o': args->output_filename = optarg; break;
    593       case 'p': /* Switch in dump radiative paths mode and configure it */
    594         args->dump_paths = 1;
    595         res = parse_multiple_options(optarg, args, parse_dump_paths_option);
    596         break;
    597       case 'q': args->quiet = 1; break;
    598       case 'R': args->receivers_filename = optarg; break;
    599       case 'r':  /* Switch in rendering mode and configure it */
    600         args->rendering = 1;
    601         res = parse_multiple_options(optarg, args, parse_rendering_option);
    602         break;
    603       case 't': /* Submit an hint on the number of threads to use */
    604         res = cstr_to_uint(optarg, &args->nthreads);
    605         if(res == RES_OK && !args->nthreads) res = RES_BAD_ARG;
    606         break;
    607       case 'v': args->verbose = 1; break;
    608       default: res = RES_BAD_ARG; break;
    609     }
    610     if(res != RES_OK) {
    611       if(optarg) {
    612         fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n",
    613           argv[0], optarg, opt);
    614       }
    615       goto error;
    616     }
    617   }
    618 
    619   if(!args->rendering
    620   && args->dump_format == SOLSTICE_ARGS_DUMP_NONE
    621   && !args->nsun_dirs) {
    622     fprintf(stderr, "Missing sun direction.\n");
    623     res = RES_BAD_ARG;
    624     goto error;
    625   }
    626 
    627   if(args->dump_format != SOLSTICE_ARGS_DUMP_NONE && args->rendering) {
    628     fprintf(stderr, "The '-g' and '-r' options are exclusives.\n");
    629     res = RES_BAD_ARG;
    630     goto error;
    631   }
    632 
    633   if(args->dump_format != SOLSTICE_ARGS_DUMP_NONE && args->dump_paths) {
    634     fprintf(stderr, "The '-g' and '-p' options are exclusives.\n");
    635     res = RES_BAD_ARG;
    636     goto error;
    637   }
    638 
    639   if(args->dump_paths && args->rendering) {
    640     fprintf(stderr, "The '-p' and '-r' options are exclusives.\n");
    641     res = RES_BAD_ARG;
    642     goto error;
    643   }
    644 
    645   if(optind < argc) {
    646     args->input_filename = argv[optind];
    647   }
    648 
    649 exit:
    650   optind = 1;
    651   return res;
    652 error:
    653   solstice_args_release(args);
    654   goto exit;
    655 }
    656 
    657 void
    658 solstice_args_release(struct solstice_args* args)
    659 {
    660   ASSERT(args);
    661   sa_release(args->sun_dirs);
    662   if(args->rng_state_input_filename) mem_rm(args->rng_state_input_filename);
    663   if(args->rng_state_output_filename) mem_rm(args->rng_state_output_filename);
    664   *args = SOLSTICE_ARGS_NULL;
    665 }
    666