Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
543d729
Update tests to use np.testing.assert_allclose
VeckoTheGecko Aug 5, 2025
8529d82
Remove lonlatdepth_dtype
VeckoTheGecko Aug 4, 2025
0a5970b
Remove MPI particle file writing code, clean up execution paths, and …
VeckoTheGecko Aug 4, 2025
f0f8cbe
Remove particle.py
VeckoTheGecko Aug 4, 2025
7d04de4
Add new Particle class implementation
VeckoTheGecko Aug 4, 2025
33a10bb
Update references to fix tests
VeckoTheGecko Aug 4, 2025
c0766a2
Remove old particle implementation
VeckoTheGecko Aug 4, 2025
e6fdd34
Create create_particle_data method on Particle class
VeckoTheGecko Aug 4, 2025
51bc4d5
Add tests for particle.py
VeckoTheGecko Aug 4, 2025
3c3ffa1
Update create_particle_data to be a function
VeckoTheGecko Aug 4, 2025
0fb9fdb
Update create_particle_data to pass arbitrary initial values
VeckoTheGecko Aug 4, 2025
e181004
Update particle data naming to 'id' instead of 'trajectory'
VeckoTheGecko Aug 4, 2025
986ce99
Update initial value for dt Variable
VeckoTheGecko Aug 4, 2025
848d981
Update time dtype for particle
VeckoTheGecko Aug 4, 2025
59275f5
Create get_default_particle
VeckoTheGecko Aug 4, 2025
b8d3315
Add dlon,dlat,ddepth variables to particle
VeckoTheGecko Aug 4, 2025
c67f6c4
Create particle data based on fieldset time interval
VeckoTheGecko Aug 4, 2025
38709fb
Update create_particle_data arg
VeckoTheGecko Aug 4, 2025
31e2670
Add test_create_particle_data
VeckoTheGecko Aug 5, 2025
b182a75
Update test_advection.py to use lon/lat instead of lon_nextloop...
VeckoTheGecko Aug 5, 2025
0701a0b
Remove commented code
VeckoTheGecko Aug 5, 2025
69e3759
Add example usage
VeckoTheGecko Aug 5, 2025
79bdb88
Review feedback
VeckoTheGecko Aug 6, 2025
ab67bd4
Add test_particleclass_add_variable_in_loop
VeckoTheGecko Aug 6, 2025
d0a2c6a
Update particle ID varname to be 'trajectory'
VeckoTheGecko Aug 6, 2025
07f7c94
Merge branch 'v4-dev' into particle-particledata
VeckoTheGecko Aug 6, 2025
4c56dde
Skip RK45 advection tests
VeckoTheGecko Aug 6, 2025
79e4420
Increasing RK45_tol to speed up tests
erikvansebille Aug 7, 2025
c43c694
Update LocalStore to DirectoryStore
VeckoTheGecko Aug 7, 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_globcurrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def test_globcurrent_particle_independence(rundays=5):
time0 = fieldset.U.grid.time[0]

def DeleteP0(particle, fieldset, time): # pragma: no cover
if particle.id == 0:
if particle.trajectory == 0:
particle.delete()

