Skip to content

Commit 4a923a0

Browse files
authored
add teaching materials (#54)
1 parent aab602b commit 4a923a0

26 files changed

+7437
-1
lines changed

teaching_material/.DS_Store

6 KB
Binary file not shown.
18.5 KB
Binary file not shown.
78 KB
Binary file not shown.
250 KB
Binary file not shown.
Binary file not shown.
Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "0172ff30-bee8-4cc9-8146-97d65e5289fb",
6+
"metadata": {},
7+
"source": [
8+
"### Python Assignment 1\n",
9+
"\n",
10+
"In this assignment you will get familiar with analyzing drifter data. The drifters that we will use were released during the Australasian Antarctic Expedition in December 2013, a project designed to characterize eddy dispersion and diffusivity along an Antarctic Circumpolar Current front. For more information you may take a look at [this scientific article](https://doi.org/10.1002/2015JC010972). An animation of the drifters can be found on http://oceanparcels.org/aaemap.\n",
11+
"\n",
12+
"We assume that you have completed all steps in the *Getting python-ready for Dynamical Oceanography* document and have a new environment called *dyoc*. Now check that you are in the right environment by running the following cell:"
13+
]
14+
},
15+
{
16+
"cell_type": "code",
17+
"execution_count": null,
18+
"id": "9256daff-3fdd-4893-aced-382f93bbb794",
19+
"metadata": {},
20+
"outputs": [],
21+
"source": [
22+
"!conda env list"
23+
]
24+
},
25+
{
26+
"cell_type": "markdown",
27+
"id": "99ea69f2-3199-4a7d-b4e2-0d4bce78c7f0",
28+
"metadata": {},
29+
"source": [
30+
"You should see an asterisk next to your *dyoc* environment. If this is not the case, you may stop the *Jupyter lab* instance using `ctrl + c` in your terminal (Mac) or Anaconda prompt (Windows). Then type `conda activate dyoc` to activate the environment and `jupyter lab` to start *Jupyter lab* again. \n",
31+
"\n",
32+
"Now import the following packages:"
33+
]
34+
},
35+
{
36+
"cell_type": "code",
37+
"execution_count": null,
38+
"id": "09816b5e-3c73-48fa-98fa-81f2766743cf",
39+
"metadata": {
40+
"tags": []
41+
},
42+
"outputs": [],
43+
"source": [
44+
"import numpy as np\n",
45+
"import json\n",
46+
"import matplotlib.pyplot as plt\n",
47+
"import matplotlib.dates as mdates\n",
48+
"import matplotlib.colors as mcolors\n",
49+
"import matplotlib.cm\n",
50+
"import cartopy\n",
51+
"import cmocean\n",
52+
"import geopy.distance"
53+
]
54+
},
55+
{
56+
"cell_type": "markdown",
57+
"id": "257c5e38-a90c-49f2-bb2c-73b175d15b45",
58+
"metadata": {},
59+
"source": [
60+
"Then download the file `aaedrifters.json` from http://oceanparcels.org/aaemap by clicking on the right-most of the four icons on the bottom-left. Put that file it in the same directory or folder as this notebook and load the data using the code below"
61+
]
62+
},
63+
{
64+
"cell_type": "code",
65+
"execution_count": null,
66+
"id": "06e5f44e-7570-4e10-9d13-96a88b173a4e",
67+
"metadata": {
68+
"tags": []
69+
},
70+
"outputs": [],
71+
"source": [
72+
"def reshape_jsonarrays(item):\n",
73+
" time = np.array([d[0] for d in item[1]], dtype='datetime64')\n",
74+
" lat = np.array([d[1] for d in item[1]], dtype='float')\n",
75+
" lon = np.array([d[2] for d in item[1]], dtype='float')\n",
76+
" return {'name':item[0], 'time':time, 'lat':lat, 'lon':lon}\n",
77+
"\n",
78+
"with open(\"aaedrifters.json\") as fp:\n",
79+
" jsondata = json.load(fp)\n",
80+
"\n",
81+
"drifters = [reshape_jsonarrays(item) for item in jsondata.items()]"
82+
]
83+
},
84+
{
85+
"cell_type": "markdown",
86+
"id": "7b2fbd30",
87+
"metadata": {},
88+
"source": [
89+
"Have a quick look of the content of the new `drifter` list by running the following cell:"
90+
]
91+
},
92+
{
93+
"cell_type": "code",
94+
"execution_count": null,
95+
"id": "440cae47",
96+
"metadata": {},
97+
"outputs": [],
98+
"source": [
99+
"drifters"
100+
]
101+
},
102+
{
103+
"cell_type": "markdown",
104+
"id": "ee738656-47e6-4a19-9dcc-700c555f6a33",
105+
"metadata": {},
106+
"source": [
107+
"The drifter pairs were deployed on a straight line along the cruise track between approximately (56.0°S, 157.2°E) and (58.8°S, 153.5°E) over a 25 h period starting on 11 December 2013. \n",
108+
"\n",
109+
"Make a simple plot of all trajectories in `drifters` using `plt.plot`, both for the complete duration of the experiment and the first ten days after release."
110+
]
111+
},
112+
{
113+
"cell_type": "code",
114+
"execution_count": null,
115+
"id": "c5342e54-a195-4883-80ce-5c265ab9069f",
116+
"metadata": {
117+
"tags": []
118+
},
119+
"outputs": [],
120+
"source": []
121+
},
122+
{
123+
"cell_type": "markdown",
124+
"id": "6ce3b0b6-7469-41b2-9fcf-3b62d462d27e",
125+
"metadata": {},
126+
"source": [
127+
"To get a better view on the trajectories, it might be nice to show the continents as well. For this we use the `cartopy` package. Now run the following cell and see the resulting map."
128+
]
129+
},
130+
{
131+
"cell_type": "code",
132+
"execution_count": null,
133+
"id": "9de0bef7-36ae-4cc3-b025-bb1dcf89c242",
134+
"metadata": {
135+
"tags": []
136+
},
137+
"outputs": [],
138+
"source": [
139+
"# We use the Platecarree projection centered at 180 degrees longitude\n",
140+
"# on Cartopy's website you can find other projections to play around with\n",
141+
"projection = cartopy.crs.PlateCarree(central_longitude=180)\n",
142+
"\n",
143+
"# whenever adding data having latitude-longitude coordinates, use PlateCarree() as coordinate reference system\n",
144+
"data_crs = cartopy.crs.PlateCarree()\n",
145+
"\n",
146+
"# initialisation of the figure\n",
147+
"fig = plt.figure(figsize=(12, 5))\n",
148+
"ax = fig.add_subplot(1, 1, 1, projection=projection)\n",
149+
"\n",
150+
"# plot coastlines and colour the land surfaces\n",
151+
"ax.coastlines(resolution='50m')\n",
152+
"ax.add_feature(cartopy.feature.LAND)\n",
153+
"\n",
154+
"# plot trajectories\n",
155+
"for d in drifters[:]:\n",
156+
" ax.plot(d['lon'], d['lat'], transform=data_crs, lw=0.8)\n",
157+
"\n",
158+
"# plot grid lines and format the labels\n",
159+
"# xlocs is needed because the locator does not work so well when crossing 180 degrees longitude\n",
160+
"gl = ax.gridlines(crs=data_crs, draw_labels=['bottom','left'], linewidth=0.5,\n",
161+
" color='gray', alpha=0.5, linestyle='--', xlocs=range(-180,181,10))\n",
162+
"gl.xformatter = cartopy.mpl.gridliner.LONGITUDE_FORMATTER\n",
163+
"gl.yformatter = cartopy.mpl.gridliner.LATITUDE_FORMATTER\n",
164+
"\n",
165+
"# zoom out to see more land masses\n",
166+
"ax.set_extent((142,228,-77,-32), crs=data_crs)"
167+
]
168+
},
169+
{
170+
"cell_type": "markdown",
171+
"id": "ccc5136f-377d-408d-a7c3-02254c2757e5",
172+
"metadata": {},
173+
"source": [
174+
"Complete the function `drifter_velocity` below that computes the zonal and meridional velocities of a drifter. The function takes an element of `drifters` as an argument and returns two arrays, `u` and `v`. The remaining code will plot your results for drifter 130263. Make sure the vectors are aligned with the trajectory."
175+
]
176+
},
177+
{
178+
"cell_type": "code",
179+
"execution_count": null,
180+
"id": "a605b9eb-6774-43cb-9a19-9bd8cd5f90a3",
181+
"metadata": {},
182+
"outputs": [],
183+
"source": [
184+
"# ANSWER\n",
185+
"def drifter_velocity(data):\n",
186+
" \"\"\"compute zonal and meridional velocities of drifter\n",
187+
" data : dictionary containing arrays time, lat and lon\n",
188+
" u, v : array, zonal (meridional) velocity\n",
189+
" \"\"\"\n",
190+
" # enter your code here\n",
191+
" return u,v\n",
192+
"\n",
193+
"data = drifters[4]\n",
194+
"u,v = drifter_velocity(data)\n",
195+
"\n",
196+
"crs = cartopy.crs.PlateCarree()\n",
197+
"ax = plt.axes(projection=crs)\n",
198+
"pids = slice(1200,1550,4)\n",
199+
"Q = ax.quiver(data['lon'][pids], data['lat'][pids], u[pids], v[pids], transform=crs, lw=0.5)\n",
200+
"ax.quiverkey(Q, 0.92, 0.05, 0.5, label='0.5 m/s')\n",
201+
"ax.set_title(f\"velocity of drifter {data['name']}\")\n",
202+
"ax.gridlines(draw_labels=['left','bottom'], dms=True)\n",
203+
"plt.show()"
204+
]
205+
},
206+
{
207+
"cell_type": "markdown",
208+
"id": "ad1b4032-437a-4797-9502-bb98e6e04409",
209+
"metadata": {},
210+
"source": [
211+
"Now use the function `drifter_velocity` to create maps of zonal and meridional velocity for all drifters. Part of the code to plot the results has been given below. \n",
212+
"Hint: use `ax.scatter` to change colour along the trajectories."
213+
]
214+
},
215+
{
216+
"cell_type": "code",
217+
"execution_count": null,
218+
"id": "dc3be3f1-312f-472e-89b1-1d3a8d390cb3",
219+
"metadata": {},
220+
"outputs": [],
221+
"source": [
222+
"# compute velocities here"
223+
]
224+
},
225+
{
226+
"cell_type": "code",
227+
"execution_count": null,
228+
"id": "ac2f5a0d-7030-451f-91ef-9086075aa824",
229+
"metadata": {},
230+
"outputs": [],
231+
"source": [
232+
"ax = plt.axes(projection=cartopy.crs.PlateCarree(central_longitude=180))\n",
233+
"\n",
234+
"# plot results for u here\n",
235+
"\n",
236+
"ax.set_title(\"zonal velocity\")\n",
237+
"ax.coastlines('50m')\n",
238+
"ax.gridlines(draw_labels=['left','bottom'], transform=cartopy.crs.PlateCarree(), xlocs=range(-180,180,10))\n",
239+
"ax.figure.colorbar(p, orientation='horizontal', extend='both', label=\"u (m/s)\", aspect=50, pad=0.08)"
240+
]
241+
},
242+
{
243+
"cell_type": "code",
244+
"execution_count": null,
245+
"id": "a7c7cfff-00de-4883-9e3c-a2ca77702ae0",
246+
"metadata": {},
247+
"outputs": [],
248+
"source": [
249+
"ax = plt.axes(projection=cartopy.crs.PlateCarree(central_longitude=180))\n",
250+
"\n",
251+
"# plot results for v here\n",
252+
"\n",
253+
"ax.set_title(\"meridional velocity\")\n",
254+
"ax.coastlines('50m')\n",
255+
"ax.gridlines(draw_labels=['left','bottom'], transform=cartopy.crs.PlateCarree(), xlocs=range(-180,180,10))\n",
256+
"ax.figure.colorbar(p, orientation='horizontal', extend='both', label=\"v (m/s)\", aspect=50, pad=0.08)"
257+
]
258+
},
259+
{
260+
"cell_type": "markdown",
261+
"id": "26b13693-4587-4d8a-9126-a24fe3146d4e",
262+
"metadata": {},
263+
"source": [
264+
"The drifters have been released in pairs, about 13 meter apart on either side of the ship. This makes it possible to analyze the separation distance, which is a measure for dispersion. The cell below creates a list, `dists`, of dictionaries containing time, launching latitude, names and distance, $D$, between the drifters of each pair. \n",
265+
"Run it now and plot the drifter separation as a function of time for all drifters for the first ten days after launch. You may change the scale of the y-axis to logarithmic or symmetric logarithmic with `ax.set_yscale`. How does the separation after ten days depend on launching latitude?"
266+
]
267+
},
268+
{
269+
"cell_type": "code",
270+
"execution_count": null,
271+
"id": "366a3e9c-14df-44b4-b8f2-25ce1ca75a16",
272+
"metadata": {},
273+
"outputs": [],
274+
"source": [
275+
"def separation(data1, data2):\n",
276+
" \"\"\"calculate separation distance between two drifters\"\"\"\n",
277+
" time1 = data1['time']\n",
278+
" time2 = data2['time']\n",
279+
" time = np.array(sorted(set(time1).intersection(set(time2))))\n",
280+
" tids1 = np.searchsorted(time1, time)\n",
281+
" tids2 = np.searchsorted(time2, time)\n",
282+
" lats1 = data1['lat'][tids1]\n",
283+
" lons1 = data1['lon'][tids1]\n",
284+
" lats2 = data2['lat'][tids2]\n",
285+
" lons2 = data2['lon'][tids2]\n",
286+
" dist = [geopy.distance.distance((lat1,lon1),(lat2,lon2)).m for (lat1, lon1, lat2, lon2)\n",
287+
" in zip(lats1, lons1, lats2, lons2)]\n",
288+
" return {'name1':data1['name'], 'name2':data2['name'], 'time':time,\n",
289+
" 'lat0':data1['lat'][0], 'distance':np.array(dist)}\n",
290+
"\n",
291+
"lats = np.array([d['lat'][0] for d in drifters])\n",
292+
"pairids = lats.argsort().reshape((-1,2)).tolist()\n",
293+
"dists = []\n",
294+
"for n,(i,j) in enumerate(pairids):\n",
295+
" print(f\"\\ranalyzing pair {n+1}/{len(pairids)}\", end=\"\")\n",
296+
" dists.append(separation(drifters[i], drifters[j]))"
297+
]
298+
},
299+
{
300+
"cell_type": "code",
301+
"execution_count": null,
302+
"id": "8416d797-094b-480c-b7dc-a1d1a2ec0b8d",
303+
"metadata": {},
304+
"outputs": [],
305+
"source": []
306+
},
307+
{
308+
"cell_type": "markdown",
309+
"id": "840a24d2-b388-4489-ad0d-e478cefe3a91",
310+
"metadata": {},
311+
"source": [
312+
"It is time to compare your results. Create a plot of the pairwise dispersion, $D^2$, similar to figure 5a in the [article](https://doi.org/10.1002/2015JC010972). You do not need to fit the data and calculate the slopes, but do check if the impact of the outlier drifter pair on the mean is similar."
313+
]
314+
},
315+
{
316+
"cell_type": "code",
317+
"execution_count": null,
318+
"id": "ba5fe542-78fa-48f6-8ac9-baf9d276143e",
319+
"metadata": {},
320+
"outputs": [],
321+
"source": []
322+
}
323+
],
324+
"metadata": {
325+
"kernelspec": {
326+
"display_name": "Python 3 (ipykernel)",
327+
"language": "python",
328+
"name": "python3"
329+
},
330+
"language_info": {
331+
"codemirror_mode": {
332+
"name": "ipython",
333+
"version": 3
334+
},
335+
"file_extension": ".py",
336+
"mimetype": "text/x-python",
337+
"name": "python",
338+
"nbconvert_exporter": "python",
339+
"pygments_lexer": "ipython3",
340+
"version": "3.12.1"
341+
}
342+
},
343+
"nbformat": 4,
344+
"nbformat_minor": 5
345+
}

0 commit comments

Comments
 (0)