Repository: OceanParcels/Parcels (python)

Issue:
Improve `repr`s for key Parcels classes
So far, we haven't really worked on the output of `print()` on objects like `Field`, `FieldSet` etc. While there is a [`ParticleSet.__repr__`](https://github.com/OceanParcels/Parcels/blob/5fff42c2ea4cc12d03df8c936dd391e251658c8f/parcels/particleset.py#L339-L340), even that is pretty clunky and doesn't take into account notebook functionality.

It would be nice to have better `print()` implementation for at least the following classes
- [x] `Field`
- [x] `FieldSet`
- [x] `ParticleSet`
- [x] `Particle`
- [x] `ParticleFile`
- ~~`Kernel`(?)~~ EDIT Vecko: Out of scope

Pull request description:
Update ParticleType repr
Changes:
- Add ParticleType repr (fixes #1693)
- Patch indentation for pset
- xfail test_mitgcm for "auto" chunk_mode (xref #1762)

Indentation before:

```
<ParticleSet>
    fieldset   : <FieldSet>
    fields:
        <Field>
            name            : 'U'
            grid            : RectilinearZGrid(lon=array([ 0.00,  0.05,  0.11, ...,  0.89,  0.95,  1.00], dtype=float32), lat=array([ 0.00,  0.05,  0.11, ...,  0.89,  0.95,  1.00], dtype=float32), time=array([ 0.00]), time_origin=0, mesh='flat')
            extrapolate time: True
            time_periodic   : False
            gridindexingtype: 'nemo'
            to_write        : False
        <Field>
            name            : 'V'
            grid            : RectilinearZGrid(lon=array([ 0.00,  0.05,  0.11, ...,  0.89,  0.95,  1.00], dtype=float32), lat=array([ 0.00,  0.05,  0.11, ...,  0.89,  0.95,  1.00], dtype=float32), time=array([ 0.00]), time_origin=0, mesh='flat')
            extrapolate time: True
            time_periodic   : False
            gridindexingtype: 'nemo'
            to_write        : False
        <VectorField>
            name: 'UV'
            U: <parcels.field.Field object at 0x10562acb0>
            V: <parcels.field.Field object at 0x163543a30>
            W: None
    pclass     : <class 'parcels.particle.ScipyParticle'>
    repeatdt   : None
    # particles: 1
    particles  : [
        P[0](lon=0.000000, lat=0.000000, depth=0.000000, time=0.000000)
    ]

```

After:
```
<ParticleSet>
    fieldset   :
        <FieldSet>
            fields:
                <Field>
                    name            : 'U'
                    grid            : RectilinearZGrid(lon=array([ 0.00,  0.05,  0.11, ...,  0.89,  0.95,  1.00], dtype=float32), lat=array([ 0.00,  0.05,  0.11, ...,  0.89,  0.95,  1.00], dtype=float32), time=array([ 0.00]), time_origin=0, mesh='flat')
                    extrapolate time: True
                    time_periodic   : False
                    gridindexingtype: 'nemo'
                    to_write        : False
                <Field>
                    name            : 'V'
                    grid            : RectilinearZGrid(lon=array([ 0.00,  0.05,  0.11, ...,  0.89,  0.95,  1.00], dtype=float32), lat=array([ 0.00,  0.05,  0.11, ...,  0.89,  0.95,  1.00], dtype=float32), time=array([ 0.00]), time_origin=0, mesh='flat')
                    extrapolate time: True
                    time_periodic   : False
                    gridindexingtype: 'nemo'
                    to_write        : False
                <VectorField>
                    name: 'UV'
                    U: <parcels.field.Field object at 0x104866cb0>
                    V: <parcels.field.Field object at 0x168d11270>
                    W: None
    pclass     : <class 'parcels.particle.ScipyParticle'>
    repeatdt   : None
    # particles: 1
    particles  : [
        P[0](lon=0.000000, lat=0.000000, depth=0.000000, time=0.000000)
    ]

```

Patch:
diff --git a/docs/examples/example_dask_chunk_OCMs.py b/docs/examples/example_dask_chunk_OCMs.py
index fd2b2d91..9a4cf7f5 100644
--- a/docs/examples/example_dask_chunk_OCMs.py
+++ b/docs/examples/example_dask_chunk_OCMs.py
@@ -483,7 +483,18 @@ def test_ofam_3D(mode, chunk_mode):
 
 @pytest.mark.parametrize("mode", ["jit"])
 @pytest.mark.parametrize(
-    "chunk_mode", [False, "auto", "specific_same", "specific_different"]
+    "chunk_mode",
+    [
+        False,
+        pytest.param(
+            "auto",
+            marks=pytest.mark.xfail(
+                reason="Dask v2024.11.0 caused auto chunking to fail. See #1762"
+            ),
+        ),
+        "specific_same",
+        "specific_different",
+    ],
 )
 @pytest.mark.parametrize("using_add_field", [False, True])
 def test_mitgcm(mode, chunk_mode, using_add_field):
diff --git a/docs/examples/parcels_tutorial.ipynb b/docs/examples/parcels_tutorial.ipynb
index 102a8741..3077dfe5 100644
--- a/docs/examples/parcels_tutorial.ipynb
+++ b/docs/examples/parcels_tutorial.ipynb
@@ -183,27 +183,28 @@
      "output_type": "stream",
      "text": [
       "<ParticleSet>\n",
-      "    fieldset   : <FieldSet>\n",
-      "    fields:\n",
-      "        <Field>\n",
-      "            name            : 'U'\n",
-      "            grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 0.00,  86400.00]), time_origin=0.0, mesh='flat')\n",
-      "            extrapolate time: False\n",
-      "            time_periodic   : False\n",
-      "            gridindexingtype: 'nemo'\n",
-      "            to_write        : False\n",
-      "        <Field>\n",
-      "            name            : 'V'\n",
-      "            grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 0.00,  86400.00]), time_origin=0.0, mesh='flat')\n",
-      "            extrapolate time: False\n",
-      "            time_periodic   : False\n",
-      "            gridindexingtype: 'nemo'\n",
-      "            to_write        : False\n",
-      "        <VectorField>\n",
-      "            name: 'UV'\n",
-      "            U: <parcels.field.Field object at 0x1580e0980>\n",
-      "            V: <parcels.field.Field object at 0x1585d8920>\n",
-      "            W: None\n",
+      "    fieldset   :\n",
+      "        <FieldSet>\n",
+      "            fields:\n",
+      "                <Field>\n",
+      "                    name            : 'U'\n",
+      "                    grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 0.00,  86400.00]), time_origin=0.0, mesh='flat')\n",
+      "                    extrapolate time: False\n",
+      "                    time_periodic   : False\n",
+      "                    gridindexingtype: 'nemo'\n",
+      "                    to_write        : False\n",
+      "                <Field>\n",
+      "                    name            : 'V'\n",
+      "                    grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 0.00,  86400.00]), time_origin=0.0, mesh='flat')\n",
+      "                    extrapolate time: False\n",
+      "                    time_periodic   : False\n",
+      "                    gridindexingtype: 'nemo'\n",
+      "                    to_write        : False\n",
+      "                <VectorField>\n",
+      "                    name: 'UV'\n",
+      "                    U: <parcels.field.Field object at 0x164112020>\n",
+      "                    V: <parcels.field.Field object at 0x164111a80>\n",
+      "                    W: None\n",
       "    pclass     : <class 'parcels.particle.JITParticle'>\n",
       "    repeatdt   : None\n",
       "    # particles: 2\n",
@@ -338,27 +339,28 @@
      "output_type": "stream",
      "text": [
       "<ParticleSet>\n",
-      "    fieldset   : <FieldSet>\n",
-      "    fields:\n",
-      "        <Field>\n",
-      "            name            : 'U'\n",
-      "            grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 432000.00,  518400.00]), time_origin=0.0, mesh='flat')\n",
-      "            extrapolate time: False\n",
-      "            time_periodic   : False\n",
-      "            gridindexingtype: 'nemo'\n",
-      "            to_write        : False\n",
-      "        <Field>\n",
-      "            name            : 'V'\n",
-      "            grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 432000.00,  518400.00]), time_origin=0.0, mesh='flat')\n",
-      "            extrapolate time: False\n",
-      "            time_periodic   : False\n",
-      "            gridindexingtype: 'nemo'\n",
-      "            to_write        : False\n",
-      "        <VectorField>\n",
-      "            name: 'UV'\n",
-      "            U: <parcels.field.Field object at 0x1580e0980>\n",
-      "            V: <parcels.field.Field object at 0x1585d8920>\n",
-      "            W: None\n",
+      "    fieldset   :\n",
+      "        <FieldSet>\n",
+      "            fields:\n",
+      "                <Field>\n",
+      "                    name            : 'U'\n",
+      "                    grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 432000.00,  518400.00]), time_origin=0.0, mesh='flat')\n",
+      "                    extrapolate time: False\n",
+      "                    time_periodic   : False\n",
+      "                    gridindexingtype: 'nemo'\n",
+      "                    to_write        : False\n",
+      "                <Field>\n",
+      "                    name            : 'V'\n",
+      "                    grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 432000.00,  518400.00]), time_origin=0.0, mesh='flat')\n",
+      "                    extrapolate time: False\n",
+      "                    time_periodic   : False\n",
+      "                    gridindexingtype: 'nemo'\n",
+      "                    to_write        : False\n",
+      "                <VectorField>\n",
+      "                    name: 'UV'\n",
+      "                    U: <parcels.field.Field object at 0x164112020>\n",
+      "                    V: <parcels.field.Field object at 0x164111a80>\n",
+      "                    W: None\n",
       "    pclass     : <class 'parcels.particle.JITParticle'>\n",
       "    repeatdt   : None\n",
       "    # particles: 2\n",
@@ -13338,27 +13340,28 @@
      "output_type": "stream",
      "text": [
       "<ParticleSet>\n",
-      "    fieldset   : <FieldSet>\n",
-      "    fields:\n",
-      "        <Field>\n",
-      "            name            : 'U'\n",
-      "            grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 0.00,  86400.00]), time_origin=0.0, mesh='flat')\n",
-      "            extrapolate time: False\n",
-      "            time_periodic   : False\n",
-      "            gridindexingtype: 'nemo'\n",
-      "            to_write        : False\n",
-      "        <Field>\n",
-      "            name            : 'V'\n",
-      "            grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 0.00,  86400.00]), time_origin=0.0, mesh='flat')\n",
-      "            extrapolate time: False\n",
-      "            time_periodic   : False\n",
-      "            gridindexingtype: 'nemo'\n",
-      "            to_write        : False\n",
-      "        <VectorField>\n",
-      "            name: 'UV'\n",
-      "            U: <parcels.field.Field object at 0x1580e0980>\n",
-      "            V: <parcels.field.Field object at 0x1585d8920>\n",
-      "            W: None\n",
+      "    fieldset   :\n",
+      "        <FieldSet>\n",
+      "            fields:\n",
+      "                <Field>\n",
+      "                    name            : 'U'\n",
+      "                    grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 0.00,  86400.00]), time_origin=0.0, mesh='flat')\n",
+      "                    extrapolate time: False\n",
+      "                    time_periodic   : False\n",
+      "                    gridindexingtype: 'nemo'\n",
+      "                    to_write        : False\n",
+      "                <Field>\n",
+      "                    name            : 'V'\n",
+      "                    grid            : RectilinearZGrid(lon=array([ 0.00,  2010.05,  4020.10, ...,  395979.91,  397989.94,  400000.00], dtype=float32), lat=array([ 0.00,  2005.73,  4011.46, ...,  695988.56,  697994.25,  700000.00], dtype=float32), time=array([ 0.00,  86400.00]), time_origin=0.0, mesh='flat')\n",
+      "                    extrapolate time: False\n",
+      "                    time_periodic   : False\n",
+      "                    gridindexingtype: 'nemo'\n",
+      "                    to_write        : False\n",
+      "                <VectorField>\n",
+      "                    name: 'UV'\n",
+      "                    U: <parcels.field.Field object at 0x164112020>\n",
+      "                    V: <parcels.field.Field object at 0x164111a80>\n",
+      "                    W: None\n",
       "    pclass     : <class 'parcels.particle.JITParticle'>\n",
       "    repeatdt   : None\n",
       "    # particles: 2\n",
@@ -13818,7 +13821,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 27,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [
     {
diff --git a/parcels/compilation/codegenerator.py b/parcels/compilation/codegenerator.py
index 3038f234..33351ac6 100644
--- a/parcels/compilation/codegenerator.py
+++ b/parcels/compilation/codegenerator.py
@@ -3,7 +3,6 @@ import collections
 import math
 import random
 import warnings
-from abc import ABC
 from copy import copy
 
 import cgen as c
@@ -412,7 +411,7 @@ class TupleSplitter(ast.NodeTransformer):
         return node
 
 
-class KernelGenerator(ABC, ast.NodeVisitor):
+class KernelGenerator(ast.NodeVisitor):
     """Code generator class that translates simple Python kernel functions into C functions.
 
     Works by populating and accessing the `ccode` attribute on nodes in the Python AST.
diff --git a/parcels/particle.py b/parcels/particle.py
index 87dce269..08e46ff0 100644
--- a/parcels/particle.py
+++ b/parcels/particle.py
@@ -96,7 +96,7 @@ class ParticleType:
         self.variables = [v for v in self.variables if v.is64bit()] + [v for v in self.variables if not v.is64bit()]
 
     def __repr__(self):
-        return f"PType<{self.name}>::{self.variables}"
+        return f"{type(self).__name__}(pclass={self.name})"
 
     def __getitem__(self, item):
         for v in self.variables:
diff --git a/parcels/tools/_helpers.py b/parcels/tools/_helpers.py
index 1f2d3feb..8701f58f 100644
--- a/parcels/tools/_helpers.py
+++ b/parcels/tools/_helpers.py
@@ -115,7 +115,8 @@ def particleset_repr(pset: ParticleSet) -> str:
         particles = [repr(pset[i]) for i in range(7)] + ["..."]
 
     out = f"""<{type(pset).__name__}>
-    fieldset   : {pset.fieldset}
+    fieldset   :
+{textwrap.indent(repr(pset.fieldset), " " * 8)}
     pclass     : {pset.pclass}
     repeatdt   : {pset.repeatdt}
     # particles: {len(pset)}


Test patch:
diff --git a/tests/test_reprs.py b/tests/test_reprs.py
index bce4bce4..18ed68a0 100644
--- a/tests/test_reprs.py
+++ b/tests/test_reprs.py
@@ -51,6 +51,11 @@ test
     assert not valid_indentation(invalid)
 
 
+def test_particletype_repr():
+    kwargs = dict(pclass=parcels.JITParticle)
+    assert_simple_repr(parcels.particle.ParticleType, kwargs)
+
+
 def test_grid_repr():
     """Test arguments are in the repr of a Grid object"""
     kwargs = dict(