htgop

Optical properties of a gas mixture
git clone git://git.meso-star.fr/htgop.git
Log | Files | Refs | README | LICENSE

htgop.c (30706B)


      1 /* Copyright (C) 2018-2021, 2023 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #define _POSIX_C_SOURCE 200112L /* nextafter support */
     17 
     18 #include "htgop.h"
     19 #include "htgop_c.h"
     20 #include "htgop_reader.h"
     21 
     22 #include <rsys/algorithm.h>
     23 #include <rsys/cstr.h>
     24 #include <rsys/logger.h>
     25 #include <rsys/mem_allocator.h>
     26 
     27 #include <math.h>
     28 
     29 /*******************************************************************************
     30  * Helper functions
     31  ******************************************************************************/
     32 static void
     33 log_msg
     34   (const struct htgop* htgop,
     35    const enum log_type stream,
     36    const char* msg,
     37    va_list vargs)
     38 {
     39   ASSERT(htgop && msg);
     40   if(htgop->verbose) {
     41     res_T res; (void)res;
     42     res = logger_vprint(htgop->logger, stream, msg, vargs);
     43     ASSERT(res == RES_OK);
     44   }
     45 }
     46 
     47 static INLINE int
     48 cmp_lvl(const void* key, const void* data)
     49 {
     50   const double height = *((const double*)key);
     51   const struct htgop_level* lvl = (const struct htgop_level*)data;
     52   return height < lvl->height ? -1 : (height > lvl->height ? 1 : 0);
     53 }
     54 
     55 static res_T
     56 parse_spectral_intervals
     57   (struct htgop* htgop,
     58    struct reader* rdr,
     59    const unsigned long nspecints,
     60    struct spectral_intervals* specints)
     61 {
     62   unsigned long ispecint;
     63   double* wave_numbers = NULL;
     64   struct darray_double* pdfs = NULL;
     65   struct darray_double* cdfs = NULL;
     66   res_T res = RES_OK;
     67   (void)htgop;
     68   ASSERT(htgop && rdr && nspecints && specints);
     69 
     70   #define CALL(Func) { if((res = Func) != RES_OK) goto error; } (void)0
     71 
     72   /* Allocate the memory space for the wave numbers and the quadratures */
     73   CALL(darray_double_resize(&specints->wave_numbers, nspecints + 1));
     74   CALL(darray_dbllst_resize(&specints->quadrature_pdfs, nspecints));
     75   CALL(darray_dbllst_resize(&specints->quadrature_cdfs, nspecints));
     76   wave_numbers = darray_double_data_get(&specints->wave_numbers);
     77   pdfs = darray_dbllst_data_get(&specints->quadrature_pdfs);
     78   cdfs = darray_dbllst_data_get(&specints->quadrature_cdfs);
     79 
     80   FOR_EACH(ispecint, 0, nspecints) {
     81     double* pdf = NULL;
     82     double* cdf = NULL;
     83     double wave_number_low;
     84     double wave_number_upp;
     85     double sum = 0;
     86     unsigned long quad_len;
     87     unsigned long iquad;
     88 
     89     /* Parse the interval bounds */
     90     CALL(cstr_to_double(read_line(rdr), &wave_number_low));
     91     CALL(cstr_to_double(read_line(rdr), &wave_number_upp));
     92 
     93     /* Check and register the interval bounds */
     94     if(wave_number_low >= wave_number_upp) {
     95       res = RES_BAD_ARG;
     96       goto error;
     97     }
     98     if(ispecint == 0) {
     99       wave_numbers[ispecint] = wave_number_low;
    100     } else if(wave_number_low != wave_numbers[ispecint]) {
    101       res = RES_BAD_ARG;
    102       goto error;
    103     }
    104     wave_numbers[ispecint + 1] = wave_number_upp;
    105 
    106     /* Parse and allocate the quadrature length */
    107     CALL(cstr_to_ulong(read_line(rdr), &quad_len));
    108 
    109     /* Allocate the points of the quadrature */
    110     CALL(darray_double_resize(&pdfs[ispecint], quad_len));
    111     CALL(darray_double_resize(&cdfs[ispecint], quad_len));
    112     pdf = darray_double_data_get(&pdfs[ispecint]);
    113     cdf = darray_double_data_get(&cdfs[ispecint]);
    114 
    115     /* Read the weight of the quadrature points */
    116     sum = 0;
    117     FOR_EACH(iquad, 0, quad_len) {
    118       CALL(cstr_to_double(read_line(rdr), &pdf[iquad]));
    119       cdf[iquad] = iquad == 0 ? pdf[iquad] : pdf[iquad]+cdf[iquad-1];
    120       sum += pdf[iquad];
    121     }
    122     ASSERT(eq_eps(sum, 1.0, 1.e6));
    123     ASSERT(eq_eps(cdf[quad_len-1], 1.0, 1.e6));
    124     cdf[quad_len-1] = 1.0; /* Handle numerical imprecision */
    125   }
    126 
    127   #undef CALL
    128 
    129 exit:
    130   return res;
    131 error:
    132   darray_double_clear(&specints->wave_numbers);
    133   darray_dbllst_clear(&specints->quadrature_pdfs);
    134   darray_dbllst_clear(&specints->quadrature_cdfs);
    135   goto exit;
    136 }
    137 
    138 /* Generate the parse_layers_spectral_intervals_ka_lw function */
    139 #define DOMAIN lw
    140 #define DATA ka
    141 #include "htgop_parse_layers_spectral_intervals_data.h"
    142 /* Generate the parse_layers_spectral_intervals_ka_sw function */
    143 #define DOMAIN sw
    144 #define DATA ka
    145 #include "htgop_parse_layers_spectral_intervals_data.h"
    146 /* Generate the parse_layers_spectral_intervals_ks_sw function */
    147 #define DOMAIN sw
    148 #define DATA ks
    149 #include "htgop_parse_layers_spectral_intervals_data.h"
    150 
    151 static res_T
    152 parse_layers_spectral_intervals(struct htgop* htgop, struct reader* rdr)
    153 {
    154   struct layer* layers = NULL;
    155   size_t lw_nspecints, sw_nspecints, nlays;
    156   size_t ilay;
    157   res_T res = RES_OK;
    158   ASSERT(htgop && rdr);
    159 
    160   layers = darray_layer_data_get(&htgop->layers);
    161   nlays = darray_layer_size_get(&htgop->layers);
    162   ASSERT(nlays > 0);
    163   lw_nspecints = darray_double_size_get(&htgop->lw_specints.wave_numbers) - 1;
    164   sw_nspecints = darray_double_size_get(&htgop->sw_specints.wave_numbers) - 1;
    165   ASSERT(lw_nspecints > 0 && sw_nspecints);
    166 
    167   #define CALL(Func) { if((res = Func) != RES_OK) goto error; } (void)0
    168   /* Allocate the per layer spectral intervals */
    169   FOR_EACH(ilay, 0, nlays) {
    170     CALL(darray_lay_lw_specint_resize(&layers[ilay].lw_specints, lw_nspecints));
    171     CALL(darray_lay_sw_specint_resize(&layers[ilay].sw_specints, sw_nspecints));
    172   }
    173   /* Parse the spectral data of the layers */
    174   CALL(parse_layers_spectral_intervals_ka_lw(htgop, rdr));
    175   CALL(parse_layers_spectral_intervals_ka_sw(htgop, rdr));
    176   CALL(parse_layers_spectral_intervals_ks_sw(htgop, rdr));
    177   #undef CALL
    178 
    179 exit:
    180   return res;
    181 error:
    182   FOR_EACH(ilay, 0, nlays) {
    183     darray_lay_lw_specint_clear(&layers[ilay].lw_specints);
    184     darray_lay_sw_specint_clear(&layers[ilay].sw_specints);
    185   }
    186   goto exit;
    187 }
    188 
    189 static res_T
    190 load_stream(struct htgop* htgop, FILE* stream, const char* stream_name)
    191 {
    192   struct reader rdr;
    193   struct htgop_level* levels = NULL;
    194   struct layer* layers = NULL;
    195   unsigned long nlvls, nlays, tab_len, nspecints_lw, nspecints_sw;
    196   unsigned long ilvl, ilay, itab;
    197   unsigned long iline_saved;
    198   res_T res = RES_OK;
    199   ASSERT(htgop && stream && stream_name);
    200   reader_init(&rdr, stream, stream_name);
    201 
    202   #define CALL(Func) {                                                         \
    203     if((res = Func) != RES_OK) {                                               \
    204       log_err(htgop, "%s:%lu: Parsing error.\n", rdr.name, rdr.iline);         \
    205       goto error;                                                              \
    206     }                                                                          \
    207   } (void)0
    208 
    209   /* Parse the number of levels/layers */
    210   CALL(cstr_to_ulong(read_line(&rdr), &nlvls));
    211   iline_saved = rdr.iline;
    212   CALL(cstr_to_ulong(read_line(&rdr), &nlays));
    213   if(!nlays) {
    214     log_err(htgop, "%s:%lu: The number of layers cannot be null.\n",
    215       rdr.name, rdr.iline);
    216     res = RES_BAD_ARG;
    217     goto error;
    218   }
    219   if(nlvls != nlays + 1) {
    220     log_err(htgop,
    221       "%s:%lu: Invalid number of levels `%lu' (#layers = `%lu').\n",
    222       rdr.name, iline_saved, nlvls, nlays);
    223     res = RES_BAD_ARG;
    224     goto error;
    225   }
    226 
    227   /* Parse the ground temperature */
    228   CALL(cstr_to_double(read_line(&rdr), &htgop->ground.temperature));
    229 
    230   /* Allocate the per level data */
    231   CALL(darray_level_resize(&htgop->levels, nlvls));
    232   CALL(darray_layer_resize(&htgop->layers, nlays));
    233   levels = darray_level_data_get(&htgop->levels);
    234   layers = darray_layer_data_get(&htgop->layers);
    235 
    236   /* Per level data */
    237   FOR_EACH(ilvl, 0, nlvls) { /* Pressure */
    238     CALL(cstr_to_double(read_line(&rdr), &levels[ilvl].pressure));
    239   }
    240   FOR_EACH(ilvl, 0, nlvls) { /* Temperature */
    241     CALL(cstr_to_double(read_line(&rdr), &levels[ilvl].temperature));
    242   }
    243   FOR_EACH(ilvl, 0, nlvls) { /* Height */
    244     CALL(cstr_to_double(read_line(&rdr), &levels[ilvl].height));
    245     if(ilvl && levels[ilvl].height <= levels[ilvl-1].height) {
    246       log_err(htgop,
    247         "%s:%lu: The levels must be sorted in strict ascending order "
    248         "wrt their height.\n", rdr.name, rdr.iline);
    249       res = RES_BAD_ARG;
    250       goto error;
    251     }
    252   }
    253 
    254   /* Per layer x H2O nominal */
    255   FOR_EACH(ilay, 0, nlays) {
    256     CALL(cstr_to_double(read_line(&rdr), &layers[ilay].x_h2o_nominal));
    257   }
    258 
    259   /* Parse the length of the tabulation */
    260   CALL(cstr_to_ulong(read_line(&rdr), &tab_len));
    261   if(!tab_len) {
    262     log_err(htgop,
    263       "%s:%lu: The tabulation length cannot be null.\n", rdr.name, rdr.iline);
    264     res = RES_BAD_ARG;
    265     goto error;
    266   }
    267 
    268   /* Allocate the tabulated xH2O of each layer */
    269   FOR_EACH(ilay, 0, nlays) {
    270     CALL(darray_double_resize(&layers[ilay].x_h2o_tab, tab_len));
    271   }
    272 
    273   /* Parse the tabulated xH2O of each layer */
    274   FOR_EACH(itab, 0, tab_len) {
    275     FOR_EACH(ilay, 0, nlays) {
    276       double* x_h2o_tab = darray_double_data_get(&layers[ilay].x_h2o_tab);
    277       CALL(cstr_to_double(read_line(&rdr), &x_h2o_tab[itab]));
    278       if(x_h2o_tab[itab] < 0) {
    279         log_err(htgop,
    280           "%s:%lu: the tabulated water vapor molar fractions must be in [0, 1].\n",
    281           rdr.name, rdr.iline);
    282         res = RES_BAD_ARG;
    283         goto error;
    284       }
    285       if(itab && x_h2o_tab[itab] < x_h2o_tab[itab-1]) {
    286         log_err(htgop,
    287           "%s:%lu: the tabulated water vapor molar fractions must be sorted in "
    288           "strict ascending order.\n", rdr.name, rdr.iline);
    289         res = RES_BAD_ARG;
    290         goto error;
    291       }
    292       if(itab == tab_len-1 && x_h2o_tab[itab] != 1) {
    293         log_err(htgop,
    294         "%s:%lu: the tabulated water vapor molar fractions is not normalized.\n",
    295         rdr.name, rdr.iline);
    296         res = RES_BAD_ARG;
    297         goto error;
    298       }
    299     }
    300   }
    301 
    302   /* Parse the long/short wave emissivity of the hround */
    303   CALL(cstr_to_double(read_line(&rdr), &htgop->ground.lw_emissivity));
    304   CALL(cstr_to_double(read_line(&rdr), &htgop->ground.sw_emissivity));
    305 
    306   /* Parse the number of long/short wave spectral intervals */
    307   CALL(cstr_to_ulong(read_line(&rdr), &nspecints_lw));
    308   CALL(cstr_to_ulong(read_line(&rdr), &nspecints_sw));
    309 
    310   /* Parse the data of the long/short wave spectral intervals */
    311   CALL(parse_spectral_intervals(htgop, &rdr, nspecints_lw, &htgop->lw_specints));
    312   CALL(parse_spectral_intervals(htgop, &rdr, nspecints_sw, &htgop->sw_specints));
    313 
    314   /* Parse the spectral data of the layers */
    315   CALL(parse_layers_spectral_intervals(htgop, &rdr));
    316 
    317   #undef CALL
    318 
    319 exit:
    320   reader_release(&rdr);
    321   return res;
    322 error:
    323   goto exit;
    324 }
    325 
    326 static void
    327 release_htgop(ref_T* ref)
    328 {
    329   struct htgop* htgop;
    330   ASSERT(ref);
    331   htgop = CONTAINER_OF(ref, struct htgop, ref);
    332   spectral_intervals_release(&htgop->lw_specints);
    333   spectral_intervals_release(&htgop->sw_specints);
    334   darray_double_release(&htgop->sw_X_cdf);
    335   darray_double_release(&htgop->sw_Y_cdf);
    336   darray_double_release(&htgop->sw_Z_cdf);
    337   darray_level_release(&htgop->levels);
    338   darray_layer_release(&htgop->layers);
    339   MEM_RM(htgop->allocator, htgop);
    340 }
    341 
    342 /*******************************************************************************
    343  * Exported functions
    344  ******************************************************************************/
    345 res_T
    346 htgop_create
    347   (struct logger* log,
    348    struct mem_allocator* mem_allocator,
    349    const int verbose,
    350    struct htgop** out_htgop)
    351 {
    352   struct mem_allocator* allocator = NULL;
    353   struct logger* logger = NULL;
    354   struct htgop* htgop = NULL;
    355   res_T res = RES_OK;
    356 
    357   if(!out_htgop) {
    358     res = RES_BAD_ARG;
    359     goto error;
    360   }
    361 
    362   allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
    363   logger = log ? log : LOGGER_DEFAULT;
    364 
    365   htgop = MEM_CALLOC(allocator, 1, sizeof(struct htgop));
    366   if(!htgop) {
    367     if(verbose) {
    368       logger_print(logger, LOG_ERROR,
    369         "Could not allocate the HTGOP handler.\n");
    370     }
    371     res = RES_MEM_ERR;
    372     goto error;
    373   }
    374 
    375   ref_init(&htgop->ref);
    376   htgop->allocator = allocator;
    377   htgop->logger = logger;
    378   htgop->verbose = verbose;
    379   spectral_intervals_init(allocator, &htgop->lw_specints);
    380   spectral_intervals_init(allocator, &htgop->sw_specints);
    381   darray_double_init(allocator, &htgop->sw_X_cdf);
    382   darray_double_init(allocator, &htgop->sw_Y_cdf);
    383   darray_double_init(allocator, &htgop->sw_Z_cdf);
    384   darray_level_init(allocator, &htgop->levels);
    385   darray_layer_init(allocator, &htgop->layers);
    386 
    387 exit:
    388   if(out_htgop) *out_htgop = htgop;
    389   return res;
    390 error:
    391   if(htgop) {
    392     HTGOP(ref_put(htgop));
    393     htgop = NULL;
    394   }
    395   goto exit;
    396 }
    397 
    398 res_T
    399 htgop_ref_get(struct htgop* htgop)
    400 {
    401   if(!htgop) return RES_BAD_ARG;
    402   ref_get(&htgop->ref);
    403   return RES_OK;
    404 }
    405 
    406 res_T
    407 htgop_ref_put(struct htgop* htgop)
    408 {
    409   if(!htgop) return RES_BAD_ARG;
    410   ref_put(&htgop->ref, release_htgop);
    411   return RES_OK;
    412 }
    413 
    414 res_T
    415 htgop_load(struct htgop* htgop, const char* filename)
    416 {
    417   FILE* file = NULL;
    418   res_T res = RES_OK;
    419 
    420   if(!htgop || !filename) {
    421     res = RES_BAD_ARG;
    422     goto error;
    423   }
    424 
    425   file = fopen(filename, "r");
    426   if(!file) {
    427     log_err(htgop, "%s: error opening file `%s'.\n", FUNC_NAME, filename);
    428     res = RES_IO_ERR;
    429     goto error;
    430   }
    431 
    432   res = load_stream(htgop, file, filename);
    433   if(res != RES_OK) goto error;
    434 
    435 exit:
    436   if(file) fclose(file);
    437   return res;
    438 error:
    439   goto exit;
    440 }
    441 
    442 res_T
    443 htgop_load_stream(struct htgop* htgop, FILE* stream)
    444 {
    445   if(!htgop || !stream) return RES_BAD_ARG;
    446   return load_stream(htgop, stream, "<stream>");
    447 }
    448 
    449 res_T
    450 htgop_get_ground(const struct htgop* htgop, struct htgop_ground* ground)
    451 {
    452   if(!htgop || !ground) return RES_BAD_ARG;
    453   *ground = htgop->ground;
    454   return RES_OK;
    455 }
    456 
    457 res_T
    458 htgop_get_layers_count(const struct htgop* htgop, size_t* nlayers)
    459 {
    460   if(!htgop || !nlayers) return RES_BAD_ARG;
    461   *nlayers = darray_layer_size_get(&htgop->layers);
    462   return RES_OK;
    463 }
    464 
    465 res_T
    466 htgop_get_levels_count(const struct htgop* htgop, size_t* nlevels)
    467 {
    468   if(!htgop || !nlevels) return RES_BAD_ARG;
    469   *nlevels = darray_level_size_get(&htgop->levels);
    470   return RES_OK;
    471 }
    472 
    473 res_T
    474 htgop_get_level
    475   (const struct htgop* htgop, const size_t ilvl, struct htgop_level* lvl)
    476 {
    477   size_t n;
    478   if(!htgop || !lvl) return RES_BAD_ARG;
    479   HTGOP(get_levels_count(htgop, &n));
    480   if(ilvl >= n) {
    481     log_err(htgop, "%s: invalid level `%lu'.\n", FUNC_NAME, ilvl);
    482     return RES_BAD_ARG;
    483   }
    484   *lvl = darray_level_cdata_get(&htgop->levels)[ilvl];
    485   return RES_OK;
    486 }
    487 
    488 res_T
    489 htgop_get_layer
    490   (const struct htgop* htgop,
    491    const size_t ilayer,
    492    struct htgop_layer* layer)
    493 {
    494   const struct layer* l;
    495   size_t n;
    496   if(!htgop || !layer) return RES_BAD_ARG;
    497   HTGOP(get_layers_count(htgop, &n));
    498   if(ilayer >= n) {
    499     log_err(htgop, "%s: invalid layer `%lu'.\n",
    500       FUNC_NAME, (unsigned long)ilayer);
    501     return RES_BAD_ARG;
    502   }
    503   l = darray_layer_cdata_get(&htgop->layers) + ilayer;
    504   layer->x_h2o_nominal = l->x_h2o_nominal;
    505   layer->x_h2o_tab = darray_double_cdata_get(&l->x_h2o_tab);
    506   layer->tab_length = darray_double_size_get(&l->x_h2o_tab);
    507   layer->lw_spectral_intervals_count =
    508     darray_lay_lw_specint_size_get(&l->lw_specints);
    509   layer->sw_spectral_intervals_count =
    510     darray_lay_sw_specint_size_get(&l->sw_specints);
    511   layer->data__ = l;
    512   layer->htgop = htgop;
    513   return RES_OK;
    514 }
    515 
    516 res_T
    517 htgop_layer_get_lw_spectral_interval
    518   (const struct htgop_layer* layer,
    519    const size_t ispecint,
    520    struct htgop_layer_lw_spectral_interval* specint)
    521 {
    522   const struct layer* l;
    523   const struct layer_lw_spectral_interval* lspecint;
    524   if(!layer || !specint) return RES_BAD_ARG;
    525   if(ispecint >= layer->lw_spectral_intervals_count) {
    526     log_err(layer->htgop, "%s: invalid spectral interval `%lu'.\n",
    527       FUNC_NAME, (unsigned long)ispecint);
    528     return RES_BAD_ARG;
    529   }
    530   l = layer->data__;
    531   lspecint = darray_lay_lw_specint_cdata_get(&l->lw_specints) + ispecint;
    532   specint->ka_nominal = darray_double_cdata_get(&lspecint->ka_nominal);
    533   specint->quadrature_length = darray_double_size_get(&lspecint->ka_nominal);
    534   specint->htgop = layer->htgop;
    535   specint->data__ = lspecint;
    536   specint->layer__ = l;
    537   return RES_OK;
    538 }
    539 
    540 res_T
    541 htgop_layer_get_sw_spectral_interval
    542   (const struct htgop_layer* layer,
    543    const size_t ispecint,
    544    struct htgop_layer_sw_spectral_interval* specint)
    545 {
    546   const struct layer* l;
    547   const struct layer_sw_spectral_interval* lspecint;
    548   if(!layer || !specint) return RES_BAD_ARG;
    549   if(ispecint >= layer->sw_spectral_intervals_count) {
    550     log_err(layer->htgop, "%s: invalid spectral interval `%lu'.\n",
    551       FUNC_NAME, (unsigned long)ispecint);
    552     return RES_BAD_ARG;
    553   }
    554   l = layer->data__;
    555   lspecint = darray_lay_sw_specint_cdata_get(&l->sw_specints) + ispecint;
    556   specint->ka_nominal = darray_double_cdata_get(&lspecint->ka_nominal);
    557   specint->ks_nominal = darray_double_cdata_get(&lspecint->ks_nominal);
    558   specint->quadrature_length = darray_double_size_get(&lspecint->ka_nominal);
    559   specint->htgop = layer->htgop;
    560   specint->data__ = lspecint;
    561   specint->layer__ = l;
    562   return RES_OK;
    563 }
    564 
    565 res_T
    566 htgop_layer_lw_spectral_interval_get_tab
    567   (const struct htgop_layer_lw_spectral_interval* specint,
    568    const size_t iquad,
    569    struct htgop_layer_lw_spectral_interval_tab* tab)
    570 {
    571   const struct layer_lw_spectral_interval* lspecint;
    572   const struct layer* l;
    573   const struct darray_double* t;
    574   if(!specint || !tab) return RES_BAD_ARG;
    575   if(iquad >= specint->quadrature_length) {
    576     log_err(specint->htgop, "%s: invalid quadrature point `%lu'.\n",
    577       FUNC_NAME, (unsigned long)iquad);
    578     return RES_BAD_ARG;
    579   }
    580   lspecint = specint->data__;
    581   l = specint->layer__;
    582   t = darray_dbllst_cdata_get(&lspecint->ka_tab) + iquad;
    583   tab->x_h2o_tab = darray_double_cdata_get(&l->x_h2o_tab);
    584   tab->ka_tab = darray_double_cdata_get(t);
    585   tab->tab_length = darray_double_size_get(t);
    586   tab->htgop = specint->htgop;
    587   return RES_OK;
    588 }
    589 
    590 res_T
    591 htgop_layer_sw_spectral_interval_get_tab
    592   (const struct htgop_layer_sw_spectral_interval* specint,
    593    const size_t iquad,
    594    struct htgop_layer_sw_spectral_interval_tab* tab)
    595 {
    596   const struct layer_sw_spectral_interval* lspecint;
    597   const struct layer* l;
    598   const struct darray_double* ka_tab;
    599   const struct darray_double* ks_tab;
    600   if(!specint || !tab) return RES_BAD_ARG;
    601   if(iquad >= specint->quadrature_length) {
    602     log_err(specint->htgop, "%s: invalid quadrature point `%lu'.\n",
    603       FUNC_NAME, (unsigned long)iquad);
    604     return RES_BAD_ARG;
    605   }
    606   lspecint = specint->data__;
    607   l = specint->layer__;
    608 
    609   ka_tab = darray_dbllst_cdata_get(&lspecint->ka_tab) + iquad;
    610   ks_tab = darray_dbllst_cdata_get(&lspecint->ks_tab) + iquad;
    611   tab->x_h2o_tab = darray_double_cdata_get(&l->x_h2o_tab);
    612   tab->ka_tab = darray_double_cdata_get(ka_tab);
    613   tab->ks_tab = darray_double_cdata_get(ks_tab);
    614   tab->tab_length = darray_double_size_get(ka_tab);
    615   tab->htgop = specint->htgop;
    616   return RES_OK;
    617 }
    618 
    619 res_T
    620 htgop_get_lw_spectral_intervals_count(const struct htgop* htgop, size_t* nspecints)
    621 {
    622   if(!htgop || !nspecints) return RES_BAD_ARG;
    623   *nspecints = darray_dbllst_size_get(&htgop->lw_specints.quadrature_pdfs);
    624   return RES_OK;
    625 }
    626 
    627 res_T
    628 htgop_get_sw_spectral_intervals_count(const struct htgop* htgop, size_t* nspecints)
    629 {
    630   if(!htgop || !nspecints) return RES_BAD_ARG;
    631   *nspecints = darray_dbllst_size_get(&htgop->sw_specints.quadrature_pdfs);
    632   return RES_OK;
    633 }
    634 
    635 res_T
    636 htgop_get_lw_spectral_intervals_wave_numbers
    637   (const struct htgop* htgop, const double* wave_numbers[])
    638 {
    639   if(!htgop || !wave_numbers) return RES_BAD_ARG;
    640   *wave_numbers = darray_double_cdata_get(&htgop->lw_specints.wave_numbers);
    641   return RES_OK;
    642 }
    643 
    644 res_T
    645 htgop_get_sw_spectral_intervals_wave_numbers
    646   (const struct htgop* htgop, const double* wave_numbers[])
    647 {
    648   if(!htgop || !wave_numbers) return RES_BAD_ARG;
    649   *wave_numbers = darray_double_cdata_get(&htgop->sw_specints.wave_numbers);
    650   return RES_OK;
    651 }
    652 
    653 res_T
    654 htgop_get_lw_spectral_interval
    655   (const struct htgop* htgop,
    656    const size_t ispecint,
    657    struct htgop_spectral_interval* interval)
    658 {
    659   const double* wave_numbers;
    660   const struct darray_double* quad_cdfs;
    661   const struct darray_double* quad_pdfs;
    662   size_t n;
    663   if(!htgop || !interval) return RES_BAD_ARG;
    664   HTGOP(get_lw_spectral_intervals_count(htgop, &n));
    665   if(ispecint >= n) {
    666     log_err(htgop, "%s: invalid long wave spectral interval `%lu'.\n",
    667       FUNC_NAME, (unsigned long)ispecint);
    668     return RES_BAD_ARG;
    669   }
    670   wave_numbers = darray_double_cdata_get(&htgop->lw_specints.wave_numbers);
    671   quad_pdfs = darray_dbllst_cdata_get(&htgop->lw_specints.quadrature_pdfs);
    672   quad_cdfs = darray_dbllst_cdata_get(&htgop->lw_specints.quadrature_cdfs);
    673   interval->wave_numbers[0] = wave_numbers[ispecint+0];
    674   interval->wave_numbers[1] = wave_numbers[ispecint+1];
    675   interval->quadrature_pdf = darray_double_cdata_get(&quad_pdfs[ispecint]);
    676   interval->quadrature_cdf = darray_double_cdata_get(&quad_cdfs[ispecint]);
    677   interval->quadrature_length = darray_double_size_get(&quad_pdfs[ispecint]);
    678   interval->htgop = htgop;
    679   return RES_OK;
    680 }
    681 
    682 res_T
    683 htgop_get_sw_spectral_interval
    684   (const struct htgop* htgop,
    685    const size_t ispecint,
    686    struct htgop_spectral_interval* interval)
    687 {
    688   const double* wave_numbers;
    689   const struct darray_double* quad_pdfs;
    690   const struct darray_double* quad_cdfs;
    691   size_t n;
    692   if(!htgop || !interval) return RES_BAD_ARG;
    693   HTGOP(get_sw_spectral_intervals_count(htgop, &n));
    694   if(ispecint >= n) {
    695     log_err(htgop, "%s: invalid short wave spectral interval `%lu'.\n",
    696       FUNC_NAME, (unsigned long)ispecint);
    697     return RES_BAD_ARG;
    698   }
    699   wave_numbers = darray_double_cdata_get(&htgop->sw_specints.wave_numbers);
    700   quad_pdfs = darray_dbllst_cdata_get(&htgop->sw_specints.quadrature_pdfs);
    701   quad_cdfs = darray_dbllst_cdata_get(&htgop->sw_specints.quadrature_cdfs);
    702   interval->wave_numbers[0] = wave_numbers[ispecint+0];
    703   interval->wave_numbers[1] = wave_numbers[ispecint+1];
    704   interval->quadrature_pdf = darray_double_cdata_get(&quad_pdfs[ispecint]);
    705   interval->quadrature_cdf = darray_double_cdata_get(&quad_cdfs[ispecint]);
    706   interval->quadrature_length = darray_double_size_get(&quad_pdfs[ispecint]);
    707   interval->htgop = htgop;
    708   return RES_OK;
    709 }
    710 
    711 res_T
    712 htgop_find_lw_spectral_interval_id
    713   (const struct htgop* htgop,
    714    const double wnum,
    715    size_t* ispecint)
    716 {
    717   const double* find = NULL;
    718   const double* wnums = NULL;
    719   size_t nwnums = 0;
    720   if(!htgop || !ispecint) return RES_BAD_ARG;
    721 
    722   wnums = darray_double_cdata_get(&htgop->lw_specints.wave_numbers);
    723   nwnums = darray_double_size_get(&htgop->lw_specints.wave_numbers);
    724 
    725   find = search_lower_bound(&wnum, wnums, nwnums, sizeof(*wnums), cmp_dbl);
    726   if(!find || find == wnums) {
    727     log_warn(htgop, "%s: the wavenumber `%g' is not included in any band.\n",
    728       FUNC_NAME, wnum);
    729     *ispecint = SIZE_MAX;
    730   } else {
    731     *ispecint = (size_t)(find - wnums - 1);
    732 #ifndef NDEBUG
    733     {
    734       size_t n;
    735       HTGOP(get_lw_spectral_intervals_count(htgop, &n));
    736       ASSERT(*ispecint < n);
    737     }
    738 #endif
    739   }
    740   return RES_OK;
    741 }
    742 
    743 res_T
    744 htgop_find_sw_spectral_interval_id
    745   (const struct htgop* htgop,
    746    const double wnum,
    747    size_t* ispecint)
    748 {
    749   const double* find = NULL;
    750   const double* wnums = NULL;
    751   size_t nwnums = 0;
    752   if(!htgop || !ispecint) return RES_BAD_ARG;
    753 
    754   wnums = darray_double_cdata_get(&htgop->sw_specints.wave_numbers);
    755   nwnums = darray_double_size_get(&htgop->sw_specints.wave_numbers);
    756 
    757   find = search_lower_bound(&wnum, wnums, nwnums, sizeof(*wnums), cmp_dbl);
    758   if(!find || find == wnums) {
    759     log_warn(htgop, "%s: the wavenumber `%g' is not included in any band.\n",
    760       FUNC_NAME, wnum);
    761     *ispecint = SIZE_MAX;
    762   } else {
    763     *ispecint = (size_t)(find - wnums - 1);
    764 #ifndef NDEBUG
    765     {
    766       size_t n;
    767       HTGOP(get_sw_spectral_intervals_count(htgop, &n));
    768       ASSERT(*ispecint < n);
    769     }
    770 #endif
    771 
    772   }
    773   return RES_OK;
    774 }
    775 
    776 res_T
    777 htgop_spectral_interval_sample_quadrature
    778   (const struct htgop_spectral_interval* specint,
    779    const double r, /* Canonical number in [0, 1[ */
    780    size_t* iquad_point) /* Id of the sample quadrature point */
    781 {
    782   double r_next = nextafter(r, DBL_MAX);
    783   double* find;
    784   size_t i;
    785 
    786   if(!specint || !iquad_point) return RES_BAD_ARG;
    787 
    788   if(r < 0 || r >= 1) {
    789     log_err(specint->htgop,
    790       "%s: invalid canonical random number `%g'.\n", FUNC_NAME, r);
    791     return RES_BAD_ARG;
    792   }
    793 
    794   /* Use r_next rather than r in order to find the first entry that is not less
    795    * than *or equal* to r */
    796   find = search_lower_bound(&r_next, specint->quadrature_cdf,
    797     specint->quadrature_length, sizeof(double), cmp_dbl);
    798   ASSERT(find);
    799   i = (size_t)(find - specint->quadrature_cdf);
    800   ASSERT(i < specint->quadrature_length);
    801   ASSERT(specint->quadrature_cdf[i] > r);
    802   ASSERT(!i || specint->quadrature_cdf[i-1] <= r);
    803   *iquad_point = i;
    804   return RES_OK;
    805 }
    806 
    807 res_T
    808 htgop_position_to_layer_id
    809   (const struct htgop* htgop, const double pos, size_t* ilayer)
    810 {
    811   const struct htgop_level* lvls;
    812   size_t nlvls;
    813 
    814   if(!htgop || !ilayer) return RES_BAD_ARG;
    815 
    816   lvls = darray_level_cdata_get(&htgop->levels);
    817   nlvls = darray_level_size_get(&htgop->levels);
    818   ASSERT(nlvls);
    819 
    820   if(pos == lvls[0].height) {
    821     *ilayer = 0;
    822   } else if(pos == lvls[nlvls-1].height) {
    823     *ilayer = nlvls - 2;
    824   } else {
    825     const struct htgop_level* find;
    826     size_t i;
    827 
    828     find = search_lower_bound(&pos, lvls, nlvls, sizeof(*lvls), cmp_lvl);
    829     if(!find || find == lvls) {
    830       log_err(htgop, "%s: the position `%g' is outside the atmospheric slices.\n",
    831         FUNC_NAME, pos);
    832       return RES_BAD_ARG;
    833     }
    834 
    835     i = (size_t)(find - lvls);
    836     ASSERT(i < nlvls && i > 0);
    837     ASSERT(lvls[i].height > pos && (!i || lvls[i-1].height <= pos));
    838     *ilayer = i - 1;
    839   }
    840   return RES_OK;
    841 }
    842 
    843 res_T
    844 htgop_get_sw_spectral_intervals
    845   (const struct htgop* htgop,
    846    const double wnum_range[2],
    847    size_t specint_range[2])
    848 {
    849   const double* wnums = NULL;
    850   size_t nwnums = 0;
    851   res_T res = RES_OK;
    852 
    853   if(!htgop || !wnum_range || wnum_range[0]>wnum_range[1] || !specint_range) {
    854     res = RES_BAD_ARG;
    855     goto error;
    856   }
    857 
    858   wnums = darray_double_cdata_get(&htgop->sw_specints.wave_numbers);
    859   nwnums = darray_double_size_get(&htgop->sw_specints.wave_numbers);
    860 
    861   res = get_spectral_intervals
    862     (htgop, FUNC_NAME, wnum_range, wnums, nwnums, specint_range);
    863   if(res != RES_OK) goto error;
    864 
    865 exit:
    866   return res;
    867 error:
    868   goto exit;
    869 }
    870 
    871 res_T
    872 htgop_get_lw_spectral_intervals
    873   (const struct htgop* htgop,
    874    const double wnum_range[2],
    875    size_t specint_range[2])
    876 {
    877   const double* wnums = NULL;
    878   size_t nwnums = 0;
    879   res_T res = RES_OK;
    880 
    881   if(!htgop || !wnum_range || wnum_range[0]>wnum_range[1] || !specint_range) {
    882     res = RES_BAD_ARG;
    883     goto error;
    884   }
    885 
    886   wnums = darray_double_cdata_get(&htgop->lw_specints.wave_numbers);
    887   nwnums = darray_double_size_get(&htgop->lw_specints.wave_numbers);
    888 
    889   res = get_spectral_intervals
    890     (htgop, FUNC_NAME, wnum_range, wnums, nwnums, specint_range);
    891   if(res != RES_OK) goto error;
    892 
    893 exit:
    894   return res;
    895 error:
    896   goto exit;
    897 }
    898 
    899 /* Generate the htgop_layer_fetch_lw_ka function */
    900 #define DOMAIN lw
    901 #define DATA ka
    902 #include "htgop_fetch_radiative_properties.h"
    903 
    904 /* Generate the htgop_layer_fetch_sw_ka function */
    905 #define DOMAIN sw
    906 #define DATA ka
    907 #include "htgop_fetch_radiative_properties.h"
    908 
    909 /* Generate the htgop_layer_fetch_sw_ks function */
    910 #define DOMAIN sw
    911 #define DATA ks
    912 #include "htgop_fetch_radiative_properties.h"
    913 
    914 /* Generate the htgop_layer_get_sw_kext function */
    915 #define GET_DATA(Tab, Id) ((Tab)->ka_tab[Id] + (Tab)->ks_tab[Id])
    916 #define DOMAIN sw
    917 #define DATA kext
    918 #include "htgop_fetch_radiative_properties.h"
    919 
    920 /* Generate the functions that get the boundaries of ka in LW */
    921 #define DOMAIN lw
    922 #define DATA ka
    923 #include "htgop_get_radiative_properties_bounds.h"
    924 
    925 /* Generate the functions that get the boundaries of ka in SW */
    926 #define DOMAIN sw
    927 #define DATA ka
    928 #include "htgop_get_radiative_properties_bounds.h"
    929 
    930 /* Generate the functions that get the boundaries of ks in SW */
    931 #define DOMAIN sw
    932 #define DATA ks
    933 #include "htgop_get_radiative_properties_bounds.h"
    934 
    935 /* Generate the functions that get the boundaries of kext in SW */
    936 #define GET_DATA(Tab, Id) ((Tab)->ka_tab[Id] + (Tab)->ks_tab[Id])
    937 #define DOMAIN sw
    938 #define DATA kext
    939 #include "htgop_get_radiative_properties_bounds.h"
    940 
    941 /*******************************************************************************
    942  * Local functions
    943  ******************************************************************************/
    944 void
    945 log_err(const struct htgop* htgop, const char* msg, ...)
    946 {
    947   va_list vargs_list;
    948   ASSERT(htgop && msg);
    949 
    950   va_start(vargs_list, msg);
    951   log_msg(htgop, LOG_ERROR, msg, vargs_list);
    952   va_end(vargs_list);
    953 }
    954 
    955 void
    956 log_warn(const struct htgop* htgop, const char* msg, ...)
    957 {
    958   va_list vargs_list;
    959   ASSERT(htgop && msg);
    960 
    961   va_start(vargs_list, msg);
    962   log_msg(htgop, LOG_WARNING, msg, vargs_list);
    963   va_end(vargs_list);
    964 }
    965 
    966 res_T
    967 get_spectral_intervals
    968   (const struct htgop* htgop,
    969    const char* func_name,
    970    const double wnum_range[2],
    971    const double* wnums,
    972    const size_t nwnums,
    973    size_t specint_range[2])
    974 {
    975   double wnum_min = 0;
    976   double wnum_max = 0;
    977   double* low = NULL;
    978   double* upp = NULL;
    979   res_T res = RES_OK;
    980   ASSERT(htgop && wnum_range && wnums && nwnums && specint_range);
    981   ASSERT(wnum_range[0] <= wnum_range[1]);
    982 
    983   wnum_min = nextafter(wnum_range[0], DBL_MAX);
    984   wnum_max = wnum_range[1];
    985   low = search_lower_bound(&wnum_min, wnums, nwnums, sizeof(double), cmp_dbl);
    986   upp = search_lower_bound(&wnum_max, wnums, nwnums, sizeof(double), cmp_dbl);
    987 
    988   if(!low || upp == wnums) {
    989     log_err(htgop,
    990       "%s: the loaded data do not overlap the wave numbers in [%g, %g].\n",
    991       func_name, wnum_range[0], wnum_range[1]);
    992     res = RES_BAD_OP;
    993     goto error;
    994   }
    995   ASSERT(low[0] >= wnum_min && (low == wnums || low[-1] < wnum_min));
    996   ASSERT(!upp || (upp[0] >= wnum_max && upp[-1] < wnum_max));
    997 
    998   specint_range[0] = low == wnums ? 0 : (size_t)(low - wnums) - 1;
    999   specint_range[1] = !upp ? nwnums - 1 : (size_t)(upp - wnums);
   1000   /* Transform the upper wnum bound in spectral interval id */
   1001   specint_range[1] = specint_range[1] - 1;
   1002 
   1003 
   1004   ASSERT(specint_range[0] <= specint_range[1]);
   1005   ASSERT(wnums[specint_range[0]+1] >  wnum_range[0]);
   1006   ASSERT(wnums[specint_range[1]+0] <  wnum_range[1]);
   1007 
   1008   ASSERT(wnums[specint_range[0]+0] <= wnum_range[0]
   1009     || specint_range[0]==0); /* The data do not include `wnum_range' */
   1010   ASSERT(wnums[specint_range[1]+1] >= wnum_range[1]
   1011     || specint_range[1] + 1 == nwnums-1);/* The data do not include `wnum_range' */
   1012 
   1013 exit:
   1014   return res;
   1015 error:
   1016   if(specint_range) {
   1017     specint_range[0] = SIZE_MAX;
   1018     specint_range[1] = 0;
   1019   }
   1020   goto exit;
   1021 }