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.
- tfv_get_tools/__init__.py +4 -0
- tfv_get_tools/_standard_attrs.py +107 -0
- tfv_get_tools/atmos.py +167 -0
- tfv_get_tools/cli/_cli_base.py +173 -0
- tfv_get_tools/cli/atmos_cli.py +192 -0
- tfv_get_tools/cli/ocean_cli.py +204 -0
- tfv_get_tools/cli/tide_cli.py +118 -0
- tfv_get_tools/cli/wave_cli.py +183 -0
- tfv_get_tools/fvc/__init__.py +3 -0
- tfv_get_tools/fvc/_atmos.py +230 -0
- tfv_get_tools/fvc/_fvc.py +218 -0
- tfv_get_tools/fvc/_ocean.py +171 -0
- tfv_get_tools/fvc/_tide.py +195 -0
- tfv_get_tools/ocean.py +170 -0
- tfv_get_tools/providers/__init__.py +0 -0
- tfv_get_tools/providers/_custom_conversions.py +34 -0
- tfv_get_tools/providers/_downloader.py +566 -0
- tfv_get_tools/providers/_merger.py +520 -0
- tfv_get_tools/providers/_utilities.py +255 -0
- tfv_get_tools/providers/atmos/barra2.py +209 -0
- tfv_get_tools/providers/atmos/cfgs/barra2_c2.yaml +52 -0
- tfv_get_tools/providers/atmos/cfgs/barra2_r2.yaml +85 -0
- tfv_get_tools/providers/atmos/cfgs/barra2_re2.yaml +70 -0
- tfv_get_tools/providers/atmos/cfgs/cfsr.yaml +68 -0
- tfv_get_tools/providers/atmos/cfgs/era5.yaml +77 -0
- tfv_get_tools/providers/atmos/cfgs/era5_gcp.yaml +77 -0
- tfv_get_tools/providers/atmos/cfsr.py +207 -0
- tfv_get_tools/providers/atmos/era5.py +20 -0
- tfv_get_tools/providers/atmos/era5_gcp.py +20 -0
- tfv_get_tools/providers/ocean/cfgs/copernicus_blk.yaml +64 -0
- tfv_get_tools/providers/ocean/cfgs/copernicus_glo.yaml +67 -0
- tfv_get_tools/providers/ocean/cfgs/copernicus_nws.yaml +62 -0
- tfv_get_tools/providers/ocean/cfgs/hycom.yaml +73 -0
- tfv_get_tools/providers/ocean/copernicus_ocean.py +457 -0
- tfv_get_tools/providers/ocean/hycom.py +611 -0
- tfv_get_tools/providers/wave/cawcr.py +166 -0
- tfv_get_tools/providers/wave/cfgs/cawcr_aus_10m.yaml +39 -0
- tfv_get_tools/providers/wave/cfgs/cawcr_aus_4m.yaml +39 -0
- tfv_get_tools/providers/wave/cfgs/cawcr_glob_24m.yaml +39 -0
- tfv_get_tools/providers/wave/cfgs/cawcr_pac_10m.yaml +39 -0
- tfv_get_tools/providers/wave/cfgs/cawcr_pac_4m.yaml +39 -0
- tfv_get_tools/providers/wave/cfgs/copernicus_glo.yaml +56 -0
- tfv_get_tools/providers/wave/cfgs/copernicus_nws.yaml +51 -0
- tfv_get_tools/providers/wave/cfgs/era5.yaml +48 -0
- tfv_get_tools/providers/wave/cfgs/era5_gcp.yaml +48 -0
- tfv_get_tools/providers/wave/copernicus_wave.py +38 -0
- tfv_get_tools/providers/wave/era5.py +232 -0
- tfv_get_tools/providers/wave/era5_gcp.py +169 -0
- tfv_get_tools/tide/__init__.py +2 -0
- tfv_get_tools/tide/_nodestring.py +214 -0
- tfv_get_tools/tide/_tidal_base.py +568 -0
- tfv_get_tools/utilities/_tfv_bc.py +78 -0
- tfv_get_tools/utilities/horizontal_padding.py +89 -0
- tfv_get_tools/utilities/land_masking.py +93 -0
- tfv_get_tools/utilities/parsers.py +44 -0
- tfv_get_tools/utilities/warnings.py +38 -0
- tfv_get_tools/wave.py +179 -0
- tfv_get_tools-0.2.0.dist-info/METADATA +286 -0
- tfv_get_tools-0.2.0.dist-info/RECORD +62 -0
- tfv_get_tools-0.2.0.dist-info/WHEEL +5 -0
- tfv_get_tools-0.2.0.dist-info/entry_points.txt +5 -0
- tfv_get_tools-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Set of functions to managing horizontally padding an OCEAN dataset
|
|
3
|
+
|
|
4
|
+
This will cast the nearest value horizontally (lat/lon) for each depth level, through all times
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import xarray as xr
|
|
8
|
+
import numpy as np
|
|
9
|
+
from scipy import spatial
|
|
10
|
+
|
|
11
|
+
def horizontal_pad(ds):
|
|
12
|
+
"""Pad an ocean dataset horizontally for each time.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
ds (xr.Dataset): input merged ocean dataset
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
xr.Dataset: resulting padded ocean dataset
|
|
19
|
+
"""
|
|
20
|
+
for var in ds.data_vars:
|
|
21
|
+
if set(ds[var].dims) >= {'latitude', 'longitude', 'time'}:
|
|
22
|
+
# Compute nearest indices for each depth level
|
|
23
|
+
nearest_indices = xr.apply_ufunc(
|
|
24
|
+
_compute_nearest_indices,
|
|
25
|
+
ds[var].isel(time=0),
|
|
26
|
+
input_core_dims=[['latitude', 'longitude']],
|
|
27
|
+
output_core_dims=[['latitude', 'longitude']],
|
|
28
|
+
vectorize=True
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Apply filling to all time steps for each depth
|
|
32
|
+
filled_var = xr.apply_ufunc(
|
|
33
|
+
_fill_nans_with_precomputed_indices,
|
|
34
|
+
ds[var],
|
|
35
|
+
nearest_indices,
|
|
36
|
+
input_core_dims=[['latitude', 'longitude'], ['latitude', 'longitude']],
|
|
37
|
+
output_core_dims=[['latitude', 'longitude']],
|
|
38
|
+
vectorize=True
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
ds[var] = filled_var
|
|
42
|
+
|
|
43
|
+
return ds
|
|
44
|
+
|
|
45
|
+
def _compute_nearest_indices(values: np.ndarray) -> np.ndarray:
|
|
46
|
+
"""Find the indices of the nearest non nan values across longitude/latitude
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
values (np.ndarray): variable values
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
np.ndarray: indices array
|
|
53
|
+
"""
|
|
54
|
+
ny, nx = values.shape
|
|
55
|
+
lat = np.arange(ny)
|
|
56
|
+
lon = np.arange(nx)
|
|
57
|
+
lat, lon = np.meshgrid(lat, lon, indexing='ij')
|
|
58
|
+
|
|
59
|
+
valid_mask = ~np.isnan(values)
|
|
60
|
+
valid_points = np.column_stack((lat[valid_mask], lon[valid_mask]))
|
|
61
|
+
|
|
62
|
+
# Create KDTree for efficient nearest neighbor search
|
|
63
|
+
tree = spatial.cKDTree(valid_points)
|
|
64
|
+
|
|
65
|
+
# Find nearest non-NaN point for each point
|
|
66
|
+
all_points = np.column_stack((lat.ravel(), lon.ravel()))
|
|
67
|
+
_, indices = tree.query(all_points)
|
|
68
|
+
|
|
69
|
+
# Reshape indices to match the original shape
|
|
70
|
+
return indices.reshape(values.shape)
|
|
71
|
+
|
|
72
|
+
def _fill_nans_with_precomputed_indices(values: np.ndarray, nearest_indices: np.ndarray) -> np.ndarray:
|
|
73
|
+
"""Fill nan values in dataset wih the pre-calced indicies.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
values (np.ndarray): variable values
|
|
77
|
+
nearest_indices (np.ndarray): indicies if nearest non-nan value across longitude/latitude
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
np.ndarray: filled variable values
|
|
81
|
+
"""
|
|
82
|
+
valid_values = values[~np.isnan(values)]
|
|
83
|
+
|
|
84
|
+
# Apply the precomputed indices to fill NaN values
|
|
85
|
+
filled_values = values.copy()
|
|
86
|
+
nan_mask = np.isnan(filled_values)
|
|
87
|
+
filled_values[nan_mask] = valid_values[nearest_indices[nan_mask]]
|
|
88
|
+
|
|
89
|
+
return filled_values
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module to handle land masking of an atmospheric dataset.
|
|
3
|
+
This has been straight air-lifted from GetAtmos with no modifications.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import lzma
|
|
7
|
+
import pickle
|
|
8
|
+
import importlib.resources
|
|
9
|
+
|
|
10
|
+
import xarray as xr
|
|
11
|
+
import numpy as np
|
|
12
|
+
import dask.array as da
|
|
13
|
+
import pandas as pd
|
|
14
|
+
import logging
|
|
15
|
+
from scipy.ndimage import distance_transform_edt
|
|
16
|
+
|
|
17
|
+
from tqdm.auto import trange
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def load_atmos_masks():
|
|
21
|
+
"""Returns a dictionary containing landmasks for ERA5, CFSR, CFSv2 and BARRA_R.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
lm (dict): Dict containing keys 'cfsr', 'cfsv2', 'barra_r', 'era5'.
|
|
25
|
+
Each of these contains a sub-dict that has 'x', 'y' and 'lm' keys.
|
|
26
|
+
"""
|
|
27
|
+
with importlib.resources.files(__name__).parent.joinpath("providers/atmos/data/atmos_masks.xz").open("rb") as stream:
|
|
28
|
+
with lzma.open(stream, "rb") as f:
|
|
29
|
+
lmdict = pickle.load(f)
|
|
30
|
+
|
|
31
|
+
return lmdict
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def mask_land_data(ds, source):
|
|
35
|
+
lms = load_atmos_masks()
|
|
36
|
+
|
|
37
|
+
# Load all land masks (TODO: BETTER HANDLING OF CFSR and CFSv2 SWITCH)
|
|
38
|
+
if "cfs" in source:
|
|
39
|
+
if ds["time"][-1] > pd.Timestamp(2011, 1, 1):
|
|
40
|
+
k = "cfsv2"
|
|
41
|
+
else:
|
|
42
|
+
k = "cfsr"
|
|
43
|
+
else:
|
|
44
|
+
k = source.lower()
|
|
45
|
+
lm = lms[k]
|
|
46
|
+
|
|
47
|
+
dsm = xr.DataArray(
|
|
48
|
+
np.squeeze(lm["lm"]), coords=dict(latitude=lm["y"], longitude=lm["x"])
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
ds["lsm"] = dsm.reindex(
|
|
52
|
+
longitude=ds["longitude"], latitude=ds["latitude"], method="nearest"
|
|
53
|
+
)
|
|
54
|
+
ds["lsm"].attrs = {"units": "-", "long_name": f"{k} land mask index"}
|
|
55
|
+
|
|
56
|
+
# If the Atmos dataset is in negative longitudes, we need to wrap the land mask around 180
|
|
57
|
+
# (i.e., -180 to 180), not (0 to 360)
|
|
58
|
+
if ds["longitude"].min() < 0.0:
|
|
59
|
+
lon = dsm['longitude'].values
|
|
60
|
+
lon[lon > 180] = (lon - 360)[lon > 180]
|
|
61
|
+
dsm['longitude'] = lon
|
|
62
|
+
dsm = dsm.sortby('longitude')
|
|
63
|
+
|
|
64
|
+
mask = (ds["lsm"] > 0.2).values
|
|
65
|
+
mask_3d = repeat_2d_array(mask, ds.sizes["time"])
|
|
66
|
+
|
|
67
|
+
for var in ds.data_vars.keys():
|
|
68
|
+
dims = ds[var].dims
|
|
69
|
+
if ('longitude' in dims) & ('latitude' in dims) & ('time' in dims):
|
|
70
|
+
logging.debug(f"{var} in ds, running land mask")
|
|
71
|
+
ds[var].data = fill(ds[var].values, mask_3d)
|
|
72
|
+
|
|
73
|
+
return ds
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def fill(data, mask):
|
|
77
|
+
"""Replace "masked" data with the nearest valid data cell
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
data (da.ndarray): 2D input data array
|
|
81
|
+
invalid (da.ndarray): 2D boolean array, where True cells are replaced by the nearest data.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
filled_data: 2D output array with filled data
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
ind = distance_transform_edt(mask, return_distances=False, return_indices=True)
|
|
88
|
+
return data[tuple(ind)]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def repeat_2d_array(array, n):
|
|
92
|
+
repeated_array = da.tile(array, (n, 1, 1))
|
|
93
|
+
return repeated_array
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Union
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _parse_date(date: Union[str, datetime, pd.Timestamp]) -> datetime:
|
|
8
|
+
if isinstance(date, str):
|
|
9
|
+
if len(date) == 10:
|
|
10
|
+
fmt = "%Y-%m-%d"
|
|
11
|
+
elif len(date) == 13:
|
|
12
|
+
fmt = "%Y-%m-%d %H"
|
|
13
|
+
elif len(date) == 16:
|
|
14
|
+
fmt = "%Y-%m-%d %H:%M"
|
|
15
|
+
elif len(date) == 19:
|
|
16
|
+
fmt = "%Y-%m-%d %H:%M:%S"
|
|
17
|
+
elif len(date) == 8:
|
|
18
|
+
fmt = "%Y%m%d"
|
|
19
|
+
elif len(date) == 15:
|
|
20
|
+
fmt = "%Y%m%d.%H%M%S"
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
date = datetime.strptime(date, fmt)
|
|
24
|
+
except:
|
|
25
|
+
raise ValueError(f'Failed to convert date `{date}` to a Timestamp, please check format (e.g., YYYY-mm-dd)')
|
|
26
|
+
return date
|
|
27
|
+
|
|
28
|
+
elif isinstance(date, pd.Timestamp):
|
|
29
|
+
return date.to_pydatetime()
|
|
30
|
+
elif isinstance(date, datetime):
|
|
31
|
+
return date
|
|
32
|
+
else:
|
|
33
|
+
raise ValueError(
|
|
34
|
+
"Date must be a string, datetime, or pandas Timestamp object"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def _parse_path(path: Union[str, Path]) -> Path:
|
|
38
|
+
path = Path(path)
|
|
39
|
+
if path.is_dir():
|
|
40
|
+
return path
|
|
41
|
+
else:
|
|
42
|
+
raise NotADirectoryError(
|
|
43
|
+
f"`{path.as_posix()}` does not exist, please check."
|
|
44
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
import functools
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def deprecated(new_func):
|
|
6
|
+
"""
|
|
7
|
+
Decorator to mark functions as deprecated.
|
|
8
|
+
|
|
9
|
+
This decorator will result in a warning being emitted
|
|
10
|
+
when the decorated function is used.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
new_func (callable): The new function to be used instead of the deprecated function.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
callable: A decorator function.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> def new_function(x, y):
|
|
20
|
+
... return x + y
|
|
21
|
+
...
|
|
22
|
+
>>> @deprecated(new_function)
|
|
23
|
+
... def old_function(x, y):
|
|
24
|
+
... return new_function(x, y)
|
|
25
|
+
...
|
|
26
|
+
>>> old_function(1, 2)
|
|
27
|
+
3
|
|
28
|
+
# Warning: Call to deprecated function old_function. Use new_function instead.
|
|
29
|
+
"""
|
|
30
|
+
def decorator(old_func):
|
|
31
|
+
@functools.wraps(old_func)
|
|
32
|
+
def wrapper(*args, **kwargs):
|
|
33
|
+
warnings.warn(f"Call to deprecated function {old_func.__name__}. "
|
|
34
|
+
f"Use {new_func.__name__} instead.",
|
|
35
|
+
category=DeprecationWarning, stacklevel=2)
|
|
36
|
+
return new_func(*args, **kwargs)
|
|
37
|
+
return wrapper
|
|
38
|
+
return decorator
|
tfv_get_tools/wave.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Union, Tuple, Optional, List
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from tfv_get_tools.utilities._tfv_bc import write_tuflowfv_fvc
|
|
7
|
+
from tfv_get_tools.providers._downloader import BatchDownloadResult
|
|
8
|
+
|
|
9
|
+
def DownloadWave(
|
|
10
|
+
start_date: Union[str, datetime, pd.Timestamp],
|
|
11
|
+
end_date: Union[str, datetime, pd.Timestamp],
|
|
12
|
+
xlims: Tuple[float, float],
|
|
13
|
+
ylims: Tuple[float, float],
|
|
14
|
+
out_path: Union[str, Path] = Path("./raw"),
|
|
15
|
+
source: str = "CAWCR",
|
|
16
|
+
model: str = "default",
|
|
17
|
+
prefix: Optional[str] = None,
|
|
18
|
+
verbose: bool = False,
|
|
19
|
+
variables: Optional[List[str]] = None,
|
|
20
|
+
skip_check: bool = False,
|
|
21
|
+
**kwargs,
|
|
22
|
+
) -> BatchDownloadResult:
|
|
23
|
+
"""Download Wave Data
|
|
24
|
+
|
|
25
|
+
Users should call this function, not the individual downloader classes directly.
|
|
26
|
+
|
|
27
|
+
This module will download wave data from several possible sources to facilitate
|
|
28
|
+
TUFLOW FV and SWAN modelling.
|
|
29
|
+
|
|
30
|
+
The following sources have been implemented:
|
|
31
|
+
- `CAWCR` - CSIRO Ocean wave hindcast forced using NCEP's CFSR Atmospheric Model
|
|
32
|
+
- `glob_24m` - Global domain at 24' resolution
|
|
33
|
+
- `aus_10m` - Australia ribbon model domain at 10' resolution
|
|
34
|
+
- `aus_4m` - Australia ribbon model domain at 4' resolution
|
|
35
|
+
- `pac_10m` - Pacific island domain at 10' resolution
|
|
36
|
+
- `pac_4m` - Pacific island domain at 4' resolution
|
|
37
|
+
- `Copernicus` - Various models from the Copernicus Marine Service
|
|
38
|
+
- `GLO` - Global wave model
|
|
39
|
+
- `ERA5` - Global 0.5deg Wave model (WAM) from the European Centre for
|
|
40
|
+
Medium-Range Weather Forecasts (ECMWF)
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
start_date: Start date. The string format is `%Y-%m-%d` (e.g., '2011-01-01')
|
|
44
|
+
end_date: End date. The string format is `%Y-%m-%d` (e.g., '2011-02-01')
|
|
45
|
+
xlims: Minimum and maximum longitude, as floats. e.g., (115, 120)
|
|
46
|
+
ylims: Minimum and maximum latitude, as floats. e.g., (-40, -35)
|
|
47
|
+
out_path: Output directory for data files
|
|
48
|
+
source: Data source to download. One of {'CAWCR', 'Copernicus', 'ERA5'}
|
|
49
|
+
model: Choice of model, depending on "source"
|
|
50
|
+
prefix: Extra file name prefix
|
|
51
|
+
verbose: Print extra program information
|
|
52
|
+
variables: List of variables to download
|
|
53
|
+
skip_check: Skip user confirmation
|
|
54
|
+
**kwargs: Additional arguments
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
BatchDownloadResult: Results of the download operation
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
if source.lower() == "cawcr":
|
|
61
|
+
from tfv_get_tools.providers.wave.cawcr import DownloadCAWCR
|
|
62
|
+
|
|
63
|
+
downloader = DownloadCAWCR(
|
|
64
|
+
start_date=start_date,
|
|
65
|
+
end_date=end_date,
|
|
66
|
+
xlims=xlims,
|
|
67
|
+
ylims=ylims,
|
|
68
|
+
out_path=out_path,
|
|
69
|
+
model=model,
|
|
70
|
+
prefix=prefix,
|
|
71
|
+
verbose=verbose,
|
|
72
|
+
variables=variables,
|
|
73
|
+
skip_check=skip_check,
|
|
74
|
+
**kwargs
|
|
75
|
+
)
|
|
76
|
+
return downloader.execute_download()
|
|
77
|
+
|
|
78
|
+
elif source.lower() == "copernicus":
|
|
79
|
+
from tfv_get_tools.providers.wave.copernicus_wave import DownloadCopernicusWave
|
|
80
|
+
|
|
81
|
+
downloader = DownloadCopernicusWave(
|
|
82
|
+
start_date=start_date,
|
|
83
|
+
end_date=end_date,
|
|
84
|
+
xlims=xlims,
|
|
85
|
+
ylims=ylims,
|
|
86
|
+
out_path=out_path,
|
|
87
|
+
model=model,
|
|
88
|
+
prefix=prefix,
|
|
89
|
+
verbose=verbose,
|
|
90
|
+
variables=variables,
|
|
91
|
+
skip_check=skip_check,
|
|
92
|
+
**kwargs
|
|
93
|
+
)
|
|
94
|
+
return downloader.execute_download()
|
|
95
|
+
|
|
96
|
+
elif source.lower() == "era5":
|
|
97
|
+
from tfv_get_tools.providers.wave.era5 import DownloadERA5Wave
|
|
98
|
+
|
|
99
|
+
downloader = DownloadERA5Wave(
|
|
100
|
+
start_date=start_date,
|
|
101
|
+
end_date=end_date,
|
|
102
|
+
xlims=xlims,
|
|
103
|
+
ylims=ylims,
|
|
104
|
+
out_path=out_path,
|
|
105
|
+
model=model,
|
|
106
|
+
prefix=prefix,
|
|
107
|
+
verbose=verbose,
|
|
108
|
+
variables=variables,
|
|
109
|
+
skip_check=skip_check,
|
|
110
|
+
**kwargs
|
|
111
|
+
)
|
|
112
|
+
return downloader.execute_download()
|
|
113
|
+
|
|
114
|
+
else:
|
|
115
|
+
raise ValueError(f'Unrecognised source {source}. Must be one of: CAWCR, Copernicus, ERA5')
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def MergeWave(
|
|
120
|
+
in_path: Path = Path("./raw"),
|
|
121
|
+
out_path: Path = Path("."),
|
|
122
|
+
fname: str = None,
|
|
123
|
+
source: str = 'CAWCR',
|
|
124
|
+
model: str = 'default',
|
|
125
|
+
time_start: str = None,
|
|
126
|
+
time_end: str = None,
|
|
127
|
+
reproject: int = None,
|
|
128
|
+
local_tz: Tuple[float, str] = None,
|
|
129
|
+
wrapto360=False,
|
|
130
|
+
):
|
|
131
|
+
"""
|
|
132
|
+
Merge raw downloaded wave datafiles into a single netcdf file.
|
|
133
|
+
|
|
134
|
+
**Use the same `source` and `model` that was supplied to the Downloader function**
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
in_path (Path, optional): Directory of the raw ocean data-files. Defaults to Path(".").
|
|
138
|
+
out_path (Path, optional): Output directory for the merged ocean netcdf and (opt) the fvc. Defaults to Path(".").
|
|
139
|
+
fname (str, optional): Merged ocean netcdf filename. Defaults to None.
|
|
140
|
+
source (str, optional): Source to be merged, defaults to "CAWCR".
|
|
141
|
+
model (str, optional): Model for source to be merged. Defaults to 'default' (which is 'glob_24m' for CAWCR).
|
|
142
|
+
time_start (str, optional): Start time limit of the merged dataset (str: "YYYY-mm-dd HH:MM"). Defaults to None.
|
|
143
|
+
time_end (str, optional): End time limit of the merged dataset (str: "YYYY-mm-dd HH:MM"). Defaults to None.
|
|
144
|
+
reproject (int, optional): Optionally reproject based, based on EPSG code. Defaults to None.
|
|
145
|
+
local_tz: (Tuple(float, str): optional): Add local timezone format is a tuple with Offset[float] and Label[str]
|
|
146
|
+
source: (str, optional): Ocean data source. Defaults to Hycom.
|
|
147
|
+
wrapto360: (bool, optional): Optionally wrap longitude to (0, 360) rather than (-180, 180). Defaults to False.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
args = tuple()
|
|
151
|
+
|
|
152
|
+
kwargs = dict(
|
|
153
|
+
in_path=in_path,
|
|
154
|
+
out_path=out_path,
|
|
155
|
+
fname=fname,
|
|
156
|
+
source=source,
|
|
157
|
+
model=model,
|
|
158
|
+
time_start=time_start,
|
|
159
|
+
time_end=time_end,
|
|
160
|
+
reproject=reproject,
|
|
161
|
+
local_tz=local_tz,
|
|
162
|
+
pad_dry=False,
|
|
163
|
+
wrapto360=wrapto360,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if 'write_fvc' in kwargs:
|
|
167
|
+
print('Writing an FVC include file is not implemented for wave data. Skipping this flag')
|
|
168
|
+
|
|
169
|
+
if source.lower() == "cawcr":
|
|
170
|
+
from tfv_get_tools.providers.wave.cawcr import MergeCAWCR
|
|
171
|
+
MergeCAWCR(*args, **kwargs)
|
|
172
|
+
|
|
173
|
+
elif source.lower() == "copernicus":
|
|
174
|
+
from tfv_get_tools.providers.wave.copernicus_wave import MergeCopernicusWave
|
|
175
|
+
MergeCopernicusWave(*args, **kwargs)
|
|
176
|
+
|
|
177
|
+
elif source.lower() == "era5":
|
|
178
|
+
from tfv_get_tools.providers.wave.era5 import MergeERA5Wave
|
|
179
|
+
MergeERA5Wave(*args, **kwargs)
|