diff --git a/docs/examples/tutorial_kernelloop.ipynb b/docs/examples/tutorial_kernelloop.ipynb index c9fb43fda5..0fff52d811 100644 --- a/docs/examples/tutorial_kernelloop.ipynb +++ b/docs/examples/tutorial_kernelloop.ipynb @@ -118,7 +118,9 @@ " \"V\": \"northward_eulerian_current_velocity\",\n", "}\n", "dimensions = {\"lat\": \"lat\", \"lon\": \"lon\", \"time\": \"time\"}\n", - "fieldset = parcels.FieldSet.from_netcdf(filenames, variables, dimensions)\n", + "fieldset = parcels.FieldSet.from_netcdf(\n", + " filenames, variables, dimensions, allow_time_extrapolation=True\n", + ")\n", "# uppermost layer in the hydrodynamic data\n", "fieldset.mindepth = fieldset.U.depth[0]" ] @@ -137,11 +139,15 @@ " lon=fieldset.U.lon,\n", " lat=fieldset.U.lat,\n", " mesh=\"spherical\",\n", - " fieldtype=\"U\",\n", ")\n", "VWind = parcels.Field(\n", - " \"VWind\", np.zeros((ydim, xdim), dtype=np.float32), grid=UWind.grid, fieldtype=\"V\"\n", + " \"VWind\",\n", + " np.zeros((ydim, xdim), dtype=np.float32),\n", + " grid=UWind.grid,\n", ")\n", + "UWind.units = parcels.tools.converters.GeographicPolar()\n", + "VWind.units = parcels.tools.converters.Geographic()\n", + "\n", "fieldset_wind = parcels.FieldSet(UWind, VWind)\n", "\n", "fieldset.add_field(fieldset_wind.U, name=\"UWind\")\n", diff --git a/docs/examples/tutorial_unitconverters.ipynb b/docs/examples/tutorial_unitconverters.ipynb index 14dd33d388..838c21e5ac 100644 --- a/docs/examples/tutorial_unitconverters.ipynb +++ b/docs/examples/tutorial_unitconverters.ipynb @@ -326,31 +326,6 @@ "print(fieldset.Ustokes[0, 0, 40, -5])\n", "print(fieldset.Ustokes[0, 0, 40, -5] * 1852 * 60 * np.cos(40 * np.pi / 180))" ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternatively, the UnitConverter can be set when the `FieldSet` or `Field` is created by using the `fieldtype` argument (use a dictionary in the case of `FieldSet` construction.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fieldset.add_field(\n", - " parcels.Field(\n", - " \"Ustokes2\",\n", - " np.ones((ydim, xdim), dtype=np.float32),\n", - " grid=fieldset.U.grid,\n", - " fieldtype=\"U\",\n", - " )\n", - ")\n", - "print(fieldset.Ustokes2[0, 0, 40, -5])" - ] } ], "metadata": { diff --git a/parcels/field.py b/parcels/field.py index 761cee0f82..1b40893482 100644 --- a/parcels/field.py +++ b/parcels/field.py @@ -123,8 +123,6 @@ class Field: grid : parcels.grid.Grid :class:`parcels.grid.Grid` object containing all the lon, lat depth, time mesh and time_origin information. Can be constructed from any of the Grid objects - fieldtype : str - Type of Field to be used for UnitConverter (either 'U', 'V', 'Kh_zonal', 'Kh_meridional' or None) time_origin : parcels.tools.converters.TimeConverter Time origin of the time axis (only if grid is None) interp_method : str @@ -148,7 +146,6 @@ def __init__( time=None, grid=None, mesh: Mesh = "flat", - fieldtype=None, time_origin: TimeConverter | None = None, interp_method: InterpMethod = "linear", allow_time_extrapolation: bool | None = None, @@ -170,13 +167,12 @@ def __init__( time_origin = TimeConverter(0) self._grid = Grid.create_grid(lon, lat, depth, time, time_origin=time_origin, mesh=mesh) self.igrid = -1 - self.fieldtype = self.name if fieldtype is None else fieldtype - if self.grid.mesh == "flat" or (self.fieldtype not in unitconverters_map.keys()): - self.units = UnitConverter() - elif self.grid.mesh == "spherical": - self.units = unitconverters_map[self.fieldtype] - else: - raise ValueError("Unsupported mesh type. Choose either: 'spherical' or 'flat'") + self.units = UnitConverter() + if self.grid.mesh == "spherical": + try: + self.units = unitconverters_map[self.name] + except KeyError: + pass if isinstance(interp_method, dict): if self.name in interp_method: self.interp_method = interp_method[self.name] @@ -216,6 +212,16 @@ def __init__( def __repr__(self) -> str: return field_repr(self) + @property + def units(self): + return self._units + + @units.setter + def units(self, value): + if not isinstance(value, UnitConverter): + raise ValueError(f"Units must be a UnitConverter object, got {type(value)}") + self._units = value + @property def grid(self): return self._grid diff --git a/parcels/fieldset.py b/parcels/fieldset.py index cc8707c20a..b6a42bb282 100644 --- a/parcels/fieldset.py +++ b/parcels/fieldset.py @@ -279,7 +279,6 @@ def from_netcdf( filenames, variables, dimensions, - fieldtype=None, mesh: Mesh = "spherical", allow_time_extrapolation: bool | None = None, **kwargs, @@ -305,9 +304,6 @@ def from_netcdf( Note that dimensions can also be a dictionary of dictionaries if dimension names are different for each variable (e.g. dimensions['U'], dimensions['V'], etc). - fieldtype : - Optional dictionary mapping fields to fieldtypes to be used for UnitConverter. - (either 'U', 'V', 'Kh_zonal', 'Kh_meridional' or None) (Default value = None) mesh : str String indicating the type of mesh coordinates and units used during velocity interpolation, see also `this tutorial <../examples/tutorial_unitconverters.ipynb>`__: @@ -353,7 +349,6 @@ def from_netcdf( # Use dimensions[var] if it's a dict of dicts dims = dimensions[var] if var in dimensions else dimensions cls.checkvaliddimensionsdict(dims) - fieldtype = fieldtype[var] if (fieldtype and var in fieldtype) else fieldtype grid = None @@ -364,7 +359,6 @@ def from_netcdf( grid=grid, mesh=mesh, allow_time_extrapolation=allow_time_extrapolation, - fieldtype=fieldtype, **kwargs, ) @@ -426,9 +420,6 @@ def from_nemo( (for indexing details: https://www.nemo-ocean.eu/doc/img360.png ) In 3D, the depth is the one corresponding to W nodes The gridindexingtype is set to 'nemo'. See also the Grid indexing documentation on oceanparcels.org - fieldtype : - Optional dictionary mapping fields to fieldtypes to be used for UnitConverter. - (either 'U', 'V', 'Kh_zonal', 'Kh_meridional' or None) mesh : str String indicating the type of mesh coordinates and units used during velocity interpolation, see also `this tutorial <../examples/tutorial_unitconverters.ipynb>`__: @@ -635,9 +626,6 @@ def from_c_grid_dataset( which are located on the corners of the cells. (for indexing details: https://www.nemo-ocean.eu/doc/img360.png ) In 3D, the depth is the one corresponding to W nodes. - fieldtype : - Optional dictionary mapping fields to fieldtypes to be used for UnitConverter. - (either 'U', 'V', 'Kh_zonal', 'Kh_meridional' or None) mesh : str String indicating the type of mesh coordinates and units used during velocity interpolation, see also `this tutorial <../examples/tutorial_unitconverters.ipynb>`__: @@ -738,9 +726,6 @@ def from_mom5( Note that W is normally directed upward in MOM5, but Parcels requires W in the positive z-direction (downward) so W is multiplied by -1. T node is at the cell centre, and constant per cell. - fieldtype : - Optional dictionary mapping fields to fieldtypes to be used for UnitConverter. - (either 'U', 'V', 'Kh_zonal', 'Kh_meridional' or None) mesh : str String indicating the type of mesh coordinates and units used during velocity interpolation, see also the `Unit converters tutorial <../examples/tutorial_unitconverters.ipynb>`__: @@ -841,9 +826,6 @@ def from_b_grid_dataset( W nodes are at the centre of the horizontal interfaces. They are interpolated linearly (as a function of z) in the cell. T node is at the cell centre, and constant per cell. - fieldtype : - Optional dictionary mapping fields to fieldtypes to be used for UnitConverter. - (either 'U', 'V', 'Kh_zonal', 'Kh_meridional' or None) mesh : str String indicating the type of mesh coordinates and units used during velocity interpolation, see also `this tutorial <../examples/tutorial_unitconverters.ipynb>`__: @@ -908,9 +890,6 @@ def from_xarray_dataset(cls, ds, variables, dimensions, mesh="spherical", allow_ Note that dimensions can also be a dictionary of dictionaries if dimension names are different for each variable (e.g. dimensions['U'], dimensions['V'], etc). - fieldtype : - Optional dictionary mapping fields to fieldtypes to be used for UnitConverter. - (either 'U', 'V', 'Kh_zonal', 'Kh_meridional' or None) mesh : str String indicating the type of mesh coordinates and units used during velocity interpolation, see also `this tutorial <../examples/tutorial_unitconverters.ipynb>`__: diff --git a/tests/test_fieldset.py b/tests/test_fieldset.py index c461d2770f..f81165bfca 100644 --- a/tests/test_fieldset.py +++ b/tests/test_fieldset.py @@ -13,10 +13,6 @@ Variable, ) from parcels.field import Field, VectorField -from parcels.tools.converters import ( - GeographicPolar, - UnitConverter, -) from tests.utils import TEST_DATA @@ -161,31 +157,6 @@ def test_fieldset_from_modulefile(): FieldSet.from_modulefile(nemo_error_fname, modulename="none_returning_function") -def test_field_from_netcdf_fieldtypes(): - filenames = { - "varU": { - "lon": str(TEST_DATA / "mask_nemo_cross_180lon.nc"), - "lat": str(TEST_DATA / "mask_nemo_cross_180lon.nc"), - "data": str(TEST_DATA / "Uu_eastward_nemo_cross_180lon.nc"), - }, - "varV": { - "lon": str(TEST_DATA / "mask_nemo_cross_180lon.nc"), - "lat": str(TEST_DATA / "mask_nemo_cross_180lon.nc"), - "data": str(TEST_DATA / "Vv_eastward_nemo_cross_180lon.nc"), - }, - } - variables = {"varU": "U", "varV": "V"} - dimensions = {"lon": "glamf", "lat": "gphif"} - - # first try without setting fieldtype - fset = FieldSet.from_nemo(filenames, variables, dimensions) - assert isinstance(fset.varU.units, UnitConverter) - - # now try with setting fieldtype - fset = FieldSet.from_nemo(filenames, variables, dimensions, fieldtype={"varU": "U", "varV": "V"}) - assert isinstance(fset.varU.units, GeographicPolar) - - def test_fieldset_from_agrid_dataset(): filenames = { "lon": str(TEST_DATA / "mask_nemo_cross_180lon.nc"), @@ -250,8 +221,8 @@ def test_add_duplicate_field(dupobject): fieldset.add_field(field2) -@pytest.mark.parametrize("fieldtype", ["normal", "vector"]) -def test_add_field_after_pset(fieldtype): +@pytest.mark.parametrize("field_type", ["normal", "vector"]) +def test_add_field_after_pset(field_type): data, dimensions = generate_fieldset_data(100, 100) fieldset = FieldSet.from_data(data, dimensions) pset = ParticleSet(fieldset, Particle, lon=0, lat=0) # noqa ; to trigger fieldset._check_complete @@ -259,9 +230,9 @@ def test_add_field_after_pset(fieldtype): field2 = Field("field2", fieldset.U.data, lon=fieldset.U.lon, lat=fieldset.U.lat) vfield = VectorField("vfield", field1, field2) with pytest.raises(RuntimeError): - if fieldtype == "normal": + if field_type == "normal": fieldset.add_field(field1) - elif fieldtype == "vector": + elif field_type == "vector": fieldset.add_vector_field(vfield)