def geom_syn_field(rp0, rp1, N_x, N_y): # Polar grid 2 horizontal scans rmin0,rmax0,nr0,phimin0,phimax0,np0,orig0 = rp0 rmin1,rmax1,nr1,phimin1,phimax1,np1,orig1 = rp1 d = orig1-orig0 r_0_g, phi_0_g, r_0_t, phi_0_t = geom_polar_grid(rmin0,rmax0,nr0,phimin0,phimax0,np0,-d) r_1_g, phi_1_g, r_1_t, phi_1_t = geom_polar_grid(rmin1,rmax1,nr1,phimin1,phimax1,np1,d) x_max = np.max(np.r_[(r_0_t*np.cos(phi_0_t)).flatten(),(r_1_t*np.cos(phi_1_t)).flatten()]) x_min = np.min(np.r_[(r_0_t*np.cos(phi_0_t)).flatten(),(r_1_t*np.cos(phi_1_t)).flatten()]) y_max = np.max(np.r_[(r_0_t*np.sin(phi_0_t)).flatten(),(r_1_t*np.sin(phi_1_t)).flatten()]) y_min = np.min(np.r_[(r_0_t*np.sin(phi_0_t)).flatten(),(r_1_t*np.sin(phi_1_t)).flatten()]) L_x = x_max-x_min L_y = y_max-y_min x = np.linspace(x_min,x_max,N_x) y = np.linspace(y_min,y_max,N_y) grid = np.meshgrid(x,y) tri = Delaunay(np.c_[grid[0].flatten(),grid[1].flatten()], qhull_options = "QJ") # Square grid for reconstruction _,tri_overlap,_,_,_,_,_,_ = wr.grid_over2((r_1_g, np.pi-phi_1_g),(r_0_g, np.pi-phi_0_g),-d) r_min=np.min(np.sqrt(tri_overlap.x**2+tri_overlap.y**2)) d_grid = r_min*2*np.pi/180 n_next = int(2**np.ceil(np.log(L_y/d_grid+1)/np.log(2))) x_new = np.linspace(x.min(),x.max(),n_next) y_new = np.linspace(y.min(),y.max(),n_next) grid_new = np.meshgrid(x_new,y_new) return (L_x, L_y, grid, x, y, tri, grid_new,d)
indt0 = [df_0.scan.isin(s0[(t0>=t_1h[i]) & (t0<t_1h[i+1])]) for i in range(len(t_1h)-1)] indt1 = [df_1.scan.isin(s1[(t1>=t_1h[i]) & (t1<t_1h[i+1])]) for i in range(len(t_1h)-1)] indt0 = [x for x in indt0 if np.sum(x) > 0] indt1 = [x for x in indt1 if np.sum(x) > 0] if (len(indt0)>0)&(len(indt1)>0): for i0,i1 in zip(indt0,indt1): df0 = df_0.loc[i0] df1 = df_1.loc[i1] if switch == 0: phi0 = df0.azim.unique() phi1 = df1.azim.unique() r0 = df0.range_gate.iloc[0].values r1 = df1.range_gate.iloc[0].values r_0, phi_0 = np.meshgrid(r0, np.pi/2-np.radians(phi0)) # meshgrid r_1, phi_1 = np.meshgrid(r0, np.pi/2-np.radians(phi1)) # meshgrid tree,tri, w0, neigh0, index0, w1, neigh1, index1 = wr.grid_over2((r_0, phi_0),(r_1,phi_1), -d) switch = 1 uo, vo, grdu, so = wr.direct_wf_rec(df0.astype(np.float32), df1.astype(np.float32), tri, d, N_grid = chunk) ulist.append(uo) vlist.append(vo) ulist = [item for sublist in ulist for item in sublist] vlist = [item for sublist in vlist for item in sublist] dr0 = np.array(np.r_[-d/2,np.ones(1)]).T dr1 = np.array(np.r_[d/2,np.ones(1)]).T fig0, ax0 = plt.subplots(figsize=(8, 8)) ax0.set_aspect('equal') ax0.use_sticky_edges = False ax0.margins(0.07)
def geom_syn_field(rp0, rp1, N_x, N_y): # Polar grid 2 horizontal scans rmin0, rmax0, nr0, phimin0, phimax0, np0, orig0 = rp0 rmin1, rmax1, nr1, phimin1, phimax1, np1, orig1 = rp1 d = orig1 - orig0 r_0_g, phi_0_g, r_0_t, phi_0_t = geom_polar_grid( rmin0, rmax0, nr0, phimin0, phimax0, np0, -d ) #km2: you translate it again because you pass only the tuple as an input r_1_g, phi_1_g, r_1_t, phi_1_t = geom_polar_grid(rmin1, rmax1, nr1, phimin1, phimax1, np1, d) #km2: I think that this should be changed in the new version where we have to triangulate the whole long field #km change: we must change the limits of the domain x_max x_min y_max y_min so that they will correspond to the long domain ! u_mean = 15 #remember to pass it as parameter in the function #x_max = np.max(np.r_[(r_0_t*np.cos(phi_0_t)).flatten(),(r_1_t*np.cos(phi_1_t)).flatten()])+u_mean*45#km: finds the maximum x in cartesian coordinates by by taking into account both scaners x_min = np.min(np.r_[(r_0_t * np.cos(phi_0_t)).flatten(), (r_1_t * np.cos(phi_1_t)).flatten()]) x_max = 9882.563743135905 - abs(x_min) #km: why you use only the translated coordinate systems? """ answer la: because when translated both scans are in the same polar coordinate system and represent the size of the whole domain that will be used later in the synthetic wind field generation. This is not our case since the domain might be several times bigger than the domain covered by the two scans, since it is moving """ y_max = np.max(np.r_[(r_0_t * np.sin(phi_0_t)).flatten(), (r_1_t * np.sin(phi_1_t)).flatten()]) y_min = np.min(np.r_[(r_0_t * np.sin(phi_0_t)).flatten(), (r_1_t * np.sin(phi_1_t)).flatten()]) L_x = x_max - x_min #km: length of the synthetic domain in x direction """answer la: yes""" L_y = y_max - y_min #km: length of the synthetic domain in y direction """answer la: yes""" x = np.linspace(x_min, x_max, N_x) print("x_max=", x_max, "x_min=", x_min) print("x_max_lin=", np.max(x), "x_min_lin=", np.min(x)) #km:new x discretization over the synthetic region? """answer la: yes, after knowing the limits of the squared domain covered by the scan, the grid is defined in x""" y = np.linspace(y_min, y_max, N_y) #km:new y discretization over the synthetic region ? """answer la: yes, after knowing the limits of the squared domain covered by the scan, the grid is defined in y""" grid = np.meshgrid(x, y) #km:overlap cartesian grid ? (N_y x N_x x 2) """answer la: it is just the cartesian grid for the synthetic wind filed""" tri = Delaunay(np.c_[grid[0].flatten(), grid[1].flatten()], qhull_options="QJ") #km: Delauney triangulation of the grid Why dont you triangulate the whole output of the mann model? np.c_ stacks the vectors and creates sets of points """answer la: Since the points we will use to interpolate on the scans grid points correspond to the ones in the synthetic wind field, the triangulation is done over the wind field cartesian grid. The output of the Mann's turbulence box are u and v, the grid (or at least the parameters of the gird, like L_x, L_y and N_x and N_y) is an input, defined previously. The Delaunay trinagulation is an scipy object that allows for example the identification of the corresponding triangle for a particular point (interpolation) and can be used as an input for a cubic interpolator for example""" # Square grid for reconstruction #km4: this last part has confused me a lot. #km4: You do a triangulation of the intersection set centers of the two scanners and you use the distance of the closest vertex to define a spacing through a formula. #km4: Then you generate a strctured grid based on this spacing #km4: whats the purpose ? """answer la4: As we discussed earlier, this new grid is generated to place the reconstructed wind field, The scans sample from a fine cartesian squared mesh, to a coarser polar mesh (coarser in tue outer regin of the scan, very fine near the origin of each beam) then it is interpolated back to a cartesian suqared mesh with a different refinement, this time depending on the smallest element size of the scan polar mesh. The V_los of each scan need to be interpolated to a squared cart. mesh to have V_los and azimuth angles from both scans at more common points than just the intersection points (with this we average less an retain more information)""" _, tri_overlap, _, _, _, _, _, _ = wr.grid_over2( (r_1_g, np.pi - phi_1_g), (r_0_g, np.pi - phi_0_g), -d) """ Comment: this is a function in windfieldrec, that define the tringulation of the intersection points of beams coming form the two scans """ r_min = np.min(np.sqrt(tri_overlap.x**2 + tri_overlap.y**2)) #km4: find the shortest distance from (0,0)? in cartesian coordinates """answer la4: yes, the closest to the lidars""" d_grid = r_min * 2 * np.pi / 180 #km4: splits the perimeter of the smalest circle in steps of 2 degrees ? d_grid holds this spacing? """answer la4: yes, 2 deg. is the azimuth step of the lidars""" n_next = int(2**np.ceil(np.log(L_y / d_grid + 1) / np.log(2))) #km4: some kind of formula that calculates the number of points in each direction. why this formula ? """answer la4: It is a formula to estimate the the number of grid points with base 2 (the base can be any number though)""" x_new = np.linspace(x.min(), x.max(), n_next) y_new = np.linspace(y.min(), y.max(), n_next) grid_new = np.meshgrid(x_new, y_new) #km4: a structired grid with uniform spacing in both directions in cartesian coordinates """answer la4: yes""" return (L_x, L_y, grid, x, y, tri, grid_new, d)
def geom_syn_field2(rp0, rp1, N_x, N_y, u_mean, rot_speed, dir_mean, tri_ret=True): """WRAP TO 0 TO 2*PI""" dir_mean = wrap_angle(dir_mean) # Polar grid 2 horizontal scans rmin0, rmax0, nr0, phimin0, phimax0, np0, orig0 = rp0 rmin1, rmax1, nr1, phimin1, phimax1, np1, orig1 = rp1 d = orig1 - orig0 r_0_g, phi_0_g, r_0_t, phi_0_t = geom_polar_grid( rmin0, rmax0, nr0, phimin0, phimax0, np0, d ) #km2: you translate it again because you pass only the tuple as an input r_1_g, phi_1_g, r_1_t, phi_1_t = geom_polar_grid(rmin1, rmax1, nr1, phimin1, phimax1, np1, -d) ################################ """ No wind direction yet just to locate the scans before any movement """ x_0_t, y_0_t = r_0_t * np.cos(phi_0_t), r_0_t * np.sin(phi_0_t) x_1_t, y_1_t = r_1_t * np.cos(phi_1_t), r_1_t * np.sin(phi_1_t) """ Rotation and translation: we then rotate according to wind direction. To do this we first translate to the position that the midpoint between the two lidars should be (see Figure_rot1,2 in the repo), and then rotate to wind direction, in local coordinates. The wind rose and cartesian have a different orientations that is why I put the 2*pi below) """ ind_max = np.argsort(np.abs(np.r_[x_0_t.flatten(), x_1_t.flatten()])) """ center is half way to the most distant point of the scan, in y """ center = np.r_[x_0_t.flatten(), x_1_t.flatten()][ind_max[-1]] / 2 #print(dir_mean*180/np.pi) gamma = -wrap_angle(np.pi - dir_mean) y_trans = (center) * np.sin(gamma) x_trans = (center) * (1 - np.cos(gamma)) S11 = np.cos(gamma) S12 = np.sin(gamma) T1 = np.array([[1, 0, x_trans], [0, 1, y_trans], [0, 0, 1]]) R = np.array([[S11, S12, 0], [-S12, S11, 0], [0, 0, 1]]) Xx0 = np.array(np.c_[x_0_t.flatten(), y_0_t.flatten(), np.ones(len(y_0_t.flatten()))]).T Xx1 = np.array(np.c_[x_1_t.flatten(), y_1_t.flatten(), np.ones(len(y_1_t.flatten()))]).T Xx0 = np.dot(T1, np.dot(R, Xx0))[:2, :].T Xx1 = np.dot(T1, np.dot(R, Xx1))[:2, :].T """ Once both scans are translated and rotated, we apply the final displacement due to wind field advection, we calculate the displacements per beam separatdly """ # Here I suppose that the mesh is equally spaced in phi, and it changes in axis 1, # you can make it more general dphi = np.abs(phimin0 - phimax0) / (np0 - 1) t_step = dphi / rot_speed t_total = np.abs(phimin0 - phimax0) / rot_speed #Below it is t_total+t_step to include the last point t_array = np.arange(0, t_total + t_step, t_step) """ The displacements array is the same for both scans with the same shape as x_0_t, x_1_t, y_0_t and y_1_t so I repeat the column of displacements per beam nr0 times, or the number of range gates along each beam """ disp_x = np.array([ list(u_mean * t_array), ] * nr0).transpose() """ No displacement in y, since the global coordinates are the streamwise and lateral components""" disp_y = np.zeros(disp_x.shape) #Just to check that everything is ok #print(disp_x, disp_y) """ Finally, the scans translated, rotated and advected to estimate the domain size """ Xx0 = Xx0 - np.c_[disp_x.flatten(), disp_y.flatten()] Xx1 = Xx1 - np.c_[disp_x.flatten(), disp_y.flatten()] """ Finally, the domain size """ x_max = np.max(np.r_[Xx0[:, 0], Xx1[:, 0]]) x_min = np.min(np.r_[Xx0[:, 0], Xx1[:, 0]]) y_max = np.max(np.r_[Xx0[:, 1], Xx1[:, 1]]) y_min = np.min(np.r_[Xx0[:, 1], Xx1[:, 1]]) """ Next step is just to return the scans (rta stands for rotated, translated, advected), they will be refined in early_weights routine. Nevetheless, both functions could be merged in one function. """ x_0_rta, y_0_rta = np.reshape(Xx0[:, 0], x_0_t.shape), np.reshape( Xx0[:, 1], x_0_t.shape) x_1_rta, y_1_rta = np.reshape(Xx1[:, 0], x_1_t.shape), np.reshape( Xx1[:, 1], x_1_t.shape) L_x = x_max - x_min L_y = y_max - y_min x = np.linspace(x_min, x_max, N_x) y = np.linspace(y_min, y_max, N_y) grid = np.meshgrid(x, y) print(grid[0].shape, x.shape) if tri_ret: tri = Delaunay(np.c_[grid[0].flatten(), grid[1].flatten()], qhull_options="QJ") else: tri = [] _, tri_overlap, _, _, _, _, _, _ = wr.grid_over2((r_1_g, phi_1_g), (r_0_g, phi_0_g), d) r_min = np.min(np.sqrt(tri_overlap.x**2 + tri_overlap.y**2)) d_grid = r_min * 2 * np.pi / 180 n_next = int(2**np.ceil(np.log(L_y / d_grid + 1) / np.log(2))) xn = np.r_[x_0_t.flatten(), x_1_t.flatten()] yn = np.r_[y_0_t.flatten(), y_1_t.flatten()] x_new = np.linspace(xn.min(), xn.max(), n_next) y_new = np.linspace(yn.min(), yn.max(), n_next) grid_new = np.meshgrid(x_new, y_new) return (L_x, L_y, grid, x, y, tri, grid_new, d, x_0_rta, y_0_rta, x_1_rta, y_1_rta, center)
mask_CNR.columns = mask.columns mask.mask(mask_CNR, other=False, inplace=True) df.ws = df.ws.mask(mask) DF.append(df) phi0w = DF[0].azim.unique() phi1w = DF[0].azim.unique() r0w = np.array( DF[0].iloc[(DF[0].azim == min(phi0w)).nonzero()[0][0]].range_gate) r1w = np.array( DF[0].iloc[(DF[0].azim == min(phi1w)).nonzero()[0][0]].range_gate) r_vaw, phi_vaw = np.meshgrid(r0w, np.radians(phi0w)) # meshgrid r_siw, phi_siw = np.meshgrid(r1w, np.radians(phi1w)) # meshgrid treew, triw, wvaw, neighvaw, indexvaw, wsiw, neighsiw, indexsiw = wr.grid_over2( (r_vaw, phi_vaw), (r_siw, phi_siw), d) fig, ax = plt.subplots() ax.set_aspect('equal') ax.use_sticky_edges = False ax.margins(0.07) for scan_n in range(10000, 13000): ax.cla() plt.title('Scan num. %i' % scan_n) ax.contourf(r_vaw * np.cos(phi_vaw), r_vaw * np.sin(phi_vaw), DF[1].ws.loc[DF[1].scan == scan_n].values, 100, cmap='rainbow') plt.pause(.01)
gamma = -(2 * np.pi - Dir) # Rotation # Components in matrix of coefficients S11 = np.cos(gamma) S12 = np.sin(gamma) T = np.array([[S11, S12], [-S12, S11]]) vel = np.array(np.c_[U.flatten(), V.flatten()]).T vel = np.dot(T, vel) X = np.array(np.c_[x, y]).T X = np.dot(T, X) U_prime = np.reshape(vel[0, :], (N_x, N_y)) V_prime = np.reshape(vel[1, :], (N_x, N_y)) # In[] tree, tri, wva, neighva, indexva, wsi, neighsi, indexsi = wr.grid_over2( (r_v_g, np.pi - phi_v_g), (r_s_g, np.pi - phi_s_g), -d) r_s_r = np.linspace(150, 7000, 198 * 2) r_v_r = np.linspace(150, 7000, 198 * 2) phi_s_r = np.linspace(256, 344, 45 * 2) * np.pi / 180 phi_v_r = np.linspace(196, 284, 45 * 2) * np.pi / 180 r_s_g_r, phi_s_g_r = np.meshgrid(r_s_r, phi_s_r) r_v_g_r, phi_v_g_r = np.meshgrid(r_v_r, phi_v_r) tree_r, tri_r, wva_r, neighva_r, indexva_r, wsi_r, neighsi_r, indexsi_r = wr.grid_over2( (r_v_g_r, np.pi - phi_v_g_r), (r_s_g_r, np.pi - phi_s_g_r), -d) # In[] #vloss0 = num_lidar0(r_s_g, np.pi-phi_s_g,U_prime,V_prime,x,y,d/2) #vlosv0 = num_lidar0(r_v_g, np.pi-phi_v_g,U_prime,V_prime,x,y,-d/2) vloss, _ = num_lidar(r_s_g,
# rmin1,rmax1,nr1,phimin1,phimax1,np1,orig1 rmin1,rmax1,nr1,phimin1,phimax1,np1,orig1 = 105,7000,198,196,284,45,np.array([6327082.4,0]) rp1 = (rmin1,rmax1,nr1,phimin1,phimax1,np1,orig1) # Grids, polar and cartesian d = orig1-orig0 # Polar grids for Scan 0 (local and translated) r_0_g, phi_0_g, r_0_t, phi_0_t = sy.geom_polar_grid(rmin0,rmax0,nr0,phimin0,phimax0,np0,-d) # Polar grids for Scan 1 (local and translated) r_1_g, phi_1_g, r_1_t, phi_1_t = sy.geom_polar_grid(rmin1,rmax1,nr1,phimin1,phimax1,np1, d) L_x, L_y, grid, x, y, tri, grid_new, d = sy.geom_syn_field(rp0, rp1, N_x, N_y) _,tri_i,_, _, _, _, _, _ = wr.grid_over2((r_1_g, np.pi-phi_1_g),(r_0_g, np.pi-phi_0_g),-d) # Triangulation and weights for each scan dl = 75 # From Cartesian coord. to polar in global grid r_tri_s = np.sqrt(grid_new[0]**2 + grid_new[1]**2) phi_tri_s = np.arctan2(grid_new[1],grid_new[0]) r_tri_1_s, phi_tri_1_s = wr.translationpolargrid((r_tri_s, phi_tri_s),-d/2) r_tri_0_s, phi_tri_0_s = wr.translationpolargrid((r_tri_s, phi_tri_s),d/2) # Mann-model parameters ae = [0.025, 0.05, 0.075] L = [62,62.5,125,250,500,750,1000] G = [0,1,2,2.5,3.5] seed = np.arange(1,10)