Skip to content

Commit 437ee6d

Browse files
committed
amplitude simplification
1 parent 8f81399 commit 437ee6d

File tree

3 files changed

+48
-47
lines changed

3 files changed

+48
-47
lines changed

src/py_eddy_tracker/dataset/grid.py

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ def raw_resample(datas, fixed_size):
3232
return interp(arange(fixed_size), arange(nb_value) * (fixed_size - 1) / (nb_value - 1) , datas)
3333

3434

35-
def contour_iter(self, anticyclonic_search):
36-
for coll in self.collections[::1 if anticyclonic_search else -1]:
37-
yield coll
38-
39-
40-
BaseQuadContourSet.iter_ = contour_iter
41-
42-
4335
@property
4436
def mean_coordinates(self):
4537
return self.vertices.mean(axis=0)
@@ -100,8 +92,8 @@ def uniform_resample(x_val, y_val, num_fac=2, fixed_size=None):
10092
dist = empty(x_val.shape)
10193
dist[0] = 0
10294
dist[1:] = distance(x_val[:-1], y_val[:-1], x_val[1:], y_val[1:])
103-
# To be still monotonous
104-
dist[1:][dist[1:]<1e-10] = 1e-10
95+
# To be still monotonous (dist is store in m)
96+
dist[1:][dist[1:]<1e-3] = 1e-3
10597
dist = dist.cumsum()
10698
# Get uniform distances
10799
if fixed_size is None:
@@ -121,13 +113,21 @@ def uniform_resample_stack(vertices, num_fac=2, fixed_size=None):
121113
data[:, 1] = y_new
122114
return data
123115

116+
124117
@njit(cache=True)
125118
def value_on_regular_contour(x_g, y_g, z_g, m_g, vertices, num_fac=2, fixed_size=None):
126119
x_val, y_val = vertices[:, 0], vertices[:, 1]
127120
x_new, y_new = uniform_resample(x_val, y_val, num_fac, fixed_size)
128121
return interp2d_geo(x_g, y_g, z_g, m_g, x_new[1:], y_new[1:])
129122

130123

