Skip to content

Commit 146e4cd

Browse files
committed
Bugfix for missing anticyclones
1 parent a56f2fc commit 146e4cd

5 files changed

+85
-35
lines changed

eddy_tracker_configuration.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ DIAGNOSTIC_TYPE: 'SLA'
1010
# Specify the AVISO domain
1111
DOMAIN:
1212
THE_DOMAIN: 'Global'
13+
# THE_DOMAIN: 'Regional'
1314
#THE_DOMAIN = 'BlackSea'
1415
#THE_DOMAIN = 'MedSea' # not yet implemented
1516
LONMIN: -40.

make_eddy_track_AVISO.py

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ def set_initial_indices(self, LONMIN, LONMAX, LATMIN, LATMAX):
145145
"""
146146
Get indices for desired domain
147147
"""
148-
print '--- Setting initial indices to LONMIN, LONMAX, LATMIN, LATMAX'
148+
print '--- Setting initial indices to %s domain' % self.THE_DOMAIN
149+
print '------ LONMIN = %s, LONMAX = %s, LATMIN = %s, LATMAX = %s' % (
150+
LONMIN, LONMAX, LATMIN, LATMAX)
149151
self.i0, junk = self.nearest_point(LONMIN,
150152
LATMIN + 0.5 * (LATMAX - LATMIN))
151153
self.i1, junk = self.nearest_point(LONMAX,
@@ -199,7 +201,7 @@ def set_index_padding(self, pad=2):
199201
around 2d variables.
200202
Padded matrices are needed only for geostrophic velocity computation.
201203
"""
202-
print '--- Setting padding indices with PAD=%s' % pad
204+
print '--- Setting padding indices with PAD = %s' % pad
203205

204206
self.pad = pad
205207

@@ -489,13 +491,14 @@ class AvisoGrid (PyEddyTracker):
489491
Class to satisfy the need of the eddy tracker
490492
to have a grid class
491493
"""
492-
def __init__(self, AVISO_FILE, LONMIN, LONMAX, LATMIN, LATMAX,
494+
def __init__(self, AVISO_FILE, THE_DOMAIN, LONMIN, LONMAX, LATMIN, LATMAX,
493495
with_pad=True):
494496
"""
495497
Initialise the grid object
496498
"""
497499
super(AvisoGrid, self).__init__()
498500
print '\nInitialising the AVISO_grid'
501+
self.THE_DOMAIN = THE_DOMAIN
499502
self.LONMIN = LONMIN
500503
self.LONMAX = LONMAX
501504
self.LATMIN = LATMIN
@@ -766,10 +769,21 @@ def set_interp_coeffs(self, sla, uspd):
766769
DIAGNOSTIC_TYPE = config['DIAGNOSTIC_TYPE']
767770

768771
config['THE_DOMAIN'] = config['DOMAIN']['THE_DOMAIN']
769-
config['LONMIN'] = config['DOMAIN']['LONMIN']
770-
config['LONMAX'] = config['DOMAIN']['LONMAX']
771-
config['LATMIN'] = config['DOMAIN']['LATMIN']
772-
config['LATMAX'] = config['DOMAIN']['LATMAX']
772+
773+
# It is not recommended to change values given below
774+
# for 'Global', 'BlackSea' or 'MedSea'...
775+
if 'Global' in config['THE_DOMAIN']:
776+
config['LONMIN'] = -100.
777+
config['LONMAX'] = 290.
778+
config['LATMIN'] = -80.
779+
config['LATMAX'] = 80.
780+
781+
elif 'Regional' in config['THE_DOMAIN']:
782+
config['LONMIN'] = config['DOMAIN']['LONMIN']
783+
config['LONMAX'] = config['DOMAIN']['LONMAX']
784+
config['LATMIN'] = config['DOMAIN']['LATMIN']
785+
config['LATMAX'] = config['DOMAIN']['LATMAX']
786+
773787
DATE_STR = config['DATE_STR'] = config['DOMAIN']['DATE_STR']
774788
DATE_END = config['DATE_END'] = config['DOMAIN']['DATE_END']
775789

@@ -865,8 +879,9 @@ def set_interp_coeffs(self, sla, uspd):
865879
AVISO_FILES = AVISO_FILES[5:-5:np.int(DAYS_BTWN_RECORDS)]
866880

867881
# Set up a grid object using first AVISO file in the list
868-
sla_grd = AvisoGrid(AVISO_FILES[0], config['LONMIN'], config['LONMAX'],
869-
config['LATMIN'], config['LATMAX'])
882+
sla_grd = AvisoGrid(AVISO_FILES[0], config['THE_DOMAIN'],
883+
config['LONMIN'], config['LONMAX'],
884+
config['LATMIN'], config['LATMAX'])
870885

871886
Mx, My = (sla_grd.Mx[sla_grd.jup0:sla_grd.jup1, sla_grd.iup0:sla_grd.iup1],
872887
sla_grd.My[sla_grd.jup0:sla_grd.jup1, sla_grd.iup0:sla_grd.iup1])
@@ -897,9 +912,10 @@ def set_interp_coeffs(self, sla, uspd):
897912
#kwargs = config
898913
A_eddy = eddy_tracker.TrackList('AVISO', 'Anticyclonic', A_SAVEFILE,
899914
sla_grd, search_ellipse, **config)
915+
900916
C_eddy = eddy_tracker.TrackList('AVISO', 'Cyclonic', C_SAVEFILE,
901917
sla_grd, search_ellipse, **config)
902-
918+
903919
A_eddy.search_ellipse = search_ellipse
904920
C_eddy.search_ellipse = search_ellipse
905921

@@ -920,8 +936,8 @@ def set_interp_coeffs(self, sla, uspd):
920936
C_eddy.PIXEL_THRESHOLD = [PIXMIN, PIXMAX]
921937

922938
# Create nc files for saving of eddy tracks
923-
A_eddy.create_netcdf(DATA_DIR, A_SAVEFILE, 'Anticyclonic')
924-
C_eddy.create_netcdf(DATA_DIR, C_SAVEFILE, 'Cyclonic')
939+
A_eddy.create_netcdf(DATA_DIR, A_SAVEFILE)
940+
C_eddy.create_netcdf(DATA_DIR, C_SAVEFILE)
925941

926942

927943
# Loop through the AVISO files...
@@ -1084,12 +1100,11 @@ def set_interp_coeffs(self, sla, uspd):
10841100
A_CS = ax.contour(sla_grd.lon(),
10851101
sla_grd.lat(),
10861102
A_eddy.sla, A_eddy.CONTOUR_PARAMETER)
1087-
# Note that CSc is for the cyclonics,
1088-
# CONTOUR_PARAMETER in reverse order
1103+
# Note that C_CS is in reverse order
10891104
C_CS = ax.contour(sla_grd.lon(),
10901105
sla_grd.lat(),
10911106
C_eddy.sla, C_eddy.CONTOUR_PARAMETER)
1092-
1107+
10931108
else:
10941109
Exception
10951110

@@ -1113,10 +1128,6 @@ def set_interp_coeffs(self, sla, uspd):
11131128
C_eddy.swirl = SwirlSpeed(C_CS)
11141129

11151130
# Now we loop over the CS collection
1116-
A_eddy.SIGN_TYPE = 'Anticyclonic'
1117-
C_eddy.SIGN_TYPE = 'Cyclonic'
1118-
1119-
qqq
11201131
if 'Q' in DIAGNOSTIC_TYPE:
11211132
A_eddy, C_eddy = collection_loop(CS, sla_grd, rtime,
11221133
A_list_obj=A_eddy, C_list_obj=C_eddy,

make_eddy_tracker_list_obj.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def __init__(self, DATATYPE, SIGN_TYPE, SAVE_DIR, grd, search_ellipse,
287287

288288
self.DIAGNOSTIC_TYPE = kwargs.get('DIAGNOSTIC_TYPE', 'SLA')
289289

290-
self.THE_DOMAIN = kwargs.get('THE_DOMAIN', 'Global')
290+
self.THE_DOMAIN = kwargs.get('THE_DOMAIN', 'Regional')
291291
self.LONMIN = np.float64(kwargs.get('LONMIN', -40))
292292
self.LONMAX = np.float64(kwargs.get('LONMAX', -30))
293293
self.LATMIN = np.float64(kwargs.get('LATMIN', 20))
@@ -304,10 +304,13 @@ def __init__(self, DATATYPE, SIGN_TYPE, SAVE_DIR, grd, search_ellipse,
304304

305305
self.INTERP_METHOD = kwargs.get('INTERP_METHOD', 'RectBivariate')
306306
self.JDAY_REFERENCE = kwargs.get('JDAY_REFERENCE', 2448623.0)
307+
308+
# NOTE: '.copy()' suffix is essential here
307309
self.CONTOUR_PARAMETER = kwargs.get('CONTOUR_PARAMETER',
308-
np.linspace(-100., 101, 1))
310+
np.arange(-100., 101, 1)).copy()
309311
if 'Cyclonic' in SIGN_TYPE:
310-
self.CONTOUR_PARAMETER = self.CONTOUR_PARAMETER[::-1]
312+
self.CONTOUR_PARAMETER *= -1
313+
311314
self.SHAPE_ERROR = kwargs.get('SHAPE_ERROR',
312315
np.full(self.CONTOUR_PARAMETER.size, 55.))
313316

@@ -514,7 +517,7 @@ def get_inactive_tracks(self, rtime):
514517

515518

516519

517-
def create_netcdf(self, directory, savedir, title,
520+
def create_netcdf(self, directory, savedir,
518521
grd=None, Ymin=None, Ymax=None,
519522
Mmin=None, Mmax=None, model=None,
520523
sigma_lev=None, rho_ntr=None):
@@ -526,7 +529,7 @@ def create_netcdf(self, directory, savedir, title,
526529
else:
527530
self.savedir = savedir.replace('.nc', '_ARGO_enabled.nc')
528531
nc = Dataset(self.savedir, 'w', format='NETCDF4')
529-
nc.title = ''.join((title, ' eddy tracks'))
532+
nc.title = ''.join((self.SIGN_TYPE, ' eddy tracks'))
530533
nc.directory = directory
531534
nc.DAYS_BTWN_RECORDS = np.float64(self.DAYS_BTWN_RECORDS)
532535
nc.TRACK_DURATION_MIN = np.float64(self.TRACK_DURATION_MIN)
@@ -1074,7 +1077,7 @@ def __init__(self, THE_DOMAIN, grd, RW_PATH=None):
10741077
self.ZERO_CROSSING = grd.ZERO_CROSSING
10751078
self.RW_PATH = RW_PATH
10761079
self._tree = None
1077-
if self.THE_DOMAIN in ('Global', 'ROMS'):
1080+
if self.THE_DOMAIN in ('Global', 'Regional', 'ROMS'):
10781081
assert self.RW_PATH is not None, \
10791082
'Must supply a path for the Rossby deformation radius data'
10801083
data = np.loadtxt(RW_PATH)
@@ -1094,6 +1097,7 @@ def __init__(self, THE_DOMAIN, grd, RW_PATH=None):
10941097

10951098
def __getstate__(self):
10961099
"""
1100+
Needed for Pickle
10971101
"""
10981102
result = self.__dict__.copy()
10991103
result.pop('_tree')
@@ -1102,15 +1106,18 @@ def __getstate__(self):
11021106

11031107
def __setstate__(self, thedict):
11041108
"""
1109+
Needed for Pickle
11051110
"""
11061111
self.__dict__ = thedict
11071112
self._make_kdtree()
11081113

11091114

11101115
def get_rwdistance(self, xpt, ypt, DAYS_BTWN_RECORDS):
11111116
"""
1117+
Return the distance required by SearchEllipse
1118+
to construct a search ellipse for eddy tracking.
11121119
"""
1113-
if self.THE_DOMAIN in ('Global', 'ROMS'):
1120+
if self.THE_DOMAIN in ('Global', 'Regional', 'ROMS'):
11141121
#print 'xpt, ypt', xpt, ypt
11151122
self.distance[:] = self._get_rlongwave_spd(xpt, ypt)
11161123
self.distance *= 86400.
@@ -1152,6 +1159,8 @@ def get_rwdistance(self, xpt, ypt, DAYS_BTWN_RECORDS):
11521159

11531160
def _make_subset(self):
11541161
"""
1162+
Make a subset of _defrad data over the domain.
1163+
If 'Global' is defined then widen the domain.
11551164
"""
11561165
pad = 1.5 # degrees
11571166
LONMIN, LONMAX, LATMIN, LATMAX = self.limits
@@ -1168,11 +1177,21 @@ def _make_subset(self):
11681177
self._lon = self._lon[lloi]
11691178
self._lat = self._lat[lloi]
11701179
self._defrad = self._defrad[lloi]
1180+
1181+
if 'Global' in self.THE_DOMAIN:
1182+
lloi = self._lon > 260.
1183+
self._lon = np.append(self._lon, self._lon[lloi] - 360.)
1184+
self._lat = np.append(self._lat, self._lat[lloi])
1185+
self._defrad = np.append(self._defrad, self._defrad[lloi])
1186+
11711187
self.x, self.y = self.M(self._lon, self._lat)
11721188
return self
11731189

11741190

11751191
def _make_kdtree(self):
1192+
"""
1193+
Compute KDE tree for nearest indices.
1194+
"""
11761195
points = np.vstack([self.x, self.y]).T
11771196
self._tree = spatial.cKDTree(points)
11781197
return self
@@ -1207,8 +1226,25 @@ def _get_rlongwave_spd(self, xpt, ypt):
12071226
return self.r_spd_long
12081227

12091228

1210-
1211-
1229+
def view_grid_subset(self):
1230+
"""
1231+
Figure to check RossbyWaveSpeed grid after call to
1232+
self._make_subset()
1233+
To use, uncomment in SearchEllipse __init__ method
1234+
"""
1235+
stride = 30
1236+
plt.figure()
1237+
ax = plt.subplot()
1238+
self.M.scatter(self.x, self.y, c='b')
1239+
self.M.drawcoastlines()
1240+
self.M.fillcontinents()
1241+
self.M.drawparallels(np.arange(-90, 90. + stride, stride),
1242+
labels=[1, 0, 0, 0], ax=ax)
1243+
self.M.drawmeridians(np.arange(-360, 360. + stride, stride),
1244+
labels=[0, 0, 0, 1], ax=ax)
1245+
plt.show()
1246+
1247+
12121248
class SearchEllipse (object):
12131249
"""
12141250
Class to construct a search ellipse/circle around a specified point.
@@ -1238,6 +1274,7 @@ def __init__(self, THE_DOMAIN, grd, DAYS_BTWN_RECORDS, RW_PATH=None):
12381274
self.n_s_minor = self.DAYS_BTWN_RECORDS * 15e4 / 7.
12391275
self.semi_n_s_minor = 0.5 * self.n_s_minor
12401276
self.rwv = RossbyWaveSpeed(THE_DOMAIN, grd, RW_PATH=RW_PATH)
1277+
#self.rwv.view_grid_subset()
12411278
self.rw_c = np.empty(1)
12421279
self.rw_c_mod = np.empty(1)
12431280
self.rw_c_fac = 1.75
@@ -1306,7 +1343,7 @@ def set_search_ellipse(self, xpt, ypt):
13061343
self.xpt = xpt
13071344
self.ypt = ypt
13081345

1309-
if self.THE_DOMAIN in ('Global', 'ROMS'):
1346+
if self.THE_DOMAIN in ('Global', 'Regional', 'ROMS'):
13101347
self.rw_c[:] = self.rwv.get_rwdistance(xpt, ypt,
13111348
self.DAYS_BTWN_RECORDS)
13121349
self.rw_c_mod[:] = 1.75

py_eddy_tracker_classes.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ def get_uavg(Eddy, CS, collind, centlon_e, centlat_e, poly_eff,
421421
poly_eff.vertices[:, 1].copy()
422422

423423
theseglon, theseglat = eddy_tracker.uniform_resample(
424-
theseglon, theseglat)
424+
theseglon, theseglat, method='akima')
425425
if 'RectBivariate' in Eddy.INTERP_METHOD:
426426
uavg = Eddy.uspd_coeffs.ev(theseglat[1:], theseglon[1:]).mean()
427427

@@ -480,7 +480,7 @@ def get_uavg(Eddy, CS, collind, centlon_e, centlat_e, poly_eff,
480480

481481
seglon, seglat = poly_i.vertices[:, 0], poly_i.vertices[:, 1]
482482
seglon, seglat = eddy_tracker.uniform_resample(
483-
seglon, seglat)
483+
seglon, seglat, method='akima')
484484

485485
# Interpolate uspd to seglon, seglat, then get mean
486486
if 'RectBivariate' in Eddy.INTERP_METHOD:
@@ -681,7 +681,7 @@ def collection_loop(CS, grd, rtime, A_list_obj, C_list_obj,
681681
# circumferential distribution
682682
contlon_e, contlat_e = \
683683
eddy_tracker.uniform_resample(contlon_e,
684-
contlat_e)
684+
contlat_e, method='akima')
685685

686686
if 'Q' in Eddy.DIAGNOSTIC_TYPE:
687687
# Note, eddy amplitude == max(abs(vort/f)) within eddy, KCCMC11
@@ -1299,7 +1299,8 @@ def accounting(Eddy, old_ind, centlon, centlat,
12991299
Eddy.insert_at_index('new_shape_error', Eddy.index, shape_error)
13001300

13011301
if Eddy.new_list is True: # initialise a new list
1302-
print '------ starting a new track list for %ss' % Eddy.SIGN_TYPE
1302+
print ('------ starting a new track list for %s' %
1303+
Eddy.SIGN_TYPE.replace('nic', 'nes'))
13031304
Eddy.new_list = False
13041305

13051306
args = (centlon, centlat, rtime, uavg, teke,

py_eddy_tracker_property_classes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ def _set_local_extrema(self, sign):
213213
"""
214214
Set count of local SLA maxima/minima within eddy
215215
"""
216-
#local_extrema = np.ma.masked_where(self.mask == False, self.sla)
217-
local_extrema = self.sla
216+
local_extrema = np.ma.masked_where(self.mask == False, self.sla)
217+
#local_extrema = self.sla
218218
local_extrema *= sign
219219
self._detect_local_minima(local_extrema)
220220
return self

0 commit comments

Comments
 (0)