Skip to content

Commit 5f4e47e

Browse files
committed
- Cleanup
- Save always, short untracked - Add propagation function for virtual
1 parent 9d7b956 commit 5f4e47e

File tree

7 files changed

+126
-87
lines changed

7 files changed

+126
-87
lines changed

src/py_eddy_tracker/featured_tracking/old_tracker_reference.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@ def mask_function(self, other):
1818
"""We mask link with ellips and ratio
1919
"""
2020
# Compute Parameter of ellips
21+
minor, major = 1.05, 1.5
2122
y = self.basic_formula_ellips_major_axis(
2223
self.obs['lat'],
2324
degrees=True,
24-
c0=1.05,
25-
cmin=1.05,
26-
cmax=1.5,
25+
c0=minor,
26+
cmin=minor,
27+
cmax=major,
2728
lat1=23,
2829
lat2=5,
2930
)
3031
# mask from ellips
3132
mask = self.shifted_ellipsoid_degrees_mask(
3233
other,
33-
minor=1.5,
34+
minor=minor, # Minor can be bigger than major??
3435
major=y)
3536

3637
# We check ratio

src/py_eddy_tracker/grid/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ def browse_dataset_in(data_dir, files_model, date_regexp, date_model,
5050

5151
filenames = bytes_(glob(full_path))
5252
dataset_list = empty(len(filenames),
53-
dtype=[('filename', 'S256'),
53+
dtype=[('filename', 'S500'),
5454
('date', 'datetime64[D]'),
5555
])
56-
dataset_list['filename'] = bytes_(glob(full_path))
56+
dataset_list['filename'] = filenames
5757

5858
logging.info('%s grids available', dataset_list.shape[0])
5959
mode_attrs = False

src/py_eddy_tracker/observations.py

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
===========================================================================
2828
2929
"""
30-
from numpy import zeros, empty, nan, arange, interp, where, unique, \
30+
from numpy import zeros, empty, nan, arange, where, unique, \
3131
ma, concatenate, cos, radians, isnan, ones, ndarray, meshgrid, \
32-
bincount, bool_, array, interp, int_, int32, round, maximum, floor
32+
array, interp, int_, int32, round, maximum, floor
3333
from scipy.interpolate import interp1d
3434
from netCDF4 import Dataset
3535
from py_eddy_tracker.tools import distance_matrix, distance_vector
@@ -38,7 +38,6 @@
3838
from . import VAR_DESCR, VAR_DESCR_inv
3939
import logging
4040
from datetime import datetime
41-
from scipy.interpolate import RectBivariateSpline
4241

4342

4443
class GridDataset(object):
@@ -185,6 +184,10 @@ def __init__(self, size=0, track_extra_variables=None,
185184
self.active = True
186185
self.sign_type = None
187186

187+
@property
188+
def sign_legend(self):
189+
return 'Cyclonic' if self.sign_type == -1 else 'Anticyclonic'
190+
188191
@property
189192
def shape(self):
190193
return self.observations.shape
@@ -222,7 +225,7 @@ def elements(self):
222225

223226
if len(self.track_extra_variables):
224227
elements += self.track_extra_variables
225-
return elements
228+
return list(set(elements))
226229

227230
def coherence(self, other):
228231
"""Check coherence between two dataset
@@ -341,6 +344,8 @@ def index(self, index):
341344
@classmethod
342345
def load_from_netcdf(cls, filename):
343346
array_dim = 'NbSample'
347+
if not isinstance(filename, str):
348+
filename = filename.astype(str)
344349
with Dataset(filename) as h_nc:
345350
nb_obs = len(h_nc.dimensions['Nobs'])
346351
kwargs = dict()
@@ -386,6 +391,53 @@ def from_netcdf(cls, handler):
386391
eddies.obs[VAR_DESCR_inv[variable]] = handler.variables[variable][:]
387392
return eddies
388393

394+
@staticmethod
395+
def propagate(previous_obs, current_obs, obs_to_extend, dead_track, nb_next, model):
396+
"""
397+
Filled virtual obs (C)
398+
Args:
399+
previous_obs: previous obs from current (A)
400+
current_obs: previous obs from virtual (B)
401+
obs_to_extend:
402+
dead_track:
403+
nb_next:
404+
model:
405+
406+
Returns:
407+
New position C = B + AB
408+
"""
409+
next_obs = VirtualEddiesObservations(
410+
size=nb_next,
411+
track_extra_variables=model.track_extra_variables,
412+
track_array_variables=model.track_array_variables,
413+
array_variables=model.array_variables)
414+
nb_dead = len(previous_obs)
415+
nb_virtual_extend = nb_next - nb_dead
416+
417+
for key in model.elements:
418+
if key in ['lon', 'lat', 'time'] or 'contour_' in key:
419+
continue
420+
next_obs[key][:nb_dead] = current_obs[key]
421+
next_obs['dlon'][:nb_dead] = current_obs['lon'] - previous_obs['lon']
422+
next_obs['dlat'][:nb_dead] = current_obs['lat'] - previous_obs['lat']
423+
next_obs['lon'][:nb_dead] = current_obs['lon'] + next_obs['dlon'][:nb_dead]
424+
next_obs['lat'][:nb_dead] = current_obs['lat'] + next_obs['dlat'][:nb_dead]
425+
# Id which are extended
426+
next_obs['track'][:nb_dead] = dead_track
427+
# Add previous virtual
428+
if nb_virtual_extend > 0:
429+
for key in next_obs.elements:
430+
if key in ['lon', 'lat', 'time', 'track', 'segment_size'] or 'contour_' in key:
431+
continue
432+
next_obs[key][nb_dead:] = obs_to_extend[key]
433+
next_obs['lon'][nb_dead:] = obs_to_extend['lon'] + obs_to_extend['dlon']
434+
next_obs['lat'][nb_dead:] = obs_to_extend['lat'] + obs_to_extend['dlat']
435+
next_obs['track'][nb_dead:] = obs_to_extend['track']
436+
next_obs['segment_size'][nb_dead:] = obs_to_extend['segment_size']
437+
# Count
438+
next_obs['segment_size'][:] += 1
439+
return next_obs
440+
389441
@staticmethod
390442
def cost_function2(records_in, records_out, distance):
391443
nb_records = records_in.shape[0]
@@ -715,8 +767,7 @@ def write_netcdf(self, path='./', filename='%(path)s/%(sign_type)s.nc'):
715767
"""Write a netcdf with eddy obs
716768
"""
717769
eddy_size = len(self.observations)
718-
sign_type = 'Cyclonic' if self.sign_type == -1 else 'Anticyclonic'
719-
filename = filename % dict(path=path, sign_type=sign_type, prod_time=datetime.now().strftime('%Y%m%d'))
770+
filename = filename % dict(path=path, sign_type=self.sign_legend, prod_time=datetime.now().strftime('%Y%m%d'))
720771
logging.info('Store in %s', filename)
721772
with Dataset(filename, 'w', format='NETCDF4') as h_nc:
722773
logging.info('Create file %s', filename)
@@ -763,7 +814,7 @@ class VirtualEddiesObservations(EddiesObservations):
763814
def elements(self):
764815
elements = super(VirtualEddiesObservations, self).elements
765816
elements.extend(['track', 'segment_size', 'dlon', 'dlat'])
766-
return elements
817+
return list(set(elements))
767818

768819

769820
class TrackEddiesObservations(EddiesObservations):
@@ -783,8 +834,16 @@ def filled_by_interpolation(self, mask):
783834
var = field[0]
784835
if var in ['n', 'virtual', 'track'] or var in self.array_variables:
785836
continue
786-
self.obs[var][mask] = interp(index[mask], index[~mask],
787-
self.obs[var][~mask])
837+
# to normalize longitude before interpolation
838+
if var== 'lon':
839+
lon = self.obs[var]
840+
first = where(self.obs['n'] == 0)[0]
841+
nb_obs = empty(first.shape, dtype='u4')
842+
nb_obs[:-1] = first[1:] - first[:-1]
843+
nb_obs[-1] = lon.shape[0] - first[-1]
844+
lon0 = (lon[first] - 180).repeat(nb_obs)
845+
self.obs[var] = (lon - lon0) % 360 + lon0
846+
self.obs[var][mask] = interp(index[mask], index[~mask], self.obs[var][~mask])
788847

789848
def extract_longer_eddies(self, nb_min, nb_obs, compress_id=True):
790849
"""Select eddies which are longer than nb_min

src/py_eddy_tracker/tracking.py

Lines changed: 16 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,6 @@ def recense_dead_id_to_extend(self):
260260

261261
# Save previous state to count virtual obs
262262
self.previous_virtual_obs = self.virtual_obs
263-
# Creation of an virtual step for dead one
264-
self.virtual_obs = VirtualEddiesObservations(
265-
size=nb_dead + nb_virtual_extend,
266-
track_extra_variables=self.previous_obs.track_extra_variables,
267-
track_array_variables=self.previous_obs.track_array_variables,
268-
array_variables=self.previous_obs.array_variables)
269263

270264
# Find mask/index on previous correspondance to extrapolate
271265
# position
@@ -275,33 +269,13 @@ def recense_dead_id_to_extend(self):
275269
# Selection of observations on N-2 and N-1
276270
obs_a = self.previous2_obs.obs[self[-2][i_dead_id]['in']]
277271
obs_b = self.previous_obs.obs[self[-2][i_dead_id]['out']]
278-
# Position N-2 : A
279-
# Position N-1 : B
280-
# Virtual Position : C
281-
# New position C = B + AB
282-
for key in self.previous_obs.elements:
283-
if key in ['lon', 'lat', 'time'] or 'contour_' in key:
284-
continue
285-
self.virtual_obs[key][:nb_dead] = obs_b[key]
286-
self.virtual_obs['dlon'][:nb_dead] = obs_b['lon'] - obs_a['lon']
287-
self.virtual_obs['dlat'][:nb_dead] = obs_b['lat'] - obs_a['lat']
288-
self.virtual_obs['lon'][:nb_dead] = obs_b['lon'] + self.virtual_obs['dlon'][:nb_dead]
289-
self.virtual_obs['lat'][:nb_dead] = obs_b['lat'] + self.virtual_obs['dlat'][:nb_dead]
290-
# Id which are extended
291-
self.virtual_obs['track'][:nb_dead] = dead_id
292-
# Add previous virtual
293-
if nb_virtual_extend > 0:
294-
obs_to_extend = self.previous_virtual_obs.obs[i_virtual_dead_id][alive_virtual_obs]
295-
for key in self.virtual_obs.elements:
296-
if key in ['lon', 'lat', 'time', 'track', 'segment_size'] or 'contour_' in key:
297-
continue
298-
self.virtual_obs[key][nb_dead:] = obs_to_extend[key]
299-
self.virtual_obs['lon'][nb_dead:] = obs_to_extend['lon'] + obs_to_extend['dlon']
300-
self.virtual_obs['lat'][nb_dead:] = obs_to_extend['lat'] + obs_to_extend['dlat']
301-
self.virtual_obs['track'][nb_dead:] = obs_to_extend['track']
302-
self.virtual_obs['segment_size'][nb_dead:] = obs_to_extend['segment_size']
303-
# Count
304-
self.virtual_obs['segment_size'][:] += 1
272+
273+
self.virtual_obs = self.previous_obs.propagate(
274+
obs_a, obs_b,
275+
self.previous_virtual_obs.obs[i_virtual_dead_id][alive_virtual_obs] if nb_virtual_extend > 0 else None,
276+
dead_track=dead_id,
277+
nb_next=nb_dead + nb_virtual_extend,
278+
model=self.previous_obs)
305279

306280
def load_state(self):
307281
# If we have a previous file of correspondance, we will replay only recent part
@@ -616,20 +590,18 @@ def get_unused_data(self):
616590
has_virtual = 'virtual' in self[0].dtype.names
617591
for i, filename in enumerate(self.datasets):
618592
last_dataset = i == (nb_dataset - 1)
619-
first_dataset = i == 0
620-
if has_virtual:
621-
if not last_dataset:
622-
m_in = ~self[i]['virtual']
623-
if not first_dataset:
624-
m_out = ~self[i - 1]['virtual']
593+
if has_virtual and not last_dataset:
594+
m_in = ~self[i]['virtual']
625595
else:
626-
m_in, m_out = slice(None), slice(None)
596+
m_in = slice(None)
627597
if i == 0:
628-
eddies_used = self[i]['in'][m_in]
598+
eddies_used = self[i]['in']
629599
elif last_dataset:
630-
eddies_used = self[i - 1]['out'][m_out]
600+
eddies_used = self[i - 1]['out']
631601
else:
632-
eddies_used = unique(concatenate((self[i - 1]['out'][m_out], self[i]['in'][m_in])))
602+
eddies_used = unique(concatenate((self[i - 1]['out'], self[i]['in'][m_in])))
603+
if not isinstance(filename, str):
604+
filename = filename.astype(str)
633605
with Dataset(filename) as h:
634606
nb_obs_day = len(h.dimensions['Nobs'])
635607
m = ones(nb_obs_day, dtype='bool')
@@ -645,7 +617,7 @@ def get_unused_data(self):
645617
j = 0
646618
for i, dataset in enumerate(self.datasets):
647619
current_obs = self.class_method.load_from_netcdf(dataset)
648-
if i ==0:
620+
if i == 0:
649621
eddies.sign_type = current_obs.sign_type
650622
unused_obs = current_obs.observations[list_mask[i]]
651623
nb = unused_obs.shape[0]

src/scripts/EddyFinalTracking

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ from py_eddy_tracker.tracking import Correspondances
88
from os.path import exists
99
from os import mkdir
1010
import logging
11-
from numpy import unique
1211
import datetime as dt
1312

1413

@@ -24,9 +23,6 @@ def usage():
2423
parser.add_argument('--path_out',
2524
default='./',
2625
help='Path, where to write file')
27-
parser.add_argument('--filename_model',
28-
default='%(path)s/%(sign_type)s.nc',
29-
help='Model to create file')
3026
parser.add_argument('--nb_obs_min',
3127
type=int,
3228
default=28,
@@ -40,7 +36,8 @@ if __name__ == '__main__':
4036
# Create output directory
4137
if not exists(CONFIG.path_out):
4238
mkdir(CONFIG.path_out)
43-
39+
SAVE_DIR = CONFIG.path_out
40+
NB_OBS_MIN = CONFIG.nb_obs_min
4441
START_TIME = dt.datetime.now()
4542

4643
CORRESPONDANCES = Correspondances.load(CONFIG.nc_file)
@@ -51,18 +48,30 @@ if __name__ == '__main__':
5148

5249
logging.info('The longest tracks have %d observations', CORRESPONDANCES.nb_obs_by_tracks.max())
5350
logging.info('The mean length is %d observations before filtering', CORRESPONDANCES.nb_obs_by_tracks.mean())
54-
CORRESPONDANCES.longer_than(size_min=CONFIG.nb_obs_min)
51+
52+
CORRESPONDANCES.get_unused_data().write_netcdf(path=SAVE_DIR, filename='%(path)s/%(sign_type)s_untracked.nc')
53+
54+
SHORT_CORRESPONDANCES = CORRESPONDANCES._copy()
55+
SHORT_CORRESPONDANCES.shorter_than(size_max=NB_OBS_MIN)
56+
57+
CORRESPONDANCES.longer_than(size_min=NB_OBS_MIN)
58+
5559
FINAL_EDDIES = CORRESPONDANCES.merge()
60+
SHORT_TRACK = SHORT_CORRESPONDANCES.merge()
5661

5762
# We flag obs
5863
if CORRESPONDANCES.virtual:
5964
FINAL_EDDIES['virtual'][:] = FINAL_EDDIES['time'] == 0
6065
FINAL_EDDIES.filled_by_interpolation(FINAL_EDDIES['virtual'] == 1)
66+
SHORT_TRACK['virtual'][:] = SHORT_TRACK['time'] == 0
67+
SHORT_TRACK.filled_by_interpolation(SHORT_TRACK['virtual'] == 1)
6168

69+
# Total running time
6270
FULL_TIME = dt.datetime.now() - START_TIME
6371
logging.info('Duration : %s', FULL_TIME)
6472

65-
logging.info('%d tracks will be saved', len(unique(FINAL_EDDIES['track'])))
73+
logging.info('Longer track saved have %d obs', CORRESPONDANCES.nb_obs_by_tracks.max())
6674
logging.info('The mean length is %d observations after filtering', CORRESPONDANCES.nb_obs_by_tracks.mean())
6775

68-
FINAL_EDDIES.write_netcdf(path=CONFIG.path_out, filename=CONFIG.filename_model)
76+
FINAL_EDDIES.write_netcdf(path=SAVE_DIR)
77+
SHORT_TRACK.write_netcdf(filename='%(path)s/%(sign_type)s_track_too_short.nc', path=SAVE_DIR)

src/scripts/EddyIdentification

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ Email: [email protected]
2222
"""
2323

2424
import logging
25-
from glob import glob
2625
from yaml import load as yaml_load
2726
from datetime import datetime
2827
import datetime as dt
@@ -31,7 +30,7 @@ from matplotlib.dates import date2num, num2julian
3130
from matplotlib.figure import Figure
3231
from os.path import exists
3332
from os import mkdir
34-
from numpy import array, arange, atleast_1d, unique, round, int, interp, pi, \
33+
from numpy import arange, atleast_1d, round, int, interp, pi, \
3534
float_, errstate, ma
3635

3736
from py_eddy_tracker import EddyParser

0 commit comments

Comments
 (0)