2626 "execution_count" : null ,
2727 "metadata" : {},
2828 "outputs" : [],
29- "source" : " from pathlib import Path\n\n import matplotlib.pyplot as plt\n import matplotlib.tri as mtri\n import numpy as np\n import uxarray as ux\n\n import parcels\n import parcels.tutorial"
29+ "source" : [
30+ " from pathlib import Path\n " ,
31+ " \n " ,
32+ " import matplotlib.pyplot as plt\n " ,
33+ " import matplotlib.tri as mtri\n " ,
34+ " import numpy as np\n " ,
35+ " import uxarray as ux\n " ,
36+ " \n " ,
37+ " import parcels\n " ,
38+ " import parcels.tutorial"
39+ ]
3040 },
3141 {
3242 "cell_type" : " markdown" ,
4656 "outputs" : [],
4757 "source" : [
4858 " for name in [\n " ,
49- " \" FESOM_periodic_channel/fesom_channel\" , # grid description\n " ,
59+ " \" FESOM_periodic_channel/fesom_channel\" , # grid description\n " ,
5060 " \" FESOM_periodic_channel/u.fesom_channel\" , # zonal velocity (face-registered)\n " ,
5161 " \" FESOM_periodic_channel/v.fesom_channel\" , # meridional velocity (face-registered)\n " ,
5262 " \" FESOM_periodic_channel/w.fesom_channel\" , # vertical velocity (node-registered)\n " ,
5363 " ]:\n " ,
5464 " parcels.tutorial.open_dataset(name)\n " ,
5565 " \n " ,
5666 " from parcels._datasets.remote import _DATA_HOME\n " ,
67+ " \n " ,
5768 " data_dir = Path(_DATA_HOME) / \" data\" / \" FESOM_periodic_channel\"\n " ,
5869 " \n " ,
5970 " grid_path = str(data_dir / \" fesom_channel.nc\" )\n " ,
8899 "metadata" : {},
89100 "outputs" : [],
90101 "source" : [
91- " ds = ux.open_mfdataset(grid_path, data_paths).rename_vars({\" u\" : \" U\" , \" v\" : \" V\" , \" w\" : \" W\" })\n " ,
102+ " ds = ux.open_mfdataset(grid_path, data_paths).rename_vars(\n " ,
103+ " {\" u\" : \" U\" , \" v\" : \" V\" , \" w\" : \" W\" }\n " ,
104+ " )\n " ,
92105 " ds"
93106 ]
94107 },
113126 "execution_count" : null ,
114127 "metadata" : {},
115128 "outputs" : [],
116- "source" : " ds = parcels.convert.fesom_to_ugrid(ds)\n print(\" dims:\" , dict(ds.sizes))"
129+ "source" : [
130+ " ds = parcels.convert.fesom_to_ugrid(ds)\n " ,
131+ " print(\" dims:\" , dict(ds.sizes))"
132+ ]
117133 },
118134 {
119135 "cell_type" : " markdown" ,
155171 "execution_count" : null ,
156172 "metadata" : {},
157173 "outputs" : [],
158- "source" : " lon_grid, lat_grid = np.meshgrid(\n np.linspace(0.5, 4.0, 10),\n np.linspace(3.0, 15.0, 4),\n )\n lon = lon_grid.ravel()\n lat = lat_grid.ravel()\n z = np.full(lon.size, 50.0) # release at 50 m depth\n\n pset = parcels.ParticleSet(\n fieldset=fieldset, pclass=parcels.Particle, lon=lon, lat=lat, z=z,\n )\n\n output_file = parcels.ParticleFile(\" output-fesom.parquet\" , outputdt=np.timedelta64(1, \" h\" ))\n\n pset.execute(\n [parcels.kernels.AdvectionRK4],\n runtime=np.timedelta64(2, \" D\" ),\n dt=np.timedelta64(5, \" m\" ),\n output_file=output_file,\n verbose_progress=False,\n )"
174+ "source" : [
175+ " lon_grid, lat_grid = np.meshgrid(\n " ,
176+ " np.linspace(0.5, 4.0, 10),\n " ,
177+ " np.linspace(3.0, 15.0, 4),\n " ,
178+ " )\n " ,
179+ " lon = lon_grid.ravel()\n " ,
180+ " lat = lat_grid.ravel()\n " ,
181+ " z = np.full(lon.size, 50.0) # release at 50 m depth\n " ,
182+ " \n " ,
183+ " pset = parcels.ParticleSet(\n " ,
184+ " fieldset=fieldset,\n " ,
185+ " pclass=parcels.Particle,\n " ,
186+ " lon=lon,\n " ,
187+ " lat=lat,\n " ,
188+ " z=z,\n " ,
189+ " )\n " ,
190+ " \n " ,
191+ " output_file = parcels.ParticleFile(\n " ,
192+ " \" output-fesom.parquet\" , outputdt=np.timedelta64(1, \" h\" )\n " ,
193+ " )\n " ,
194+ " \n " ,
195+ " pset.execute(\n " ,
196+ " [parcels.kernels.AdvectionRK4],\n " ,
197+ " runtime=np.timedelta64(2, \" D\" ),\n " ,
198+ " dt=np.timedelta64(5, \" m\" ),\n " ,
199+ " output_file=output_file,\n " ,
200+ " verbose_progress=False,\n " ,
201+ " )"
202+ ]
159203 },
160204 {
161205 "cell_type" : " markdown" ,
167211 "execution_count" : null ,
168212 "metadata" : {},
169213 "outputs" : [],
170- "source": "df = parcels.read_particlefile(\"output-fesom.parquet\")\n\ntriang = mtri.Triangulation(\n ds.uxgrid.node_lon.values,\n ds.uxgrid.node_lat.values,\n triangles=ds.uxgrid.face_node_connectivity.values,\n)\n\ndepth_idx = int(np.argmin(np.abs(ds.zc.values - 50.0)))\nU_face = np.asarray(ds[\"U\"].isel(zc=depth_idx)).squeeze()\nV_face = np.asarray(ds[\"V\"].isel(zc=depth_idx)).squeeze()\nspeed = np.hypot(U_face, V_face)\n\nfig, ax = plt.subplots(figsize=(11, 5))\n\n# Background: speed at the release depth.\ntpc = ax.tripcolor(triang, facecolors=speed, shading=\"flat\", cmap=\"Blues\")\nfig.colorbar(tpc, ax=ax, label=\"speed [m/s]\", location=\"left\", shrink=0.85)\n\n# Velocity arrows at face centres. Drawing in lon/lat space (angles/scale_units\n# \"xy\") keeps the arrows aligned with the trajectories; length tracks speed.\nstep = max(1, U_face.size // 400)\nxf = ds.uxgrid.face_lon.values\nyf = ds.uxgrid.face_lat.values\nmax_speed = float(np.nanmax(speed))\nq = ax.quiver(\n xf[::step], yf[::step], U_face[::step], V_face[::step],\n angles=\"xy\", scale_units=\"xy\", scale=max_speed / 0.3,\n color=\"k\", width=0.0018, pivot=\"tail\",\n)\nax.quiverkey(q, 0.86, 1.04, max_speed, f\"{max_speed:.2f} m/s\", labelpos=\"E\", coordinates=\"axes\")\n\n# Particle paths (grey lines) and positions coloured by time.\nfor traj in df.sort(\"time\").partition_by(\"particle_id\"):\n ax.plot(traj[\"lon\"], traj[\"lat\"], color=\"0.4\", linewidth=0.6, alpha=0.7, zorder=2)\nax.scatter(lon, lat, facecolors=\"none\", edgecolors=\"k\", s=60, label=\"release\", zorder=3)\nsc = ax.scatter(\n df[\"lon\"], df[\"lat\"], c=df[\"time\"].dt.total_seconds(), s=6, cmap=\"viridis\", zorder=3,\n)\nfig.colorbar(sc, ax=ax, label=\"time since release [s]\", shrink=0.85)\n\nax.set_xlabel(\"Longitude [deg E]\")\nax.set_ylabel(\"Latitude [deg N]\")\nax.set_title(f\"FESOM2 velocity at z ≈ {float(ds.zc.values[depth_idx]):.1f} m with particle trajectories\")\nax.legend(loc=\"upper right\")\nplt.show()"
214+ "source" : [
215+ " df = parcels.read_particlefile(\" output-fesom.parquet\" )\n " ,
216+ " \n " ,
217+ " triang = mtri.Triangulation(\n " ,
218+ " ds.uxgrid.node_lon.values,\n " ,
219+ " ds.uxgrid.node_lat.values,\n " ,
220+ " triangles=ds.uxgrid.face_node_connectivity.values,\n " ,
221+ " )\n " ,
222+ " \n " ,
223+ " depth_idx = int(np.argmin(np.abs(ds.zc.values - 50.0)))\n " ,
224+ " U_face = np.asarray(ds[\" U\" ].isel(zc=depth_idx)).squeeze()\n " ,
225+ " V_face = np.asarray(ds[\" V\" ].isel(zc=depth_idx)).squeeze()\n " ,
226+ " speed = np.hypot(U_face, V_face)\n " ,
227+ " \n " ,
228+ " fig, ax = plt.subplots(figsize=(11, 5))\n " ,
229+ " \n " ,
230+ " # Background: speed at the release depth.\n " ,
231+ " tpc = ax.tripcolor(triang, facecolors=speed, shading=\" flat\" , cmap=\" Blues\" )\n " ,
232+ " fig.colorbar(tpc, ax=ax, label=\" speed [m/s]\" , location=\" left\" , shrink=0.85)\n " ,
233+ " \n " ,
234+ " # Velocity arrows at face centres. Drawing in lon/lat space (angles/scale_units\n " ,
235+ " # \" xy\" ) keeps the arrows aligned with the trajectories; length tracks speed.\n " ,
236+ " step = max(1, U_face.size // 400)\n " ,
237+ " xf = ds.uxgrid.face_lon.values\n " ,
238+ " yf = ds.uxgrid.face_lat.values\n " ,
239+ " max_speed = float(np.nanmax(speed))\n " ,
240+ " q = ax.quiver(\n " ,
241+ " xf[::step],\n " ,
242+ " yf[::step],\n " ,
243+ " U_face[::step],\n " ,
244+ " V_face[::step],\n " ,
245+ " angles=\" xy\" ,\n " ,
246+ " scale_units=\" xy\" ,\n " ,
247+ " scale=max_speed / 0.3,\n " ,
248+ " color=\" k\" ,\n " ,
249+ " width=0.0018,\n " ,
250+ " pivot=\" tail\" ,\n " ,
251+ " )\n " ,
252+ " ax.quiverkey(\n " ,
253+ " q, 0.86, 1.04, max_speed, f\" {max_speed:.2f} m/s\" , labelpos=\" E\" , coordinates=\" axes\"\n " ,
254+ " )\n " ,
255+ " \n " ,
256+ " # Particle paths (grey lines) and positions coloured by time.\n " ,
257+ " for traj in df.sort(\" time\" ).partition_by(\" particle_id\" ):\n " ,
258+ " ax.plot(traj[\" lon\" ], traj[\" lat\" ], color=\" 0.4\" , linewidth=0.6, alpha=0.7, zorder=2)\n " ,
259+ " ax.scatter(lon, lat, facecolors=\" none\" , edgecolors=\" k\" , s=60, label=\" release\" , zorder=3)\n " ,
260+ " sc = ax.scatter(\n " ,
261+ " df[\" lon\" ],\n " ,
262+ " df[\" lat\" ],\n " ,
263+ " c=df[\" time\" ].dt.total_seconds(),\n " ,
264+ " s=6,\n " ,
265+ " cmap=\" viridis\" ,\n " ,
266+ " zorder=3,\n " ,
267+ " )\n " ,
268+ " fig.colorbar(sc, ax=ax, label=\" time since release [s]\" , shrink=0.85)\n " ,
269+ " \n " ,
270+ " ax.set_xlabel(\" Longitude [deg E]\" )\n " ,
271+ " ax.set_ylabel(\" Latitude [deg N]\" )\n " ,
272+ " ax.set_title(\n " ,
273+ " f\" FESOM2 velocity at z ≈ {float(ds.zc.values[depth_idx]):.1f} m with particle trajectories\"\n " ,
274+ " )\n " ,
275+ " ax.legend(loc=\" upper right\" )\n " ,
276+ " plt.show()"
277+ ]
171278 },
172279 {
173280 "cell_type" : " markdown" ,
198305 },
199306 "nbformat" : 4 ,
200307 "nbformat_minor" : 4
201- }
308+ }
0 commit comments