2
2
# -*- coding: utf-8 -*-
3
3
"""
4
4
"""
5
+
5
6
from py_eddy_tracker import EddyParser
6
7
from glob import glob
7
8
from 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
9
10
#~ from py_eddy_tracker.make_eddy_tracker_list_obj import RossbyWaveSpeed
11
+ from py_eddy_tracker .tools import distance_matrix
12
+
10
13
import logging
11
14
import numpy as np
12
15
import datetime as dt
13
16
14
17
D2R = 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 ):
16
20
"""Why it's quicker than cython version?
17
21
"""
18
22
nb_elt0 = lon0 .shape [0 ]
@@ -26,6 +30,7 @@ def distance_matrix(lon0, lat0, lon1, lat1):
26
30
a_val = sin_dlon ** 2 * cos_lat1 * cos_lat2 + sin_dlat ** 2
27
31
return 6371.315 * 2 * np .arctan2 (a_val ** 0.5 , (1 - a_val ) ** 0.5 )
28
32
UINT32_MAX = 4294967295
33
+ UINT16_MAX = 65535
29
34
30
35
if __name__ == '__main__' :
31
36
# Run using:
@@ -40,6 +45,7 @@ if __name__ == '__main__':
40
45
CONFIG = yaml_load (stream )
41
46
42
47
NB_OBS_MIN = int (CONFIG ['TRACK_DURATION_MIN' ])
48
+ NB_VIRTUAL_OBS_MAX_BY_SEGMENT = int (CONFIG ['VIRTUAL_LEGNTH_MAX' ])
43
49
44
50
PATTERN = CONFIG ['PATHS' ]['FILES_PATTERN' ]
45
51
FILENAMES = glob (PATTERN )
@@ -53,105 +59,184 @@ if __name__ == '__main__':
53
59
CURRENT_ID = 0
54
60
CORRESPONDANCES = []
55
61
START = True
62
+ FLG_VIRTUAL = False
56
63
57
64
for file_name in FILENAMES [1 :]:
58
65
logging .debug ('%s match with previous state' , file_name )
59
66
e_current = EddiesObservations .load_from_netcdf (file_name )
60
67
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
72
68
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. )
74
79
nb_match = i_previous .shape [0 ]
75
80
76
81
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_ ) ])
78
83
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
+
79
91
if START :
80
92
START = False
81
93
# Set an id for each match
82
94
correspondance ['id' ] = np .arange (nb_match )
83
95
# Set counter
84
96
CURRENT_ID += nb_match
85
97
else :
98
+ # We set all id to UINT32_MAX
86
99
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
88
103
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
90
136
# we count the number of new
91
137
mask_new_id = correspondance ['id' ] == UINT32_MAX
92
138
nb_new_tracks = mask_new_id .sum ()
93
139
# Set new id
94
140
correspondance ['id' ][mask_new_id ] = np .arange (CURRENT_ID , CURRENT_ID + nb_new_tracks )
95
141
# Set counter
96
142
CURRENT_ID += nb_new_tracks
143
+
97
144
CORRESPONDANCES .append (correspondance )
98
145
146
+ e_previous2 = e_previous
99
147
e_previous = e_current
148
+
100
149
logging .info ('Track finish' )
101
150
logging .info ('Start merging' )
102
151
# count obs by tracks
103
152
nb_obs_by_tracks = np .zeros (CURRENT_ID , dtype = 'u2' ) + 1
104
153
for correspondance in CORRESPONDANCES :
105
154
nb_obs_by_tracks [correspondance ['id' ]] += 1
155
+ nb_obs_by_tracks [correspondance ['id' ][correspondance ['virtual' ]]] += 1
106
156
107
157
i_current_by_tracks = nb_obs_by_tracks .cumsum () - nb_obs_by_tracks
108
158
nb_obs = nb_obs_by_tracks .sum ()
109
159
logging .info ('%d tracks will be create' , CURRENT_ID )
110
160
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
113
162
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
+
117
164
n = np .arange (nb_obs , dtype = 'u4' ) - i_current_by_tracks .repeat (nb_obs_by_tracks )
118
165
FINAL_EDDIES .obs ['n' ] = np .uint16 (n )
166
+ FINAL_EDDIES .obs ['track' ] = np .arange (CURRENT_ID ).repeat (nb_obs_by_tracks )
119
167
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
129
173
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_ )
133
176
134
177
for i , file_name in enumerate (FILENAMES [1 :]):
178
+ # Load current file (we begin with second one)
135
179
eddies_current = EddiesObservations .load_from_netcdf (file_name )
136
-
180
+ # We select the list of id which are involve in the correspondance
137
181
i_id = CORRESPONDANCES [i ]['id' ]
182
+ # Index where we will write in the final object
138
183
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
+
140
208
# Copy all variable
141
209
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
+
143
213
# Add increment for each index used
144
214
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
+
147
225
# Total running time
148
226
logging .info ('Mean duration by loop : %s' ,
149
227
(dt .datetime .now () - START_TIME ) / (len (FILENAMES ) - 1 ))
150
228
logging .info ('Duration : %s' , dt .datetime .now () - START_TIME )
151
229
152
230
logging .info ('The longest tracks have %d observations' , nb_obs_by_tracks .max ())
153
231
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