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 af5fea6978ffd0a8e3e5ba0070652a479a613377
parent 343901793f9902e5b1fbad7e733692575f20f4f2
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue,  8 Feb 2022 14:41:40 +0100

Implement sht_isotopes_metadata_load[_stream]

Diffstat:
Mcmake/CMakeLists.txt | 3++-
Adoc/isotopes.txt | 17+++++++++++++++++
Ddoc/isotopologues.txt | 17-----------------
Msrc/sht.h | 18+++++++++---------
Asrc/sht_isotope_metadata.c | 621+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/sht_isotopologues.c | 426-------------------------------------------------------------------------------
Asrc/test_sht_isotope_metadata.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 734 insertions(+), 453 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -46,7 +46,7 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SHT_FILES_SRC sht.c sht_log.c - sht_isotopologues.c) + sht_isotope_metadata.c) set(SHT_FILES_INC sht_c.h sht_log.h) @@ -86,6 +86,7 @@ if(NOT NO_TEST) endfunction() new_test(test_sht) + new_test(test_sht_isotope_metadata) endif() diff --git a/doc/isotopes.txt b/doc/isotopes.txt @@ -0,0 +1,17 @@ +sht ::= <comment-line> + [<molecule|isotope>...] + +<molecule> ::= <molecule-name> (<id>) +<molecule-name> ::= <alpha>[<alnum>...] + +<isotope> ::= <id> <abundance> <Q(296K)> <gj> <molar-mass(g)> +<abundance> ::= REAL # in ]0, 1] +<Q(296K)> ::= REAL # partition function at Tref = 296K in ]0, inf) +<gj> ::= INTEGER # state independant degeneracy factor (no check) +<molar-mass> ::= REAL # ]0, inf) + +<alpha> ::= A-Za-z +<alnum> ::= A-Za-z0-9 + +<isotope> ::= <id> +<id> ::= INTEGER diff --git a/doc/isotopologues.txt b/doc/isotopologues.txt @@ -1,17 +0,0 @@ -sht ::= <comment-line> - [<molecule|isotope>...] - -<molecule> ::= <molecule-name> (<id>) -<molecule-name> ::= <alpha>[<alnum>...] - -<isotope> ::= <id> <abundance> <Q(296K)> <gj> <molar-mass(g)> -<abundance> ::= REAL -<Q(296K)> ::= REAL -<gj> ::= INTEGER -<molar-mass> ::= REAL - -<alpha> ::= A-Za-z -<alnum> ::= A-Za-z0-9 - -<isotope> ::= <id> -<id> ::= INTEGER diff --git a/src/sht.h b/src/sht.h @@ -50,7 +50,7 @@ static const struct sht_create_args SHT_CREATE_ARGS_DEFAULT = /* Forware declarations */ struct sht; -struct sht_isotopologues; +struct sht_isotope_metadata; BEGIN_DECLS @@ -74,25 +74,25 @@ sht_ref_put * Isotopologues API ******************************************************************************/ SHT_API res_T -sht_isotopologues_load +sht_isotope_metadata_load (struct sht* sht, const char* path, - struct sht_isotopologues** isotopologues); + struct sht_isotope_metadata** metadata); SHT_API res_T -sht_isotopologues_load_from_stream +sht_isotope_metadata_load_stream (struct sht* sht, FILE* stream, const char* stream_name, /* NULL <=> use default stream name */ - struct sht_isotopologues** isotopologues); + struct sht_isotope_metadata** metadata); SHT_API res_T -sht_isotopologues_ref_get - (struct sht_isotopologues* isotopologues); +sht_isotope_metadata_ref_get + (struct sht_isotope_metadata* metadata); SHT_API res_T -sht_isotopologues_ref_put - (struct sht_isotopologues* isotopologues); +sht_isotope_metadata_ref_put + (struct sht_isotope_metadata* metadata); END_DECLS diff --git a/src/sht_isotope_metadata.c b/src/sht_isotope_metadata.c @@ -0,0 +1,621 @@ +/* Copyright (C) 2022 CNRS - LMD + * Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2022 Université Paul Sabatier - IRIT + * Copyright (C) 2022 Université Paul Sabatier - Laplace + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* strtok_r support */ + +#include "sht.h" +#include "sht_c.h" +#include "sht_log.h" + +#include <rsys/cstr.h> +#include <rsys/dynamic_array.h> +#include <rsys/hash_table.h> +#include <rsys/ref_count.h> +#include <rsys/str.h> +#include <rsys/text_reader.h> + +#include <ctype.h> +#include <string.h> + +#define C_FORMAT_double "%g" +#define C_FORMAT_int "%d" + +struct param_desc { + const char* path; /* Path where the param lies */ + const char* name; /* Name of the param */ + size_t iline; /* Line number of the param */ + + /* Domain of the param */ + double low, upp; + int is_low_incl; /* Define if the lower bound is inclusive */ + int is_upp_incl; /* Define if the upper bound is inclusive */ +}; +#define PARAM_DESC_NULL__ {NULL, NULL, 0, 0, 0, 0, 0} +static const struct param_desc PARAM_DESC_NULL = PARAM_DESC_NULL__; + +struct isotope { + double abundance; /* in ]0, 1] */ + double Q296K; /* Partition function at Tref = 296K */ + double molar_mass; /* In g */ + size_t molecule; /* Registered molecule to which the isotope belongs */ + int gj; /* State independent degeneracy factor */ + int id; /* Unique identifier of the isotope */ +}; +#define ISOTOPOLOGUE_NULL__ {0,0,0,0,0,0} +static const struct isotope ISOTOPOLOGUE_NULL = ISOTOPOLOGUE_NULL__; + +/* Generate the dynamic array of isotopes */ +#define DARRAY_NAME isotope +#define DARRAY_DATA struct isotope +#include <rsys/dynamic_array.h> + +struct molecule { + struct str name; + size_t isotopes_range[2]; /* Range of the 1st and last isotopes */ + int id; /* Unique identifier of the molecule */ +}; +#define MOLECULE_IS_VALID(Molecule) ((Molecule)->id >= 0) + +static INLINE void +molecule_clear(struct molecule* molecule) +{ + ASSERT(molecule); + molecule->isotopes_range[0] = SIZE_MAX; + molecule->isotopes_range[1] = 0; + molecule->id = -1; +} + +static INLINE void +molecule_init(struct mem_allocator* allocator, struct molecule* molecule) +{ + str_init(allocator, &molecule->name); + molecule_clear(molecule); +} + +static INLINE void +molecule_release(struct molecule* molecule) +{ + str_release(&molecule->name); +} + +static INLINE res_T +molecule_copy(struct molecule* dst, const struct molecule* src) +{ + dst->isotopes_range[0] = src->isotopes_range[0]; + dst->isotopes_range[1] = src->isotopes_range[1]; + dst->id = src->id; + return str_copy(&dst->name, &src->name); +} + +static INLINE res_T +molecule_copy_and_release(struct molecule* dst, struct molecule* src) +{ + dst->isotopes_range[0] = src->isotopes_range[0]; + dst->isotopes_range[1] = src->isotopes_range[1]; + dst->id = src->id; + return str_copy_and_release(&dst->name, &src->name); +} + +/* Generate the dynamic array of molecules */ +#define DARRAY_NAME molecule +#define DARRAY_DATA struct molecule +#define DARRAY_FUNCTOR_INIT molecule_init +#define DARRAY_FUNCTOR_RELEASE molecule_release +#define DARRAY_FUNCTOR_COPY molecule_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE molecule_copy_and_release +#include <rsys/dynamic_array.h> + +/* Generate the hash table that map a unique identifier to its index */ +#define HTABLE_NAME id2entry +#define HTABLE_KEY int /* Unique identifier */ +#define HTABLE_DATA size_t /* Index of the corresponding registered data */ +#include <rsys/hash_table.h> + +struct sht_isotope_metadata { + /* List of molecules and isotopes */ + struct darray_molecule molecules; + struct darray_isotope isotopes; + + /* Map the identifier of a molecule/isotope to its correspond index into + * their corresponding dynamic arays into which they are registered */ + struct htable_id2entry molid2idx; + struct htable_id2entry isoid2idx; + + struct sht* sht; + ref_T ref; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +#define DEFINE_PARSE_PARAM_FUNCTION(Type) \ + static res_T \ + parse_param_##Type \ + (struct sht* sht, \ + const char* str, \ + const char* path, \ + size_t line_num, \ + const struct param_desc* desc, \ + Type* out_param) \ + { \ + Type param = 0; \ + res_T res = RES_OK; \ + ASSERT(sht && path && desc && out_param); \ + ASSERT(desc->low < desc->upp \ + || (desc->low == desc->upp && desc->is_low_incl && desc->is_upp_incl));\ + \ + if(!str) { \ + log_err(sht, "%s:%lu: %s is missing.\n", \ + path, (unsigned long)line_num, desc->name); \ + res = RES_BAD_ARG; \ + goto error; \ + } \ + \ + res = cstr_to_##Type(str, &param); \ + if(res != RES_OK) { \ + log_err(sht, "%s:%lu: invalid %s `%s'.\n", \ + desc->path, (unsigned long)desc->iline, desc->name, str); \ + res = RES_BAD_ARG; \ + goto error; \ + } \ + \ + if(param < desc->low || (param == desc->low && !desc->is_low_incl) \ + || param > desc->upp || (param == desc->upp && !desc->is_upp_incl)) { \ + log_err(sht, \ + "%s:%lu: invalid isotope %s `%s'. It must be in " \ + "%c"CONCAT(C_FORMAT_, Type)", "CONCAT(C_FORMAT_, Type)"%c.\n", \ + path, (unsigned long)line_num, desc->name, str, \ + desc->is_low_incl ? '[' : ']', (Type)desc->low, \ + (Type)desc->upp, desc->is_upp_incl ? ']' : '['); \ + res = RES_BAD_ARG; \ + goto error; \ + } \ + \ + exit: \ + *out_param = param; \ + return res; \ + error: \ + goto exit; \ + } +DEFINE_PARSE_PARAM_FUNCTION(double) +DEFINE_PARSE_PARAM_FUNCTION(int) +#undef DEFINE_PARSE_PARAM_FUNCTION + +static res_T +create_isotoplogues + (struct sht* sht, + struct sht_isotope_metadata** out_isotopes) +{ + struct sht_isotope_metadata* metadata = NULL; + res_T res = RES_OK; + ASSERT(sht && out_isotopes); + + metadata = MEM_CALLOC(sht->allocator, 1, sizeof(*metadata)); + if(!metadata) { + log_err(sht, "Could not allocate the metadata data structure.\n"); + res = RES_MEM_ERR; + goto error; + } + ref_init(&metadata->ref); + SHT(ref_get(sht)); + metadata->sht = sht; + darray_molecule_init(sht->allocator, &metadata->molecules); + darray_isotope_init(sht->allocator, &metadata->isotopes); + htable_id2entry_init(sht->allocator, &metadata->molid2idx); + htable_id2entry_init(sht->allocator, &metadata->isoid2idx); + +exit: + *out_isotopes = metadata; + return res; +error: + goto exit; +} + +static res_T +flush_molecule + (struct sht_isotope_metadata* metadata, + struct molecule* molecule, /* Currently parsed molecule */ + struct txtrdr* txtrdr) +{ + size_t ientry = SIZE_MAX; + res_T res = RES_OK; + ASSERT(metadata && molecule && MOLECULE_IS_VALID(molecule)); + + /* Fetch _exclusive_ upper bound */ + molecule->isotopes_range[1] = + darray_isotope_size_get(&metadata->isotopes); + if(molecule->isotopes_range[0] >= molecule->isotopes_range[1]) { + log_warn(metadata->sht, + "%s: the %s molecule does not have any isotope.\n", + txtrdr_get_name(txtrdr), str_cget(&molecule->name)); + } + + /* Fetch the index where the molecule is going to be store */ + ientry = darray_molecule_size_get(&metadata->molecules); + + /* Store the molecule */ + res = darray_molecule_push_back(&metadata->molecules, molecule); + if(res != RES_OK) { + log_err(metadata->sht, + "%s: error storing the %s molecule -- %s.\n", + txtrdr_get_name(txtrdr), str_cget(&molecule->name), res_to_cstr(res)); + goto error; + } + + /* Register the molecule */ + ASSERT(!htable_id2entry_find(&metadata->molid2idx, &molecule->id)); + res = htable_id2entry_set(&metadata->molid2idx, &molecule->id, &ientry); + if(res != RES_OK) { + log_err(metadata->sht, + "%s: error registering the %s molecule -- %s.\n", + txtrdr_get_name(txtrdr), str_cget(&molecule->name), res_to_cstr(res)); + goto error; + } + + molecule_clear(molecule); + +exit: + return res; +error: + if(ientry != SIZE_MAX) darray_molecule_resize(&metadata->molecules, ientry); + htable_id2entry_erase(&metadata->molid2idx, &molecule->id); + goto exit; +} + +static res_T +parse_molecule + (struct sht_isotope_metadata* metadata, + struct molecule* molecule, + struct txtrdr* txtrdr) +{ + char* name = NULL; + char* id = NULL; + char* tk = NULL; + char* tk_ctx = NULL; + size_t len; + res_T res = RES_OK; + ASSERT(molecule && txtrdr); + + name = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); + id = strtok_r(NULL, " \t", &tk_ctx); + + if(!name) { + log_err(metadata->sht, "%s:%lu: molecule name is missing.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + res = str_set(&molecule->name, name); + if(res != RES_OK) { + log_err(metadata->sht, + "%s:%lu: error seting the molecule name `%s' -- %s.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), + name, res_to_cstr(res)); + goto error; + } + + len = strlen(id); + if(!id || !len || id[0] != '(' || id[len-1] != ')') { + log_err(metadata->sht, "%s:%lu: invalid molecule identifier.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); + res = RES_BAD_ARG; + goto error; + } + + id[len-1] = '\0'; /* Rm trailing parenthesis */ + res = cstr_to_int(id+1/*Rm leading parenthesis*/, &molecule->id); + if(res != RES_OK || !MOLECULE_IS_VALID(molecule)) { + id[len-1] = ')'; /* Re-add the trailing parenthesis */ + log_err(metadata->sht, "%s:%lu: invalid molecule identifier `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), id); + res = RES_BAD_ARG; + goto error; + } + + tk = strtok_r(NULL, " \t", &tk_ctx); + if(tk) { + log_warn(metadata->sht, "%s:%lu: unexpected text `%s'.\n", + txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); + } + + molecule->isotopes_range[0] = darray_isotope_size_get(&metadata->isotopes); + +exit: + return res; +error: + goto exit; +} + +static res_T +parse_isotope + (struct sht_isotope_metadata* metadata, + struct txtrdr* txtrdr) +{ + struct isotope isotope = ISOTOPOLOGUE_NULL; + struct param_desc param_desc = PARAM_DESC_NULL; + struct sht* sht = NULL; + char* tk = NULL; + char* tk_ctx = NULL; + const char* path = NULL; + size_t line_num = 0; + size_t ientry = SIZE_MAX; + res_T res = RES_OK; + ASSERT(metadata && txtrdr); + + sht = metadata->sht; + path = txtrdr_get_name(txtrdr); + line_num = txtrdr_get_line_num(txtrdr); + + /* Fetch the index of the molecule to which the isotope belongs */ + isotope.molecule = darray_molecule_size_get(&metadata->molecules); + + tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); + param_desc.name = "isotope id"; + param_desc.low = 0; + param_desc.upp = INT_MAX; + param_desc.is_low_incl = 1; + param_desc.is_upp_incl = 1; + res = parse_param_int(sht, tk, path, line_num, &param_desc, &isotope.id); + if(res != RES_OK) goto error; + + tk = strtok_r(NULL, " \t", &tk_ctx); + param_desc.name = "isotope abundance"; + param_desc.low = 0; + param_desc.upp = 1; + param_desc.is_low_incl = 0; + param_desc.is_upp_incl = 1; + res = parse_param_double + (sht, tk, path, line_num, &param_desc, &isotope.abundance); + if(res != RES_OK) goto error; + + tk = strtok_r(NULL, " \t", &tk_ctx); + param_desc.name = "isotope Q(256K)"; + param_desc.low = 0; + param_desc.upp = INF; + param_desc.is_low_incl = 0; + param_desc.is_upp_incl = 1; + res = parse_param_double + (sht, tk, path, line_num, &param_desc, &isotope.Q296K); + if(res != RES_OK) goto error; + + tk = strtok_r(NULL, " \t", &tk_ctx); + param_desc.name = "isotope state independant degeneracy factor"; + param_desc.low = -INT_MAX; + param_desc.upp = INT_MAX; + param_desc.is_low_incl = 1; + param_desc.is_upp_incl = 1; + res = parse_param_int + (sht, tk, path, line_num, &param_desc, &isotope.gj); + if(res != RES_OK) goto error; + + tk = strtok_r(NULL, " \t", &tk_ctx), + param_desc.name = "isotope molar mass"; + param_desc.low = 0; + param_desc.upp = INF; + param_desc.is_low_incl = 0; + param_desc.is_upp_incl = 1; + res = parse_param_double + (sht, tk, path, line_num, &param_desc, &isotope.molar_mass); + if(res != RES_OK) goto error; + + /* Fetch the index where the isotope is going to be store */ + ientry = darray_isotope_size_get(&metadata->isotopes); + + /* Store the isotope */ + res = darray_isotope_push_back(&metadata->isotopes, &isotope); + if(res != RES_OK) { + log_err(sht, "%s:%lu: error storing the isotope %d -- %s.\n", + path, (unsigned long)line_num, isotope.id, res_to_cstr(res)); + res = RES_OK; + goto error; + } + + /* Register the isotope */ + ASSERT(!htable_id2entry_find(&metadata->isoid2idx, &isotope.id)); + res = htable_id2entry_set(&metadata->isoid2idx, &isotope.id, &ientry); + if(res != RES_OK) { + log_err(sht, "%s:%lu: error registering the isotopoe %d -- %s.\n", + path, (unsigned long)line_num, isotope.id, res_to_cstr(res)); + res = RES_OK; + goto error; + } + +exit: + return res; +error: + if(ientry != SIZE_MAX) darray_isotope_resize(&metadata->isotopes, ientry); + htable_id2entry_erase(&metadata->isoid2idx, &isotope.id); + goto exit; +} + +static res_T +parse_line + (struct sht_isotope_metadata* metadata, + struct molecule* molecule, /* Currently parsed molecule */ + struct txtrdr* txtrdr) +{ + const char* line = NULL; + size_t i; + res_T res = RES_OK; + ASSERT(metadata && molecule && txtrdr); + + line = txtrdr_get_cline(txtrdr); + ASSERT(line); + i = strspn(line, " \t"); + ASSERT(i < strlen(line)); + + if(!isalpha(line[i])) { + res = parse_isotope(metadata, txtrdr); + if(res != RES_OK) goto error; + } else { + if(MOLECULE_IS_VALID(molecule)) { + res = flush_molecule(metadata, molecule, txtrdr); + if(res != RES_OK) goto error; + } + res = parse_molecule(metadata, molecule, txtrdr); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +static res_T +load_stream + (struct sht* sht, + FILE* stream, + const char* name, + struct sht_isotope_metadata** out_isotopes) +{ + struct molecule molecule; /* Current molecule */ + struct sht_isotope_metadata* metadata = NULL; + struct txtrdr* txtrdr = NULL; + res_T res = RES_OK; + ASSERT(sht && stream && name && out_isotopes); + + molecule_init(sht->allocator, &molecule); + + res = create_isotoplogues(sht, &metadata); + if(res != RES_OK) goto error; + + res = txtrdr_stream(metadata->sht->allocator, stream, name, + 0/*No comment char*/, &txtrdr); + if(res != RES_OK) { + log_err(sht, "%s: error creating the text reader -- %s.\n", + name, res_to_cstr(res)); + goto error; + } + + #define READ_LINE { \ + res = txtrdr_read_line(txtrdr); \ + if(res != RES_OK) { \ + log_err(sht, "%s: error reading the line `%lu' -- %s.\n", \ + name, (unsigned long)txtrdr_get_line_num(txtrdr), res_to_cstr(res)); \ + goto error; \ + } \ + } (void)0 + + /* Skip the 1st line that is a comment line*/ + READ_LINE; + if(!txtrdr_get_cline(txtrdr)) goto exit; + + for(;;) { + READ_LINE; + + if(!txtrdr_get_cline(txtrdr)) break; /* No more parsed line */ + res = parse_line(metadata, &molecule, txtrdr); + if(res != RES_OK) goto error; + } + #undef READ_LINE + + if(MOLECULE_IS_VALID(&molecule)) { + res = flush_molecule(metadata, &molecule, txtrdr); + if(res != RES_OK) goto error; + } + +exit: + if(txtrdr) txtrdr_ref_put(txtrdr); + *out_isotopes = metadata; + molecule_release(&molecule); + return res; +error: + goto exit; +} + +static void +release_isotope_metadata(ref_T* ref) +{ + struct sht* sht = NULL; + struct sht_isotope_metadata* metadata = CONTAINER_OF + (ref, struct sht_isotope_metadata, ref); + ASSERT(ref); + sht = metadata->sht; + darray_molecule_release(&metadata->molecules); + darray_isotope_release(&metadata->isotopes); + htable_id2entry_release(&metadata->molid2idx); + htable_id2entry_release(&metadata->isoid2idx); + MEM_RM(sht->allocator, metadata); + SHT(ref_put(sht)); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +sht_isotope_metadata_load + (struct sht* sht, + const char* path, + struct sht_isotope_metadata** metadata) +{ + FILE* file = NULL; + res_T res = RES_OK; + + if(!sht || !path || !metadata) { + res = RES_BAD_ARG; + goto error; + } + + file = fopen(path, "r"); + if(!file) { + log_err(sht, "%s: error opening file `%s'.\n", FUNC_NAME, path); + res = RES_IO_ERR; + goto error; + } + + res = load_stream(sht, file, path, metadata); + if(res != RES_OK) goto error; + +exit: + if(file) fclose(file); + return res; +error: + goto exit; +} + +res_T +sht_isotope_metadata_load_stream + (struct sht* sht, + FILE* stream, + const char* stream_name, + struct sht_isotope_metadata** metadata) +{ + if(!sht || !stream || !metadata) return RES_BAD_ARG; + return load_stream + (sht, stream, stream_name ? stream_name : "<stream>", metadata); +} + +res_T +sht_isotope_metadata_ref_get + (struct sht_isotope_metadata* metadata) +{ + if(!metadata) return RES_BAD_ARG; + ref_get(&metadata->ref); + return RES_OK; +} + +res_T +sht_isotope_metadata_ref_put + (struct sht_isotope_metadata* metadata) +{ + if(!metadata) return RES_BAD_ARG; + ref_put(&metadata->ref, release_isotope_metadata); + return RES_OK; +} diff --git a/src/sht_isotopologues.c b/src/sht_isotopologues.c @@ -1,426 +0,0 @@ -/* Copyright (C) 2022 CNRS - LMD - * Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) - * Copyright (C) 2022 Université Paul Sabatier - IRIT - * Copyright (C) 2022 Université Paul Sabatier - Laplace - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#define _POSIX_C_SOURCE 200112L /* strtok_r support */ - -#include "sht.h" -#include "sht_c.h" -#include "sht_log.h" - -#include <rsys/cstr.h> -#include <rsys/dynamic_array.h> -#include <rsys/hash_table.h> -#include <rsys/ref_count.h> -#include <rsys/str.h> -#include <rsys/text_reader.h> - -#include <ctype.h> -#include <string.h> - -struct isotope { - double abundance; - double Q; /* At 296 K */ - double gj; - double molar_mass; /* In g */ - size_t molecule; /* Index of the molecule to which the isotope belongs */ - int id; /* Unique identifier of the isotope */ -}; - -/* Generate the dynamic array of isotopes */ -#define DARRAY_NAME isotope -#define DARRAY_DATA struct isotope -#include <rsys/dynamic_array.h> - -struct molecule { - struct str name; - size_t isotopes_range[2]; /* Range of the 1st and last regisered isotopes */ - int id; /* Unique identifier of the molecule */ -}; -#define MOLECULE_IS_VALID(Molecule) ((Molecule)->id >= 0) - -static INLINE void -molecule_clear(struct molecule* molecule) -{ - ASSERT(molecule); - molecule->isotopes_range[0] = SIZE_MAX; - molecule->isotopes_range[1] = 0; - molecule->id = -1; -} - -static INLINE void -molecule_init(struct mem_allocator* allocator, struct molecule* molecule) -{ - str_init(allocator, &molecule->name); - molecule_clear(molecule); -} - -static INLINE void -molecule_release(struct molecule* molecule) -{ - str_release(&molecule->name); -} - -static INLINE res_T -molecule_copy(struct molecule* dst, const struct molecule* src) -{ - dst->isotopes_range[0] = src->isotopes_range[0]; - dst->isotopes_range[1] = src->isotopes_range[1]; - dst->id = src->id; - return str_copy(&dst->name, &src->name); -} - -static INLINE res_T -molecule_copy_and_release(struct molecule* dst, struct molecule* src) -{ - dst->isotopes_range[0] = src->isotopes_range[0]; - dst->isotopes_range[1] = src->isotopes_range[1]; - dst->id = src->id; - return str_copy_and_release(&dst->name, &src->name); -} - -/* Generate the dynamic array of molecules */ -#define DARRAY_NAME molecule -#define DARRAY_DATA struct molecule -#define DARRAY_FUNCTOR_INIT molecule_init -#define DARRAY_FUNCTOR_RELEASE molecule_release -#define DARRAY_FUNCTOR_COPY molecule_copy -#define DARRAY_FUNCTOR_COPY_AND_RELEASE molecule_copy_and_release -#include <rsys/dynamic_array.h> - -/* Generate the hash table that map a unique identifier to its index */ -#define HTABLE_NAME id2entry -#define HTABLE_KEY int /* Unique identifier */ -#define HTABLE_DATA size_t /* Index of the corresponding registered data */ -#include <rsys/hash_table.h> - -struct sht_isotopologues { - - /* List of molecules and isotopes */ - struct darray_molecule molecules; - struct darray_isotope isotopes; - - /* Map the identifier of a molecule/isotope to its correspond index into - * their corresponding dynamic arays into which they are registered */ - struct htable_id2entry molid2idx; - struct htable_id2entry isoid2idx; - - struct sht* sht; - ref_T ref; -}; - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static res_T -create_isotoplogues - (struct sht* sht, - struct sht_isotopologues** out_isotopologues) -{ - struct sht_isotopologues* isotopologues = NULL; - res_T res = RES_OK; - ASSERT(sht && out_isotopologues); - - isotopologues = MEM_CALLOC(sht->allocator, 1, sizeof(*isotopologues)); - if(!isotopologues) { - log_err(sht, "Could not allocate the isotopologues data structure.\n"); - res = RES_MEM_ERR; - goto error; - } - ref_init(&isotopologues->ref); - SHT(ref_get(sht)); - isotopologues->sht = sht; - darray_molecule_init(sht->allocator, &isotopologues->molecules); - darray_isotope_init(sht->allocator, &isotopologues->isotopes); - htable_id2entry_init(sht->allocator, &isotopologues->molid2idx); - htable_id2entry_init(sht->allocator, &isotopologues->isoid2idx); - -exit: - *out_isotopologues = isotopologues; - return res; -error: - goto exit; -} - -static res_T -flush_molecule - (struct sht_isotopologues* isotopologues, - struct molecule* molecule, /* Currently parsed molecule */ - struct txtrdr* txtrdr) -{ - size_t entry = 0; - res_T res = RES_OK; - ASSERT(isotopologues && molecule && MOLECULE_IS_VALID(molecule)); - - /* Fetch _exclusive_ upper bound */ - molecule->isotopes_range[1] = darray_isotope_size_get(&isotopologues->isotopes); - if(molecule->isotopes_range[0] >= molecule->isotopes_range[1]) { - log_warn(isotopologues->sht, - "%s: the %s molecule does not have any isotopes.\n", - txtrdr_get_name(txtrdr), str_cget(&molecule->name)); - } - - /* Fetch the index of the registered molecule */ - entry = darray_molecule_size_get(&isotopologues->molecules); - - /* Storing the molecule */ - res = darray_molecule_push_back(&isotopologues->molecules, molecule); - if(res != RES_OK) { - log_err(isotopologues->sht, - "%s: error storing the %s molecule -- %s.\n", - txtrdr_get_name(txtrdr), str_cget(&molecule->name), res_to_cstr(res)); - goto error; - } - - /* Registering the molecule */ - ASSERT(!htable_id2entry_find(&isotopologues->molid2idx, &molecule->id)); - res = htable_id2entry_set(&isotopologues->molid2idx, &molecule->id, &entry); - if(res != RES_OK) { - log_err(isotopologues->sht, - "%s: error registering the %s molecule -- %s.\n", - txtrdr_get_name(txtrdr), str_cget(&molecule->name), res_to_cstr(res)); - goto error; - } - - molecule_clear(molecule); - -exit: - return res; -error: - goto exit; -} - -static res_T -parse_molecule - (struct sht_isotopologues* isotopologues, - struct molecule* molecule, - struct txtrdr* txtrdr) -{ - char* name = NULL; - char* id = NULL; - char* tk = NULL; - char* tk_ctx = NULL; - size_t len; - res_T res = RES_OK; - ASSERT(molecule && txtrdr); - - name = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); - id = strtok_r(NULL, " \t", &tk_ctx); - - if(!name) { - log_err(isotopologues->sht, "%s:%lu: molecule name is missing.\n", - txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); - res = RES_BAD_ARG; - goto error; - } - - len = strlen(id); - if(!id || !len || id[0] != '(' || id[len-1] != ')') { - log_err(isotopologues->sht, "%s:%lu: invalid molecule identifier.\n", - txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); - res = RES_BAD_ARG; - goto error; - } - - id[len-1] = '\0'; /* Rm trailing parenthesis */ - res = cstr_to_int(id+1/*Rm leading parenthesis*/, &molecule->id); - if(res != RES_OK || !MOLECULE_IS_VALID(molecule)) { - id[len-1] = ')'; /* Re-add the trailing parenthesis */ - log_err(isotopologues->sht, "%s:%lu: invalid molecule identifier `%s'.\n", - txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), id); - res = RES_BAD_ARG; - goto error; - } - - tk = strtok_r(NULL, " \t", &tk_ctx); - if(tk) { - log_warn(isotopologues->sht, "%s:%lu: unexpected text `%s'.\n", - txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); - } - -exit: - return res; -error: - goto exit; -} - -static res_T -parse_line - (struct sht_isotopologues* isotopologues, - struct molecule* molecule, /* Currently parsed molecule */ - struct txtrdr* txtrdr) -{ - const char* line = NULL; - size_t i; - res_T res = RES_OK; - ASSERT(isotopologues && molecule && txtrdr); - - line = txtrdr_get_cline(txtrdr); - ASSERT(line); - i = strcspn(line, " \t"); - ASSERT(i < strlen(line)); - - if(isalpha(line[i])) { - if(MOLECULE_IS_VALID(molecule)) { - res = flush_molecule(isotopologues, molecule, txtrdr); - if(res != RES_OK) goto error; - } - res = parse_molecule(isotopologues, molecule, txtrdr); - if(res != RES_OK) goto error; - } else { - /* TODO parse the isotope */ - } - -exit: - return res; -error: - goto exit; -} - -static res_T -load_stream - (struct sht* sht, - FILE* stream, - const char* name, - struct sht_isotopologues** out_isotopologues) -{ - struct molecule molecule; /* Current molecule */ - struct sht_isotopologues* isotopologues = NULL; - struct txtrdr* txtrdr = NULL; - res_T res = RES_OK; - ASSERT(sht && stream && name && out_isotopologues); - - molecule_init(sht->allocator, &molecule); - - res = create_isotoplogues(sht, &isotopologues); - if(res != RES_OK) goto error; - - res = txtrdr_stream(isotopologues->sht->allocator, stream, name, - 0/*comment char*/, &txtrdr); - if(res != RES_OK) { - log_err(sht, "%s: error creating the text reader -- %s.\n", - name, res_to_cstr(res)); - goto error; - } - - #define READ_LINE { \ - res = txtrdr_read_line(txtrdr); \ - if(res != RES_OK) { \ - log_err(sht, "%s: error reading the line `%lu' -- %s.\n", \ - name, (unsigned long)txtrdr_get_line_num(txtrdr), res_to_cstr(res)); \ - goto error; \ - } \ - } (void)0 - - /* Skip the 1st line that is a comment line*/ - READ_LINE; - if(!txtrdr_get_cline(txtrdr)) goto exit; - - for(;;) { - READ_LINE; - - if(!txtrdr_get_cline(txtrdr)) break; /* No more parsed line */ - res = parse_line(isotopologues, &molecule, txtrdr); - if(res != RES_OK) goto error; - } - #undef READ_LINE - -exit: - *out_isotopologues = isotopologues; - molecule_release(&molecule); - return res; -error: - goto exit; -} - -static void -release_isotopologues(ref_T* ref) -{ - struct sht* sht = NULL; - struct sht_isotopologues* isotopologues = CONTAINER_OF - (ref, struct sht_isotopologues, ref); - ASSERT(ref); - sht = isotopologues->sht; - darray_molecule_release(&isotopologues->molecules); - darray_isotope_release(&isotopologues->isotopes); - htable_id2entry_release(&isotopologues->molid2idx); - htable_id2entry_release(&isotopologues->isoid2idx); - MEM_RM(sht->allocator, isotopologues); - SHT(ref_put(sht)); -} - -/******************************************************************************* - * Exported functions - ******************************************************************************/ -res_T -sht_isotopologues_load - (struct sht* sht, - const char* path, - struct sht_isotopologues** isotopologues) -{ - FILE* file = NULL; - res_T res = RES_OK; - - if(!sht || !path) { - res = RES_BAD_ARG; - goto error; - } - - file = fopen(path, "r"); - if(!file) { - log_err(sht, "%s: error opening file `%s'.\n", FUNC_NAME, path); - res = RES_IO_ERR; - goto error; - } - - res = load_stream(sht, file, path, isotopologues); - if(res != RES_OK) goto error; - -exit: - if(file) fclose(file); - return res; -error: - goto exit; -} - -res_T -sht_isotopologues_load_from_stream - (struct sht* sht, - FILE* stream, - const char* stream_name, - struct sht_isotopologues** isotopologues) -{ - if(!sht || !stream) return RES_BAD_ARG; - return load_stream - (sht, stream, stream_name ? stream_name : "<stream>", isotopologues); -} - -res_T -sht_isotopologues_ref_get(struct sht_isotopologues* isotopologues) -{ - if(!isotopologues) return RES_BAD_ARG; - ref_get(&isotopologues->ref); - return RES_OK; -} - -res_T -sht_isotopologues_ref_put(struct sht_isotopologues* isotopologues) -{ - if(!isotopologues) return RES_BAD_ARG; - ref_put(&isotopologues->ref, release_isotopologues); - return RES_OK; -} diff --git a/src/test_sht_isotope_metadata.c b/src/test_sht_isotope_metadata.c @@ -0,0 +1,85 @@ +/* Copyright (C) 2022 CNRS - LMD + * Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2022 Université Paul Sabatier - IRIT + * Copyright (C) 2022 Université Paul Sabatier - Laplace + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sht.h" + +#include <rsys/mem_allocator.h> + +static void +test_load(struct sht* sht) +{ + const char* filename = "test_isotope_metadata.txt"; + struct sht_isotope_metadata* metadata = NULL; + FILE* fp = NULL; + + CHK(fp = fopen(filename, "w+")); + + fprintf(fp, "Molecule # Iso Abundance Q(296K) gj Molar Mass(g)\n"); + fprintf(fp, " H20 (1)\n"); + fprintf(fp, " 161 9.97317E-01 1.7458E+02 1 18.010565\n"); + fprintf(fp, " 181 1.99983E-03 1.7605E+02 1 20.014811\n"); + fprintf(fp, " 171 3.71884E-04 1.0521E+03 6 19.014780\n"); + fprintf(fp, " 162 3.10693E-04 8.6474E+02 6 19.016740\n"); + fprintf(fp, " 182 6.23003E-07 8.7557E+02 6 21.020985\n"); + fprintf(fp, " 172 1.15853E-07 5.2268E+03 36 20.020956\n"); + fprintf(fp, " 262 2.41974E-08 1.0278E+03 1 20.022915\n"); + fprintf(fp, " CO2 (2)\n"); + fprintf(fp, " 626 9.84204E-01 2.8609E+02 1 43.989830\n"); + fprintf(fp, " 636 1.10574E-02 5.7664E+02 2 44.993185\n"); + fprintf(fp, " 628 3.94707E-03 6.0781E+02 1 45.994076\n"); + fprintf(fp, " 627 7.33989E-04 3.5426E+03 6 44.994045\n"); + fprintf(fp, " 638 4.43446E-05 1.2255E+03 2 46.997431\n"); + fprintf(fp, " 637 8.24623E-06 7.1413E+03 12 45.997400\n"); + fprintf(fp, " 828 3.95734E-06 3.2342E+02 1 47.998320\n"); + fprintf(fp, " 827 1.47180E-06 3.7666E+03 6 46.998291\n"); + fprintf(fp, " 727 1.36847E-07 1.0972E+04 1 45.998262\n"); + fprintf(fp, " 838 4.44600E-08 6.5224E+02 2 49.001675\n"); + fprintf(fp, " 837 1.65354E-08 7.5950E+03 12 48.001646\n"); + fprintf(fp, " 737 1.53745E-09 2.2120E+04 2 47.001618\n"); + rewind(fp); + + CHK(sht_isotope_metadata_load_stream(NULL, fp, NULL, &metadata) == RES_BAD_ARG); + CHK(sht_isotope_metadata_load_stream(sht, NULL, NULL, &metadata) == RES_BAD_ARG); + CHK(sht_isotope_metadata_load_stream(sht, fp, NULL, NULL) == RES_BAD_ARG); + CHK(sht_isotope_metadata_load_stream(sht, fp, NULL, &metadata) == RES_OK); + + CHK(sht_isotope_metadata_ref_get(NULL) == RES_BAD_ARG); + CHK(sht_isotope_metadata_ref_get(metadata) == RES_OK); + CHK(sht_isotope_metadata_ref_put(NULL) == RES_BAD_ARG); + CHK(sht_isotope_metadata_ref_put(metadata) == RES_OK); + CHK(sht_isotope_metadata_ref_put(metadata) == RES_OK); + + CHK(fclose(fp) == 0); +} + +int +main(int argc, char** argv) +{ + struct sht* sht = NULL; + struct sht_create_args args = SHT_CREATE_ARGS_DEFAULT; + (void)argc, (void)argv; + + args.verbose = 1; + CHK(sht_create(&args, &sht) == RES_OK); + + test_load(sht); + + CHK(sht_ref_put(sht) == RES_OK); + CHK(mem_allocated_size() == 0); + return 0; +}