tfv-get-tools 0.2.0__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 (62) hide show
  1. tfv_get_tools/__init__.py +4 -0
  2. tfv_get_tools/_standard_attrs.py +107 -0
  3. tfv_get_tools/atmos.py +167 -0
  4. tfv_get_tools/cli/_cli_base.py +173 -0
  5. tfv_get_tools/cli/atmos_cli.py +192 -0
  6. tfv_get_tools/cli/ocean_cli.py +204 -0
  7. tfv_get_tools/cli/tide_cli.py +118 -0
  8. tfv_get_tools/cli/wave_cli.py +183 -0
  9. tfv_get_tools/fvc/__init__.py +3 -0
  10. tfv_get_tools/fvc/_atmos.py +230 -0
  11. tfv_get_tools/fvc/_fvc.py +218 -0
  12. tfv_get_tools/fvc/_ocean.py +171 -0
  13. tfv_get_tools/fvc/_tide.py +195 -0
  14. tfv_get_tools/ocean.py +170 -0
  15. tfv_get_tools/providers/__init__.py +0 -0
  16. tfv_get_tools/providers/_custom_conversions.py +34 -0
  17. tfv_get_tools/providers/_downloader.py +566 -0
  18. tfv_get_tools/providers/_merger.py +520 -0
  19. tfv_get_tools/providers/_utilities.py +255 -0
  20. tfv_get_tools/providers/atmos/barra2.py +209 -0
  21. tfv_get_tools/providers/atmos/cfgs/barra2_c2.yaml +52 -0
  22. tfv_get_tools/providers/atmos/cfgs/barra2_r2.yaml +85 -0
  23. tfv_get_tools/providers/atmos/cfgs/barra2_re2.yaml +70 -0
  24. tfv_get_tools/providers/atmos/cfgs/cfsr.yaml +68 -0
  25. tfv_get_tools/providers/atmos/cfgs/era5.yaml +77 -0
  26. tfv_get_tools/providers/atmos/cfgs/era5_gcp.yaml +77 -0
  27. tfv_get_tools/providers/atmos/cfsr.py +207 -0
  28. tfv_get_tools/providers/atmos/era5.py +20 -0
  29. tfv_get_tools/providers/atmos/era5_gcp.py +20 -0
  30. tfv_get_tools/providers/ocean/cfgs/copernicus_blk.yaml +64 -0
  31. tfv_get_tools/providers/ocean/cfgs/copernicus_glo.yaml +67 -0
  32. tfv_get_tools/providers/ocean/cfgs/copernicus_nws.yaml +62 -0
  33. tfv_get_tools/providers/ocean/cfgs/hycom.yaml +73 -0
  34. tfv_get_tools/providers/ocean/copernicus_ocean.py +457 -0
  35. tfv_get_tools/providers/ocean/hycom.py +611 -0
  36. tfv_get_tools/providers/wave/cawcr.py +166 -0
  37. tfv_get_tools/providers/wave/cfgs/cawcr_aus_10m.yaml +39 -0
  38. tfv_get_tools/providers/wave/cfgs/cawcr_aus_4m.yaml +39 -0
  39. tfv_get_tools/providers/wave/cfgs/cawcr_glob_24m.yaml +39 -0
  40. tfv_get_tools/providers/wave/cfgs/cawcr_pac_10m.yaml +39 -0
  41. tfv_get_tools/providers/wave/cfgs/cawcr_pac_4m.yaml +39 -0
  42. tfv_get_tools/providers/wave/cfgs/copernicus_glo.yaml +56 -0
  43. tfv_get_tools/providers/wave/cfgs/copernicus_nws.yaml +51 -0
  44. tfv_get_tools/providers/wave/cfgs/era5.yaml +48 -0
  45. tfv_get_tools/providers/wave/cfgs/era5_gcp.yaml +48 -0
  46. tfv_get_tools/providers/wave/copernicus_wave.py +38 -0
  47. tfv_get_tools/providers/wave/era5.py +232 -0
  48. tfv_get_tools/providers/wave/era5_gcp.py +169 -0
  49. tfv_get_tools/tide/__init__.py +2 -0
  50. tfv_get_tools/tide/_nodestring.py +214 -0
  51. tfv_get_tools/tide/_tidal_base.py +568 -0
  52. tfv_get_tools/utilities/_tfv_bc.py +78 -0
  53. tfv_get_tools/utilities/horizontal_padding.py +89 -0
  54. tfv_get_tools/utilities/land_masking.py +93 -0
  55. tfv_get_tools/utilities/parsers.py +44 -0
  56. tfv_get_tools/utilities/warnings.py +38 -0
  57. tfv_get_tools/wave.py +179 -0
  58. tfv_get_tools-0.2.0.dist-info/METADATA +286 -0
  59. tfv_get_tools-0.2.0.dist-info/RECORD +62 -0
  60. tfv_get_tools-0.2.0.dist-info/WHEEL +5 -0
  61. tfv_get_tools-0.2.0.dist-info/entry_points.txt +5 -0
  62. tfv_get_tools-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,4 @@
