Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
639e2ae
Remove test parametrization of verbose_progress
VeckoTheGecko Jul 24, 2025
bd619e7
Add hash of final locations in test_uxstommelgyre_pset_execute
VeckoTheGecko Jul 24, 2025
42a68bf
Remove particle.delete()
VeckoTheGecko Jul 28, 2025
4a60523
Remove BaseKernel
VeckoTheGecko Jul 28, 2025
0628af8
remove unused vars
VeckoTheGecko Jul 28, 2025
33e1a28
Update kernel evaluation to use _pyfuncs
VeckoTheGecko Jul 28, 2025
37cf7fe
Move particle_d{lon,lat,depth} to live on the particle itself
VeckoTheGecko Jul 29, 2025
5013e82
Refactor dynamic function compiling
VeckoTheGecko Jul 29, 2025
773728c
Remove funccode rewriting
VeckoTheGecko Jul 29, 2025
ea86863
Simplify funcvars setting
VeckoTheGecko Jul 29, 2025
c5a2ddc
Remove Kernel.funcvars
VeckoTheGecko Jul 29, 2025
c709007
Refactor Kernel init
VeckoTheGecko Jul 29, 2025
6ce6e66
Remove Kernel.funccode
VeckoTheGecko Jul 29, 2025
d0fdae9
Remove funcname from Kernel.__init__
VeckoTheGecko Jul 29, 2025
a9e233e
Remove generating and storing kernel code in pset.execute
VeckoTheGecko Jul 29, 2025
17a628c
Remove under the hood combining of kernels
VeckoTheGecko Jul 29, 2025
831ed4b
Remove test_multi_kernel_reuse_varnames
VeckoTheGecko Jul 29, 2025
6421136
Fix early stopping in kernel loop
VeckoTheGecko Jul 29, 2025
d9427b3
Note breaking changes
VeckoTheGecko Jul 29, 2025
5cbdc2d
Remove unused kernel helpers
VeckoTheGecko Jul 29, 2025
56886e3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 29, 2025
e02ec01
Update comments
VeckoTheGecko Jul 30, 2025
2498716
Update v3to4-breaking-changes.md
VeckoTheGecko Jul 30, 2025
9c866c6
Update function to round_and_hash_float_array and add test
VeckoTheGecko Jul 30, 2025
dc07a76
xfail test
VeckoTheGecko Jul 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/examples/example_mitgcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ def run_mitgcm_zonally_reentrant(path: Path):

def periodicBC(particle, fieldset, time): # pragma: no cover
if particle.lon < 0:
particle_dlon += fieldset.domain_width # noqa
particle.dlon += fieldset.domain_width
elif particle.lon > fieldset.domain_width:
particle_dlon -= fieldset.domain_width
particle.dlon -= fieldset.domain_width

