Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 17 additions & 9 deletions parcels/particleset.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def cptr(i):
raise ValueError("Latitude and longitude required for generating ParticleSet")

@classmethod
def from_list(cls, fieldset, pclass, lon, lat, depth=None, time=None, repeatdt=None, **kwargs):
def from_list(cls, fieldset, pclass, lon, lat, depth=None, time=None, repeatdt=None, lonlatdepth_dtype=None, **kwargs):
"""Initialise the ParticleSet from lists of lon and lat

:param fieldset: :mod:`parcels.fieldset.FieldSet` object from which to sample velocity
Expand All @@ -139,12 +139,15 @@ def from_list(cls, fieldset, pclass, lon, lat, depth=None, time=None, repeatdt=N
:param depth: Optional list of initial depth values for particles. Default is 0m
:param time: Optional list of start time values for particles. Default is fieldset.U.time[0]
:param repeatdt: Optional interval (in seconds) on which to repeat the release of the ParticleSet
:param lonlatdepth_dtype: Floating precision for lon, lat, depth particle coordinates.
It is either np.float32 or np.float64. Default is np.float32 if fieldset.U.interp_method is 'linear'
and np.float64 if the interpolation method is 'cgrid_velocity'
Other Variables can be initialised using further arguments (e.g. v=... for a Variable named 'v')
"""
return cls(fieldset=fieldset, pclass=pclass, lon=lon, lat=lat, depth=depth, time=time, repeatdt=repeatdt, **kwargs)
return cls(fieldset=fieldset, pclass=pclass, lon=lon, lat=lat, depth=depth, time=time, repeatdt=repeatdt, lonlatdepth_dtype=lonlatdepth_dtype, **kwargs)

@classmethod
def from_line(cls, fieldset, pclass, start, finish, size, depth=None, time=None, repeatdt=None):
def from_line(cls, fieldset, pclass, start, finish, size, depth=None, time=None, repeatdt=None, lonlatdepth_dtype=None):
"""Initialise the ParticleSet from start/finish coordinates with equidistant spacing
Note that this method uses simple numpy.linspace calls and does not take into account
great circles, so may not be a exact on a globe
Expand All @@ -158,17 +161,19 @@ def from_line(cls, fieldset, pclass, start, finish, size, depth=None, time=None,
:param depth: Optional list of initial depth values for particles. Default is 0m
:param time: Optional start time value for particles. Default is fieldset.U.time[0]
:param repeatdt: Optional interval (in seconds) on which to repeat the release of the ParticleSet
:param lonlatdepth_dtype: Floating precision for lon, lat, depth particle coordinates.
It is either np.float32 or np.float64. Default is np.float32 if fieldset.U.interp_method is 'linear'
and np.float64 if the interpolation method is 'cgrid_velocity'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want all those changes?
I thought we could simply set lonlatdepth_dtype through the **kwargs.
(but they were missing in from_line)
So both methods are obviously valid, but I don't know what's the most common one. And if the param is not in the argument list, then should it be in the docstring, or only in the docstring of pset constructor? Let's have a look how such parameters usually work)

"""

lonlat_type = cls.lonlatdepth_dtype_from_field_interp_method(fieldset.U)
lon = np.linspace(start[0], finish[0], size, dtype=lonlat_type)
lat = np.linspace(start[1], finish[1], size, dtype=lonlat_type)
lon = np.linspace(start[0], finish[0], size)
lat = np.linspace(start[1], finish[1], size)
if type(depth) in [int, float]:
depth = [depth] * size
return cls(fieldset=fieldset, pclass=pclass, lon=lon, lat=lat, depth=depth, time=time, repeatdt=repeatdt)
return cls(fieldset=fieldset, pclass=pclass, lon=lon, lat=lat, depth=depth, time=time, repeatdt=repeatdt, lonlatdepth_dtype=lonlatdepth_dtype)