1
+ from tfv_get_tools.ocean import DownloadOcean, MergeOcean
2
+ from tfv_get_tools.wave import DownloadWave, MergeWave
3
+ from tfv_get_tools.atmos import DownloadAtmos, MergeAtmos
4
+ from tfv_get_tools.tide._tidal_base import ExtractTide
@@ -0,0 +1,107 @@
1
+ """
2
+ Set of standard variable attributes for the merged dataset
3
+ """
4
+
5
+ STDVARS = {
6
+ "u10": {
7
+ "long_name": "10m_eastward_wind",
8
+ "standard_name": "eastward_wind",
9
+ "units": "m s-1"
10
+ },
11
+ "v10": {
12
+ "long_name": "10m_northward_wind",
13
+ "standard_name": "northward_wind",
14
+ "units": "m s-1"
15
+ },
16
+ "mslp": {
17
+ "long_name": "mean_sea_level_pressure",
18
+ "standard_name": "air_pressure_at_sea_level",
19
+ "units": "Pa"
20
+ },
21
+ "dlwrf": {
22
+ "long_name": "surface_downward_longwave_flux",
23
+ "standard_name": "surface_downwelling_longwave_flux_in_air",
24
+ "units": "W m-2"
25
+ },
26
+ "dswrf": {
27
+ "long_name": "surface_downward_shortwave_flux",
28
+ "standard_name": "surface_downwelling_shortwave_flux_in_air",
29
+ "units": "W m-2"
30
+ },
31
+ "t2m": {
32
+ "long_name": "2m_air_temperature",
33
+ "standard_name": "air_temperature",
34
+ "units": "K"
35
+ },
36
+ "prate": {
37
+ "long_name": "precipitation_rate",
38
+ "standard_name": "precipitation_flux",
39
+ "units": "kg m-2 s-1"
40
+ },
41
+ "relhum": {
42
+ "long_name": "relative_humidity",
43
+ "standard_name": "relative_humidity",
44
+ "units": "1"
45
+ },
46
+ "water_u": {
47
+ "long_name": "eastward_sea_water_velocity",
48
+ "standard_name": "eastward_sea_water_velocity",
49
+ "units": "m s-1"
50
+ },
51
+ "water_v": {
52
+ "long_name": "northward_sea_water_velocity",
53
+ "standard_name": "northward_sea_water_velocity",
54
+ "units": "m s-1"
55
+ },
56
+ "surf_el": {
57
+ "long_name": "sea_surface_height_above_geoid",
58
+ "standard_name": "sea_surface_height_above_geoid",
59
+ "units": "m"
60
+ },
61
+ "water_temp": {
62
+ "long_name": "sea_water_temperature",
63
+ "standard_name": "sea_water_temperature",
64
+ "units": "K"
65
+ },
66
+ "salinity": {
67
+ "long_name": "sea_water_salinity",
68
+ "standard_name": "sea_water_practical_salinity",
69
+ "units": "1e-3"
70
+ },
71
+ # Wave parameters
72
+ "hs": {
73
+ "long_name": "significant_wave_height",
74
+ "standard_name": "sea_surface_wave_significant_height",
75
+ "units": "m"
76
+ },
77
+ "tp": {
78
+ "long_name": "peak_wave_period",
79
+ "standard_name": "sea_surface_wave_period_at_variance_spectral_density_maximum",
80
+ "units": "s"
81
+ },
82
+ "tm02": {
83
+ "long_name": "mean_wave_period_tm02",
84
+ "standard_name": "sea_surface_wave_mean_period_from_variance_spectral_density_second_frequency_moment",
85
+ "units": "s"
86
+ },
87
+ "tm10": {
88
+ "long_name": "mean_wave_period_tm10",
89
+ "standard_name": "sea_surface_wave_mean_period_from_variance_spectral_density_inverse_frequency_moment",
90
+ "units": "s"
91
+ },
92
+ "mwd": {
93
+ "long_name": "mean_wave_direction",
94
+ "standard_name": "sea_surface_wave_from_direction",
95
+ "units": "degree"
96
+ },
97
+ "pwd": {
98
+ "long_name": "peak_wave_direction",
99
+ "standard_name": "sea_surface_wave_from_direction_at_variance_spectral_density_maximum",
100
+ "units": "degree"
101
+ },
102
+ "spr": {
103
+ "long_name": "mean_wave_directional_spreading",
104
+ "standard_name": "sea_surface_wave_directional_spread",
105
+ "units": "degree"
106
+ }
107
+ }
tfv_get_tools/atmos.py ADDED
@@ -0,0 +1,167 @@
1
+ from datetime import datetime
2
+ from pathlib import Path
3
+ from typing import Union, Tuple, Optional, List
4
+ import pandas as pd
5
+ from tfv_get_tools.providers._downloader import BatchDownloadResult
6
+
7
+
8
+ def DownloadAtmos(
9
+ start_date: Union[str, datetime, pd.Timestamp],
10
+ end_date: Union[str, datetime, pd.Timestamp],
11
+ xlims: Tuple[float, float],
12
+ ylims: Tuple[float, float],
13
+ out_path: Union[str, Path] = Path("./raw"),
14
+ source: str = "ERA5",
15
+ model: str = "default",
16
+ prefix: Optional[str] = None,
17
+ verbose: bool = False,
18
+ variables: Optional[List[str]] = None,
19
+ skip_check: bool = False,
20
+ **kwargs,
21
+ ) -> BatchDownloadResult:
22
+ """Download Atmospheric Data - the proper user-facing API
23
+
24
+ Users should call this function, not the individual downloader classes directly.
25
+
26
+ Args:
27
+ start_date: Start date
28
+ end_date: End date
29
+ xlims: Longitude bounds
30
+ ylims: Latitude bounds
31
+ out_path: Output directory
32
+ source: Data source ('ERA5', 'CFSR', 'BARRA2')
33
+ model: Model variant
34
+ prefix: File prefix
35
+ verbose: Verbose output
36
+ variables: Custom variables to download
37
+ skip_check: Skip user confirmation
38
+ **kwargs: Additional arguments
39
+
40
+ Returns:
41
+ BatchDownloadResult: Results of the download operation
42
+ """
43
+
44
+ if source.lower() == "cfsr":
45
+ from tfv_get_tools.providers.atmos.cfsr import DownloadCFSRAtmos
46
+
47
+ downloader = DownloadCFSRAtmos(
48
+ start_date=start_date,
49
+ end_date=end_date,
50
+ xlims=xlims,
51
+ ylims=ylims,
52
+ out_path=out_path,
53
+ model=model,
54
+ prefix=prefix,
55
+ verbose=verbose,
56
+ variables=variables,
57
+ skip_check=skip_check,
58
+ **kwargs
59
+ )
60
+ return downloader.execute_download()
61
+
62
+ elif source.lower() == "era5":
63
+ from tfv_get_tools.providers.atmos.era5 import DownloadERA5Atmos
64
+
65
+ downloader = DownloadERA5Atmos(
66
+ start_date=start_date,
67
+ end_date=end_date,
68
+ xlims=xlims,
69
+ ylims=ylims,
70
+ out_path=out_path,
71
+ model=model,
72
+ prefix=prefix,
73
+ verbose=verbose,
74
+ variables=variables,
75
+ skip_check=skip_check,
76
+ **kwargs
77
+ )
78
+ return downloader.execute_download()
79
+
80
+ elif source.lower() == "barra2":
81
+ from tfv_get_tools.providers.atmos.barra2 import DownloadBARRA2
82
+
83
+ downloader = DownloadBARRA2(
84
+ start_date=start_date,
85
+ end_date=end_date,
86
+ xlims=xlims,
87
+ ylims=ylims,
88
+ out_path=out_path,
89
+ model=model,
90
+ prefix=prefix,
91
+ verbose=verbose,
92
+ variables=variables,
93
+ skip_check=skip_check,
94
+ **kwargs
95
+ )
96
+ return downloader.execute_download()
97
+
98
+ else:
99
+ raise ValueError(f'Unrecognised source {source}. Must be one of: CFSR, ERA5, BARRA2')
100
+
101
+
102
+ def MergeAtmos(
103
+ in_path: Path = Path("./raw"),
104
+ out_path: Path = Path("."),
105
+ fname: str = None,
106
+ source: str = 'ERA5',
107
+ model: str = 'default',
108
+ time_start: str = None,
109
+ time_end: str = None,
110
+ reproject: int = None,
111
+ local_tz: Tuple[float, str] = None,
112
+ pad_dry: bool = False,
113
+ wrapto360: bool = False,
114
+ write: bool = True,
115
+ ):
116
+ """
117
+ Merge raw downloaded atmos datafiles into a single netcdf file.
118
+
119
+ **Use the same `source` and `model` that was supplied to the Downloader function**
120
+
121
+ Args:
122
+ in_path (Path, optional): Directory of the raw ocean data-files. Defaults to Path(".").
123
+ out_path (Path, optional): Output directory for the merged ocean netcdf and (opt) the fvc. Defaults to Path(".").
124
+ fname (str, optional): Merged ocean netcdf filename. Defaults to None.
125
+ source (str, optional): Source to be merged, defaults to "ERA5".
126
+ model (str, optional): Model for source to be merged. Defaults are listed in the wiki documentation.
127
+ time_start (str, optional): Start time limit of the merged dataset (str: "YYYY-mm-dd HH:MM"). Defaults to None.
128
+ time_end (str, optional): End time limit of the merged dataset (str: "YYYY-mm-dd HH:MM"). Defaults to None.
129
+ reproject (int, optional): Optionally reproject based, based on EPSG code. Defaults to None.
130
+ local_tz: (Tuple(float, str): optional): Add local timezone format is a tuple with Offset[float] and Label[str]
131
+ pad_dry: (bool, optional): Optionally pad landwards (i.e., fill nans horizontally). Defaults to False.
132
+ wrapto360: (bool, optional): Optionally wrap longitude to (0, 360) rather than (-180, 180). Defaults to False.
133
+ write (bool): Write the dataset. If False, the virtual merged dataset will be returned.
134
+ """
135
+
136
+ args = tuple()
137
+
138
+ kwargs = dict(
139
+ in_path=in_path,
140
+ out_path=out_path,
141
+ fname=fname,
142
+ source=source,
143
+ model=model,
144
+ time_start=time_start,
145
+ time_end=time_end,
146
+ reproject=reproject,
147
+ local_tz=local_tz,
148
+ pad_dry=pad_dry,
149
+ wrapto360=wrapto360,
150
+ write=write,
151
+ )
152
+
153
+ if source.lower() == "era5":
154
+ from tfv_get_tools.providers.atmos.era5 import MergeERA5Atmos
155
+ mrg = MergeERA5Atmos(*args, **kwargs)
156
+
157
+ elif source.lower() == "cfsr":
158
+ from tfv_get_tools.providers.atmos.cfsr import MergeCFSRAtmos
159
+ mrg = MergeCFSRAtmos(*args, **kwargs)
160
+
161
+ elif source.lower() == "barra2":
162
+ from tfv_get_tools.providers.atmos.barra2 import MergeBARRA2
163
+ mrg = MergeBARRA2(*args, **kwargs)
164
+
165
+ # If the user requested no-write, return the dataset object.
166
+ if not write:
167
+ return mrg.ds
@@ -0,0 +1,173 @@
1
+ """Base CLI Class
2
+
3
+ This is where all the default arguments are setup for the CLI tools (for Downloader "A" or Merger "B")
4
+
5
+ For the mode specific arguments and messaging, go to X_cli.py program.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ import argparse
10
+ from pathlib import Path
11
+ import re
12
+ import sys
13
+
14
+
15
+ class CLIBase(ABC):
16
+ def __init__(self, prog_name, description):
17
+ self.parser = argparse.ArgumentParser(
18
+ prog=prog_name,
19
+ description=description,
20
+ epilog="See '<command> --help' to read about a specific sub-command.",
21
+ )
22
+ self.subparsers = self.parser.add_subparsers(
23
+ dest="command", help="Sub-commands"
24
+ )
25
+
26
+ def dir_path(self, path):
27
+ if Path(path).is_dir():
28
+ return Path(path)
29
+ else:
30
+ raise argparse.ArgumentTypeError(
31
+ f"--path:{path} is not a valid path - check that it exists"
32
+ )
33
+
34
+ @abstractmethod
35
+ def add_source_arguments(self, parser):
36
+ pass
37
+
38
+ def add_download_parser(self, name, help_text):
39
+ parser = self.subparsers.add_parser(name, help=help_text)
40
+ parser.add_argument(
41
+ "time_start", type=str, help='Start time in format "yyyy-mm-dd"'
42
+ )
43
+ parser.add_argument("time_end", type=str, help='End time in format "yyyy-mm-dd"')
44
+ parser.add_argument(
45
+ "bbox",
46
+ nargs=4,
47
+ type=float,
48
+ help='Bounding box lon/lat extents as a list "xmin xmax ymin ymax"',
49
+ )
50
+ parser.add_argument(
51
+ "-p",
52
+ "--path",
53
+ default=".",
54
+ type=self.dir_path,
55
+ help="Output directory, needs to exist first",
56
+ )
57
+ self.add_source_arguments(parser)
58
+ parser.set_defaults(func=self.run_download)
59
+
60
+ return parser
61
+
62
+ def add_merge_parser(self, name, help_text):
63
+ parser = self.subparsers.add_parser(name, help=help_text)
64
+ parser.add_argument(
65
+ "-f",
66
+ "--file_name",
67
+ help='Merged netcdf filename. Default: "<Type>_<time_start>_<time_end>.nc"',
68
+ )
69
+ parser.add_argument(
70
+ "--time_start", help='Start time in format "yyyy-mm-dd"'
71
+ )
72
+ parser.add_argument("--time_end", help='End time in format "yyyy-mm-dd"')
73
+ parser.add_argument(
74
+ "-i",
75
+ "--in_path",
76
+ default=".",
77
+ type=self.dir_path,
78
+ help="Path to the directory holding the raw data files",
79
+ )
80
+ parser.add_argument(
81
+ "-o",
82
+ "--out_path",
83
+ default="./raw",
84
+ type=self.dir_path,
85
+ help="Output directory for the merged dataset, should exist first. Defaults to `./raw`",
86
+ )
87
+ parser.add_argument(
88
+ "-fvc",
89
+ "--write_fvc",
90
+ action="store_true",
91
+ help="Write a TUFLOW FV '.fvc' file to accompany merged dataset",
92
+ )
93
+ parser.add_argument(
94
+ "-rp",
95
+ "--reproject",
96
+ type=int,
97
+ default=None,
98
+ help="Reproject coordinates to a new coord system, input as EPSG code (e.g., 4326)",
99
+ )
100
+ parser.add_argument(
101
+ "-tz",
102
+ "--timezone_offset",
103
+ type=float,
104
+ default=None,
105
+ help='Fixed offset hours for local timezone, e.g. "-tz 10"',
106
+ )
107
+ parser.add_argument(
108
+ "-ltz",
109
+ "--timezone_label",
110
+ type=str,
111
+ default=None,
112
+ help='Custom timezone label, e.g. "-ltz AEST"',
113
+ )
114
+ parser.add_argument(
115
+ "--wrapto360",
116
+ action="store_true",
117
+ help="Format longitude as (0, 360) rather than (-180, 180)",
118
+ )
119
+ parser.add_argument(
120
+ "--pad_dry",
121
+ action="store_true",
122
+ help="Flag to pad values out over land or dry cells by applied nearest neighbour extrapolation",
123
+ )
124
+ self.add_source_arguments(parser)
125
+ parser.set_defaults(func=self.run_merge)
126
+
127
+ return parser
128
+
129
+ @abstractmethod
130
+ def run_download(self, args):
131
+ pass
132
+
133
+ @abstractmethod
134
+ def run_merge(self, args):
135
+ pass
136
+
137
+
138
+ def check_bbox(args):
139
+ """
140
+ Unfortunate edge case hackfix for programmer style floats like "-28." isntead of "-28.0".
141
+ Fixes negative numbers with trailing dots in the final 4 arguments (bbox).
142
+ Only modifies if all 4 final args are numeric-like!!
143
+ Otherwise spits fire
144
+ """
145
+ # Early exit if only 1 or 2 args are provided - or --help / -h
146
+ if len(args) == 1 or len(args) == 2 or (len(args) >= 2 and (('-h' in args) or ('--help' in args))):
147
+ return args
148
+
149
+ if (len(args) < 4):
150
+ raise ValueError("Must supply at minimum 6 arguments - time_start time_end xmin xmax ymin ymax")
151
+
152
+ # Get the last 4 arguments ( Should be bbox )
153
+ bbox_args = args[-4:]
154
+ other_args = args[:-4]
155
+
156
+ # Check if all 4 look like numbers (including problematic trailing dots)
157
+ numeric_pattern = r'^-?\d*\.?\d*$'
158
+ if not all(re.match(numeric_pattern, arg) and arg not in ['', '.', '-.', '-'] for arg in bbox_args):
159
+ raise ValueError("The final 4 arguments don't appear to be valid bbox coordinates. "
160
+ "Expected format: xmin xmax ymin ymax (numeric values)")
161
+
162
+ # Fix trailing dots by adding '0'
163
+ trailing_dot_pattern = r'^-?\d+\.$'
164
+ fixed_bbox = [arg + '0' if re.match(trailing_dot_pattern, arg) else arg for arg in bbox_args]
165
+
166
+ # Verify they're actually parseable as floats
167
+ try:
168
+ [float(arg) for arg in fixed_bbox]
169
+ except ValueError:
170
+ raise ValueError("The final 4 arguments cannot be parsed as bbox coordinates."
171
+ "Expected format: xmin xmax ymin ymax (numeric values)")
172
+
173
+ return other_args + fixed_bbox
@@ -0,0 +1,192 @@
1
+ """ATMOS CLI
2
+
3
+ This tool provides two sub-programs:
4
+ - Downloading raw atmospheric data
5
+ - Merging raw atmospheric data and assisting with TUFLOW FV fvc template setup
6
+ """
7
+
8
+ from argparse import ArgumentParser
9
+ import sys
10
+ import textwrap
11
+
12
+ from tfv_get_tools import DownloadAtmos, MergeAtmos
13
+ from tfv_get_tools.cli._cli_base import CLIBase, check_bbox
14
+
15
+ def print_atmos_info():
16
+ """Returns detailed help text for GetAtmos"""
17
+ return textwrap.dedent("""
18
+ GetAtmos
19
+ =======================================
20
+
21
+ This tool is designed to support a user with downloading atmospheric datasets
22
+ from common online publicly available sources, as well as subsequent
23
+ pre-processing and collation for TUFLOW FV modelling.
24
+ The ideology of the tool is to provide the datasets in a true to original
25
+ raw format (program "A"), and then in a processed "simplified"
26
+ merged format ready for TUFLOW FV modelling (program "B").
27
+
28
+ GetAtmos works with atmospheric reanalysis and analysis data, including 10m wind
29
+ velocity components, mean sea-level pressure, temperature, relative humidity
30
+ and short/long wave downward radiation.
31
+
32
+ There are several data sources and sub-models available currently. These are listed
33
+ below. Some of these may require registration.
34
+
35
+ Available data sources and sub-models:
36
+ -------------
37
+ 1. (Default) ECMWF's "ERA5" - registration required
38
+ - "default" - A global reanalysis
39
+
40
+ 2. NOAA's "CFSR"
41
+ - "default" - A global reanalysis
42
+
43
+ 3. BoM's "BARRA2"
44
+ - "R2" - An 11-km grid reanalysis covering Australia
45
+ - "C2" - An 4-km grid reanalysis covering Australia, with only wind and pressure fields downloaded.
46
+ - "RE2" - (Testing Only) An experimental ensembles 22-km grid covering Australia.
47
+
48
+ Example Usage:
49
+ ---------
50
+ Download ERA5 Reanalysis Data - all defaults
51
+ `GetAtmos A 2011-01-01 2012-01-01 145 150 -30 -25`
52
+
53
+ Merge ERA5 Reanalysis Data - all defaults, merge all data in the raw folder
54
+ `GetAtmos B`
55
+
56
+ Download BARRA2 C2 Dataset
57
+ `GetAtmos A -s BARRA2 -m R2 2011-01-01 2012-01-01 150 153 -30 -25`
58
+
59
+ Merge BARRA2 C2 Dataset with reprojection and local time
60
+ `GetAtmos B -s BARRA2 -m R2 -tz 10 -ltz AEST -rp 7856`
61
+
62
+ For more specific help, please use:
63
+ `GetAtmos A -h` or `GetAtmos B -h`
64
+ """)
65
+
66
+
67
+ def entry():
68
+ """This is the entrypoint to the CLI, linked in the pyproject.toml"""
69
+ cli = GetAtmos()
70
+ sys.argv = check_bbox(sys.argv)
71
+ cli.run_cli()
72
+
73
+
74
+ class GetAtmos(CLIBase):
75
+ def __init__(self, download_func=None, merge_func=None):
76
+ super().__init__("GetAtmos", "Tool for downloading and merging Atmos data")
77
+
78
+ # Allow injection of functions for testing
79
+ self.download_func = download_func or DownloadAtmos
80
+ self.merge_func = merge_func or MergeAtmos
81
+
82
+ self.add_download_parser(
83
+ "A",
84
+ "Download raw Atmos files for a set time period and bounding box extents",
85
+ )
86
+ self.add_merge_parser(
87
+ "B",
88
+ "Merge raw Atmos files into a single netcdf, and optionally write a TUFLOW FV FVC file",
89
+ )
90
+
91
+ self.add_info_parser()
92
+
93
+ def run_cli(self):
94
+ """Parse arguments and execute the appropriate function."""
95
+ args = self.parser.parse_args()
96
+ if args.command is not None:
97
+ args.func(args)
98
+ else:
99
+ self.parser.print_help()
100
+
101
+ def add_info_parser(self):
102
+ parser = self.subparsers.add_parser('info', help="Print detailed information about this program and the options")
103
+ parser.set_defaults(func=self.print_detailed_info)
104
+
105
+ def print_detailed_info(self, args):
106
+ text = print_atmos_info()
107
+ print(text)
108
+
109
+ def add_source_arguments(self, parser: ArgumentParser):
110
+ """Add source arguments
111
+
112
+ Args:
113
+ parser: The parser to add arguments to.
114
+ """
115
+ super().add_source_arguments(parser)
116
+
117
+ parser.add_argument(
118
+ '--info',
119
+ action="store_true",
120
+ help="Display the full program help"
121
+ )
122
+
123
+ parser.add_argument(
124
+ "-s",
125
+ "--source",
126
+ type=str,
127
+ default="ERA5",
128
+ help='Atmos data source. Default = "ERA5". Optionally others see wiki.',
129
+ )
130
+ parser.add_argument(
131
+ "-m",
132
+ "--model",
133
+ type=str,
134
+ default="default",
135
+ help='Model from source. Default is "default". Optionally others see wiki.',
136
+ )
137
+ parser.add_argument(
138
+ "--test",
139
+ action="store_true",
140
+ help="Run in test mode - no actual downloads performed",
141
+ )
142
+
143
+ def run_download(self, args):
144
+ """Call the DownloadAtmos function
145
+
146
+ Args:
147
+ args: CLI argument parser
148
+ """
149
+ xlims = tuple([float(x) for x in args.bbox[:2]])
150
+ ylims = tuple([float(x) for x in args.bbox[2:]])
151
+
152
+ self.download_func(
153
+ args.time_start,
154
+ args.time_end,
155
+ xlims,
156
+ ylims,
157
+ source=args.source,
158
+ out_path=args.path,
159
+ TEST_MODE=args.test,
160
+ )
161
+
162
+ def run_merge(self, args):
163
+ """Call the MergeAtmos function
164
+
165
+ Args:
166
+ args: CLI argument parser
167
+ """
168
+ # Sort out the timezone arguments
169
+ if (args.timezone_offset is not None) and (args.timezone_label is not None):
170
+ tz = (args.timezone_offset, args.timezone_label)
171
+ elif args.timezone_offset is not None:
172
+ sign = "+" if args.timezone_offset > 0 else "-"
173
+ tz = (args.timezone_offset, f"UTC{sign}{abs(args.timezone_offset):0.1f}")
174
+ elif (args.timezone_offset is None) and (args.timezone_label is not None):
175
+ raise ValueError("Need to supply a timezone_offset!")
176
+ else:
177
+ tz = None
178
+
179
+ self.merge_func(
180
+ in_path=args.in_path,
181
+ out_path=args.out_path,
182
+ fname=args.file_name,
183
+ source=args.source,
184
+ model=args.model,
185
+ time_start=args.time_start,
186
+ time_end=args.time_end,
187
+ # write_fvc=args.write_fvc,
188
+ reproject=args.reproject,
189
+ local_tz=tz,
190
+ wrapto360=args.wrapto360,
191
+ pad_dry=args.pad_dry,
192
+ )