# Release particles 5 cells away from the Eastern boundary
pset = parcels.ParticleSet.from_line(
Expand Down
12 changes: 6 additions & 6 deletions docs/examples/example_moving_eddies.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,17 +272,17 @@ def test_periodic_and_computeTimeChunk_eddies():

def periodicBC(particle, fieldset, time): # pragma: no cover
if particle.lon < fieldset.halo_west:
particle_dlon += fieldset.halo_east - fieldset.halo_west # noqa
particle.dlon += fieldset.halo_east - fieldset.halo_west
elif particle.lon > fieldset.halo_east:
particle_dlon -= fieldset.halo_east - fieldset.halo_west
particle.dlon -= fieldset.halo_east - fieldset.halo_west
if particle.lat < fieldset.halo_south:
particle_dlat += fieldset.halo_north - fieldset.halo_south # noqa
particle.dlat += fieldset.halo_north - fieldset.halo_south
elif particle.lat > fieldset.halo_north:
particle_dlat -= fieldset.halo_north - fieldset.halo_south
particle.dlat -= fieldset.halo_north - fieldset.halo_south

def slowlySouthWestward(particle, fieldset, time): # pragma: no cover
particle_dlon -= 5 * particle.dt / 1e5 # noqa
particle_dlat -= 3 * particle.dt / 1e5 # noqa
particle.dlon -= 5 * particle.dt / 1e5
particle.dlat -= 3 * particle.dt / 1e5

kernels = pset.Kernel(parcels.AdvectionRK4) + slowlySouthWestward + periodicBC
pset.execute(kernels, runtime=timedelta(days=6), dt=timedelta(hours=1))
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/example_nemo_curvilinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def run_nemo_curvilinear(outfile, advtype="RK4"):

def periodicBC(particle, fieldSet, time): # pragma: no cover
if particle.lon > 180:
particle_dlon -= 360 # noqa
particle.dlon -= 360

pset = parcels.ParticleSet.from_list(fieldset, parcels.Particle, lon=lonp, lat=latp)
pfile = parcels.ParticleFile(outfile, pset, outputdt=timedelta(days=1))
Expand Down
44 changes: 24 additions & 20 deletions parcels/application_kernels/advection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import math

import numpy as np

from parcels.tools.statuscodes import StatusCode

__all__ = [
Expand All @@ -16,21 +18,21 @@

def AdvectionRK4(particle, fieldset, time): # pragma: no cover
"""Advection of particles using fourth-order Runge-Kutta integration."""
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
dt = particle.dt / np.timedelta64(1, "s") # TODO: improve API for converting dt to seconds
(u1, v1) = fieldset.UV[particle]
lon1, lat1 = (particle.lon + u1 * 0.5 * dt, particle.lat + v1 * 0.5 * dt)
(u2, v2) = fieldset.UV[time + 0.5 * particle.dt, particle.depth, lat1, lon1, particle]
lon2, lat2 = (particle.lon + u2 * 0.5 * dt, particle.lat + v2 * 0.5 * dt)
(u3, v3) = fieldset.UV[time + 0.5 * particle.dt, particle.depth, lat2, lon2, particle]
lon3, lat3 = (particle.lon + u3 * dt, particle.lat + v3 * dt)
(u4, v4) = fieldset.UV[time + particle.dt, particle.depth, lat3, lon3, particle]
particle_dlon += (u1 + 2 * u2 + 2 * u3 + u4) / 6.0 * dt # noqa
particle_dlat += (v1 + 2 * v2 + 2 * v3 + v4) / 6.0 * dt # noqa
particle.dlon += (u1 + 2 * u2 + 2 * u3 + u4) / 6.0 * dt
particle.dlat += (v1 + 2 * v2 + 2 * v3 + v4) / 6.0 * dt


def AdvectionRK4_3D(particle, fieldset, time): # pragma: no cover
"""Advection of particles using fourth-order Runge-Kutta integration including vertical velocity."""
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
dt = particle.dt / np.timedelta64(1, "s")
(u1, v1, w1) = fieldset.UVW[particle]
lon1 = particle.lon + u1 * 0.5 * dt
lat1 = particle.lat + v1 * 0.5 * dt
Expand All @@ -44,16 +46,16 @@ def AdvectionRK4_3D(particle, fieldset, time): # pragma: no cover
lat3 = particle.lat + v3 * dt
dep3 = particle.depth + w3 * dt
(u4, v4, w4) = fieldset.UVW[time + particle.dt, dep3, lat3, lon3, particle]
particle_dlon += (u1 + 2 * u2 + 2 * u3 + u4) / 6 * dt # noqa
particle_dlat += (v1 + 2 * v2 + 2 * v3 + v4) / 6 * dt # noqa
particle_ddepth += (w1 + 2 * w2 + 2 * w3 + w4) / 6 * dt # noqa
particle.dlon += (u1 + 2 * u2 + 2 * u3 + u4) / 6 * dt
particle.dlat += (v1 + 2 * v2 + 2 * v3 + v4) / 6 * dt
particle.ddepth += (w1 + 2 * w2 + 2 * w3 + w4) / 6 * dt


def AdvectionRK4_3D_CROCO(particle, fieldset, time): # pragma: no cover
"""Advection of particles using fourth-order Runge-Kutta integration including vertical velocity.
This kernel assumes the vertical velocity is the 'w' field from CROCO output and works on sigma-layers.
"""
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
dt = particle.dt / np.timedelta64(1, "s") # TODO: improve API for converting dt to seconds
sig_dep = particle.depth / fieldset.H[time, 0, particle.lat, particle.lon]

(u1, v1, w1) = fieldset.UVW[time, particle.depth, particle.lat, particle.lon, particle]
Expand Down Expand Up @@ -84,9 +86,9 @@ def AdvectionRK4_3D_CROCO(particle, fieldset, time): # pragma: no cover
sig_dep4 = sig_dep + w4 * dt
dep4 = sig_dep4 * fieldset.H[time, 0, lat4, lon4]

particle_dlon += (u1 + 2 * u2 + 2 * u3 + u4) / 6 * dt # noqa
particle_dlat += (v1 + 2 * v2 + 2 * v3 + v4) / 6 * dt # noqa
particle_ddepth += ( # noqa
particle.dlon += (u1 + 2 * u2 + 2 * u3 + u4) / 6 * dt
particle.dlat += (v1 + 2 * v2 + 2 * v3 + v4) / 6 * dt
particle.ddepth += (
(dep1 - particle.depth) * 2
+ 2 * (dep2 - particle.depth) * 2
+ 2 * (dep3 - particle.depth)
Expand All @@ -97,10 +99,10 @@ def AdvectionRK4_3D_CROCO(particle, fieldset, time): # pragma: no cover

def AdvectionEE(particle, fieldset, time): # pragma: no cover
"""Advection of particles using Explicit Euler (aka Euler Forward) integration."""
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
dt = particle.dt / np.timedelta64(1, "s") # TODO: improve API for converting dt to seconds
(u1, v1) = fieldset.UV[particle]
particle_dlon += u1 * dt # noqa
particle_dlat += v1 * dt # noqa
particle.dlon += u1 * dt
particle.dlat += v1 * dt


def AdvectionRK45(particle, fieldset, time): # pragma: no cover
Expand All @@ -113,7 +115,9 @@ def AdvectionRK45(particle, fieldset, time): # pragma: no cover
Time-step dt is halved if error is larger than fieldset.RK45_tol,
and doubled if error is smaller than 1/10th of tolerance.
"""
dt = min(particle.next_dt / np.timedelta64(1, "s"), fieldset.RK45_max_dt) # noqa TODO improve API for converting dt to seconds
dt = min(
particle.next_dt / np.timedelta64(1, "s"), fieldset.RK45_max_dt
) # TODO: improve API for converting dt to seconds
c = [1.0 / 4.0, 3.0 / 8.0, 12.0 / 13.0, 1.0, 1.0 / 2.0]
A = [
[1.0 / 4.0, 0.0, 0.0, 0.0, 0.0],
Expand Down Expand Up @@ -156,8 +160,8 @@ def AdvectionRK45(particle, fieldset, time): # pragma: no cover

kappa = math.sqrt(math.pow(lon_5th - lon_4th, 2) + math.pow(lat_5th - lat_4th, 2))
if (kappa <= fieldset.RK45_tol) or (math.fabs(dt) < math.fabs(fieldset.RK45_min_dt)):
particle_dlon += lon_4th # noqa
particle_dlat += lat_4th # noqa
particle.dlon += lon_4th
particle.dlat += lat_4th
if (kappa <= fieldset.RK45_tol) / 10 and (math.fabs(dt * 2) <= math.fabs(fieldset.RK45_max_dt)):
particle.next_dt *= 2
else:
Expand Down Expand Up @@ -314,14 +318,14 @@ def compute_rs(r, B, delta, s_min):
rs_x = compute_rs(xsi, B_x, delta_x, s_min)
rs_y = compute_rs(eta, B_y, delta_y, s_min)

particle_dlon += ( # noqa
particle.dlon += (
(1.0 - rs_x) * (1.0 - rs_y) * px[0]
+ rs_x * (1.0 - rs_y) * px[1]
+ rs_x * rs_y * px[2]
+ (1.0 - rs_x) * rs_y * px[3]
- particle.lon
)
particle_dlat += ( # noqa
particle.dlat += (
(1.0 - rs_x) * (1.0 - rs_y) * py[0]
+ rs_x * (1.0 - rs_y) * py[1]
+ rs_x * rs_y * py[2]
Expand All @@ -331,7 +335,7 @@ def compute_rs(r, B, delta, s_min):

if withW:
rs_z = compute_rs(zeta, B_z, delta_z, s_min)
particle_ddepth += (1.0 - rs_z) * pz[0] + rs_z * pz[1] - particle.depth # noqa
particle.ddepth += (1.0 - rs_z) * pz[0] + rs_z * pz[1] - particle.depth

if particle.dt > 0:
particle.dt = max(direction * s_min * (dxdy * dz), 1e-7).astype("timedelta64[s]")
Expand Down
20 changes: 11 additions & 9 deletions parcels/application_kernels/advectiondiffusion.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import math
import random

import numpy as np

__all__ = ["AdvectionDiffusionEM", "AdvectionDiffusionM1", "DiffusionUniformKh"]


Expand All @@ -24,7 +26,7 @@ def AdvectionDiffusionM1(particle, fieldset, time): # pragma: no cover
The Wiener increment `dW` is normally distributed with zero
mean and a standard deviation of sqrt(dt).
"""
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
dt = particle.dt / np.timedelta64(1, "s") # TODO: improve API for converting dt to seconds
# Wiener increment with zero mean and std of sqrt(dt)
dWx = random.normalvariate(0, math.sqrt(math.fabs(dt)))
dWy = random.normalvariate(0, math.sqrt(math.fabs(dt)))
Expand All @@ -43,8 +45,8 @@ def AdvectionDiffusionM1(particle, fieldset, time): # pragma: no cover
by = math.sqrt(2 * fieldset.Kh_meridional[time, particle.depth, particle.lat, particle.lon])

# Particle positions are updated only after evaluating all terms.
particle_dlon += u * dt + 0.5 * dKdx * (dWx**2 + dt) + bx * dWx # noqa
particle_dlat += v * dt + 0.5 * dKdy * (dWy**2 + dt) + by * dWy # noqa
particle.dlon += u * dt + 0.5 * dKdx * (dWx**2 + dt) + bx * dWx
particle.dlat += v * dt + 0.5 * dKdy * (dWy**2 + dt) + by * dWy


def AdvectionDiffusionEM(particle, fieldset, time): # pragma: no cover
Expand All @@ -60,7 +62,7 @@ def AdvectionDiffusionEM(particle, fieldset, time): # pragma: no cover
The Wiener increment `dW` is normally distributed with zero
mean and a standard deviation of sqrt(dt).
"""
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
dt = particle.dt / np.timedelta64(1, "s")
# Wiener increment with zero mean and std of sqrt(dt)
dWx = random.normalvariate(0, math.sqrt(math.fabs(dt)))
dWy = random.normalvariate(0, math.sqrt(math.fabs(dt)))
Expand All @@ -80,8 +82,8 @@ def AdvectionDiffusionEM(particle, fieldset, time): # pragma: no cover
by = math.sqrt(2 * fieldset.Kh_meridional[time, particle.depth, particle.lat, particle.lon])

# Particle positions are updated only after evaluating all terms.
particle_dlon += ax * dt + bx * dWx # noqa
particle_dlat += ay * dt + by * dWy # noqa
particle.dlon += ax * dt + bx * dWx
particle.dlat += ay * dt + by * dWy


def DiffusionUniformKh(particle, fieldset, time): # pragma: no cover
Expand All @@ -102,13 +104,13 @@ def DiffusionUniformKh(particle, fieldset, time): # pragma: no cover
The Wiener increment `dW` is normally distributed with zero
mean and a standard deviation of sqrt(dt).
"""
dt = particle.dt / np.timedelta64(1, "s") # noqa TODO improve API for converting dt to seconds
dt = particle.dt / np.timedelta64(1, "s")
# Wiener increment with zero mean and std of sqrt(dt)
dWx = random.normalvariate(0, math.sqrt(math.fabs(dt)))
dWy = random.normalvariate(0, math.sqrt(math.fabs(dt)))

bx = math.sqrt(2 * fieldset.Kh_zonal[particle])
by = math.sqrt(2 * fieldset.Kh_meridional[particle])

particle_dlon += bx * dWx # noqa
particle_dlat += by * dWy # noqa
particle.dlon += bx * dWx
particle.dlat += by * dWy
2 changes: 1 addition & 1 deletion parcels/interaction/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .interactionkernel import InteractionKernel # noqa
# from .interactionkernel import InteractionKernel
4 changes: 0 additions & 4 deletions parcels/interaction/interactionkernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ def __init__(
ptype,
pyfunc=None,
funcname=None,
funccode=None,
py_ast=None,
funcvars=None,
):
if MPI is not None and MPI.COMM_WORLD.Get_size() > 1:
raise NotImplementedError(
Expand All @@ -52,9 +50,7 @@ def __init__(
ptype=ptype,
pyfunc=pyfunc,
funcname=funcname,
funccode=funccode,
py_ast=py_ast,
funcvars=funcvars,
)

if pyfunc is not None:
Expand Down
Loading
Loading