Skip to content

Commit 1bc768f

Browse files
committed
Management of grid bound in advection
1 parent b85fbf3 commit 1bc768f

File tree

11 files changed

+562
-170
lines changed

11 files changed

+562
-170
lines changed

examples/06_grid_manipulation/pet_advect.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Grid advection
33
==============
44
5-
Dummy advection which use only static geostrophic current, which didn't resolve the complex circulation of the ocean.
5+
Dummy advection which use only static geostrophic current, which didn't solve the complex circulation of the ocean.
66
"""
77
import re
88

@@ -11,22 +11,22 @@
1111
from numpy import arange, isnan, meshgrid, ones
1212

1313
import py_eddy_tracker.gui
14-
from py_eddy_tracker import data
14+
from py_eddy_tracker.data import get_path
1515
from py_eddy_tracker.dataset.grid import RegularGridDataset
1616
from py_eddy_tracker.observations.observation import EddiesObservations
1717

1818
# %%
19-
# Load Input grid, ADT is used to detect eddies
19+
# Load Input grid ADT
2020
g = RegularGridDataset(
21-
data.get_path("dt_med_allsat_phy_l4_20160515_20190101.nc"), "longitude", "latitude"
21+
get_path("dt_med_allsat_phy_l4_20160515_20190101.nc"), "longitude", "latitude"
2222
)
2323
# Compute u/v from height
2424
g.add_uv("adt")
2525

2626
# %%
2727
# Load detection files
28-
a = EddiesObservations.load_file(data.get_path("Anticyclonic_20160515.nc"))
29-
c = EddiesObservations.load_file(data.get_path("Cyclonic_20160515.nc"))
28+
a = EddiesObservations.load_file(get_path("Anticyclonic_20160515.nc"))
29+
c = EddiesObservations.load_file(get_path("Cyclonic_20160515.nc"))
3030

3131

3232
# %%
@@ -78,15 +78,15 @@ def save(self, *args, **kwargs):
7878

7979

8080
# %%
81-
# Method
81+
# Function
8282
def anim_ax(**kw):
8383
t = 0
8484
fig = plt.figure(figsize=(10, 5), dpi=55)
85-
ax = fig.add_axes([0, 0, 1, 1], projection="full_axes")
86-
ax.set_xlim(19, 30), ax.set_ylim(31, 36.5), ax.grid()
87-
a.filled(ax, facecolors="r", alpha=0.1), c.filled(ax, facecolors="b", alpha=0.1)
88-
line = ax.plot([], [], "k", **kw)[0]
89-
return fig, ax.text(21, 32.1, ""), line, t
85+
axes = fig.add_axes([0, 0, 1, 1], projection="full_axes")
86+
axes.set_xlim(19, 30), axes.set_ylim(31, 36.5), axes.grid()
87+
a.filled(axes, facecolors="r", alpha=0.1), c.filled(axes, facecolors="b", alpha=0.1)
88+
line = axes.plot([], [], "k", **kw)[0]
89+
return fig, axes.text(21, 32.1, ""), line, t
9090

9191

9292
def update(i_frame, t_step):
@@ -127,11 +127,10 @@ def update(i_frame, t_step):
127127
# %%
128128
# Time_step settings
129129
# ^^^^^^^^^^^^^^^^^^
130-
# Dummy experiment to test advection precision, we run particles 50 days forward and backward ad different time step
130+
# Dummy experiment to test advection precision, we run particles 50 days forward and backward with different time step
131131
# and we measure distance between new positions and original positions.
132132
fig = plt.figure()
133133
ax = fig.add_subplot(111)
134-
ax.grid()
135134
kw = dict(
136135
bins=arange(0, 50, 0.001),
137136
cumulative=True,
@@ -145,7 +144,7 @@ def update(i_frame, t_step):
145144
g.advect(x, y, "u", "v", **kw_advect, backward=True).__next__()
146145
d = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5
147146
ax.hist(d, **kw, label=f"{86400. / time_step:.0f} time step by day")
148-
ax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc="lower right")
147+
ax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc="lower right"), ax.grid()
149148
ax.set_title("Distance after 50 days forward and 50 days backward")
150149
ax.set_xlabel("Distance between original position and final position (in degrees)")
151150
_ = ax.set_ylabel("Percent of particles with distance lesser than")
@@ -156,7 +155,6 @@ def update(i_frame, t_step):
156155
# We keep same time_step but change time duration
157156
fig = plt.figure()
158157
ax = fig.add_subplot(111)
159-
ax.grid()
160158
time_step = 10800
161159
for duration in (5, 50, 100):
162160
x, y = x0.copy(), y0.copy()
@@ -165,7 +163,7 @@ def update(i_frame, t_step):
165163
g.advect(x, y, "u", "v", **kw_advect, backward=True).__next__()
166164
d = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5
167165
ax.hist(d, **kw, label=f"Time duration {duration} days")
168-
ax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc="lower right")
166+
ax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc="lower right"), ax.grid()
169167
ax.set_title(
170168
"Distance after N days forward and N days backward\nwith a time step of 1/8 days"
171169
)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
"""
2+
Time advection
3+
==============
4+
5+
Example which use CMEMS surface current with a Runge-Kutta 4 algorithm to advect particles.
6+
"""
7+
# sphinx_gallery_thumbnail_number = 2
8+
import re
9+
from datetime import datetime, timedelta
10+
11+
from matplotlib import pyplot as plt
12+
from matplotlib.animation import FuncAnimation
13+
from numpy import arange, isnan, meshgrid, ones
14+
15+
import py_eddy_tracker.gui
16+
from py_eddy_tracker import start_logger
17+
from py_eddy_tracker.data import get_path
18+
from py_eddy_tracker.dataset.grid import GridCollection
19+
20+
start_logger().setLevel("ERROR")
21+
22+
23+
# %%
24+
class VideoAnimation(FuncAnimation):
25+
def _repr_html_(self, *args, **kwargs):
26+
"""To get video in html and have a player"""
27+
content = self.to_html5_video()
28+
return re.sub(
29+
r'width="[0-9]*"\sheight="[0-9]*"', 'width="100%" height="100%"', content
30+
)
31+
32+
def save(self, *args, **kwargs):
33+
if args[0].endswith("gif"):
34+
# In this case gif is use to create thumbnail which are not use but consume same time than video
35+
# So we create an empty file, to save time
36+
with open(args[0], "w") as _:
37+
pass
38+
return
39+
return super().save(*args, **kwargs)
40+
41+
42+
# %%
43+
# Data
44+
# ----
45+
# Load Input time grid ADT
46+
c = GridCollection.from_netcdf_cube(
47+
get_path("dt_med_allsat_phy_l4_2005T2.nc"),
48+
"longitude",
49+
"latitude",
50+
"time",
51+
# To create U/V variable
52+
heigth="adt",
53+
)
54+
55+
# %%
56+
# Anim
57+
# ----
58+
# Particles setup
59+
step_p = 1 / 8
60+
x, y = meshgrid(arange(13, 36, step_p), arange(28, 40, step_p))
61+
x, y = x.reshape(-1), y.reshape(-1)
62+
# Remove all original position that we can't advect at first place
63+
t0 = 20181
64+
m = ~isnan(c[t0].interp("u", x, y))
65+
x0, y0 = x[m], y[m]
66+
x, y = x0.copy(), y0.copy()
67+
68+
69+
# %%
70+
# Function
71+
def anim_ax(**kw):
72+
fig = plt.figure(figsize=(10, 5), dpi=55)
73+
axes = fig.add_axes([0, 0, 1, 1], projection="full_axes")
74+
axes.set_xlim(19, 30), axes.set_ylim(31, 36.5), axes.grid()
75+
line = axes.plot([], [], "k", **kw)[0]
76+
return fig, axes.text(21, 32.1, ""), line
77+
78+
79+
def update(_):
80+
tt, xt, yt = f.__next__()
81+
mappable.set_data(xt, yt)
82+
d = timedelta(tt / 86400.0) + datetime(1950, 1, 1)
83+
txt.set_text(f"{d:%Y/%m/%d-%H}")
84+
85+
86+
# %%
87+
f = c.filament(x, y, "u", "v", t_init=t0, nb_step=2, time_step=21600, filament_size=3)
88+
fig, txt, mappable = anim_ax(lw=0.5)
89+
ani = VideoAnimation(fig, update, frames=arange(160), interval=100)
90+
91+
92+
# %%
93+
# Particules stat
94+
# ---------------
95+
# Time_step settings
96+
# ^^^^^^^^^^^^^^^^^^
97+
# Dummy experiment to test advection precision, we run particles 50 days forward and backward with different time step
98+
# and we measure distance between new positions and original positions.
99+
fig = plt.figure()
100+
ax = fig.add_subplot(111)
101+
kw = dict(
102+
bins=arange(0, 50, 0.002),
103+
cumulative=True,
104+
weights=ones(x0.shape) / x0.shape[0] * 100.0,
105+
histtype="step",
106+
)
107+
kw_p = dict(u_name="u", v_name="v", nb_step=1)
108+
for time_step in (10800, 21600, 43200, 86400):
109+
x, y = x0.copy(), y0.copy()
110+
nb = int(30 * 86400 / time_step)
111+
# Go forward
112+
p = c.advect(x, y, time_step=time_step, t_init=20181.5, **kw_p)
113+
for i in range(nb):
114+
t_, _, _ = p.__next__()
115+
# Go backward
116+
p = c.advect(x, y, time_step=time_step, backward=True, t_init=t_ / 86400.0, **kw_p)
117+
for i in range(nb):
118+
t_, _, _ = p.__next__()
119+
d = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5
120+
ax.hist(d, **kw, label=f"{86400. / time_step:.0f} time step by day")
121+
ax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc="lower right"), ax.grid()
122+
ax.set_title("Distance after 50 days forward and 50 days backward")
123+
ax.set_xlabel("Distance between original position and final position (in degrees)")
124+
_ = ax.set_ylabel("Percent of particles with distance lesser than")
125+
126+
# %%
127+
# Time duration
128+
# ^^^^^^^^^^^^^
129+
# We keep same time_step but change time duration
130+
fig = plt.figure()
131+
ax = fig.add_subplot(111)
132+
time_step = 10800
133+
for duration in (10, 40, 80):
134+
x, y = x0.copy(), y0.copy()
135+
nb = int(duration * 86400 / time_step)
136+
# Go forward
137+
p = c.advect(x, y, time_step=time_step, t_init=20181.5, **kw_p)
138+
for i in range(nb):
139+
t_, _, _ = p.__next__()
140+
# Go backward
141+
p = c.advect(x, y, time_step=time_step, backward=True, t_init=t_ / 86400.0, **kw_p)
142+
for i in range(nb):
143+
t_, _, _ = p.__next__()
144+
d = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5
145+
ax.hist(d, **kw, label=f"Time duration {duration} days")
146+
ax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc="lower right"), ax.grid()
147+
ax.set_title(
148+
"Distance after N days forward and N days backward\nwith a time step of 1/8 days"
149+
)
150+
ax.set_xlabel("Distance between original position and final position (in degrees)")
151+
_ = ax.set_ylabel("Percent of particles with distance lesser than ")

examples/07_cube_manipulation/pet_fsle_med.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"longitude",
3232
"latitude",
3333
"time",
34+
# To create U/V variable
3435
heigth="adt",
3536
)
3637

examples/16_network/pet_group_anim.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Network group process
33
=====================
44
"""
5+
# sphinx_gallery_thumbnail_number = 2
56
import re
67
from datetime import datetime
78

