Skip to content

timedelta_to_float fails on cftime._cftime.Datetime360Day calendars #2486

@erikvansebille

Description

@erikvansebille

While working on #2483, I ran into an issue in our timedelta_to_float function in time.py, which can't handle the array below

[datetime.timedelta(0) datetime.timedelta(days=1)
 datetime.timedelta(days=2) datetime.timedelta(days=3)
 datetime.timedelta(days=4) datetime.timedelta(days=5)
 datetime.timedelta(days=6) datetime.timedelta(days=7)
 datetime.timedelta(days=8) datetime.timedelta(days=9)]

The following test breaks

data_folder = parcels.download_example_dataset("MITgcm_example_data")
ds_fields = xr.open_dataset(data_folder / "mitgcm_UV_surface_zonally_reentrant.nc")

# t = ds_fields["time"].values
# secs = np.array([(ti - t[0]).total_seconds() for ti in t])
# td_ns = np.rint(secs * 1e9).astype("int64").astype("timedelta64[ns]")
# ds_fields = ds_fields.assign_coords(time=td_ns)

coords = ds_fields[["XG", "YG", "Zl", "time"]]
ds_fset = convert.mitgcm_to_sgrid(fields={"U": ds_fields.UVEL, "V": ds_fields.VVEL}, coords=coords)
fieldset = FieldSet.from_sgrid_conventions(ds_fset)

npart = 10
lon = [24e3] * npart
lat = np.linspace(22e3, 1950e3, npart)

pset = parcels.ParticleSet(fieldset, lon=lon, lat=lat)
pset.execute(AdvectionRK4, runtime=np.timedelta64(5, "D"), dt=np.timedelta64(30, "m"))

with error

src/parcels/_core/particleset.py:463: in execute
    self._kernel.execute(self, endtime=next_time, dt=dt)
src/parcels/_core/kernel.py:256: in execute
    f(pset[evaluate_particles], self._fieldset)
src/parcels/kernels/_advection.py:61: in AdvectionRK4
    (u1, v1) = fieldset.UV[particles]
               ^^^^^^^^^^^^^^^^^^^^^^
src/parcels/_core/field.py:337: in __getitem__
    return self.eval(key.time, key.z, key.lat, key.lon, key)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/parcels/_core/field.py:322: in eval
    particle_positions, grid_positions = _get_positions(self.U, time, z, y, x, particles, _ei)
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/parcels/_core/field.py:468: in _get_positions
    grid_positions.update(_search_time_index(field, time))
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/parcels/_core/index_search.py:89: in _search_time_index
    time_flt = timedelta_to_float(field.data.time.data - field.time_interval.left)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

dt = array([datetime.timedelta(0), datetime.timedelta(days=1),
       datetime.timedelta(days=2), datetime.timedelta(days=3...ays=6), datetime.timedelta(days=7),
       datetime.timedelta(days=8), datetime.timedelta(days=9)],
      dtype=object)

    def timedelta_to_float(dt: float | timedelta | np.timedelta64) -> float:
        """Convert a timedelta to a float in seconds."""
        print(dt)
        if isinstance(dt, timedelta):
            return dt.total_seconds()
        if isinstance(dt, np.timedelta64):
            return float(dt / np.timedelta64(1, "s"))
        if hasattr(dt, "dtype") and np.issubdtype(dt.dtype, np.timedelta64):  # in case of array
            return (dt / np.timedelta64(1, "s")).astype(float)
>       return float(dt)
               ^^^^^^^^^
E       TypeError: only length-1 arrays can be converted to Python scalars

src/parcels/_core/utils/time.py:168: TypeError

Note that the code does work with the commented-out lines (so updating ds_fields["time"]), which originally is

<xarray.DataArray 'time' (time: 10)> Size: 80B
array([cftime.Datetime360Day(2000, 1, 2, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 3, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 4, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 5, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 6, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 7, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 8, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 9, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 10, 0, 0, 0, 0, has_year_zero=True),
       cftime.Datetime360Day(2000, 1, 11, 0, 0, 0, 0, has_year_zero=True)],
      dtype=object)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions