Skip to content

Commit 4cfb9c9

Browse files
First batch of tutorial cleanups after #2719
1 parent ca5c8d0 commit 4cfb9c9

10 files changed

Lines changed: 128 additions & 139 deletions

docs/getting_started/explanation_concepts.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Here, we will explain the most important classes and functions. This overview ca
1313
A Parcels simulation is generally built up from four different components:
1414

1515
1. [**FieldSet**](#1-fieldset). The input dataset of gridded fields (e.g. ocean current velocity, temperature) in which virtual particles are defined.
16-
2. [**ParticleSet**](#2-particleset). The dataset of virtual particles. These always contain time, z, lat, and lon, for which initial values must be defined. The ParticleSet may also contain other, custom variables.
16+
2. [**ParticleSet**](#2-particleset). The dataset of virtual particles. These always contain time, z, y, and x, for which initial values must be defined. The ParticleSet may also contain other, custom variables.
1717
3. [**Kernels**](#3-kernels). Kernels perform some specific operation on the particles every time step (e.g. advect the particles with the three-dimensional flow; or interpolate the temperature field to the particle location).
1818
4. [**Execute**](#4-execute). Execute the simulation. The core method which integrates the operations defined in Kernels for a given runtime and timestep, and writes output to a ParticleFile.
1919

@@ -82,16 +82,16 @@ Once the environment has a `parcels.FieldSet` object, you can start defining you
8282

8383
1. The `parcels.FieldSet` object in which the particles will be released.
8484
2. The type of `parcels.Particle`: A default `Particle` or a custom `Particle`-type with additional `Variable`s (see the [custom kernel example](custom-kernel)).
85-
3. Initial conditions for each `Variable` defined in the `Particle`, most notably the release coordinates of `time`, `z`, `lat` and `lon`.
85+
3. Initial conditions for each `Variable` defined in the `Particle`, most notably the release coordinates of `time`, `z`, `y` and `x`.
8686

8787
```python
8888
time = np.array([0])
8989
z = np.array([0])
90-
lat = np.array([0])
91-
lon = np.array([0])
90+
y = np.array([0])
91+
x = np.array([0])
9292

9393
# Create a ParticleSet
94-
pset = parcels.ParticleSet(fieldset=fieldset, pclass=parcels.Particle, time=time, z=z, lat=lat, lon=lon)
94+
pset = parcels.ParticleSet(fieldset=fieldset, pclass=parcels.Particle, time=time, z=z, y=y, x=x)
9595
```
9696

9797
```{admonition} 🖥️ Learn more about how to create ParticleSets
@@ -103,7 +103,7 @@ pset = parcels.ParticleSet(fieldset=fieldset, pclass=parcels.Particle, time=time
103103

104104
A **`parcels.Kernel`** object is a little snippet of code, which is applied to the particles in the `ParticleSet`, for every time step during a simulation. Kernels define the computation or numerical integration done by Parcels, and can represent many processes such as advection, ageing, growth, or simply the sampling of a field.
105105

106-
Advection of a particle by the flow, the change in position $\mathbf{x}(t) = (lon(t), lat(t))$ at time $t$, can be described by the equation:
106+
Advection of a particle by the flow, the change in position $\mathbf{x}(t) = (x(t), y(t))$ at time $t$, can be described by the equation:
107107

108108
$$
109109
\begin{aligned}
@@ -113,20 +113,20 @@ $$
113113

114114
where $\mathbf{v}(\mathbf{x},t) = (u(\mathbf{x},t), v(\mathbf{x},t))$ describes the ocean velocity field at position $\mathbf{x}$ at time $t$.
115115

116-
In Parcels, we can write a kernel function which integrates this equation at each timestep `particles.dt`. To do so, we need the ocean velocity field `fieldset.UV` at the `particles` location, and compute the change in position, `particles.dlon` and `particles.dlat`.
116+
In Parcels, we can write a kernel function which integrates this equation at each timestep `particles.dt`. To do so, we need the ocean velocity field `fieldset.UV` at the `particles` location, and compute the change in position, `particles.dx` and `particles.dy`.
117117

118118
```python
119119
def AdvectionEE(particles, fieldset):
120120
"""Advection of particles using Explicit Euler (aka Euler Forward) integration."""
121121
(u1, v1) = fieldset.UV[particles]
122-
particles.dlon += u1 * particles.dt
123-
particles.dlat += v1 * particles.dt
122+
particles.dx += u1 * particles.dt
123+
particles.dy += v1 * particles.dt
124124
```
125125

126126
Basic kernels are included in Parcels to compute advection and diffusion. The standard advection kernel is `parcels.kernels.AdvectionRK2`, a [second-order Runge-Kutta integrator](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods#The_Runge%E2%80%93Kutta_method) of the advection function.
127127

128128
```{warning}
129-
It is advised _not_ to update the particle coordinates (`particles.time`, `particles.z`, `particles.lat`, or `particles.lon`) directly within a Kernel, as that can negatively interfere with the way that particle movements by different kernels are vectorially added. Use a change in the coordinates: `particles.dlon`, `particles.dlat` and/or `particles.dz`. Read the [kernel loop tutorial](https://docs.oceanparcels.org/en/latest/examples/tutorial_kernelloop.html) to understand why.
129+
It is advised _not_ to update the particle coordinates (`particles.time`, `particles.z`, `particles.y`, or `particles.x`) directly within a Kernel, as that can negatively interfere with the way that particle movements by different kernels are vectorially added. Use a change in the coordinates: `particles.dy`, `particles.dx` and/or `particles.dz`. Read the [kernel loop tutorial](https://docs.oceanparcels.org/en/latest/examples/tutorial_kernelloop.html) to understand why.
130130
```
131131

132132
(custom-kernel)=
@@ -186,7 +186,7 @@ pset.execute(kernels=kernels, dt=dt, runtime=runtime)
186186

187187
### Output
188188

189-
To analyse the particle data generated in the simulation, we need to define a `parcels.ParticleFile` and add it as an argument to `parcels.ParticleSet.execute()`. The output will be written in a [parquet format](https://parquet.apache.org/), which can be opened as a `polars.DataFrame`. The dataset will contain the particle data with at least `time`, `z`, `lat` and `lon`, for each particle at timesteps defined by the `outputdt` argument.
189+
To analyse the particle data generated in the simulation, we need to define a `parcels.ParticleFile` and add it as an argument to `parcels.ParticleSet.execute()`. The output will be written in a [parquet format](https://parquet.apache.org/), which can be opened as a `polars.DataFrame`. The dataset will contain the particle data with at least `time`, `z`, `y` and `x`, for each particle at timesteps defined by the `outputdt` argument.
190190

191191
There are many ways to analyze particle output, and although we provide [a short tutorial to get started](./tutorial_output.ipynb), we recommend writing your own analysis code and checking out [related Lagrangian analysis projects in our community page](../community/index.md#analysis-code).
192192

docs/getting_started/tutorial_output.ipynb

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@
7777
"npart = 10 # number of particles to be released\n",
7878
"lon = 32 * np.ones(npart)\n",
7979
"lat = np.linspace(-32.5, -30.5, npart, dtype=np.float32)\n",
80-
"time = ds_fields.time.values[0] + np.arange(0, npart) * np.timedelta64(2, \"h\")\n",
8180
"z = np.repeat(ds_fields.depth.values[0], npart)\n",
81+
"time = ds_fields.time.values[0] + np.arange(0, npart) * np.timedelta64(2, \"h\")\n",
8282
"\n",
8383
"pset = parcels.ParticleSet(\n",
84-
" fieldset=fieldset, pclass=parcels.Particle, lon=lon, lat=lat, time=time, z=z\n",
84+
" fieldset=fieldset, pclass=parcels.Particle, x=lon, y=lat, z=z, time=time\n",
8585
")\n",
8686
"\n",
8787
"output_file = parcels.ParticleFile(\"output.parquet\", outputdt=np.timedelta64(2, \"h\"))"
@@ -263,7 +263,7 @@
263263
"outputs": [],
264264
"source": [
265265
"fig, ax = plt.subplots(figsize=(5, 3))\n",
266-
"ax.plot(df_particles[\"lon\"], df_particles[\"lat\"], \".-\")\n",
266+
"ax.plot(df_particles[\"x\"], df_particles[\"y\"], \".-\")\n",
267267
"plt.show()"
268268
]
269269
},
@@ -283,7 +283,7 @@
283283
"fig, ax = plt.subplots(figsize=(5, 3))\n",
284284
"for traj in df_particles.partition_by(\"particle_id\", maintain_order=True):\n",
285285
" traj_id = traj[\"particle_id\"][0]\n",
286-
" ax.plot(traj[\"lon\"], traj[\"lat\"], \".-\", label=f\"P{traj_id}\")\n",
286+
" ax.plot(traj[\"x\"], traj[\"y\"], \".-\", label=f\"P{traj_id}\")\n",
287287
"ax.legend(loc=\"center left\", bbox_to_anchor=(1.02, 0.5), borderaxespad=0.0)\n",
288288
"plt.tight_layout()\n",
289289
"plt.show()"
@@ -307,7 +307,7 @@
307307
"particles = df_particles.filter(pl.col(\"time\") == pl.lit(time_to_plot))\n",
308308
"\n",
309309
"fig, ax = plt.subplots(figsize=(5, 3))\n",
310-
"ax.plot(particles[\"lon\"], particles[\"lat\"], \"o\")\n",
310+
"ax.plot(particles[\"x\"], particles[\"y\"], \"o\")\n",
311311
"title_time = pd.to_datetime(time_to_plot).strftime(\"%Y-%m-%d %H:%M:%S\")\n",
312312
"ax.set_title(f\"Particle locations at {title_time}\")\n",
313313
"plt.show()"
@@ -332,7 +332,7 @@
332332
")\n",
333333
"\n",
334334
"fig, ax = plt.subplots(figsize=(5, 3))\n",
335-
"ax.plot(particles[\"lon\"], particles[\"lat\"], \"o\")\n",
335+
"ax.plot(particles[\"x\"], particles[\"y\"], \"o\")\n",
336336
"ax.set_title(f\"Particle locations {time_step} after their release\")\n",
337337
"plt.show()"
338338
]
@@ -354,7 +354,7 @@
354354
"\n",
355355
"for traj in df_particles.partition_by(\"particle_id\", maintain_order=True):\n",
356356
" distance = np.sqrt(\n",
357-
" (traj[\"lon\"] - traj[\"lon\"][0]) ** 2 + (traj[\"lat\"] - traj[\"lat\"][0]) ** 2\n",
357+
" (traj[\"x\"] - traj[\"x\"][0]) ** 2 + (traj[\"y\"] - traj[\"y\"][0]) ** 2\n",
358358
" )\n",
359359
" ax[0].plot(traj[\"time\"], distance, \".-\", label=f\"P{traj['particle_id'][0]}\")\n",
360360
"\n",
@@ -439,8 +439,8 @@
439439
"# --> plot first timestep\n",
440440
"particles = df_particles.filter(pl.col(\"time\") == pl.lit(timerange[0]))\n",
441441
"scatter = ax.scatter(\n",
442-
" particles[\"lon\"],\n",
443-
" particles[\"lat\"],\n",
442+
" particles[\"x\"],\n",
443+
" particles[\"y\"],\n",
444444
" s=10,\n",
445445
" c=[trajectory_to_color[p] for p in particles[\"particle_id\"]],\n",
446446
")\n",
@@ -464,7 +464,7 @@
464464
" particles = df_particles.filter(pl.col(\"time\") == pl.lit(timerange[i]))\n",
465465
"\n",
466466
" if len(particles) > 0:\n",
467-
" scatter.set_offsets(np.c_[particles[\"lon\"], particles[\"lat\"]])\n",
467+
" scatter.set_offsets(np.c_[particles[\"x\"], particles[\"y\"]])\n",
468468
" scatter.set_color([trajectory_to_color[p] for p in particles[\"particle_id\"]])\n",
469469
"\n",
470470
" # --> reset trails\n",
@@ -481,8 +481,8 @@
481481
" )\n",
482482
" if len(traj_trail) > 1:\n",
483483
" (trail,) = ax.plot(\n",
484-
" traj_trail[\"lon\"],\n",
485-
" traj_trail[\"lat\"],\n",
484+
" traj_trail[\"x\"],\n",
485+
" traj_trail[\"y\"],\n",
486486
" color=trajectory_to_color[traj],\n",
487487
" linewidth=0.6,\n",
488488
" alpha=0.6,\n",
@@ -516,7 +516,7 @@
516516
"name": "python",
517517
"nbconvert_exporter": "python",
518518
"pygments_lexer": "ipython3",
519-
"version": "3.14.4"
519+
"version": "3.14.6"
520520
}
521521
},
522522
"nbformat": 4,

docs/getting_started/tutorial_quickstart.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ the virtual particles for which we will calculate the trajectories.
7373

7474
We need to create a {py:obj}`parcels.ParticleSet` object with the particles' initial time and position. The `parcels.ParticleSet`
7575
object also needs to know about the `FieldSet` in which the particles "live". Finally, we need to specify the type of
76-
{py:obj}`parcels.ParticleClass` we want to use. The default particles have `time`, `z`, `lat`, and `lon`, but you can easily add
76+
{py:obj}`parcels.ParticleClass` we want to use. The default particles have `time`, `z`, `y`, and `x`, but you can easily add
7777
other {py:obj}`parcels.Variable`s such as size, temperature, or age to create your own particles to mimic plastic or an [ARGO float](../user_guide/examples/tutorial_Argofloats.ipynb).
7878

7979
```{code-cell}
@@ -86,7 +86,7 @@ time = np.repeat(ds_fields.time.values[0], npart) # at initial time of input dat
8686
z = np.repeat(ds_fields.depth.values[0], npart) # at the first depth (surface)
8787
8888
pset = parcels.ParticleSet(
89-
fieldset=fieldset, pclass=parcels.Particle, time=time, z=z, lat=lat, lon=lon
89+
fieldset=fieldset, pclass=parcels.Particle, time=time, z=z, y=lat, x=lon
9090
)
9191
```
9292

@@ -168,8 +168,8 @@ Let's verify that Parcels has computed the advection of the virtual particles!
168168
import matplotlib.pyplot as plt
169169
170170
# plot positions and color particles by time
171-
scatter = plt.scatter(df['lon'], df['lat'], c=df['time'])
172-
plt.scatter(df['lon'][:npart], df['lat'][:npart], facecolors="none", edgecolors='r') # starting positions
171+
scatter = plt.scatter(df['x'], df['y'], c=df['time'])
172+
plt.scatter(df['x'][:npart], df['y'][:npart], facecolors="none", edgecolors='r') # starting positions
173173
plt.scatter(lon, lat, facecolors="none", edgecolors='r') # starting positions
174174
plt.xlim(31,33)
175175
plt.ylabel("Latitude [deg N]")
@@ -211,9 +211,9 @@ When we check the output, we can see that the particles have returned to their o
211211
```{code-cell}
212212
df_back = parcels.read_particlefile("output-backwards.parquet")
213213
214-
scatter = plt.scatter(df_back['lon'], df_back['lat'], c=df_back['time'])
214+
scatter = plt.scatter(df_back['x'], df_back['y'], c=df_back['time'])
215215
particles_at_max_time = df_back.filter(pl.col("time") == df_back["time"].max())
216-
plt.scatter(particles_at_max_time['lon'], particles_at_max_time['lat'], facecolors="none", edgecolors='r') # starting positions
216+
plt.scatter(particles_at_max_time['x'], particles_at_max_time['y'], facecolors="none", edgecolors='r') # starting positions
217217
plt.xlabel("Longitude [deg E]")
218218
plt.xlim(31,33)
219219
plt.ylabel("Latitude [deg N]")
@@ -227,6 +227,6 @@ Using Euler forward advection, the final positions are equal to the original pos
227227
```{code-cell}
228228
# testing that final location == original location
229229
particles_at_min_time = df_back.filter(pl.col("time") == df_back["time"].min())
230-
np.testing.assert_almost_equal(particles_at_min_time["lat"], lat, 2)
231-
np.testing.assert_almost_equal(particles_at_min_time['lon'], lon, 2)
230+
np.testing.assert_almost_equal(particles_at_min_time["y"], lat, 2)
231+
np.testing.assert_almost_equal(particles_at_min_time['x'], lon, 2)
232232
```

docs/user_guide/examples/tutorial_Argofloats.ipynb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"\n",
6464
" # Phase 3: Rising with vertical_speed until at surface\n",
6565
" ptcls3.dz -= vertical_speed * ptcls3.dt\n",
66-
" ptcls3.temp = fieldset.thetao[ptcls3.time, ptcls3.z, ptcls3.lat, ptcls3.lon]\n",
66+
" ptcls3.temp = fieldset.thetao[ptcls3.time, ptcls3.z, ptcls3.y, ptcls3.x]\n",
6767
" next_phase = ptcls3.z + ptcls3.dz <= mindepth\n",
6868
" ptcls3.cycle_phase[next_phase] = 4\n",
6969
" ptcls3.dz[next_phase] = mindepth - ptcls3.z[next_phase] # avoid overshoot\n",
@@ -135,8 +135,8 @@
135135
"pset = parcels.ParticleSet(\n",
136136
" fieldset=fieldset,\n",
137137
" pclass=ArgoParticle,\n",
138-
" lon=[32],\n",
139-
" lat=[-31],\n",
138+
" x=[32],\n",
139+
" y=[-31],\n",
140140
" z=[mindepth],\n",
141141
")\n",
142142
"\n",
@@ -210,8 +210,8 @@
210210
"fig = plt.figure(figsize=(13, 8))\n",
211211
"ax = plt.axes(projection=\"3d\")\n",
212212
"ax.view_init(azim=-145)\n",
213-
"ax.plot3D(df[\"lon\"], df[\"lat\"], df[\"z\"], color=\"gray\")\n",
214-
"cb = ax.scatter(df[\"lon\"], df[\"lat\"], df[\"z\"], c=df[\"temp\"], s=20, marker=\"o\", zorder=2)\n",
213+
"ax.plot3D(df[\"x\"], df[\"y\"], df[\"z\"], color=\"gray\")\n",
214+
"cb = ax.scatter(df[\"x\"], df[\"y\"], df[\"z\"], c=df[\"temp\"], s=20, marker=\"o\", zorder=2)\n",
215215
"ax.set_xlabel(\"Longitude\")\n",
216216
"ax.set_ylabel(\"Latitude\")\n",
217217
"ax.set_zlabel(\"Depth (m)\")\n",
@@ -223,7 +223,7 @@
223223
],
224224
"metadata": {
225225
"kernelspec": {
226-
"display_name": "Parcels:test (3.14.4)",
226+
"display_name": "Parcels:test (3.14.6)",
227227
"language": "python",
228228
"name": "python3"
229229
},
@@ -237,7 +237,7 @@
237237
"name": "python",
238238
"nbconvert_exporter": "python",
239239
"pygments_lexer": "ipython3",
240-
"version": "3.14.4"
240+
"version": "3.14.6"
241241
}
242242
},
243243
"nbformat": 4,

0 commit comments

Comments
 (0)