Skip to content

Commit eab2842

Browse files
Merge branch 'v4-dev' into patch/ux-uv-uvw
2 parents 42dd690 + 479bd09 commit eab2842

13 files changed

Lines changed: 61 additions & 31 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ defaults:
1616
run:
1717
shell: bash -el {0}
1818

19-
env:
20-
PIXI_VERSION: "v0.63.0"
21-
2219
jobs:
2320
should-run-ci:
2421
name: should run ci
@@ -48,10 +45,8 @@ jobs:
4845
pixi-version: ${{ steps.pixi-lock.outputs.pixi-version }}
4946
steps:
5047
- uses: actions/checkout@v4
51-
- uses: Parcels-code/pixi-lock/create-and-cache@2b823a4a804631370fbde7e6088ee5db71a26c60 # TODO: Update to action in prefix-dev once available
48+
- uses: Parcels-code/pixi-lock/create-and-cache@2b823a4a804631370fbde7e6088ee5db71a26c60
5249
id: pixi-lock
53-
with:
54-
pixi-version: ${{env.PIXI_VERSION}}
5550
- uses: actions/upload-artifact@v6
5651
with:
5752
name: pixi-lock

docs/user_guide/examples/tutorial_Argofloats.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
"# Convert to SGRID-compliant dataset and create FieldSet\n",
132132
"ds_fset = parcels.convert.copernicusmarine_to_sgrid(fields=fields)\n",
133133
"fieldset = parcels.FieldSet.from_sgrid_conventions(ds_fset)\n",
134-
"fieldset.constants[\"mindepth\"] = 1.0\n",
134+
"fieldset.add_constant(\"mindepth\", 1.0)\n",
135135
"\n",
136136
"# Define a new Particle type including extra Variables\n",
137137
"ArgoParticle = parcels.Particle.add_variable(\n",

