windborne 1.2.7__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: windborne
3
- Version: 1.2.7
3
+ Version: 1.3.0
4
4
  Summary: A Python library for interacting with WindBorne Data and Forecasts API
5
5
  Author-email: WindBorne Systems <data@windbornesystems.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "windborne"
7
- version = "1.2.7"
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
- get_historical_initialization_times,
26
- get_forecast_hours,
27
- get_generation_times,
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
- "get_historical_initialization_times",
69
- "get_forecast_hours",
70
- "get_generation_times",
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
- "get_temperature_2m",
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
  ]
@@ -4,6 +4,8 @@ import requests
4
4
  import re
5
5
  import os
6
6
 
7
+ API_BASE_URL = "https://api.windbornesystems.com"
8
+
7
9
  def is_valid_uuid_v4(client_id):
8
10
  return re.fullmatch(r"[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}", client_id) is not None
9
11
 
@@ -48,14 +50,6 @@ def verify_api_credentials(client_id, api_key):
48
50
  "For instructions, refer to https://windbornesystems.com/docs/api/cli#introduction or https://windbornesystems.com/docs/api/pip_data#introduction."
49
51
  )
50
52
 
51
- # Validate WB_CLIENT_ID format
52
- if is_valid_uuid_v4(client_id):
53
- raise NotImplementedError(
54
- "Personal API tokens are not yet supported. "
55
- "You will need to get a globally-authorizing API key. "
56
- "For questions, email data@windbornesystems.com."
57
- )
58
-
59
53
  if not (is_valid_uuid_v4(client_id) or is_valid_client_id_format(client_id)):
60
54
  raise ValueError(
61
55
  f"Your Client ID is misformatted: {client_id}. "
@@ -110,20 +104,10 @@ def make_api_request(url, params=None, as_json=True, retry_counter=0):
110
104
 
111
105
  client_id, api_key = get_verified_api_credentials()
112
106
 
113
- if is_valid_uuid_v4(client_id):
114
- token_id = client_id
115
- client_id = 'api_token'
116
-
117
- signed_token = jwt.encode({
118
- 'client_id': client_id,
119
- 'iat': int(time.time()),
120
- 'token_id': token_id
121
- }, api_key, algorithm='HS256')
122
- else:
123
- signed_token = jwt.encode({
124
- 'client_id': client_id,
125
- 'iat': int(time.time()),
126
- }, api_key, algorithm='HS256')
107
+ signed_token = jwt.encode({
108
+ 'client_id': client_id,
109
+ 'iat': int(time.time()),
110
+ }, api_key, algorithm='HS256')
127
111
 
128
112
  try:
129
113
  if params:
@@ -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
- get_historical_initialization_times,
23
- get_forecast_hours,
24
- get_generation_times,
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', '--external-model', help='External model (eg gfs, ifs, hrrr, aifs)')
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', '--external-model', help='External model (eg gfs, ifs, hrrr, aifs)')
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
- # Historical Initialization Times Command
256
- hist_initialization_times_parser = subparsers.add_parser('hist_init_times', help='Get available historical initialization times')
257
- hist_initialization_times_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
258
- hist_initialization_times_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
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
- # Forecast Hours Command
261
- forecast_hours_parser = subparsers.add_parser('forecast_hours', help='Get available forecast hours for WeatherMesh')
262
- forecast_hours_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
263
- forecast_hours_parser.add_argument('-e', '--ens-member', help='Ensemble member (eg 1 or mean)')
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
- # Output Creation Times Command
266
- output_creation_times_parser = subparsers.add_parser('generation_times', help='Get the time at which each forecast hour output file was generated')
267
- output_creation_times_parser.add_argument('-i', '--intracycle', action='store_true', help='Use the intracycle forecast')
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, intracycle=args.intracycle)
437
+ get_initialization_times(print_response=True, ensemble_member=args.ens_member, model=args.model)
468
438
 
469
- elif args.command == 'hist_init_times':
470
- get_historical_initialization_times(print_response=True, ensemble_member=args.ens_member, intracycle=args.intracycle)
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 == 'forecast_hours':
473
- get_forecast_hours(print_response=True, ensemble_member=args.ens_member, intracycle=args.intracycle)
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 == 'generation_times':
476
- get_generation_times(print_response=True, ensemble_member=args.ens_member, intracycle=args.intracycle)
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, intracycle=args.intracycle)
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
- get_gridded_forecast(variable=args.args[0], initialization_time=args.args[1], forecast_hour=args.args[2], output_file=args.args[3], ensemble_member=args.ens_member, intracycle=args.intracycle)
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