smashbox 1.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. smashbox/.spyproject/config/backups/codestyle.ini.bak +8 -0
  2. smashbox/.spyproject/config/backups/encoding.ini.bak +6 -0
  3. smashbox/.spyproject/config/backups/vcs.ini.bak +7 -0
  4. smashbox/.spyproject/config/backups/workspace.ini.bak +12 -0
  5. smashbox/.spyproject/config/codestyle.ini +8 -0
  6. smashbox/.spyproject/config/defaults/defaults-codestyle-0.2.0.ini +5 -0
  7. smashbox/.spyproject/config/defaults/defaults-encoding-0.2.0.ini +3 -0
  8. smashbox/.spyproject/config/defaults/defaults-vcs-0.2.0.ini +4 -0
  9. smashbox/.spyproject/config/defaults/defaults-workspace-0.2.0.ini +6 -0
  10. smashbox/.spyproject/config/encoding.ini +6 -0
  11. smashbox/.spyproject/config/vcs.ini +7 -0
  12. smashbox/.spyproject/config/workspace.ini +12 -0
  13. smashbox/__init__.py +8 -0
  14. smashbox/asset/flwdir/flowdir_fr_1000m.tif +0 -0
  15. smashbox/asset/outlets/.Rhistory +0 -0
  16. smashbox/asset/outlets/db_bnbv_fr.csv +142704 -0
  17. smashbox/asset/outlets/db_bnbv_light.csv +42084 -0
  18. smashbox/asset/outlets/db_sites.csv +8700 -0
  19. smashbox/asset/outlets/db_stations.csv +2916 -0
  20. smashbox/asset/outlets/db_stations_example.csv +19 -0
  21. smashbox/asset/outlets/edit_database.py +185 -0
  22. smashbox/asset/outlets/readme.txt +5 -0
  23. smashbox/asset/params/ci.tif +0 -0
  24. smashbox/asset/params/cp.tif +0 -0
  25. smashbox/asset/params/ct.tif +0 -0
  26. smashbox/asset/params/kexc.tif +0 -0
  27. smashbox/asset/params/kmlt.tif +0 -0
  28. smashbox/asset/params/llr.tif +0 -0
  29. smashbox/asset/setup/setup_rhax_gr4_dt3600.yaml +15 -0
  30. smashbox/asset/setup/setup_rhax_gr4_dt900.yaml +15 -0
  31. smashbox/asset/setup/setup_rhax_gr5_dt3600.yaml +15 -0
  32. smashbox/asset/setup/setup_rhax_gr5_dt900.yaml +15 -0
  33. smashbox/init/README.md +3 -0
  34. smashbox/init/__init__.py +3 -0
  35. smashbox/init/multimodel_statistics.py +405 -0
  36. smashbox/init/param.py +799 -0
  37. smashbox/init/smashbox.py +186 -0
  38. smashbox/model/__init__.py +1 -0
  39. smashbox/model/atmos_data_connector.py +518 -0
  40. smashbox/model/mesh.py +185 -0
  41. smashbox/model/model.py +829 -0
  42. smashbox/model/setup.py +109 -0
  43. smashbox/plot/__init__.py +1 -0
  44. smashbox/plot/myplot.py +1133 -0
  45. smashbox/plot/plot.py +1662 -0
  46. smashbox/read_inputdata/__init__.py +1 -0
  47. smashbox/read_inputdata/read_data.py +1229 -0
  48. smashbox/read_inputdata/smashmodel.py +395 -0
  49. smashbox/stats/__init__.py +1 -0
  50. smashbox/stats/mystats.py +1632 -0
  51. smashbox/stats/stats.py +2022 -0
  52. smashbox/test.py +532 -0
  53. smashbox/test_average_stats.py +122 -0
  54. smashbox/test_mesh.r +8 -0
  55. smashbox/test_mesh_from_graffas.py +69 -0
  56. smashbox/tools/__init__.py +1 -0
  57. smashbox/tools/geo_toolbox.py +1028 -0
  58. smashbox/tools/tools.py +461 -0
  59. smashbox/tutorial_R.r +182 -0
  60. smashbox/tutorial_R_graffas.r +88 -0
  61. smashbox/tutorial_R_graffas_local.r +33 -0
  62. smashbox/tutorial_python.py +102 -0
  63. smashbox/tutorial_readme.py +261 -0
  64. smashbox/tutorial_report.py +58 -0
  65. smashbox/tutorials/Python_tutorial.md +124 -0
  66. smashbox/tutorials/R_Graffas_tutorial.md +153 -0
  67. smashbox/tutorials/R_tutorial.md +121 -0
  68. smashbox/tutorials/__init__.py +6 -0
  69. smashbox/tutorials/generate_doc.md +7 -0
  70. smashbox-1.0.dist-info/METADATA +998 -0
  71. smashbox-1.0.dist-info/RECORD +73 -0
  72. smashbox-1.0.dist-info/WHEEL +5 -0
  73. smashbox-1.0.dist-info/licenses/LICENSE +100 -0
