def horizontal_wind_component(self, UV=None, UV_DIR=None, working_with_xarray=False, xarray_data=None, wind_name="Wind", wind_dir_name="Wind_DIR", verbose=True): """ U = -np.sin(UV_DIR) * UV V = -np.cos(UV_DIR) * UV Computes U and V component from wind direction and wind speed Parameters ---------- UV : dnarray Wind speed UV_DIR : dnarray Wind_direction working_with_xarray : boolean, optionnal If True, computes U and V on an xarray dataframe (provided with names of variables) and returns the dataframe Returns ------- U : dnarray Horizontal wind speed component U V : string, optional Horizontal wind speed component V """ if not (working_with_xarray): if self._numexpr: U = ne.evaluate("-sin(UV_DIR) * UV") V = ne.evaluate("-cos(UV_DIR) * UV") else: U = -np.sin(UV_DIR) * UV V = -np.cos(UV_DIR) * UV if verbose: print('__Horizontal wind component calculated') U = change_dtype_if_required(U, np.float32) V = change_dtype_if_required(V, np.float32) return (U, V) if working_with_xarray: xarray_data = xarray_data.assign(theta=lambda x: (np.pi / 180) * (x[wind_dir_name] % 360)) xarray_data = xarray_data.assign(U=lambda x: -x[wind_name] * np.sin(x["theta"])) xarray_data = xarray_data.assign(V=lambda x: -x[wind_name] * np.cos(x["theta"])) if verbose: print('__Horizontal wind component calculated on xarray') return (xarray_data)
def direction_from_u_and_v(self, U, V, output_in_degree=True, verbose=True): """ UV_DIR = 180 + (180/3.14159)*arctan2(U,V)) % 360 Compute wind direction from U and V components Parameters ---------- U : dnarray Horizontal wind speed component U V : string, optional Horizontal wind speed component V output_in_degree : boolean, optionnal If True, the wind direction is converted to degrees Returns ------- UV_DIR : dnarray Wind direction """ if output_in_degree: if self._numexpr: UV_DIR = ne.evaluate("(180 + (180/3.14159)*arctan2(U,V)) % 360") else: UV_DIR = np.mod(180 + np.rad2deg(np.arctan2(U, V)), 360) if verbose: print('__Wind_DIR calculated from U and V') UV_DIR = change_dtype_if_required(UV_DIR, np.float32) return (UV_DIR)
def angular_deviation(self, U, V, verbose=True): """ Angular deviation from incoming flow. THe incoming flow is supposed from the West so that V=0. If deviated, V != 0. The angular deviation is then np.arctan(V / U) Parameters ---------- U : dnarray Horizontal wind speed component U V : string, optional Horizontal wind speed component V Returns ------- alpha : ndarray Angular deviation [rad] """ if self._numexpr: alpha = ne.evaluate("where(U == 0, where(V == 0, 0, V/abs(V) * 3.14159 / 2), arctan(V / U))") else: alpha = np.where(U == 0, np.where(V == 0, 0, np.sign(V) * np.pi / 2), np.arctan(V / U)) alpha = change_dtype_if_required(alpha, np.float32) if verbose: print("__Angular deviation calculated") return (alpha)
def direction_from_alpha(self, wind_dir, alpha, input_dir_in_degree=True, verbose=True): """ wind_dir - alpha Wind direction modified by angular deviation due to wind/topography interaction. Warning: this function might return a new wind direction in a rotated coordinates. Parameters ---------- wind_dir : dnarray Initial NWP wind direction alpha : dnarray Angular deviation input_dir_in_degree : boolean, optionnal If True, converts the input wind direction from radans to degrees (Default: True) Returns ------- alpha : ndarray Modified wind direction """ if input_dir_in_degree: if self._numexpr: UV_DIR = ne.evaluate("(3.14159/180) * wind_dir - alpha") else: UV_DIR = (np.pi / 180) * wind_dir - alpha UV_DIR = change_dtype_if_required(UV_DIR, np.float32) if verbose: print("__Wind_DIR calculated from alpha") return (UV_DIR)
def xsi_helbig_map(self, mnt, mu, idx_x, idx_y, reduce_mnt=True, nb_pixels_x=100, nb_pixels_y=100, librairie="numba", verbose=True): y_left, y_right, x_left, x_right = self._get_window_idx_boundaries(idx_x, idx_y, x_win=69//2, y_wind=79//2, reshape=False) if reduce_mnt: small_idx_y = idx_y[nb_pixels_y:-nb_pixels_y:, nb_pixels_x:-nb_pixels_x] small_idx_x = idx_x[nb_pixels_y:-nb_pixels_y:, nb_pixels_x:-nb_pixels_x] y_left, y_right, x_left, x_right = self._get_window_idx_boundaries(small_idx_x, small_idx_y) small_shape = small_idx_y.shape if librairie == "numba" and _numba: mnt, y_left, y_right, x_left, x_right = change_several_dtype_if_required([mnt, y_left, y_right, x_left, x_right], [np.float32, np.int32, np.int32, np.int32, np.int32]) boundaries_mnt = [mnt.shape[0], mnt.shape[0], mnt.shape[1], mnt.shape[1]] y_left, y_right, x_left, x_right = self._control_idx_boundaries([y_left, y_right, x_left, x_right], min_idx=[0, 0, 0, 0], max_idx=boundaries_mnt) std_slicing_numba = jit([float32[:](float32[:, :], int32[:], int32[:], int32[:], int32[:])], cache=True, nopython=True)(self.std_slicing_numpy) std_flat = std_slicing_numba(mnt, y_left, y_right, x_left, x_right) librairie = "numba" else: std_flat = np.array([np.std(mnt[i1:j1, i2:j2]) for i1, j1, i2, j2 in zip(y_left, y_right, x_left, x_right)]) librairie = "numpy" std = std_flat.reshape((small_shape[0], small_shape[1])) if reduce_mnt else std_flat if verbose: print(f"__Subgrid: computed average std. Output shape: {std.shape}. Librairie: {librairie}") xsi = np.sqrt(2) * std / mu xsi = change_dtype_if_required(xsi, np.float32) if verbose: print(f"__Subgrid: computed average xsi. Output shape: {xsi.shape}") return(xsi)
def wind_speed_scaling(self, scaling_wind, prediction, linear=True, verbose=True): """ scaling_wind * prediction / 3 Parameters ---------- scaling_wind : dnarray Scaling wind (ex: NWP wind) prediction : ndarray CNN ouptut linear : boolean, optionnal Linear scaling (Default: True) Returns ------- prediction : ndarray Scaled wind """ if linear: if self._numexpr: prediction = ne.evaluate("scaling_wind * prediction / 3") else: prediction = scaling_wind * prediction / 3 prediction = change_dtype_if_required(prediction, np.float32) if verbose: print('__Wind speed scaling done') return (prediction)
def x_dsc_topo_helbig(self, mnt, dx=25, idx_x=None, idx_y=None, type="map", librairie="numba", verbose=True): a = 17.0393 b = 0.737 c = 1.0234 d = 0.3794 e = 1.9821 if type == "map": laplacian = self.laplacian_map(mnt, dx, librairie=librairie, helbig=True) mu = self.mu_helbig_map(mnt, dx) elif type == "indexes": idx_x, idx_y = change_several_dtype_if_required([idx_x, idx_y], [np.int32, np.int32]) laplacian = self.laplacian_idx(mnt, idx_x, idx_y, dx, librairie=librairie, helbig=True) mu = self.mu_helbig_idx(mnt, dx, idx_x, idx_y) term_1 = 1 - a*laplacian/(1 + a*np.abs(laplacian)**b) term_2 = c / (1 + d*mu**e) x = term_1*term_2 if verbose: print(f"__MNT shape: {mnt.shape}") print(f"__x_dsc_topo computed. x shape: {x.shape}") x = change_dtype_if_required(x, np.float32) return(x)
def mu_helbig_average(self, mnt, dx, idx_x, idx_y, reduce_mnt=True, type="map", nb_pixels_x=100, nb_pixels_y=100, librairie="numba", verbose=True): if reduce_mnt: small_idx_y = idx_y[nb_pixels_y:-nb_pixels_y, nb_pixels_x:-nb_pixels_x] small_idx_x = idx_x[nb_pixels_y:-nb_pixels_y, nb_pixels_x:-nb_pixels_x] shape = small_idx_y.shape y_left, y_right, x_left, x_right = self._get_window_idx_boundaries(small_idx_x, small_idx_y) else: y_left, y_right, x_left, x_right = self._get_window_idx_boundaries(idx_x, idx_y, reshape=False) if type == "map": mu = self.mu_helbig_map(mnt, dx) if librairie == 'numba' and _numba: mu, y_left, y_right, x_left, x_right = change_several_dtype_if_required( [mnt, y_left, y_right, x_left, x_right], [np.float32, np.int32, np.int32, np.int32, np.int32]) jit_mean = jit([float32[:](float32[:, :], int32[:], int32[:], int32[:], int32[:])], nopython=True, cache=True)(self.mean_slicing_numpy) mu_flat = jit_mean(mu, y_left, y_right, x_left, x_right) librairie = "numba" else: mu_flat = np.array([np.mean(mu[i1:j1, i2:j2]) for i1, j1, i2, j2 in zip(y_left, y_right, x_left, x_right)]) librairie = "numpy" elif type == "indexes": boundaries_mnt = [mnt.shape[0], mnt.shape[0], mnt.shape[1], mnt.shape[1]] y_left, y_right, x_left, x_right = self._control_idx_boundaries([y_left, y_right, x_left, x_right], min_idx=[0, 0, 0, 0], max_idx=boundaries_mnt) mu_flat = np.array([np.mean(self.mu_helbig_map(mnt[i1:j1, i2:j2], dx)) for i1, j1, i2, j2 in zip(y_left, y_right, x_left, x_right)]) mu = mu_flat.reshape((shape[0], shape[1])) if reduce_mnt else mu_flat mu = change_dtype_if_required(mu, np.float32) if verbose: print(f"__Subgrid: computed average mu. Output shape: {mu.shape}. Librairie: {librairie}") return(mu)
def mu_helbig_map(self, mnt, dx, verbose=True): """ Adapted from I. Gouttevin From Helbig et al. 2017 """ mu = np.sqrt(np.sum(np.array(np.gradient(mnt, dx))**2, axis=0)/2) mu = change_dtype_if_required(mu, np.float32) if verbose: print("__mu calculation using numpy") return(mu)
def laplacian_idx(self, mnt, idx_x, idx_y, dx, verbose=True, librairie='numpy', helbig=True): """ Discrete laplacian on a regular grid """ if librairie == 'numba' and _numba: mnt = change_dtype_if_required(mnt, np.float32) idx_x = change_dtype_if_required(idx_x, np.int32) idx_y = change_dtype_if_required(idx_y, np.int32) laplacian = self._laplacian_numba_idx(mnt, idx_x, idx_y, dx, helbig=helbig) librairie = librairie else: laplacian = self._laplacian_numpy_idx(mnt, idx_x, idx_y, dx, helbig=helbig) librairie = "numpy" laplacian = change_dtype_if_required(laplacian, np.float32) if verbose: print(f"__Laplacian calculated. Librarie: {librairie}") return(laplacian)
def compute_wind_speed(self, U=None, V=None, W=None, computing='num', out=None, xarray_data=None, u_name="U", v_name="V", verbose=True): """ Calculates wind speed from wind speed components. First detects the number of wind component then calculates wind speed. The calculation can be performed on nmexpr, numpy or xarray dataset Parameters ---------- U : dnarray Horizontal wind speed component U V : string, optional Horizontal wind speed component V W : string, optional Vertical wind speed component V out : ndarray, optional If specified, the output of the calculation is directly written in out, which is best for memory consumption (Default: None) computing : str, optional Select the librairie to use for calculation. If 'num' first test numexpr, if not available select numpy. If 'xarray', a xarray dataframe needs to be specified with the corresponding names for wind components. (Default: 'num') Returns ------- UV : ndarray Wind speed """ # Numexpr or numpy if computing == 'num': if out is None: if W is None: wind_speed = self._2D_wind_speed(U=U, V=V, verbose=verbose) else: wind_speed = self._3D_wind_speed(U=U, V=V, W=W, verbose=verbose) wind_speed = change_dtype_if_required(wind_speed, np.float32) return (wind_speed) else: if W is None: self._2D_wind_speed(U=U, V=V, out=out, verbose=verbose) else: self._3D_wind_speed(U=U, V=V, W=W, out=out, verbose=verbose) if computing == 'xarray': xarray_data = xarray_data.assign(Wind=lambda x: np.sqrt(x[u_name] ** 2 + x[v_name] ** 2)) xarray_data = xarray_data.assign( Wind_DIR=lambda x: np.mod(180 + np.rad2deg(np.arctan2(x[u_name], x[v_name])), 360)) if verbose: print("__Wind and Wind_DIR calculated on xarray") return (xarray_data)
def mu_helbig_idx(self, mnt, dx, idx_x, idx_y, verbose=True): mu = self.mu_helbig_map(mnt, dx) mu = change_dtype_if_required(mu, np.float32) if verbose: print("__Selecting indexes on mu") return(mu[idx_y, idx_x])
if verbose: print(f"__Subgrid: computed average xsi. Output shape: {xsi.shape}") return(xsi) def x_sgp_topo_helbig_idx(self, mnt, idx_x, idx_y, dx, L=2_000, type="map", reduce_mnt=True, nb_pixels_x=100, nb_pixels_y=100, verbose=True): a = 3.354688 b = 1.998767 c = 0.20286 d = 5.951 mu = self.mu_helbig_average(mnt, dx, idx_x, idx_y, type=type, reduce_mnt=reduce_mnt) xsi = self.xsi_helbig_map(mnt, mu, idx_x, idx_y, reduce_mnt=reduce_mnt, nb_pixels_x=nb_pixels_x, nb_pixels_y=nb_pixels_y, librairie="numba") x = 1 - (1 - (1/(1+a*mu**b))**c)*np.exp(-d*(L/xsi)**(-2)) x = change_dtype_if_required(x, np.float32) if verbose: print(f"__Subgrid: computed x_sgp_topo. Output shape: {x.shape}") return(x) def subgrid(self, mnt_large, dx=25, L=2_000, idx_x=None, idx_y=None, type="map", reduce_mnt=True, nb_pixels_x=100, nb_pixels_y=100, verbose=True): if type == "map": shape = mnt_large.shape all_x_idx = range(shape[1]) all_y_idx = range(shape[0]) idx_x, idx_y = np.array(np.meshgrid(all_x_idx, all_y_idx)).astype(np.int32) if verbose: print(f"Large mnt shape: {shape}. Size reduction on x: 2 * {nb_pixels_x}. Size reduction on x: 2 * {nb_pixels_y} ") reduce_mnt = False if type == "indexes" else reduce_mnt x_sgp_topo = self.x_sgp_topo_helbig_idx(mnt_large, idx_x, idx_y, dx,