-
Notifications
You must be signed in to change notification settings - Fork 168
Add Statuscodes guide v4 #2402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Statuscodes guide v4 #2402
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,103 @@ | ||||||||
| --- | ||||||||
| file_format: mystnb | ||||||||
| kernelspec: | ||||||||
| name: python3 | ||||||||
| --- | ||||||||
|
|
||||||||
| # Working with Status Codes | ||||||||
|
|
||||||||
| 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. | ||||||||
|
|
||||||||
| ```{code-cell} | ||||||||
| import parcels | ||||||||
|
|
||||||||
| for statuscode, val in parcels.StatusCode.__dict__.items(): | ||||||||
| if statuscode.startswith("__"): | ||||||||
| continue | ||||||||
| print(f"{statuscode} = {val}") | ||||||||
| ``` | ||||||||
|
|
||||||||
| 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. | ||||||||
|
|
||||||||
| 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()`. | ||||||||
|
|
||||||||
| ``` | ||||||||
| def DeleteOutOfBounds(particles, fieldset): | ||||||||
| out_of_bounds = particles.state == parcels.StatusCode.ErrorOutOfBounds | ||||||||
| particles[out_of_bounds].state = parcels.StatusCode.Delete | ||||||||
|
|
||||||||
|
|
||||||||
| def DeleteAnyError(particles, fieldset): | ||||||||
| any_error = particles.state >= 50 # This captures all Errors | ||||||||
| particles[any_error].state = parcels.StatusCode.Delete | ||||||||
| ``` | ||||||||
|
|
||||||||
| 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: | ||||||||
|
|
||||||||
| ``` | ||||||||
| def Move1DegreeWest(particles, fieldset): | ||||||||
| out_of_bounds = particles.state == parcels.StatusCode.ErrorOutOfBounds | ||||||||
| particles[out_of_bounds].dlon -= 1.0 | ||||||||
| particles[out_of_bounds].state = parcels.StatusCode.Evaluate | ||||||||
| ``` | ||||||||
|
|
||||||||
| Or, if you want to make sure that particles don't escape through the water surface | ||||||||
|
|
||||||||
| ```{code-cell} | ||||||||
| def KeepInOcean(particles, fieldset): | ||||||||
| # find particles that move through the surface | ||||||||
| through_surface = particles.state == parcels.StatusCode.ErrorThroughSurface | ||||||||
|
|
||||||||
| # move particles to surface | ||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| particles[through_surface].dz = fieldset.W.grid.depth[0] - particles[through_surface].z | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line may be a bit magical to new users. Does this need a bit of information why this works? Or even replace with
Suggested change
even though we advice not to change particles.z directly?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have expanded both the description before the code-cell and the comment slightly. I personally like adapting |
||||||||
|
|
||||||||
| # change state from error to evaluate | ||||||||
| particles[through_surface].state = parcels.StatusCode.Evaluate | ||||||||
| ``` | ||||||||
|
|
||||||||
| Kernel functions such as the ones above can then be added to the list of kernels in `pset.execute()`. | ||||||||
|
|
||||||||
| Let's add the `KeepInOcean` Kernel to an particle simulation where particles move through the surface: | ||||||||
|
|
||||||||
| ```{code-cell} | ||||||||
| import numpy as np | ||||||||
| from parcels._datasets.structured.generated import simple_UV_dataset | ||||||||
|
|
||||||||
| ds = simple_UV_dataset(dims=(1, 2, 5, 4), mesh="flat").isel(time=0) | ||||||||
|
|
||||||||
| dx, dy = 1.0 / len(ds.XG), 1.0 / len(ds.YG) | ||||||||
|
|
||||||||
| # Add W velocity that pushes through surface | ||||||||
| ds["W"] = ds["U"] - 0.1 # 0.1 m/s towards the surface | ||||||||
|
|
||||||||
| grid = parcels.XGrid.from_dataset(ds, mesh="flat") | ||||||||
| U = parcels.Field("U", ds["U"], grid, interp_method=parcels.interpolators.XLinear) | ||||||||
| V = parcels.Field("V", ds["V"], grid, interp_method=parcels.interpolators.XLinear) | ||||||||
| W = parcels.Field("W", ds["W"], grid, interp_method=parcels.interpolators.XLinear) | ||||||||
| UVW = parcels.VectorField("UVW", U, V, W) | ||||||||
| fieldset = parcels.FieldSet([U, V, W, UVW]) | ||||||||
| ``` | ||||||||
|
|
||||||||
| If we advect particles with the `AdvectionRK2_3D` kernel, Parcels will raise a `FieldOutOfBoundSurfaceError`: | ||||||||
|
|
||||||||
| ```{code-cell} | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will it be a problem for the GitHub Action that this cell fails?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||||||||
| :tags: [raises-exception] | ||||||||
| pset = parcels.ParticleSet(fieldset, parcels.Particle, z=[0.5],lat=[2], lon=[1.5]) | ||||||||
reint-fischer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| pset.execute([parcels.kernels.AdvectionRK2_3D],runtime=np.timedelta64(1, "m"), dt=np.timedelta64(1, "s"), verbose_progress=False) | ||||||||
reint-fischer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| ``` | ||||||||
|
|
||||||||
| When we add the `KeepInOcean` Kernel, particles will stay at the surface: | ||||||||
|
|
||||||||
| ```{code-cell} | ||||||||
| pset = parcels.ParticleSet(fieldset, parcels.Particle, z=[0.5],lat=[2], lon=[1.5]) | ||||||||
reint-fischer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
|
|
||||||||
| kernels = [parcels.kernels.AdvectionRK2_3D, KeepInOcean] | ||||||||
|
|
||||||||
| pset.execute(kernels,runtime=np.timedelta64(20, "s"), dt=np.timedelta64(1, "s"), verbose_progress=False) | ||||||||
|
|
||||||||
| print(f"particle z at end of run = {pset.z}") | ||||||||
| ``` | ||||||||
|
|
||||||||
| ```{note} | ||||||||
| 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. | ||||||||
reint-fischer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| ``` | ||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.