@@ -0,0 +1,395 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on Fri Sep 27 12:58:19 2024
5
+
6
+ @author: maxime
7
+ """
8
+
9
+ import smash
10
+ import yaml
11
+
12
+ import os
13
+ import pandas as pd
14
+ import datetime
15
+ import shutil
16
+ import glob
17
+ import random
18
+ from . import read_data
19
+
20
+ update_default_setup = {
21
+ "prcp_date_pattern": "",
22
+ "pet_date_pattern": "",
23
+ "snow_date_pattern": "",
24
+ "temp_date_pattern": "",
25
+ "prcp_directories": {},
26
+ "pet_directories": {},
27
+ "snow_directories": {},
28
+ "temp_directories": {},
29
+ "continuous_pet": {},
30
+ "timezone": "UTC",
31
+ }
32
+
33
+
34
+ def standardize_update_default_setup_options(model, update_default_setup):
35
+
36
+ for var in [
37
+ "prcp_date_pattern",
38
+ "snow_date_pattern",
39
+ "temp_date_pattern",
40
+ "pet_date_pattern",
41
+ ]:
42
+ if update_default_setup[var] == "":
43
+ if model.setup.dt == 86_400:
44
+ update_default_setup[var] = "%Y%m%d"
45
+ else:
46
+ update_default_setup[var] = "%Y%m%d%H%M"
47
+
48
+ if model.setup.daily_interannual_pet:
49
+ update_default_setup["pet_date_pattern"] = "%Y%m%d"
50
+
51
+
52
+ def standardize_model_date_pattern(key: str, value: str) -> str:
53
+ if len(value) > 0:
54
+ if not isinstance(value, str):
55
+ raise TypeError(f"{key} model setup must be a str")
56
+
57
+ if not value.startswith("%"):
58
+ raise ValueError(f"{value} must start by character %")
59
+
60
+ sample = value.split("%")
61
+ sample.pop(0)
62
+
63
+ for s in sample:
64
+ if not isinstance(s, str) and len(s) > 1:
65
+ raise ValueError(
66
+ f"%{s} in {value} must start by character % following by a unique letter: Ex: %Y%m%d%H%M"
67
+ )
68
+
69
+ return value
70
+
71
+
72
+ def standardize_model_data_directories(key: str, value: dict) -> str:
73
+
74
+ if not isinstance(value, dict):
75
+ raise TypeError(f"{key} model setup must be a dict")
76
+
77
+ key_to_pop = []
78
+ for subkey, subvalue in value.items():
79
+
80
+ if isinstance(subkey, str):
81
+ try:
82
+ int(subkey)
83
+ except:
84
+ raise ValueError(
85
+ f"{subkey}, must represent an integer as the dict key (order priority of the data)."
86
+ )
87
+ elif not isinstance(subkey, int):
88
+ raise ValueError(
89
+ f"{subkey}, must be an integer as the dict key (order priority of the data)."
90
+ )
91
+
92
+ if not isinstance(subvalue, str):
93
+ raise ValueError(f"{subvalue}, must be a str, a pathlike.")
94
+
95
+ if len(subvalue) == 0:
96
+ key_to_pop.append(subkey)
97
+
98
+ if len(subvalue) > 0 and not os.path.exists(subvalue):
99
+ raise ValueError(f"'{subvalue}', is not an existing path.")
100
+
101
+ # remove empty keys
102
+ for subkey in key_to_pop:
103
+ value.pop(subkey)
104
+
105
+ return value
106
+
107
+
108
+ def standardize_model_continuous_pet(key: str, value: dict) -> str:
109
+
110
+ if not isinstance(value, dict):
111
+ raise TypeError(f"{key} model setup must be a dict")
112
+
113
+ true_count = 0
114
+ for subkey, subvalue in value.items():
115
+
116
+ if isinstance(subkey, str):
117
+ try:
118
+ int(subkey)
119
+ except:
120
+ raise ValueError(
121
+ f"{subkey}, must represent an integer as the dict key (order priority of the data)."
122
+ )
123
+ elif not isinstance(subkey, int):
124
+ raise ValueError(
125
+ f"{subkey}, must be an integer as the dict key (order priority of the pet data)."
126
+ )
127
+
128
+ if not isinstance(subvalue, bool):
129
+ raise ValueError(f"{subvalue}, must be a bool.")
130
+
131
+ if subvalue == True:
132
+ true_count = true_count + 1
133
+
134
+ if true_count > 1:
135
+ raise ValueError("Only one True data is allowed in continuous pet")
136
+
137
+ return value
138
+
139
+
140
+ def update_model_setup(model, update_default_setup):
141
+
142
+ for key, value in update_default_setup.items():
143
+ setattr(model.setup, key, value)
144
+
145
+
146
+ # Build a smash model from a setup and a mesh with external new options
147
+ # path_setup: path to the setup file
148
+ # path_mesh: path to the mesh file
149
+ # setup_options: dictionnary to pass setup options that will overwrite options in path_setup
150
+ # new options can be defined in path_setup:
151
+ # prcp_directories: dict
152
+ # pet_directories: dict
153
+ # snow_directories: dict
154
+ # temp_directories: dict
155
+ # prcp_date_pattern : str
156
+ # pet_date_pattern : str
157
+ # snow_date_pattern : str
158
+ # temp_date_pattern: str
159
+ # continuous_pet : dict of bool
160
+ # timezone: str
161
+
162
+
163
+ def SmashModel(input_setup, input_mesh, setup_options={}):
164
+ """
165
+
166
+ Description
167
+ -----------
168
+
169
+ Build a model Smash like smash.Model() but extend capabilities of the atmos data reading
170
+
171
+ Paramerters
172
+ -----------
173
+
174
+ input_setup: str() | dict()
175
+ the path to the setup.yaml file or the setup dictionary
176
+
177
+ input_mesh: str() | dict()
178
+ the path to the mesh.hdf5 file or the mesh dictionary
179
+
180
+ setup_options: dict()
181
+ dictionnary of setup options that will erase setup.yaml
182
+
183
+ Return
184
+ ------
185
+
186
+ smash.Model()
187
+ Un objet model smash.
188
+
189
+ """
190
+
191
+ if isinstance(input_setup, str):
192
+ setup = smash.io.read_setup(input_setup)
193
+ elif isinstance(input_setup, dict):
194
+ setup = input_setup.copy()
195
+ else:
196
+ raise ValueError("Wrong setup data type: input_setup must be a path or a dict")
197
+
198
+ sending_options = setup_options.copy()
199
+
200
+ for key, value in setup_options.items():
201
+ setup.update({key: value})
202
+
203
+ # filter setup options not allowed in smash to prevent warnings
204
+ for key in update_default_setup:
205
+ if key in setup:
206
+ setup.pop(key)
207
+
208
+ if isinstance(input_mesh, str):
209
+ mesh = smash.io.read_mesh(input_mesh)
210
+ elif isinstance(input_mesh, dict):
211
+ mesh = input_mesh.copy()
212
+ else:
213
+ raise ValueError("Wrong mesh data type: setup must be a path or a dict")
214
+
215
+ # force to prevent reading data
216
+ if "read_prcp" in setup:
217
+ setup["read_prcp"] = False
218
+ if "read_pet" in setup:
219
+ setup["read_pet"] = False
220
+ if "read_temp" in setup:
221
+ setup["read_temp"] = False
222
+ if "read_snow" in setup:
223
+ setup["read_snow"] = False
224
+ if "compute_mean_atmos" in setup:
225
+ setup["compute_mean_atmos"] = False
226
+
227
+ print(
228
+ f"</> Building model from {setup['start_time']} to {setup['end_time']} with time-step {setup['dt']}"
229
+ )
230
+
231
+ # build the model
232
+ model = smash.Model(setup, mesh)
233
+
234
+ # Updtade model.setup with new options
235
+ standardize_update_default_setup_options(model, update_default_setup)
236
+ update_model_setup(model, update_default_setup)
237
+
238
+ # read all options in setup, not only those for smash:
239
+ if isinstance(input_setup, str):
240
+ with open(input_setup, "r") as file:
241
+ setup_overwrite = yaml.safe_load(file)
242
+ elif isinstance(input_setup, dict):
243
+ setup_overwrite = input_setup.copy()
244
+ else:
245
+ raise ValueError("Wrong setup data type: input_setup must be a path or a dict")
246
+
247
+ # overwrite setup_overwrite with setup_options
248
+ for key, value in setup_options.items():
249
+ setup_overwrite.update({key: value})
250
+
251
+ # Overight options with the config file
252
+ for key, value in setup_overwrite.items():
253
+ if hasattr(model.setup, key):
254
+ setattr(model.setup, key, value)
255
+
256
+ # print(setup_overwrite)
257
+ # print(model.setup)
258
+ # standardize new user options
259
+ standardize_model_date_pattern("prcp_date_pattern", model.setup.prcp_date_pattern)
260
+ standardize_model_date_pattern("pet_date_pattern", model.setup.pet_date_pattern)
261
+ standardize_model_date_pattern("snow_date_pattern", model.setup.snow_date_pattern)
262
+ standardize_model_date_pattern("temp_date_pattern", model.setup.temp_date_pattern)
263
+ standardize_model_data_directories("prcp_directories", model.setup.prcp_directories)
264
+ standardize_model_data_directories("pet_directories", model.setup.pet_directories)
265
+ standardize_model_data_directories("temp_directories", model.setup.temp_directories)
266
+ standardize_model_data_directories("snow_directories", model.setup.snow_directories)
267
+ standardize_model_continuous_pet("continuous_pet", model.setup.continuous_pet)
268
+
269
+ # here we must manage etpc... => must be moved into read_data
270
+ # Get ready with Continuous ETP
271
+ path_to_smashbox = os.path.join(
272
+ os.path.expandvars("$HOME"), ".smashbox", "simlink_pet"
273
+ )
274
+
275
+ if not os.path.exists(path_to_smashbox):
276
+ os.makedirs(path_to_smashbox)
277
+
278
+ path_to_symlink_etp = None
279
+
280
+ if "continuous_pet" in setup_overwrite:
281
+
282
+ if "pet_directories" in setup_overwrite:
283
+ dir_etp = setup_overwrite["pet_directories"]
284
+ elif "pet_directory" in setup_overwrite:
285
+ dir_etp = {1: setup_overwrite["pet_directory"]}
286
+ else:
287
+ dir_etp = {}
288
+
289
+ continuous_pet = setup_overwrite["continuous_pet"]
290
+ list_dir = os.listdir(path_to_smashbox)
291
+ path_to_symlink_etp = os.path.join(
292
+ path_to_smashbox,
293
+ f"symlinked_etpc_{len(list_dir)+1}_{int(random.random()*1000000)}",
294
+ )
295
+ print("</> Path to symlinked pet is set to ", path_to_symlink_etp)
296
+ else:
297
+ continuous_pet = {}
298
+
299
+ for key, value in continuous_pet.items():
300
+ if value:
301
+ prepare_etp_c(
302
+ path_to_symlink_etp=path_to_symlink_etp,
303
+ start_time=model.setup.start_time,
304
+ end_time=model.setup.end_time,
305
+ pet_date_pattern=model.setup.pet_date_pattern,
306
+ fmt=".tif",
307
+ dir_etp=dir_etp[key],
308
+ )
309
+ dir_etp[key] = path_to_symlink_etp
310
+ sending_options.update({"pet_directories": dir_etp})
311
+ break # only one source of continuous PET
312
+
313
+ # print(model.setup)
314
+ # read atmos data with updated func
315
+ print("</> Reading atmospheric data ...")
316
+ read_data.read_atmos_data(model, setup_options=sending_options)
317
+
318
+ # clean path to symplink pet files
319
+ if path_to_symlink_etp is not None:
320
+ if os.path.exists(path_to_symlink_etp):
321
+ print("</> Removing path to symplink pet ", path_to_symlink_etp)
322
+ shutil.rmtree(path_to_symlink_etp)
323
+
324
+ return model
325
+
326
+
327
+ def prepare_etp_c(
328
+ path_to_symlink_etp="symlinked_etp_c",
329
+ start_time=None,
330
+ end_time=None,
331
+ pet_date_pattern="%Y%m%d",
332
+ fmt=".tif",
333
+ dir_etp="",
334
+ ):
335
+
336
+ if (start_time is None) or (end_time is None):
337
+ print("start_time or end_time are empty... ")
338
+ return None
339
+
340
+ if dir_etp == "":
341
+ print("dir_etp empty...")
342
+ return None
343
+
344
+ date_range = pd.date_range(
345
+ start=datetime.datetime.fromisoformat(start_time),
346
+ end=datetime.datetime.fromisoformat(end_time),
347
+ freq=datetime.timedelta(days=1),
348
+ )
349
+
350
+ print("</> Prepare continuous PET ...")
351
+
352
+ if os.path.exists(path_to_symlink_etp):
353
+ shutil.rmtree(path_to_symlink_etp)
354
+
355
+ os.makedirs(path_to_symlink_etp)
356
+
357
+ warn_list = []
358
+ for date in date_range:
359
+
360
+ date_etp_c_to_read = (date - datetime.timedelta(days=1)).strftime(
361
+ pet_date_pattern
362
+ )
363
+ date_etp_c_j0 = (date).strftime("%m%d")
364
+ date_etp_c_j1 = (date + datetime.timedelta(days=1)).strftime("%m%d")
365
+
366
+ file_etp_c = glob.glob(f"{dir_etp}/**/*{date_etp_c_to_read}*.tif", recursive=True)
367
+
368
+ if len(file_etp_c) > 0:
369
+ file_etp_c = file_etp_c[0]
370
+
371
+ if os.path.exists(file_etp_c):
372
+ mylink = os.path.join(path_to_symlink_etp, f"ETPjc_{date_etp_c_j0}{fmt}")
373
+
374
+ if os.path.exists(mylink):
375
+ os.remove(mylink)
376
+
377
+ os.symlink(file_etp_c, mylink)
378
+
379
+ mylink = os.path.join(path_to_symlink_etp, f"ETPjc_{date_etp_c_j1}{fmt}")
380
+
381
+ if os.path.exists(mylink):
382
+ os.remove(mylink)
383
+
384
+ os.symlink(file_etp_c, mylink)
385
+
386
+ warn_list.append(
387
+ "Linked "
388
+ + f"ETPjc_{date_etp_c_j0}{fmt} and ETPjc_{date_etp_c_j1}{fmt} "
389
+ + f"to file {os.path.basename(file_etp_c)}"
390
+ )
391
+
392
+ if len(warn_list) > 1:
393
+ print(f"</> {warn_list[0]}, ..., {warn_list[-1]}")
394
+ elif len(warn_list) > 0:
395
+ print(f"</> {warn_list[0]}")
@@ -0,0 +1 @@
1
+