Skip to content

Commit e925820

Browse files
author
adelepoulle
committed
move action in class method
1 parent 91f80a5 commit e925820

File tree

2 files changed

+322
-23
lines changed

2 files changed

+322
-23
lines changed
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
===========================================================================
4+
This file is part of py-eddy-tracker.
5+
6+
py-eddy-tracker is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
py-eddy-tracker is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with py-eddy-tracker. If not, see <http://www.gnu.org/licenses/>.
18+
19+
Copyright (c) 2014-2015 by Evan Mason
20+
21+
===========================================================================
22+
23+
24+
py_eddy_tracker_amplitude.py
25+
26+
Version 2.0.3
27+
28+
===========================================================================
29+
30+
"""
31+
from numpy import zeros, empty, nan, arange, interp
32+
from netCDF4 import Dataset
33+
from py_eddy_tracker.tools import distance_matrix
34+
from . import VAR_DESCR, VAR_DESCR_inv
35+
import logging
36+
37+
38+
class EddiesObservations(object):
39+
"""
40+
Class to hold eddy properties *amplitude* and counts of
41+
*local maxima/minima* within a closed region of a sea level anomaly field.
42+
43+
Variables:
44+
centlon:
45+
Longitude centroid coordinate
46+
47+
centlat:
48+
Latitude centroid coordinate
49+
50+
eddy_radius_s:
51+
Speed based radius
52+
53+
eddy_radius_e:
54+
Effective radius
55+
56+
amplitude:
57+
Eddy amplitude
58+
59+
uavg:
60+
Average eddy swirl speed
61+
62+
teke:
63+
Average eddy kinetic energy within eddy
64+
65+
rtime:
66+
Time
67+
"""
68+
69+
def __init__(self, size=0, track_extra_variables=False):
70+
self.track_extra_variables = track_extra_variables
71+
for elt in self.elements:
72+
if elt not in VAR_DESCR:
73+
raise Exception('Unknown element : %s' % elt)
74+
self.observations = zeros(size, dtype=self.dtype)
75+
self.active = True
76+
self.sign_type = None
77+
78+
def __repr__(self):
79+
return str(self.observations)
80+
81+
@property
82+
def dtype(self):
83+
dtype = list()
84+
for elt in self.elements:
85+
dtype.append((elt, VAR_DESCR[elt][
86+
'compute_type' if 'compute_type' in VAR_DESCR[elt] else
87+
'nc_type']))
88+
return dtype
89+
90+
@property
91+
def elements(self):
92+
elements = [
93+
'lon', # 'centlon'
94+
'lat', # 'centlat'
95+
'radius_s', # 'eddy_radius_s'
96+
'radius_e', # 'eddy_radius_e'
97+
'amplitude', # 'amplitude'
98+
'speed_radius', # 'uavg'
99+
'eke', # 'teke'
100+
'time'] # 'rtime'
101+
102+
if self.track_extra_variables:
103+
elements += ['contour_e',
104+
'contour_s',
105+
'uavg_profile',
106+
'shape_error',
107+
]
108+
return elements
109+
110+
def coherence(self, other):
111+
return self.track_extra_variables == other.track_extra_variables
112+
113+
def merge(self, other):
114+
nb_obs_self = len(self)
115+
nb_obs = nb_obs_self + len(other)
116+
eddies = self.__class__(size=nb_obs)
117+
eddies.obs[:nb_obs_self] = self.obs[:]
118+
eddies.obs[nb_obs_self:] = other.obs[:]
119+
eddies.sign_type = self.sign_type
120+
return eddies
121+
122+
@property
123+
def obs(self):
124+
return self.observations
125+
126+
def __len__(self):
127+
return len(self.observations)
128+
129+
def __iter__(self):
130+
for obs in self.obs:
131+
yield obs
132+
133+
def insert_observations(self, other, index):
134+
if not self.coherence(other):
135+
raise Exception('Observations with no coherence')
136+
insert_size = len(other.obs)
137+
self_size = len(self.obs)
138+
new_size = self_size + insert_size
139+
if self_size == 0:
140+
self.observations = other.obs
141+
return self
142+
elif insert_size == 0:
143+
return self
144+
if index < 0:
145+
index = self_size + index + 1
146+
eddies = self.__class__(new_size, self.track_extra_variables)
147+
eddies.obs[:index] = self.obs[:index]
148+
eddies.obs[index: index + insert_size] = other.obs
149+
eddies.obs[index + insert_size:] = self.obs[index:]
150+
self.observations = eddies.obs
151+
return self
152+
153+
def append(self, other):
154+
return self + other
155+
156+
def __add__(self, other):
157+
return self.insert_observations(other, -1)
158+
159+
def distance(self, other):
160+
""" Use haversine distance for distance matrix between every self and
161+
other eddies"""
162+
dist_result = empty((len(self), len(other)), dtype='f8') + nan
163+
distance_matrix(
164+
self.obs['lon'], self.obs['lat'],
165+
other.obs['lon'], other.obs['lat'],
166+
dist_result)
167+
return dist_result
168+
169+
def index(self, index):
170+
size = 1
171+
if hasattr(index, '__iter__'):
172+
size = len(index)
173+
eddies = self.__class__(size, self.track_extra_variables)
174+
eddies.obs[:] = self.obs[index]
175+
return eddies
176+
177+
@staticmethod
178+
def load_from_netcdf(filename):
179+
with Dataset(filename) as h_nc:
180+
nb_obs = len(h_nc.dimensions['Nobs'])
181+
eddies = EddiesObservations(size=nb_obs)
182+
for variable in h_nc.variables:
183+
if variable == 'cyc':
184+
continue
185+
eddies.obs[VAR_DESCR_inv[variable]] = h_nc.variables[variable][:]
186+
eddies.sign_type = h_nc.variables['cyc'][0]
187+
return eddies
188+
189+
190+
class VirtualEddiesObservations(EddiesObservations):
191+
192+
@property
193+
def elements(self):
194+
elements = super(VirtualEddiesObservations, self).elements
195+
elements.extend(['track', 'segment_size', 'dlon', 'dlat'])
196+
return elements
197+
198+
199+
class TrackEddiesObservations(EddiesObservations):
200+
201+
def filled_by_interpolation(self, mask):
202+
"""Filled selected values by interpolation
203+
"""
204+
nb_filled = mask.sum()
205+
logging.info('%d obs will be filled (unobserved)', nb_filled)
206+
207+
nb_obs = len(self)
208+
index = arange(nb_obs)
209+
210+
for var, _ in self.obs.dtype.descr:
211+
if var in ['n', 'virtual', 'track']:
212+
continue
213+
self.obs[var][mask] = interp(index[mask], index[-mask],
214+
self.obs[var][-mask])
215+
216+
def extract_longer_eddies(self, nb_min, nb_obs):
217+
m = nb_obs >= nb_min
218+
nb_obs_select = m.sum()
219+
logging.info('Selection of %d observations', nb_obs_select)
220+
eddies = TrackEddiesObservations(size=nb_obs_select)
221+
eddies.sign_type = self.sign_type
222+
for var, _ in eddies.obs.dtype.descr:
223+
eddies.obs[var] = self.obs[var][m]
224+
return eddies
225+
226+
@property
227+
def elements(self):
228+
elements = super(TrackEddiesObservations, self).elements
229+
elements.extend(['track', 'n', 'virtual'])
230+
return elements
231+
232+
def create_variable(self, handler_nc, kwargs_variable,
233+
attr_variable, data, scale_factor=None):
234+
var = handler_nc.createVariable(
235+
zlib=True,
236+
complevel=1,
237+
**kwargs_variable)
238+
for attr, attr_value in attr_variable.iteritems():
239+
var.setncattr(attr, attr_value)
240+
241+
var[:] = data
242+
243+
#~ var.set_auto_maskandscale(False)
244+
if scale_factor is not None:
245+
var.scale_factor = scale_factor
246+
247+
try:
248+
var.setncattr('min', var[:].min())
249+
var.setncattr('max', var[:].max())
250+
except ValueError:
251+
logging.warn('Data is empty')
252+
253+
def write_netcdf(self):
254+
"""Write a netcdf with eddy obs
255+
"""
256+
eddy_size = len(self.observations)
257+
sign_type = 'Cyclonic' if self.sign_type == -1 else 'Anticyclonic'
258+
filename = '%s.nc' % sign_type
259+
with Dataset(filename, 'w', format='NETCDF4') as h_nc:
260+
logging.info('Create file %s', filename)
261+
# Create dimensions
262+
logging.debug('Create Dimensions "Nobs" : %d', eddy_size)
263+
h_nc.createDimension('Nobs', eddy_size)
264+
# Iter on variables to create:
265+
for name, _ in self.observations.dtype.descr:
266+
logging.debug('Create Variable %s', VAR_DESCR[name]['nc_name'])
267+
self.create_variable(
268+
h_nc,
269+
dict(varname=VAR_DESCR[name]['nc_name'],
270+
datatype=VAR_DESCR[name]['nc_type'],
271+
dimensions=VAR_DESCR[name]['nc_dims']),
272+
VAR_DESCR[name]['nc_attr'],
273+
self.observations[name],
274+
scale_factor=None if 'scale_factor' not in VAR_DESCR[name] else VAR_DESCR[name]['scale_factor'])
275+
276+
# Add cyclonic information
277+
self.create_variable(
278+
h_nc,
279+
dict(varname=VAR_DESCR['type_cyc']['nc_name'],
280+
datatype=VAR_DESCR['type_cyc']['nc_type'],
281+
dimensions=VAR_DESCR['type_cyc']['nc_dims']),
282+
VAR_DESCR['type_cyc']['nc_attr'],
283+
self.sign_type)
284+
# Global attr
285+
self.set_global_attr_netcdf(h_nc)
286+
287+
def set_global_attr_netcdf(self, h_nc):
288+
h_nc.title = 'Cyclonic' if self.sign_type == -1 else 'Anticyclonic' + ' eddy tracks'
289+
#~ h_nc.grid_filename = self.grd.grid_filename
290+
#~ h_nc.grid_date = str(self.grd.grid_date)
291+
#~ h_nc.product = self.product
292+
293+
#~ h_nc.contour_parameter = self.contour_parameter
294+
#~ h_nc.shape_error = self.shape_error
295+
#~ h_nc.pixel_threshold = self.pixel_threshold
296+
297+
#~ if self.smoothing in locals():
298+
#~ h_nc.smoothing = self.smoothing
299+
#~ h_nc.SMOOTH_FAC = self.SMOOTH_FAC
300+
#~ else:
301+
#~ h_nc.smoothing = 'None'
302+
303+
#~ h_nc.evolve_amp_min = self.evolve_amp_min
304+
#~ h_nc.evolve_amp_max = self.evolve_amp_max
305+
#~ h_nc.evolve_area_min = self.evolve_area_min
306+
#~ h_nc.evolve_area_max = self.evolve_area_max
307+
#~
308+
#~ h_nc.llcrnrlon = self.grd.lonmin
309+
#~ h_nc.urcrnrlon = self.grd.lonmax
310+
#~ h_nc.llcrnrlat = self.grd.latmin
311+
#~ h_nc.urcrnrlat = self.grd.latmax

src/scripts/EddyTracking

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
from py_eddy_tracker import EddyParser
77
from glob import glob
88
from yaml import load as yaml_load
9-
from py_eddy_tracker.py_eddy_tracker_property_classes import \
9+
from py_eddy_tracker.observations import \
1010
EddiesObservations, TrackEddiesObservations, \
1111
VirtualEddiesObservations
12-
from py_eddy_tracker.tools import distance_matrix
1312

1413
import logging
1514
import numpy as np
@@ -57,18 +56,12 @@ if __name__ == '__main__':
5756

5857
if FLG_VIRTUAL:
5958
nb_real_obs = len(e_previous)
60-
# If you comment this the virtual fonctionnality will be disable
6159
logging.debug('%d virtual obs will be add to previous',
6260
len(virtual_obs))
61+
# If you comment this the virtual fonctionnality will be disable
6362
e_previous = e_previous.merge(virtual_obs)
64-
dist_result = np.empty((len(e_previous),
65-
len(e_current)),
66-
dtype='f8') + np.nan
67-
distance_matrix(
68-
e_previous.obs['lon'], e_previous.obs['lat'],
69-
e_current.obs['lon'], e_current.obs['lat'],
70-
dist_result)
71-
i_previous, i_current = np.where(dist_result < 20.)
63+
dist = e_previous.distance(e_current)
64+
i_previous, i_current = np.where(dist < 20.)
7265
nb_match = i_previous.shape[0]
7366

7467
logging.debug('%d match with previous', nb_match)
@@ -276,6 +269,7 @@ if __name__ == '__main__':
276269
# We flag obs
277270
FINAL_EDDIES.obs['virtual'] = FINAL_EDDIES.obs['time'] == 0
278271

272+
FINAL_EDDIES.filled_by_interpolation(FINAL_EDDIES.obs['virtual'] == 1)
279273
# Localization of virtual observation
280274
m_i = FINAL_EDDIES.obs['virtual'] == 1
281275
# Count virtual observations
@@ -299,16 +293,10 @@ if __name__ == '__main__':
299293
logging.info('The longest tracks have %d observations',
300294
nb_obs_by_tracks.max())
301295

302-
FINAL_EDDIES.extract_longer_eddies(
303-
NB_OBS_MIN, nb_obs_by_tracks.repeat(nb_obs_by_tracks)).write_netcdf()
304-
305-
306-
307-
#~ previous_amp, current_amp = np.meshgrid(
308-
#~ e_current.obs['amplitude'], e_previous.obs['amplitude'])
309-
#~ delta_amp = abs(current_amp - previous_amp) / previous_amp
296+
SUBSET_EDDIES = FINAL_EDDIES.extract_longer_eddies(
297+
NB_OBS_MIN, nb_obs_by_tracks.repeat(nb_obs_by_tracks))
298+
299+
logging.info('%d tracks will be saved',
300+
len(np.unique(SUBSET_EDDIES.obs['track'])))
310301

311-
#~ previous_radius, current_radius = np.meshgrid(
312-
#~ e_current.obs['radius_e'], e_previous.obs['radius_e'])
313-
#~ delta_radius = abs(current_radius ** 2 - previous_radius ** 2) ** .5 \
314-
#~ / previous_radius
302+
SUBSET_EDDIES.write_netcdf()

0 commit comments

Comments
 (0)