Skip to content

Commit 2abecb1

Browse files
Merge pull request #2452 from Parcels-code/reprs_for_v4
Adding reprs for all relevant classes in v4
2 parents 92fe65a + 9790413 commit 2abecb1

File tree

10 files changed

+177
-77
lines changed

10 files changed

+177
-77
lines changed

src/parcels/_core/field.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from parcels._core.uxgrid import UxGrid
2525
from parcels._core.xgrid import XGrid, _transpose_xfield_data_to_tzyx, assert_all_field_dims_have_axis
2626
from parcels._python import assert_same_function_signature
27-
from parcels._reprs import default_repr
27+
from parcels._reprs import field_repr, vectorfield_repr
2828
from parcels._typing import VectorType
2929
from parcels.interpolators import (
3030
ZeroInterpolator,
@@ -148,6 +148,9 @@ def __init__(
148148
if "time" not in self.data.coords:
149149
raise ValueError("Field data is missing a 'time' coordinate.")
150150

151+
def __repr__(self):
152+
return field_repr(self)
153+
151154
@property
152155
def units(self):
153156
return self._units
@@ -277,11 +280,7 @@ def __init__(
277280
self._vector_interp_method = vector_interp_method
278281

279282
def __repr__(self):
280-
return f"""<{type(self).__name__}>
281-
name: {self.name!r}
282-
U: {default_repr(self.U)}
283-
V: {default_repr(self.V)}
284-
W: {default_repr(self.W)}"""
283+
return vectorfield_repr(self)
285284

286285
@property
287286
def vector_interp_method(self):

src/parcels/_core/fieldset.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from parcels._core.uxgrid import UxGrid
1919
from parcels._core.xgrid import _DEFAULT_XGCM_KWARGS, XGrid
2020
from parcels._logger import logger
21+
from parcels._reprs import fieldset_repr
2122
from parcels._typing import Mesh
2223
from parcels.interpolators import UxPiecewiseConstantFace, UxPiecewiseLinearNode, XConstantField, XLinear
2324

@@ -75,6 +76,9 @@ def __getattr__(self, name):
7576
else:
7677
raise AttributeError(f"FieldSet has no attribute '{name}'")
7778

79+
def __repr__(self):
80+
return fieldset_repr(self)
81+
7882
@property
7983
def time_interval(self):
8084
"""Returns the valid executable time interval of the FieldSet,

src/parcels/_core/particle.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from parcels._core.statuscodes import StatusCode
1010
from parcels._core.utils.string import _assert_str_and_python_varname
1111
from parcels._core.utils.time import TimeInterval
12-
from parcels._reprs import _format_list_items_multiline
12+
from parcels._reprs import particleclass_repr, variable_repr
1313

1414
__all__ = ["Particle", "ParticleClass", "Variable"]
1515
_TO_WRITE_OPTIONS = [True, False, "once"]
@@ -70,7 +70,7 @@ def name(self):
7070
return self._name
7171

7272
def __repr__(self):
73-
return f"Variable(name={self._name!r}, dtype={self.dtype!r}, initial={self.initial!r}, to_write={self.to_write!r}, attrs={self.attrs!r})"
73+
return variable_repr(self)
7474

7575

7676
class ParticleClass:
@@ -92,8 +92,7 @@ def __init__(self, variables: list[Variable]):
9292
self.variables = variables
9393

9494
def __repr__(self):
95-
vars = [repr(v) for v in self.variables]
96-
return f"ParticleClass(variables={_format_list_items_multiline(vars)})"
95+
return particleclass_repr(self)
9796

9897
def add_variable(self, variable: Variable | list[Variable]):
9998
"""Add a new variable to the Particle class. This returns a new Particle class with the added variable(s).

src/parcels/_core/particlefile.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import parcels
1717
from parcels._core.particle import ParticleClass
1818
from parcels._core.utils.time import timedelta_to_float
19+
from parcels._reprs import particlefile_repr
1920

2021
if TYPE_CHECKING:
2122
from parcels._core.particle import Variable
@@ -96,12 +97,7 @@ def __init__(self, store, outputdt, chunks=None, create_new_zarrfile=True):
9697
# TODO v4: Add check that if create_new_zarrfile is False, the store already exists
9798

9899
def __repr__(self) -> str:
99-
return (
100-
f"{type(self).__name__}("
101-
f"outputdt={self.outputdt!r}, "
102-
f"chunks={self.chunks!r}, "
103-
f"create_new_zarrfile={self.create_new_zarrfile!r})"
104-
)
100+
return particlefile_repr(self)
105101

106102
def set_metadata(self, parcels_grid_mesh: Literal["spherical", "flat"]):
107103
self.metadata.update(

src/parcels/_core/particleset.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import numpy as np
88
import xarray as xr
99
from tqdm import tqdm
10-
from zarr.storage import DirectoryStore
1110

1211
from parcels._core.converters import _convert_to_flat_array
1312
from parcels._core.kernel import Kernel
@@ -21,7 +20,7 @@
2120
)
2221
from parcels._core.warnings import ParticleSetWarning
2322
from parcels._logger import logger
24-
from parcels._reprs import particleset_repr
23+
from parcels._reprs import _format_zarr_output_location, particleset_repr
2524

2625
__all__ = ["ParticleSet"]
2726

@@ -70,7 +69,6 @@ def __init__(
7069
**kwargs,
7170
):
7271
self._data = None
73-
self._repeat_starttime = None
7472
self._kernel = None
7573

7674
self.fieldset = fieldset
@@ -167,7 +165,7 @@ def __getattr__(self, name):
167165

168166
def __getitem__(self, index):
169167
"""Get a single particle by index."""
170-
return ParticleSetView(self._data, index=index)
168+
return ParticleSetView(self._data, index=index, ptype=self._ptype)
171169

172170
def __setattr__(self, name, value):
173171
if name in ["_data"]:
@@ -447,7 +445,7 @@ def execute(
447445

448446
# Set up pbar
449447
if output_file:
450-
logger.info(f"Output files are stored in {_format_output_location(output_file.store)}")
448+
logger.info(f"Output files are stored in {_format_zarr_output_location(output_file.store)}")
451449

452450
if verbose_progress:
453451
pbar = tqdm(total=end_time - start_time, file=sys.stdout)
@@ -592,9 +590,3 @@ def _get_start_time(first_release_time, time_interval, sign_dt, runtime):
592590

593591
start_time = first_release_time if not np.isnan(first_release_time) else fieldset_start
594592
return start_time
595-
596-
597-
def _format_output_location(zarr_obj):
598-
if isinstance(zarr_obj, DirectoryStore):
599-
return zarr_obj.path
600-
return repr(zarr_obj)

src/parcels/_core/particlesetview.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import numpy as np
22

3+
from parcels._reprs import particlesetview_repr
4+
35

46
class ParticleSetView:
57
"""Class to be used in a kernel that links a View of the ParticleSet (on the kernel level) to a ParticleSet."""
68

7-
def __init__(self, data, index):
9+
def __init__(self, data, index, ptype):
810
self._data = data
911
self._index = index
12+
self._ptype = ptype
1013

1114
def __getattr__(self, name):
1215
# Return a proxy that behaves like the underlying numpy array but
@@ -25,11 +28,14 @@ def __getattr__(self, name):
2528
return self._data[name][self._index]
2629

2730
def __setattr__(self, name, value):
28-
if name in ["_data", "_index"]:
31+
if name in ["_data", "_index", "_ptype"]:
2932
object.__setattr__(self, name, value)
3033
else:
3134
self._data[name][self._index] = value
3235

36+
def __repr__(self):
37+
return particlesetview_repr(self)
38+
3339
def __getitem__(self, index):
3440
# normalize single-element tuple indexing (e.g., (inds,))
3541
if isinstance(index, tuple) and len(index) == 1:
@@ -50,7 +56,7 @@ def __getitem__(self, index):
5056
raise ValueError(
5157
f"Boolean index has incompatible length {arr.size} for selection of size {int(np.sum(base))}"
5258
)
53-
return ParticleSetView(self._data, new_index)
59+
return ParticleSetView(self._data, new_index, self._ptype)
5460

5561
# Integer array/list, slice or single integer relative to the local view
5662
# (boolean masks were handled above). Normalize and map to global
@@ -65,12 +71,12 @@ def __getitem__(self, index):
6571
base_arr = np.asarray(base)
6672
sel = base_arr[idx]
6773
new_index[sel] = True
68-
return ParticleSetView(self._data, new_index)
74+
return ParticleSetView(self._data, new_index, self._ptype)
6975

7076
# Fallback: try to assign directly (preserves previous behaviour for other index types)
7177
try:
7278
new_index[base] = index
73-
return ParticleSetView(self._data, new_index)
79+
return ParticleSetView(self._data, new_index, self._ptype)
7480
except Exception as e:
7581
raise TypeError(f"Unsupported index type for ParticleSetView.__getitem__: {type(index)!r}") from e
7682

src/parcels/_core/utils/time.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import cftime
77
import numpy as np
88

9+
from parcels._reprs import timeinterval_repr
10+
911
if TYPE_CHECKING:
1012
from parcels._typing import TimeLike
1113

@@ -61,7 +63,7 @@ def is_all_time_in_interval(self, time: float):
6163
return (0 <= item).all() and (item <= self.time_length_as_flt).all()
6264

6365
def __repr__(self) -> str:
64-
return f"TimeInterval(left={self.left!r}, right={self.right!r})"
66+
return timeinterval_repr(self)
6567

6668
def __eq__(self, other: object) -> bool:
6769
if not isinstance(other, TimeInterval):

src/parcels/_core/xgrid.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from parcels._core.basegrid import BaseGrid
1111
from parcels._core.index_search import _search_1d_array, _search_indices_curvilinear_2d
12+
from parcels._reprs import xgrid_repr
1213
from parcels._typing import assert_valid_mesh
1314

1415
_XGRID_AXES = Literal["X", "Y", "Z"]
@@ -135,6 +136,9 @@ def from_dataset(cls, ds: xr.Dataset, mesh, xgcm_kwargs=None):
135136
grid = xgcm.Grid(ds, **xgcm_kwargs)
136137
return cls(grid, mesh=mesh)
137138

139+
def __repr__(self):
140+
return xgrid_repr(self)
141+
138142
@property
139143
def axes(self) -> list[_XGRID_AXES]:
140144
return _get_xgrid_axes(self.xgcm_grid)

0 commit comments

Comments
 (0)