Dummy advection which use only static geostrophic current, which didn't solve the complex circulation of the ocean. """ import re from matplotlib import pyplot as plt from matplotlib.animation import FuncAnimation from numpy import arange, isnan, meshgrid, ones import py_eddy_tracker.gui from py_eddy_tracker.data import get_path from py_eddy_tracker.dataset.grid import RegularGridDataset from py_eddy_tracker.observations.observation import EddiesObservations # %% # Load Input grid ADT g = RegularGridDataset(get_path("dt_med_allsat_phy_l4_20160515_20190101.nc"), "longitude", "latitude") # Compute u/v from height g.add_uv("adt") # %% # Load detection files a = EddiesObservations.load_file(get_path("Anticyclonic_20160515.nc")) c = EddiesObservations.load_file(get_path("Cyclonic_20160515.nc")) # %% # Quiver from u/v with eddies fig = plt.figure(figsize=(10, 5)) ax = fig.add_axes([0, 0, 1, 1], projection="full_axes") ax.set_xlim(19, 30), ax.set_ylim(31, 36.5), ax.grid() x, y = meshgrid(g.x_c, g.y_c)
ax.set_extent(extent) ax.gridlines() ax.coastlines(resolution='50m') ax.set_title(title) return ax def update_axes(ax, mappable=None, unit=''): ax.grid() if mappable: plt.colorbar(mappable, cax=ax.figure.add_axes([0.95, 0.05, 0.01, 0.9],title=unit)) # %% # Loading SLA and first display # ----------------------------- g = RegularGridDataset(data.get_path(filename_alt), lon_name_alt,lat_name_alt) ax = start_axes("SLA", extent=extent) m = g.display(ax, "sla", vmin=0.05, vmax=0.35) u,v = g.grid("ugosa").T,g.grid("vgosa").T ax.quiver(g.x_c, g.y_c, u, v, scale=10) update_axes(ax, m, unit='[m]') # %% # Loading SST and first display # ----------------------------- t = RegularGridDataset(filename=data.get_path(filename_sst), x_name=lon_name_sst, y_name=lat_name_sst) # The following now load the corresponding variables from the SST netcdf (it's not needed to load it beforehand, so not executed.) # t.grid(var_name_sst)
ax.set_aspect("equal") ax.set_title(title) return ax def update_axes(ax, mappable=None): ax.grid() if mappable: plt.colorbar(mappable, cax=ax.figure.add_axes([0.95, 0.05, 0.01, 0.9])) # %% # Load Input grid, ADT will be used to detect eddies margin = 30 g = RegularGridDataset( data.get_path("nrt_global_allsat_phy_l4_20190223_20190226.nc"), "longitude", "latitude", # Manual area subset indexs=dict( longitude=slice(1116 - margin, 1216 + margin), latitude=slice(476 - margin, 536 + margin), ), ) ax = start_axes("ADT (m)") m = g.display(ax, "adt", vmin=-0.15, vmax=1) # Draw line on the gulf stream front great_current = Contours(g.x_c, g.y_c, g.grid("adt"),
from matplotlib.path import Path from numpy import array, ma from pytest import approx from py_eddy_tracker.data import get_path from py_eddy_tracker.dataset.grid import RegularGridDataset G = RegularGridDataset(get_path("mask_1_60.nc"), "lon", "lat") X = 0.025 contour = Path(( (-X, 0), (X, 0), (X, X), (-X, X), (-X, 0), )) # contour def test_contour_lon(): assert (contour.lon == (-X, X, X, -X, -X)).all() def test_contour_lat(): assert (contour.lat == (0, 0, X, X, 0)).all() def test_contour_mean(): assert (contour.mean_coordinates == (0, X / 2)).all()
from py_eddy_tracker.observations.observation import EddiesObservations, Table from py_eddy_tracker.observations.tracking import TrackEddiesObservations # %% # Eddies can be stored in 2 formats with the same structure: # # - zarr (https://zarr.readthedocs.io/en/stable/), which allow efficiency in IO,... # - NetCDF4 (https://unidata.github.io/netcdf4-python/), well-known format # # Each field are stored in column, each row corresponds at 1 observation, # array field like contour/profile are 2D column. # %% # Eddies files (zarr or netcdf) could be loaded with ```load_file``` method: eddies_collections = EddiesObservations.load_file( get_path("Cyclonic_20160515.nc")) eddies_collections.field_table() # offset and scale_factor are used only when data is stored in zarr or netCDF4 # %% # Field access # ------------ # To access the total field, here ```amplitude``` eddies_collections.amplitude # To access only a specific part of the field eddies_collections.amplitude[4:15] # %% # Data matrix is a numpy ndarray eddies_collections.obs
Script will use py-eddy-tracker methods to upload external data (sea surface temperature, SST) in a common structure with altimetry. Figures higlights the different steps. """ from datetime import datetime from matplotlib import pyplot as plt from py_eddy_tracker import data from py_eddy_tracker.dataset.grid import RegularGridDataset date = datetime(2016, 7, 7) filename_alt = data.get_path( f"dt_blacksea_allsat_phy_l4_{date:%Y%m%d}_20200801.nc") filename_sst = data.get_path( f"{date:%Y%m%d}000000-GOS-L4_GHRSST-SSTfnd-OISST_HR_REP-BLK-v02.0-fv01.0.nc" ) var_name_sst = "analysed_sst" extent = [27, 42, 40.5, 47] # %% # Loading data # ------------ sst = RegularGridDataset(filename=filename_sst, x_name="lon", y_name="lat") alti = RegularGridDataset(data.get_path(filename_alt), x_name="longitude", y_name="latitude") # We can use `Grid` tools to interpolate ADT on the sst grid
i_next_ = -1 else: ids["previous_obs"][i_next_] = i_next i_next = i_next_ def get_obs(dataset): "Function to isolate a specific obs" return where((dataset.lat > 33) * (dataset.lat < 34) * (dataset.lon > 22) * (dataset.lon < 23) * (dataset.time > 20630) * (dataset.time < 20650))[0][0] # %% # Get original network, we will isolate only relative at order *2* n = NetworkObservations.load_file(get_path("network_med.nc")).network(651) n_ = n.relative(get_obs(n), order=2) # %% # Display the default segmentation ax = start_axes(n_.infos()) n_.plot(ax, color_cycle=n.COLORS) update_axes(ax) fig = plt.figure(figsize=(15, 5)) ax = fig.add_axes([0.04, 0.05, 0.92, 0.92]) ax.xaxis.set_major_formatter(formatter), ax.grid() _ = n_.display_timeline(ax) # %% # Run a new segmentation # ----------------------
axes.set_xlim(270, 340), axes.set_ylim(20, 50) axes.set_aspect("equal") axes.set_title(title) return axes def update_axes(axes, mappable=None): axes.grid() if mappable: plt.colorbar(mappable, cax=axes.figure.add_axes([0.94, 0.05, 0.01, 0.9])) # %% # Load detection files a = EddiesObservations.load_file(data.get_path("Anticyclonic_20190223.nc")) c = EddiesObservations.load_file(data.get_path("Cyclonic_20190223.nc")) # %% # Load Input grid, ADT will be used to detect eddies g = RegularGridDataset( data.get_path("nrt_global_allsat_phy_l4_20190223_20190226.nc"), "longitude", "latitude", ) ax = start_axes("ADT (cm)") m = g.display(ax, "adt", vmin=-120, vmax=120, factor=100) update_axes(ax, m) # %%
import zarr from netCDF4 import Dataset from py_eddy_tracker.data import get_path from py_eddy_tracker.featured_tracking.area_tracker import AreaTracker from py_eddy_tracker.observations.observation import EddiesObservations from py_eddy_tracker.tracking import Correspondances filename = get_path("Anticyclonic_20190223.nc") a0 = EddiesObservations.load_file(filename) a1 = a0.copy() def test_area_tracking_parameter(): delta = 0.2 # All eddies will be shift of delta in longitude and latitude for k in ( "lon", "lon_max", "contour_lon_s", "contour_lon_e", "lat", "lat_max", "contour_lat_s", "contour_lat_e", ): a1[k][:] -= delta a1.time[:] += 1 # wrote in memory a0 and a1 h0, h1 = zarr.group(), zarr.group() a0.to_zarr(h0), a1.to_zarr(h1)
import zarr from py_eddy_tracker.data import get_path from py_eddy_tracker.observations.observation import EddiesObservations a_filename, c_filename = ( get_path("Anticyclonic_20190223.nc"), get_path("Cyclonic_20190223.nc"), ) a = EddiesObservations.load_file(a_filename) a_raw = EddiesObservations.load_file(a_filename, raw_data=True) memory_store = zarr.group() # Dataset was raw loaded from netcdf and save in zarr a_raw.to_zarr(memory_store, chunck_size=100000) # We load zarr data without raw option a_zarr = EddiesObservations.load_from_zarr(memory_store) c = EddiesObservations.load_file(c_filename) def test_merge(): new = a.merge(c) assert len(new) == len(a) + len(c) def test_zarr_raw(): assert a == a_zarr def test_index(): a_nc_subset = EddiesObservations.load_file( a_filename, indexs=dict(obs=slice(500, 1000)))
# Merge of group, ref over etu for i_, j_ in zip(ii[m], ij[m]): g0, g1 = gr_i[i_], gr_j[j_] apply_replace(gr, g0, g1) NETWORK_GROUPS.append((i, j, gr.copy())) return gr # %% # Movie period t0 = (datetime(2005, 5, 1) - datetime(1950, 1, 1)).days t1 = (datetime(2005, 6, 1) - datetime(1950, 1, 1)).days # %% # Get data from period and area e = EddiesObservations.load_file(data.get_path("network_med.nc")) e = e.extract_with_mask((e.time >= t0) * (e.time < t1)).extract_with_area( dict(llcrnrlon=25, urcrnrlon=35, llcrnrlat=31, urcrnrlat=37.5)) # %% # Reproduce individual daily identification(for demonstration) EDDIES_BY_DAYS = list() for i, b0, b1 in e.iter_on("time"): EDDIES_BY_DAYS.append(e.index(i)) # need for display e = EddiesObservations.concatenate(EDDIES_BY_DAYS) # %% # Run network building group to intercept every step n = MyNetwork.from_eddiesobservations(EDDIES_BY_DAYS, window=7) _ = n.group_observations(minimal_area=True)
content = self.to_html5_video() return re.sub(r'width="[0-9]*"\sheight="[0-9]*"', 'width="100%" height="100%"', content) def save(self, *args, **kwargs): if args[0].endswith("gif"): # In this case gif is use to create thumbnail which are not use but consume same time than video # So we create an empty file, to save time with open(args[0], "w") as _: pass return return super().save(*args, **kwargs) # %% n = NetworkObservations.load_file(get_path("network_med.nc")).network(651) n = n.extract_with_mask((n.time >= 20180) * (n.time <= 20269)) n = n.remove_dead_end(nobs=0, ndays=10) n.numbering_segment() c = GridCollection.from_netcdf_cube( get_path("dt_med_allsat_phy_l4_2005T2.nc"), "longitude", "latitude", "time", heigth="adt", ) # %% # Schema # ------ fig = plt.figure(figsize=(12, 6))
from datetime import datetime from py_eddy_tracker.data import get_path from py_eddy_tracker.dataset.grid import RegularGridDataset g = RegularGridDataset(get_path("dt_med_allsat_phy_l4_20160515_20190101.nc"), "longitude", "latitude") def test_id(): g.add_uv("adt") a, c = g.eddy_identification("adt", "u", "v", datetime(2019, 2, 23)) assert len(a) == 36 assert len(c) == 36
Dummy advection which use only static geostrophic current, which didn't resolve the complex circulation of the ocean. """ import numpy as np from matplotlib import pyplot as plt from matplotlib.animation import FuncAnimation import py_eddy_tracker.gui from py_eddy_tracker import data from py_eddy_tracker.dataset.grid import RegularGridDataset from py_eddy_tracker.observations.observation import EddiesObservations # %% # Load Input grid, ADT is used to detect eddies g = RegularGridDataset( data.get_path("dt_med_allsat_phy_l4_20160515_20190101.nc"), "longitude", "latitude") # Compute u/v from height g.add_uv("adt") # %% # Load detection files a = EddiesObservations.load_file(data.get_path("Anticyclonic_20160515.nc")) c = EddiesObservations.load_file(data.get_path("Cyclonic_20160515.nc")) # %% # Quiver from u/v with eddies fig = plt.figure(figsize=(10, 5)) ax = fig.add_axes([0, 0, 1, 1], projection="full_axes") ax.set_xlim(19, 30), ax.set_ylim(31, 36.5), ax.grid() x, y = np.meshgrid(g.x_c, g.y_c)
Network basic manipulation ========================== """ from matplotlib import pyplot as plt import py_eddy_tracker.gui from py_eddy_tracker import data from py_eddy_tracker.observations.network import NetworkObservations from py_eddy_tracker.observations.tracking import TrackEddiesObservations # %% # Load data # --------- # Load data where observations are put in same network but no segmentation e = TrackEddiesObservations.load_file(data.get_path("c568803.nc")) # FIXME : Must be rewrote e.lon[:] = (e.lon + 180) % 360 - 180 e.contour_lon_e[:] = ((e.contour_lon_e.T - e.lon + 180) % 360 - 180 + e.lon).T e.contour_lon_s[:] = ((e.contour_lon_s.T - e.lon + 180) % 360 - 180 + e.lon).T # %% # Do segmentation # --------------- # Segmentation based on maximum overlap, temporal window for candidates = 5 days n = NetworkObservations.from_split_network(e, e.split_network(intern=False, window=5)) # %% # Timeline # -------- # %%
ax.set_xlim(-6, 36.5), ax.set_ylim(30, 46) ax.set_aspect("equal") ax.set_title(title) return ax def update_axes(ax, mappable=None): ax.grid() if mappable: plt.colorbar(m, cax=ax.figure.add_axes([0.95, 0.05, 0.01, 0.9])) # %% # Load Input grid, ADT will be used to detect eddies g = RegularGridDataset( data.get_path("dt_med_allsat_phy_l4_20160515_20190101.nc"), "longitude", "latitude") ax = start_axes("ADT (m)") m = g.display(ax, "adt", vmin=-0.15, vmax=0.15) update_axes(ax, m) # %% # Get u/v # ------- # U/V are deduced from ADT, this algortihm are not usable around equator (~+- 2°) g.add_uv("adt") ax = start_axes("U/V deduce from ADT (m)") ax.set_xlim(2.5, 9), ax.set_ylim(37.5, 40) m = g.display(ax, "adt", vmin=-0.15, vmax=0.15) u, v = g.grid("u").T, g.grid("v").T
from matplotlib import pyplot as plt from numba import njit from numpy import arange, arctan2, empty, isnan, log2, ma, meshgrid, ones, pi, zeros from py_eddy_tracker import start_logger from py_eddy_tracker.data import get_path from py_eddy_tracker.dataset.grid import GridCollection, RegularGridDataset start_logger().setLevel("ERROR") # %% # ADT in med # ---------- c = GridCollection.from_netcdf_cube( get_path("dt_med_allsat_phy_l4_2005T2.nc"), "longitude", "latitude", "time", # To create U/V variable heigth="adt", ) # %% # Methods to compute FSLE # ----------------------- @njit(cache=True, fastmath=True) def check_p(x, y, flse, theta, m_set, m, dt, dist_init=0.02, dist_max=0.6): """ Check if distance between eastern or northern particle to center particle is bigger than `dist_max`
- Circle In the two case we use a least square algorithm """ from matplotlib import pyplot as plt from numpy import cos, linspace, radians, sin from py_eddy_tracker import data from py_eddy_tracker.generic import coordinates_to_local, local_to_coordinates from py_eddy_tracker.observations.observation import EddiesObservations from py_eddy_tracker.poly import fit_circle_, fit_ellips # %% # Load example identification file a = EddiesObservations.load_file(data.get_path("Anticyclonic_20190223.nc")) # %% # Function to draw circle or ellips from parameter def build_circle(x0, y0, r): angle = radians(linspace(0, 360, 50)) x_norm, y_norm = cos(angle), sin(angle) return local_to_coordinates(x_norm * r, y_norm * r, x0, y0) def build_ellips(x0, y0, a, b, theta): angle = radians(linspace(0, 360, 50)) x = a * cos(theta) * cos(angle) - b * sin(theta) * sin(angle) y = a * sin(theta) * cos(angle) + b * cos(theta) * sin(angle) return local_to_coordinates(x, y, x0, y0)
with open(args[0], "w") as _: pass return return super().save(*args, **kwargs) # %% # Data # ---- # To compute vorticity(:math:`\omega`) we compute u/v field with a stencil and apply the following equation with stencil # method : # # .. math:: # \omega = \frac{\partial v}{\partial x} - \frac{\partial u}{\partial y} g = RegularGridDataset( get_path("dt_med_allsat_phy_l4_20160515_20190101.nc"), "longitude", "latitude" ) g.add_uv("adt") u_y = g.compute_stencil(g.grid("u"), vertical=True) v_x = g.compute_stencil(g.grid("v")) g.vars["vort"] = v_x - u_y # %% # Display vorticity field fig, ax, _ = start_ax() mappable = g.display(ax, abs(g.grid("vort")), **kw_vorticity) update_axes(ax, mappable) # %% # Particles # ---------