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 }