Skip to content

Commit 98c48a7

Browse files
Merge pull request #1880 from OceanParcels/v/time-periodic
Remove `time_periodic`
2 parents 1a2b41c + 93ae75c commit 98c48a7

File tree

10 files changed

+27
-180
lines changed

10 files changed

+27
-180
lines changed

docs/examples/example_globcurrent.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ def set_globcurrent_fieldset(
1313
indices=None,
1414
deferred_load=True,
1515
use_xarray=False,
16-
time_periodic=False,
1716
timestamps=None,
1817
):
1918
if filename is None:
@@ -32,7 +31,9 @@ def set_globcurrent_fieldset(
3231
if use_xarray:
3332
ds = xr.open_mfdataset(filename, combine="by_coords")
3433
return parcels.FieldSet.from_xarray_dataset(
35-
ds, variables, dimensions, time_periodic=time_periodic
34+
ds,
35+
variables,
36+
dimensions,
3637
)
3738
else:
3839
return parcels.FieldSet.from_netcdf(
@@ -41,7 +42,6 @@ def set_globcurrent_fieldset(
4142
dimensions,
4243
indices,
4344
deferred_load=deferred_load,
44-
time_periodic=time_periodic,
4545
timestamps=timestamps,
4646
)
4747

@@ -118,6 +118,8 @@ def test_globcurrent_particles(use_xarray):
118118
assert abs(pset[0].lat - -35.3) < 1
119119

120120

121+
@pytest.mark.v4remove
122+
@pytest.mark.xfail(reason="time_periodic removed in v4")
121123
@pytest.mark.parametrize("rundays", [300, 900])
122124
def test_globcurrent_time_periodic(rundays):
123125
sample_var = []

docs/examples/tutorial_timestamps.ipynb

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,6 @@
155155
" )"
156156
]
157157
},
158-
{
159-
"attachments": {},
160-
"cell_type": "markdown",
161-
"metadata": {},
162-
"source": [
163-
"Note, by the way, that adding the `time_periodic` argument to `Field.from_netcdf()` will also mean that the climatology can be cycled for multiple years.\n"
164-
]
165-
},
166158
{
167159
"attachments": {},
168160
"cell_type": "markdown",

docs/v4/TODO.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ List of tasks that are important to do before the release of version 4 (but can'
55
- [ ] Make migration guide for v3 to v4
66
- [ ] Just prior to release: Update conda feedstock recipe dependencies (remove cgen and compiler dependencies). Make sure that recipe is up-to-date.
77
- [ ] Revamp the oceanparcels.org landing page, and perhaps also consider new logo/branding?
8+
- [ ] Look into xarray and whether users can create periodic datasets without increasing the size of the original dataset (i.e., no compromise alternative to `time_periodic` param in v3). Update docs accordingly.
9+
- [ ] Rerun all the tutorials so that their output is in line with new v4 print statements etc

parcels/_typing.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
77
"""
88

9-
import datetime
109
import os
1110
from collections.abc import Callable
1211
from typing import Any, Literal, get_args
@@ -32,7 +31,6 @@
3231
ChunkMode = Literal["auto", "specific", "failsafe"] # corresponds with `chunk_mode`
3332
GridIndexingType = Literal["pop", "mom5", "mitgcm", "nemo", "croco"] # corresponds with `gridindexingtype`
3433
UpdateStatus = Literal["not_updated", "first_updated", "updated"] # corresponds with `_update_status`
35-
TimePeriodic = float | datetime.timedelta | Literal[False] # corresponds with `time_periodic`
3634
NetcdfEngine = Literal["netcdf4", "xarray", "scipy"]
3735

3836

parcels/field.py

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@
2222
GridIndexingType,
2323
InterpMethod,
2424
Mesh,
25-
TimePeriodic,
2625
VectorType,
2726
assert_valid_gridindexingtype,
2827
assert_valid_interp_method,
2928
)
30-
from parcels.tools._helpers import default_repr, field_repr, timedelta_to_float
29+
from parcels.tools._helpers import default_repr, field_repr
3130
from parcels.tools.converters import (
3231
TimeConverter,
3332
UnitConverter,
@@ -155,10 +154,6 @@ class Field:
155154
allow_time_extrapolation : bool
156155
boolean whether to allow for extrapolation in time
157156
(i.e. beyond the last available time snapshot)
158-
time_periodic : bool, float or datetime.timedelta
159-
To loop periodically over the time component of the Field. It is set to either False or the length of the period (either float in seconds or datetime.timedelta object).
160-
The last value of the time series can be provided (which is the same as the initial one) or not (Default: False)
161-
This flag overrides the allow_time_extrapolation and sets it to False
162157
chunkdims_name_map : str, optional
163158
Gives a name map to the FieldFileBuffer that declared a mapping between chunksize name, NetCDF dimension and Parcels dimension;
164159
required only if currently incompatible OCM field is loaded and chunking is used by 'chunksize' (which is the default)
@@ -174,7 +169,6 @@ class Field:
174169
"""
175170

176171
allow_time_extrapolation: bool
177-
time_periodic: TimePeriodic
178172
_cast_data_dtype: type[np.float32] | type[np.float64]
179173

180174
def __init__(
@@ -196,7 +190,6 @@ def __init__(
196190
time_origin: TimeConverter | None = None,
197191
interp_method: InterpMethod = "linear",
198192
allow_time_extrapolation: bool | None = None,
199-
time_periodic: TimePeriodic = False,
200193
gridindexingtype: GridIndexingType = "nemo",
201194
to_write: bool = False,
202195
**kwargs,
@@ -261,28 +254,6 @@ def __init__(
261254
else:
262255
self.allow_time_extrapolation = allow_time_extrapolation
263256

264-
self.time_periodic = time_periodic
265-
if self.time_periodic is not False and self.allow_time_extrapolation:
266-
warnings.warn(
267-
"allow_time_extrapolation and time_periodic cannot be used together. allow_time_extrapolation is set to False",
268-
FieldSetWarning,
269-
stacklevel=2,
270-
)
271-
self.allow_time_extrapolation = False
272-
if self.time_periodic is True:
273-
raise ValueError(
274-
"Unsupported time_periodic=True. time_periodic must now be either False or the length of the period (either float in seconds or datetime.timedelta object."
275-
)
276-
if self.time_periodic is not False:
277-
self.time_periodic = timedelta_to_float(self.time_periodic)
278-
279-
if not np.isclose(self.grid.time[-1] - self.grid.time[0], self.time_periodic):
280-
if self.grid.time[-1] - self.grid.time[0] > self.time_periodic:
281-
raise ValueError("Time series provided is longer than the time_periodic parameter")
282-
self.grid._add_last_periodic_data_timestep = True
283-
self.grid.time = np.append(self.grid.time, self.grid.time[0] + self.time_periodic)
284-
self.grid.time_full = self.grid.time
285-
286257
self.vmin = vmin
287258
self.vmax = vmax
288259

@@ -312,16 +283,11 @@ def __init__(
312283
if self.vmax is not None:
313284
self.data[self.data > self.vmax] = 0.0
314285

315-
if self.grid._add_last_periodic_data_timestep:
316-
self.data = lib.concatenate((self.data, self.data[:1, :]), axis=0)
317-
318286
self._scaling_factor = None
319287

320288
self._dimensions = kwargs.pop("dimensions", None)
321289
self.indices = kwargs.pop("indices", None)
322290
self._dataFiles = kwargs.pop("dataFiles", None)
323-
if self.grid._add_last_periodic_data_timestep and self._dataFiles is not None:
324-
self._dataFiles = np.append(self._dataFiles, self._dataFiles[0])
325291
self._field_fb_class = kwargs.pop("FieldFileBuffer", None)
326292
self._netcdf_engine = kwargs.pop("netcdf_engine", "netcdf4")
327293
self._creation_log = kwargs.pop("creation_log", "")
@@ -457,7 +423,6 @@ def from_netcdf(
457423
mesh: Mesh = "spherical",
458424
timestamps=None,
459425
allow_time_extrapolation: bool | None = None,
460-
time_periodic: TimePeriodic = False,
461426
deferred_load: bool = True,
462427
**kwargs,
463428
) -> "Field":
@@ -491,9 +456,6 @@ def from_netcdf(
491456
boolean whether to allow for extrapolation in time
492457
(i.e. beyond the last available time snapshot)
493458
Default is False if dimensions includes time, else True
494-
time_periodic : bool, float or datetime.timedelta
495-
boolean whether to loop periodically over the time component of the FieldSet
496-
This flag overrides the allow_time_extrapolation and sets it to False (Default value = False)
497459
deferred_load : bool
498460
boolean whether to only pre-load data (in deferred mode) or
499461
fully load them (default: True). It is advised to deferred load the data, since in
@@ -743,7 +705,6 @@ def from_netcdf(
743705

744706
kwargs["dimensions"] = dimensions.copy()
745707
kwargs["indices"] = indices
746-
kwargs["time_periodic"] = time_periodic
747708
kwargs["netcdf_engine"] = netcdf_engine
748709

749710
return cls(
@@ -764,7 +725,6 @@ def from_xarray(
764725
dimensions,
765726
mesh: Mesh = "spherical",
766727
allow_time_extrapolation: bool | None = None,
767-
time_periodic: TimePeriodic = False,
768728
**kwargs,
769729
):
770730
"""Create field from xarray Variable.
@@ -788,9 +748,6 @@ def from_xarray(
788748
boolean whether to allow for extrapolation in time
789749
(i.e. beyond the last available time snapshot)
790750
Default is False if dimensions includes time, else True
791-
time_periodic : bool, float or datetime.timedelta
792-
boolean whether to loop periodically over the time component of the FieldSet
793-
This flag overrides the allow_time_extrapolation and sets it to False (Default value = False)
794751
**kwargs :
795752
Keyword arguments passed to the :class:`Field` constructor.
796753
"""
@@ -806,7 +763,6 @@ def from_xarray(
806763
time = time_origin.reltime(time) # type: ignore[assignment]
807764

808765
grid = Grid.create_grid(lon, lat, depth, time, time_origin=time_origin, mesh=mesh)
809-
kwargs["time_periodic"] = time_periodic
810766
return cls(
811767
name,
812768
data,
@@ -1001,24 +957,10 @@ def _time_index(self, time):
1001957
Note that we normalize to either the first or the last index
1002958
if the sampled value is outside the time value range.
1003959
"""
1004-
if (
1005-
not self.time_periodic
1006-
and not self.allow_time_extrapolation
1007-
and (time < self.grid.time[0] or time > self.grid.time[-1])
1008-
):
960+
if not self.allow_time_extrapolation and (time < self.grid.time[0] or time > self.grid.time[-1]):
1009961
raise TimeExtrapolationError(time, field=self)
1010962
time_index = self.grid.time <= time
1011-
if self.time_periodic:
1012-
if time_index.all() or np.logical_not(time_index).all():
1013-
periods = int(
1014-
math.floor((time - self.grid.time_full[0]) / (self.grid.time_full[-1] - self.grid.time_full[0]))
1015-
)
1016-
self.grid.periods = periods
1017-
time -= periods * (self.grid.time_full[-1] - self.grid.time_full[0])
1018-
time_index = self.grid.time <= time
1019-
ti = time_index.argmin() - 1 if time_index.any() else 0
1020-
return (ti, periods)
1021-
return (time_index.argmin() - 1 if time_index.any() else 0, 0)
963+
1022964
if time_index.all():
1023965
# If given time > last known field time, use
1024966
# the last field frame without interpolation

0 commit comments

Comments
 (0)