star-hitran

Load line-by-line data from the HITRAN database
git clone git://git.meso-star.fr/star-hitran.git
Log | Files | Refs | README | LICENSE

commit 350e81af171467d07413abd4d400c8a3304720ec
parent 181d4d63c4b31082ac5113f7e55554bb3d0e24ed
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 16 May 2022 16:45:40 +0200

Manage pressure and molecul cutoffs in lines view

Diffstat:
Msrc/shtr.h | 10+++++++---
Msrc/shtr_lines_view.c | 268+++++++++++++++++++++++++++++++++----------------------------------------------
Msrc/test_shtr_lines.c | 56+++++++++++++++++++++++++++++++++++++++++++++++---------
3 files changed, 165 insertions(+), 169 deletions(-)

diff --git a/src/shtr.h b/src/shtr.h @@ -45,7 +45,7 @@ struct shtr_isotope { double abundance; /* in ]0, 1] */ double Q296K; /* Partition function at Tref = 296K */ - double molar_mass; /* In g */ + double molar_mass; /* In g.mol^-1 */ /* Local idx of the molecule to which the isotope belongs */ size_t molecule_id_local; @@ -133,8 +133,9 @@ struct shtr_isotope_selection { size_t nisotopes; /* 0 <=> select all the isotopes */ int32_t id; /* Molecule to which the isotopes belongs */ + double cutoff; /* In cm^-1 */ }; -#define SHTR_ISOTOPE_SELECTION_NULL__ {0} +#define SHTR_ISOTOPE_SELECTION_NULL__ {{0}, 0, 0, 0} static const struct shtr_isotope_selection SHTR_ISOTOPE_SELECTION_NULL = SHTR_ISOTOPE_SELECTION_NULL__; @@ -144,8 +145,11 @@ struct shtr_lines_view_create_args { /* List of molecule be selected */ struct shtr_isotope_selection molecules[SHTR_MAX_MOLECULES_COUNT]; size_t nmolecules; + + double pressure; /* In atm. Used to compute the line center */ }; -#define SHTR_LINES_VIEW_CREATE_ARGS_NULL__ {0} +#define SHTR_LINES_VIEW_CREATE_ARGS_NULL__ \ + {{0,0}, {SHTR_ISOTOPE_SELECTION_NULL__}, 0, 0} static const struct shtr_lines_view_create_args SHTR_LINES_VIEW_CREATE_ARGS_NULL = SHTR_LINES_VIEW_CREATE_ARGS_NULL__; diff --git a/src/shtr_lines_view.c b/src/shtr_lines_view.c @@ -23,7 +23,6 @@ #include "shtr_lines_list_c.h" #include "shtr_log.h" -#include <rsys/algorithm.h> #include <rsys/cstr.h> #include <rsys/dynamic_array_size_t.h> @@ -35,200 +34,155 @@ struct shtr_lines_view { ref_T ref; }; +struct molecule_selection { + /* Map the isotope local identifier to a boolean defining if the isotope is + * selected or not */ + char isotopes[SHTR_MAX_ISOTOPES_COUNT]; + double cutoff; /* Molecule cutoff */ +}; + /******************************************************************************* * Helper functions ******************************************************************************/ static res_T -check_shtr_lines_view_create_args +check_shtr_isotope_selection (struct shtr* shtr, - const struct shtr_lines_view_create_args* args) + const char* caller, + const struct shtr_isotope_selection* molecule) { - if(!args) return RES_BAD_ARG; - if(args->wavenumber_range[0] > args->wavenumber_range[1]) { - log_err(shtr, "Invalid lines view spectral range [%g, %g].\n", - args->wavenumber_range[0], args->wavenumber_range[1]); - return RES_BAD_ARG; - } - return RES_OK; -} + size_t i; + ASSERT(caller && molecule); -static int -cmp_line_wavenumber(const void* key, const void* entry) -{ - const double wavenumber = *((double*)key); - const struct shtr_line* line = entry; - if(wavenumber < line->wavenumber) { - return -1; - } else if(wavenumber > line->wavenumber) { - return 1; - } else { - return 0; + if((size_t)molecule->id >= SHTR_MAX_MOLECULES_COUNT) { + log_err(shtr, + "%s: molecule %d: invalid molecule identifier. " + "It must be less than %d.\n", + caller, molecule->id, SHTR_MAX_MOLECULES_COUNT); + return RES_BAD_ARG; } -} - -static res_T -find_line_id_range - (const struct shtr_lines_view* view, - const double wavenumber_range[2], - size_t line_id_range[2]) -{ - const struct shtr_line* lines = NULL; - const struct shtr_line* l0 = NULL; - const struct shtr_line* l1 = NULL; - size_t nlines = 0; - double exclusive_bound = 0; - res_T res = RES_OK; - ASSERT(view && wavenumber_range && line_id_range); - - res = shtr_lines_list_get(view->list, &lines); - if(res != RES_OK) goto error; - res = shtr_lines_list_get_size(view->list, &nlines); - if(res != RES_OK) goto error; - l0 = search_lower_bound(&wavenumber_range[0], lines, nlines, - sizeof(struct shtr_line), cmp_line_wavenumber); - if(l0) { /* No lower bound is found */ - line_id_range[0] = (size_t)(l0 - lines); - } else { - line_id_range[0] = SIZE_MAX; - line_id_range[1] = 0; - goto exit; + if(molecule->cutoff <= 0) { + log_err(shtr, "%s: molecule %d: invalid cutoff %g.\n", + caller, molecule->id, molecule->cutoff); + return RES_BAD_ARG; } - /* The search_lower_bound function returns the first entry that is greater - * than _or_ equal to the search value. This is fine for finding an inclusive - * lower bound, but not for finding an inclusive upper bound like below. In - * the latter case, it is instead necessary to find the first entry greater - * than the value sought and return the previous one. To do this, we - * therefore seek the next representable floating point number directly above - * the upper bound wavenumber.*/ - exclusive_bound = nextafter(wavenumber_range[1], INF); - l1 = search_lower_bound(&exclusive_bound, lines, nlines, - sizeof(struct shtr_line), cmp_line_wavenumber); - if(!l1) { /* No upper bound is found. Take all the lines to the end */ - line_id_range[1] = nlines - 1; - } else { - line_id_range[1] = (size_t)(l1 - lines) - 1; + FOR_EACH(i, 0, molecule->nisotopes) { + if(molecule->isotope_ids_local[i] >= SHTR_MAX_ISOTOPES_COUNT) { + log_err(shtr, + "%s: molecule %d: isotope %d: invalid isotope local identifier. " + "It must be less than %d.\n", + caller, + molecule->id, + molecule->isotope_ids_local[i], + SHTR_MAX_ISOTOPES_COUNT); + return RES_BAD_ARG; + } } - ASSERT(line_id_range[0] <= line_id_range[1]); - ASSERT(line_id_range[1] < nlines); - -exit: - return res; -error: - goto exit; -} - -static int -cmp_isotope_selection(const void* a, const void* b) -{ - const struct shtr_isotope_selection* molecule0 = a; - const struct shtr_isotope_selection* molecule1 = b; - ASSERT(a && b); - return molecule0->id - molecule1->id; -} - -static int -cmp_isotope_selection_molecule_id(const void* key, const void* val) -{ - const int32_t* molecule_id = key; - const struct shtr_isotope_selection* molecule = val; - ASSERT(key && val); - return *molecule_id - molecule->id; + return RES_OK; } -static int -cmp_int32(const void* a, const void* b) -{ - ASSERT(a && b); - return *((int32_t*)a) - *((int32_t*)b); -} -/* Return 1 if the line belongs to an isotope to be selected and 0 otherwise */ -static INLINE int -is_the_line_to_select - (const struct shtr_line* line, - const struct shtr_isotope_selection* molecules, - const size_t nmolecules) +static res_T +check_shtr_lines_view_create_args + (struct shtr* shtr, + const char* caller, + const struct shtr_lines_view_create_args* args) { - const struct shtr_isotope_selection* molecule = NULL; - int32_t* isotope = NULL; - ASSERT(molecules); - - molecule = search_lower_bound(&line->molecule_id, molecules, nmolecules, - sizeof(*molecules), cmp_isotope_selection_molecule_id); + size_t i; + ASSERT(caller); - if(molecule == NULL || molecule->id != line->molecule_id) - return 0; /* The lines does not belongs to a molecule to be selected */ + if(!args) return RES_BAD_ARG; - if(molecule->nisotopes == 0) /* <=> All the isotopes are accepted */ - return 1; + if(args->wavenumber_range[0] > args->wavenumber_range[1]) { + log_err(shtr, "%s: invalid lines view spectral range [%g, %g].\n", + caller, args->wavenumber_range[0], args->wavenumber_range[1]); + return RES_BAD_ARG; + } - isotope = search_lower_bound(&line->isotope_id_local, - molecule->isotope_ids_local, molecule->nisotopes, - sizeof(*molecule->isotope_ids_local), cmp_int32); + if(args->pressure < 0) { + log_err(shtr, "%s: invalid pressure %g.\n", caller, args->pressure); + return RES_BAD_ARG; + } - if(isotope == NULL || *isotope != line->isotope_id_local) - return 0; /* The lines does not belongs to an isotope to be selected */ + FOR_EACH(i, 0, args->nmolecules) { + const res_T res = check_shtr_isotope_selection + (shtr, caller, &args->molecules[i]); + if(res != RES_OK) return res; - return 1; + } + return RES_OK; } static res_T select_lines (struct shtr_lines_view* view, + const char* caller, const struct shtr_lines_view_create_args* args) { const struct shtr_line* lines; - struct shtr_isotope_selection molecules[SHTR_MAX_MOLECULES_COUNT]; - size_t line_id_range[2]; - size_t i; + struct molecule_selection selection[SHTR_MAX_MOLECULES_COUNT]; + size_t imol; + size_t iiso; + size_t iline; + size_t nlines; res_T res = RES_OK; - ASSERT(view && args); + ASSERT(view && caller && args); /* Nothing to do */ if(args->nmolecules == 0) goto exit; - /* Sort in ascending order the ids of the molecules to include */ - memcpy(molecules, args->molecules, args->nmolecules*sizeof(molecules[0])); - qsort - (molecules, - args->nmolecules, - sizeof(molecules[0]), - cmp_isotope_selection); + /* Setup the selection lookup table that map the isotope of a molecule to a + * boolean defining if it is selected */ + memset(selection, 0, sizeof(selection)); + FOR_EACH(imol, 0, args->nmolecules) { + const int32_t mol_id = args->molecules[imol].id; + ASSERT(mol_id < SHTR_MAX_MOLECULES_COUNT); + selection[mol_id].cutoff = args->molecules[imol].cutoff; + + if(args->molecules[imol].nisotopes == 0) { /* Select all isotopes */ + FOR_EACH(iiso, 0, SHTR_MAX_ISOTOPES_COUNT) { + selection[mol_id].isotopes[iiso] = 1; + } - /* Sort in ascending order the ids of the par molecule isotopes to include */ - FOR_EACH(i, 0, args->nmolecules) { - qsort - (molecules[i].isotope_ids_local, - molecules[i].nisotopes, - sizeof(molecules[i].isotope_ids_local[0]), - cmp_int32); + } else { + FOR_EACH(iiso, 0, args->molecules[imol].nisotopes) { + const int32_t iso_id = args->molecules[imol].isotope_ids_local[iiso]; + ASSERT(iso_id < SHTR_MAX_ISOTOPES_COUNT); + selection[mol_id].isotopes[iso_id] = 1; + } + } } -#ifndef NDEBUG - FOR_EACH(i, 1, args->nmolecules) { - size_t j; - ASSERT(molecules[i].id >= molecules[i-1].id); - FOR_EACH(j, 1, molecules[i].nisotopes) { - ASSERT(molecules[i].isotope_ids_local[j] >= molecules[i].isotope_ids_local[j-1]); + lines = darray_line_cdata_get(&view->list->lines); + nlines = darray_line_size_get(&view->list->lines); + + /* Iterate through list of lines to find the ones to select based on spectral + * range and isotope selection */ + FOR_EACH(iline, 0, nlines) { + const struct shtr_line* line = lines + iline; + double nu = 0; + + /* The line is not selected */ + if(selection[line->molecule_id].isotopes[line->isotope_id_local] == 0) { + continue; } - } -#endif - res = find_line_id_range(view, args->wavenumber_range, line_id_range); - if(res != RES_OK) goto error; + /* Compute the line center for the submitted pressure */ + nu = line->wavenumber + line->delta_air * args->pressure; - lines = darray_line_cdata_get(&view->list->lines); - FOR_EACH(i, line_id_range[0], line_id_range[1]+1) { - if(is_the_line_to_select(&lines[i], molecules, args->nmolecules)) { - res = darray_size_t_push_back(&view->line_ids, &i); - if(res != RES_OK) { - log_err(view->list->shtr, - "Could not register the line into the view -- %s.\n", - res_to_cstr(res)); - goto error; - } + /* The line is out of the spectral range */ + if(nu + selection[line->molecule_id].cutoff < args->wavenumber_range[0] + || nu - selection[line->molecule_id].cutoff > args->wavenumber_range[1]) { + continue; + } + + res = darray_size_t_push_back(&view->line_ids, &iline); + if(res != RES_OK) { + log_err(view->list->shtr, + "%s: could not register the line into the view -- %s.\n", + caller, res_to_cstr(res)); + goto error; } } @@ -263,7 +217,7 @@ shtr_lines_view_create res_T res = RES_OK; if(!list || !out_view) { res = RES_BAD_ARG; goto error; } - res = check_shtr_lines_view_create_args(list->shtr, args); + res = check_shtr_lines_view_create_args(list->shtr, FUNC_NAME, args); if(res != RES_OK) goto error; view = MEM_CALLOC(list->shtr->allocator, 1, sizeof(*view)); if(!view) { @@ -276,7 +230,7 @@ shtr_lines_view_create view->list = list; darray_size_t_init(list->shtr->allocator, &view->line_ids); - res = select_lines(view, args); + res = select_lines(view, FUNC_NAME, args); if(res != RES_OK) goto error; exit: diff --git a/src/test_shtr_lines.c b/src/test_shtr_lines.c @@ -251,12 +251,9 @@ check_view CHK(shtr_lines_list_get(list, &lines) == RES_OK); FOR_EACH(i, 0, n) { const struct shtr_line* line = NULL; + double nu = 0; size_t imolecule; - /* Discard lines that are out of ranges */ - if(lines[i].wavenumber < args->wavenumber_range[0]) continue; - if(lines[i].wavenumber > args->wavenumber_range[1]) break; - /* Discard lines that does not belongs to a listed molecule */ FOR_EACH(imolecule, 0, args->nmolecules) { if(lines[i].molecule_id == args->molecules[imolecule].id) break; @@ -264,6 +261,15 @@ check_view if(imolecule >= args->nmolecules) continue; /* Skip the molecule */ + /* Compute the line center for the given pressure */ + nu = lines[i].wavenumber + lines[i].delta_air * args->pressure; + + /* Discard lines that are out of ranges */ + if(nu + args->molecules[imolecule].cutoff < args->wavenumber_range[0]) + continue; + if(nu - args->molecules[imolecule].cutoff > args->wavenumber_range[1]) + continue; + /* nisotope == 0 <=> All isotopes must be selected */ if(args->molecules[imolecule].nisotopes) { size_t iisotope; @@ -355,6 +361,8 @@ test_view(struct shtr* shtr) }; const size_t nlines = sizeof(l) / sizeof(struct shtr_line); + const double cutoff = 0.01; + struct shtr_lines_view_create_args args = SHTR_LINES_VIEW_CREATE_ARGS_NULL; struct shtr_lines_list* list = NULL; struct shtr_lines_view* view = NULL; @@ -374,7 +382,11 @@ test_view(struct shtr* shtr) args.molecules[0].id = 1; args.molecules[1].id = 2; args.molecules[2].id = 3; + args.molecules[0].cutoff = cutoff; + args.molecules[1].cutoff = cutoff; + args.molecules[2].cutoff = cutoff; args.nmolecules = 3; + args.pressure = 0; CHK(shtr_lines_view_create(NULL, &args, &view) == RES_BAD_ARG); CHK(shtr_lines_view_create(list, NULL, &view) == RES_BAD_ARG); CHK(shtr_lines_view_create(list, &args, NULL) == RES_BAD_ARG); @@ -412,9 +424,16 @@ test_view(struct shtr* shtr) CHK(n == 0); CHK(shtr_lines_view_ref_put(view) == RES_OK); - args.wavenumber_range[0] = 2; - args.wavenumber_range[1] = INF; + args.pressure = -1; + CHK(shtr_lines_view_create(list, &args, &view) == RES_BAD_ARG); + args.pressure = 0; + args.molecules[0].cutoff = -1; args.nmolecules = 3; + CHK(shtr_lines_view_create(list, &args, &view) == RES_BAD_ARG); + args.molecules[0].cutoff = cutoff; + + args.wavenumber_range[0] = nextafter(l[nlines-1].wavenumber, INF) + cutoff; + args.wavenumber_range[1] = INF; CHK(shtr_lines_view_create(list, &args, &view) == RES_OK); CHK(shtr_lines_view_get_size(view, &n) == RES_OK); CHK(n == 0); @@ -436,7 +455,7 @@ test_view(struct shtr* shtr) args.wavenumber_range[1] = 0.29477; CHK(shtr_lines_view_create(list, &args, &view) == RES_OK); CHK(shtr_lines_view_get_size(view, &n) == RES_OK); - CHK(n == 1); + CHK(n == 6); check_view(list, view, &args); CHK(shtr_lines_view_ref_put(view) == RES_OK); @@ -444,7 +463,7 @@ test_view(struct shtr* shtr) args.wavenumber_range[1] = 0.21283; CHK(shtr_lines_view_create(list, &args, &view) == RES_OK); CHK(shtr_lines_view_get_size(view, &n) == RES_OK); - CHK(n == 10); + CHK(n == 14); check_view(list, view, &args); CHK(shtr_lines_view_ref_put(view) == RES_OK); @@ -467,7 +486,7 @@ test_view(struct shtr* shtr) args.nmolecules = 2; CHK(shtr_lines_view_create(list, &args, &view) == RES_OK); CHK(shtr_lines_view_get_size(view, &n) == RES_OK); - CHK(n == 6); + CHK(n == 10); check_view(list, view, &args); CHK(shtr_lines_view_ref_put(view) == RES_OK); @@ -479,15 +498,34 @@ test_view(struct shtr* shtr) check_view(list, view, &args); CHK(shtr_lines_view_ref_put(view) == RES_OK); + args.molecules[0].id = 1; + args.molecules[0].cutoff = 1e-6; + args.molecules[0].nisotopes = 0; + args.nmolecules = 1; + args.wavenumber_range[0] = 0.1899; + args.wavenumber_range[1] = 0.195; + CHK(shtr_lines_view_create(list, &args, &view) == RES_OK); + CHK(shtr_lines_view_get_size(view, &n) == RES_OK); + CHK(n == 1); + CHK(shtr_lines_view_ref_put(view) == RES_OK); + args.pressure = 1; + CHK(shtr_lines_view_create(list, &args, &view) == RES_OK); + CHK(shtr_lines_view_get_size(view, &n) == RES_OK); + CHK(n == 2); + check_view(list, view, &args); + CHK(shtr_lines_view_ref_put(view) == RES_OK); + args.wavenumber_range[0] = 0; args.wavenumber_range[1] = INF; args.molecules[0].id = 3; + args.molecules[0].cutoff = cutoff; args.molecules[0].isotope_ids_local[0] = 4; args.molecules[0].isotope_ids_local[1] = 1; args.molecules[0].isotope_ids_local[2] = 2; args.molecules[0].isotope_ids_local[3] = 0; args.molecules[0].nisotopes = 4; args.molecules[1].id = 1; + args.molecules[0].cutoff = cutoff; args.molecules[1].isotope_ids_local[0] = 4; args.molecules[1].isotope_ids_local[1] = 3; args.molecules[1].nisotopes = 2;