star-meteo

Time varying meteorological data
git clone git://git.meso-star.fr/star-meteo.git
Log | Files | Refs | README | LICENSE

stardis_smeteo_library.c (7877B)


      1 /* Copyright (C) 2025 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is dismshbuted in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #define _POSIX_C_SOURCE 200112L /* getopt support */
     17 
     18 #include "smeteo.h"
     19 #include "stardis_smeteo.h"
     20 #include "stardis_smeteo_library.h"
     21 
     22 #include <rsys/math.h>
     23 #include <rsys/mem_allocator.h>
     24 #include <rsys/ref_count.h>
     25 #include <rsys/str.h>
     26 
     27 #include <float.h> /* DBL_MAX */
     28 #include <unistd.h> /* getopt */
     29 
     30 struct stardis_smeteo_lib {
     31   struct smeteo* smeteo;
     32   struct str filename; /* Filename of the loaded smeteo file */
     33   double max_convection_coef;
     34   double Tsrf_range[2]; /* Range of the surface temperatures [K] */
     35   double Trad_range[2]; /* Range of the radiative temperatures [K] */
     36 
     37   /* Algorithm for computing the solar position */
     38   enum scem_sun_algo algo;
     39 
     40   /* Number of seconds elapsed since the epoch until January 1, 1850, UTC+00:00
     41    * The day_1850 field in the smeteo file can therefore be used to calculate
     42    * the number of seconds to add to this member variable in order to convert
     43    * the smeteo time to UTC+00:00 */
     44   time_t jan_1_1850;
     45 
     46   ref_T ref;
     47 };
     48 
     49 struct args {
     50   char* filename;
     51   enum scem_sun_algo algo;
     52 };
     53 #define ARGS_DEFAULT__ {NULL, SCEM_SUN_MEEUS}
     54 static const struct args ARGS_DEFAULT = ARGS_DEFAULT__;
     55 
     56 /*******************************************************************************
     57  * Helper functions
     58  ******************************************************************************/
     59 static void
     60 usage(FILE* stream, const char* name)
     61 {
     62   ASSERT(stream && name);
     63   fprintf(stream, "usage: %s [-a sun_algo] file\n", name);
     64 }
     65 
     66 static res_T
     67 parse_algo(const char* str, enum scem_sun_algo* algo)
     68 {
     69   ASSERT(str && algo);
     70 
     71   if(!strcmp(str, "meeus")) {
     72     *algo = SCEM_SUN_MEEUS;
     73   } else if(!strcmp(str, "psa")) {
     74     *algo = SCEM_SUN_PSA;
     75   } else {
     76     return RES_BAD_ARG;
     77   }
     78   return RES_OK;
     79 }
     80 
     81 static res_T
     82 args_init(struct args* args, int argc, char* argv[])
     83 {
     84   int opt = 0;
     85   res_T res = RES_OK;
     86   ASSERT(args && argc >= 1 && argv);
     87 
     88   *args = ARGS_DEFAULT;
     89 
     90   optind = 1;
     91   while((opt=getopt(argc, argv, "a:")) != -1) {
     92     switch(opt) {
     93       case 'a':
     94         res = parse_algo(optarg, &args->algo);
     95         break;
     96       default: res = RES_BAD_ARG; break;
     97     }
     98     if(res != RES_OK) {
     99       if(optarg) {
    100         fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n",
    101           argv[0], optarg, opt);
    102       }
    103       goto error;
    104     }
    105   }
    106   if(optind >= argc) { res = RES_BAD_ARG; goto error; }
    107   args->filename = argv[optind];
    108 
    109 exit:
    110   return res;
    111 error:
    112   usage(stderr, argv[0]);
    113   goto exit;
    114 }
    115 
    116 /* Retrieve the number of seconds elapsed since the epoch until January 1, 1850,
    117  * local time */
    118 static res_T
    119 setup_utc_reference(struct stardis_smeteo_lib* lib)
    120 {
    121   struct tm date = {0};
    122   struct smeteo_desc desc = SMETEO_DESC_NULL;
    123   res_T res = RES_OK;
    124   ASSERT(lib);
    125 
    126   if((res = smeteo_get_desc(lib->smeteo, &desc)) != RES_OK) return res;
    127 
    128   date.tm_mday = 1;
    129   date.tm_mon = 0; /* January */
    130   date.tm_year = 1850 - 1900;
    131   date.tm_min = 0;
    132   date.tm_hour = 0;
    133   date.tm_sec = 0;
    134   date.tm_isdst = -1; /* Daylight saving time is unknown */
    135 
    136   lib->jan_1_1850 = mktime(&date);
    137   if(lib->jan_1_1850 == (time_t)-1) return RES_UNKNOWN_ERR;
    138 
    139   return RES_OK;
    140 }
    141 
    142 static res_T
    143 setup_smeteo
    144   (struct stardis_smeteo_lib* lib,
    145    const struct stardis_program_context* ctx,
    146    const struct args* args)
    147 {
    148   struct smeteo_create_args smeteo_args = SMETEO_CREATE_ARGS_DEFAULT;
    149   struct smeteo_desc desc = SMETEO_DESC_NULL;
    150 
    151   double max_H = -DBL_MAX;
    152   double Trad_range[2] = {DBL_MAX, -DBL_MAX};
    153   double Tsrf_range[2] = {DBL_MAX, -DBL_MAX};
    154   size_t i = 0;
    155   res_T res = RES_OK;
    156 
    157   ASSERT(lib && ctx && args);
    158 
    159   /* Create a smeteo instance */
    160   smeteo_args.verbose = ctx->verbosity_level;
    161   res = smeteo_create(&smeteo_args, &lib->smeteo);
    162   if(res != RES_OK) goto error;
    163 
    164   /* Load meteorological data */
    165   if((res = smeteo_load(lib->smeteo, args->filename)) != RES_OK) goto error;
    166   if((res = smeteo_get_desc(lib->smeteo, &desc)) != RES_OK) goto error;
    167 
    168   if((res = setup_utc_reference(lib)) != RES_OK) goto error;
    169   lib->algo = args->algo;
    170 
    171   /* Retrieve the maximum convection coefficient from meteorological data */
    172   FOR_EACH(i, 0, desc.nentries) max_H = MMAX(desc.entries[i].H, max_H);
    173   lib->max_convection_coef = max_H;
    174 
    175   /* Retrieve the ground/radiative temperature range from meteorological data */
    176   FOR_EACH(i, 0, desc.nentries) {
    177     Tsrf_range[0] = MMIN(desc.entries[i].Tsrf, Tsrf_range[0]);
    178     Tsrf_range[1] = MMAX(desc.entries[i].Tsrf, Tsrf_range[1]);
    179     Trad_range[0] = MMIN(desc.entries[i].Trad, Trad_range[0]);
    180     Trad_range[1] = MMAX(desc.entries[i].Trad, Trad_range[1]);
    181   }
    182   lib->Tsrf_range[0] = Tsrf_range[0];
    183   lib->Tsrf_range[1] = Tsrf_range[1];
    184   lib->Trad_range[0] = Trad_range[0];
    185   lib->Trad_range[1] = Trad_range[1];
    186 
    187 exit:
    188   return res;
    189 error:
    190   goto exit;
    191 }
    192 
    193 static void
    194 release_stardis_smeteo_lib(ref_T* ref)
    195 {
    196   struct stardis_smeteo_lib* lib =
    197     CONTAINER_OF(ref, struct stardis_smeteo_lib, ref);
    198   ASSERT(ref);
    199 
    200   if(lib->smeteo) SMETEO(ref_put(lib->smeteo));
    201   str_release(&lib->filename);
    202   mem_rm(lib);
    203 }
    204 
    205 /*******************************************************************************
    206  * Exported symbols
    207  ******************************************************************************/
    208 void*
    209 stardis_create_library_data
    210   (const struct stardis_program_context* ctx,
    211    size_t argc,
    212    char* argv[])
    213 {
    214   struct args args = ARGS_DEFAULT;
    215   struct stardis_smeteo_lib* lib = NULL;
    216 
    217   res_T res = RES_OK;
    218 
    219   if(!ctx || argc < 1 || !argv) goto error;
    220 
    221   /* Parse library arguments */
    222   if((res = args_init(&args, (int)argc, argv)) != RES_OK) goto error;
    223 
    224   /* Allocate the stardis_smeteo_lib data structure */
    225   if(!(lib = mem_calloc(1, sizeof(*lib)))) goto error;
    226   ref_init(&lib->ref);
    227   str_init(NULL, &lib->filename);
    228 
    229   if((res = setup_smeteo(lib, ctx, &args)) != RES_OK) goto error;
    230   if((res = str_set(&lib->filename, args.filename)) != RES_OK) goto error;
    231 
    232 exit:
    233   return lib;
    234 error:
    235   if(lib) { stardis_smeteo_lib_ref_put(lib); lib = NULL; }
    236   goto exit;
    237 }
    238 
    239 void
    240 stardis_release_library_data(void* lib_data)
    241 {
    242   stardis_smeteo_lib_ref_put(lib_data);
    243 }
    244 
    245 enum stardis_return_status
    246 stardis_finalize_library_data(void* lib_data)
    247 {
    248   (void)lib_data;
    249   return STARDIS_SUCCESS; /* Nothing to be done */
    250 }
    251 
    252 /*******************************************************************************
    253  * Local functions
    254  ******************************************************************************/
    255 void
    256 stardis_smeteo_lib_ref_get(struct stardis_smeteo_lib* lib)
    257 {
    258   ASSERT(lib);
    259   ref_get(&lib->ref);
    260 }
    261 
    262 void
    263 stardis_smeteo_lib_ref_put(struct stardis_smeteo_lib* lib)
    264 {
    265   ASSERT(lib);
    266   ref_put(&lib->ref, release_stardis_smeteo_lib);
    267 }
    268 
    269 void
    270 stardis_smeteo_lib_get_desc
    271   (const struct stardis_smeteo_lib* lib,
    272    struct stardis_smeteo_lib_desc* desc)
    273 {
    274   ASSERT(lib && desc);
    275   SMETEO(get_desc(lib->smeteo, &desc->smeteo_desc));
    276   desc->filename = str_cget(&lib->filename);
    277   desc->max_convection_coef = lib->max_convection_coef;
    278   desc->Tsrf_range[0] = lib->Tsrf_range[0];
    279   desc->Tsrf_range[1] = lib->Tsrf_range[1];
    280   desc->Trad_range[0] = lib->Trad_range[0];
    281   desc->Trad_range[1] = lib->Trad_range[1];
    282   desc->jan_1_1850 = lib->jan_1_1850;
    283   desc->algo = lib->algo;
    284 }