commit f8214a066414d837d75c793845c67ef885abe456
parent 7b8417a8634615edc387834ec7a71c47cf7e318d
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 15 Jun 2022 09:33:58 +0200
Implement sars_load and sars_load_stream
Diffstat:
| M | src/sars.c | | | 215 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 215 insertions(+), 0 deletions(-)
diff --git a/src/sars.c b/src/sars.c
@@ -13,12 +13,19 @@
* 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 200809L /* mmap support */
+#define _DEFAULT_SOURCE 1 /* MAP_POPULATE support */
+#define _BSD_SOURCE 1 /* MAP_POPULATE for glibc < 2.19 */
+
#include "sars.h"
#include "sars_c.h"
#include "sars_log.h"
#include <unistd.h> /* sysconf support */
+#include <errno.h>
+#include <sys/mman.h> /* mmap */
+
/*******************************************************************************
* Helper functions
******************************************************************************/
@@ -30,6 +37,176 @@ check_sars_create_args(const struct sars_create_args* args)
}
static void
+reset_sars(struct sars* sars)
+{
+ ASSERT(sars);
+ sars->pagesize = 0;
+ sars->nnodes = 0;
+ darray_band_purge(&sars->bands);
+}
+
+static res_T
+read_band
+ (struct sars* sars,
+ struct band* band,
+ FILE* stream,
+ const char* stream_name)
+{
+ size_t iband;
+ res_T res = RES_OK;
+ ASSERT(sars && band && stream_name);
+
+ iband = (size_t)(band - darray_band_cdata_get(&sars->bands));
+
+ /* Read band definition */
+ #define READ(Var, Name) { \
+ if(fread((Var), sizeof(*(Var)), 1, stream) != 1) { \
+ log_err(sars, "%s: band %lu: could not read the %s.\n", \
+ stream_name, (unsigned long)iband, (Name)); \
+ res = RES_IO_ERR; \
+ goto error; \
+ } \
+ } (void)0
+ READ(&band->low, "band lower bound");
+ READ(&band->upp, "band upper bound");
+ #undef READ
+
+ /* Check band description */
+ if(band->low < 0 || band->low > band->upp) {
+ log_err(sars,
+ "%s: band %lu: invalid band range [%g, %g].\n",
+ stream_name, (unsigned long)iband, band->low, band->upp);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+
+static res_T
+load_stream(struct sars* sars, FILE* stream, const char* stream_name)
+{
+ size_t map_len;
+ size_t iband;
+ uint64_t nbands;
+ off_t offset = 0;
+ res_T res = RES_OK;
+ ASSERT(sars && stream && stream_name);
+
+ reset_sars(sars);
+
+ /* Read file header */
+ #define READ(Var, Name) { \
+ if(fread((Var), sizeof(*(Var)), 1, stream) != 1) { \
+ log_err(sars, "%s: could not read the %s.\n", stream_name, (Name)); \
+ res = RES_IO_ERR; \
+ goto error; \
+ } \
+ } (void)0
+ READ(&sars->pagesize, "page size");
+ READ(&nbands, "number of bands");
+ READ(&sars->nnodes, "number of nodes");
+ #undef READ
+
+ /* Check band description */
+ if(!IS_ALIGNED(sars->pagesize, sars->pagesize_os)) {
+ log_err(sars,
+ "%s: invalid page size %lu. The page size attribute must be aligned on "
+ "the page size of the operating system (%lu).\n",
+ stream_name,
+ (unsigned long)sars->pagesize,
+ (unsigned long)sars->pagesize_os);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(!nbands) {
+ log_err(sars, "%s: invalid number of bands %lu.\n",
+ stream_name, (unsigned long)nbands);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(!sars->nnodes) {
+ log_err(sars, "%s: invalid number of nodes %lu.\n",
+ stream_name, (unsigned long)sars->nnodes);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Allocate the bands */
+ res = darray_band_resize(&sars->bands, nbands);
+ if(res != RES_OK) {
+ log_err(sars, "%s: could not allocate the list of bands (#bands=%lu).\n",
+ stream_name, (unsigned long)nbands);
+ goto error;
+ }
+
+ /* Read the band description */
+ FOR_EACH(iband, 0, nbands) {
+ struct band* band = darray_band_data_get(&sars->bands) + iband;
+ res = read_band(sars, band, stream, stream_name);
+ if(res != RES_OK) goto error;
+ if(iband > 0 && band[0].low < band[-1].upp) {
+ log_err(sars,
+ "%s: bands must be sorted in ascending order and must not "
+ "overlap (band %lu in [%g, %g] nm; band %lu in [%g, %g] nm).\n",
+ stream_name,
+ (unsigned long)(iband-1), band[-1].low, band[-1].upp,
+ (unsigned long)(iband), band[ 0].low, band[ 0].upp);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+
+ /* Compute the length in bytes of the k to map for each band/quadrature point */
+ map_len = ALIGN_SIZE(sars->nnodes * sizeof(float), sars->pagesize);
+
+ /* Compute the offset toward the 1st list of radiative coefficients */
+ offset = ftell(stream);
+ offset = (off_t)ALIGN_SIZE((uint64_t)offset, sars->pagesize);
+
+ FOR_EACH(iband, 0, nbands) {
+ struct band* band = NULL;
+
+ band = darray_band_data_get(&sars->bands) + iband;
+ band->map_len = map_len;
+
+ /* Map the per band scattering coefficient */
+ band->ks_list = mmap(NULL, band->map_len, PROT_READ,
+ MAP_PRIVATE|MAP_POPULATE, fileno(stream), offset);
+ if(band->ks_list == MAP_FAILED) {
+ log_err(sars,
+ "%s: band %lu: could not map the scattering coefficients -- %s\n",
+ stream_name, (unsigned long)iband, strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+ offset = (off_t)((size_t)offset + map_len);
+
+ /* Map the per band absorption coefficient */
+ band->ka_list = mmap(NULL, band->map_len, PROT_READ,
+ MAP_PRIVATE|MAP_POPULATE, fileno(stream), offset);
+ if(band->ks_list == MAP_FAILED) {
+ log_err(sars,
+ "%s: band %lu: could not map the absorption coefficients -- %s\n",
+ stream_name, (unsigned long)iband, strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+ offset = (off_t)((size_t)offset + map_len);
+ }
+
+exit:
+ return res;
+error:
+ reset_sars(sars);
+ goto exit;
+}
+
+static void
release_sars(ref_T* ref)
{
struct sars* sars = NULL;
@@ -106,3 +283,41 @@ sars_ref_put(struct sars* sars)
ref_put(&sars->ref, release_sars);
return RES_OK;
}
+
+res_T
+sars_load(struct sars* sars, const char* path)
+{
+ FILE* file = NULL;
+ res_T res = RES_OK;
+
+ if(!sars || !path) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ file = fopen(path, "r");
+ if(!file) {
+ log_err(sars, "%s: error opening file `%s'.\n", FUNC_NAME, path);
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ res = load_stream(sars, file, path);
+ if(res != RES_OK) goto error;
+
+exit:
+ if(file) fclose(file);
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+sars_load_stream
+ (struct sars* sars,
+ FILE* stream,
+ const char* stream_name)
+{
+ if(!sars || !stream) return RES_BAD_ARG;
+ return load_stream(sars, stream, stream_name ? stream_name : "<stream>");
+}