diff --git a/.github/ci/min_deps_check.py b/.github/ci/min_deps_check.py index ea23ba657f..3a353e724e 100644 --- a/.github/ci/min_deps_check.py +++ b/.github/ci/min_deps_check.py @@ -193,7 +193,7 @@ def main() -> None: print("\nErrors:") print("-------") for i, e in enumerate(errors): - print(f"{i+1}. {e}") + print(f"{i + 1}. {e}") sys.exit(1) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ea0d8e8e7a..e6d03551e3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: types: [text] files: \.(json|ipynb)$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 + rev: v0.12.5 hooks: - id: ruff name: ruff lint (.py) @@ -23,13 +23,13 @@ repos: - id: ruff-format types_or: [python, jupyter] - repo: https://github.com/rbubley/mirrors-prettier # Update mirror as official mirror is deprecated - rev: v3.4.2 + rev: v3.6.2 hooks: - id: prettier # Ruff doesn't have full coverage of pydoclint https://github.com/astral-sh/ruff/issues/12434 - repo: https://github.com/PyCQA/flake8 - rev: 7.1.1 + rev: 7.3.0 hooks: - id: flake8 name: pydoclint diff --git a/docs/examples/example_dask_chunk_OCMs.py b/docs/examples/example_dask_chunk_OCMs.py index 27dd8c2332..c8e4c99573 100644 --- a/docs/examples/example_dask_chunk_OCMs.py +++ b/docs/examples/example_dask_chunk_OCMs.py @@ -377,13 +377,13 @@ def test_swash(mode, chunk_mode): if chunk_mode not in [ "failsafe", ]: - assert len(fieldset.U.grid._load_chunk) == len( - fieldset.V.grid._load_chunk - ), f"U {fieldset.U.grid.chunk_info} vs V {fieldset.V.grid.chunk_info}" + assert len(fieldset.U.grid._load_chunk) == len(fieldset.V.grid._load_chunk), ( + f"U {fieldset.U.grid.chunk_info} vs V {fieldset.V.grid.chunk_info}" + ) if chunk_mode not in ["failsafe", "auto"]: - assert len(fieldset.U.grid._load_chunk) == len( - fieldset.W.grid._load_chunk - ), f"U {fieldset.U.grid.chunk_info} vs W {fieldset.W.grid.chunk_info}" + assert len(fieldset.U.grid._load_chunk) == len(fieldset.W.grid._load_chunk), ( + f"U {fieldset.U.grid.chunk_info} vs W {fieldset.W.grid.chunk_info}" + ) if chunk_mode is False: assert len(fieldset.U.grid._load_chunk) == 1 else: diff --git a/docs/examples/tutorial_NestedFields.ipynb b/docs/examples/tutorial_NestedFields.ipynb index 9ea16da61f..9dc1bc114d 100644 --- a/docs/examples/tutorial_NestedFields.ipynb +++ b/docs/examples/tutorial_NestedFields.ipynb @@ -235,15 +235,13 @@ "pset = parcels.ParticleSet(fieldset, pclass=SampleParticle, lon=[1000], lat=[500])\n", "pset.execute(SampleNestedFieldIndex, runtime=1)\n", "print(\n", - " f\"Particle ({pset[0].lon:g}, {pset[0].lat:g}) \"\n", - " f\"interpolates Field #{int(pset[0].f)}\"\n", + " f\"Particle ({pset[0].lon:g}, {pset[0].lat:g}) interpolates Field #{int(pset[0].f)}\"\n", ")\n", "\n", "pset[0].lon = 10000\n", "pset.execute(SampleNestedFieldIndex, runtime=1)\n", "print(\n", - " f\"Particle ({pset[0].lon:g}, {pset[0].lat:g}) \"\n", - " f\"interpolates Field #{int(pset[0].f)}\"\n", + " f\"Particle ({pset[0].lon:g}, {pset[0].lat:g}) interpolates Field #{int(pset[0].f)}\"\n", ")" ] } diff --git a/docs/examples/tutorial_nemo_3D.ipynb b/docs/examples/tutorial_nemo_3D.ipynb index 2afae24644..52206a26b8 100644 --- a/docs/examples/tutorial_nemo_3D.ipynb +++ b/docs/examples/tutorial_nemo_3D.ipynb @@ -155,7 +155,7 @@ "print(\n", " f\"Level[{int(depth_level)}] depth is: \"\n", " f\"[{fieldset.W.grid.depth[depth_level]:g} \"\n", - " f\"{fieldset.W.grid.depth[depth_level+1]:g}]\"\n", + " f\"{fieldset.W.grid.depth[depth_level + 1]:g}]\"\n", ")\n", "\n", "plt.pcolormesh(\n", diff --git a/docs/examples/tutorial_peninsula_AvsCgrid.ipynb b/docs/examples/tutorial_peninsula_AvsCgrid.ipynb index f084997a3e..14aa42ff8c 100644 --- a/docs/examples/tutorial_peninsula_AvsCgrid.ipynb +++ b/docs/examples/tutorial_peninsula_AvsCgrid.ipynb @@ -272,7 +272,7 @@ " # Set the same limits for all subplots\n", " ax.set_xlim([fieldset.U.lon.min(), fieldset.U.lon.max()])\n", " ax.set_ylim([0, 23e3])\n", - " m2km = lambda x, _: f\"{x/1000:.1f}\"\n", + " m2km = lambda x, _: f\"{x / 1000:.1f}\"\n", " ax.xaxis.set_major_formatter(m2km)\n", " ax.yaxis.set_major_formatter(m2km)\n", " ax.set_xlabel(\"x [km]\")\n", @@ -347,7 +347,7 @@ "ax.set_ylim([0, 23e3])\n", "ax.set_ylabel(\"y [km]\")\n", "ax.set_xlabel(\"x [km]\")\n", - "m2km = lambda x, _: f\"{x/1000:.1f}\"\n", + "m2km = lambda x, _: f\"{x / 1000:.1f}\"\n", "ax.xaxis.set_major_formatter(m2km)\n", "ax.yaxis.set_major_formatter(m2km)\n", "\n", diff --git a/parcels/compilation/codegenerator.py b/parcels/compilation/codegenerator.py index 9119eb4027..2e7f374f0f 100644 --- a/parcels/compilation/codegenerator.py +++ b/parcels/compilation/codegenerator.py @@ -832,7 +832,7 @@ def visit_FieldEvalNode(self, node): # Get Cs_w values directly from fieldset (since they are 1D in vertical only) Cs_w = [float(self.fieldset.Cs_w.data[0][zi][0][0]) for zi in range(self.fieldset.Cs_w.data.shape[1])] statements_croco = [ - c.Statement(f"float cs_w[] = {*Cs_w, }".replace("(", "{").replace(")", "}")), + c.Statement(f"float cs_w[] = {(*Cs_w,)}".replace("(", "{").replace(")", "}")), c.Statement( f"{node.var} = croco_from_z_to_sigma(time, {args[1]}, {args[2]}, {args[3]}, U, H, Zeta, &particles->ti[pnum*ngrid], &particles->zi[pnum*ngrid], &particles->yi[pnum*ngrid], &particles->xi[pnum*ngrid], hc, &cs_w)" ), @@ -861,7 +861,7 @@ def visit_VectorFieldEvalNode(self, node): # Get Cs_w values directly from fieldset (since they are 1D in vertical only) Cs_w = [float(self.fieldset.Cs_w.data[0][zi][0][0]) for zi in range(self.fieldset.Cs_w.data.shape[1])] statements_croco = [ - c.Statement(f"float cs_w[] = {*Cs_w, }".replace("(", "{").replace(")", "}")), + c.Statement(f"float cs_w[] = {(*Cs_w,)}".replace("(", "{").replace(")", "}")), c.Statement( f"{node.var4} = croco_from_z_to_sigma(time, {args[1]}, {args[2]}, {args[3]}, U, H, Zeta, &particles->ti[pnum*ngrid], &particles->zi[pnum*ngrid], &particles->yi[pnum*ngrid], &particles->xi[pnum*ngrid], hc, &cs_w)" ), diff --git a/parcels/field.py b/parcels/field.py index ece916e36d..1c4ee57a83 100644 --- a/parcels/field.py +++ b/parcels/field.py @@ -334,9 +334,9 @@ def __init__( self.grid.depth_field = kwargs.pop("depth_field", None) if self.grid.depth_field == "not_yet_set": - assert ( - self.grid._z4d - ), "Providing the depth dimensions from another field data is only available for 4d S grids" + assert self.grid._z4d, ( + "Providing the depth dimensions from another field data is only available for 4d S grids" + ) # data_full_zdim is the vertical dimension of the complete field data, ignoring the indices. # (data_full_zdim = grid.zdim if no indices are used, for A- and C-grids and for some B-grids). It is used for the B-grid, @@ -572,20 +572,20 @@ def from_netcdf( # Ensure the timestamps array is compatible with the user-provided datafiles. if timestamps is not None: if isinstance(filenames, list): - assert len(filenames) == len( - timestamps - ), "Outer dimension of timestamps should correspond to number of files." + assert len(filenames) == len(timestamps), ( + "Outer dimension of timestamps should correspond to number of files." + ) elif isinstance(filenames, dict): for k in filenames.keys(): if k not in ["lat", "lon", "depth", "time"]: if isinstance(filenames[k], list): - assert len(filenames[k]) == len( - timestamps - ), "Outer dimension of timestamps should correspond to number of files." + assert len(filenames[k]) == len(timestamps), ( + "Outer dimension of timestamps should correspond to number of files." + ) else: - assert ( - len(timestamps) == 1 - ), "Outer dimension of timestamps should correspond to number of files." + assert len(timestamps) == 1, ( + "Outer dimension of timestamps should correspond to number of files." + ) for t in timestamps: assert isinstance(t, (list, np.ndarray)), "timestamps should be a list for each file" @@ -597,13 +597,13 @@ def from_netcdf( if isinstance(variable, str): # for backward compatibility with Parcels < 2.0.0 variable = (variable, variable) elif isinstance(variable, dict): - assert ( - len(variable) == 1 - ), "Field.from_netcdf() supports only one variable at a time. Use FieldSet.from_netcdf() for multiple variables." + assert len(variable) == 1, ( + "Field.from_netcdf() supports only one variable at a time. Use FieldSet.from_netcdf() for multiple variables." + ) variable = tuple(variable.items())[0] - assert ( - len(variable) == 2 - ), "The variable tuple must have length 2. Use FieldSet.from_netcdf() for multiple variables" + assert len(variable) == 2, ( + "The variable tuple must have length 2. Use FieldSet.from_netcdf() for multiple variables" + ) data_filenames = cls._get_dim_filenames(filenames, "data") lonlat_filename = cls._get_dim_filenames(filenames, "lon") @@ -2136,21 +2136,21 @@ def __init__(self, name: str, F, V=None, W=None): if isinstance(F[0], VectorField): vector_type = F[0].vector_type for Fi in F: - assert isinstance(Fi, Field) or ( - isinstance(Fi, VectorField) and Fi.vector_type == vector_type - ), "Components of a NestedField must be Field or VectorField" + assert isinstance(Fi, Field) or (isinstance(Fi, VectorField) and Fi.vector_type == vector_type), ( + "Components of a NestedField must be Field or VectorField" + ) self.append(Fi) elif W is None: for i, Fi, Vi in zip(range(len(F)), F, V, strict=True): - assert isinstance(Fi, Field) and isinstance( - Vi, Field - ), "F, and V components of a NestedField must be Field" + assert isinstance(Fi, Field) and isinstance(Vi, Field), ( + "F, and V components of a NestedField must be Field" + ) self.append(VectorField(f"{name}_{i}", Fi, Vi)) else: for i, Fi, Vi, Wi in zip(range(len(F)), F, V, W, strict=True): - assert ( - isinstance(Fi, Field) and isinstance(Vi, Field) and isinstance(Wi, Field) - ), "F, V and W components of a NestedField must be Field" + assert isinstance(Fi, Field) and isinstance(Vi, Field) and isinstance(Wi, Field), ( + "F, V and W components of a NestedField must be Field" + ) self.append(VectorField(f"{name}_{i}", Fi, Vi, Wi)) self.name = name diff --git a/parcels/fieldset.py b/parcels/fieldset.py index 8a2e8ffbe6..de2062ce61 100644 --- a/parcels/fieldset.py +++ b/parcels/fieldset.py @@ -313,9 +313,9 @@ def check_velocityfields(U, V, W): g._check_zonal_periodic() if len(g.time) == 1: continue - assert isinstance( - g.time_origin.time_origin, type(self.time_origin.time_origin) - ), "time origins of different grids must be have the same type" + assert isinstance(g.time_origin.time_origin, type(self.time_origin.time_origin)), ( + "time origins of different grids must be have the same type" + ) g.time = g.time + self.time_origin.reltime(g.time_origin) if g.defer_load: g.time_full = g.time_full + self.time_origin.reltime(g.time_origin) diff --git a/parcels/grid.py b/parcels/grid.py index 244afe1494..7f6abaa07d 100644 --- a/parcels/grid.py +++ b/parcels/grid.py @@ -64,9 +64,9 @@ def __init__( if not lat.dtype == np.float32: lat = lat.astype(np.float32) if not time.dtype == np.float64: - assert isinstance( - time[0], (np.integer, np.floating, float, int) - ), "Time vector must be an array of int or floats" + assert isinstance(time[0], (np.integer, np.floating, float, int)), ( + "Time vector must be an array of int or floats" + ) time = time.astype(np.float64) self._lon = lon @@ -627,22 +627,22 @@ def __init__( self._z4d = 1 if len(self.depth.shape) == 4 else 0 if self._z4d: # self.depth.shape[0] is 0 for S grids loaded from netcdf file - assert ( - self.tdim == self.depth.shape[0] or self.depth.shape[0] == 0 - ), "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" - assert ( - self.xdim == self.depth.shape[-1] or self.depth.shape[-1] == 0 - ), "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" - assert ( - self.ydim == self.depth.shape[-2] or self.depth.shape[-2] == 0 - ), "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" + assert self.tdim == self.depth.shape[0] or self.depth.shape[0] == 0, ( + "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" + ) + assert self.xdim == self.depth.shape[-1] or self.depth.shape[-1] == 0, ( + "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" + ) + assert self.ydim == self.depth.shape[-2] or self.depth.shape[-2] == 0, ( + "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" + ) else: - assert ( - self.xdim == self.depth.shape[-1] - ), "depth dimension has the wrong format. It should be [zdim, ydim, xdim]" - assert ( - self.ydim == self.depth.shape[-2] - ), "depth dimension has the wrong format. It should be [zdim, ydim, xdim]" + assert self.xdim == self.depth.shape[-1], ( + "depth dimension has the wrong format. It should be [zdim, ydim, xdim]" + ) + assert self.ydim == self.depth.shape[-2], ( + "depth dimension has the wrong format. It should be [zdim, ydim, xdim]" + ) if not self.depth.dtype == np.float32: self._depth = self.depth.astype(np.float32) if self._lat_flipped: @@ -799,22 +799,22 @@ def __init__( self._z4d = 1 if len(self.depth.shape) == 4 else 0 if self._z4d: # self.depth.shape[0] is 0 for S grids loaded from netcdf file - assert ( - self.tdim == self.depth.shape[0] or self.depth.shape[0] == 0 - ), "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" - assert ( - self.xdim == self.depth.shape[-1] or self.depth.shape[-1] == 0 - ), "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" - assert ( - self.ydim == self.depth.shape[-2] or self.depth.shape[-2] == 0 - ), "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" + assert self.tdim == self.depth.shape[0] or self.depth.shape[0] == 0, ( + "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" + ) + assert self.xdim == self.depth.shape[-1] or self.depth.shape[-1] == 0, ( + "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" + ) + assert self.ydim == self.depth.shape[-2] or self.depth.shape[-2] == 0, ( + "depth dimension has the wrong format. It should be [tdim, zdim, ydim, xdim]" + ) else: - assert ( - self.xdim == self.depth.shape[-1] - ), "depth dimension has the wrong format. It should be [zdim, ydim, xdim]" - assert ( - self.ydim == self.depth.shape[-2] - ), "depth dimension has the wrong format. It should be [zdim, ydim, xdim]" + assert self.xdim == self.depth.shape[-1], ( + "depth dimension has the wrong format. It should be [zdim, ydim, xdim]" + ) + assert self.ydim == self.depth.shape[-2], ( + "depth dimension has the wrong format. It should be [zdim, ydim, xdim]" + ) if not self.depth.dtype == np.float32: self._depth = self.depth.astype(np.float32) diff --git a/parcels/interaction/interactionkernel.py b/parcels/interaction/interactionkernel.py index 0e3979a2e9..b953b8af31 100644 --- a/parcels/interaction/interactionkernel.py +++ b/parcels/interaction/interactionkernel.py @@ -83,9 +83,9 @@ def __init__( numkernelargs = self.check_kernel_signature_on_version() - assert numkernelargs[0] == 5 and numkernelargs.count(numkernelargs[0]) == len( - numkernelargs - ), "Interactionkernels take exactly 5 arguments: particle, fieldset, time, neighbors, mutator" + assert numkernelargs[0] == 5 and numkernelargs.count(numkernelargs[0]) == len(numkernelargs), ( + "Interactionkernels take exactly 5 arguments: particle, fieldset, time, neighbors, mutator" + ) # At this time, JIT mode is not supported for InteractionKernels, # so there is no need for any further "processing" of pyfunc's. diff --git a/parcels/particledata.py b/parcels/particledata.py index 9f2eb5ccdc..fabdae07bc 100644 --- a/parcels/particledata.py +++ b/parcels/particledata.py @@ -59,9 +59,9 @@ def __init__(self, pclass, lon, lat, depth, time, lonlatdepth_dtype, pid_orig, n self._sorted = np.all(np.diff(pid) >= 0) - assert ( - depth is not None - ), "particle's initial depth is None - incompatible with the ParticleData class. Invalid state." + assert depth is not None, ( + "particle's initial depth is None - incompatible with the ParticleData class. Invalid state." + ) assert lon.size == lat.size and lon.size == depth.size, "lon, lat, depth don't all have the same lenghts." assert lon.size == time.size, "time and positions (lon, lat, depth) don't have the same lengths." @@ -72,9 +72,9 @@ def __init__(self, pclass, lon, lat, depth, time, lonlatdepth_dtype, pid_orig, n partition_function = kwargs.pop("partition_function", partitionParticlesMPI_default) for kwvar in kwargs: - assert ( - lon.size == kwargs[kwvar].size - ), f"{kwvar} and positions (lon, lat, depth) don't have the same lengths." + assert lon.size == kwargs[kwvar].size, ( + f"{kwvar} and positions (lon, lat, depth) don't have the same lengths." + ) offset = np.max(pid) if (pid is not None) and len(pid) > 0 else -1 if MPI: @@ -132,9 +132,9 @@ def __init__(self, pclass, lon, lat, depth, time, lonlatdepth_dtype, pid_orig, n if lon is not None and lat is not None: # Initialise from lists of lon/lat coordinates - assert self._ncount == len(lon) and self._ncount == len( - lat - ), "Size of ParticleSet does not match length of lon and lat." + assert self._ncount == len(lon) and self._ncount == len(lat), ( + "Size of ParticleSet does not match length of lon and lat." + ) # mimic the variables that get initialised in the constructor self._data["lat"][:] = lat @@ -268,9 +268,9 @@ def get_single_by_index(self, index): def add_same(self, same_class): """Add another ParticleData instance to this ParticleData instance. This is done by concatenating both instances.""" - assert ( - same_class is not None - ), f"Trying to add another {type(self)} to this one, but the other one is None - invalid operation." + assert same_class is not None, ( + f"Trying to add another {type(self)} to this one, but the other one is None - invalid operation." + ) assert type(same_class) is type(self) if same_class._ncount == 0: @@ -313,20 +313,20 @@ def remove_single_by_index(self, index): def remove_multi_by_indices(self, indices): """Remove particles from the ParticleData instance based on their indices.""" - assert ( - indices is not None - ), "Trying to remove particles by their ParticleData instance indices, but the index list is None - invalid operation." - assert ( - type(indices) in [list, dict, np.ndarray] - ), "Trying to remove particles by their indices, but the index container is not a valid Python-collection - invalid operation." + assert indices is not None, ( + "Trying to remove particles by their ParticleData instance indices, but the index list is None - invalid operation." + ) + assert type(indices) in [list, dict, np.ndarray], ( + "Trying to remove particles by their indices, but the index container is not a valid Python-collection - invalid operation." + ) if type(indices) is not dict: - assert ( - len(indices) == 0 or type(indices[0]) in [int, np.int32, np.intp] - ), "Trying to remove particles by their index, but the index type in the Python collection is not a 32-bit integer - invalid operation." + assert len(indices) == 0 or type(indices[0]) in [int, np.int32, np.intp], ( + "Trying to remove particles by their index, but the index type in the Python collection is not a 32-bit integer - invalid operation." + ) else: - assert ( - len(list(indices.values())) == 0 or type(list(indices.values())[0]) in [int, np.int32, np.intp] - ), "Trying to remove particles by their index, but the index type in the Python collection is not a 32-bit integer - invalid operation." + assert len(list(indices.values())) == 0 or type(list(indices.values())[0]) in [int, np.int32, np.intp], ( + "Trying to remove particles by their index, but the index type in the Python collection is not a 32-bit integer - invalid operation." + ) if type(indices) is dict: indices = list(indices.values()) diff --git a/parcels/particleset.py b/parcels/particleset.py index 011f423e57..497cd7fdbd 100644 --- a/parcels/particleset.py +++ b/parcels/particleset.py @@ -187,9 +187,9 @@ def ArrayClass_init(self, *args, **kwargs): for kwvar in kwargs: if kwvar not in ["partition_function"]: kwargs[kwvar] = convert_to_flat_array(kwargs[kwvar]) - assert ( - lon.size == kwargs[kwvar].size - ), f"{kwvar} and positions (lon, lat, depth) don't have the same lengths." + assert lon.size == kwargs[kwvar].size, ( + f"{kwvar} and positions (lon, lat, depth) don't have the same lengths." + ) self.repeatdt = timedelta_to_float(repeatdt) if repeatdt is not None else None diff --git a/parcels/tools/timer.py b/parcels/tools/timer.py index cd6725fb2f..ea1181a448 100644 --- a/parcels/tools/timer.py +++ b/parcels/tools/timer.py @@ -48,7 +48,7 @@ def print_tree_sequential(self, step=0, root_time=0, parent_time=0): if step > 0: print(f"({round(time / parent_time * 100):3d}%) ", end="") t_str = f"{time:1.3e} s" if root_time < 300 else datetime.timedelta(seconds=time) - print(f"Timer {(self._name).ljust(20 - 2*step + 7*(step == 0))}: {t_str}") + print(f"Timer {(self._name).ljust(20 - 2 * step + 7 * (step == 0))}: {t_str}") for child in self._children: child.print_tree_sequential(step + 1, root_time, time)