diff --git a/src/virtualship/instruments/base.py b/src/virtualship/instruments/base.py index cc406def..f5d285e9 100644 --- a/src/virtualship/instruments/base.py +++ b/src/virtualship/instruments/base.py @@ -2,6 +2,7 @@ import abc import collections +import tempfile from datetime import timedelta from itertools import pairwise from pathlib import Path @@ -183,6 +184,9 @@ def _generate_fieldset(self) -> parcels.FieldSet: Create and combine FieldSets for each variable, supporting both local and Copernicus Marine data sources. N.B. Per variable avoids issues when using copernicusmarine and creating directly one FieldSet of ds's sourced from different Copernicus Marine product IDs (which can also have different temporal resolutions), which is often the case for BGC variables. + + Includes an intermediate step of writing to tmp files, as per https://github.com/Parcels-code/parcels-benchmarks/pull/49 + TODO: the need for this step may be removed as Parcels x copernicusmarine integration improves, tracked in https://github.com/Parcels-code/Parcels/issues/2756 and xref'd in VirtualShip #357 (https://github.com/Parcels-code/virtualship/issues/357) """ fieldsets_list = [] keys = list(self.variables.keys()) @@ -222,11 +226,12 @@ def _generate_fieldset(self) -> parcels.FieldSet: ds["depth"] = -ds["depth"] ds = ds.reindex(depth=ds["depth"][::-1]) - # TODO: update when decision on handling of nans/0s in v4 is made (i.e. https://github.com/Parcels-code/Parcels/issues/2393) + # TODO: to be removed when Parcels #2746 is merged (i.e. https://github.com/Parcels-code/Parcels/pull/2746) ds = ds.fillna(0) fields = {key: ds[field_var_name]} ds_fset = parcels.convert.copernicusmarine_to_sgrid(fields=fields) + ds_fset = self._via_tmp_ds(ds_fset) fs = parcels.FieldSet.from_sgrid_conventions(ds_fset) @@ -253,3 +258,12 @@ def _get_spec_value(self, spec_type: str, key: str, default=None): """Helper to extract a value from spacetime_buffer_size or limit_spec.""" spec = self.spacetime_buffer_size if spec_type == "buffer" else self.limit_spec return spec.get(key) if spec and spec.get(key) is not None else default + + @staticmethod + def _via_tmp_ds(ds) -> xr.Dataset: + """Create and re-load a temporary local dataset.""" + tmpdir = tempfile.TemporaryDirectory() + tmp_fpath = Path(tmpdir.name).joinpath("tmp.nc") + ds.to_netcdf(tmp_fpath) + del ds + return xr.open_dataset(tmp_fpath)