pset0 = parcels.ParticleSet(
Expand Down Expand Up @@ -244,7 +244,7 @@ def test_globcurrent_pset_fromfile(dt, pid_offset, tmpdir):
pset.execute(parcels.AdvectionRK4, runtime=timedelta(days=1), dt=dt)
pset_new.execute(parcels.AdvectionRK4, runtime=timedelta(days=1), dt=dt)

for var in ["lon", "lat", "depth", "time", "id"]:
for var in ["lon", "lat", "depth", "time", "trajectory"]:
assert np.allclose(
[getattr(p, var) for p in pset], [getattr(p, var) for p in pset_new]
)
Expand Down
21 changes: 21 additions & 0 deletions parcels/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,24 @@
from sklearn.cluster import KMeans # type: ignore[no-redef]
except ModuleNotFoundError:
pass


# for compat with v3 of parcels when users provide `initial=attrgetter("lon")` to a Variable
# so that particle initial state matches another variable
class _AttrgetterHelper:
"""
Example usage

>>> _attrgetter_helper = _AttrgetterHelper()
>>> _attrgetter_helper.some_attribute
'some_attribute'
>>> from operator import attrgetter
>>> attrgetter('some_attribute')(_attrgetter_helper)
'some_attribute'
"""

def __getattr__(self, name):
return name


_attrgetter_helper = _AttrgetterHelper()
14 changes: 7 additions & 7 deletions parcels/application_kernels/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ def NearestNeighborWithinRange(particle, fieldset, time, neighbors, mutator):
# undesirable results.
if dist < min_dist or min_dist < 0:
min_dist = dist
neighbor_id = n.id
neighbor_id = n.trajectory

def f(p, neighbor):
p.nearest_neighbor = neighbor

mutator[particle.id].append((f, [neighbor_id]))
mutator[particle.trajectory].append((f, [neighbor_id]))

return StatusCode.Success

Expand All @@ -54,14 +54,14 @@ def merge_with_neighbor(p, nlat, nlon, ndepth, nmass):
p.mass = p.mass + nmass

for n in neighbors:
if n.id == particle.nearest_neighbor:
if n.nearest_neighbor == particle.id and particle.id < n.id:
if n.trajectory == particle.nearest_neighbor:
if n.nearest_neighbor == particle.trajectory and particle.trajectory < n.trajectory:
# Merge particles:
# Delete neighbor
mutator[n.id].append((delete_particle, ()))
mutator[n.trajectory].append((delete_particle, ()))
# Take position at the mid point and sum of masses
args = np.array([n.lat, n.lon, n.depth, n.mass])
mutator[particle.id].append((merge_with_neighbor, args))
mutator[particle.trajectory].append((merge_with_neighbor, args))

return StatusCode.Success
else:
Expand Down Expand Up @@ -101,6 +101,6 @@ def f(n, dlat, dlon, ddepth):
n.lon_nextloop += dlon
n.depth_nextloop += ddepth

mutator[n.id].append((f, d_vec))
mutator[n.trajectory].append((f, d_vec))

return StatusCode.Success
10 changes: 5 additions & 5 deletions parcels/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
VectorType,
assert_valid_mesh,
)
from parcels.particle import Particle
from parcels.particle import KernelParticle
from parcels.tools.converters import (
UnitConverter,
unitconverters_map,
Expand All @@ -37,9 +37,9 @@


def _deal_with_errors(error, key, vector_type: VectorType):
if isinstance(key, Particle):
if isinstance(key, KernelParticle):
key.state = AllParcelsErrorCodes[type(error)]
elif isinstance(key[-1], Particle):
elif isinstance(key[-1], KernelParticle):
key[-1].state = AllParcelsErrorCodes[type(error)]
else:
raise RuntimeError(f"{error}. Error could not be handled because particle was not part of the Field Sampling.")
Expand Down Expand Up @@ -278,7 +278,7 @@ def eval(self, time: datetime, z, y, x, particle=None, applyConversion=True):
def __getitem__(self, key):
self._check_velocitysampling()
try:
if isinstance(key, Particle):
if isinstance(key, KernelParticle):
return self.eval(key.time, key.depth, key.lat, key.lon, key)
else:
return self.eval(*key)
Expand Down Expand Up @@ -373,7 +373,7 @@ def eval(self, time: datetime, z, y, x, particle=None, applyConversion=True):

def __getitem__(self, key):
try:
if isinstance(key, Particle):
if isinstance(key, KernelParticle):
return self.eval(key.time, key.depth, key.lat, key.lon, key)
else:
return self.eval(*key)
Expand Down
4 changes: 2 additions & 2 deletions parcels/interaction/interactionkernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def execute_python(self, pset, endtime, dt):
for particle_idx in active_idx:
p = pset[particle_idx]
try:
for mutator_func, args in mutator[p.id]:
for mutator_func, args in mutator[p.trajectory]:
mutator_func(p, *args)
except KeyError:
pass
Expand Down Expand Up @@ -201,7 +201,7 @@ def execute(self, pset, endtime, dt, output_file=None):
pass
else:
warnings.warn(
f"Deleting particle {p.id} because of non-recoverable error",
f"Deleting particle {p.trajectory} because of non-recoverable error",
RuntimeWarning,
stacklevel=2,
)
Expand Down
2 changes: 1 addition & 1 deletion parcels/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def execute(self, pset, endtime, dt):
pass
else:
warnings.warn(
f"Deleting particle {p.id} because of non-recoverable error",
f"Deleting particle {p.trajectory} because of non-recoverable error",
RuntimeWarning,
stacklevel=2,
)
Expand Down
Loading
Loading