Skip to content

Commit 0d419a2

Browse files
Merge pull request #2402 from Parcels-code/statuscodes_documentation_v4
Add Statuscodes guide v4
2 parents 142f833 + 94aa170 commit 0d419a2

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
file_format: mystnb
3+
kernelspec:
4+
name: python3
5+
---
6+
7+
# Working with Status Codes
8+
9+
In order to capture errors in the [Kernel loop](explanation_kernelloop.md), Parcels uses a Status Code system. There are several Status Codes, listed below.
10+
11+
```{code-cell}
12+
import parcels
13+
14+
for statuscode, val in parcels.StatusCode.__dict__.items():
15+
if statuscode.startswith("__"):
16+
continue
17+
print(f"{statuscode} = {val}")
18+
```
19+
20+
Once an error is thrown (for example, a Field Interpolation error), then the `particles.state` is updated to the corresponding status code. This gives you the flexibility to write a Kernel that checks for a status code and does something with it.
21+
22+
For example, you can write a Kernel that checks for `particles.state == parcels.StatusCode.ErrorOutOfBounds` and deletes the particle, and then append this custom Kernel to the Kernel list in `pset.execute()`.
23+
24+
```
25+
def DeleteOutOfBounds(particles, fieldset):
26+
out_of_bounds = particles.state == parcels.StatusCode.ErrorOutOfBounds
27+
particles[out_of_bounds].state = parcels.StatusCode.Delete
28+
29+
30+
def DeleteAnyError(particles, fieldset):
31+
any_error = particles.state >= 50 # This captures all Errors
32+
particles[any_error].state = parcels.StatusCode.Delete
33+
```
34+
35+
But of course, you can also write code for more sophisticated behaviour than just deleting the particle. It's up to you! Note that if you don't delete the particle, you will have to update the `particles.state = parcels.StatusCode.Evaluate` yourself. For example:
36+
37+
```
38+
def Move1DegreeWest(particles, fieldset):
39+
out_of_bounds = particles.state == parcels.StatusCode.ErrorOutOfBounds
40+
particles[out_of_bounds].dlon -= 1.0
41+
particles[out_of_bounds].state = parcels.StatusCode.Evaluate
42+
```
43+
44+
Or, if you want to make sure that particles don't escape through the water surface
45+
46+
```{code-cell}
47+
def KeepInOcean(particles, fieldset):
48+
# find particles that move through the surface
49+
through_surface = particles.state == parcels.StatusCode.ErrorThroughSurface
50+
51+
# move particles to surface
52+
particles[through_surface].dz = fieldset.W.grid.depth[0] - particles[through_surface].z
53+
54+
# change state from error to evaluate
55+
particles[through_surface].state = parcels.StatusCode.Evaluate
56+
```
57+
58+
Kernel functions such as the ones above can then be added to the list of kernels in `pset.execute()`.
59+
60+
Let's add the `KeepInOcean` Kernel to an particle simulation where particles move through the surface:
61+
62+
```{code-cell}
63+
import numpy as np
64+
from parcels._datasets.structured.generated import simple_UV_dataset
65+
66+
ds = simple_UV_dataset(dims=(1, 2, 5, 4), mesh="flat").isel(time=0)
67+
68+
dx, dy = 1.0 / len(ds.XG), 1.0 / len(ds.YG)
69+
70+
# Add W velocity that pushes through surface
71+
ds["W"] = ds["U"] - 0.1 # 0.1 m/s towards the surface
72+
73+
grid = parcels.XGrid.from_dataset(ds, mesh="flat")
74+
U = parcels.Field("U", ds["U"], grid, interp_method=parcels.interpolators.XLinear)
75+
V = parcels.Field("V", ds["V"], grid, interp_method=parcels.interpolators.XLinear)
76+
W = parcels.Field("W", ds["W"], grid, interp_method=parcels.interpolators.XLinear)
77+
UVW = parcels.VectorField("UVW", U, V, W)
78+
fieldset = parcels.FieldSet([U, V, W, UVW])
79+
```
80+
81+
If we advect particles with the `AdvectionRK2_3D` kernel, Parcels will raise a `FieldOutOfBoundSurfaceError`:
82+
83+
```{code-cell}
84+
:tags: [raises-exception]
85+
pset = parcels.ParticleSet(fieldset, parcels.Particle, z=[0.5], lat=[2], lon=[1.5])
86+
kernels = [parcels.kernels.AdvectionRK2_3D]
87+
pset.execute(kernels, runtime=np.timedelta64(1, "m"), dt=np.timedelta64(1, "s"), verbose_progress=False)
88+
```
89+
90+
When we add the `KeepInOcean` Kernel, particles will stay at the surface:
91+
92+
```{code-cell}
93+
pset = parcels.ParticleSet(fieldset, parcels.Particle, z=[0.5], lat=[2], lon=[1.5])
94+
95+
kernels = [parcels.kernels.AdvectionRK2_3D, KeepInOcean]
96+
97+
pset.execute(kernels,runtime=np.timedelta64(20, "s"), dt=np.timedelta64(1, "s"), verbose_progress=False)
98+
99+
print(f"particle z at end of run = {pset.z}")
100+
```
101+
102+
```{note}
103+
Kernels that control what to do with `particles.state` should typically be added at the _end_ of the Kernel list, because otherwise later Kernels may overwrite the `particles.state` or the `particles.dlon` variables (see [Kernel loop explanation](explanation_kernelloop.md)).
104+
```

docs/user_guide/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ examples/tutorial_delaystart.ipynb
4646
4747
examples/explanation_kernelloop.md
4848
examples/tutorial_sampling.ipynb
49+
examples/tutorial_statuscodes.ipynb
4950
examples/tutorial_gsw_density.ipynb
5051
examples/tutorial_Argofloats.ipynb
5152
```

0 commit comments

Comments
 (0)