29
29
30
30
"""
31
31
from numpy import zeros , empty , nan , arange , interp , where , unique , \
32
- ma , concatenate , cos , radians , isnan
32
+ ma , concatenate , cos , radians , isnan , ones , ndarray
33
33
from netCDF4 import Dataset
34
34
from py_eddy_tracker .tools import distance_matrix , distance_vector
35
35
from shapely .geometry import Polygon
@@ -247,20 +247,21 @@ def load_from_netcdf(filename):
247
247
eddies .sign_type = h_nc .variables ['cyc' ][0 ]
248
248
return eddies
249
249
250
- def cost_function2 (self , records_in , records_out , distance ):
250
+ @staticmethod
251
+ def cost_function2 (records_in , records_out , distance ):
251
252
nb_records = records_in .shape [0 ]
252
253
costs = ma .empty (nb_records ,dtype = 'f4' )
253
254
for i_record in xrange (nb_records ):
254
255
poly_in = Polygon (
255
256
concatenate ((
256
- (records_in [i_record ]['contour_lon ' ],),
257
- (records_in [i_record ]['contour_lat ' ],))
257
+ (records_in [i_record ]['contour_lon_e ' ],),
258
+ (records_in [i_record ]['contour_lat_e ' ],))
258
259
).T
259
260
)
260
261
poly_out = Polygon (
261
262
concatenate ((
262
- (records_out [i_record ]['contour_lon ' ],),
263
- (records_out [i_record ]['contour_lat ' ],))
263
+ (records_out [i_record ]['contour_lon_e ' ],),
264
+ (records_out [i_record ]['contour_lat_e ' ],))
264
265
).T
265
266
)
266
267
try :
@@ -269,8 +270,36 @@ def cost_function2(self, records_in, records_out, distance):
269
270
costs [i_record ] = 1
270
271
costs .mask = costs == 1
271
272
return costs
273
+
274
+ @staticmethod
275
+ def across_ground (record0 , record1 , distance ):
276
+ from netCDF4 import Dataset
277
+ from scipy .interpolate import RectBivariateSpline
278
+ opts_interpolation = {'kx' : 1 , 'ky' : 1 , 's' : 0 }
279
+
280
+ mask = empty (record0 .shape , dtype = 'bool' )
281
+
282
+ with Dataset ('/data/adelepoulle/Test/Test_eddy/20161102_mask_from_bathy/mask.nc' ) as h :
283
+ function = RectBivariateSpline (h .variables ['x' ][:], h .variables ['y' ][:], h .variables ['coast_dist' ][:], ** opts_interpolation )
272
284
273
- def cost_function (self , records_in , records_out , distance ):
285
+ for i , record_in in enumerate (record0 ):
286
+ if distance [i ] < 30 :
287
+ mask [i ] = False
288
+ continue
289
+ nb_point = int (distance [i ] / 10 )
290
+ dx , dy = (record1 [i ]['lon' ] - record_in ['lon' ]) / nb_point , (record1 [i ]['lat' ] - record_in ['lat' ]) / nb_point
291
+ nb_point += 1
292
+ lon_traj , lat_traj = arange (nb_point ) * dx + record_in ['lon' ], arange (nb_point ) * dy + record_in ['lat' ]
293
+
294
+ mask [i ] = (function .ev (lon_traj , lat_traj ) < (record_in ['radius_e' ])).any ()
295
+ return mask
296
+
297
+ @staticmethod
298
+ def cost_function (records_in , records_out , distance ):
299
+ m = EddiesObservations .across_ground (records_in , records_out , distance )
300
+ # print m.sum()
301
+ # print m.shape
302
+ # exit()
274
303
cost = ((records_in ['amplitude' ] - records_out ['amplitude' ]
275
304
) / records_in ['amplitude' ]
276
305
) ** 2
@@ -280,26 +309,30 @@ def cost_function(self, records_in, records_out, distance):
280
309
cost += (distance / 125 ) ** 2
281
310
cost **= 0.5
282
311
# Mask value superior at 60 % of variation
283
- # return ma.array(cost, mask=cost > 0.6 )
312
+ return ma .array (cost , mask = m )
284
313
return cost
285
314
286
315
def circle_mask (self , other , radius = 100 ):
287
316
"""Return a mask of available link"""
288
317
return self .distance (other ) < radius
289
318
290
319
def fixed_ellipsoid_mask (self , other , minor = 50 , major = 100 , only_east = False ):
291
- dist = self .distance (other )
320
+ dist = self .distance (other ). T
292
321
accepted = dist < minor
293
322
rejected = dist > major
294
323
rejected += isnan (dist )
295
324
296
325
# All obs we are not in rejected and accepted, there are between
297
326
# two circle
298
327
needs_investigation = - (rejected + accepted )
299
- index_self , index_other = where (needs_investigation )
328
+ index_other , index_self = where (needs_investigation )
300
329
301
330
nb_case = index_self .shape [0 ]
302
331
if nb_case != 0 :
332
+ if isinstance (major , ndarray ):
333
+ major = major [index_self ]
334
+ if isinstance (minor , ndarray ):
335
+ minor = minor [index_self ]
303
336
c_degree = ((major ** 2 - minor ** 2 ) ** .5 ) / (111.3 * cos (radians (self .obs ['lat' ][index_self ])))
304
337
305
338
lon_self = self .obs ['lon' ][index_self ]
@@ -318,20 +351,40 @@ def fixed_ellipsoid_mask(self, other, minor=50, major=100, only_east=False):
318
351
dist_right_f )
319
352
dist_2a = (dist_left_f + dist_right_f ) / 1000
320
353
321
- accepted [index_self , index_other ] = dist_2a < (2 * major )
354
+ accepted [index_other , index_self ] = dist_2a < (2 * major )
322
355
if only_east :
323
356
d_lon = (other .obs ['lon' ][index_other ] - lon_self + 180 ) % 360 - 180
324
357
mask = d_lon < 0
325
- accepted [index_self [mask ], index_other [mask ]] = False
326
- return accepted
358
+ accepted [index_other [mask ], index_self [mask ]] = False
359
+ return accepted .T
360
+
361
+ @staticmethod
362
+ def basic_formula_ellips_major_axis (lats , cmin = 1.5 , cmax = 10. , c0 = 1.5 , lat1 = 13.5 , lat2 = 5. ):
363
+ """Give major axis in km with a given latitude
364
+ """
365
+ # Straight line between lat1 and lat2:
366
+ # y = a * x + b
367
+ a = (cmin - cmax ) / (lat1 - lat2 )
368
+ b = a * - lat1 + cmin
369
+
370
+ abs_lats = abs (lats )
371
+ major_axis = ones (lats .shape , dtype = 'f8' ) * cmin
372
+ major_axis [abs_lats < lat2 ] = cmax
373
+ m = abs_lats > lat1
374
+ m += abs_lats < lat2
375
+
376
+ major_axis [- m ] = a * abs_lats [- m ] + b
377
+ return major_axis * 111.2
327
378
328
379
def tracking (self , other ):
329
380
"""Track obs between self and other
330
381
"""
331
382
dist = self .distance (other )
332
383
# Links available which are close (circle area selection)
333
- mask_accept_dist = self .circle_mask (other , radius = 125 )
334
- # mask_accept_dist = self.fixed_ellipsoid_mask(other)
384
+ # mask_accept_dist = self.circle_mask(other, radius=125)
385
+ y = self .basic_formula_ellips_major_axis (self .obs ['lat' ])
386
+ mask_accept_dist = self .fixed_ellipsoid_mask (other , minor = 1.5 * 111.2 , major = y , only_east = True )
387
+
335
388
indexs_closest = where (mask_accept_dist )
336
389
337
390
cost_values = self .cost_function (
@@ -368,19 +421,22 @@ def tracking(self, other):
368
421
# Cost to resolve conflict
369
422
cost_reduce = cost [i_self_keep ][:, i_other_keep ]
370
423
shape = cost_reduce .shape
371
- logging .debug ('Shape conflict matrix : %s' , shape )
424
+ nb_conflict = (- cost_reduce .mask ).sum ()
425
+ logging .debug ('Shape conflict matrix : %s, %d conflicts' , shape , nb_conflict )
372
426
373
- matrix_size = shape [ 0 ] * shape [ 1 ]
374
- if ( matrix_size ) >= 20000 :
375
- logging .warning ('High number of conflict : %d (matrix_size )' ,
376
- matrix_size )
427
+
428
+ if nb_conflict >= ( shape [ 0 ] + shape [ 1 ]) :
429
+ logging .warning ('High number of conflict : %d (nb_conflict )' ,
430
+ shape [ 0 ] + shape [ 1 ] )
377
431
378
432
links_resolve = 0
379
433
# Arbitrary value
380
434
max_iteration = max (cost_reduce .shape )
381
435
security_increment = 0
382
436
while False in cost_reduce .mask :
383
437
if security_increment > max_iteration :
438
+ # Maybe check if the size decrease if not rise an exception
439
+ # x_i, y_i = where(-cost_reduce.mask)
384
440
raise Exception ('To many iteration: %d' % security_increment )
385
441
security_increment += 1
386
442
i_min_value = cost_reduce .argmin ()
@@ -452,6 +508,7 @@ def extract_longer_eddies(self, nb_min, nb_obs, compress_id=True):
452
508
)
453
509
eddies .sign_type = self .sign_type
454
510
for field in self .obs .dtype .descr :
511
+ logging .debug ('Copy of field %s ...' , field )
455
512
var = field [0 ]
456
513
eddies .obs [var ] = self .obs [var ][mask ]
457
514
if compress_id :
@@ -490,12 +547,12 @@ def create_variable(handler_nc, kwargs_variable, attr_variable,
490
547
except ValueError :
491
548
logging .warn ('Data is empty' )
492
549
493
- def write_netcdf (self , path = './' ):
550
+ def write_netcdf (self , path = './' , filename = '%(path)s/%(sign_type)s.nc' ):
494
551
"""Write a netcdf with eddy obs
495
552
"""
496
553
eddy_size = len (self .observations )
497
554
sign_type = 'Cyclonic' if self .sign_type == - 1 else 'Anticyclonic'
498
- filename = '%s/%s.nc' % (path , sign_type )
555
+ filename = filename % dict (path = path , sign_type = sign_type )
499
556
with Dataset (filename , 'w' , format = 'NETCDF4' ) as h_nc :
500
557
logging .info ('Create file %s' , filename )
501
558
# Create dimensions
0 commit comments