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:
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, ¶m); \
+ 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, ¶m_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, ¶m_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, ¶m_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, ¶m_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, ¶m_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;
+}