star-line

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

sln.h (17012B)


      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 file is part of Star-Line.
      7  *
      8  * This program is free software: you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation, either version 3 of the License, or
     11  * (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     20 
     21 #ifndef SLN_H
     22 #define SLN_H
     23 
     24 #include <star/shtr.h>
     25 #include <rsys/rsys.h>
     26 
     27 #include <float.h>
     28 #include <math.h>
     29 
     30 /* Library symbol management */
     31 #if defined(SLN_SHARED_BUILD)  /* Build shared library */
     32   #define SLN_API extern EXPORT_SYM
     33 #elif defined(SLN_STATIC)  /* Use/build static library */
     34   #define SLN_API extern LOCAL_SYM
     35 #else
     36   #define SLN_API extern IMPORT_SYM
     37 #endif
     38 
     39 /* Helper macro that asserts if the invocation of the sln function `Func'
     40  * returns an error. One should use this macro on sln calls for which no
     41  * explicit error checking is performed */
     42 #ifndef NDEBUG
     43   #define SLN(Func) ASSERT(sln_ ## Func == RES_OK)
     44 #else
     45   #define SLN(Func) sln_ ## Func
     46 #endif
     47 
     48 #define SLN_TREE_DEPTH_MAX 64 /* Maximum depth of a tree */
     49 #define SLN_TREE_ARITY_MAX 256  /* Maximum arity of a tree */
     50 #define SLN_LEAF_NLINES_MAX 16384 /* Maximum number of lines per leaf */
     51 
     52 /* Forward declaration of external data structures */
     53 struct logger;
     54 struct mem_allocator;
     55 struct shtr;
     56 struct shtr_line;
     57 struct shtr_isotope_metadata;
     58 struct shtr_line_list;
     59 
     60 enum sln_mesh_type {
     61   SLN_MESH_FIT, /* Fit the spectrum */
     62   SLN_MESH_UPPER, /* Upper limit of the spectrum */
     63   SLN_MESH_TYPES_COUNT__
     64 };
     65 
     66 enum sln_line_profile {
     67   SLN_LINE_PROFILE_VOIGT,
     68   SLN_LINE_PROFILES_COUNT__
     69 };
     70 
     71 struct sln_device_create_args {
     72   struct logger* logger; /* May be NULL <=> default logger */
     73   struct mem_allocator* allocator; /* NULL <=> use default allocator */
     74   int verbose; /* Verbosity level */
     75 };
     76 #define SLN_DEVICE_CREATE_ARGS_DEFAULT__ {NULL,NULL,0}
     77 static const struct sln_device_create_args SLN_DEVICE_CREATE_ARGS_DEFAULT =
     78   SLN_DEVICE_CREATE_ARGS_DEFAULT__;
     79 
     80 struct sln_isotope {
     81   double abundance; /* in [0, 1] */
     82   int id; /* Identifier of the isotope */
     83 };
     84 
     85 struct sln_molecule {
     86   struct sln_isotope isotopes[SHTR_MAX_ISOTOPE_COUNT];
     87   double concentration;
     88   double cutoff; /* [cm^-1] */
     89   int non_default_isotope_abundances;
     90 };
     91 #define SLN_MOLECULE_NULL__ {{{0}},0,0,0}
     92 static const struct sln_molecule SLN_MOLECULE_NULL = SLN_MOLECULE_NULL__;
     93 
     94 struct sln_tree_create_args {
     95   /* Isotope metadata and list of spectral lines */
     96   struct shtr_isotope_metadata* metadata;
     97   struct shtr_line_list* lines;
     98 
     99   enum sln_line_profile line_profile;
    100   /* Mixture description */
    101   struct sln_molecule molecules[SHTR_MAX_MOLECULE_COUNT];
    102 
    103     /* Thermo dynamic properties */
    104   double pressure; /* [atm] */
    105   double temperature; /* [K] */
    106 
    107   /* Hint on the number of vertices around the line center */
    108   size_t nvertices_hint;
    109 
    110   /* Relative error used to simplify the spectrum mesh. The larger it is, the
    111    * coarser the mesh */
    112   double mesh_decimation_err; /* > 0 */
    113   enum sln_mesh_type mesh_type; /* Type of mesh to generate */
    114 
    115   /* Maximum number of children per node */
    116   unsigned arity;
    117 
    118   /* Maximum number of lines per leaf */
    119   unsigned leaf_nlines;
    120 
    121   /* When this option is enabled, the polylines of internal nodes are
    122    * constructed by merging their children's polylines in pairs (and then
    123    * simplifying the result), and repeating the process until only a single
    124    * polyline remains, which becomes the internal node's polyline.
    125    *
    126    * If this option is disabled, all child polylines are merged in a single step
    127    * before being simplified.
    128    *
    129    * Enabling this option only makes sense for trees with an arity greater than
    130    * two. For a binary tree, both methods should produce exactly the same tree,
    131    * down to the bit */
    132   int collapse_polylines;
    133 
    134   /* Advice on the number of threads to use */
    135   unsigned nthreads_hint;
    136 };
    137 #define SLN_TREE_CREATE_ARGS_DEFAULT__ { \
    138   NULL, /* metadata */ \
    139   NULL, /* line list */ \
    140   SLN_LINE_PROFILE_VOIGT, /* Profile */ \
    141   {SLN_MOLECULE_NULL__}, /* Molecules */ \
    142   0, /* Pressure [atm] */ \
    143   0, /* Temperature [K] */ \
    144   16, /* #vertices hint */ \
    145   0.01f, /* Mesh decimation error */ \
    146   SLN_MESH_UPPER, /* Mesh type */ \
    147   2, /* Arity */ \
    148   1, /* Number of lines per leaf */ \
    149   0, /* Collapse polylines */ \
    150   (unsigned)(-1), /* #threads hint */ \
    151 }
    152 static const struct sln_tree_create_args SLN_TREE_CREATE_ARGS_DEFAULT =
    153   SLN_TREE_CREATE_ARGS_DEFAULT__;
    154 
    155 struct sln_tree_read_args {
    156   /* Metadata and list of spectral lines from which the tree was constructed */
    157   struct shtr_isotope_metadata* metadata;
    158   struct shtr_line_list* lines;
    159 
    160   /* Name of the file to read or of the provided stream.
    161    * NULL <=> uses a default name for the stream to be read, which must
    162    * therefore be defined. */
    163   const char* filename; /* Name of the file to read */
    164   FILE* file; /* Stream from where data are read. NULL <=> read from file */
    165 
    166   /* Verify that the digital signature of the input lines matches the one stored
    167    * in the tree. In other words, ensure that this list of lines is indeed the
    168    * one used to construct the tree. An error is returned if the signatures do
    169    * not match.
    170    *
    171    * Although it is always advisable to verify that the data matches what is
    172    * expected, calculating the signatures of the lines can be time-consuming.
    173    * Therefore, a user who is _certain_ that the data matches can disable this
    174    * verification */
    175   int disable_line_hash_check;
    176 };
    177 #define SLN_TREE_READ_ARGS_NULL__ {NULL,NULL,NULL,NULL,0}
    178 static const struct sln_tree_read_args SLN_TREE_READ_ARGS_NULL =
    179   SLN_TREE_READ_ARGS_NULL__;
    180 
    181 struct sln_tree_write_args {
    182   /* Name of the file in which the tree is serialized.
    183    * NULL <=> uses a default name for the stream to be written, which must
    184    * therefore be defined. */
    185   const char* filename; /* Name of the file to read */
    186 
    187   /* Stream where data is written.
    188    * NULL <=> write to the file defined by "filename" */
    189   FILE* file;
    190 };
    191 #define SLN_TREE_WRITE_ARGS_NULL__ {NULL,NULL}
    192 static const struct sln_tree_write_args SLN_TREE_WRITE_ARGS_NULL =
    193   SLN_TREE_WRITE_ARGS_NULL__;
    194 
    195 struct sln_tree_desc {
    196   double mesh_decimation_err;
    197   enum sln_mesh_type mesh_type;
    198   enum sln_line_profile line_profile;
    199 
    200   double pressure; /* [atm] */
    201   double temperature; /* [K] */
    202 
    203   unsigned depth; /* #edges from the root to the deepest leaf */
    204   size_t nlines;
    205   size_t nvertices;
    206   size_t nnodes;
    207   unsigned arity;
    208   unsigned leaf_nlines;
    209 };
    210 #define SLN_TREE_DESC_NULL__ { \
    211   0,SLN_MESH_TYPES_COUNT__,SLN_LINE_PROFILES_COUNT__,0,0,0,0,0,0,0,0 \
    212 }
    213 static const struct sln_tree_desc SLN_TREE_DESC_NULL = SLN_TREE_DESC_NULL__;
    214 
    215 struct sln_thermo_props {
    216   double concentrations[SHTR_MAX_MOLECULE_COUNT];
    217   double pressure; /* [atm] */
    218   double temperature; /* [K] */
    219 };
    220 #define SLN_THERMO_PROPS_NULL__ {{0},0,0}
    221 static const struct sln_thermo_props SLN_THERMO_PROPS_NULL =
    222   SLN_THERMO_PROPS_NULL__;
    223 
    224 struct sln_node_desc {
    225   /* Range of lines belonging to the node. The endpoints are included */
    226   size_t ilines[2];
    227   size_t nvertices;
    228   unsigned nchildren;
    229 };
    230 #define SLN_NODE_DESC_NULL__ {{0,0},0,0}
    231 static const struct sln_node_desc SLN_NODE_DESC_NULL = SLN_NODE_DESC_NULL__;
    232 
    233 struct sln_vertex { /* 8 Bytes */
    234   float wavenumber; /* in cm^-1 */
    235   float ka;
    236 };
    237 #define SLN_VERTEX_NULL__ {0,0}
    238 static const struct sln_vertex SLN_VERTEX_NULL = SLN_VERTEX_NULL__;
    239 
    240 struct sln_mesh {
    241   const struct sln_vertex* vertices;
    242   size_t nvertices;
    243 };
    244 #define SLN_MESH_NULL__ {NULL,0}
    245 static const struct sln_mesh SLN_MESH_NULL = SLN_MESH_NULL__;
    246 
    247 struct sln_mixture_load_args {
    248   const char* filename; /* Name of the file to load or of the provided stream */
    249   FILE* file; /* Stream from where data are loaded. NULL <=> load from file */
    250 
    251   /* Metadata from which the mix is defined */
    252   struct shtr_isotope_metadata* molparam;
    253 };
    254 #define SLN_MIXTURE_LOAD_ARGS_NULL__ {NULL,NULL,NULL}
    255 static const struct sln_mixture_load_args SLN_MIXTURE_LOAD_ARGS_NULL =
    256   SLN_MIXTURE_LOAD_ARGS_NULL__;
    257 
    258 struct sln_line {
    259   double wavenumber; /* Line center wrt pressure in cm^-1 */
    260   double profile_factor; /* m^-1.cm^-1 (1e2*density*intensity) */
    261   double gamma_d; /* Doppler half width */
    262   double gamma_l; /* Lorentz half width */
    263   enum shtr_molecule_id molecule_id;
    264 };
    265 #define SLN_LINE_NULL__ {0,0,0,0,SHTR_MOLECULE_ID_NULL}
    266 static const struct sln_line SLN_LINE_NULL = SLN_LINE_NULL__;
    267 
    268 /* External data structure */
    269 struct ssp_rng;
    270 
    271 /* Forward declarations of opaque data structures */
    272 struct sln_device;
    273 struct sln_mixture;
    274 struct sln_node;
    275 struct sln_tree;
    276 
    277 BEGIN_DECLS
    278 
    279 /*******************************************************************************
    280  * Device API
    281  ******************************************************************************/
    282 SLN_API res_T
    283 sln_device_create
    284   (const struct sln_device_create_args* args,
    285    struct sln_device** sln);
    286 
    287 SLN_API res_T
    288 sln_device_ref_get
    289   (struct sln_device* sln);
    290 
    291 SLN_API res_T
    292 sln_device_ref_put
    293   (struct sln_device* sln);
    294 
    295 
    296 /*******************************************************************************
    297  * Mixture API
    298  ******************************************************************************/
    299 SLN_API res_T
    300 sln_mixture_load
    301   (struct sln_device* dev,
    302    const struct sln_mixture_load_args* args,
    303    struct sln_mixture** mixture);
    304 
    305 SLN_API res_T
    306 sln_mixture_ref_get
    307   (struct sln_mixture* mixture);
    308 
    309 SLN_API res_T
    310 sln_mixture_ref_put
    311   (struct sln_mixture* mixture);
    312 
    313 SLN_API int
    314 sln_mixture_get_molecule_count
    315   (const struct sln_mixture* mixture);
    316 
    317 SLN_API enum shtr_molecule_id
    318 sln_mixture_get_molecule_id
    319   (const struct sln_mixture* mixture,
    320    const int index);
    321 
    322 SLN_API res_T
    323 sln_mixture_get_molecule
    324   (const struct sln_mixture* mixture,
    325    const int index,
    326    struct sln_molecule* molecule);
    327 
    328 /*******************************************************************************
    329  * Tree API
    330  ******************************************************************************/
    331 SLN_API res_T
    332 sln_tree_create
    333   (struct sln_device* dev,
    334    const struct sln_tree_create_args* args,
    335    struct sln_tree** tree);
    336 
    337 /* Read a tree serialized with the "sln_tree_write" function */
    338 SLN_API res_T
    339 sln_tree_read
    340   (struct sln_device* sln,
    341    const struct sln_tree_read_args* args,
    342    struct sln_tree** tree);
    343 
    344 SLN_API res_T
    345 sln_tree_ref_get
    346   (struct sln_tree* tree);
    347 
    348 SLN_API res_T
    349 sln_tree_ref_put
    350   (struct sln_tree* tree);
    351 
    352 SLN_API res_T
    353 sln_tree_get_desc
    354   (const struct sln_tree* tree,
    355    struct sln_tree_desc* desc);
    356 
    357 SLN_API const struct sln_node* /* NULL <=> No node */
    358 sln_tree_get_root
    359   (const struct sln_tree* tree);
    360 
    361 SLN_API res_T
    362 sln_tree_get_line
    363   (const struct sln_tree* tree,
    364    const size_t iline,
    365    /* Thermodynamic properties to which the line is recovered.
    366     * Can be NULL, so these properties are those used to build the tree */
    367    const struct sln_thermo_props* props,
    368    struct sln_line* line);
    369 
    370 SLN_API res_T
    371 sln_tree_write
    372   (const struct sln_tree* tree,
    373    const struct sln_tree_write_args* args);
    374 
    375 /*******************************************************************************
    376  * Node API
    377  ******************************************************************************/
    378 SLN_API int
    379 sln_node_is_leaf
    380   (const struct sln_node* node);
    381 
    382 SLN_API unsigned
    383 sln_node_get_child_count
    384   (const struct sln_tree* tree,
    385    const struct sln_node* node);
    386 
    387 /* The node must not be a leaf */
    388 SLN_API const struct sln_node*
    389 sln_node_get_child
    390   (const struct sln_tree* tree,
    391    const struct sln_node* node,
    392    const unsigned ichild); /* 0 or #children */
    393 
    394 SLN_API double
    395 sln_node_eval
    396   (const struct sln_tree* tree,
    397    const struct sln_node* node,
    398    /* Thermodynamic properties to which the node lines are evaluated.
    399     * Can be NULL, so these properties are those used to build the tree */
    400    const struct sln_thermo_props* props,
    401    const double wavenumber); /* In cm^-1 */
    402 
    403 SLN_API res_T
    404 sln_node_get_desc
    405   (const struct sln_tree* tree,
    406    const struct sln_node* node,
    407    struct sln_node_desc* desc);
    408 
    409 SLN_API res_T
    410 sln_node_get_mesh
    411   (const struct sln_tree* tree,
    412    const struct sln_node* node,
    413    struct sln_mesh* mesh);
    414 
    415 /* Sample a leaf based on its importance, that is, its contribution to the
    416  * node's absorption spectrum at the given wave number */
    417 SLN_API const struct sln_node* /* NULL <=> an error occurs */
    418 sln_node_sample_leaf
    419   (const struct sln_tree* tree,
    420    const struct sln_node* node,
    421    const double nu, /* [cm^-1] */
    422    struct ssp_rng* rng,
    423    double* proba); /* May be NULL */
    424 
    425 /*******************************************************************************
    426  * Miscellaneous
    427  ******************************************************************************/
    428 SLN_API double
    429 sln_line_eval
    430   (const struct sln_tree* tree,
    431    const struct sln_line* line,
    432    const double wavenumber); /* In cm^-1 */
    433 
    434 SLN_API double
    435 sln_mesh_eval
    436   (const struct sln_mesh* mesh,
    437    const double wavenumber); /* In cm^-1 */
    438 
    439 /*******************************************************************************
    440  * Helper functions
    441  ******************************************************************************/
    442 /* Purpose: to calculate the Faddeeva function with relative error less than
    443  * 10^(-4).
    444  *
    445  * Inputs: x and y, parameters for the Voigt function :
    446  * - x is defined as x=(nu-nu_c)/gamma_D*sqrt(ln(2)) with nu the current
    447  *   wavenumber, nu_c the wavenumber at line center, gamma_D the Doppler
    448  *   linewidth.
    449  * - y is defined as y=gamma_L/gamma_D*sqrt(ln(2)) with gamma_L the Lorentz
    450  *   linewith and gamma_D the Doppler linewidth
    451  *
    452  * Output: k, the Voigt function; it has to be multiplied by
    453  * sqrt(ln(2)/pi)*1/gamma_D so that the result may be interpretable in terms of
    454  * line profile.
    455  *
    456  * TODO check the copyright */
    457 SLN_API double
    458 sln_faddeeva
    459   (const double x,
    460    const double y);
    461 
    462 static INLINE double
    463 sln_compute_line_half_width_doppler
    464   (const double nu, /* Line center wrt pressure in cm^-1 */ /* TODO check this */
    465    const double molar_mass, /* In kg.mol^-1 */
    466    const double temperature) /* In K */
    467 {
    468   /* kb = 1.3806e-23
    469    * Na = 6.02214076e23
    470    * c = 299792458
    471    * sqrt(2*log(2)*kb*Na)/c */
    472   const double sqrt_two_ln2_kb_Na_over_c = 1.1324431552553545042e-08;
    473   const double gamma_d = nu * sqrt_two_ln2_kb_Na_over_c * sqrt(temperature/molar_mass);
    474   ASSERT(temperature >= 0 && molar_mass > 0);
    475   return gamma_d;
    476 }
    477 
    478 static INLINE double
    479 sln_compute_line_half_width_lorentz
    480   (const double gamma_air, /* Air broadening half width [cm^-1.atm^-1] */
    481    const double gamma_self, /* Air broadening half width [cm^-1.atm^-1] */
    482    const double temperature, /* [K] */
    483    const double pressure, /* [atm^-1] */
    484    const double n_air,
    485    const double concentration)
    486 {
    487   const double TREF=296; /* Ref temperature [K] for HITRAN/HITEMP database */
    488   const double Ps = pressure * concentration;
    489   const double n_self = n_air; /* In HITRAN n_air == n_self */
    490   const double gamma_l =
    491     pow(TREF/temperature, n_air)  * (pressure - Ps) * gamma_air
    492   + pow(TREF/temperature, n_self) * Ps * gamma_self;
    493   ASSERT(gamma_air > 0 && gamma_self > 0);
    494   ASSERT(pressure > 0 && concentration >= 0 && concentration <= 1);
    495 
    496   return gamma_l;
    497 }
    498 
    499 static INLINE double
    500 sln_compute_voigt_profile
    501   (const double wavenumber, /* In cm^-1 */
    502    const double nu, /* Line center in cm^-1 */
    503    const double gamma_d, /* Doppler line half width in cm^-1 */
    504    const double gamma_l) /* Lorentz line half width in cm^-1 */
    505 {
    506   /* Constants */
    507   const double sqrt_ln2 = 0.83255461115769768821; /* sqrt(log(2)) */
    508   const double sqrt_ln2_over_pi = 0.46971863934982566180; /* sqrt(log(2)/M_PI) */
    509   const double sqrt_ln2_over_gamma_d = sqrt_ln2 / gamma_d;
    510 
    511   const double x = (wavenumber - nu) * sqrt_ln2_over_gamma_d;
    512   const double y = gamma_l * sqrt_ln2_over_gamma_d;
    513   const double k = sln_faddeeva(x, y);
    514   return k*sqrt_ln2_over_pi/gamma_d;
    515 }
    516 
    517 static INLINE const char*
    518 sln_mesh_type_cstr(const enum sln_mesh_type type)
    519 {
    520   const char* cstr = NULL;
    521 
    522   switch(type) {
    523     case SLN_MESH_FIT:   cstr = "fit"; break;
    524     case SLN_MESH_UPPER: cstr = "upper"; break;
    525     default: FATAL("Unreachable code\n"); break;
    526   }
    527   return cstr;
    528 }
    529 
    530 static INLINE const char*
    531 sln_line_profile_cstr(const enum sln_line_profile profile)
    532 {
    533   const char* cstr = NULL;
    534 
    535   switch(profile) {
    536     case SLN_LINE_PROFILE_VOIGT: cstr = "voigt"; break;
    537     default: FATAL("Unreachable code\n"); break;
    538   }
    539   return cstr;
    540 }
    541 
    542 END_DECLS
    543 
    544 #endif /* SLN_H */