|
56 | 56 | "cell_type": "markdown", |
57 | 57 | "metadata": {}, |
58 | 58 | "source": [ |
59 | | - "Suppose we want to study the environmental temperature for plankton drifting around a peninsula. We have a dataset with surface ocean velocities and the corresponding sea surface temperature stored in netcdf files in the folder `\"Peninsula_data\"`. Besides the velocity fields, we load the temperature field using `extra_fields={'T': 'T'}`. The particles are released on the left hand side of the domain.\n" |
| 59 | + "Suppose we want to study the environmental temperature for plankton drifting in the Agulhas current. We have a CopernicusMarine dataset with surface ocean velocities and the corresponding potential temperature stored in netcdf files in the [parcels example dataset repository](https://github.com/OceanParcels/parcels-data). Loading in the FieldSet, parcels detects U and V because they have CF standard names and tells us that they are assumed as the velocity fields to be used in the simulation.\n" |
60 | 60 | ] |
61 | 61 | }, |
62 | 62 | { |
|
65 | 65 | "metadata": {}, |
66 | 66 | "outputs": [], |
67 | 67 | "source": [ |
68 | | - "# Velocity and temperature fields\n", |
69 | | - "example_dataset_folder = parcels.download_example_dataset(\"Peninsula_data\")\n", |
70 | | - "filenames = {\n", |
71 | | - " \"U\": str(example_dataset_folder / \"peninsulaU.nc\"),\n", |
72 | | - " \"V\": str(example_dataset_folder / \"peninsulaV.nc\"),\n", |
73 | | - " \"T\": str(example_dataset_folder / \"peninsulaT.nc\"),\n", |
74 | | - "}\n", |
75 | | - "variables = {\"U\": \"vozocrtx\", \"V\": \"vomecrty\", \"T\": \"T\"}\n", |
76 | | - "dimensions = {\"lon\": \"nav_lon\", \"lat\": \"nav_lat\", \"time\": \"time_counter\"}\n", |
77 | | - "fieldset = parcels.FieldSet.from_netcdf(\n", |
78 | | - " filenames, variables, dimensions, allow_time_extrapolation=True\n", |
| 68 | + "# Load the CopernicusMarine data in the Agulhas region from the example_datasets\n", |
| 69 | + "example_dataset_folder = parcels.download_example_dataset(\n", |
| 70 | + " \"CopernicusMarine_data_for_Argo_tutorial\"\n", |
79 | 71 | ")\n", |
80 | 72 | "\n", |
| 73 | + "ds = xr.open_mfdataset(f\"{example_dataset_folder}/*.nc\", combine=\"by_coords\")\n", |
| 74 | + "ds.load() # load the dataset into memory\n", |
| 75 | + "\n", |
| 76 | + "fieldset = parcels.FieldSet.from_copernicusmarine(ds)" |
| 77 | + ] |
| 78 | + }, |
| 79 | + { |
| 80 | + "cell_type": "markdown", |
| 81 | + "metadata": {}, |
| 82 | + "source": [ |
| 83 | + " Ten particles are initialized at the surface in the center of our domain, at the initial time step." |
| 84 | + ] |
| 85 | + }, |
| 86 | + { |
| 87 | + "cell_type": "code", |
| 88 | + "execution_count": null, |
| 89 | + "metadata": {}, |
| 90 | + "outputs": [], |
| 91 | + "source": [ |
81 | 92 | "# Particle locations and initial time\n", |
82 | 93 | "npart = 10 # number of particles to be released\n", |
83 | | - "lon = 3e3 * np.ones(npart)\n", |
84 | | - "lat = np.linspace(3e3, 45e3, npart, dtype=np.float32)\n", |
85 | | - "time = (\n", |
86 | | - " np.arange(0, npart) * timedelta(hours=2).total_seconds()\n", |
87 | | - ") # release each particle two hours later\n", |
| 94 | + "lon = 32 * np.ones(npart)\n", |
| 95 | + "lat = np.linspace(-32.5, -30.5, npart, dtype=np.float32)\n", |
| 96 | + "time = np.repeat(\n", |
| 97 | + " ds.time.values[0], npart\n", |
| 98 | + ") # release all particles at the start time of the fieldset\n", |
| 99 | + "depth = np.repeat(ds.depth.values[0], npart)\n", |
88 | 100 | "\n", |
89 | 101 | "# Plot temperature field and initial particle locations\n", |
90 | | - "T_data = xr.open_dataset(f\"{example_dataset_folder}/peninsulaT.nc\")\n", |
91 | 102 | "plt.figure()\n", |
92 | 103 | "ax = plt.axes()\n", |
93 | 104 | "T_contour = ax.contourf(\n", |
94 | | - " T_data.x.values, T_data.y.values, T_data.T.values[0, 0], cmap=plt.cm.inferno\n", |
| 105 | + " ds.longitude.values,\n", |
| 106 | + " ds.latitude.values,\n", |
| 107 | + " ds.thetao.values[0, 0],\n", |
| 108 | + " cmap=plt.cm.inferno,\n", |
| 109 | + " vmin=20,\n", |
| 110 | + " vmax=27,\n", |
95 | 111 | ")\n", |
96 | 112 | "ax.scatter(lon, lat, c=\"w\")\n", |
97 | 113 | "plt.colorbar(T_contour, label=r\"T [$^{\\circ} C$]\")\n", |
|
112 | 128 | "metadata": {}, |
113 | 129 | "outputs": [], |
114 | 130 | "source": [ |
115 | | - "SampleParticle = parcels.Particle.add_variable(\"temperature\")\n", |
116 | | - "\n", |
117 | | - "pset = parcels.ParticleSet(\n", |
118 | | - " fieldset=fieldset, pclass=SampleParticle, lon=lon, lat=lat, time=time\n", |
| 131 | + "SampleParticle = parcels.Particle.add_variable(\n", |
| 132 | + " parcels.Variable(\"temperature\", dtype=np.float32, initial=np.nan)\n", |
119 | 133 | ")\n", |
120 | 134 | "\n", |
121 | 135 | "\n", |
122 | | - "def SampleT(particle, fieldset, time):\n", |
123 | | - " particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon]" |
| 136 | + "def SampleT(particles, fieldset):\n", |
| 137 | + " particles.temperature = fieldset.thetao[\n", |
| 138 | + " particles.time, particles.depth, particles.lat, particles.lon\n", |
| 139 | + " ]" |
124 | 140 | ] |
125 | 141 | }, |
126 | 142 | { |
|
138 | 154 | "outputs": [], |
139 | 155 | "source": [ |
140 | 156 | "pset = parcels.ParticleSet(\n", |
141 | | - " fieldset=fieldset, pclass=SampleParticle, lon=lon, lat=lat, time=time\n", |
| 157 | + " fieldset=fieldset, pclass=SampleParticle, lon=lon, lat=lat, time=time, depth=depth\n", |
142 | 158 | ")\n", |
143 | 159 | "\n", |
144 | | - "output_file = pset.ParticleFile(name=\"SampleTemp.zarr\", outputdt=timedelta(hours=1))\n", |
| 160 | + "output_file = parcels.ParticleFile(\"SampleTemp.zarr\", outputdt=timedelta(hours=1))\n", |
145 | 161 | "\n", |
146 | 162 | "pset.execute(\n", |
147 | 163 | " [parcels.kernels.AdvectionRK4, SampleT],\n", |
|
165 | 181 | "metadata": {}, |
166 | 182 | "outputs": [], |
167 | 183 | "source": [ |
168 | | - "Particle_data = xr.open_zarr(\"SampleTemp.zarr\")\n", |
| 184 | + "Particle_data = xr.open_zarr(\"SampleTemp.zarr\", decode_times=False)\n", |
169 | 185 | "\n", |
170 | 186 | "plt.figure()\n", |
171 | 187 | "ax = plt.axes()\n", |
172 | | - "ax.set_ylabel(\"Y\")\n", |
173 | | - "ax.set_xlabel(\"X\")\n", |
174 | | - "ax.set_ylim(1000, 49000)\n", |
175 | | - "ax.set_xlim(1000, 99000)\n", |
| 188 | + "ax.set_ylabel(\"Latitude\")\n", |
| 189 | + "ax.set_xlabel(\"Longitude\")\n", |
176 | 190 | "ax.plot(Particle_data.lon.transpose(), Particle_data.lat.transpose(), c=\"k\", zorder=1)\n", |
177 | 191 | "T_scatter = ax.scatter(\n", |
178 | 192 | " Particle_data.lon,\n", |
179 | 193 | " Particle_data.lat,\n", |
180 | 194 | " c=Particle_data.temperature,\n", |
181 | 195 | " cmap=plt.cm.inferno,\n", |
182 | | - " norm=mpl.colors.Normalize(vmin=0.0, vmax=20.0),\n", |
| 196 | + " norm=mpl.colors.Normalize(vmin=22.0, vmax=24.0),\n", |
183 | 197 | " edgecolor=\"k\",\n", |
184 | 198 | " zorder=2,\n", |
185 | 199 | ")\n", |
|
209 | 223 | "metadata": {}, |
210 | 224 | "outputs": [], |
211 | 225 | "source": [ |
212 | | - "def SampleVel_wrong(particle, fieldset, time):\n", |
213 | | - " u = fieldset.U[particle]\n", |
| 226 | + "def SampleVel_wrong(particles, fieldset):\n", |
| 227 | + " u = fieldset.U[particles]\n", |
214 | 228 | "\n", |
215 | 229 | "\n", |
216 | 230 | "pset = parcels.ParticleSet(\n", |
217 | | - " fieldset=fieldset, pclass=parcels.Particle, lon=lon, lat=lat, time=time\n", |
| 231 | + " fieldset=fieldset, pclass=parcels.Particle, lon=lon, lat=lat, time=time, depth=depth\n", |
218 | 232 | ")\n", |
219 | 233 | "\n", |
220 | | - "pset.execute(SampleVel_wrong)" |
| 234 | + "pset.execute(\n", |
| 235 | + " SampleVel_wrong,\n", |
| 236 | + " runtime=timedelta(hours=30),\n", |
| 237 | + " dt=timedelta(minutes=5),\n", |
| 238 | + ")" |
221 | 239 | ] |
222 | 240 | }, |
223 | 241 | { |
|
234 | 252 | "metadata": {}, |
235 | 253 | "outputs": [], |
236 | 254 | "source": [ |
237 | | - "def SampleVel_correct(particle, fieldset, time):\n", |
238 | | - " u, v = fieldset.UV[particle]\n", |
| 255 | + "def SampleVel_correct(particles, fieldset):\n", |
| 256 | + " u, v = fieldset.UV[particles]\n", |
239 | 257 | "\n", |
240 | 258 | "\n", |
241 | 259 | "pset = parcels.ParticleSet(\n", |
242 | | - " fieldset=fieldset, pclass=parcels.Particle, lon=lon, lat=lat, time=time\n", |
| 260 | + " fieldset=fieldset, pclass=parcels.Particle, lon=lon, lat=lat, time=time, depth=depth\n", |
243 | 261 | ")\n", |
244 | 262 | "\n", |
245 | | - "pset.execute(SampleVel_correct)" |
| 263 | + "pset.execute(SampleVel_correct, runtime=timedelta(hours=30), dt=timedelta(minutes=5))" |
246 | 264 | ] |
247 | 265 | }, |
248 | 266 | { |
|
259 | 277 | "metadata": {}, |
260 | 278 | "outputs": [], |
261 | 279 | "source": [ |
262 | | - "SampleParticle = parcels.Particle.add_variables(\n", |
| 280 | + "SampleParticle = parcels.Particle.add_variable(\n", |
263 | 281 | " [\n", |
264 | 282 | " parcels.Variable(\"U\", dtype=np.float32, initial=np.nan),\n", |
265 | 283 | " parcels.Variable(\"V\", dtype=np.float32, initial=np.nan),\n", |
266 | 284 | " ]\n", |
267 | 285 | ")\n", |
268 | 286 | "\n", |
269 | 287 | "\n", |
270 | | - "def SampleVel_correct(particle, fieldset, time):\n", |
| 288 | + "def SampleVel_correct(particles, fieldset):\n", |
271 | 289 | " # attention: samples particle velocity in units of the mesh (deg/s or m/s)\n", |
272 | | - " particle.U, particle.V = fieldset.UV[\n", |
273 | | - " time, particle.depth, particle.lat, particle.lon, particle\n", |
274 | | - " ]" |
| 290 | + " particles.U, particles.V = fieldset.UV[particles]" |
275 | 291 | ] |
276 | 292 | }, |
277 | 293 | { |
|
306 | 322 | "outputs": [], |
307 | 323 | "source": [ |
308 | 324 | "SampleParticleOnce = parcels.Particle.add_variable(\n", |
309 | | - " \"temperature\", initial=0, to_write=\"once\"\n", |
| 325 | + " parcels.Variable(\"temperature\", initial=np.nan, to_write=\"once\")\n", |
310 | 326 | ")\n", |
311 | 327 | "\n", |
312 | 328 | "pset = parcels.ParticleSet(\n", |
313 | | - " fieldset=fieldset, pclass=SampleParticleOnce, lon=lon, lat=lat, time=time\n", |
| 329 | + " fieldset=fieldset,\n", |
| 330 | + " pclass=SampleParticleOnce,\n", |
| 331 | + " lon=lon,\n", |
| 332 | + " lat=lat,\n", |
| 333 | + " time=time,\n", |
| 334 | + " depth=depth,\n", |
314 | 335 | ")" |
315 | 336 | ] |
316 | 337 | }, |
|
320 | 341 | "metadata": {}, |
321 | 342 | "outputs": [], |
322 | 343 | "source": [ |
323 | | - "output_file = pset.ParticleFile(name=\"WriteOnce.zarr\", outputdt=timedelta(hours=1))\n", |
| 344 | + "output_file = parcels.ParticleFile(\"WriteOnce.zarr\", outputdt=timedelta(hours=1))\n", |
324 | 345 | "\n", |
325 | 346 | "pset.execute(\n", |
326 | 347 | " [parcels.kernels.AdvectionRK4, SampleT],\n", |
|
331 | 352 | ] |
332 | 353 | }, |
333 | 354 | { |
334 | | - "attachments": {}, |
335 | 355 | "cell_type": "markdown", |
336 | 356 | "metadata": {}, |
337 | 357 | "source": [ |
338 | | - "Since all the particles are released at the same x-position and the temperature field is invariant in the y-direction, all particles have an initial temperature of 0.4$^\\circ$C\n" |
| 358 | + "We can compare the output where only the initial value is written to output, with the original simulation, where the temperature at each outputdt is written:" |
339 | 359 | ] |
340 | 360 | }, |
341 | 361 | { |
|
344 | 364 | "metadata": {}, |
345 | 365 | "outputs": [], |
346 | 366 | "source": [ |
347 | | - "Particle_data = xr.open_zarr(\"WriteOnce.zarr\")\n", |
| 367 | + "Particle_data_once = xr.open_zarr(\"WriteOnce.zarr\", decode_times=False)\n", |
348 | 368 | "\n", |
349 | 369 | "plt.figure()\n", |
350 | 370 | "ax = plt.axes()\n", |
351 | | - "ax.set_ylabel(\"Y\")\n", |
352 | | - "ax.set_xlabel(\"X\")\n", |
353 | | - "ax.set_ylim(1000, 49000)\n", |
354 | | - "ax.set_xlim(1000, 99000)\n", |
355 | | - "ax.plot(Particle_data.lon.transpose(), Particle_data.lat.transpose(), c=\"k\", zorder=1)\n", |
356 | | - "T_scatter = ax.scatter(\n", |
357 | | - " Particle_data.lon,\n", |
358 | | - " Particle_data.lat,\n", |
359 | | - " c=np.tile(Particle_data.temperature, (Particle_data.lon.shape[1], 1)).T,\n", |
360 | | - " cmap=plt.cm.inferno,\n", |
361 | | - " norm=mpl.colors.Normalize(vmin=0.0, vmax=1.0),\n", |
362 | | - " edgecolor=\"k\",\n", |
363 | | - " zorder=2,\n", |
| 371 | + "ax.set_ylabel(\"Temperature [$^{\\\\circ}$C]\")\n", |
| 372 | + "ax.set_xlabel(\"Observation Number (-)\")\n", |
| 373 | + "ax.set_ylim(22.2, 24.5)\n", |
| 374 | + "l1 = ax.plot(Particle_data.obs, Particle_data.temperature.T, color=\"red\")\n", |
| 375 | + "l2 = ax.plot(\n", |
| 376 | + " Particle_data_once.obs,\n", |
| 377 | + " np.tile(Particle_data_once.temperature, (Particle_data_once.lon.shape[1], 1)),\n", |
| 378 | + " color=\"tab:blue\",\n", |
364 | 379 | ")\n", |
365 | | - "plt.colorbar(T_scatter, label=r\"Initial T [$^{\\circ} C$]\")\n", |
| 380 | + "ax.legend([l1[0], l2[0]], [\"Write every outputdt\", \"Write once\"])\n", |
366 | 381 | "plt.show()" |
367 | 382 | ] |
| 383 | + }, |
| 384 | + { |
| 385 | + "cell_type": "code", |
| 386 | + "execution_count": null, |
| 387 | + "metadata": {}, |
| 388 | + "outputs": [], |
| 389 | + "source": [] |
368 | 390 | } |
369 | 391 | ], |
370 | 392 | "metadata": { |
371 | 393 | "celltoolbar": "Raw-celnotatie", |
372 | 394 | "kernelspec": { |
373 | | - "display_name": "parcels", |
| 395 | + "display_name": "test-notebooks", |
374 | 396 | "language": "python", |
375 | 397 | "name": "python3" |
376 | 398 | }, |
|
384 | 406 | "name": "python", |
385 | 407 | "nbconvert_exporter": "python", |
386 | 408 | "pygments_lexer": "ipython3", |
387 | | - "version": "3.12.3" |
| 409 | + "version": "3.12.11" |
388 | 410 | }, |
389 | 411 | "pycharm": { |
390 | 412 | "stem_cell": { |
|
0 commit comments