docs/user_guide/examples/tutorial_croco_3D.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
"fieldset = parcels.FieldSet.from_sgrid_conventions(ds_fset)\n",
9292
"\n",
9393
"# Add the critical depth (`hc`) as a constant to the fieldset\n",
94-
"fieldset.constants[\"hc\"] = ds_fields.hc.item()"
94+
"fieldset.add_constant(\"hc\", ds_fields.hc.item())"
9595
]
9696
},
9797
{
@@ -204,7 +204,7 @@
204204
"source": [
205205
"fieldset_noW = parcels.FieldSet.from_sgrid_conventions(ds_fset)\n",
206206
"fieldset_noW.W.data[:] = 0.0\n",
207-
"fieldset_noW.constants[\"hc\"] = ds_fields.hc.item()\n",
207+
"fieldset_noW.add_constant(\"hc\", ds_fields.hc.item())\n",
208208
"\n",
209209
"X, Z = np.meshgrid(\n",
210210
" [40e3, 80e3, 120e3],\n",

docs/user_guide/examples/tutorial_diffusion.ipynb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
"\n",
9191
"Just like velocities, diffusivities are passed to Parcels in the form of `Field` objects. When using `DiffusionUniformKh`, they should be added to the `FieldSet` object as constant fields, e.g. `fieldset.add_constant_field(\"Kh_zonal\", 1, mesh=\"flat\")`.\n",
9292
"\n",
93-
"To make a central difference approximation for computing the gradient in diffusivity, a resolution for this approximation `dres` is needed: _Parcels_ approximates the gradients in diffusivities by using their values at the particle's location ± `dres` (in both $x$ and $y$). A value of `dres` must be specified and added to the FieldSet by the user (e.g. `fieldset.constants[\"dres\"] = 0.01`). Currently, it is unclear what the best value of `dres` is. From experience, the size of `dres` should be smaller than the spatial resolution of the data, but within reasonable limits of machine precision to avoid numerical errors. We are working on a method to compute gradients differently so that specifying `dres` is not necessary anymore.\n",
93+
"To make a central difference approximation for computing the gradient in diffusivity, a resolution for this approximation `dres` is needed: _Parcels_ approximates the gradients in diffusivities by using their values at the particle's location ± `dres` (in both $x$ and $y$). A value of `dres` must be specified and added to the FieldSet by the user (e.g. `fieldset.add_constant(\"dres\", 0.01)`). Currently, it is unclear what the best value of `dres` is. From experience, the size of `dres` should be smaller than the spatial resolution of the data, but within reasonable limits of machine precision to avoid numerical errors. We are working on a method to compute gradients differently so that specifying `dres` is not necessary anymore.\n",
9494
"\n",
9595
"## Example: Impermeable Diffusivity Profile\n",
9696
"\n",
@@ -206,7 +206,7 @@
206206
"source": [
207207
"fieldset = parcels.FieldSet.from_sgrid_conventions(ds, mesh=\"flat\")\n",
208208
"fieldset.add_constant_field(\"Kh_zonal\", 1, mesh=\"flat\")\n",
209-
"fieldset.constants[\"dres\"] = 0.00005"
209+
"fieldset.add_constant(\"dres\", 0.00005)"
210210
]
211211
},
212212
{
@@ -537,7 +537,7 @@
537537
")\n",
538538
"fieldset.add_field(cell_areas_field)\n",
539539
"\n",
540-
"fieldset.constants[\"Cs\"] = 0.1"
540+
"fieldset.add_constant(\"Cs\", 0.1)"
541541
]
542542
},
543543
{

docs/user_guide/v4-migration.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ Version 4 of Parcels is unreleased at the moment. The information in this migrat
2121
## FieldSet
2222

2323
- `interp_method` has to be an Interpolation function, instead of a string.
24-
- `add_constant` has been removed as a method. Instead, use direct dictionary assignment (`fieldset.constants[key] = value`)
2524

2625
## Particle
2726

pixi.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ name = "Parcels"
33
preview = ["pixi-build"]
44
channels = ["conda-forge"]
55
platforms = ["win-64", "linux-64", "osx-64", "osx-arm64"]
6+
requires-pixi = ">=0.63.0"
67

78
[package]
89
name = "parcels"
910
version = "dynamic" # dynamic versioning needs better support in pixi https://github.com/prefix-dev/pixi/issues/2923#issuecomment-2598460666 . Putting `version = "dynamic"` here for now until pixi recommends something else.
10-
license = "MIT" # can remove this once https://github.com/prefix-dev/pixi-build-backends/issues/397 is resolved
1111

1212
[package.build]
1313
backend = { name = "pixi-build-python", version = "0.4.*" }

src/parcels/_core/fieldset.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from parcels._core.field import Field, VectorField
1414
from parcels._core.utils import sgrid
15+
from parcels._core.utils.string import _assert_str_and_python_varname
1516
from parcels._core.utils.time import get_datetime_type_calendar
1617
from parcels._core.utils.time import is_compatible as datetime_is_compatible
1718
from parcels._core.uxgrid import UxGrid
@@ -152,6 +153,25 @@ def add_constant_field(self, name: str, value, mesh: Mesh = "spherical"):
152153
grid = XGrid(xgrid, mesh=mesh)
153154
self.add_field(Field(name, ds[name], grid, interp_method=XConstantField))
154155

156+
def add_constant(self, name, value):
157+
"""Add a constant to the FieldSet.
158+
159+
Parameters
160+
----------
161+
name : str
162+
Name of the constant
163+
value :
164+
Value of the constant
165+
166+
"""
167+
_assert_str_and_python_varname(name)
168+
169+
if name in self.constants:
170+
raise ValueError(f"FieldSet already has a constant with name '{name}'")
171+
if not isinstance(value, (float, np.floating, int, np.integer)):
172+
raise ValueError(f"FieldSet constants have to be of type float or int, got a {type(value)}")
173+
self.constants[name] = value
174+
155175
@property
156176
def gridset(self) -> list[BaseGrid]:
157177
grids = []

src/parcels/_core/kernel.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,29 +142,29 @@ def check_fieldsets_in_kernels(self, pyfunc): # TODO v4: this can go into anoth
142142
raise ValueError('ParticleClass requires a "next_dt" for AdvectionRK45 Kernel.')
143143
if not hasattr(self.fieldset, "RK45_tol"):
144144
warnings.warn(
145-
"Setting RK45 tolerance to 10 m. Set fieldset.constants['RK45_tol'] = [distance] to change.",
145+
"Setting RK45 tolerance to 10 m. Use fieldset.add_constant('RK45_tol', [distance]) to change.",
146146
KernelWarning,
147147
stacklevel=2,
148148
)
149-
self.fieldset.constants["RK45_tol"] = 10
149+
self.fieldset.add_constant("RK45_tol", 10)
150150
if self.fieldset.U.grid._mesh == "spherical":
151151
self.fieldset.RK45_tol /= (
152152
1852 * 60
153153
) # TODO does not account for zonal variation in meter -> degree conversion
154154
if not hasattr(self.fieldset, "RK45_min_dt"):
155155
warnings.warn(
156-
"Setting RK45 minimum timestep to 1 s. Set fieldset.constants['RK45_min_dt'] = [timestep] to change.",
156+
"Setting RK45 minimum timestep to 1 s. Use fieldset.add_constant('RK45_min_dt', [timestep]) to change.",
157157
KernelWarning,
158158
stacklevel=2,
159159
)
160-
self.fieldset.constants["RK45_min_dt"] = 1
160+
self.fieldset.add_constant("RK45_min_dt", 1)
161161
if not hasattr(self.fieldset, "RK45_max_dt"):
162162
warnings.warn(
163-
"Setting RK45 maximum timestep to 1 day. Set fieldset.constants['RK45_max_dt'] = [timestep] to change.",
163+
"Setting RK45 maximum timestep to 1 day. Use fieldset.add_constant('RK45_max_dt', [timestep]) to change.",
164164
KernelWarning,
165165
stacklevel=2,
166166
)
167-
self.fieldset.constants["RK45_max_dt"] = 60 * 60 * 24
167+
self.fieldset.add_constant("RK45_max_dt", 60 * 60 * 24)
168168

169169
def merge(self, kernel):
170170
if not isinstance(kernel, type(self)):

tests/test_advection.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,14 @@ def test_moving_eddy(kernel, rtol):
297297
ds["Kh"] = (["time", "depth", "YG", "XG"], np.full(ds["U"].shape, 0))
298298
fieldset.add_field(Field("Kh", ds["Kh"], grid, interp_method=XLinear), "Kh_zonal")
299299
fieldset.add_field(Field("Kh", ds["Kh"], grid, interp_method=XLinear), "Kh_meridional")
300-
fieldset.constants["dres"] = 0.1
300+
fieldset.add_constant("dres", 0.1)
301301

302302
start_lon, start_lat, start_z = 12000, 12500, 12500
303303
dt = np.timedelta64(30, "m")
304304
endtime = np.timedelta64(1, "h")
305305

306306
if kernel == AdvectionRK45:
307-
fieldset.constants["RK45_tol"] = rtol
307+
fieldset.add_constant("RK45_tol", rtol)
308308

309309
pset = ParticleSet(
310310
fieldset, pclass=DEFAULT_PARTICLES[kernel], lon=start_lon, lat=start_lat, z=start_z, time=np.timedelta64(0, "s")
@@ -346,8 +346,8 @@ def test_decaying_moving_eddy(kernel, rtol):
346346
endtime = np.timedelta64(23, "h")
347347

348348
if kernel == AdvectionRK45:
349-
fieldset.constants["RK45_tol"] = rtol
350-
fieldset.constants["RK45_min_dt"] = 10 * 60
349+
fieldset.add_constant("RK45_tol", rtol)
350+
fieldset.add_constant("RK45_min_dt", 10 * 60)
351351

352352
pset = ParticleSet(
353353
fieldset, pclass=DEFAULT_PARTICLES[kernel], lon=start_lon, lat=start_lat, time=np.timedelta64(0, "s")
@@ -403,7 +403,7 @@ def test_stommelgyre_fieldset(kernel, rtol, grid_type):
403403
)
404404

405405
if kernel == AdvectionRK45:
406-
fieldset.constants["RK45_tol"] = rtol
406+
fieldset.add_constant("RK45_tol", rtol)
407407

408408
def UpdateP(particles, fieldset): # pragma: no cover
409409
particles.p = fieldset.P[particles.time, particles.z, particles.lat, particles.lon]
@@ -443,7 +443,7 @@ def test_peninsula_fieldset(kernel, rtol, grid_type):
443443
)
444444

445445
if kernel == AdvectionRK45:
446-
fieldset.constants["RK45_tol"] = rtol
446+
fieldset.add_constant("RK45_tol", rtol)
447447

448448
def UpdateP(particles, fieldset): # pragma: no cover
449449
particles.p = fieldset.P[particles.time, particles.z, particles.lat, particles.lon]

tests/test_diffusion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def test_fieldKh_SpatiallyVaryingDiffusion(mesh, kernel):
6464
ds["Kh_zonal"] = (["time", "depth", "YG", "XG"], np.full((2, 1, ydim, xdim), Kh))
6565
ds["Kh_meridional"] = (["time", "depth", "YG", "XG"], np.full((2, 1, ydim, xdim), Kh))
6666
fieldset = FieldSet.from_sgrid_conventions(ds, mesh=mesh)
67-
fieldset.constants["dres"] = float(ds["lon"][1] - ds["lon"][0])
67+
fieldset.add_constant("dres", float(ds["lon"][1] - ds["lon"][0]))
6868

6969
npart = 10000
7070

@@ -84,7 +84,7 @@ def test_randomexponential(lambd):
8484
npart = 1000
8585

8686
# Rate parameter for random.expovariate
87-
fieldset.constants["lambd"] = lambd
87+
fieldset.add_constant("lambd", lambd)
8888

8989
# Set random seed
9090
np.random.seed(1234)

0 commit comments

Comments
 (0)