@classmethod
def from_field(cls, fieldset, pclass, start_field, size, mode='monte_carlo', depth=None, time=None, repeatdt=None):
def from_field(cls, fieldset, pclass, start_field, size, mode='monte_carlo', depth=None, time=None, repeatdt=None, lonlatdepth_dtype=None):
"""Initialise the ParticleSet randomly drawn according to distribution from a field

:param fieldset: :mod:`parcels.fieldset.FieldSet` object from which to sample velocity
Expand All @@ -180,6 +185,9 @@ def from_field(cls, fieldset, pclass, start_field, size, mode='monte_carlo', dep
:param depth: Optional list of initial depth values for particles. Default is 0m
:param time: Optional start time value for particles. Default is fieldset.U.time[0]
:param repeatdt: Optional interval (in seconds) on which to repeat the release of the ParticleSet
:param lonlatdepth_dtype: Floating precision for lon, lat, depth particle coordinates.
It is either np.float32 or np.float64. Default is np.float32 if fieldset.U.interp_method is 'linear'
and np.float64 if the interpolation method is 'cgrid_velocity'
"""

if mode == 'monte_carlo':
Expand Down Expand Up @@ -217,7 +225,7 @@ def from_field(cls, fieldset, pclass, start_field, size, mode='monte_carlo', dep
else:
raise NotImplementedError('Mode %s not implemented. Please use "monte carlo" algorithm instead.' % mode)

return cls(fieldset=fieldset, pclass=pclass, lon=lon, lat=lat, depth=depth, time=time, repeatdt=repeatdt)
return cls(fieldset=fieldset, pclass=pclass, lon=lon, lat=lat, depth=depth, time=time, lonlatdepth_dtype=lonlatdepth_dtype, repeatdt=repeatdt)

@staticmethod
def lonlatdepth_dtype_from_field_interp_method(field):
Expand Down
18 changes: 12 additions & 6 deletions tests/test_particle_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ def test_pset_create_lon_lat(fieldset, mode, npart=100):


@pytest.mark.parametrize('mode', ['scipy', 'jit'])
def test_pset_create_line(fieldset, mode, npart=100):
lon = np.linspace(0, 1, npart, dtype=np.float32)
lat = np.linspace(1, 0, npart, dtype=np.float32)
pset = ParticleSet.from_line(fieldset, size=npart, start=(0, 1), finish=(1, 0), pclass=ptype[mode])
@pytest.mark.parametrize('lonlatdepth_dtype', [np.float64, np.float32])
def test_pset_create_line(fieldset, mode, lonlatdepth_dtype, npart=100):
lon = np.linspace(0, 1, npart, dtype=lonlatdepth_dtype)
lat = np.linspace(1, 0, npart, dtype=lonlatdepth_dtype)
pset = ParticleSet.from_line(fieldset, size=npart, start=(0, 1), finish=(1, 0),
pclass=ptype[mode], lonlatdepth_dtype=lonlatdepth_dtype)
assert np.allclose([p.lon for p in pset], lon, rtol=1e-12)
assert np.allclose([p.lat for p in pset], lat, rtol=1e-12)
assert isinstance(pset[0].lat, lonlatdepth_dtype)


@pytest.mark.parametrize('mode', ['scipy', 'jit'])
Expand All @@ -54,16 +57,19 @@ class MyParticle(ptype[mode]):


@pytest.mark.parametrize('mode', ['scipy'])
def test_pset_create_field(fieldset, mode, npart=100):
@pytest.mark.parametrize('lonlatdepth_dtype', [np.float64, np.float32])
def test_pset_create_field(fieldset, mode, lonlatdepth_dtype, npart=100):
np.random.seed(123456)
shape = (fieldset.U.lon.size, fieldset.U.lat.size)
K = Field('K', lon=fieldset.U.lon, lat=fieldset.U.lat,
data=np.ones(shape, dtype=np.float32), transpose=True)
pset = ParticleSet.from_field(fieldset, size=npart, pclass=ptype[mode], start_field=K)
pset = ParticleSet.from_field(fieldset, size=npart, pclass=ptype[mode],
start_field=K, lonlatdepth_dtype=lonlatdepth_dtype)
assert (np.array([p.lon for p in pset]) <= K.lon[-1]).all()
assert (np.array([p.lon for p in pset]) >= K.lon[0]).all()
assert (np.array([p.lat for p in pset]) <= K.lat[-1]).all()
assert (np.array([p.lat for p in pset]) >= K.lat[0]).all()
assert isinstance(pset[0].lat, lonlatdepth_dtype)


def test_pset_create_field_curvi(npart=100):
Expand Down