27
27
===========================================================================
28
28
29
29
"""
30
+ from matplotlib .dates import julian2num , num2date
31
+
30
32
from py_eddy_tracker .observations import EddiesObservations , \
31
33
VirtualEddiesObservations , TrackEddiesObservations
32
34
from numpy import bool_ , array , arange , ones , setdiff1d , zeros , uint16 , \
@@ -47,7 +49,7 @@ class Correspondances(list):
47
49
# Track limit to 65535
48
50
N_DTYPE = 'u2'
49
51
50
- def __init__ (self , datasets , virtual = 0 , class_method = None ):
52
+ def __init__ (self , datasets , virtual = 0 , class_method = None , previous_correspondance = None ):
51
53
"""Initiate tracking
52
54
"""
53
55
super (Correspondances , self ).__init__ ()
@@ -59,6 +61,7 @@ def __init__(self, datasets, virtual=0, class_method=None):
59
61
self .class_method = EddiesObservations
60
62
else :
61
63
self .class_method = class_method
64
+
62
65
# To count ID
63
66
self .current_id = 0
64
67
# To know the number maximal of link between two state
@@ -76,10 +79,17 @@ def __init__(self, datasets, virtual=0, class_method=None):
76
79
self .virtual = virtual > 0
77
80
self .virtual_obs = None
78
81
self .previous_virtual_obs = None
82
+
83
+ # Correspondance to prolongate
84
+ self .filename_previous_correspondance = previous_correspondance
85
+ self .previous_correspondance = self .load_compatible (self .filename_previous_correspondance )
86
+
79
87
if self .virtual :
80
88
# Add field to dtype to follow virtual observations
81
89
self .correspondance_dtype += [
90
+ # True if it isn't a real obs
82
91
('virtual' , bool_ ),
92
+ # Length of virtual segment
83
93
('virtual_length' , self .VIRTUAL_DTYPE )]
84
94
85
95
# Array to simply merged
@@ -93,6 +103,17 @@ def reset_dataset_cache(self):
93
103
self .previous_obs = None
94
104
self .current_obs = None
95
105
106
+ @property
107
+ def period (self ):
108
+ """To rethink
109
+
110
+ Returns: period coverage by obs
111
+
112
+ """
113
+ date_start = num2date (julian2num (self .class_method .load_from_netcdf (self .datasets [0 ]).obs ['time' ][0 ] - 0.5 ))
114
+ date_stop = num2date (julian2num (self .class_method .load_from_netcdf (self .datasets [- 1 ]).obs ['time' ][0 ] - 0.5 ))
115
+ return date_start , date_stop
116
+
96
117
def swap_dataset (self , dataset ):
97
118
""" Swap to next dataset
98
119
"""
@@ -138,6 +159,8 @@ def store_correspondance(self, i_previous, i_current, nb_real_obs):
138
159
"""
139
160
# Create array to store correspondance data
140
161
correspondance = array (i_previous , dtype = self .correspondance_dtype )
162
+ if self .virtual :
163
+ correspondance ['virtual_length' ][:] = 255
141
164
# index from current_obs
142
165
correspondance ['out' ] = i_current
143
166
@@ -279,14 +302,33 @@ def recense_dead_id_to_extend(self):
279
302
# Count
280
303
self .virtual_obs ['segment_size' ][:] += 1
281
304
305
+ def load_state (self ):
306
+ # If we have a previous file of correspondance, we will replay only recent part
307
+ if self .previous_correspondance is not None :
308
+ first_dataset = len (self .previous_correspondance .datasets )
309
+ for correspondance in self .previous_correspondance [:first_dataset ]:
310
+ self .append (correspondance )
311
+ self .current_obs = self .class_method .load_from_netcdf (self .datasets [first_dataset - 2 ])
312
+ flg_virtual = self .previous_correspondance .virtual
313
+ with Dataset (self .filename_previous_correspondance ) as general_handler :
314
+ self .current_id = general_handler .last_current_id
315
+ # Load last virtual obs
316
+ self .virtual_obs = VirtualEddiesObservations .from_netcdf (general_handler .groups ['LastVirtualObs' ])
317
+ # Load and last previous virtual obs to be merge with current => will be previous2_obs
318
+ self .current_obs = self .current_obs .merge (
319
+ VirtualEddiesObservations .from_netcdf (general_handler .groups ['LastPreviousVirtualObs' ]))
320
+ return first_dataset , flg_virtual
321
+ return 1 , False
322
+
282
323
def track (self ):
283
324
"""Run tracking
284
325
"""
285
- flg_virtual = False
286
326
self .reset_dataset_cache ()
287
- self .swap_dataset (self .datasets [0 ])
327
+ first_dataset , flg_virtual = self .load_state ()
328
+
329
+ self .swap_dataset (self .datasets [first_dataset - 1 ])
288
330
# We begin with second file, first one is in previous
289
- for i , file_name in enumerate ( self .datasets [1 :]) :
331
+ for file_name in self .datasets [first_dataset :] :
290
332
self .swap_dataset (file_name )
291
333
logging .debug ('%s match with previous state' , file_name )
292
334
logging .debug ('%d obs to match' , len (self .current_obs ))
@@ -295,13 +337,11 @@ def track(self):
295
337
if flg_virtual :
296
338
logging .debug ('%d virtual obs will be add to previous' ,
297
339
len (self .virtual_obs ))
298
- # If you comment this the virtual fonctionnality will be
299
- # disable
300
340
self .previous_obs = self .previous_obs .merge (self .virtual_obs )
301
-
302
341
i_previous , i_current = self .previous_obs .tracking (
303
342
self .current_obs )
304
343
344
+ # return true if the first time (previous2obs is none)
305
345
if self .store_correspondance (i_previous , i_current , nb_real_obs ):
306
346
continue
307
347
@@ -310,9 +350,11 @@ def track(self):
310
350
if self .virtual :
311
351
flg_virtual = True
312
352
313
- def save (self , filename ):
353
+ def save (self , filename , dict_completion = None ):
314
354
self .prepare_merging ()
315
355
nb_step = len (self .datasets ) - 1
356
+ if isinstance (dict_completion , dict ):
357
+ filename = filename .format (** dict_completion )
316
358
logging .info ('Create correspondance file %s' , filename )
317
359
with Dataset (filename , 'w' , format = 'NETCDF4' ) as h_nc :
318
360
# Create dimensions
@@ -337,19 +379,46 @@ def save(self, filename):
337
379
338
380
for name , dtype in self .correspondance_dtype :
339
381
if dtype is bool_ :
340
- dtype = 'byte'
382
+ dtype = 'u1'
383
+ kwargs_cv = dict ()
384
+ if 'u1' in dtype :
385
+ kwargs_cv ['fill_value' ] = 255 ,
341
386
h_nc .createVariable (zlib = True ,
342
387
complevel = 1 ,
343
388
varname = name ,
344
389
datatype = dtype ,
345
- dimensions = ('Nstep' , 'Nlink' ))
390
+ dimensions = ('Nstep' , 'Nlink' ),
391
+ ** kwargs_cv
392
+ )
346
393
347
394
for i , correspondance in enumerate (self ):
348
395
nb_elt = correspondance .shape [0 ]
349
396
var_nb_link [i ] = nb_elt
350
397
for name , _ in self .correspondance_dtype :
351
398
h_nc .variables [name ][i , :nb_elt ] = correspondance [name ]
352
- h_nc .virtual = int (self .virtual )
399
+ h_nc .virtual_use = str (self .virtual )
400
+ h_nc .virtual_max_segment = self .nb_virtual
401
+ h_nc .last_current_id = self .current_id
402
+ if self .virtual_obs is not None :
403
+ group = h_nc .createGroup ('LastVirtualObs' )
404
+ self .virtual_obs .to_netcdf (group )
405
+ group = h_nc .createGroup ('LastPreviousVirtualObs' )
406
+ self .previous_virtual_obs .to_netcdf (group )
407
+ h_nc .module = self .class_method .__module__
408
+ h_nc .classname = self .class_method .__qualname__
409
+
410
+ def load_compatible (self , filename ):
411
+ if filename is None :
412
+ return None
413
+ previous_correspondance = Correspondances .load (filename )
414
+ if self .nb_virtual != previous_correspondance .nb_virtual :
415
+ raise Exception ('File of correspondance IN contains a different virtual segment size : file(%d), yaml(%d)' %
416
+ (previous_correspondance .nb_virtual , self .nb_virtual ))
417
+
418
+ if self .class_method != previous_correspondance .class_method :
419
+ raise Exception ('File of correspondance IN contains a different class method: file(%s), yaml(%s)' %
420
+ (previous_correspondance .class_method , self .class_method ))
421
+ return previous_correspondance
353
422
354
423
@classmethod
355
424
def load (cls , filename ):
@@ -358,7 +427,11 @@ def load(cls, filename):
358
427
datasets = list (h_nc .variables ['FileIn' ][:])
359
428
datasets .append (h_nc .variables ['FileOut' ][- 1 ])
360
429
361
- obj = cls (datasets , h_nc .virtual )
430
+ if hasattr (h_nc , 'module' ):
431
+ class_method = getattr (__import__ (h_nc .module , globals (), locals (), h_nc .classname ), h_nc .classname )
432
+ else :
433
+ class_method = None
434
+ obj = cls (datasets , h_nc .virtual_max_segment , class_method = class_method )
362
435
363
436
id_max = 0
364
437
for i , nb_elt in enumerate (h_nc .variables ['nb_link' ][:]):
@@ -371,7 +444,8 @@ def load(cls, filename):
371
444
for name , _ in obj .correspondance_dtype :
372
445
if name == 'in' :
373
446
continue
374
- correspondance [name ] = h_nc .variables [name ][i , :nb_elt ]
447
+ if name == 'virtual_length' :
448
+ correspondance [name ] = 255
375
449
correspondance [name ] = h_nc .variables [name ][i , :nb_elt ]
376
450
id_max = max (id_max , correspondance ['id' ].max ())
377
451
obj .append (correspondance )
0 commit comments