22# -*- coding: utf-8 -*-
33"""
44"""
5+
56from py_eddy_tracker import EddyParser
67from glob import glob
78from yaml import load as yaml_load
8- from py_eddy_tracker .py_eddy_tracker_property_classes import EddiesObservations , TrackEddiesObservations
9+ from py_eddy_tracker .py_eddy_tracker_property_classes import EddiesObservations , TrackEddiesObservations , VirtualEddiesObservations
910#~ from py_eddy_tracker.make_eddy_tracker_list_obj import RossbyWaveSpeed
11+ from py_eddy_tracker .tools import distance_matrix
12+
1013import logging
1114import numpy as np
1215import datetime as dt
1316
1417D2R = 0.017453292519943295
15- def distance_matrix (lon0 , lat0 , lon1 , lat1 ):
18+ #~ def distance_matrix(lon0, lat0, lon1, lat1):
19+ def distance_matrix2 (lon0 , lat0 , lon1 , lat1 ):
1620 """Why it's quicker than cython version?
1721 """
1822 nb_elt0 = lon0 .shape [0 ]
@@ -26,6 +30,7 @@ def distance_matrix(lon0, lat0, lon1, lat1):
2630 a_val = sin_dlon ** 2 * cos_lat1 * cos_lat2 + sin_dlat ** 2
2731 return 6371.315 * 2 * np .arctan2 (a_val ** 0.5 , (1 - a_val ) ** 0.5 )
2832UINT32_MAX = 4294967295
33+ UINT16_MAX = 65535
2934
3035if __name__ == '__main__' :
3136 # Run using:
@@ -40,6 +45,7 @@ if __name__ == '__main__':
4045 CONFIG = yaml_load (stream )
4146
4247 NB_OBS_MIN = int (CONFIG ['TRACK_DURATION_MIN' ])
48+ NB_VIRTUAL_OBS_MAX_BY_SEGMENT = int (CONFIG ['VIRTUAL_LEGNTH_MAX' ])
4349
4450 PATTERN = CONFIG ['PATHS' ]['FILES_PATTERN' ]
4551 FILENAMES = glob (PATTERN )
@@ -53,105 +59,184 @@ if __name__ == '__main__':
5359 CURRENT_ID = 0
5460 CORRESPONDANCES = []
5561 START = True
62+ FLG_VIRTUAL = False
5663
5764 for file_name in FILENAMES [1 :]:
5865 logging .debug ('%s match with previous state' , file_name )
5966 e_current = EddiesObservations .load_from_netcdf (file_name )
6067 logging .debug ('%d obs to match' , len (e_current ))
61- dist_result = distance_matrix (
62- e_previous .obs ['lon' ], e_previous .obs ['lat' ],
63- e_current .obs ['lon' ], e_current .obs ['lat' ])
64-
65- #~ previous_amp, current_amp = np.meshgrid(
66- #~ e_current.obs['amplitude'], e_previous.obs['amplitude'])
67- #~ delta_amp = abs(current_amp - previous_amp) / previous_amp
68-
69- #~ previous_radius, current_radius = np.meshgrid(
70- #~ e_current.obs['radius_e'], e_previous.obs['radius_e'])
71- #~ delta_radius = abs(current_radius ** 2 - previous_radius ** 2) ** .5 / previous_radius
7268
73- i_previous , i_current = np .where (dist_result < 25 )
69+ if FLG_VIRTUAL :
70+ nb_real_obs = len (e_previous )
71+ # If you comment this the virtual fonctionnality will be disable
72+ e_previous = e_previous .merge (virtual_obs )
73+ dist_result = np .empty ((len (e_previous ), len (e_current )), dtype = 'f8' ) + np .nan
74+ distance_matrix (
75+ e_previous .obs ['lon' ], e_previous .obs ['lat' ],
76+ e_current .obs ['lon' ], e_current .obs ['lat' ],
77+ dist_result )
78+ i_previous , i_current = np .where (dist_result < 5. )
7479 nb_match = i_previous .shape [0 ]
7580
7681 logging .debug ('%d match with previous' , nb_match )
77- correspondance = np .array (i_previous , dtype = [('in' , 'u2' ), ('out' , 'u2' ), ('id' , 'u4' )])
82+ correspondance = np .array (i_previous , dtype = [('in' , 'u2' ), ('out' , 'u2' ), ('id' , 'u4' ), ( 'virtual' , np . bool_ ) ])
7883 correspondance ['out' ] = i_current
84+
85+ if FLG_VIRTUAL :
86+ correspondance ['virtual' ] = i_previous >= nb_real_obs
87+
88+ else :
89+ correspondance ['virtual' ] = False
90+
7991 if START :
8092 START = False
8193 # Set an id for each match
8294 correspondance ['id' ] = np .arange (nb_match )
8395 # Set counter
8496 CURRENT_ID += nb_match
8597 else :
98+ # We set all id to UINT32_MAX
8699 id_previous = np .ones (len (e_previous ), dtype = 'u4' ) * UINT32_MAX
87- id_previous [CORRESPONDANCES [- 1 ]['out' ]] = CORRESPONDANCES [- 1 ]['id' ]
100+ # We get old id for previously eddies tracked
101+ previous_id = CORRESPONDANCES [- 1 ]['id' ]
102+ id_previous [CORRESPONDANCES [- 1 ]['out' ]] = previous_id
88103 correspondance ['id' ] = id_previous [correspondance ['in' ]]
89- # new_id is -1 we must add a new ones
104+ if FLG_VIRTUAL :
105+ # Set id for virtual
106+ correspondance ['id' ][correspondance ['virtual' ]] = \
107+ virtual_obs .obs ['track' ][correspondance ['in' ][correspondance ['virtual' ]] - nb_real_obs ]
108+ #~ correspondance['in'][correspondance['virtual']] = UINT16_MAX
109+
110+ # SECTION for virtual observation
111+ # List previous id which are not use in the next step
112+ dead_id = np .setdiff1d (previous_id , correspondance ['id' ])
113+ # Creation of an virtual step for dead one
114+ virtual_obs = VirtualEddiesObservations (size = len (dead_id ))
115+ # Find mask/index on previous correspondance to extrapolate
116+ # position
117+ list_previous_id = previous_id .tolist ()
118+ i_dead_id = [list_previous_id .index (i ) for i in dead_id ]
119+
120+ # Selection of observations on N-2 and N-1
121+ obs_a = e_previous2 .obs [CORRESPONDANCES [- 1 ][i_dead_id ]['in' ]]
122+ obs_b = e_previous .obs [CORRESPONDANCES [- 1 ][i_dead_id ]['out' ]]
123+ # Position N-2 : A
124+ # Position N-1 : B
125+ # Virtual Position : C
126+ # New position C = B + AB
127+ virtual_obs .obs ['lon' ] = 2 * obs_b ['lon' ] - obs_a ['lon' ]
128+ virtual_obs .obs ['lat' ] = 2 * obs_b ['lat' ] - obs_a ['lat' ]
129+ # Id which are prolongated
130+ virtual_obs .obs ['track' ] = dead_id
131+ FLG_VIRTUAL = True
132+ # END
133+
134+
135+ # new_id is equal to UINT32_MAX we must add a new ones
90136 # we count the number of new
91137 mask_new_id = correspondance ['id' ] == UINT32_MAX
92138 nb_new_tracks = mask_new_id .sum ()
93139 # Set new id
94140 correspondance ['id' ][mask_new_id ] = np .arange (CURRENT_ID , CURRENT_ID + nb_new_tracks )
95141 # Set counter
96142 CURRENT_ID += nb_new_tracks
143+
97144 CORRESPONDANCES .append (correspondance )
98145
146+ e_previous2 = e_previous
99147 e_previous = e_current
148+
100149 logging .info ('Track finish' )
101150 logging .info ('Start merging' )
102151 # count obs by tracks
103152 nb_obs_by_tracks = np .zeros (CURRENT_ID , dtype = 'u2' ) + 1
104153 for correspondance in CORRESPONDANCES :
105154 nb_obs_by_tracks [correspondance ['id' ]] += 1
155+ nb_obs_by_tracks [correspondance ['id' ][correspondance ['virtual' ]]] += 1
106156
107157 i_current_by_tracks = nb_obs_by_tracks .cumsum () - nb_obs_by_tracks
108158 nb_obs = nb_obs_by_tracks .sum ()
109159 logging .info ('%d tracks will be create' , CURRENT_ID )
110160 logging .info ('%d observations will be join' , nb_obs )
111- # Start create netcdf to agglomerate all eddy
112-
161+ # Start create netcdf to agglomerate all eddy
113162 FINAL_EDDIES = TrackEddiesObservations (size = nb_obs )
114-
115- lon = np .ones (nb_obs , dtype = 'f4' ) * np .nan
116- lat = np .ones (nb_obs , dtype = 'f4' ) * np .nan
163+
117164 n = np .arange (nb_obs , dtype = 'u4' ) - i_current_by_tracks .repeat (nb_obs_by_tracks )
118165 FINAL_EDDIES .obs ['n' ] = np .uint16 (n )
166+ FINAL_EDDIES .obs ['track' ] = np .arange (CURRENT_ID ).repeat (nb_obs_by_tracks )
119167
120- # Start
121- eddies_init = EddiesObservations .load_from_netcdf (FILENAMES [0 ])
122- FINAL_EDDIES .sign_type = eddies_init .sign_type
123- i_id = CORRESPONDANCES [0 ]['id' ]
124- index_final = i_current_by_tracks [i_id ]
125- index_source = CORRESPONDANCES [0 ]['in' ]
126- # Copy all variable
127- for var , _ in eddies_init .obs .dtype .descr :
128- FINAL_EDDIES .obs [var ][index_final ] = eddies_init .obs [var ][index_source ]
168+ # Start loading identification again to save in the finals tracks
169+ # Load first file
170+ eddies_previous = EddiesObservations .load_from_netcdf (FILENAMES [0 ])
171+ # Set type of eddy with first file
172+ FINAL_EDDIES .sign_type = eddies_previous .sign_type
129173
130- del eddies_init
131- # Add increment for each index used
132- i_current_by_tracks [i_id ] += 1
174+ # To know if the track start
175+ first_obs_save_in_tracks = np .zeros (i_current_by_tracks .shape , dtype = np .bool_ )
133176
134177 for i , file_name in enumerate (FILENAMES [1 :]):
178+ # Load current file (we begin with second one)
135179 eddies_current = EddiesObservations .load_from_netcdf (file_name )
136-
180+ # We select the list of id which are involve in the correspondance
137181 i_id = CORRESPONDANCES [i ]['id' ]
182+ # Index where we will write in the final object
138183 index_final = i_current_by_tracks [i_id ]
139- index_source = CORRESPONDANCES [i ]['out' ]
184+
185+ # First obs of eddies
186+ m_first_obs = - first_obs_save_in_tracks [i_id ]
187+ if m_first_obs .any ():
188+ # Index in the current file
189+ index_in = CORRESPONDANCES [i ]['in' ][m_first_obs ]
190+ # Copy all variable
191+ for var , _ in eddies_current .obs .dtype .descr :
192+ FINAL_EDDIES .obs [var ][index_final [m_first_obs ]] = eddies_previous .obs [var ][index_in ]
193+ # Increment
194+ i_current_by_tracks [i_id [m_first_obs ]] += 1
195+ first_obs_save_in_tracks [i_id ] = True
196+ index_final = i_current_by_tracks [i_id ]
197+
198+ # Index in the current file
199+ index_current = CORRESPONDANCES [i ]['out' ]
200+ # If the flag virtual in correspondance is active, the previous is virtual
201+ m_virtual = CORRESPONDANCES [i ]['virtual' ]
202+ if m_virtual .any ():
203+ index_virtual = index_final [m_virtual ]
204+ FINAL_EDDIES .obs ['virtual' ][index_virtual ] = 1
205+ i_current_by_tracks [i_id [m_virtual ]] += 1
206+ index_final = i_current_by_tracks [i_id ]
207+
140208 # Copy all variable
141209 for var , _ in eddies_current .obs .dtype .descr :
142- FINAL_EDDIES .obs [var ][index_final ] = eddies_current .obs [var ][index_source ]
210+ FINAL_EDDIES .obs [var ][index_final ] = eddies_current .obs [var ][index_current ]
211+
212+
143213 # Add increment for each index used
144214 i_current_by_tracks [i_id ] += 1
145-
146-
215+ eddies_previous = eddies_current
216+
217+ i = np .where (FINAL_EDDIES .obs ['virtual' ])[0 ]
218+ nb_virtual = FINAL_EDDIES .obs ['virtual' ].sum ()
219+ logging .info ('%d obs are virtual (unobserved)' , nb_virtual )
220+ FINAL_EDDIES .obs ['lon' ][i ] = (FINAL_EDDIES .obs ['lon' ][i - 1 ] + FINAL_EDDIES .obs ['lon' ][i + 1 ]) / 2
221+ FINAL_EDDIES .obs ['lat' ][i ] = (FINAL_EDDIES .obs ['lat' ][i - 1 ] + FINAL_EDDIES .obs ['lat' ][i + 1 ]) / 2
222+ FINAL_EDDIES .obs ['time' ][i ] = (FINAL_EDDIES .obs ['time' ][i - 1 ] + FINAL_EDDIES .obs ['time' ][i + 1 ]) / 2
223+
224+
147225 # Total running time
148226 logging .info ('Mean duration by loop : %s' ,
149227 (dt .datetime .now () - START_TIME ) / (len (FILENAMES ) - 1 ))
150228 logging .info ('Duration : %s' , dt .datetime .now () - START_TIME )
151229
152230 logging .info ('The longest tracks have %d observations' , nb_obs_by_tracks .max ())
153231
154- FINAL_EDDIES .write_netcdf ()
155-
156- #~ nb_obs_by_tracks = nb_obs_by_tracks.repeat(nb_obs_by_tracks)
157- #~ m = nb_obs_by_tracks > NB_OBS_MIN
232+ FINAL_EDDIES .extract_longer_eddies (NB_OBS_MIN , nb_obs_by_tracks .repeat (nb_obs_by_tracks )).write_netcdf ()
233+
234+
235+
236+ #~ previous_amp, current_amp = np.meshgrid(
237+ #~ e_current.obs['amplitude'], e_previous.obs['amplitude'])
238+ #~ delta_amp = abs(current_amp - previous_amp) / previous_amp
239+
240+ #~ previous_radius, current_radius = np.meshgrid(
241+ #~ e_current.obs['radius_e'], e_previous.obs['radius_e'])
242+ #~ delta_radius = abs(current_radius ** 2 - previous_radius ** 2) ** .5 / previous_radius
0 commit comments