Skip to content

Commit 4cc5d0a

Browse files
Removing time_origin for v4
As time will become a datetime64?timedelta64 object, no need anymore for time_origin and TimeConverter
1 parent 399ae10 commit 4cc5d0a

7 files changed

Lines changed: 4 additions & 217 deletions

File tree

parcels/particlefile.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,6 @@ def fname(self):
150150
def vars_to_write(self):
151151
return self._vars_to_write
152152

153-
@property
154-
def time_origin(self):
155-
return self.particleset.time_origin
156-
157153
def _create_variables_attribute_dict(self):
158154
"""Creates the dictionary with variable attributes.
159155
@@ -173,9 +169,8 @@ def _create_variables_attribute_dict(self):
173169
"lat": {"long_name": "", "standard_name": "latitude", "units": "degrees_north", "axis": "Y"},
174170
}
175171

176-
if self.time_origin.calendar is not None:
177-
attrs["time"]["units"] = "seconds since " + str(self.time_origin)
178-
attrs["time"]["calendar"] = _set_calendar(self.time_origin.calendar)
172+
attrs["time"]["units"] = "seconds since " # TODO fix units
173+
attrs["time"]["calendar"] = "None" # TODO fix calendar
179174

180175
for vname in self.vars_to_write:
181176
if vname not in ["time", "lat", "lon", "depth", "id"]:

parcels/particleset.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,14 @@
1717
from parcels.particle import Particle
1818
from parcels.particledata import ParticleData, ParticleDataIterator
1919
from parcels.particlefile import ParticleFile
20-
from parcels.tools.converters import _get_cftime_calendars, convert_to_flat_array
20+
from parcels.tools.converters import convert_to_flat_array
2121
from parcels.tools.loggers import logger
2222
from parcels.tools.statuscodes import StatusCode
2323
from parcels.tools.warnings import ParticleSetWarning
2424

2525
__all__ = ["ParticleSet"]
2626

2727

28-
def _convert_to_reltime(time):
29-
"""Check to determine if the value of the time parameter needs to be converted to a relative value (relative to the time_origin)."""
30-
if isinstance(time, np.datetime64) or (hasattr(time, "calendar") and time.calendar in _get_cftime_calendars()):
31-
return True
32-
return False
33-
34-
3528
class ParticleSet:
3629
"""Class for storing particle and executing kernel over them.
3730
@@ -126,7 +119,6 @@ def __init__(
126119
else:
127120
raise NotImplementedError("particle time must be a datetime or date object")
128121

129-
time = np.array([self.time_origin.reltime(t) if _convert_to_reltime(t) else t for t in time])
130122
assert lon.size == time.size, "time and positions (lon, lat, depth) do not have the same lengths."
131123
if fieldset.time_interval:
132124
_warn_particle_times_outside_fieldset_time_bounds(time, fieldset.time_interval)

parcels/tools/converters.py

Lines changed: 0 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
import inspect
4-
from datetime import timedelta
54
from math import cos, pi
65

76
import cftime
@@ -13,7 +12,6 @@
1312
"GeographicPolar",
1413
"GeographicPolarSquare",
1514
"GeographicSquare",
16-
"TimeConverter",
1715
"UnitConverter",
1816
"convert_to_flat_array",
1917
"unitconverters_map",
@@ -42,122 +40,6 @@ def _get_cftime_calendars() -> list[str]:
4240
return [getattr(cftime, cf_datetime)(1990, 1, 1).calendar for cf_datetime in _get_cftime_datetimes()]
4341

4442

45-
class TimeConverter:
46-
"""Converter class for dates with different calendars in FieldSets
47-
48-
Parameters
49-
----------
50-
time_origin : float, integer, numpy.datetime64 or cftime.DatetimeNoLeap
51-
time origin of the class.
52-
"""
53-
54-
def __init__(self, time_origin: float | np.datetime64 | np.timedelta64 | cftime.datetime = 0):
55-
self.time_origin = time_origin
56-
self.calendar: str | None = None
57-
if isinstance(time_origin, np.datetime64):
58-
self.calendar = "np_datetime64"
59-
elif isinstance(time_origin, np.timedelta64):
60-
self.calendar = "np_timedelta64"
61-
elif isinstance(time_origin, cftime.datetime):
62-
self.calendar = time_origin.calendar
63-
64-
def reltime(self, time: TimeConverter | np.datetime64 | np.timedelta64 | cftime.datetime) -> float | npt.NDArray:
65-
"""Method to compute the difference, in seconds, between a time and the time_origin
66-
of the TimeConverter
67-
68-
Parameters
69-
----------
70-
time :
71-
72-
73-
Returns
74-
-------
75-
type
76-
time - self.time_origin
77-
78-
"""
79-
time = time.time_origin if isinstance(time, TimeConverter) else time
80-
if self.calendar in ["np_datetime64", "np_timedelta64"]:
81-
return (time - self.time_origin) / np.timedelta64(1, "s") # type: ignore
82-
elif self.calendar in _get_cftime_calendars():
83-
if isinstance(time, (list, np.ndarray)):
84-
try:
85-
return np.array([(t - self.time_origin).total_seconds() for t in time]) # type: ignore
86-
except ValueError:
87-
raise ValueError(
88-
f"Cannot subtract 'time' (a {type(time)} object) from a {self.calendar} calendar.\n"
89-
f"Provide 'time' as a {type(self.time_origin)} object?"
90-
)
91-
else:
92-
try:
93-
return (time - self.time_origin).total_seconds() # type: ignore
94-
except ValueError:
95-
raise ValueError(
96-
f"Cannot subtract 'time' (a {type(time)} object) from a {self.calendar} calendar.\n"
97-
f"Provide 'time' as a {type(self.time_origin)} object?"
98-
)
99-
elif self.calendar is None:
100-
return time - self.time_origin # type: ignore
101-
else:
102-
raise RuntimeError(f"Calendar {self.calendar} not implemented in TimeConverter")
103-
104-
def fulltime(self, time):
105-
"""Method to convert a time difference in seconds to a date, based on the time_origin
106-
107-
Parameters
108-
----------
109-
time :
110-
111-
112-
Returns
113-
-------
114-
type
115-
self.time_origin + time
116-
117-
"""
118-
time = time.time_origin if isinstance(time, TimeConverter) else time
119-
if self.calendar in ["np_datetime64", "np_timedelta64"]:
120-
if isinstance(time, (list, np.ndarray)):
121-
return [self.time_origin + np.timedelta64(int(t), "s") for t in time]
122-
else:
123-
return self.time_origin + np.timedelta64(int(time), "s")
124-
elif self.calendar in _get_cftime_calendars():
125-
return self.time_origin + timedelta(seconds=time)
126-
elif self.calendar is None:
127-
return self.time_origin + time
128-
else:
129-
raise RuntimeError(f"Calendar {self.calendar} not implemented in TimeConverter")
130-
131-
def __repr__(self):
132-
return f"{self.time_origin}"
133-
134-
def __eq__(self, other):
135-
other = other.time_origin if isinstance(other, TimeConverter) else other
136-
return self.time_origin == other
137-
138-
def __ne__(self, other):
139-
other = other.time_origin if isinstance(other, TimeConverter) else other
140-
if not isinstance(other, type(self.time_origin)):
141-
return True
142-
return self.time_origin != other
143-
144-
def __gt__(self, other):
145-
other = other.time_origin if isinstance(other, TimeConverter) else other
146-
return self.time_origin > other
147-
148-
def __lt__(self, other):
149-
other = other.time_origin if isinstance(other, TimeConverter) else other
150-
return self.time_origin < other
151-
152-
def __ge__(self, other):
153-
other = other.time_origin if isinstance(other, TimeConverter) else other
154-
return self.time_origin >= other
155-
156-
def __le__(self, other):
157-
other = other.time_origin if isinstance(other, TimeConverter) else other
158-
return self.time_origin <= other
159-
160-
16143
class UnitConverter:
16244
"""Interface class for spatial unit conversion during field sampling that performs no conversion."""
16345

parcels/tools/statuscodes.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ class TimeExtrapolationError(RuntimeError):
6969
"""Utility error class to propagate erroneous time extrapolation sampling."""
7070

7171
def __init__(self, time, field=None):
72-
if field is not None and field.grid.time_origin and time is not None:
73-
time = field.grid.time_origin.fulltime(time)
7472
message = (
7573
f"{field.name if field else 'Field'} sampled outside time domain at time {time}."
7674
" Try setting allow_time_extrapolation to True."
@@ -86,23 +84,12 @@ class KernelError(RuntimeError):
8684
"""General particle kernel error with optional custom message."""
8785

8886
def __init__(self, particle, fieldset=None, msg=None):
89-
message = (
90-
f"{particle.state}\n"
91-
f"Particle {particle}\n"
92-
f"Time: {_parse_particletime(particle.time, fieldset)}\n"
93-
f"timestep dt: {particle.dt}\n"
94-
)
87+
message = f"{particle.state}\nParticle {particle}\nTime: {particle.time}\ntimestep dt: {particle.dt}\n"
9588
if msg:
9689
message += msg
9790
super().__init__(message)
9891

9992

100-
def _parse_particletime(time, fieldset):
101-
if fieldset is not None and fieldset.time_origin:
102-
time = fieldset.time_origin.fulltime(time)
103-
return time
104-
105-
10693
AllParcelsErrorCodes = {
10794
FieldSamplingError: StatusCode.Error,
10895
FieldOutOfBoundError: StatusCode.ErrorOutOfBounds,

parcels/xgrid.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from parcels import xgcm
99
from parcels._index_search import _search_indices_curvilinear_2d
1010
from parcels.basegrid import BaseGrid
11-
from parcels.tools.converters import TimeConverter
1211

1312
_XGRID_AXES_ORDERING = "ZYX"
1413
_XGRID_AXES = Literal["X", "Y", "Z"]
@@ -130,16 +129,6 @@ def zdim(self):
130129
def tdim(self):
131130
return get_cell_edge_count_along_dim(self.xgcm_grid.axes.get("T"))
132131

133-
@property
134-
def time_origin(self):
135-
"""
136-
Note
137-
----
138-
Included for compatibility with v3 codebase. May be removed in future.
139-
TODO v4: Evaluate
140-
"""
141-
return TimeConverter(self._datetimes[0])
142-
143132
@property
144133
def _z4d(self) -> Literal[0, 1]:
145134
"""

tests/tools/test_converters.py

Lines changed: 0 additions & 55 deletions
This file was deleted.

tests/v4/test_xgrid.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
GridTestCase(datasets["ds_2d_left"], "ydim", Y),
2323
GridTestCase(datasets["ds_2d_left"], "zdim", Z),
2424
GridTestCase(datasets["ds_2d_left"], "tdim", T),
25-
GridTestCase(datasets["ds_2d_left"], "time_origin", TimeConverter(datasets["ds_2d_left"].time.values[0])),
2625
]
2726

2827

@@ -56,7 +55,6 @@ def test_xgrid_properties_ground_truth(ds, attr, expected):
5655
"ydim",
5756
"zdim",
5857
"tdim",
59-
"time_origin",
6058
"_gtype",
6159
],
6260
)
@@ -69,7 +67,6 @@ def test_xgrid_against_old(ds, attr):
6967
lat=ds.lat.values,
7068
depth=ds.depth.values,
7169
time=ds.time.values.astype("float64") / 1e9,
72-
time_origin=TimeConverter(ds.time.values[0]),
7370
mesh="spherical",
7471
)
7572
actual = getattr(grid, attr)

0 commit comments

Comments
 (0)