windborne 1.2.8__tar.gz → 1.3.0__tar.gz
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.
- {windborne-1.2.8 → windborne-1.3.0}/PKG-INFO +1 -1
- {windborne-1.2.8 → windborne-1.3.0}/pyproject.toml +1 -1
- {windborne-1.2.8 → windborne-1.3.0}/windborne/__init__.py +17 -34
- {windborne-1.2.8 → windborne-1.3.0}/windborne/api_request.py +2 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne/cli.py +112 -155
- {windborne-1.2.8 → windborne-1.3.0}/windborne/data_api.py +46 -32
- windborne-1.3.0/windborne/forecasts_api.py +676 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne.egg-info/PKG-INFO +1 -1
- windborne-1.2.8/windborne/forecasts_api.py +0 -484
- {windborne-1.2.8 → windborne-1.3.0}/README.md +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/setup.cfg +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne/observation_formatting.py +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne/track_formatting.py +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne/utils.py +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne.egg-info/SOURCES.txt +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne.egg-info/dependency_links.txt +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne.egg-info/entry_points.txt +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne.egg-info/requires.txt +0 -0
- {windborne-1.2.8 → windborne-1.3.0}/windborne.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "windborne"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.3.0"
|
|
8
8
|
description = "A Python library for interacting with WindBorne Data and Forecasts API"
|
|
9
9
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
10
|
authors = [
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Import key functions and classes for easier access when users import the package
|
|
2
2
|
|
|
3
|
+
# Import API request helpers
|
|
4
|
+
from .api_request import API_BASE_URL, make_api_request
|
|
5
|
+
|
|
3
6
|
# Import Data API functions
|
|
4
7
|
from .data_api import (
|
|
5
8
|
get_observations_page,
|
|
@@ -21,24 +24,14 @@ from .data_api import (
|
|
|
21
24
|
# Import Forecasts API functions
|
|
22
25
|
from .forecasts_api import (
|
|
23
26
|
get_point_forecasts,
|
|
27
|
+
get_point_forecasts_interpolated,
|
|
24
28
|
get_initialization_times,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
get_archived_initialization_times,
|
|
30
|
+
get_run_information,
|
|
31
|
+
get_variables,
|
|
28
32
|
|
|
29
33
|
get_gridded_forecast,
|
|
30
34
|
get_full_gridded_forecast,
|
|
31
|
-
get_temperature_2m,
|
|
32
|
-
get_dewpoint_2m,
|
|
33
|
-
get_wind_u_10m, get_wind_v_10m,
|
|
34
|
-
get_pressure_msl,
|
|
35
|
-
get_500hpa_wind_u, get_500hpa_wind_v,
|
|
36
|
-
get_500hpa_geopotential, get_850hpa_geopotential,
|
|
37
|
-
get_500hpa_temperature, get_850hpa_temperature,
|
|
38
|
-
|
|
39
|
-
get_historical_temperature_2m,
|
|
40
|
-
get_historical_500hpa_geopotential,
|
|
41
|
-
get_historical_500hpa_wind_u, get_historical_500hpa_wind_v,
|
|
42
35
|
|
|
43
36
|
get_tropical_cyclones,
|
|
44
37
|
|
|
@@ -64,31 +57,21 @@ __all__ = [
|
|
|
64
57
|
"get_flight_path",
|
|
65
58
|
|
|
66
59
|
"get_point_forecasts",
|
|
60
|
+
"get_point_forecasts_interpolated",
|
|
67
61
|
"get_initialization_times",
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
62
|
+
"get_archived_initialization_times",
|
|
63
|
+
"get_run_information",
|
|
64
|
+
"get_variables",
|
|
71
65
|
|
|
72
66
|
"get_gridded_forecast",
|
|
73
67
|
"get_full_gridded_forecast",
|
|
74
|
-
|
|
75
|
-
"get_dewpoint_2m",
|
|
76
|
-
"get_wind_u_10m",
|
|
77
|
-
"get_wind_v_10m",
|
|
78
|
-
"get_500hpa_wind_u",
|
|
79
|
-
"get_500hpa_wind_v",
|
|
80
|
-
"get_pressure_msl",
|
|
81
|
-
"get_500hpa_geopotential",
|
|
82
|
-
"get_850hpa_geopotential",
|
|
83
|
-
"get_500hpa_temperature",
|
|
84
|
-
"get_850hpa_temperature",
|
|
85
|
-
|
|
86
|
-
"get_historical_temperature_2m",
|
|
87
|
-
"get_historical_500hpa_geopotential",
|
|
88
|
-
"get_historical_500hpa_wind_u",
|
|
89
|
-
"get_historical_500hpa_wind_v",
|
|
68
|
+
|
|
90
69
|
"get_tropical_cyclones",
|
|
91
70
|
|
|
92
71
|
"get_population_weighted_hdd",
|
|
93
|
-
"get_population_weighted_cdd"
|
|
72
|
+
"get_population_weighted_cdd",
|
|
73
|
+
|
|
74
|
+
# API helpers
|
|
75
|
+
"API_BASE_URL",
|
|
76
|
+
"make_api_request",
|
|
94
77
|
]
|
|
@@ -18,11 +18,11 @@ from . import (
|
|
|
18
18
|
get_flight_path,
|
|
19
19
|
|
|
20
20
|
get_point_forecasts,
|
|
21
|
+
get_point_forecasts_interpolated,
|
|
21
22
|
get_initialization_times,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
get_full_gridded_forecast,
|
|
23
|
+
get_archived_initialization_times,
|
|
24
|
+
get_run_information,
|
|
25
|
+
get_variables,
|
|
26
26
|
get_gridded_forecast,
|
|
27
27
|
get_tropical_cyclones,
|
|
28
28
|
get_population_weighted_hdd,
|
|
@@ -136,6 +136,7 @@ def main():
|
|
|
136
136
|
####################################################################################################################
|
|
137
137
|
# FORECASTS API FUNCTIONS
|
|
138
138
|
####################################################################################################################
|
|
139
|
+
|
|
139
140
|
# Points Forecast Command
|
|
140
141
|
# We have quite a few quite a few optional query parameters here
|
|
141
142
|
# so we set coordinates and output_file to required instead of
|
|
@@ -147,79 +148,27 @@ def main():
|
|
|
147
148
|
points_parser.add_argument('-mh','--min-hour', type=int, help='Minimum forecast hour')
|
|
148
149
|
points_parser.add_argument('-xh','--max-hour', type=int, help='Maximum forecast hour')
|
|
149
150
|
points_parser.add_argument('-i', '--init-time', help='Initialization time')
|
|
151
|
+
points_parser.add_argument('--model', default='wm', help='Forecast model (e.g., wm, wm4)')
|
|
150
152
|
points_parser.add_argument('output_file', nargs='?', help='Output file')
|
|
151
153
|
|
|
154
|
+
# Interpolated Points Forecast Command
|
|
155
|
+
points_interpolated_parser = subparsers.add_parser('points_interpolated', help='Get interpolated forecast at given point(s)')
|
|
156
|
+
points_interpolated_parser.add_argument('coordinates', help='Coordinate pairs in format "latitudeA,longitudeA; latitudeB,longitudeB"')
|
|
157
|
+
points_interpolated_parser.add_argument('-mt','--min-time', help='Minimum forecast time')
|
|
158
|
+
points_interpolated_parser.add_argument('-xt','--max-time', help='Maximum forecast time')
|
|
159
|
+
points_interpolated_parser.add_argument('-mh','--min-hour', type=int, help='Minimum forecast hour')
|
|
160
|
+
points_interpolated_parser.add_argument('-xh','--max-hour', type=int, help='Maximum forecast hour')
|
|
161
|
+
points_interpolated_parser.add_argument('-i', '--init-time', help='Initialization time')
|
|
162
|
+
points_interpolated_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
163
|
+
points_interpolated_parser.add_argument('--model', default='wm', help='Forecast model (e.g., wm, wm4)')
|
|
164
|
+
points_interpolated_parser.add_argument('output_file', nargs='?', help='Output file (.csv or .json)')
|
|
165
|
+
|
|
152
166
|
# GRIDDED FORECASTS
|
|
153
167
|
####################################################################################################################
|
|
154
168
|
gridded_parser = subparsers.add_parser('gridded', help='Get gridded forecast for a variable')
|
|
155
169
|
gridded_parser.add_argument('args', nargs='*', help='variable time output_file')
|
|
156
|
-
gridded_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
|
157
170
|
gridded_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
158
|
-
|
|
159
|
-
hist_gridded_parser = subparsers.add_parser('hist_gridded', help='Get historical gridded forecast for a variable')
|
|
160
|
-
hist_gridded_parser.add_argument('args', nargs='*', help='variable initialization_time forecast_hour output_file')
|
|
161
|
-
hist_gridded_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
|
162
|
-
hist_gridded_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
163
|
-
|
|
164
|
-
full_gridded_parser = subparsers.add_parser('grid_full', help='Get full gridded forecast')
|
|
165
|
-
full_gridded_parser.add_argument('args', nargs='*', help='time output_file')
|
|
166
|
-
|
|
167
|
-
# Define variables for gridded forecasts - the command name will be derived from the variable name
|
|
168
|
-
# except for special cases like temperature_2m -> temp_2m
|
|
169
|
-
gridded_variables = [
|
|
170
|
-
'temperature_2m',
|
|
171
|
-
'dewpoint_2m',
|
|
172
|
-
'wind_u_10m',
|
|
173
|
-
'wind_v_10m',
|
|
174
|
-
'wind_u_100m',
|
|
175
|
-
'wind_v_100m',
|
|
176
|
-
'pressure_msl',
|
|
177
|
-
'500/temperature',
|
|
178
|
-
'500/wind_u',
|
|
179
|
-
'500/wind_v',
|
|
180
|
-
'500/geopotential',
|
|
181
|
-
'850/temperature',
|
|
182
|
-
'850/geopotential'
|
|
183
|
-
]
|
|
184
|
-
|
|
185
|
-
gridded_human_names = {
|
|
186
|
-
'temperature_2m': '2m temperature',
|
|
187
|
-
'dewpoint_2m': '2m dewpoint',
|
|
188
|
-
'wind_u_10m': '10m u-component of wind',
|
|
189
|
-
'wind_v_10m': '10m v-component of wind',
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
gridded_forecast_mapping = {}
|
|
193
|
-
for var in gridded_variables:
|
|
194
|
-
cmd_name = var.replace('/', 'hpa_')
|
|
195
|
-
if var == 'temperature_2m':
|
|
196
|
-
cmd_name = 'temp_2m'
|
|
197
|
-
|
|
198
|
-
human_name = gridded_human_names.get(var, var)
|
|
199
|
-
if '/' in var:
|
|
200
|
-
level, real_var = var.split('/')
|
|
201
|
-
human_name = f"{level}hPa {real_var}"
|
|
202
|
-
|
|
203
|
-
gridded_forecast_mapping[cmd_name] = {
|
|
204
|
-
'variable': var,
|
|
205
|
-
'human_name': human_name
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
# Dynamically create parsers for gridded forecasts
|
|
209
|
-
for cmd_name, config in gridded_forecast_mapping.items():
|
|
210
|
-
grid_help = f"Get gridded output of global {config['human_name']} forecasts"
|
|
211
|
-
grid_parser = subparsers.add_parser(f'grid_{cmd_name}', help=grid_help)
|
|
212
|
-
grid_parser.add_argument('args', nargs='*', help='time output_file')
|
|
213
|
-
grid_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
|
214
|
-
grid_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
215
|
-
grid_parser.set_defaults(variable=config['variable'])
|
|
216
|
-
|
|
217
|
-
hist_help = f"Get historical output of global {config['human_name']} forecasts"
|
|
218
|
-
hist_parser = subparsers.add_parser(f'hist_{cmd_name}', help=hist_help)
|
|
219
|
-
hist_parser.add_argument('args', nargs='*', help='initialization_time forecast_hour output_file')
|
|
220
|
-
hist_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
|
221
|
-
hist_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
222
|
-
hist_parser.set_defaults(variable=config['variable'])
|
|
171
|
+
gridded_parser.add_argument('-m', '--model', default='wm', help='Forecast model (e.g., wm, wm4, wm4-intra, ecmwf-det)')
|
|
223
172
|
|
|
224
173
|
# OTHER
|
|
225
174
|
# TCS
|
|
@@ -228,44 +177,44 @@ def main():
|
|
|
228
177
|
# Tropical Cyclones Command
|
|
229
178
|
tropical_cyclones_parser = subparsers.add_parser('tropical_cyclones', help='Get tropical cyclone forecasts')
|
|
230
179
|
tropical_cyclones_parser.add_argument('-b', '--basin', help='Optional: filter tropical cyclones on basin[ NA, EP, WP, NI, SI, AU, SP]')
|
|
180
|
+
tropical_cyclones_parser.add_argument('-m', '--model', default='wm', help='Forecast model (e.g., wm, wm4, wm4-intra, ecmwf-det)')
|
|
231
181
|
tropical_cyclones_parser.add_argument('args', nargs='*',
|
|
232
182
|
help='[optional: initialization time (YYYYMMDDHH, YYYY-MM-DDTHH, or YYYY-MM-DDTHH:mm:ss)] output_file')
|
|
233
183
|
|
|
234
184
|
# Population Weighted HDD Command
|
|
235
185
|
hdd_parser = subparsers.add_parser('hdd', help='Get population weighted heating degree days (HDD) forecasts')
|
|
236
186
|
hdd_parser.add_argument('initialization_time', help='Initialization time (YYYYMMDDHH, YYYY-MM-DDTHH, or YYYY-MM-DDTHH:mm:ss)')
|
|
237
|
-
hdd_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
|
238
187
|
hdd_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
239
|
-
hdd_parser.add_argument('-m', '--
|
|
188
|
+
hdd_parser.add_argument('-m', '--model', default='wm', help='Forecast model (e.g., wm, wm4, wm4-intra, ecmwf-det)')
|
|
240
189
|
hdd_parser.add_argument('-o', '--output', help='Output file (supports .csv and .json formats)')
|
|
241
190
|
|
|
242
191
|
# Population Weighted CDD Command
|
|
243
192
|
cdd_parser = subparsers.add_parser('cdd', help='Get population weighted cooling degree days (CDD) forecasts')
|
|
244
193
|
cdd_parser.add_argument('initialization_time', help='Initialization time (YYYYMMDDHH, YYYY-MM-DDTHH, or YYYY-MM-DDTHH:mm:ss)')
|
|
245
|
-
cdd_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
|
246
194
|
cdd_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
247
|
-
cdd_parser.add_argument('-m', '--
|
|
195
|
+
cdd_parser.add_argument('-m', '--model', default='wm', help='Forecast model (e.g., wm, wm4, wm4-intra, ecmwf-det)')
|
|
248
196
|
cdd_parser.add_argument('-o', '--output', help='Output file (supports .csv and .json formats)')
|
|
249
197
|
|
|
250
198
|
# Initialization Times Command
|
|
251
199
|
initialization_times_parser = subparsers.add_parser('init_times', help='Get available initialization times for point forecasts')
|
|
252
|
-
initialization_times_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
|
|
253
200
|
initialization_times_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
201
|
+
initialization_times_parser.add_argument('-m', '--model', default='wm', help='Forecast model (e.g., wm, wm4, wm4-intra, ecmwf-det)')
|
|
254
202
|
|
|
255
|
-
#
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
203
|
+
# Archived Initialization Times Command
|
|
204
|
+
archived_initialization_times_parser = subparsers.add_parser('archived_init_times', help='Get available archived initialization times')
|
|
205
|
+
archived_initialization_times_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
206
|
+
archived_initialization_times_parser.add_argument('-m', '--model', default='wm', help='Forecast model (e.g., wm, wm4, wm4-intra, ecmwf-det)')
|
|
207
|
+
archived_initialization_times_parser.add_argument('-p', '--page-end', help='End of page window (ISO 8601). Lists times back 7 days.')
|
|
259
208
|
|
|
260
|
-
#
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
209
|
+
# Run Information Command
|
|
210
|
+
run_information_parser = subparsers.add_parser('run_information', help='Get run information for a model run')
|
|
211
|
+
run_information_parser.add_argument('initialization_time', help='Initialization time (ISO 8601)')
|
|
212
|
+
run_information_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
213
|
+
run_information_parser.add_argument('-m', '--model', default='wm', help='Forecast model (e.g., wm, wm4, wm4-intra, ecmwf-det)')
|
|
264
214
|
|
|
265
|
-
#
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
output_creation_times_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
|
|
215
|
+
# Variables Command
|
|
216
|
+
variables_parser = subparsers.add_parser('variables', help='Get available variables for a model')
|
|
217
|
+
variables_parser.add_argument('-m', '--model', default='wm', help='Forecast model (e.g., wm, wm4, wm4-intra, ecmwf-det)')
|
|
269
218
|
|
|
270
219
|
args = parser.parse_args()
|
|
271
220
|
|
|
@@ -460,78 +409,88 @@ def main():
|
|
|
460
409
|
max_forecast_hour=max_forecast_hour,
|
|
461
410
|
initialization_time=initialization_time,
|
|
462
411
|
output_file=args.output_file,
|
|
412
|
+
model=args.model,
|
|
413
|
+
print_response=(not args.output_file)
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
elif args.command == 'points_interpolated':
|
|
417
|
+
min_forecast_time = args.min_time if args.min_time else None
|
|
418
|
+
max_forecast_time = args.max_time if args.max_time else None
|
|
419
|
+
min_forecast_hour = args.min_hour if args.min_hour else None
|
|
420
|
+
max_forecast_hour = args.max_hour if args.max_hour else None
|
|
421
|
+
initialization_time = args.init_time if args.init_time else None
|
|
422
|
+
|
|
423
|
+
get_point_forecasts_interpolated(
|
|
424
|
+
coordinates=args.coordinates,
|
|
425
|
+
min_forecast_time=min_forecast_time,
|
|
426
|
+
max_forecast_time=max_forecast_time,
|
|
427
|
+
min_forecast_hour=min_forecast_hour,
|
|
428
|
+
max_forecast_hour=max_forecast_hour,
|
|
429
|
+
initialization_time=initialization_time,
|
|
430
|
+
ensemble_member=getattr(args, 'ens_member', None),
|
|
431
|
+
output_file=args.output_file,
|
|
432
|
+
model=args.model,
|
|
463
433
|
print_response=(not args.output_file)
|
|
464
434
|
)
|
|
465
435
|
|
|
466
436
|
elif args.command == 'init_times':
|
|
467
|
-
get_initialization_times(print_response=True, ensemble_member=args.ens_member,
|
|
437
|
+
get_initialization_times(print_response=True, ensemble_member=args.ens_member, model=args.model)
|
|
468
438
|
|
|
469
|
-
elif args.command == '
|
|
470
|
-
|
|
439
|
+
elif args.command == 'archived_init_times':
|
|
440
|
+
get_archived_initialization_times(print_response=True, ensemble_member=args.ens_member, model=args.model, page_end=getattr(args, 'page_end', None))
|
|
471
441
|
|
|
472
|
-
elif args.command == '
|
|
473
|
-
|
|
442
|
+
elif args.command == 'run_information':
|
|
443
|
+
get_run_information(initialization_time=args.initialization_time, ensemble_member=getattr(args, 'ens_member', None), model=args.model, print_response=True)
|
|
474
444
|
|
|
475
|
-
elif args.command == '
|
|
476
|
-
|
|
445
|
+
elif args.command == 'variables':
|
|
446
|
+
get_variables(print_response=True, model=args.model)
|
|
477
447
|
|
|
478
448
|
elif args.command == 'gridded':
|
|
479
449
|
if len(args.args) in [0,1,2]:
|
|
480
|
-
print(f"To get the gridded forecast for a variable you need to provide the variable, time, and an output file.")
|
|
481
|
-
print(f"\nUsage: windborne gridded variable time output_file")
|
|
450
|
+
print(f"To get the gridded forecast for a variable you need to provide the variable, time (or initialization time and forecast hour), and an output file.")
|
|
451
|
+
print(f"\nUsage: windborne gridded variable time output_file or")
|
|
452
|
+
print(f"\n windborne gridded variable initialization_time forecast_hour output_file")
|
|
453
|
+
print(f"\n windborne gridded variable level time output_file")
|
|
454
|
+
print(f"\n windborne gridded variable level initialization_time forecast_hour output_file")
|
|
482
455
|
elif len(args.args) == 3:
|
|
483
|
-
get_gridded_forecast(variable=args.args[0], time=args.args[1], output_file=args.args[2], ensemble_member=args.ens_member,
|
|
484
|
-
else:
|
|
485
|
-
print("Too many arguments")
|
|
486
|
-
|
|
487
|
-
elif args.command == 'grid_full':
|
|
488
|
-
if len(args.args) in [0,1]:
|
|
489
|
-
print("To get the full gridded forecast you need to provide the time for which to get the forecast and an output file.")
|
|
490
|
-
print("\nUsage: windborne grid_full time output_file")
|
|
491
|
-
elif len(args.args) == 2:
|
|
492
|
-
get_full_gridded_forecast(time=args.args[0], output_file=args.args[1])
|
|
493
|
-
else:
|
|
494
|
-
print("Too many arguments")
|
|
495
|
-
print("\nUsage: windborne grid_full time output_file")
|
|
496
|
-
|
|
497
|
-
elif args.command == 'hist_gridded':
|
|
498
|
-
if len(args.args) in [0,1,2,3]:
|
|
499
|
-
print(f"To get the historical gridded forecast for a variable you need to provide the variable, initialization time, forecast hour, and an output file.")
|
|
500
|
-
print(f"\nUsage: windborne hist_gridded variable initialization_time forecast_hour output_file")
|
|
456
|
+
get_gridded_forecast(variable=args.args[0], time=args.args[1], output_file=args.args[2], ensemble_member=args.ens_member, model=args.model)
|
|
501
457
|
elif len(args.args) == 4:
|
|
502
|
-
|
|
458
|
+
# Support both historical form: variable initialization_time forecast_hour output
|
|
459
|
+
# and alternate "variable level time output" form by detecting numeric level
|
|
460
|
+
a0, a1, a2, a3 = args.args
|
|
461
|
+
is_level = False
|
|
462
|
+
try:
|
|
463
|
+
# Simple numeric check; levels are integers like 500, 850, 1000
|
|
464
|
+
int(a1)
|
|
465
|
+
is_level = True
|
|
466
|
+
except Exception:
|
|
467
|
+
is_level = False
|
|
468
|
+
|
|
469
|
+
# Basic heuristic to detect time-like strings for a2
|
|
470
|
+
def looks_like_time(s: str) -> bool:
|
|
471
|
+
return (
|
|
472
|
+
(len(s) == 10 and s.isdigit()) or # YYYYMMDDHH
|
|
473
|
+
('T' in s and '-' in s) # ISO-like
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
if is_level and looks_like_time(a2):
|
|
477
|
+
# Map to level/variable with time
|
|
478
|
+
get_gridded_forecast(variable=f"{a1}/{a0}", time=a2, output_file=a3, ensemble_member=args.ens_member, model=args.model)
|
|
479
|
+
else:
|
|
480
|
+
get_gridded_forecast(variable=a0, initialization_time=a1, forecast_hour=a2, output_file=a3, ensemble_member=args.ens_member, model=args.model)
|
|
481
|
+
elif len(args.args) == 5:
|
|
482
|
+
# Support historical variable level syntax:
|
|
483
|
+
# windborne gridded variable level initialization_time forecast_hour output_file
|
|
484
|
+
a0, a1, a2, a3, a4 = args.args
|
|
485
|
+
try:
|
|
486
|
+
int(a1)
|
|
487
|
+
# Treat a1 as level
|
|
488
|
+
get_gridded_forecast(variable=f"{a1}/{a0}", initialization_time=a2, forecast_hour=a3, output_file=a4, ensemble_member=args.ens_member, model=args.model)
|
|
489
|
+
except Exception:
|
|
490
|
+
# Fallback: treat like variable initialization_time forecast_hour output_file (ignore a1)
|
|
491
|
+
get_gridded_forecast(variable=a0, initialization_time=a2, forecast_hour=a3, output_file=a4, ensemble_member=args.ens_member, model=args.model)
|
|
503
492
|
else:
|
|
504
493
|
print("Too many arguments")
|
|
505
|
-
print(f"\nUsage: windborne hist_gridded variable initialization_time forecast_hour output_file")
|
|
506
|
-
|
|
507
|
-
# Handle all gridded forecast commands
|
|
508
|
-
elif args.command.startswith('grid_'):
|
|
509
|
-
cmd_name = args.command[5:] # Remove 'grid_' prefix
|
|
510
|
-
if cmd_name in gridded_forecast_mapping:
|
|
511
|
-
if len(args.args) in [0,1]:
|
|
512
|
-
print(f"To get {gridded_forecast_mapping[cmd_name]['human_name']} you need to provide the time for which to get the forecast and an output file.")
|
|
513
|
-
print(f"\nUsage: windborne {args.command} time output_file")
|
|
514
|
-
elif len(args.args) == 2:
|
|
515
|
-
get_gridded_forecast(variable=args.variable, time=args.args[0], output_file=args.args[1], ensemble_member=args.ens_member, intracycle=args.intracycle)
|
|
516
|
-
else:
|
|
517
|
-
print("Too many arguments")
|
|
518
|
-
print(f"\nUsage: windborne {args.command} time output_file")
|
|
519
|
-
|
|
520
|
-
# Handle all historical forecast commands
|
|
521
|
-
elif args.command.startswith('hist_'):
|
|
522
|
-
cmd_name = args.command[5:] # Remove 'hist_' prefix
|
|
523
|
-
if cmd_name in gridded_forecast_mapping:
|
|
524
|
-
if len(args.args) in [0,1,2]:
|
|
525
|
-
print(f"To get {gridded_forecast_mapping[cmd_name]['human_name']} you need to provide\n"
|
|
526
|
-
" - initialization time of the forecast\n"
|
|
527
|
-
" - How many hours after the initialization time the forecast is valid at\n"
|
|
528
|
-
" - An output file to save the data")
|
|
529
|
-
print(f"\nUsage: windborne {args.command} initialization_time forecast_hour output_file")
|
|
530
|
-
elif len(args.args) == 3:
|
|
531
|
-
get_gridded_forecast(variable=args.variable, initialization_time=args.args[0], forecast_hour=args.args[1], output_file=args.args[2], ensemble_member=args.ens_member, intracycle=args.intracycle)
|
|
532
|
-
else:
|
|
533
|
-
print("Too many arguments")
|
|
534
|
-
print(f"\nUsage: windborne {args.command} initialization_time forecast_hour output_file")
|
|
535
494
|
|
|
536
495
|
elif args.command == 'tropical_cyclones':
|
|
537
496
|
# Parse cyclones arguments
|
|
@@ -540,18 +499,18 @@ def main():
|
|
|
540
499
|
basin_name = f"{args.basin} basin"
|
|
541
500
|
|
|
542
501
|
if len(args.args) == 0:
|
|
543
|
-
get_tropical_cyclones(basin=args.basin, print_response=True)
|
|
502
|
+
get_tropical_cyclones(basin=args.basin, print_response=True, model=args.model)
|
|
544
503
|
return
|
|
545
504
|
elif len(args.args) == 1:
|
|
546
505
|
if '.' in args.args[0]:
|
|
547
506
|
# Save tcs with the latest available initialization time in filename
|
|
548
|
-
get_tropical_cyclones(basin=args.basin, output_file=args.args[0])
|
|
507
|
+
get_tropical_cyclones(basin=args.basin, output_file=args.args[0], model=args.model)
|
|
549
508
|
else:
|
|
550
509
|
# Display tcs for selected initialization time
|
|
551
|
-
get_tropical_cyclones(initialization_time=args.args[0], basin=args.basin, print_response=True)
|
|
510
|
+
get_tropical_cyclones(initialization_time=args.args[0], basin=args.basin, print_response=True, model=args.model)
|
|
552
511
|
elif len(args.args) == 2:
|
|
553
512
|
print(f"Saving tropical cyclones for initialization time {args.args[0]} and {basin_name}\n")
|
|
554
|
-
get_tropical_cyclones(initialization_time=args.args[0], basin=args.basin, output_file=args.args[1])
|
|
513
|
+
get_tropical_cyclones(initialization_time=args.args[0], basin=args.basin, output_file=args.args[1], model=args.model)
|
|
555
514
|
else:
|
|
556
515
|
print("Error: Too many arguments")
|
|
557
516
|
print("Usage: windborne tropical_cyclones [initialization_time] output_file")
|
|
@@ -559,22 +518,20 @@ def main():
|
|
|
559
518
|
elif args.command == 'hdd':
|
|
560
519
|
# Handle population weighted HDD
|
|
561
520
|
get_population_weighted_hdd(
|
|
562
|
-
initialization_time=args.initialization_time,
|
|
563
|
-
intracycle=args.intracycle,
|
|
521
|
+
initialization_time=args.initialization_time,
|
|
564
522
|
ens_member=args.ens_member,
|
|
565
|
-
external_model=args.external_model,
|
|
566
523
|
output_file=args.output,
|
|
524
|
+
model=args.model,
|
|
567
525
|
print_response=(not args.output)
|
|
568
526
|
)
|
|
569
527
|
|
|
570
528
|
elif args.command == 'cdd':
|
|
571
529
|
# Handle population weighted CDD
|
|
572
530
|
get_population_weighted_cdd(
|
|
573
|
-
initialization_time=args.initialization_time,
|
|
574
|
-
intracycle=args.intracycle,
|
|
531
|
+
initialization_time=args.initialization_time,
|
|
575
532
|
ens_member=args.ens_member,
|
|
576
|
-
external_model=args.external_model,
|
|
577
533
|
output_file=args.output,
|
|
534
|
+
model=args.model,
|
|
578
535
|
print_response=(not args.output)
|
|
579
536
|
)
|
|
580
537
|
|