124+
@njit(cache=True)
125+
def mean_on_regular_contour(x_g, y_g, z_g, m_g, vertices, num_fac=2, fixed_size=None):
126+
x_val, y_val = vertices[:, 0], vertices[:, 1]
127+
x_new, y_new = uniform_resample(x_val, y_val, num_fac, fixed_size)
128+
return interp2d_geo(x_g, y_g, z_g, m_g, x_new[1:], y_new[1:]).mean()
129+
130+
131131
def fit_circle_path(self):
132132
if not hasattr(self, '_circle_params'):
133133
self._circle_params = _fit_circle_path(self.vertices)
@@ -715,7 +715,7 @@ def eddy_identification(self, grid_height, uname, vname, date, step=0.005, shape
715715
c_x, c_y = proj(speed_contour.lon, speed_contour.lat)
716716
_, _, eddy_radius_s, aerr_s = fit_circle_c_numba(c_x, c_y)
717717

718-
# Instantiate new EddyObservation object
718+
# Instantiate new EddyObservation object (high cost need to be review)
719719
properties = EddiesObservations(size=1, track_extra_variables=track_extra_variables,
720720
track_array_variables=array_sampling,
721721
array_variables=array_variables)
@@ -780,7 +780,7 @@ def get_uavg(self, all_contours, centlon_e, centlat_e, original_contour, anticyc
780780
Calculate geostrophic speed around successive contours
781781
Returns the average
782782
"""
783-
max_average_speed = self.speed_coef(original_contour).mean()
783+
max_average_speed = self.speed_coef_mean(original_contour)
784784
speed_array = [max_average_speed]
785785
pixel_min = 1
786786

@@ -808,7 +808,7 @@ def get_uavg(self, all_contours, centlon_e, centlat_e, original_contour, anticyc
808808
if pixel_min > level_contour.nb_pixel:
809809
break
810810
# Interpolate uspd to seglon, seglat, then get mean
811-
level_average_speed = self.speed_coef(level_contour).mean()
811+
level_average_speed = self.speed_coef_mean(level_contour)
812812
speed_array.append(level_average_speed)
813813
if level_average_speed >= max_average_speed:
814814
max_average_speed = level_average_speed
@@ -944,12 +944,12 @@ def _low_filter(self, grid_name, x_cut, y_cut, factor=40.):
944944
z_interp = RectBivariateSpline(x_center, y_center, z_filtered, **opts_interpolation).ev(x, y)
945945
return ma.array(z_interp, mask=m_interp.ev(x, y) > 0.00001)
946946

947-
def speed_coef(self, contour):
947+
def speed_coef_mean(self, contour):
948948
dist, idx = self.index_interp.query(uniform_resample_stack(contour.vertices)[1:], k=4)
949949
i_y = idx % self.x_c.shape[1]
950950
i_x = int_((idx - i_y) / self.x_c.shape[1])
951951
# A simplified solution to be change by a weight mean
952-
return self._speed_norm[i_x, i_y].mean(axis=1)
952+
return self._speed_norm[i_x, i_y].mean(axis=1).mean()
953953

954954
def init_speed_coef(self, uname='u', vname='v'):
955955
self._speed_norm = (self.grid(uname) ** 2 + self.grid(vname) ** 2) ** .5
@@ -1348,11 +1348,11 @@ def add_uv(self, grid_height):
13481348
d_x = self.EARTH_RADIUS * 2 * pi / 360 * d_x_degrees * cos(deg2rad(self.y_c))
13491349
self.vars['v'] = d_hx / d_x * gof
13501350

1351-
def speed_coef(self, contour):
1351+
def speed_coef_mean(self, contour):
13521352
"""some nan can be compute over contour if we are near border,
13531353
something to explore
13541354
"""
1355-
return value_on_regular_contour(
1355+
return mean_on_regular_contour(
13561356
self.x_c, self.y_c,
13571357
self._speed_ev, self._speed_ev.mask,
13581358
contour.vertices)
@@ -1378,18 +1378,8 @@ def interp(self, grid_name, lons, lats):
13781378
Returns:
13791379
new z
13801380
"""
1381-
z = empty(lons.shape, dtype='f4').reshape(-1)
13821381
g = self.grid(grid_name)
1383-
interp_numba(
1384-
self.x_c,
1385-
self.y_c,
1386-
g,
1387-
lons.reshape(-1),
1388-
lats.reshape(-1),
1389-
z,
1390-
g.fill_value
1391-
)
1392-
return z
1382+
return interp2d_geo(self.x_c, self.y_c, g, g.mask, lons, lats)
13931383

13941384

13951385
@njit(cache=True, fastmath=True, parallel=True)

src/py_eddy_tracker/eddy_feature.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,16 @@ class Amplitude(object):
4646
'pixel_mask',
4747
'sla',
4848
'contour',
49-
'interval',
49+
'interval_min',
5050
'amplitude',
5151
'mle',
5252
)
5353

5454
def __init__(self, contour, contour_height, data, interval):
5555
# Height of the contour
5656
self.h_0 = contour_height
57-
# Step between two level
58-
self.interval = interval
57+
# Step minimal to consider amplitude
58+
self.interval_min = interval * 2
5959
# Indices of all pixels in contour
6060
self.contour = contour
6161
# Link on original grid (local view) or copy if it's on bound
@@ -71,10 +71,10 @@ def __init__(self, contour, contour_height, data, interval):
7171
self.grid_extract = data[x_start:x_stop, y_start:y_stop]
7272
# => maybe replace pixel out of contour by nan?
7373
self.pixel_mask = zeros(self.grid_extract.shape, dtype='bool')
74+
i_x = contour.pixels_index[0] - x_start
7475
if on_bounds:
75-
i_x = (contour.pixels_index[0] - x_start) % data.shape[0]
76-
else:
77-
i_x = contour.pixels_index[0] - x_start
76+
i_x %= data.shape[0]
77+
7878
self.pixel_mask[i_x, contour.pixels_index[1] - y_start] = True
7979

8080
# Only pixel in contour
@@ -87,7 +87,7 @@ def __init__(self, contour, contour_height, data, interval):
8787
def within_amplitude_limits(self):
8888
"""Need update
8989
"""
90-
return (self.interval * 2) <= self.amplitude
90+
return self.interval_min <= self.amplitude
9191
return self.eddy.ampmin <= self.amplitude <= self.eddy.ampmax
9292

9393
def all_pixels_below_h0(self, level):
@@ -96,26 +96,27 @@ def all_pixels_below_h0(self, level):
9696
are below a given SSH threshold for cyclonic eddies.
9797
"""
9898
# In some case pixel value must be very near of contour bounds
99-
if ((self.sla - self.h_0) > self.EPSILON).any() or self.sla.mask.any():
99+
if self.sla.mask.any() or ((self.sla.data - self.h_0) > self.EPSILON).any():
100100
return False
101101
else:
102102
# All local extrema index on th box
103-
lmi_i, lmi_j = detect_local_minima_(self.grid_extract, self.grid_extract.mask, self.pixel_mask, self.mle, 1)
103+
lmi_i, lmi_j = detect_local_minima_(self.grid_extract.data, self.grid_extract.mask, self.pixel_mask, self.mle, 1)
104+
# After we use grid.data because index are in contour and we check before than no pixel are hide
104105
nb = len(lmi_i)
105-
(x_start, _), (y_start, _) = self.contour.bbox_slice
106106
if nb == 0:
107107
logging.warning('No extrema found in contour in level %f', level)
108108
return False
109109
elif nb == 1:
110110
i, j = lmi_i[0], lmi_j[0]
111111
else:
112112
# Verify if several extrema are seriously below contour
113-
nb_real_extrema = ((level - self.grid_extract[lmi_i, lmi_j]) >= 2 * self.interval).sum()
113+
nb_real_extrema = ((level - self.grid_extract.data[lmi_i, lmi_j]) >= self.interval_min).sum()
114114
if nb_real_extrema > self.mle:
115115
return False
116-
index = self.grid_extract[lmi_i, lmi_j].argmin()
116+
index = self.grid_extract.data[lmi_i, lmi_j].argmin()
117117
i, j = lmi_i[index], lmi_j[index]
118-
self.amplitude = abs(self.grid_extract[i, j] - self.h_0)
118+
self.amplitude = abs(self.grid_extract.data[i, j] - self.h_0)
119+
(x_start, _), (y_start, _) = self.contour.bbox_slice
119120
i += x_start
120121
j += y_start
121122
return i, j
@@ -126,26 +127,26 @@ def all_pixels_above_h0(self, level):
126127
are above a given SSH threshold for anticyclonic eddies.
127128
"""
128129
# In some case pixel value must be very near of contour bounds
129-
if ((self.sla - self.h_0) < - self.EPSILON).any() or self.sla.mask.any():
130+
if self.sla.mask.any() or ((self.h_0 - self.sla.data) > self.EPSILON).any():
130131
return False
131132
else:
132133
# All local extrema index on th box
133-
lmi_i, lmi_j = detect_local_minima_(self.grid_extract, self.grid_extract.mask, self.pixel_mask, self.mle, -1)
134+
lmi_i, lmi_j = detect_local_minima_(self.grid_extract.data, self.grid_extract.mask, self.pixel_mask, self.mle, -1)
134135
nb = len(lmi_i)
135-
(x_start, _), (y_start, _) = self.contour.bbox_slice
136136
if nb == 0:
137137
logging.warning('No extrema found in contour in level %f', level)
138138
return False
139139
elif nb == 1:
140140
i, j = lmi_i[0], lmi_j[0]
141141
else:
142142
# Verify if several extrema are seriously above contour
143-
nb_real_extrema = ((self.grid_extract[lmi_i, lmi_j] - level) >= 2 * self.interval).sum()
143+
nb_real_extrema = ((self.grid_extract.data[lmi_i, lmi_j] - level) >= self.interval_min).sum()
144144
if nb_real_extrema > self.mle:
145145
return False
146-
index = self.grid_extract[lmi_i, lmi_j].argmax()
146+
index = self.grid_extract.data[lmi_i, lmi_j].argmax()
147147
i, j = lmi_i[index], lmi_j[index]
148-
self.amplitude = abs(self.grid_extract[i, j] - self.h_0)
148+
self.amplitude = abs(self.grid_extract.data[i, j] - self.h_0)
149+
(x_start, _), (y_start, _) = self.contour.bbox_slice
149150
i += x_start
150151
j += y_start
151152
return i, j

src/py_eddy_tracker/observations.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ class EddiesObservations(object):
159159
*local maxima/minima* within a closed region of a sea level anomaly field.
160160
161161
"""
162+
__slots__ = (
163+
'track_extra_variables',
164+
'track_array_variables',
165+
'array_variables',
166+
'observations',
167+
'active',
168+
'sign_type',
169+
)
162170

163171
DELTA_JJ_JE = 2448623 - (datetime(1992, 1, 1) - datetime(1950, 1, 1)).days
164172

@@ -851,6 +859,7 @@ def display(self, ax, ref=0, **kwargs):
851859
class VirtualEddiesObservations(EddiesObservations):
852860
"""Class to work with virtual obs
853861
"""
862+
__slots__ = ()
854863

855864
@property
856865
def elements(self):
@@ -862,6 +871,7 @@ def elements(self):
862871
class TrackEddiesObservations(EddiesObservations):
863872
"""Class to practice Tracking on observations
864873
"""
874+
__slots__ = ()
865875

866876
def filled_by_interpolation(self, mask):
867877
"""Filled selected values by interpolation

0 commit comments

Comments
 (0)