atrstm

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

atrstm_cache.c (13522B)


      1 /* Copyright (C) 2022, 2023 |Méso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2020, 2021 Centre National de la Recherche Scientifique
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     16 
     17 #define _POSIX_C_SOURCE 200112L /* fdopen */
     18 
     19 #include "atrstm_c.h"
     20 #include "atrstm_cache.h"
     21 #include "atrstm_log.h"
     22 
     23 #include <astoria/atrri.h>
     24 #include <astoria/atrtp.h>
     25 
     26 #include <rsys/cstr.h>
     27 #include <rsys/hash.h>
     28 #include <rsys/mem_allocator.h>
     29 #include <rsys/ref_count.h>
     30 #include <rsys/str.h>
     31 
     32 #include <star/suvm.h>
     33 
     34 #include <errno.h>
     35 #include <fcntl.h> /* open */
     36 #include <sys/stat.h> /* S_IRUSR & S_IWUSR */
     37 #include <unistd.h> /* close */
     38 #include <string.h>
     39 
     40 struct cache {
     41   FILE* stream;
     42   struct atrstm* atrstm;
     43   struct str name;
     44   int empty;
     45   ref_T ref;
     46 };
     47 
     48 struct hash {
     49   hash256_T therm_props;
     50   hash256_T refract_ids;
     51   hash256_T volume;
     52 };
     53 
     54 /* Current version the cache memory layout. One should increment it and perform
     55  * a version management onto serialized data when the cache data structure is
     56  * updated. */
     57 static const int CACHE_VERSION = 1;
     58 
     59 /*******************************************************************************
     60  * Helper functions
     61  ******************************************************************************/
     62 static res_T
     63 hash_compute(struct atrstm* atrstm, struct hash* hash)
     64 {
     65   struct atrtp_desc atrtp_desc = ATRTP_DESC_NULL;
     66   struct atrri_desc atrri_desc = ATRRI_DESC_NULL;
     67   res_T res = RES_OK;
     68   ASSERT(atrstm && hash);
     69 
     70   res = atrtp_get_desc(atrstm->atrtp, &atrtp_desc);
     71   if(res != RES_OK) goto error;
     72   res = atrri_get_desc(atrstm->atrri, &atrri_desc);
     73   if(res != RES_OK) goto error;
     74 
     75   hash_sha256
     76     (atrtp_desc.properties,
     77      atrtp_desc.nnodes*sizeof(double[ATRTP_COUNT__]),
     78      hash->therm_props);
     79 
     80   hash_sha256
     81     (atrri_desc.indices,
     82      atrri_desc.nindices*sizeof(struct atrri_refractive_index),
     83      hash->refract_ids);
     84 
     85   res = suvm_volume_compute_hash
     86     (atrstm->volume, SUVM_POSITIONS|SUVM_INDICES, hash->volume);
     87   if(res != RES_OK) goto error;
     88 
     89 exit:
     90   return res;
     91 error:
     92   log_err(atrstm, "Error computing the AtrSTM hash -- %s.\n",
     93     res_to_cstr(res));
     94   goto exit;
     95 }
     96 
     97 static res_T
     98 hash_write(const struct hash* hash, FILE* fp)
     99 {
    100   res_T res = RES_OK;
    101   ASSERT(hash && fp);
    102 
    103   #define WRITE(Var, N) {                                                      \
    104     if(fwrite((Var), sizeof(*(Var)), (N), fp) != (N)) {                        \
    105       res = RES_IO_ERR;                                                        \
    106       goto error;                                                              \
    107     }                                                                          \
    108   } (void)0
    109   WRITE(hash->therm_props, sizeof(hash256_T));
    110   WRITE(hash->refract_ids, sizeof(hash256_T));
    111   WRITE(hash->volume, sizeof(hash256_T));
    112   #undef WRITE
    113 
    114 exit:
    115   return res;
    116 error:
    117   goto exit;
    118 }
    119 
    120 static res_T
    121 hash_read(struct hash* hash, FILE* fp)
    122 {
    123   res_T res = RES_OK;
    124   ASSERT(hash && fp);
    125 
    126   #define READ(Var, N) {                                                       \
    127     if(fread((Var), sizeof(*(Var)), (N), fp) != (N)) {                         \
    128       if(feof(fp)) {                                                           \
    129         res = RES_BAD_ARG;                                                     \
    130       } else if(ferror(fp)) {                                                  \
    131         res = RES_IO_ERR;                                                      \
    132       } else {                                                                 \
    133         res = RES_UNKNOWN_ERR;                                                 \
    134       }                                                                        \
    135       goto error;                                                              \
    136     }                                                                          \
    137   } (void)0
    138   READ(hash->therm_props, sizeof(hash256_T));
    139   READ(hash->refract_ids, sizeof(hash256_T));
    140   READ(hash->volume, sizeof(hash256_T));
    141   #undef READ
    142 
    143 exit:
    144   return res;
    145 error:
    146   goto exit;
    147 }
    148 
    149 /* Setup the cache header, i.e. data that uniquely identify the cache regarding
    150  * the input data */
    151 static res_T
    152 write_cache_header(struct cache* cache)
    153 {
    154   struct hash hash;
    155   res_T res = RES_OK;
    156   ASSERT(cache);
    157 
    158   #define WRITE(Var, N) {                                                      \
    159     if(fwrite((Var), sizeof(*(Var)), (N), cache->stream) != (N)) {             \
    160       log_err(cache->atrstm,                                                   \
    161         "%s: could not write the cache header.\n", cache_get_name(cache));     \
    162       res = RES_IO_ERR;                                                        \
    163       goto error;                                                              \
    164     }                                                                          \
    165   } (void)0
    166   WRITE(&CACHE_VERSION, 1);
    167   WRITE(&cache->atrstm->fractal_prefactor, 1);
    168   WRITE(&cache->atrstm->fractal_dimension, 1);
    169   WRITE(&cache->atrstm->spectral_type, 1);
    170   WRITE(cache->atrstm->wlen_range, 2);
    171   WRITE(cache->atrstm->grid_max_definition, 3);
    172   WRITE(&cache->atrstm->optical_thickness, 1);
    173   WRITE(&cache->atrstm->use_simd, 1);
    174   #undef WRITE
    175 
    176   res = hash_compute(cache->atrstm, &hash);
    177   if(res != RES_OK) goto error;
    178   res = hash_write(&hash, cache->stream);
    179   if(res != RES_OK) goto error;
    180 
    181 exit:
    182   return res;
    183 error:
    184   goto exit;
    185 }
    186 
    187 static res_T
    188 read_cache_header(struct cache* cache)
    189 {
    190   struct hash cached_hash;
    191   struct hash current_hash;
    192   double fractal_prefactor = 0;
    193   double fractal_dimension = 0;
    194   enum atrstm_spectral_type spectral_type = ATRSTM_SPECTRAL_TYPES_COUNT__;
    195   double wlen_range[2] = {0,0};
    196   unsigned grid_max_definition[3] = {0,0,0};
    197   double optical_thickness = 0;
    198   int use_simd = 0;
    199   int cache_version = 0;
    200   res_T res = RES_OK;
    201   ASSERT(cache);
    202 
    203   /* Read the cache header */
    204   #define READ(Var, N) {                                                       \
    205     if(fread((Var), sizeof(*(Var)), (N), cache->stream) != (N)) {              \
    206       if(feof(cache->stream)) {                                                \
    207         res = RES_BAD_ARG;                                                     \
    208       } else if(ferror(cache->stream)) {                                       \
    209         res = RES_IO_ERR;                                                      \
    210       } else {                                                                 \
    211         res = RES_UNKNOWN_ERR;                                                 \
    212       }                                                                        \
    213       log_err(cache->atrstm, "%s: could not read the cache header -- %s.\n",   \
    214         cache_get_name(cache), res_to_cstr(res));                              \
    215       goto error;                                                              \
    216     }                                                                          \
    217   } (void)0
    218 
    219   READ(&cache_version, 1);
    220   if(cache_version != CACHE_VERSION) {
    221     log_err(cache->atrstm,
    222       "%s: invalid cache in version %d. Expecting a cache in version %d.\n",
    223       cache_get_name(cache), cache_version, CACHE_VERSION);
    224     res = RES_BAD_ARG;
    225     goto error;
    226   }
    227   #define CHK_VAR(CachedVal, CurrentVal, Name, Fmt) {                          \
    228     if((CachedVal) != (CurrentVal)) {                                          \
    229       log_err(cache->atrstm,                                                   \
    230         "%s: invalid cache regarding the "Name". "                             \
    231         "Cached value: "Fmt". Current Value: "Fmt".\n",                        \
    232         cache_get_name(cache), (CachedVal), (CurrentVal));                     \
    233       res = RES_BAD_ARG;                                                       \
    234       goto error;                                                              \
    235     }                                                                          \
    236   } (void)0
    237 
    238   READ(&fractal_prefactor, 1);
    239   CHK_VAR(fractal_prefactor, cache->atrstm->fractal_prefactor,
    240     "fractal prefactor", "%g");
    241 
    242   READ(&fractal_dimension, 1);
    243   CHK_VAR(fractal_dimension, cache->atrstm->fractal_dimension,
    244     "fractal dimension", "%g");
    245 
    246   READ(&spectral_type, 1);
    247   CHK_VAR(spectral_type, cache->atrstm->spectral_type,
    248     "spectral type", "%i");
    249 
    250   READ(wlen_range, 2);
    251   CHK_VAR(wlen_range[0], cache->atrstm->wlen_range[0],
    252     "spectral lower bound", "%g");
    253   CHK_VAR(wlen_range[1], cache->atrstm->wlen_range[1],
    254     "spectral upper bound", "%g");
    255 
    256   READ(grid_max_definition, 3);
    257   CHK_VAR(grid_max_definition[0], cache->atrstm->grid_max_definition[0],
    258     "grid X definition", "%u");
    259   CHK_VAR(grid_max_definition[1], cache->atrstm->grid_max_definition[1],
    260     "grid Y definition", "%u");
    261   CHK_VAR(grid_max_definition[2], cache->atrstm->grid_max_definition[2],
    262     "grid Z definition", "%u");
    263 
    264   READ(&optical_thickness, 1);
    265   CHK_VAR(optical_thickness, cache->atrstm->optical_thickness,
    266     "optical thickness", "%g");
    267 
    268   READ(&use_simd, 1);
    269   CHK_VAR(use_simd, cache->atrstm->use_simd, "use_simd flag", "%i");
    270 
    271   #undef CHK_VAR
    272 
    273   res = hash_read(&cached_hash, cache->stream);
    274   if(res != RES_OK) goto error;
    275   res = hash_compute(cache->atrstm, &current_hash);
    276   if(res != RES_OK) goto error;
    277 
    278   #define CHK_HASH(CachedHash, CurrentHash, Name) {                            \
    279     if(!hash256_eq((CachedHash), (CurrentHash))) {                             \
    280       log_err(cache->atrstm,                                                   \
    281         "%s: invalid cache regarding the submitted "Name".\n",                 \
    282         cache_get_name(cache));                                                \
    283       res = RES_BAD_ARG;                                                       \
    284       goto error;                                                              \
    285     }                                                                          \
    286   } (void)0
    287   CHK_HASH(cached_hash.therm_props, current_hash.therm_props,
    288     "thermodynamic properties");
    289   CHK_HASH(cached_hash.refract_ids, current_hash.refract_ids,
    290     "refractive indices");
    291   CHK_HASH(cached_hash.volume, current_hash.volume,
    292     "volumetric mesh");
    293   #undef CHK_HASH
    294 
    295 exit:
    296   return res;
    297 error:
    298   goto exit;
    299 }
    300 
    301 static void
    302 release_cache(ref_T* ref)
    303 {
    304   struct cache* cache = NULL;
    305   struct atrstm* atrstm = NULL;
    306   ASSERT(ref);
    307   cache = CONTAINER_OF(ref, struct cache, ref);
    308   atrstm = cache->atrstm;
    309   if(cache->stream) CHK(fclose(cache->stream) == 0);
    310   str_release(&cache->name);
    311   MEM_RM(atrstm->allocator, cache);
    312 }
    313 
    314 /*******************************************************************************
    315  * Local functions
    316  ******************************************************************************/
    317 res_T
    318 cache_create
    319   (struct atrstm* atrstm,
    320    const char* filename,
    321    struct cache** out_cache)
    322 {
    323   struct cache* cache = NULL;
    324   struct stat cache_stat;
    325   int fd = -1;
    326   int err = 0;
    327   res_T res = RES_OK;
    328   ASSERT(atrstm && filename && out_cache);
    329 
    330   cache = MEM_CALLOC(atrstm->allocator, 1, sizeof(*cache));
    331   if(!cache) {
    332     res = RES_MEM_ERR;
    333     goto error;
    334   }
    335   ref_init(&cache->ref);
    336   str_init(atrstm->allocator, &cache->name);
    337   cache->atrstm = atrstm;
    338 
    339   res = str_set(&cache->name, filename);
    340   if(res != RES_OK) {
    341     log_err(atrstm, "Could not copy the cache filename -- %s.\n",
    342       res_to_cstr(res));
    343     goto error;
    344   }
    345 
    346   fd = open(filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    347   if(fd < 0) {
    348     log_err(atrstm, "Could not open the cache file `%s' -- %s.\n",
    349       filename, strerror(errno));
    350     res = RES_IO_ERR;
    351     goto error;
    352   }
    353 
    354   cache->stream = fdopen(fd, "w+");
    355   if(!cache->stream) {
    356     log_err(atrstm, "Could not open the cache file `%s' -- %s.\n",
    357       filename, strerror(errno));
    358     res = RES_IO_ERR;
    359     goto error;
    360   }
    361   fd = -1;
    362 
    363   err = stat(filename, &cache_stat);
    364   if(err < 0) {
    365     log_err(atrstm, "Could not stat the cache file `%s' -- %s.\n",
    366       filename, strerror(errno));
    367     res = RES_IO_ERR;
    368     goto error;
    369   }
    370 
    371   if(cache_stat.st_size == 0) { /* Empty cache */
    372     cache->empty = 1;
    373     res = write_cache_header(cache);
    374     if(res != RES_OK) goto error;
    375   } else { /* Cache already exists */
    376     cache->empty = 0;
    377     res = read_cache_header(cache);
    378     if(res != RES_OK) goto error;
    379   }
    380 
    381 exit:
    382   *out_cache = cache;
    383   return res;
    384 error:
    385   if(cache) { cache_ref_put(cache); cache = NULL; }
    386   if(fd >= 0) CHK(close(fd) == 0);
    387   goto exit;
    388 }
    389 
    390 void
    391 cache_ref_get(struct cache* cache)
    392 {
    393   ASSERT(cache);
    394   ref_get(&cache->ref);
    395 }
    396 
    397 void
    398 cache_ref_put(struct cache* cache)
    399 {
    400   ASSERT(cache);
    401   ref_put(&cache->ref, release_cache);
    402 }
    403 
    404 FILE*
    405 cache_get_stream(struct cache* cache)
    406 {
    407   ASSERT(cache);
    408   return cache->stream;
    409 }
    410 
    411 int
    412 cache_is_empty(const struct cache* cache)
    413 {
    414   ASSERT(cache);
    415   return cache->empty;
    416 }
    417 
    418 const char*
    419 cache_get_name(const struct cache* cache)
    420 {
    421   ASSERT(cache);
    422   return str_cget(&cache->name);
    423 }