+ "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()"
0 commit comments