6
6
sin , deg2rad , pi , ones , cos , ma , int8 , histogram2d , arange , float_ , \
7
7
linspace , errstate , int_ , column_stack , interp , meshgrid , nan , ceil , sinc , float64 , isnan , \
8
8
floor , percentile , zeros
9
+ from numpy .linalg import lstsq
9
10
from datetime import datetime
10
11
from scipy .special import j1
11
12
from netCDF4 import Dataset
14
15
from scipy .spatial import cKDTree
15
16
from scipy .signal import welch
16
17
from cv2 import filter2D
17
- from numba import jit
18
+ from numba import njit , prange
18
19
from matplotlib .path import Path as BasePath
19
20
from matplotlib .contour import QuadContourSet as BaseQuadContourSet
20
21
from pyproj import Proj
21
22
from pint import UnitRegistry
22
- from ..tools import fit_circle_c , distance_vector , winding_number_poly , poly_contain_poly , \
23
- distance , distance_point_vector
23
+ from ..tools import distance_vector , winding_number_poly , poly_contain_poly , distance , distance_point_vector
24
24
from ..observations import EddiesObservations
25
25
from ..eddy_feature import Amplitude , Contours
26
26
from .. import VAR_DESCR
@@ -115,7 +115,7 @@ def _fit_circle_path(self):
115
115
116
116
c_x , c_y = proj (self .lon , self .lat )
117
117
try :
118
- centlon_e , centlat_e , eddy_radius_e , aerr = fit_circle_c (c_x , c_y )
118
+ centlon_e , centlat_e , eddy_radius_e , aerr = fit_circle_c_numba (c_x , c_y )
119
119
centlon_e , centlat_e = proj (centlon_e , centlat_e , inverse = True )
120
120
centlon_e = (centlon_e - lon_mean + 180 ) % 360 + lon_mean - 180
121
121
self ._circle_params = centlon_e , centlat_e , eddy_radius_e , aerr
@@ -130,6 +130,74 @@ def _fit_circle_path(self):
130
130
self ._circle_params = 0 , - 90 , nan , nan
131
131
132
132
133
+ @njit (cache = True )
134
+ def fit_circle_c_numba (x_vec , y_vec ):
135
+ nb_elt = x_vec .shape [0 ]
136
+ p_inon_x = empty (nb_elt )
137
+ p_inon_y = empty (nb_elt )
138
+
139
+ x_mean = x_vec .mean ()
140
+ y_mean = y_vec .mean ()
141
+
142
+ norme = (x_vec - x_mean ) ** 2 + (y_vec - y_mean ) ** 2
143
+ norme_max = norme .max ()
144
+ scale = norme_max ** .5
145
+
146
+ # Form matrix equation and solve it
147
+ # Maybe put f4
148
+ datas = ones ((nb_elt , 3 ))
149
+ datas [:, 0 ] = 2. * (x_vec - x_mean ) / scale
150
+ datas [:, 1 ] = 2. * (y_vec - y_mean ) / scale
151
+
152
+ (center_x , center_y , radius ), residuals , rank , s = lstsq (datas , norme / norme_max )
153
+
154
+ # Unscale data and get circle variables
155
+ radius += center_x ** 2 + center_y ** 2
156
+ radius **= .5
157
+ center_x *= scale
158
+ center_y *= scale
159
+ # radius of fitted circle
160
+ radius *= scale
161
+ # center X-position of fitted circle
162
+ center_x += x_mean
163
+ # center Y-position of fitted circle
164
+ center_y += y_mean
165
+
166
+ # area of fitted circle
167
+ c_area = (radius ** 2 ) * pi
168
+
169
+ # Find distance between circle center and contour points_inside_poly
170
+ for i_elt in range (nb_elt ):
171
+ # Find distance between circle center and contour points_inside_poly
172
+ dist_poly = ((x_vec [i_elt ] - center_x ) ** 2 + (y_vec [i_elt ] - center_y ) ** 2 ) ** .5
173
+ # Indices of polygon points outside circle
174
+ # p_inon_? : polygon x or y points inside & on the circle
175
+ if dist_poly > radius :
176
+ p_inon_y [i_elt ] = center_y + radius * (y_vec [i_elt ] - center_y ) / dist_poly
177
+ p_inon_x [i_elt ] = center_x - (center_x - x_vec [i_elt ]) * (center_y - p_inon_y [i_elt ]) / (
178
+ center_y - y_vec [i_elt ])
179
+ else :
180
+ p_inon_x [i_elt ] = x_vec [i_elt ]
181
+ p_inon_y [i_elt ] = y_vec [i_elt ]
182
+
183
+ # Area of closed contour/polygon enclosed by the circle
184
+ p_area_incirc = 0
185
+ p_area = 0
186
+ for i_elt in range (nb_elt - 1 ):
187
+ # Indices of polygon points outside circle
188
+ # p_inon_? : polygon x or y points inside & on the circle
189
+ p_area_incirc += p_inon_x [i_elt ] * p_inon_y [1 + i_elt ] - p_inon_x [i_elt + 1 ] * p_inon_y [i_elt ]
190
+ # Shape test
191
+ # Area and centroid of closed contour/polygon
192
+ p_area += x_vec [i_elt ] * y_vec [1 + i_elt ] - x_vec [1 + i_elt ] * y_vec [i_elt ]
193
+ p_area = abs (p_area ) * .5
194
+
195
+ p_area_incirc = abs (p_area_incirc ) * .5
196
+
197
+ a_err = (c_area - 2 * p_area_incirc + p_area ) * 100. / c_area
198
+ return center_x , center_y , radius , a_err
199
+
200
+
133
201
BasePath .fit_circle = fit_circle_path
134
202
BasePath ._fit_circle_path = _fit_circle_path
135
203
@@ -502,12 +570,12 @@ def eddy_identification(self, grid_height, uname, vname, date, step=0.005, shape
502
570
# First, get position based on innermost
503
571
# contour
504
572
c_x , c_y = proj (inner_contour .lon , inner_contour .lat )
505
- centx_s , centy_s , _ , _ = fit_circle_c (c_x , c_y )
573
+ centx_s , centy_s , _ , _ = fit_circle_c_numba (c_x , c_y )
506
574
centlon_s , centlat_s = proj (centx_s , centy_s , inverse = True )
507
575
# Second, get speed-based radius based on
508
576
# contour of max uavg
509
577
c_x , c_y = proj (speed_contour .lon , speed_contour .lat )
510
- _ , _ , eddy_radius_s , aerr_s = fit_circle_c (c_x , c_y )
578
+ _ , _ , eddy_radius_s , aerr_s = fit_circle_c_numba (c_x , c_y )
511
579
512
580
# Instantiate new EddyObservation object
513
581
properties = EddiesObservations (size = 1 , track_extra_variables = track_extra_variables ,
@@ -1192,26 +1260,26 @@ def interp(self, grid_name, lons, lats):
1192
1260
new z
1193
1261
"""
1194
1262
z = empty (lons .shape , dtype = 'f4' ).reshape (- 1 )
1195
- g = self .grid (grid_name ). astype ( 'f4' )
1263
+ g = self .grid (grid_name )
1196
1264
interp_numba (
1197
- self .x_c . astype ( 'f4' ) ,
1198
- self .y_c . astype ( 'f4' ) ,
1265
+ self .x_c ,
1266
+ self .y_c ,
1199
1267
g ,
1200
- lons .reshape (- 1 ). astype ( 'f4' ) ,
1201
- lats .reshape (- 1 ). astype ( 'f4' ) ,
1268
+ lons .reshape (- 1 ),
1269
+ lats .reshape (- 1 ),
1202
1270
z ,
1203
1271
g .fill_value
1204
1272
)
1205
1273
return z
1206
1274
1207
1275
1208
- @jit ( "void(f4[:], f4[:], f4[:,:], f4[:], f4[:], f4[:], f4)" , nopython = True )
1276
+ @njit ( parralel = True , cache = True )
1209
1277
def interp_numba (x_g , y_g , z , x , y , dest_z , fill_value ):
1210
1278
x_ref = x_g [0 ]
1211
1279
y_ref = y_g [0 ]
1212
1280
x_step = x_g [1 ] - x_ref
1213
1281
y_step = y_g [1 ] - y_ref
1214
- for i in range (x .size ):
1282
+ for i in prange (x .size ):
1215
1283
x_ = (x [i ] - x_ref ) / x_step
1216
1284
y_ = (y [i ] - y_ref ) / y_step
1217
1285
i0 = int (floor (x_ ))
0 commit comments