atrstm

Load and structure a combustion gas mixture
git clone git://git.meso-star.fr/atrstm.git
Log | Files | Refs | README | LICENSE

commit 95a8cd9eaa5e383b3f788fe1e1153d0e0d071997
parent a3496b80792ed74cd68c21576ffd6642b60652e0
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 20 Jan 2021 11:45:44 +0100

Finalise a first implementation of the cache

Diffstat:
Mcmake/CMakeLists.txt | 2++
Msrc/atrstm.c | 8++++++++
Msrc/atrstm.h | 2++
Msrc/atrstm_build_octrees.c | 9+++++++++
Msrc/atrstm_c.h | 3+++
Asrc/atrstm_cache.c | 373+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/atrstm_cache.h | 44++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_atrstm.c | 6+++++-
8 files changed, 446 insertions(+), 1 deletion(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -55,6 +55,7 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(ATRSTM_FILES_SRC atrstm.c atrstm_build_octrees.c + atrstm_cache.c atrstm_log.c atrstm_optprops.c atrstm_partition.c @@ -62,6 +63,7 @@ set(ATRSTM_FILES_SRC set(ATRSTM_FILES_INC atrstm_build_octrees.h atrstm_c.h + atrstm_cache.h atrstm_log.h atrstm_optprops.h atrstm_partition.h) diff --git a/src/atrstm.c b/src/atrstm.c @@ -18,6 +18,7 @@ #include "atrstm.h" #include "atrstm_build_octrees.h" #include "atrstm_c.h" +#include "atrstm_cache.h" #include "atrstm_log.h" #include <astoria/atrri.h> @@ -65,6 +66,7 @@ release_atrstm(ref_T* ref) if(atrstm->suvm) SUVM(device_ref_put(atrstm->suvm)); if(atrstm->volume) SUVM(volume_ref_put(atrstm->volume)); if(atrstm->svx) SVX(device_ref_put(atrstm->svx)); + if(atrstm->cache) cache_ref_put(atrstm->cache); if(atrstm->logger == &atrstm->logger__) logger_release(&atrstm->logger__); str_release(&atrstm->name); ASSERT(MEM_ALLOCATED_SIZE(&atrstm->svx_allocator) == 0); @@ -182,6 +184,12 @@ atrstm_create (atrstm->logger, &atrstm->svx_allocator, atrstm->verbose, &atrstm->svx); if(res != RES_OK) goto error; + /* Create the cache if necessary */ + if(args->cache_filename) { + res = cache_create(atrstm, args->cache_filename, &atrstm->cache); + if(res != RES_OK) goto error; + } + /* Build the octree */ build_octree_args.grid_max_definition[0] = args->grid_max_definition[0]; build_octree_args.grid_max_definition[1] = args->grid_max_definition[1]; diff --git a/src/atrstm.h b/src/atrstm.h @@ -69,6 +69,7 @@ struct atrstm_args { const char* atrck_filename; /* Filename of the Correlated K */ const char* atrtp_filename; /* Filename of the Thermodynamic properties */ const char* atrri_filename; /* Filename of the refraction index LUT */ + const char* cache_filename; /* Cache to use. NULL <=> no cache */ const char* name; /* Name of the medium */ enum atrstm_spectral_type spectral_type; /* Longwave/shortwave */ @@ -91,6 +92,7 @@ struct atrstm_args { NULL, /* AtrCK filename */ \ NULL, /* AtrTP filename */ \ NULL, /* AtrRI filename */ \ + NULL, /* Cache filename */ \ "semi transparent medium", /* Name */ \ ATRSTM_SPECTRAL_SW, /* Spectral type */ \ {500,500}, /* Spectral integration range */ \ diff --git a/src/atrstm_build_octrees.c b/src/atrstm_build_octrees.c @@ -600,6 +600,15 @@ build_octrees(struct atrstm* atrstm, const struct build_octrees_args* args) "Evaluating and partitionning the field of optical properties.\n"); time_current(&t0); +#if 0 + { + FILE* fp = fopen("optprops.txt", "w"); + CHK(fp); + CHK(dump_optprops(atrstm, &refract_id, fp) == RES_OK); + fclose(fp); + } +#endif + omp_set_nested(1); /* Enable nested threads for voxelize_volumetric_mesh */ #pragma omp parallel sections num_threads(2) { diff --git a/src/atrstm_c.h b/src/atrstm_c.h @@ -26,6 +26,7 @@ /* Forward declaration of external data types */ struct atrri; struct atrtp; +struct cache; struct suvm; struct atrstm { @@ -49,6 +50,8 @@ struct atrstm { enum atrstm_spectral_type spectral_type; double wlen_range[2]; /* Spectral range in nm */ + struct cache* cache; /* Cache of the SVX data structures */ + struct mem_allocator* allocator; struct mem_allocator svx_allocator; struct logger* logger; diff --git a/src/atrstm_cache.c b/src/atrstm_cache.c @@ -0,0 +1,373 @@ +/* Copyright (C) 2020 CNRS + * + * 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 "atrstm_c.h" +#include "atrstm_cache.h" +#include "atrstm_log.h" + +#include <astoria/atrri.h> +#include <astoria/atrtp.h> + +#include <rsys/cstr.h> +#include <rsys/hash.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +#include <star/suvm.h> + +#include <errno.h> +#include <fcntl.h> /* open */ +#include <sys/stat.h> /* S_IRUSR & S_IWUSR */ +#include <unistd.h> /* close */ +#include <string.h> + +struct cache { + FILE* stream; + struct atrstm* atrstm; + ref_T ref; +}; + +struct hash { + hash256_T therm_props; + hash256_T refract_ids; + hash256_T volume; +}; + +/* Current version the cache memory layout. One should increment it and perform + * a version management onto serialized data when the cache data structure is + * updated. */ +static const int CACHE_VERSION = 0; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +hash_compute(struct atrstm* atrstm, struct hash* hash) +{ + struct atrtp_desc atrtp_desc = ATRTP_DESC_NULL; + struct atrri_desc atrri_desc = ATRRI_DESC_NULL; + res_T res = RES_OK; + ASSERT(atrstm && hash); + + res = atrtp_get_desc(atrstm->atrtp, &atrtp_desc); + if(res != RES_OK) goto error; + res = atrri_get_desc(atrstm->atrri, &atrri_desc); + if(res != RES_OK) goto error; + + hash_sha256 + (atrtp_desc.properties, + atrtp_desc.nnodes*sizeof(double[ATRTP_COUNT__]), + hash->therm_props); + + hash_sha256 + (atrri_desc.indices, + atrri_desc.nindices*sizeof(struct atrri_refractive_index), + hash->refract_ids); + + res = suvm_volume_compute_hash + (atrstm->volume, SUVM_POSITIONS|SUVM_INDICES, hash->volume); + if(res != RES_OK) goto error; + +exit: + return res; +error: + log_err(atrstm, "Error computing the AtrSTM hash -- %s.\n", + res_to_cstr(res)); + goto exit; +} + +static res_T +hash_write(const struct hash* hash, FILE* fp) +{ + res_T res = RES_OK; + ASSERT(hash && fp); + + #define WRITE(Var, N) { \ + if(fwrite((Var), sizeof(*(Var)), (N), fp) != (N)) { \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + WRITE(hash->therm_props, 64); + WRITE(hash->refract_ids, 64); + WRITE(hash->volume, 64); + #undef WRITE + +exit: + return res; +error: + goto exit; +} + +static res_T +hash_read(struct hash* hash, FILE* fp) +{ + res_T res = RES_OK; + ASSERT(hash && fp); + + #define READ(Var, N) { \ + if(fread((Var), sizeof(*(Var)), (N), fp) != (N)) { \ + if(feof(fp)) { \ + res = RES_BAD_ARG; \ + } else if(ferror(fp)) { \ + res = RES_IO_ERR; \ + } else { \ + res = RES_UNKNOWN_ERR; \ + } \ + goto error; \ + } \ + } (void)0 + READ(hash->therm_props, 64); + READ(hash->refract_ids, 64); + READ(hash->volume, 64); + #undef READ + +exit: + return res; +error: + goto exit; +} + +/* Setup the cache header, i.e. data that uniquely identify the cache regarding + * the input data */ +static res_T +write_cache_header(struct cache* cache, const char* filename) +{ + struct hash hash; + res_T res = RES_OK; + ASSERT(cache); + + #define WRITE(Var, N) { \ + if(fwrite((Var), sizeof(*(Var)), (N), cache->stream) != (N)) { \ + log_err(cache->atrstm, \ + "%s: could not write the cache header.\n", filename); \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + WRITE(&CACHE_VERSION, 1); + WRITE(&cache->atrstm->gyration_radius_prefactor, 1); + WRITE(&cache->atrstm->fractal_dimension, 1); + WRITE(&cache->atrstm->spectral_type, 1); + WRITE(cache->atrstm->wlen_range, 2); + #undef WRITE + + res = hash_compute(cache->atrstm, &hash); + if(res != RES_OK) goto error; + res = hash_write(&hash, cache->stream); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +static res_T +read_cache_header(struct cache* cache, const char* filename) +{ + struct hash cached_hash; + struct hash current_hash; + double gyration_radius_prefactor = 0; + double fractal_dimension = 0; + enum atrstm_spectral_type spectral_type = ATRSTM_SPECTRAL_TYPES_COUNT__; + double wlen_range[2] = {0, 0}; + int cache_version = 0; + res_T res = RES_OK; + ASSERT(cache); + + /* Read the cache header */ + #define READ(Var, N) { \ + if(fread((Var), sizeof(*(Var)), (N), cache->stream) != (N)) { \ + if(feof(cache->stream)) { \ + res = RES_BAD_ARG; \ + } else if(ferror(cache->stream)) { \ + res = RES_IO_ERR; \ + } else { \ + res = RES_UNKNOWN_ERR; \ + } \ + log_err(cache->atrstm, "%s: could not read the cache header -- %s.\n", \ + filename, res_to_cstr(res)); \ + goto error; \ + } \ + } (void)0 + + READ(&cache_version, 1); + if(cache_version != CACHE_VERSION) { + log_err(cache->atrstm, + "%s: invalid cache in version %d. Expecting a cache in version %d.\n", + filename, cache_version, CACHE_VERSION); + res = RES_BAD_ARG; + goto error; + } + #define CHK_VAR(CachedVal, CurrentVal, Name, Fmt) { \ + if((CachedVal) != (CurrentVal)) { \ + log_err(cache->atrstm, \ + "%s: invalid cache regarding the "Name". " \ + "Cached value: "Fmt", Current Value: "Fmt".\n", \ + filename, (CachedVal), (CurrentVal)); \ + res = RES_BAD_ARG; \ + goto error; \ + } \ + } (void)0 + + READ(&gyration_radius_prefactor, 1); + CHK_VAR(gyration_radius_prefactor, cache->atrstm->gyration_radius_prefactor, + "gyration radius prefactor", "%g"); + + READ(&fractal_dimension, 1); + CHK_VAR(fractal_dimension, cache->atrstm->fractal_dimension, + "fractal dimension", "%g"); + + READ(&spectral_type, 1); + CHK_VAR(spectral_type, cache->atrstm->spectral_type, + "spectral type", "%i"); + + READ(wlen_range, 2); + CHK_VAR(wlen_range[0], cache->atrstm->wlen_range[0], + "spectral lower bound", "%g"); + CHK_VAR(wlen_range[1], cache->atrstm->wlen_range[1], + "spectral upper bound", "%g"); + + #undef CHK_VAR + + res = hash_read(&cached_hash, cache->stream); + if(res != RES_OK) goto error; + res = hash_compute(cache->atrstm, &current_hash); + if(res != RES_OK) goto error; + + #define CHK_HASH(CachedHash, CurrentHash, Name) { \ + if(!hash256_eq((CachedHash), (CurrentHash))) { \ + log_err(cache->atrstm, \ + "%s: invalid cache regarding the submitted "Name".\n", \ + filename); \ + res = RES_BAD_ARG; \ + goto error; \ + } \ + } (void)0 + CHK_HASH(cached_hash.therm_props, current_hash.therm_props, + "thermodynamic properties"); + CHK_HASH(cached_hash.refract_ids, current_hash.refract_ids, + "refractive indices"); + CHK_HASH(cached_hash.volume, current_hash.volume, + "volumetric mesh"); + #undef CHK_HASH + +exit: + return res; +error: + goto exit; +} + +static void +release_cache(ref_T* ref) +{ + struct cache* cache = NULL; + struct atrstm* atrstm = NULL; + ASSERT(ref); + cache = CONTAINER_OF(ref, struct cache, ref); + atrstm = cache->atrstm; + if(cache->stream) CHK(fclose(cache->stream) == 0); + MEM_RM(atrstm->allocator, cache); + ATRSTM(ref_put(atrstm)); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +cache_create + (struct atrstm* atrstm, + const char* filename, + struct cache** out_cache) +{ + struct cache* cache = NULL; + struct stat cache_stat; + int fd = -1; + int err = 0; + res_T res = RES_OK; + ASSERT(atrstm && filename && out_cache); + + cache = MEM_CALLOC(atrstm->allocator, 1, sizeof(*cache)); + if(!cache) { + res = RES_MEM_ERR; + goto error; + } + ref_init(&cache->ref); + ATRSTM(ref_get(atrstm)); + cache->atrstm = atrstm; + + fd = open(filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); + if(fd < 0) { + log_err(atrstm, "Could not open the cache file `%s' -- %s.\n", + filename, strerror(errno)); + res = RES_IO_ERR; + goto error; + } + + cache->stream = fdopen(fd, "w+"); + if(!cache->stream) { + log_err(atrstm, "Could not open the cache file `%s' -- %s.\n", + filename, strerror(errno)); + res = RES_IO_ERR; + goto error; + } + fd = -1; + + err = stat(filename, &cache_stat); + if(err < 0) { + log_err(atrstm, "Could not stat the cache file `%s' -- %s.\n", + filename, strerror(errno)); + res = RES_IO_ERR; + goto error; + } + + if(cache_stat.st_size == 0) { /* Empty cache */ + res = write_cache_header(cache, filename); + if(res != RES_OK) goto error; + } else { /* Cache already exists */ + res = read_cache_header(cache, filename); + if(res != RES_OK) goto error; + } + +exit: + *out_cache = cache; + return res; +error: + if(cache) { cache_ref_put(cache); cache = NULL; } + if(fd >= 0) CHK(close(fd) == 0); + goto exit; +} + +void +cache_ref_get(struct cache* cache) +{ + ASSERT(cache); + ref_get(&cache->ref); +} + +void +cache_ref_put(struct cache* cache) +{ + ASSERT(cache); + ref_put(&cache->ref, release_cache); +} + +FILE* +cache_get_stream(struct cache* cache) +{ + ASSERT(cache); + return cache->stream; +} diff --git a/src/atrstm_cache.h b/src/atrstm_cache.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2020 CNRS + * + * 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/>. */ + +#ifndef ATRSTM_CACHE_H +#define ATRSTM_CACHE_H + +#include <rsys/rsys.h> +#include <stdio.h> + +/* Cache of the acceleration data structures used to partitionned the optical + * properties of the semi transparent medium */ +struct cache; + +extern LOCAL_SYM res_T +cache_create + (struct atrstm* atrstm, + const char* cache_name, + struct cache** cache); + +extern LOCAL_SYM void +cache_ref_get + (struct cache* cache); + +extern LOCAL_SYM void +cache_ref_put + (struct cache* cache); + +extern LOCAL_SYM FILE* +cache_get_stream + (struct cache* cache); + +#endif /* ATRSTM_CACHE_H */ diff --git a/src/test_atrstm.c b/src/test_atrstm.c @@ -50,6 +50,9 @@ print_help(const char* cmd) " -n NAME name of the medium. Default is \"%s\".\n", ATRSTM_ARGS_DEFAULT.name); printf( +" -O CACHE name of the cache file to use/fill. By default\n" +" no cache is used\n"); + printf( " -p THERMOPROPS path toward the thermodynamic properties.\n"); printf( " -r REFRACT_ID path toward the per wavelength refractive\n" @@ -127,7 +130,7 @@ args_init(struct atrstm_args* args, int argc, char** argv) *args = ATRSTM_ARGS_DEFAULT; - while((opt = getopt(argc, argv, "f:g:hm:Nn:p:T:t:r:vV:w:")) != -1) { + while((opt = getopt(argc, argv, "f:g:hm:Nn:O:p:T:t:r:vV:w:")) != -1) { switch(opt) { case 'f': res = cstr_to_double(optarg, &args->fractal_dimension); @@ -146,6 +149,7 @@ args_init(struct atrstm_args* args, int argc, char** argv) case 'm': args->sth_filename = optarg; break; case 'N': args->precompute_normals = 1; break; case 'n': args->name = optarg; break; + case 'O': args->cache_filename = optarg; break; case 'p': args->atrtp_filename = optarg; break; case 'r': args->atrri_filename = optarg; break; case 'T':