2929
3030"""
3131from numpy import zeros , empty , nan , arange , interp , where , unique , \
32- ma , concatenate , cos , radians , isnan
32+ ma , concatenate , cos , radians , isnan , ones , ndarray
3333from netCDF4 import Dataset
3434from py_eddy_tracker .tools import distance_matrix , distance_vector
3535from shapely .geometry import Polygon
@@ -247,20 +247,21 @@ def load_from_netcdf(filename):
247247 eddies .sign_type = h_nc .variables ['cyc' ][0 ]
248248 return eddies
249249
250- def cost_function2 (self , records_in , records_out , distance ):
250+ @staticmethod
251+ def cost_function2 (records_in , records_out , distance ):
251252 nb_records = records_in .shape [0 ]
252253 costs = ma .empty (nb_records ,dtype = 'f4' )
253254 for i_record in xrange (nb_records ):
254255 poly_in = Polygon (
255256 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 ' ],))
258259 ).T
259260 )
260261 poly_out = Polygon (
261262 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 ' ],))
264265 ).T
265266 )
266267 try :
@@ -269,8 +270,36 @@ def cost_function2(self, records_in, records_out, distance):
269270 costs [i_record ] = 1
270271 costs .mask = costs == 1
271272 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 )
272284
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()
274303 cost = ((records_in ['amplitude' ] - records_out ['amplitude' ]
275304 ) / records_in ['amplitude' ]
276305 ) ** 2
@@ -280,26 +309,30 @@ def cost_function(self, records_in, records_out, distance):
280309 cost += (distance / 125 ) ** 2
281310 cost **= 0.5
282311 # Mask value superior at 60 % of variation
283- # return ma.array(cost, mask=cost > 0.6 )
312+ return ma .array (cost , mask = m )
284313 return cost
285314
286315 def circle_mask (self , other , radius = 100 ):
287316 """Return a mask of available link"""
288317 return self .distance (other ) < radius
289318
290319 def fixed_ellipsoid_mask (self , other , minor = 50 , major = 100 , only_east = False ):
291- dist = self .distance (other )
320+ dist = self .distance (other ). T
292321 accepted = dist < minor
293322 rejected = dist > major
294323 rejected += isnan (dist )
295324
296325 # All obs we are not in rejected and accepted, there are between
297326 # two circle
298327 needs_investigation = - (rejected + accepted )
299- index_self , index_other = where (needs_investigation )
328+ index_other , index_self = where (needs_investigation )
300329
301330 nb_case = index_self .shape [0 ]
302331 if nb_case != 0 :
332+ if isinstance (major , ndarray ):
333+ major = major [index_self ]
334+ if isinstance (minor , ndarray ):
335+ minor = minor [index_self ]
303336 c_degree = ((major ** 2 - minor ** 2 ) ** .5 ) / (111.3 * cos (radians (self .obs ['lat' ][index_self ])))
304337
305338 lon_self = self .obs ['lon' ][index_self ]
@@ -318,20 +351,40 @@ def fixed_ellipsoid_mask(self, other, minor=50, major=100, only_east=False):
318351 dist_right_f )
319352 dist_2a = (dist_left_f + dist_right_f ) / 1000
320353
321- accepted [index_self , index_other ] = dist_2a < (2 * major )
354+ accepted [index_other , index_self ] = dist_2a < (2 * major )
322355 if only_east :
323356 d_lon = (other .obs ['lon' ][index_other ] - lon_self + 180 ) % 360 - 180
324357 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
327378
328379 def tracking (self , other ):
329380 """Track obs between self and other
330381 """
331382 dist = self .distance (other )
332383 # 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+
335388 indexs_closest = where (mask_accept_dist )
336389
337390 cost_values = self .cost_function (
@@ -368,19 +421,22 @@ def tracking(self, other):
368421 # Cost to resolve conflict
369422 cost_reduce = cost [i_self_keep ][:, i_other_keep ]
370423 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 )
372426
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 ] )
377431
378432 links_resolve = 0
379433 # Arbitrary value
380434 max_iteration = max (cost_reduce .shape )
381435 security_increment = 0
382436 while False in cost_reduce .mask :
383437 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)
384440 raise Exception ('To many iteration: %d' % security_increment )
385441 security_increment += 1
386442 i_min_value = cost_reduce .argmin ()
@@ -452,6 +508,7 @@ def extract_longer_eddies(self, nb_min, nb_obs, compress_id=True):
452508 )
453509 eddies .sign_type = self .sign_type
454510 for field in self .obs .dtype .descr :
511+ logging .debug ('Copy of field %s ...' , field )
455512 var = field [0 ]
456513 eddies .obs [var ] = self .obs [var ][mask ]
457514 if compress_id :
@@ -490,12 +547,12 @@ def create_variable(handler_nc, kwargs_variable, attr_variable,
490547 except ValueError :
491548 logging .warn ('Data is empty' )
492549
493- def write_netcdf (self , path = './' ):
550+ def write_netcdf (self , path = './' , filename = '%(path)s/%(sign_type)s.nc' ):
494551 """Write a netcdf with eddy obs
495552 """
496553 eddy_size = len (self .observations )
497554 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 )
499556 with Dataset (filename , 'w' , format = 'NETCDF4' ) as h_nc :
500557 logging .info ('Create file %s' , filename )
501558 # Create dimensions
0 commit comments