@@ -146,8 +147,6 @@ def update(frame):
146147
# %%
147148
# Final Result
148149
# -----------
149-
150-
# sphinx_gallery_thumbnail_number = 2
151150
fig = plt.figure(figsize=(16, 9))
152151
ax = fig.add_axes([0, 0, 1, 1])
153152
ax.set_aspect("equal"), ax.grid(), ax.set_xlim(26, 34), ax.set_ylim(31, 35.5)

examples/16_network/pet_segmentation_anim.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Network segmentation process
33
============================
44
"""
5-
5+
# sphinx_gallery_thumbnail_number = 2
66
import re
77

88
from matplotlib import pyplot as plt
@@ -120,8 +120,6 @@ def update(i_frame):
120120
# %%
121121
# Final Result
122122
# -----------
123-
124-
# sphinx_gallery_thumbnail_number = 2
125123
fig = plt.figure(figsize=(16, 9))
126124
ax = fig.add_axes([0.04, 0.06, 0.94, 0.88], projection="full_axes")
127125
ax.set_xlim(19, 29), ax.set_ylim(31, 35.5), ax.grid()

notebooks/python_module/06_grid_manipulation/pet_advect.ipynb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"cell_type": "markdown",
1616
"metadata": {},
1717
"source": [
18-
"\n# Grid advection\n\nDummy advection which use only static geostrophic current, which didn't resolve the complex circulation of the ocean.\n"
18+
"\n# Grid advection\n\nDummy advection which use only static geostrophic current, which didn't solve the complex circulation of the ocean.\n"
1919
]
2020
},
2121
{
@@ -26,14 +26,14 @@
2626
},
2727
"outputs": [],
2828
"source": [
29-
"import re\n\nfrom matplotlib import pyplot as plt\nfrom matplotlib.animation import FuncAnimation\nfrom numpy import arange, isnan, meshgrid, ones\n\nimport py_eddy_tracker.gui\nfrom py_eddy_tracker import data\nfrom py_eddy_tracker.dataset.grid import RegularGridDataset\nfrom py_eddy_tracker.observations.observation import EddiesObservations"
29+
"import re\n\nfrom matplotlib import pyplot as plt\nfrom matplotlib.animation import FuncAnimation\nfrom numpy import arange, isnan, meshgrid, ones\n\nimport py_eddy_tracker.gui\nfrom py_eddy_tracker.data import get_path\nfrom py_eddy_tracker.dataset.grid import RegularGridDataset\nfrom py_eddy_tracker.observations.observation import EddiesObservations"
3030
]
3131
},
3232
{
3333
"cell_type": "markdown",
3434
"metadata": {},
3535
"source": [
36-
"Load Input grid, ADT is used to detect eddies\n\n"
36+
"Load Input grid ADT\n\n"
3737
]
3838
},
3939
{
@@ -44,7 +44,7 @@
4444
},
4545
"outputs": [],
4646
"source": [
47-
"g = RegularGridDataset(\n data.get_path(\"dt_med_allsat_phy_l4_20160515_20190101.nc\"), \"longitude\", \"latitude\"\n)\n# Compute u/v from height\ng.add_uv(\"adt\")"
47+
"g = RegularGridDataset(\n get_path(\"dt_med_allsat_phy_l4_20160515_20190101.nc\"), \"longitude\", \"latitude\"\n)\n# Compute u/v from height\ng.add_uv(\"adt\")"
4848
]
4949
},
5050
{
@@ -62,7 +62,7 @@
6262
},
6363
"outputs": [],
6464
"source": [
65-
"a = EddiesObservations.load_file(data.get_path(\"Anticyclonic_20160515.nc\"))\nc = EddiesObservations.load_file(data.get_path(\"Cyclonic_20160515.nc\"))"
65+
"a = EddiesObservations.load_file(get_path(\"Anticyclonic_20160515.nc\"))\nc = EddiesObservations.load_file(get_path(\"Cyclonic_20160515.nc\"))"
6666
]
6767
},
6868
{
@@ -134,7 +134,7 @@
134134
"cell_type": "markdown",
135135
"metadata": {},
136136
"source": [
137-
"Method\n\n"
137+
"Function\n\n"
138138
]
139139
},
140140
{
@@ -145,7 +145,7 @@
145145
},
146146
"outputs": [],
147147
"source": [
148-
"def anim_ax(**kw):\n t = 0\n fig = plt.figure(figsize=(10, 5), dpi=55)\n ax = fig.add_axes([0, 0, 1, 1], projection=\"full_axes\")\n ax.set_xlim(19, 30), ax.set_ylim(31, 36.5), ax.grid()\n a.filled(ax, facecolors=\"r\", alpha=0.1), c.filled(ax, facecolors=\"b\", alpha=0.1)\n line = ax.plot([], [], \"k\", **kw)[0]\n return fig, ax.text(21, 32.1, \"\"), line, t\n\n\ndef update(i_frame, t_step):\n global t\n x, y = p.__next__()\n t += t_step\n l.set_data(x, y)\n txt.set_text(f\"T0 + {t:.1f} days\")"
148+
"def anim_ax(**kw):\n t = 0\n fig = plt.figure(figsize=(10, 5), dpi=55)\n axes = fig.add_axes([0, 0, 1, 1], projection=\"full_axes\")\n axes.set_xlim(19, 30), axes.set_ylim(31, 36.5), axes.grid()\n a.filled(axes, facecolors=\"r\", alpha=0.1), c.filled(axes, facecolors=\"b\", alpha=0.1)\n line = axes.plot([], [], \"k\", **kw)[0]\n return fig, axes.text(21, 32.1, \"\"), line, t\n\n\ndef update(i_frame, t_step):\n global t\n x, y = p.__next__()\n t += t_step\n l.set_data(x, y)\n txt.set_text(f\"T0 + {t:.1f} days\")"
149149
]
150150
},
151151
{
@@ -213,7 +213,7 @@
213213
"cell_type": "markdown",
214214
"metadata": {},
215215
"source": [
216-
"### Time_step settings\nDummy experiment to test advection precision, we run particles 50 days forward and backward ad different time step\nand we measure distance between new positions and original positions.\n\n"
216+
"### Time_step settings\nDummy experiment to test advection precision, we run particles 50 days forward and backward with different time step\nand we measure distance between new positions and original positions.\n\n"
217217
]
218218
},
219219
{
@@ -224,7 +224,7 @@
224224
},
225225
"outputs": [],
226226
"source": [
227-
"fig = plt.figure()\nax = fig.add_subplot(111)\nax.grid()\nkw = dict(\n bins=arange(0, 50, 0.001),\n cumulative=True,\n weights=ones(x0.shape) / x0.shape[0] * 100.0,\n histtype=\"step\",\n)\nfor time_step in (10800, 21600, 43200, 86400):\n x, y = x0.copy(), y0.copy()\n kw_advect = dict(nb_step=int(50 * 86400 / time_step), time_step=time_step)\n g.advect(x, y, \"u\", \"v\", **kw_advect).__next__()\n g.advect(x, y, \"u\", \"v\", **kw_advect, backward=True).__next__()\n d = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5\n ax.hist(d, **kw, label=f\"{86400. / time_step:.0f} time step by day\")\nax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc=\"lower right\")\nax.set_title(\"Distance after 50 days forward and 50 days backward\")\nax.set_xlabel(\"Distance between original position and final position (in degrees)\")\n_ = ax.set_ylabel(\"Percent of particles with distance lesser than\")"
227+
"fig = plt.figure()\nax = fig.add_subplot(111)\nkw = dict(\n bins=arange(0, 50, 0.001),\n cumulative=True,\n weights=ones(x0.shape) / x0.shape[0] * 100.0,\n histtype=\"step\",\n)\nfor time_step in (10800, 21600, 43200, 86400):\n x, y = x0.copy(), y0.copy()\n kw_advect = dict(nb_step=int(50 * 86400 / time_step), time_step=time_step)\n g.advect(x, y, \"u\", \"v\", **kw_advect).__next__()\n g.advect(x, y, \"u\", \"v\", **kw_advect, backward=True).__next__()\n d = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5\n ax.hist(d, **kw, label=f\"{86400. / time_step:.0f} time step by day\")\nax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc=\"lower right\"), ax.grid()\nax.set_title(\"Distance after 50 days forward and 50 days backward\")\nax.set_xlabel(\"Distance between original position and final position (in degrees)\")\n_ = ax.set_ylabel(\"Percent of particles with distance lesser than\")"
228228
]
229229
},
230230
{
@@ -242,7 +242,7 @@
242242
},
243243
"outputs": [],
244244
"source": [
245-
"fig = plt.figure()\nax = fig.add_subplot(111)\nax.grid()\ntime_step = 10800\nfor duration in (5, 50, 100):\n x, y = x0.copy(), y0.copy()\n kw_advect = dict(nb_step=int(duration * 86400 / time_step), time_step=time_step)\n g.advect(x, y, \"u\", \"v\", **kw_advect).__next__()\n g.advect(x, y, \"u\", \"v\", **kw_advect, backward=True).__next__()\n d = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5\n ax.hist(d, **kw, label=f\"Time duration {duration} days\")\nax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc=\"lower right\")\nax.set_title(\n \"Distance after N days forward and N days backward\\nwith a time step of 1/8 days\"\n)\nax.set_xlabel(\"Distance between original position and final position (in degrees)\")\n_ = ax.set_ylabel(\"Percent of particles with distance lesser than \")"
245+
"fig = plt.figure()\nax = fig.add_subplot(111)\ntime_step = 10800\nfor duration in (5, 50, 100):\n x, y = x0.copy(), y0.copy()\n kw_advect = dict(nb_step=int(duration * 86400 / time_step), time_step=time_step)\n g.advect(x, y, \"u\", \"v\", **kw_advect).__next__()\n g.advect(x, y, \"u\", \"v\", **kw_advect, backward=True).__next__()\n d = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5\n ax.hist(d, **kw, label=f\"Time duration {duration} days\")\nax.set_xlim(0, 0.25), ax.set_ylim(0, 100), ax.legend(loc=\"lower right\"), ax.grid()\nax.set_title(\n \"Distance after N days forward and N days backward\\nwith a time step of 1/8 days\"\n)\nax.set_xlabel(\"Distance between original position and final position (in degrees)\")\n_ = ax.set_ylabel(\"Percent of particles with distance lesser than \")"
246246
]
247247
}
248248
],

0 commit comments

Comments
 (0)