solstice

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

solparser_entity.c (15194B)


      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 #include "solparser_c.h"
     18 
     19 /*******************************************************************************
     20  * Helper functions
     21  ******************************************************************************/
     22 static res_T
     23 entity_register_name
     24   (struct solparser* parser,
     25    const yaml_node_t* entity,
     26    struct htable_str2sols* htable,
     27    const size_t isolent)
     28 {
     29   struct solparser_entity* solent;
     30   size_t* pisolent;
     31   res_T res = RES_OK;
     32   ASSERT(parser && htable);
     33   ASSERT(isolent < darray_entity_size_get(&parser->entities));
     34 
     35   solent = darray_entity_data_get(&parser->entities) + isolent;
     36 
     37   pisolent = htable_str2sols_find(htable, &solent->name);
     38   if(pisolent) {
     39     log_err(parser, entity,
     40       "an entity with the name `%s' is already defined in the current context.\n",
     41       str_cget(&solent->name));
     42     return RES_BAD_ARG;
     43   }
     44 
     45   res = htable_str2sols_set(htable, &solent->name, &isolent);
     46   if(res != RES_OK) {
     47     log_err(parser, entity, "could not register the entity.\n");
     48     return res;
     49   }
     50   return RES_OK;
     51 }
     52 
     53 static res_T
     54 anchor_register_name
     55   (struct solparser* parser,
     56    const yaml_node_t* anchor,
     57    struct htable_str2sols* htable,
     58    const size_t isolanchor)
     59 {
     60   struct solparser_anchor* solanchor;
     61   size_t* pisolanchor;
     62   res_T res = RES_OK;
     63   ASSERT(parser && htable);
     64   ASSERT(isolanchor < darray_anchor_size_get(&parser->anchors));
     65 
     66   solanchor = darray_anchor_data_get(&parser->anchors) + isolanchor;
     67 
     68   pisolanchor = htable_str2sols_find(htable, &solanchor->name);
     69   if(pisolanchor) {
     70     log_err(parser, anchor,
     71       "an anchor with the name `%s' is already defined in the cunrrent context.\n",
     72       str_cget(&solanchor->name));
     73     return RES_BAD_ARG;
     74   }
     75 
     76   res = htable_str2sols_set(htable, &solanchor->name, &isolanchor);
     77   if(res != RES_OK) {
     78     log_err(parser, anchor, "could not register the anchor.\n");
     79     return res;
     80   }
     81   return RES_OK;
     82 }
     83 
     84 static res_T
     85 parse_identifier_string
     86   (struct solparser* parser,
     87    yaml_node_t* name,
     88    struct str* str)
     89 {
     90   res_T res = RES_OK;
     91   ASSERT(parser && name && str);
     92 
     93   res = parse_string(parser, name, str);
     94   if(res != RES_OK) goto error;
     95 
     96   if(strchr(str_cget(str), '.')) {
     97     log_err(parser, name, "invalid character `.' in the name `%s'.\n",
     98       str_cget(str));
     99     res = RES_BAD_ARG;
    100     goto error;
    101   }
    102   if(strchr(str_cget(str), ' ') || strchr(str_cget(str), '\t')) {
    103     log_err(parser, name, "invalid space or tabulation in the name `%s'.\n",
    104       str_cget(str));
    105     res = RES_BAD_ARG;
    106     goto error;
    107   }
    108 
    109 exit:
    110   return res;
    111 error:
    112   goto exit;
    113 }
    114 
    115 static res_T
    116 parse_anchor
    117   (struct solparser* parser,
    118    yaml_document_t* doc,
    119    const yaml_node_t* anchor,
    120    struct htable_str2sols* htable,
    121    struct solparser_anchor_id* out_isolanchor)
    122 {
    123   enum { NAME, POSITION };
    124   struct solparser_anchor* solanchor = NULL;
    125   size_t isolanchor = SIZE_MAX;
    126   intptr_t i, n;
    127   int mask = 0; /* Register the parsed attributes */
    128   res_T res = RES_OK;
    129   ASSERT(parser && anchor && out_isolanchor);
    130 
    131   if(anchor->type != YAML_MAPPING_NODE) {
    132     log_err(parser, anchor, "expect an anchor definition.\n");
    133     res = RES_BAD_ARG;
    134     goto error;
    135   }
    136 
    137   /* Allocate the anchor */
    138   isolanchor = darray_anchor_size_get(&parser->anchors);
    139   res = darray_anchor_resize(&parser->anchors, isolanchor + 1);
    140   if(res != RES_OK) {
    141     log_err(parser, anchor, "could not allocate the anchor.\n");
    142     goto error;
    143   }
    144   solanchor = darray_anchor_data_get(&parser->anchors) + isolanchor;
    145 
    146   n = anchor->data.mapping.pairs.top - anchor->data.mapping.pairs.start;
    147   FOR_EACH(i, 0, n) {
    148     yaml_node_t* key;
    149     yaml_node_t* val;
    150 
    151     key = yaml_document_get_node(doc, anchor->data.mapping.pairs.start[i].key);
    152     val = yaml_document_get_node(doc, anchor->data.mapping.pairs.start[i].value);
    153     if(key->type != YAML_SCALAR_NODE) {
    154       log_err(parser, key, "expect an anchor attribute.\n");
    155       res = RES_BAD_ARG;
    156       goto error;
    157     }
    158 
    159     #define SETUP_MASK(Flag, Name) {                                           \
    160       if(mask & BIT(Flag)) {                                                   \
    161         log_err(parser, key, "the anchor "Name" is already defined.\n");       \
    162         res = RES_BAD_ARG;                                                     \
    163         goto error;                                                            \
    164       }                                                                        \
    165       mask |= BIT(Flag);                                                       \
    166     } (void)0
    167     if(!strcmp((char*)key->data.scalar.value, "name")) {
    168       SETUP_MASK(NAME, "name");
    169       res = parse_identifier_string(parser, val, &solanchor->name);
    170     } else if(!strcmp((char*)key->data.scalar.value, "position")) {
    171       SETUP_MASK(POSITION, "position description");
    172       res = parse_real3(parser, doc, val, -DBL_MAX, DBL_MAX, solanchor->position);
    173     } else if(!strcmp((char*) key->data.scalar.value, "hyperboloid_image_focals")) {
    174       struct solparser_hyperboloid_focals focals;
    175       SETUP_MASK(POSITION, "position description");
    176       res = parse_focals_description(parser, doc, val, &focals);
    177       if(res != RES_OK) goto error;
    178       d3(solanchor->position, 0, 0, focals.image);
    179     } else {
    180       log_err(parser, key, "unknown anchor parameter `%s'.\n",
    181         key->data.scalar.value);
    182       res = RES_BAD_ARG;
    183       goto error;
    184     }
    185     if(res != RES_OK) {
    186       log_node(parser, key);
    187       goto error;
    188     }
    189     #undef SETUP_MASK
    190   }
    191 
    192   #define CHECK_PARAM(Flag, Name)                                              \
    193     if(!(mask & BIT(Flag))) {                                                  \
    194       log_err(parser, anchor, "the anchor "Name" is missing.\n");              \
    195       res = RES_BAD_ARG;                                                       \
    196       goto error;                                                              \
    197     } (void)0
    198   CHECK_PARAM(NAME, "name");
    199   CHECK_PARAM(POSITION, "position description");
    200   #undef CHECK_PARAM
    201 
    202   res = anchor_register_name(parser, anchor, htable, isolanchor);
    203   if(res != RES_OK) goto error;
    204 
    205 exit:
    206   out_isolanchor->i = isolanchor;
    207   return res;
    208 error:
    209   if(solanchor) {
    210     darray_anchor_pop_back(&parser->anchors);
    211     isolanchor = SIZE_MAX;
    212   }
    213   goto exit;
    214 }
    215 
    216 static res_T
    217 parse_anchors
    218   (struct solparser* parser,
    219    yaml_document_t* doc,
    220    const yaml_node_t* anchors,
    221    struct htable_str2sols* htable,
    222    struct darray_anchor_id* solanchors)
    223 {
    224   intptr_t i, n;
    225   res_T res = RES_OK;
    226   ASSERT(parser && anchors);
    227 
    228   if(anchors->type != YAML_SEQUENCE_NODE) {
    229     log_err(parser, anchors, "expect a list of anchors.\n");
    230     res = RES_BAD_ARG;
    231     goto error;
    232   }
    233 
    234   n = anchors->data.sequence.items.top - anchors->data.sequence.items.start;
    235   res = darray_anchor_id_resize(solanchors, (size_t)n);
    236   if(res != RES_OK) {
    237     log_err(parser, anchors, "could not allocate the anchors list.\n");
    238     goto error;
    239   }
    240 
    241   FOR_EACH(i, 0, n) {
    242     struct solparser_anchor_id* anchor_id;
    243     yaml_node_t* anchor;
    244 
    245     anchor_id = darray_anchor_id_data_get(solanchors)+i;
    246     anchor = yaml_document_get_node(doc, anchors->data.sequence.items.start[i]);
    247     res = parse_anchor(parser, doc, anchor, htable, anchor_id);
    248     if(res != RES_OK) goto error;
    249   }
    250 exit:
    251   return res;
    252 error:
    253   goto exit;
    254 }
    255 
    256 static res_T
    257 parse_children
    258   (struct solparser* parser,
    259    yaml_document_t* doc,
    260    const yaml_node_t* children,
    261    struct htable_str2sols* htable,
    262    struct darray_child_id* entities)
    263 {
    264   intptr_t i, n;
    265   res_T res = RES_OK;
    266   ASSERT(parser && children && htable && entities);
    267 
    268   if(children->type != YAML_SEQUENCE_NODE) {
    269     log_err(parser, children, "expect a list of entities.\n");
    270     res = RES_BAD_ARG;
    271     goto error;
    272   }
    273 
    274   n = children->data.sequence.items.top - children->data.sequence.items.start;
    275   res = darray_child_id_resize(entities, (size_t)n);
    276   if(res != RES_OK) {
    277     log_err(parser, children, "could not allocate the children list.\n");
    278     goto error;
    279   }
    280 
    281   FOR_EACH(i, 0, n) {
    282     struct solparser_entity_id* entity_id = darray_child_id_data_get(entities) + i;
    283     yaml_node_t* child;
    284 
    285     child = yaml_document_get_node(doc, children->data.sequence.items.start[i]);
    286     res = parse_entity(parser, doc, child, htable, entity_id);
    287     if(res != RES_OK) goto error;
    288   }
    289 
    290 exit:
    291   return res;
    292 error:
    293   darray_child_id_clear(entities);
    294   goto exit;
    295 }
    296 
    297 
    298 /*******************************************************************************
    299  * Local function
    300  ******************************************************************************/
    301 res_T
    302 parse_entity
    303   (struct solparser* parser,
    304    yaml_document_t* doc,
    305    yaml_node_t* entity,
    306    struct htable_str2sols* htable,
    307    struct solparser_entity_id* out_isolent)
    308 {
    309   enum { ANCHORS, CHILDREN, DATA, NAME, TRANSFORM, PRIMARY };
    310   struct solparser_entity solent;
    311   struct solparser_entity* psolent;
    312   size_t isolent = SIZE_MAX;
    313   intptr_t i, n;
    314   int mask = 0; /* Register the parsed attributes */
    315   res_T res = RES_OK;
    316   ASSERT(doc && entity && htable && out_isolent);
    317 
    318   solparser_entity_init(parser->allocator, &solent);
    319 
    320   if(entity->type != YAML_MAPPING_NODE) {
    321     log_err(parser, entity, "expect an entity definition.\n");
    322     res = RES_BAD_ARG;
    323     goto error;
    324   }
    325 
    326   /* Allocate the entity but *DO NOT* retrieve a pointer onto it since the
    327    * allocation of its children may update its memory location. Use the "on
    328    * stack" entity `solent' instead. */
    329   isolent = darray_entity_size_get(&parser->entities);
    330   res = darray_entity_resize(&parser->entities, isolent + 1);
    331   if(res != RES_OK) {
    332     log_err(parser, entity, "could not allocate the entity.\n");
    333     goto error;
    334   }
    335 
    336   n = entity->data.mapping.pairs.top - entity->data.mapping.pairs.start;
    337   FOR_EACH(i, 0, n) {
    338     yaml_node_t* key;
    339     yaml_node_t* val;
    340 
    341     key = yaml_document_get_node(doc, entity->data.mapping.pairs.start[i].key);
    342     val = yaml_document_get_node(doc, entity->data.mapping.pairs.start[i].value);
    343     if(key->type != YAML_SCALAR_NODE) {
    344       log_err(parser, key, "expect an entity attribute.\n");
    345       res = RES_BAD_ARG;
    346       goto error;
    347     }
    348 
    349     #define SETUP_MASK(Flag, Name) {                                           \
    350       if(mask & BIT(Flag)) {                                                   \
    351         log_err(parser, key,                                                   \
    352           "the entity "Name" is already defined.\n");                          \
    353         res = RES_BAD_ARG;                                                     \
    354         goto error;                                                            \
    355       }                                                                        \
    356       mask |= BIT(Flag);                                                       \
    357     } (void)0
    358     if(!strcmp((char*)key->data.scalar.value, "anchors")) {
    359       SETUP_MASK(ANCHORS, "anchors");
    360       res = parse_anchors
    361         (parser, doc, val, &solent.str2anchors, &solent.anchors);
    362     } else if(!strcmp((char*)key->data.scalar.value, "children")) {
    363       SETUP_MASK(CHILDREN, "children");
    364       res = parse_children
    365         (parser, doc, val, &solent.str2children, &solent.children);
    366     } else if(!strcmp((char*)key->data.scalar.value, "geometry")) {
    367       SETUP_MASK(DATA, "data");
    368       solent.type = SOLPARSER_ENTITY_GEOMETRY;
    369       res = parse_geometry(parser, doc, val, &solent.data.geometry);
    370     } else if(!strcmp((char*)key->data.scalar.value, "name")) {
    371       SETUP_MASK(NAME, "name");
    372       res = parse_identifier_string(parser, val, &solent.name);
    373       if(!strcmp(str_get(&solent.name), "self")) {
    374         /* Self is a reserved keyword */
    375         log_err(parser, key, "Reserved keywords cannot be used as names: %s.\n",
    376           str_get(&solent.name));
    377         res = RES_BAD_ARG;
    378         goto error;
    379       }
    380     } else if(!strcmp((char*)key->data.scalar.value, "x_pivot")) {
    381       SETUP_MASK(DATA, "data");
    382       solent.type = SOLPARSER_ENTITY_X_PIVOT;
    383       res = parse_x_pivot(parser, doc, val, &solent.data.x_pivot);
    384     } else if(!strcmp((char*) key->data.scalar.value, "zx_pivot")) {
    385       SETUP_MASK(DATA, "data");
    386       solent.type = SOLPARSER_ENTITY_ZX_PIVOT;
    387       res = parse_zx_pivot(parser, doc, val, &solent.data.zx_pivot);
    388     } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
    389       SETUP_MASK(TRANSFORM, "transform");
    390       res = parse_transform
    391         (parser, doc, val, solent.translation, solent.rotation);
    392     } else if(!strcmp((char*) key->data.scalar.value, "primary")) {
    393       long tmp;
    394       SETUP_MASK(PRIMARY, "primary");
    395       /* FIXME: add NONE/ FRONT / BACK / FRONT_AND_BACK qualifier
    396        * to avoid a misunderstanding about shadows results */
    397       res = parse_integer(parser, val, 0, 1, &tmp);
    398       solent.primary = (int)tmp;
    399     } else {
    400       log_err(parser, key, "unknown entity parameter `%s'.\n",
    401         key->data.scalar.value);
    402       res = RES_BAD_ARG;
    403       goto error;
    404     }
    405     if(res != RES_OK) {
    406       log_node(parser, key);
    407       goto error;
    408     }
    409     #undef SETUP_MASK
    410   }
    411 
    412   if(!(mask & BIT(DATA))) {
    413     solent.type = SOLPARSER_ENTITY_EMPTY;
    414   }
    415 
    416   #define CHECK_PARAM(Flag, Name)                                              \
    417     if(!(mask & BIT(Flag))) {                                                  \
    418       log_err(parser, entity, "the entity "Name" parameter is missing.\n");    \
    419       res = RES_BAD_ARG;                                                       \
    420       goto error;                                                              \
    421     } (void)0
    422   CHECK_PARAM(NAME, "name");
    423   if(solent.type == SOLPARSER_ENTITY_GEOMETRY) {
    424     CHECK_PARAM(PRIMARY, "primary");
    425   } else if(mask & BIT(PRIMARY)) {
    426     log_err(parser, entity,
    427       "the entity primary parameter is invalid in this context.\n");
    428     res = RES_BAD_ARG;
    429     goto error;
    430   }
    431   #undef CHECK_PARAM
    432 
    433   psolent = darray_entity_data_get(&parser->entities) + isolent;
    434   res = solparser_entity_copy_and_clear(psolent, &solent);
    435   if(res != RES_OK) {
    436     log_err(parser, entity,
    437       "could not copy the loaded entity into the parser data structures.\n");
    438     goto error;
    439   }
    440   res = entity_register_name(parser, entity, htable, isolent);
    441   if(res != RES_OK) goto error;
    442 
    443 exit:
    444   solparser_entity_release(&solent);
    445   out_isolent->i = isolent;
    446   return res;
    447 error:
    448   if(isolent != SIZE_MAX) {
    449     htable_str2sols_erase(htable, &solent.name);
    450     darray_entity_pop_back(&parser->entities);
    451     isolent = SIZE_MAX;
    452   }
    453   goto exit;
    454 }
    455 
    456