eo-tides 0.7.6.dev2__py3-none-any.whl → 0.8.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.
- eo_tides/__init__.py +14 -18
- eo_tides/eo.py +57 -41
- eo_tides/model.py +214 -151
- eo_tides/stats.py +65 -27
- eo_tides/utils.py +78 -62
- eo_tides/validation.py +26 -21
- {eo_tides-0.7.6.dev2.dist-info → eo_tides-0.8.0.dist-info}/METADATA +1 -1
- eo_tides-0.8.0.dist-info/RECORD +10 -0
- eo_tides-0.7.6.dev2.dist-info/RECORD +0 -10
- {eo_tides-0.7.6.dev2.dist-info → eo_tides-0.8.0.dist-info}/WHEEL +0 -0
- {eo_tides-0.7.6.dev2.dist-info → eo_tides-0.8.0.dist-info}/licenses/LICENSE +0 -0
eo_tides/model.py
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
"""Core tide modelling functionality.
|
2
|
+
|
3
|
+
This module provides tools for modelling ocean tide heights and phases
|
4
|
+
for any location or time period using one or more global tide models.
|
5
|
+
"""
|
6
|
+
|
1
7
|
# Used to postpone evaluation of type annotations
|
2
8
|
from __future__ import annotations
|
3
9
|
|
@@ -23,7 +29,13 @@ import pyTMD
|
|
23
29
|
import timescale.time
|
24
30
|
from tqdm import tqdm
|
25
31
|
|
26
|
-
from .utils import
|
32
|
+
from .utils import (
|
33
|
+
DatetimeLike,
|
34
|
+
_set_directory,
|
35
|
+
_standardise_models,
|
36
|
+
_standardise_time,
|
37
|
+
idw,
|
38
|
+
)
|
27
39
|
|
28
40
|
|
29
41
|
def _parallel_splits(
|
@@ -32,12 +44,13 @@ def _parallel_splits(
|
|
32
44
|
parallel_max: int | None = None,
|
33
45
|
min_points_per_split: int = 1000,
|
34
46
|
) -> int:
|
35
|
-
"""
|
36
|
-
|
37
|
-
|
47
|
+
"""Calculate the optimal number of parallel splits for data processing.
|
48
|
+
|
49
|
+
Optimal parallelisation is estimated based on system resources
|
50
|
+
and processing constraints.
|
38
51
|
|
39
|
-
Parameters
|
40
|
-
|
52
|
+
Parameters
|
53
|
+
----------
|
41
54
|
total_points : int
|
42
55
|
Total number of data points to process
|
43
56
|
model_count : int
|
@@ -46,6 +59,7 @@ def _parallel_splits(
|
|
46
59
|
Maximum number of parallel processes to use. If None, uses CPU core count
|
47
60
|
min_points_per_split : int, default=1000
|
48
61
|
Minimum number of points that should be processed in each split
|
62
|
+
|
49
63
|
"""
|
50
64
|
# Get available CPUs. First see if `CPU_GUARANTEE` exists in
|
51
65
|
# environment (if running in JupyterHub); if not use psutil
|
@@ -81,15 +95,20 @@ def _model_tides(
|
|
81
95
|
crop,
|
82
96
|
crop_buffer,
|
83
97
|
append_node,
|
98
|
+
constituents,
|
84
99
|
extra_databases,
|
85
100
|
):
|
86
|
-
"""Worker function applied in parallel by `model_tides`.
|
87
|
-
|
88
|
-
|
101
|
+
"""Worker function applied in parallel by `model_tides`.
|
102
|
+
|
103
|
+
Handles the extraction of tide modelling constituents and tide
|
104
|
+
modelling using `pyTMD`.
|
89
105
|
"""
|
90
106
|
# Load models from pyTMD database
|
91
107
|
extra_databases = [] if extra_databases is None else extra_databases
|
92
|
-
pytmd_model = pyTMD.io.model(
|
108
|
+
pytmd_model = pyTMD.io.model(
|
109
|
+
directory=directory,
|
110
|
+
extra_databases=extra_databases,
|
111
|
+
).elevation(model)
|
93
112
|
|
94
113
|
# Reproject x, y to latitude/longitude
|
95
114
|
transformer = pyproj.Transformer.from_crs(crs, "EPSG:4326", always_xy=True)
|
@@ -110,6 +129,8 @@ def _model_tides(
|
|
110
129
|
extrapolate=extrapolate,
|
111
130
|
cutoff=cutoff,
|
112
131
|
append_node=append_node,
|
132
|
+
constituents=constituents,
|
133
|
+
extra_databases=extra_databases,
|
113
134
|
)
|
114
135
|
|
115
136
|
# TODO: Return constituents
|
@@ -126,7 +147,8 @@ def _model_tides(
|
|
126
147
|
"affect your results but may lead to a minor slowdown. "
|
127
148
|
"This can occur when analysing clipped model files restricted "
|
128
149
|
"to the western hemisphere. To suppress this warning, manually "
|
129
|
-
"set `crop=False`."
|
150
|
+
"set `crop=False`.",
|
151
|
+
stacklevel=2,
|
130
152
|
)
|
131
153
|
|
132
154
|
# Read tidal constants and interpolate to grid points
|
@@ -140,6 +162,8 @@ def _model_tides(
|
|
140
162
|
extrapolate=extrapolate,
|
141
163
|
cutoff=cutoff,
|
142
164
|
append_node=append_node,
|
165
|
+
constituents=constituents,
|
166
|
+
extra_databases=extra_databases,
|
143
167
|
)
|
144
168
|
|
145
169
|
# Otherwise, raise error if cropping if set to True
|
@@ -210,13 +234,15 @@ def _model_tides(
|
|
210
234
|
|
211
235
|
# Convert data to pandas.DataFrame, and set index to our input
|
212
236
|
# time/x/y values
|
213
|
-
tide_df = pd.DataFrame(
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
237
|
+
tide_df = pd.DataFrame(
|
238
|
+
{
|
239
|
+
"time": np.tile(time, points_repeat),
|
240
|
+
"x": np.repeat(x, time_repeat),
|
241
|
+
"y": np.repeat(y, time_repeat),
|
242
|
+
"tide_model": model,
|
243
|
+
"tide_height": tide,
|
244
|
+
},
|
245
|
+
).set_index(["time", "x", "y"])
|
220
246
|
|
221
247
|
# Optionally convert outputs to integer units (can save memory)
|
222
248
|
if output_units == "m":
|
@@ -239,10 +265,11 @@ def ensemble_tides(
|
|
239
265
|
ranking_valid_perc=0.02,
|
240
266
|
**idw_kwargs,
|
241
267
|
):
|
242
|
-
"""Combine multiple tide models into a single locally optimised
|
243
|
-
|
244
|
-
|
245
|
-
|
268
|
+
"""Combine multiple tide models into a single locally optimised ensemble tide model.
|
269
|
+
|
270
|
+
Uses external model ranking data (e.g. satellite altimetry or
|
271
|
+
NDWI-tide correlations along the coastline) to inform the
|
272
|
+
selection of the best local models.
|
246
273
|
|
247
274
|
This function performs the following steps:
|
248
275
|
|
@@ -313,11 +340,12 @@ def ensemble_tides(
|
|
313
340
|
"""
|
314
341
|
# Raise data if `tide_df` provided in wide format
|
315
342
|
if "tide_model" not in tide_df:
|
316
|
-
|
343
|
+
err_msg = (
|
317
344
|
"`tide_df` does not contain the expected 'tide_model' and "
|
318
345
|
"'tide_height' columns. Ensure that tides were modelled in "
|
319
|
-
"long format (i.e. `output_format='long'` in `model_tides`)."
|
346
|
+
"long format (i.e. `output_format='long'` in `model_tides`).",
|
320
347
|
)
|
348
|
+
raise Exception(err_msg)
|
321
349
|
|
322
350
|
# Extract x and y coords from dataframe
|
323
351
|
x = tide_df.index.get_level_values(level="x")
|
@@ -333,7 +361,8 @@ def ensemble_tides(
|
|
333
361
|
gpd.read_file(ranking_points, engine="pyogrio")
|
334
362
|
.to_crs(crs)
|
335
363
|
.query(f"valid_perc > {ranking_valid_perc}")
|
336
|
-
.dropna(how="all")
|
364
|
+
.dropna(how="all")
|
365
|
+
.filter(model_ranking_cols + ["geometry"]) # noqa: RUF005
|
337
366
|
)
|
338
367
|
except KeyError:
|
339
368
|
error_msg = f"""
|
@@ -435,6 +464,7 @@ def model_tides(
|
|
435
464
|
crop: bool | str = "auto",
|
436
465
|
crop_buffer: float | None = 5,
|
437
466
|
append_node: bool = False,
|
467
|
+
constituents: list[str] | None = None,
|
438
468
|
parallel: bool = True,
|
439
469
|
parallel_splits: int | str = "auto",
|
440
470
|
parallel_max: int | None = None,
|
@@ -442,9 +472,7 @@ def model_tides(
|
|
442
472
|
extra_databases: str | os.PathLike | list | None = None,
|
443
473
|
**ensemble_kwargs,
|
444
474
|
) -> pd.DataFrame:
|
445
|
-
"""
|
446
|
-
Model tide heights at multiple coordinates and/or timesteps
|
447
|
-
using using one or more ocean tide models.
|
475
|
+
"""Model tide heights at multiple coordinates or timesteps using using multiple ocean tide models.
|
448
476
|
|
449
477
|
This function is parallelised to improve performance, and
|
450
478
|
supports all tidal models supported by `pyTMD`, including:
|
@@ -457,33 +485,30 @@ def model_tides(
|
|
457
485
|
- Technical University of Denmark tide models (DTU23)
|
458
486
|
|
459
487
|
This function requires access to tide model data files.
|
460
|
-
|
461
|
-
|
462
|
-
<https://geoscienceaustralia.github.io/eo-tides/setup/>
|
463
|
-
<https://pytmd.readthedocs.io/en/latest/getting_started/Getting-Started.html#directories>
|
488
|
+
For tide model setup instructions, refer to the guide:
|
489
|
+
https://geoscienceaustralia.github.io/eo-tides/setup/
|
464
490
|
|
465
491
|
This function is a modification of the `pyTMD` package's
|
466
492
|
`pyTMD.compute.tide_elevations` function. For more info:
|
467
|
-
|
493
|
+
https://pytmd.readthedocs.io/en/latest/api_reference/compute.html#pyTMD.compute.tide_elevations
|
494
|
+
https://pytmd.readthedocs.io/en/latest/getting_started/Getting-Started.html#directories
|
468
495
|
|
469
496
|
Parameters
|
470
497
|
----------
|
471
|
-
x : float or list of
|
472
|
-
One or more x coordinates
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
be in "EPSG:4326" WGS84 degrees latitude; use "crs" if they
|
480
|
-
are in a custom coordinate reference system.
|
498
|
+
x : float or list of floats
|
499
|
+
One or more x coordinates at which to model tides. Assumes
|
500
|
+
degrees longitude (EPSG:4326) by default; use `crs` to specify
|
501
|
+
a different coordinate reference system.
|
502
|
+
y : float or list of floats
|
503
|
+
One or more y coordinates at which to model tides. Assumes
|
504
|
+
degrees latitude (EPSG:4326) by default; use `crs` to specify
|
505
|
+
a different coordinate reference system.
|
481
506
|
time : DatetimeLike
|
482
|
-
|
483
|
-
any format
|
484
|
-
|
485
|
-
|
486
|
-
|
507
|
+
One or more UTC times at which to model tide heights. Accepts
|
508
|
+
any time format compatible with `pandas.to_datetime()`, e.g.
|
509
|
+
datetime.datetime, pd.Timestamp, pd.DatetimeIndex, numpy.datetime64,
|
510
|
+
or date/time strings (e.g. "2020-01-01 23:00"). For example:
|
511
|
+
`time = pd.date_range(start="2000", end="2001", freq="5h")`.
|
487
512
|
model : str or list of str, optional
|
488
513
|
The tide model (or list of models) to use to model tides.
|
489
514
|
Defaults to "EOT20"; specify "all" to use all models available
|
@@ -497,65 +522,67 @@ def model_tides(
|
|
497
522
|
model that match the structure required by `pyTMD`
|
498
523
|
(<https://geoscienceaustralia.github.io/eo-tides/setup/>).
|
499
524
|
crs : str, optional
|
500
|
-
Input coordinate reference system for x
|
501
|
-
Defaults to "EPSG:4326" (
|
525
|
+
Input coordinate reference system for x/y coordinates.
|
526
|
+
Defaults to "EPSG:4326" (degrees latitude, longitude).
|
502
527
|
mode : str, optional
|
503
|
-
|
504
|
-
|
505
|
-
- "one-to-many"
|
506
|
-
every
|
507
|
-
want to model tides
|
508
|
-
|
509
|
-
|
510
|
-
- "one-to-one"
|
511
|
-
|
512
|
-
|
528
|
+
Tide modelling analysis mode. Supports two options:
|
529
|
+
|
530
|
+
- `"one-to-many"`: Models tides at every x/y coordinate for
|
531
|
+
every timestep in `time`. This is useful for Earth observation
|
532
|
+
workflows where you want to model tides at many spatial points
|
533
|
+
for a common set of acquisition times (e.g. satellite overpasses).
|
534
|
+
|
535
|
+
- `"one-to-one"`: Model tides using one timestep for each x/y
|
536
|
+
coordinate. In this mode, the number of x/y coordinates must
|
537
|
+
match the number of timesteps in `time`.
|
513
538
|
output_format : str, optional
|
514
539
|
Whether to return the output dataframe in long format (with
|
515
540
|
results stacked vertically along "tide_model" and "tide_height"
|
516
541
|
columns), or wide format (with a column for each tide model).
|
517
542
|
Defaults to "long".
|
518
543
|
output_units : str, optional
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
544
|
+
Units for the returned tide heights. Options are:
|
545
|
+
|
546
|
+
- `"m"` (default): floating point values in metres
|
547
|
+
- `"cm"`: integer values in centimetres (x100)
|
548
|
+
- `"mm"`: integer values in millimetres (x1000)
|
549
|
+
|
550
|
+
Using integer units can help reduce memory usage.
|
525
551
|
method : str, optional
|
526
|
-
Method used to interpolate
|
527
|
-
|
552
|
+
Method used to interpolate tide model constituent files.
|
553
|
+
Defaults to "linear"; options include:
|
528
554
|
|
529
|
-
- "linear"
|
530
|
-
- "spline"
|
531
|
-
- "bilinear"
|
555
|
+
- `"linear"`, `"nearest"`: scipy regular grid interpolations
|
556
|
+
- `"spline"`: scipy bivariate spline interpolation
|
557
|
+
- `"bilinear"`: quick bilinear interpolation
|
532
558
|
extrapolate : bool, optional
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
559
|
+
If True (default), extrapolate tides inland of the valid tide
|
560
|
+
model extent using nearest-neighbor interpolation. This can
|
561
|
+
ensure tide are returned everywhere, but accuracy may degrade
|
562
|
+
with distance from the valid model extent (e.g. inland or along
|
563
|
+
complex estuaries or rivers). Set `cutoff` to define the
|
564
|
+
maximum extrapolation distance.
|
539
565
|
cutoff : float, optional
|
540
|
-
|
541
|
-
|
542
|
-
|
566
|
+
Maximum distance in kilometres to extrapolate tides inland of the
|
567
|
+
valid tide model extent. The default of None allows extrapolation
|
568
|
+
at any (i.e. infinite) distance.
|
543
569
|
crop : bool or str, optional
|
544
|
-
Whether to crop tide model
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
customise the buffer distance used to crop the files.
|
570
|
+
Whether to crop tide model files on-the-fly to improve performance.
|
571
|
+
Defaults to "auto", which enables cropping when supported (some
|
572
|
+
clipped model files limited to the western hemisphere may not support
|
573
|
+
on-the-fly cropping). Use `crop_buffer` to adjust the buffer
|
574
|
+
distance used for cropping.
|
550
575
|
crop_buffer : int or float, optional
|
551
|
-
The buffer distance in degrees
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
append_node: bool, optional
|
576
|
+
The buffer distance in degrees to crop tide model files around the
|
577
|
+
requested x/y coordinates. Defaults to 5, which will crop model
|
578
|
+
files using a five degree buffer.
|
579
|
+
append_node : bool, optional
|
556
580
|
Apply adjustments to harmonic constituents to allow for periodic
|
557
581
|
modulations over the 18.6-year nodal period (lunar nodal tide).
|
558
582
|
Default is False.
|
583
|
+
constituents : list, optional
|
584
|
+
Optional list of tide constituents to use for tide prediction.
|
585
|
+
Default is None, which will use all available constituents.
|
559
586
|
parallel : bool, optional
|
560
587
|
Whether to parallelise tide modelling. If multiple tide models
|
561
588
|
are requested, these will be run in parallel. If enough workers
|
@@ -585,7 +612,7 @@ def model_tides(
|
|
585
612
|
See: https://pytmd.readthedocs.io/en/latest/getting_started/Getting-Started.html#model-database
|
586
613
|
**ensemble_kwargs :
|
587
614
|
Keyword arguments used to customise the generation of optional
|
588
|
-
ensemble tide models if "ensemble" modelling
|
615
|
+
ensemble tide models if "ensemble" tide modelling is requested.
|
589
616
|
These are passed to the underlying `_ensemble_model` function.
|
590
617
|
Useful parameters include `ranking_points` (path to model
|
591
618
|
rankings data), `k` (for controlling how model rankings are
|
@@ -604,26 +631,43 @@ def model_tides(
|
|
604
631
|
time = _standardise_time(time)
|
605
632
|
|
606
633
|
# Validate input arguments
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
if
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
634
|
+
if time is None:
|
635
|
+
err_msg = "Times for modelling tides must be provided via `time`."
|
636
|
+
raise ValueError(err_msg)
|
637
|
+
|
638
|
+
if method not in ("bilinear", "spline", "linear", "nearest"):
|
639
|
+
err_msg = (
|
640
|
+
f"Invalid interpolation method '{method}'. Must be one of 'bilinear', 'spline', 'linear', or 'nearest'."
|
641
|
+
)
|
642
|
+
raise ValueError(err_msg)
|
643
|
+
|
644
|
+
if output_units not in ("m", "cm", "mm"):
|
645
|
+
err_msg = "Output units must be either 'm', 'cm', or 'mm'."
|
646
|
+
raise ValueError(err_msg)
|
647
|
+
|
648
|
+
if output_format not in ("long", "wide"):
|
649
|
+
err_msg = "Output format must be either 'long' or 'wide'."
|
650
|
+
raise ValueError(err_msg)
|
651
|
+
|
652
|
+
if not np.issubdtype(x.dtype, np.number):
|
653
|
+
err_msg = "`x` must contain only valid numeric values, and must not be None."
|
654
|
+
raise TypeError(err_msg)
|
655
|
+
|
656
|
+
if not np.issubdtype(y.dtype, np.number):
|
657
|
+
err_msg = "`y` must contain only valid numeric values, and must not be None."
|
658
|
+
raise TypeError(err_msg)
|
659
|
+
|
660
|
+
if len(x) != len(y):
|
661
|
+
err_msg = "`x` and `y` must be the same length."
|
662
|
+
raise ValueError(err_msg)
|
663
|
+
|
664
|
+
if mode == "one-to-one" and len(x) != len(time):
|
665
|
+
err_msg = (
|
666
|
+
"The number of supplied `x` and `y` points and `time` values must be "
|
667
|
+
"identical in 'one-to-one' mode. Use 'one-to-many' mode if you intended "
|
668
|
+
"to model multiple timesteps at each point."
|
626
669
|
)
|
670
|
+
raise ValueError(err_msg)
|
627
671
|
|
628
672
|
# Set tide modelling files directory. If no custom path is
|
629
673
|
# provided, try global environment variable.
|
@@ -652,6 +696,7 @@ def model_tides(
|
|
652
696
|
crop=crop,
|
653
697
|
crop_buffer=crop_buffer,
|
654
698
|
append_node=append_node,
|
699
|
+
constituents=constituents,
|
655
700
|
extra_databases=extra_databases,
|
656
701
|
)
|
657
702
|
|
@@ -666,15 +711,16 @@ def model_tides(
|
|
666
711
|
)
|
667
712
|
|
668
713
|
# Verify that parallel splits are not larger than number of points
|
669
|
-
assert isinstance(parallel_splits, int)
|
714
|
+
assert isinstance(parallel_splits, int) # noqa: S101
|
670
715
|
if parallel_splits > len(x):
|
671
|
-
|
716
|
+
err_msg = f"Parallel splits ({parallel_splits}) cannot be larger than the number of points ({len(x)})."
|
717
|
+
raise ValueError(err_msg)
|
672
718
|
|
673
719
|
# Parallelise if either multiple models or multiple splits requested
|
674
720
|
if parallel & ((len(models_to_process) > 1) | (parallel_splits > 1)):
|
675
721
|
with ProcessPoolExecutor(max_workers=parallel_max) as executor:
|
676
722
|
print(
|
677
|
-
f"Modelling tides with {', '.join(models_to_process)} in parallel (models: {len(models_to_process)}, splits: {parallel_splits})"
|
723
|
+
f"Modelling tides with {', '.join(models_to_process)} in parallel (models: {len(models_to_process)}, splits: {parallel_splits})",
|
678
724
|
)
|
679
725
|
|
680
726
|
# Optionally split lon/lat points into `splits_n` chunks
|
@@ -691,6 +737,7 @@ def model_tides(
|
|
691
737
|
if mode == "one-to-many":
|
692
738
|
model_iters, x_iters, y_iters = zip(
|
693
739
|
*[(m, x_split[i], y_split[i]) for m in models_to_process for i in range(parallel_splits)],
|
740
|
+
strict=False,
|
694
741
|
)
|
695
742
|
time_iters = [time] * len(model_iters)
|
696
743
|
elif mode == "one-to-one":
|
@@ -701,22 +748,29 @@ def model_tides(
|
|
701
748
|
for m in models_to_process
|
702
749
|
for i in range(parallel_splits)
|
703
750
|
],
|
751
|
+
strict=False,
|
704
752
|
)
|
705
753
|
|
706
754
|
# Apply func in parallel, iterating through each input param
|
707
755
|
try:
|
708
756
|
model_outputs = list(
|
709
757
|
tqdm(
|
710
|
-
executor.map(
|
758
|
+
executor.map(
|
759
|
+
iter_func,
|
760
|
+
model_iters,
|
761
|
+
x_iters,
|
762
|
+
y_iters,
|
763
|
+
time_iters,
|
764
|
+
),
|
711
765
|
total=len(model_iters),
|
712
766
|
),
|
713
767
|
)
|
714
768
|
except BrokenProcessPool:
|
715
|
-
|
769
|
+
err_msg = (
|
716
770
|
"Parallelised tide modelling failed, likely to to an out-of-memory error. "
|
717
771
|
"Try reducing the size of your analysis, or set `parallel=False`."
|
718
772
|
)
|
719
|
-
raise RuntimeError(
|
773
|
+
raise RuntimeError(err_msg) from None
|
720
774
|
|
721
775
|
# Model tides in series if parallelisation is off
|
722
776
|
else:
|
@@ -736,20 +790,27 @@ def model_tides(
|
|
736
790
|
|
737
791
|
# Update requested models with any custom ensemble models, then
|
738
792
|
# filter the dataframe to keep only models originally requested
|
739
|
-
models_requested = list(
|
740
|
-
|
793
|
+
models_requested = list(
|
794
|
+
np.union1d(models_requested, ensemble_df.tide_model.unique()),
|
795
|
+
)
|
796
|
+
tide_df = pd.concat([tide_df, ensemble_df]).query(
|
797
|
+
"tide_model in @models_requested",
|
798
|
+
)
|
741
799
|
|
742
800
|
# Optionally convert to a wide format dataframe with a tide model in
|
743
801
|
# each dataframe column
|
744
802
|
if output_format == "wide":
|
745
803
|
# Pivot into wide format with each time model as a column
|
746
804
|
print("Converting to a wide format dataframe")
|
747
|
-
tide_df = tide_df.pivot(columns="tide_model", values="tide_height")
|
805
|
+
tide_df = tide_df.pivot(columns="tide_model", values="tide_height") # noqa: PD010
|
748
806
|
|
749
807
|
# If in 'one-to-one' mode, reindex using our input time/x/y
|
750
808
|
# values to ensure the output is sorted the same as our inputs
|
751
809
|
if mode == "one-to-one":
|
752
|
-
output_indices = pd.MultiIndex.from_arrays(
|
810
|
+
output_indices = pd.MultiIndex.from_arrays(
|
811
|
+
[time, x, y],
|
812
|
+
names=["time", "x", "y"],
|
813
|
+
)
|
753
814
|
tide_df = tide_df.reindex(output_indices)
|
754
815
|
|
755
816
|
return tide_df
|
@@ -765,41 +826,39 @@ def model_phases(
|
|
765
826
|
return_tides: bool = False,
|
766
827
|
**model_tides_kwargs,
|
767
828
|
) -> pd.DataFrame:
|
768
|
-
"""
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
small time offset (by default, 15 minutes). If tides
|
777
|
-
increased over this period, they are assigned as "flow";
|
778
|
-
if they decreased, they are assigned as "ebb".
|
829
|
+
"""Model tide phases at multiple coordinates or timesteps using multiple ocean tide models.
|
830
|
+
|
831
|
+
Ebb and low phases (low-flow, high-flow, high-ebb, low-ebb)
|
832
|
+
are calculated by running the `eo_tides.model.model_tides`
|
833
|
+
function twice, once for the requested timesteps, and again
|
834
|
+
after subtracting a small time offset (15 mins by default).
|
835
|
+
If tides increased over this period, they are assigned as
|
836
|
+
"flow"; if they decreased, they are assigned as "ebb".
|
779
837
|
Tides are considered "high" if equal or greater than 0
|
780
838
|
metres tide height, otherwise "low".
|
781
839
|
|
782
840
|
This function supports all parameters that are supported
|
783
841
|
by `model_tides`.
|
784
842
|
|
843
|
+
For tide model setup instructions, refer to the guide:
|
844
|
+
https://geoscienceaustralia.github.io/eo-tides/setup/
|
845
|
+
|
785
846
|
Parameters
|
786
847
|
----------
|
787
|
-
x : float or list of
|
788
|
-
One or more x coordinates
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
should be in "EPSG:4326" WGS84 degrees latitude; use "crs"
|
796
|
-
if they are in a custom coordinate reference system.
|
848
|
+
x : float or list of floats
|
849
|
+
One or more x coordinates at which to model tides. Assumes
|
850
|
+
degrees longitude (EPSG:4326) by default; use `crs` to specify
|
851
|
+
a different coordinate reference system.
|
852
|
+
y : float or list of floats
|
853
|
+
One or more y coordinates at which to model tides. Assumes
|
854
|
+
degrees latitude (EPSG:4326) by default; use `crs` to specify
|
855
|
+
a different coordinate reference system.
|
797
856
|
time : DatetimeLike
|
798
|
-
|
799
|
-
any format
|
800
|
-
|
801
|
-
|
802
|
-
|
857
|
+
One or more UTC times at which to model tide heights. Accepts
|
858
|
+
any time format compatible with `pandas.to_datetime()`, e.g.
|
859
|
+
datetime.datetime, pd.Timestamp, pd.DatetimeIndex, numpy.datetime64,
|
860
|
+
or date/time strings (e.g. "2020-01-01 23:00"). For example:
|
861
|
+
`time = pd.date_range(start="2000", end="2001", freq="5h")`.
|
803
862
|
model : str or list of str, optional
|
804
863
|
The tide model (or list of models) to use to model tides.
|
805
864
|
Defaults to "EOT20"; specify "all" to use all models available
|
@@ -832,7 +891,6 @@ def model_phases(
|
|
832
891
|
A dataframe containing modelled tide phases.
|
833
892
|
|
834
893
|
"""
|
835
|
-
|
836
894
|
# Pop output format and mode for special handling
|
837
895
|
output_format = model_tides_kwargs.pop("output_format", "long")
|
838
896
|
mode = model_tides_kwargs.pop("mode", "one-to-many")
|
@@ -862,7 +920,9 @@ def model_phases(
|
|
862
920
|
# Compare tides computed for each timestep. If the previous tide
|
863
921
|
# was higher than the current tide, the tide is 'ebbing'. If the
|
864
922
|
# previous tide was lower, the tide is 'flowing'
|
865
|
-
ebb_flow = (tide_df.tide_height < pre_df.tide_height.
|
923
|
+
ebb_flow = (tide_df.tide_height < pre_df.tide_height.to_numpy()).replace(
|
924
|
+
{True: "ebb", False: "flow"},
|
925
|
+
)
|
866
926
|
|
867
927
|
# If tides are greater than 0, then "high", otherwise "low"
|
868
928
|
high_low = (tide_df.tide_height >= 0).replace({True: "high", False: "low"})
|
@@ -875,12 +935,15 @@ def model_phases(
|
|
875
935
|
if output_format == "wide":
|
876
936
|
# Pivot into wide format with each time model as a column
|
877
937
|
print("Converting to a wide format dataframe")
|
878
|
-
tide_df = tide_df.pivot(columns="tide_model")
|
938
|
+
tide_df = tide_df.pivot(columns="tide_model") # noqa: PD010
|
879
939
|
|
880
940
|
# If in 'one-to-one' mode, reindex using our input time/x/y
|
881
941
|
# values to ensure the output is sorted the same as our inputs
|
882
942
|
if mode == "one-to-one":
|
883
|
-
output_indices = pd.MultiIndex.from_arrays(
|
943
|
+
output_indices = pd.MultiIndex.from_arrays(
|
944
|
+
[time, x, y],
|
945
|
+
names=["time", "x", "y"],
|
946
|
+
)
|
884
947
|
tide_df = tide_df.reindex(output_indices)
|
885
948
|
|
886
949
|
# Optionally drop tides
|