star-line

Structure for accelerating line importance sampling
git clone git://git.meso-star.fr/star-line.git
Log | Files | Refs | README | LICENSE

test_sln_tree.c (20684B)


      1 /* Copyright (C) 2022, 2026 |Méso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2026 Université de Lorraine
      3  * Copyright (C) 2022 Centre National de la Recherche Scientifique
      4  * Copyright (C) 2022 Université Paul Sabatier
      5  *
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation, either version 3 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     18 
     19 #include "test_sln_lines.h"
     20 #include "sln.h"
     21 
     22 #include <star/shtr.h>
     23 
     24 #include <rsys/algorithm.h>
     25 #include <rsys/math.h>
     26 #include <rsys/mem_allocator.h>
     27 
     28 /*******************************************************************************
     29  * Helper function
     30  ******************************************************************************/
     31 /* Return the index of the line in the line_list or SIZE_MAX if the line does
     32  * not exist */
     33 static INLINE size_t
     34 find_line
     35   (struct shtr_line_list* line_list,
     36    const struct shtr_line* line)
     37 {
     38   struct shtr_line ln = SHTR_LINE_NULL;
     39   size_t lo, hi, mid;
     40   size_t iline;
     41   size_t nlines;
     42 
     43   CHK(shtr_line_list_get_size(line_list, &nlines) == RES_OK);
     44 
     45   /* Dichotomic search */
     46   lo = 0; hi = nlines -1;
     47   while(lo < hi) {
     48     mid = (lo+hi)/2;
     49 
     50     CHK(shtr_line_list_at(line_list, mid, &ln) == RES_OK);
     51     if(line->wavenumber > ln.wavenumber) {
     52       lo = mid + 1;
     53     } else {
     54       hi = mid;
     55     }
     56   }
     57   iline = lo;
     58 
     59   CHK(shtr_line_list_at(line_list, iline, &ln) == RES_OK);
     60   if(ln.wavenumber != line->wavenumber) return SIZE_MAX;
     61 
     62 
     63   /* Find a line with the same wavenumber as the one searched for and whose
     64    * other member variables are also equal to those of the line searched for */
     65   while(ln.wavenumber == line->wavenumber
     66      && !shtr_line_eq(&ln, line)
     67      && iline < nlines) {
     68     iline += 1;
     69     CHK(shtr_line_list_at(line_list, iline, &ln) == RES_OK);
     70   }
     71 
     72   return shtr_line_eq(&ln, line) ? iline : SIZE_MAX;
     73 }
     74 
     75 /* This test assumes that all the lines contained into the list are
     76  * partitionned in the tree */
     77 static void
     78 check_tree_lines
     79   (const struct sln_tree* tree,
     80    struct shtr_line_list* line_list)
     81 {
     82   #define STACK_SIZE \
     83     ( SLN_TREE_DEPTH_MAX \
     84     * SLN_TREE_ARITY_MAX - 1/*1st child*/ \
     85     + 1/*Dummy node*/)
     86   const struct sln_node* stack[STACK_SIZE] = {NULL};
     87   struct sln_tree_desc desc = SLN_TREE_DESC_NULL;
     88   size_t istack = 0;
     89   const struct sln_node* node = NULL;
     90 
     91   char* found_lines = NULL;
     92   size_t found_nlines = 0;
     93   size_t line_list_sz;
     94 
     95   CHK(shtr_line_list_get_size(line_list, &line_list_sz) == RES_OK);
     96   CHK(sln_tree_get_desc(tree, &desc) == RES_OK);
     97 
     98   CHK(found_lines = mem_calloc(line_list_sz, sizeof(char)));
     99 
    100   stack[istack++] = NULL; /* Dummy node to stop the recursion */
    101 
    102   node = sln_tree_get_root(tree);
    103   while(node) {
    104     struct sln_node_desc node_desc = SLN_NODE_DESC_NULL;
    105     size_t node_nlines = 0;
    106 
    107     CHK(sln_node_get_desc(tree, node, &node_desc) == RES_OK);
    108     node_nlines = node_desc.ilines[1] - node_desc.ilines[0] + 1;
    109 
    110     if(!sln_node_is_leaf(node)) {
    111       unsigned i = 0;
    112       unsigned n = sln_node_get_child_count(tree, node);
    113 
    114       CHK(node_nlines > desc.leaf_nlines);
    115 
    116       FOR_EACH(i, 1, n) {
    117         stack[istack++] = sln_node_get_child(tree, node, i);
    118       }
    119       node = sln_node_get_child(tree, node, 0); /* Handle the child 0 */
    120 
    121     } else {
    122       size_t iline;
    123 
    124       CHK(node_nlines <= desc.leaf_nlines);
    125       FOR_EACH(iline, node_desc.ilines[0], node_desc.ilines[1]+1) {
    126         if(!found_lines[iline]) {
    127           found_nlines += 1;
    128           found_lines[iline] = 1;
    129         }
    130       }
    131 
    132       node = stack[--istack]; /* Pop the next node */
    133     }
    134   }
    135 
    136   /* Check that all lines are found */
    137   CHK(found_nlines == line_list_sz);
    138 
    139   mem_rm(found_lines);
    140   #undef STACK_SIZE
    141 }
    142 
    143 static void
    144 check_mesh_equality(const struct sln_mesh* mesh1, const struct sln_mesh* mesh2)
    145 {
    146   size_t i;
    147   CHK(mesh1 && mesh2);
    148   CHK(mesh1->nvertices == mesh2->nvertices);
    149 
    150   FOR_EACH(i, 0, mesh1->nvertices) {
    151     CHK(mesh1->vertices[i].wavenumber == mesh2->vertices[i].wavenumber);
    152     CHK(mesh1->vertices[i].ka == mesh2->vertices[i].ka);
    153   }
    154 }
    155 
    156 static void
    157 check_node_equality
    158   (const struct sln_tree* tree1,
    159    const struct sln_tree* tree2,
    160    const struct sln_node* node1,
    161    const struct sln_node* node2)
    162 {
    163   struct sln_mesh mesh1 = SLN_MESH_NULL;
    164   struct sln_mesh mesh2 = SLN_MESH_NULL;
    165   struct sln_node_desc desc1 = SLN_NODE_DESC_NULL;
    166   struct sln_node_desc desc2 = SLN_NODE_DESC_NULL;
    167   size_t iline = 0;
    168 
    169   CHK(node1 && node2);
    170   CHK(sln_node_is_leaf(node1) == sln_node_is_leaf(node2));
    171   CHK(sln_node_get_desc(tree1, node1, &desc1) == RES_OK);
    172   CHK(sln_node_get_desc(tree2, node2, &desc2) == RES_OK);
    173 
    174   CHK(desc1.ilines[0] == desc2.ilines[0]);
    175   CHK(desc1.ilines[1] == desc2.ilines[1]);
    176   CHK(desc1.nvertices == desc2.nvertices);
    177   CHK(desc1.nchildren == desc2.nchildren);
    178 
    179   FOR_EACH(iline, desc1.ilines[0], desc1.ilines[1]+1) {
    180     struct sln_line line1 = SLN_LINE_NULL;
    181     struct sln_line line2 = SLN_LINE_NULL;
    182     CHK(sln_tree_get_line(tree1, iline, &line1) == RES_OK);
    183     CHK(sln_tree_get_line(tree2, iline, &line2) == RES_OK);
    184 
    185     CHK(line1.wavenumber == line2.wavenumber);
    186     CHK(line1.profile_factor == line2.profile_factor);
    187     CHK(line1.gamma_d == line2.gamma_d);
    188     CHK(line1.gamma_l == line2.gamma_l);
    189     CHK(line1.molecule_id == line2.molecule_id);
    190   }
    191 
    192   CHK(sln_node_get_mesh(tree1, node1, &mesh1) == RES_OK);
    193   CHK(sln_node_get_mesh(tree2, node2, &mesh2) == RES_OK);
    194   check_mesh_equality(&mesh1, &mesh2);
    195 }
    196 
    197 static void
    198 check_tree_equality
    199   (const struct sln_tree* tree1,
    200    const struct sln_tree* tree2)
    201 {
    202   #define STACK_SIZE \
    203     (  SLN_TREE_DEPTH_MAX \
    204     * (SLN_TREE_ARITY_MAX-1/*1st child*/) * 2/*#trees*/ \
    205     + 1/*Dummy node*/)
    206 
    207   const struct sln_node* stack[STACK_SIZE] = {NULL};
    208   int istack = 0;
    209 
    210   struct sln_tree_desc desc1 = SLN_TREE_DESC_NULL;
    211   struct sln_tree_desc desc2 = SLN_TREE_DESC_NULL;
    212   const struct sln_node* node1 = NULL;
    213   const struct sln_node* node2 = NULL;
    214 
    215   CHK(sln_tree_get_desc(tree1, &desc1) == RES_OK);
    216   CHK(sln_tree_get_desc(tree2, &desc2) == RES_OK);
    217   CHK(desc1.leaf_nlines == desc2.leaf_nlines);
    218   CHK(desc1.mesh_decimation_err == desc2.mesh_decimation_err);
    219   CHK(desc1.mesh_type == desc2.mesh_type);
    220   CHK(desc1.line_profile == desc2.line_profile);
    221   CHK(desc1.pressure == desc2.pressure);
    222   CHK(desc1.temperature == desc2.temperature);
    223   CHK(desc1.depth == desc2.depth);
    224   CHK(desc1.nlines == desc2.nlines);
    225   CHK(desc1.nvertices == desc2.nvertices);
    226   CHK(desc1.nnodes == desc2.nnodes);
    227   CHK(desc1.arity == desc2.arity);
    228 
    229   stack[istack++] = NULL;
    230   stack[istack++] = NULL;
    231 
    232   node1 = sln_tree_get_root(tree1);
    233   node2 = sln_tree_get_root(tree2);
    234 
    235   while(node1 || node2) {
    236     CHK((!node1 && !node2) || (node1 && node2));
    237     check_node_equality(tree1, tree2, node1, node2);
    238 
    239     if(sln_node_is_leaf(node1)) {
    240       node2 = stack[--istack];
    241       node1 = stack[--istack];
    242     } else {
    243       unsigned i = 0;
    244       unsigned n = sln_node_get_child_count(tree1, node1);
    245 
    246       FOR_EACH(i, 1, n) {
    247         stack[istack++] = sln_node_get_child(tree1, node1, i);
    248         stack[istack++] = sln_node_get_child(tree2, node2, i);
    249       }
    250       node1 = sln_node_get_child(tree1, node1, 0);
    251       node2 = sln_node_get_child(tree2, node2, 0);
    252     }
    253   }
    254 
    255   #undef STACK_SIZE
    256 }
    257 
    258 static void
    259 check_node_value(const struct sln_tree* tree, const struct sln_node* node)
    260 {
    261   struct sln_node_desc desc = SLN_NODE_DESC_NULL;
    262   struct sln_line line = SLN_LINE_NULL;
    263   size_t iline = 0;
    264   double ka_ref = 0;
    265   double ka_node = 0;
    266   double nu = 0;
    267 
    268   CHK(sln_node_get_desc(tree, node, &desc) == RES_OK);
    269 
    270   CHK(sln_tree_get_line(tree, desc.ilines[0], &line) == RES_OK);
    271   nu  = line.wavenumber + 10;
    272   CHK(sln_tree_get_line(tree, desc.ilines[1], &line) == RES_OK);
    273   nu += line.wavenumber - 10;
    274   nu *= 0.5;
    275 
    276   ka_node = sln_node_eval(tree, node, nu);
    277   FOR_EACH(iline, desc.ilines[0], desc.ilines[1]+1/*inclusive*/) {
    278     CHK(sln_tree_get_line(tree, iline, &line) == RES_OK);
    279     ka_ref += sln_line_eval(tree, &line, nu);
    280   }
    281 
    282   CHK(eq_eps(ka_node, ka_ref, ka_ref*1e-6));
    283 }
    284 
    285 static void
    286 test_tree
    287   (struct sln_device* sln,
    288    const struct sln_tree_create_args* tree_args_in,
    289    struct shtr_line_list* line_list)
    290 {
    291   struct sln_tree_create_args tree_args = SLN_TREE_CREATE_ARGS_DEFAULT;
    292   struct sln_tree_desc desc = SLN_TREE_DESC_NULL;
    293   struct sln_node_desc node_desc = SLN_NODE_DESC_NULL;
    294   struct sln_mesh mesh = SLN_MESH_NULL;
    295   struct sln_tree* tree = NULL;
    296   const struct sln_node* node = NULL;
    297   struct sln_line line = SLN_LINE_NULL;
    298   size_t nlines = 0;
    299   size_t nleaf = 0;
    300   unsigned depth = 0;
    301 
    302   CHK(sln && tree_args_in && line_list);
    303   tree_args = *tree_args_in;
    304 
    305   CHK(sln_tree_create(NULL, &tree_args, &tree) == RES_BAD_ARG);
    306   CHK(sln_tree_create(sln, NULL, &tree) == RES_BAD_ARG);
    307   CHK(sln_tree_create(sln, &tree_args, NULL) == RES_BAD_ARG);
    308   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_OK);
    309 
    310   CHK(shtr_line_list_get_size(line_list, &nlines) == RES_OK);
    311 
    312   CHK(sln_tree_get_desc(NULL, &desc) == RES_BAD_ARG);
    313   CHK(sln_tree_get_desc(tree, NULL) == RES_BAD_ARG);
    314   CHK(sln_tree_get_desc(tree, &desc) == RES_OK);
    315 
    316   CHK(desc.leaf_nlines >= 1);
    317   CHK(desc.mesh_decimation_err == tree_args.mesh_decimation_err);
    318   CHK(desc.mesh_type == tree_args.mesh_type);
    319   CHK(desc.line_profile == tree_args.line_profile);
    320   CHK(desc.nlines == nlines);
    321   CHK(desc.nvertices >= 1);
    322   CHK(desc.nnodes >= 1);
    323 
    324   nleaf = (desc.nlines + desc.leaf_nlines-1/*ceil*/)/ desc.leaf_nlines;
    325   depth = (unsigned)ceil(log((double)nleaf)/log((double)desc.arity));
    326   CHK(desc.depth == depth);
    327 
    328   CHK(desc.pressure == tree_args.pressure);
    329   CHK(desc.temperature == tree_args.temperature);
    330   CHK(desc.arity == tree_args.arity);
    331 
    332   CHK(sln_tree_get_line(NULL, 0, &line) == RES_BAD_ARG);
    333   CHK(sln_tree_get_line(tree, nlines, &line) == RES_BAD_ARG);
    334   CHK(sln_tree_get_line(tree, 0, NULL) == RES_BAD_ARG);
    335   CHK(sln_tree_get_line(tree, 0, &line) == RES_OK);
    336   CHK(sln_tree_get_line(tree, nlines-1, &line) == RES_OK);
    337 
    338   CHK(node = sln_tree_get_root(tree));
    339   CHK(node != NULL);
    340 
    341   CHK(sln_node_get_desc(NULL, node, &node_desc) == RES_BAD_ARG);
    342   CHK(sln_node_get_desc(tree, NULL, &node_desc) == RES_BAD_ARG);
    343   CHK(sln_node_get_desc(tree, node, NULL) == RES_BAD_ARG);
    344   CHK(sln_node_get_desc(tree, node, &node_desc) == RES_OK);
    345   CHK(node_desc.ilines[0] == 0);
    346   CHK(node_desc.ilines[1] == desc.nlines - 1);
    347   CHK(node_desc.nvertices >= 1 && node_desc.nvertices <= desc.nvertices);
    348   CHK(node_desc.nchildren <= desc.arity);
    349 
    350   while(!sln_node_is_leaf(node)) {
    351     struct sln_node_desc node_desc_next = SLN_NODE_DESC_NULL;
    352 
    353     CHK(sln_node_get_child_count(tree, node) != 0);
    354     CHK(sln_node_get_child_count(tree, node) <= desc.arity);
    355 
    356     node = sln_node_get_child(tree, node, 0);
    357 
    358     CHK(sln_node_get_desc(tree, node, &node_desc_next) == RES_OK);
    359 
    360     #define NLINES(Desc) ((Desc).ilines[1] - (Desc).ilines[0] + 1)
    361     CHK(NLINES(node_desc_next) >= 1);
    362     CHK(NLINES(node_desc_next) < NLINES(node_desc));
    363     #undef NLINES
    364 
    365     CHK(node_desc_next.nvertices >= 1);
    366     if(sln_node_is_leaf(node)) {
    367       CHK(node_desc_next.nchildren == 0);
    368     } else {
    369       CHK(node_desc_next.nchildren != 0);
    370       CHK(node_desc_next.nchildren <= desc.arity);
    371     }
    372 
    373     node_desc = node_desc_next;
    374   }
    375   CHK(sln_node_get_child_count(tree, node) == 0);
    376 
    377   CHK(node_desc.ilines[1] - node_desc.ilines[0] + 1 <= desc.leaf_nlines);
    378 
    379   CHK(sln_node_get_mesh(NULL, node, &mesh) == RES_BAD_ARG);
    380   CHK(sln_node_get_mesh(tree, NULL, &mesh) == RES_BAD_ARG);
    381   CHK(sln_node_get_mesh(tree, node, NULL) == RES_BAD_ARG);
    382   CHK(sln_node_get_mesh(tree, node, &mesh) == RES_OK);
    383   check_node_value(tree, node);
    384 
    385   CHK(node = sln_tree_get_root(tree));
    386   check_tree_lines(tree, line_list);
    387 
    388   CHK(sln_tree_ref_get(NULL) == RES_BAD_ARG);
    389   CHK(sln_tree_ref_get(tree) == RES_OK);
    390   CHK(sln_tree_ref_put(NULL) == RES_BAD_ARG);
    391   CHK(sln_tree_ref_put(tree) == RES_OK);
    392   CHK(sln_tree_ref_put(tree) == RES_OK);
    393 
    394   tree_args.mesh_decimation_err = -1;
    395   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG);
    396   tree_args.mesh_decimation_err = SLN_TREE_CREATE_ARGS_DEFAULT.mesh_decimation_err;
    397 
    398   tree_args.mesh_type = SLN_MESH_TYPES_COUNT__;
    399   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG);
    400   tree_args.mesh_type = SLN_TREE_CREATE_ARGS_DEFAULT.mesh_type;
    401 
    402   tree_args.line_profile = SLN_LINE_PROFILES_COUNT__;
    403   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG);
    404   tree_args.line_profile = SLN_TREE_CREATE_ARGS_DEFAULT.line_profile;
    405 
    406   tree_args.arity = 1;
    407   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG);
    408   tree_args.arity = SLN_TREE_ARITY_MAX + 1;
    409   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG);
    410   tree_args.arity = SLN_TREE_CREATE_ARGS_DEFAULT.arity;
    411 
    412   tree_args.leaf_nlines = 0;
    413   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG);
    414   tree_args.leaf_nlines = SLN_LEAF_NLINES_MAX + 1;
    415   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG);
    416   tree_args.leaf_nlines = SLN_TREE_CREATE_ARGS_DEFAULT.leaf_nlines;
    417 
    418   tree_args.nthreads_hint = 0;
    419   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG);
    420   tree_args.nthreads_hint = 1;
    421 
    422   /* Check that, as expected, the tree is created correctly using the arguments
    423    * defined so far. This will confirm that the creation failures tested so far
    424    * are indeed due to arguments explicitly defined as incorrect, and not to
    425    * invalid arguments introduced by mistake */
    426   CHK(sln_tree_create(sln, &tree_args, &tree) == RES_OK);
    427 
    428   if(tree_args.arity == 2 && tree_args.collapse_polylines == 0) {
    429     struct sln_tree* tree2 = NULL;
    430 
    431     /* Verify that, when constructing a binary tree, creating polylines for
    432      * internal nodes by reducing the polylines of their children results in
    433      * exactly the same tree as when the polyline of an internal node is
    434      * constructed by merging the polylines of its children */
    435     tree_args.collapse_polylines = 1;
    436     CHK(sln_tree_create(sln, &tree_args, &tree2) == RES_OK);
    437     check_tree_equality(tree, tree2);
    438 
    439     CHK(sln_tree_ref_put(tree2) == RES_OK);
    440   }
    441 
    442   CHK(sln_tree_ref_put(tree) == RES_OK);
    443 }
    444 
    445 
    446 static void
    447 test_tree_serialization
    448   (struct sln_device* sln,
    449    const struct sln_tree_create_args* tree_args)
    450 {
    451   struct sln_tree_write_args wargs = SLN_TREE_WRITE_ARGS_NULL;
    452   struct sln_tree_read_args rargs = SLN_TREE_READ_ARGS_NULL;
    453   struct sln_tree* tree1 = NULL;
    454   struct sln_tree* tree2 = NULL;
    455 
    456   const char* filename = "tree.sln";
    457   FILE* fp = NULL;
    458 
    459   CHK(sln_tree_create(sln, tree_args, &tree1) == RES_OK);
    460 
    461   CHK(fp = fopen(filename, "w+"));
    462 
    463   wargs.file = fp;
    464   CHK(sln_tree_write(NULL, &wargs) == RES_BAD_ARG);
    465   CHK(sln_tree_write(tree1, NULL) == RES_BAD_ARG);
    466   wargs.file = NULL;
    467   CHK(sln_tree_write(tree1, &wargs) == RES_BAD_ARG);
    468   wargs.file = fp;
    469   CHK(sln_tree_write(tree1, &wargs) == RES_OK);
    470   rewind(fp);
    471 
    472   rargs.metadata = tree_args->metadata;
    473   rargs.lines = tree_args->lines;
    474   rargs.file = fp;
    475   CHK(sln_tree_read(NULL, &rargs, &tree2) == RES_BAD_ARG);
    476   CHK(sln_tree_read(sln, NULL, &tree2) == RES_BAD_ARG);
    477   rargs.metadata = NULL;
    478   CHK(sln_tree_read(sln, &rargs, &tree2) == RES_BAD_ARG);
    479   rargs.metadata = tree_args->metadata;
    480   rargs.lines = NULL;
    481   CHK(sln_tree_read(sln, &rargs, &tree2) == RES_BAD_ARG);
    482   rargs.lines = tree_args->lines;
    483   rargs.file = NULL;
    484   CHK(sln_tree_read(sln, &rargs, &tree2) == RES_BAD_ARG);
    485   rargs.file = fp;
    486   CHK(sln_tree_read(sln, &rargs, NULL) == RES_BAD_ARG);
    487   CHK(sln_tree_read(sln, &rargs, &tree2) == RES_OK);
    488   fclose(fp);
    489 
    490   check_tree_equality(tree1, tree2);
    491   CHK(sln_tree_ref_put(tree2) == RES_OK);
    492 
    493   wargs.file = NULL;
    494   wargs.filename = filename;
    495   CHK(sln_tree_write(tree1, &wargs) == RES_OK);
    496 
    497   rargs.file = NULL;
    498   rargs.filename = "nop";
    499   CHK(sln_tree_read(sln, &rargs, &tree2) == RES_IO_ERR);
    500   rargs.filename = filename;
    501   rargs.disable_line_hash_check = 1;
    502   CHK(sln_tree_read(sln, &rargs, &tree2) == RES_OK);
    503 
    504   check_tree_equality(tree1, tree2);
    505 
    506   CHK(sln_tree_ref_put(tree2) == RES_OK);
    507   CHK(sln_tree_ref_put(tree1) == RES_OK);
    508 }
    509 
    510 /*******************************************************************************
    511  * Test function
    512  ******************************************************************************/
    513 int
    514 main(int argc, char** argv)
    515 {
    516   struct sln_device_create_args dev_args = SLN_DEVICE_CREATE_ARGS_DEFAULT;
    517   struct sln_tree_create_args tree_args = SLN_TREE_CREATE_ARGS_DEFAULT;
    518   struct sln_device* sln = NULL;
    519 
    520   struct shtr_create_args shtr_args = SHTR_CREATE_ARGS_DEFAULT;
    521   struct shtr* shtr = NULL;
    522   struct shtr_line_list_load_args line_load_args = SHTR_LINE_LIST_LOAD_ARGS_NULL;
    523   struct shtr_line_list* line_list = NULL;
    524   struct shtr_isotope_metadata* metadata = NULL;
    525 
    526   FILE* fp_lines = NULL;
    527   FILE* fp_mdata = NULL;
    528   (void)argc, (void)argv;
    529 
    530   /* Generate the file of the isotope metadata */
    531   CHK(fp_mdata = tmpfile());
    532   fprintf(fp_mdata, "Molecule # Iso Abundance Q(296K) gj Molar Mass(g)\n");
    533   write_shtr_molecule(fp_mdata, &g_H2O);
    534   write_shtr_molecule(fp_mdata, &g_CO2);
    535   write_shtr_molecule(fp_mdata, &g_O3);
    536   rewind(fp_mdata);
    537 
    538   /* Generate the file of lines */
    539   CHK(fp_lines = tmpfile());
    540   write_shtr_lines(fp_lines, g_lines, g_nlines);
    541   rewind(fp_lines);
    542 
    543   /* Load the isotope metadata and the lines */
    544   shtr_args.verbose = 1;
    545   CHK(shtr_create(&shtr_args, &shtr) == RES_OK);
    546   CHK(shtr_isotope_metadata_load_stream(shtr, fp_mdata, NULL, &metadata) == RES_OK);
    547 
    548   line_load_args.filename = "stream";
    549   line_load_args.file = fp_lines;
    550   CHK(shtr_line_list_load(shtr, &line_load_args, &line_list) == RES_OK);
    551 
    552   CHK(fclose(fp_lines) == 0);
    553   CHK(fclose(fp_mdata) == 0);
    554 
    555   dev_args.verbose = 1;
    556   CHK(sln_device_create(&dev_args, &sln) == RES_OK);
    557 
    558   /* Create the mixture */
    559   tree_args.metadata = metadata;
    560   tree_args.lines = line_list;
    561   tree_args.molecules[SHTR_H2O].concentration = 1.0/3.0;
    562   tree_args.molecules[SHTR_H2O].cutoff = 25;
    563   tree_args.molecules[SHTR_CO2].concentration = 1.0/3.0;
    564   tree_args.molecules[SHTR_CO2].cutoff = 50;
    565   tree_args.molecules[SHTR_O3 ].concentration = 1.0/3.0;
    566   tree_args.molecules[SHTR_O3 ].cutoff = 25;
    567   tree_args.pressure = 1;
    568   tree_args.temperature = 296;
    569 
    570   test_tree(sln, &tree_args, line_list);
    571   test_tree_serialization(sln, &tree_args);
    572 
    573   /* Test a tree with a non default arity */
    574   tree_args.arity = 13;
    575   test_tree(sln, &tree_args, line_list);
    576   test_tree_serialization(sln, &tree_args);
    577 
    578   /* Test a tree with a non default arity built sequentially*/
    579   tree_args.arity = 13;
    580   tree_args.nthreads_hint = 1;
    581   test_tree(sln, &tree_args, line_list);
    582   test_tree_serialization(sln, &tree_args);
    583   tree_args.nthreads_hint = SLN_TREE_CREATE_ARGS_DEFAULT.nthreads_hint;
    584 
    585   /* Test a tree with a non-standard arity, where the polylines of the internal
    586    * nodes are created by collapsing the polylines of their child nodes */
    587   tree_args.arity = 5;
    588   tree_args.collapse_polylines = 1;
    589   test_tree(sln, &tree_args, line_list);
    590   test_tree_serialization(sln, &tree_args);
    591 
    592   /* Test a tree where the number of rows per leaf is greater than 1 */
    593   tree_args.arity = 4;
    594   tree_args.leaf_nlines = 3;
    595   tree_args.collapse_polylines = 0;
    596   test_tree(sln, &tree_args, line_list);
    597   test_tree_serialization(sln, &tree_args);
    598 
    599   /* Test a tree in which the number of lines per leaf is greater than 1. The
    600    * polylines for the leaves and internal nodes are created by reducing,
    601    * respectively, the polyline of the leaf lines or those of the internal
    602    * nodes' children */
    603   tree_args.arity = 3;
    604   tree_args.leaf_nlines = 6;
    605   tree_args.collapse_polylines = 1;
    606   test_tree(sln, &tree_args, line_list);
    607   test_tree_serialization(sln, &tree_args);
    608 
    609   /* Test building a tree forcing all lines beeing in the root of the tree */
    610   CHK(g_nlines < SLN_LEAF_NLINES_MAX);
    611   tree_args.arity = 2;
    612   tree_args.leaf_nlines = (unsigned)g_nlines;
    613   tree_args.collapse_polylines = 0;
    614   test_tree(sln, &tree_args, line_list);
    615   test_tree_serialization(sln, &tree_args);
    616 
    617   CHK(sln_device_ref_put(sln) == RES_OK);
    618   CHK(shtr_ref_put(shtr) == RES_OK);
    619   CHK(shtr_line_list_ref_put(line_list) == RES_OK);
    620   CHK(shtr_isotope_metadata_ref_put(metadata) == RES_OK);
    621   CHK(mem_allocated_size() == 0);
    622   return 0;
    623 }