def __call__(self, f, ws, yaw=None, tilt=None, **kwargs): if yaw is not None or tilt is not None: co = 1 if yaw is not None: co *= np.cos(np.deg2rad(fix_shape(yaw, ws, True))) if tilt is not None: co *= np.cos(np.deg2rad(fix_shape(tilt, ws, True))) power_ct_arr = f( ws * co, **kwargs) # calculate for reduced ws (ws projection on rotor) if kwargs['run_only'] == 1: # ct # multiply ct by cos(yaw)**2 to compensate for reduced thrust return power_ct_arr * co**2 return power_ct_arr else: return f(ws, **kwargs)
def __call__(self, ws, run_only=slice(None), **kwargs): x = self.get_input(ws=ws, **kwargs) x = np.array([fix_shape(v, ws).ravel() for v in x]).T if isinstance(run_only, int): return self.function_surrogate_lst[run_only].predict_output(x).reshape(ws.shape) else: return [fs.predict_output(x).reshape(ws.shape) for fs in np.asarray(self.function_surrogate_lst)[run_only]]
def _power_ct(ws, run_only, **kwargs): try: v = fix_shape(kwargs.pop(key), ws, True) except KeyError: if optional: v = 1 else: raise return tab_powerct_curve(ws, run_only) * (v, 1)[run_only]
def _power_ct(self, ws, run_only, **kwargs): kwargs = { **self.default_value_dict, 'ws': ws, **{k: v for k, v in kwargs.items() if v is not None} } args = np.moveaxis([fix_shape(kwargs[k], ws) for k in self.input_keys], 0, -1) try: return self.interp[run_only](args) except ValueError: check_input(self.interp[run_only].grid, args.T, self.input_keys)
def _power_ct(self, ws, run_only, **kwargs): m = (ws > self.ws_cutin) & (ws < self.ws_cutout) kwargs = {k: fix_shape(v, ws)[m] for k, v in kwargs.items()} arr_m = PowerCtSurrogate._power_ct(self, ws[m], run_only=run_only, **kwargs) if run_only == 0: power = np.zeros_like(ws) power[m] = arr_m return power else: ct = np.full(ws.shape, self.ct_idle) ct_m = arr_m * 1000 / (1 / 2 * 1.225 * (65**2 * np.pi) * ws[m]**2) ct[m] = ct_m return ct
def __call__(self, ws, run_only=slice(None), **kwargs): ws_flat = ws.ravel() x = self.get_input(ws=ws, **kwargs) x = np.array([fix_shape(v, ws).ravel() for v in x]).T def predict(fs): output = np.empty(len(x)) for fs_, m in zip(fs, [ ws_flat < self.ws_cutin, (self.ws_cutin <= ws_flat) & (ws_flat <= self.ws_cutout), ws_flat > self.ws_cutout ]): if m.sum(): output[m] = fs_.predict_output(x[m], bounds='ignore')[:, 0] return output return [ predict(fs).reshape(ws.shape) for fs in np.asarray(self.function_surrogate_lst)[run_only] ]
def _fix_shape(k, v): if k[-3:] == 'ijl': return fix_shape(v, ws_iilk) else: return np.broadcast_to( fix_shape(v, WS_eff_ilk)[na], (I, I, L, K))
def loads(self, method, lifetime_years=20, n_eq_lifetime=1e7, normalize_probabilities=False, softmax_base=None): assert method in ['TwoWT', 'OneWT_WDAvg', 'OneWT'] wt = self.windFarmModel.windTurbines P_ilk = self.P_ilk if normalize_probabilities: P_ilk /= P_ilk.sum((1, 2))[:, na, na] WS_eff_ilk = self.WS_eff_ilk TI_eff_ilk = self.TI_eff_ilk kwargs = self.wt_inputs if method == 'OneWT_WDAvg': # average over wd p_wd_ilk = P_ilk.sum((0, 2))[na, :, na] ws_ik = (WS_eff_ilk * p_wd_ilk).sum(1) kwargs_ik = { k: (fix_shape(v, WS_eff_ilk) * p_wd_ilk).sum(1) for k, v in kwargs.items() if k != 'TI_eff' and v is not None } kwargs_ik.update({k: v for k, v in kwargs.items() if v is None}) loads, i_lst = [], [] m_lst = np.asarray(wt.loadFunction.wohler_exponents) for m in np.unique(m_lst): i = np.where(m_lst == m)[0] if 'TI_eff' in kwargs: kwargs_ik['TI_eff'] = ((p_wd_ilk * TI_eff_ilk**m).sum(1))**(1 / m) loads.extend(wt.loads(ws_ik, run_only=i, **kwargs_ik)) i_lst.extend(i) loads = [loads[i] for i in np.argsort(i_lst)] # reorder ds = xr.DataArray(loads, dims=['sensor', 'wt', 'ws'], coords={ 'sensor': wt.loadFunction.output_keys, 'm': ('sensor', wt.loadFunction.wohler_exponents), 'wt': self.wt, 'ws': self.ws }, attrs={ 'description': '1Hz Damage Equivalent Load' }).to_dataset(name='DEL') ds['P'] = self.P.sum('wd') t_flowcase = ds.P * lifetime_years * 365 * 24 * 3600 f = ds.DEL.mean( ) # factor used to reduce numerical errors in power ds['LDEL'] = ( (t_flowcase * (ds.DEL / f)**ds.m).sum('ws') / n_eq_lifetime)**(1 / ds.m) * f ds.LDEL.attrs[ 'description'] = "Lifetime (%d years) equivalent loads, n_eq_L=%d" % ( lifetime_years, n_eq_lifetime) elif method == 'OneWT' or method == 'TwoWT': if method == 'OneWT': loads_silk = wt.loads(WS_eff_ilk, **kwargs) else: # method == 'TwoWT': I, L, K = WS_eff_ilk.shape ws_iilk = np.broadcast_to(WS_eff_ilk[na], (I, I, L, K)) def _fix_shape(k, v): if k[-3:] == 'ijl': return fix_shape(v, ws_iilk) else: return np.broadcast_to( fix_shape(v, WS_eff_ilk)[na], (I, I, L, K)) kwargs_iilk = { k: _fix_shape(k, v) for k, v in kwargs.items() if k in wt.loadFunction.required_inputs + wt.loadFunction.optional_inputs } loads_siilk = np.array(wt.loads(ws_iilk, **kwargs_iilk)) if softmax_base is None: loads_silk = loads_siilk.max(1) else: # factor used to reduce numerical errors in power f = loads_siilk.mean((1, 2, 3, 4)) / 10 loads_silk = (np.log( (softmax_base **(loads_siilk / f[:, na, na, na, na])).sum(1)) / np.log(softmax_base) * f[:, na, na, na]) if 'time' in self.dims: ds = xr.DataArray(np.array(loads_silk)[..., 0], dims=['sensor', 'wt', 'time'], coords={ 'sensor': wt.loadFunction.output_keys, 'm': ('sensor', wt.loadFunction.wohler_exponents, { 'description': 'Wohler exponents' }), 'wt': self.wt, 'time': self.time, 'wd': self.wd, 'ws': self.ws }, attrs={ 'description': '1Hz Damage Equivalent Load' }).to_dataset(name='DEL') else: ds = xr.DataArray(loads_silk, dims=['sensor', 'wt', 'wd', 'ws'], coords={ 'sensor': wt.loadFunction.output_keys, 'm': ('sensor', wt.loadFunction.wohler_exponents, { 'description': 'Wohler exponents' }), 'wt': self.wt, 'wd': self.wd, 'ws': self.ws }, attrs={ 'description': '1Hz Damage Equivalent Load' }).to_dataset(name='DEL') f = ds.DEL.mean( ) # factor used to reduce numerical errors in power if 'time' in self.dims: assert 'duration' in self, "Simulation must contain a dataarray 'duration' with length of time steps in seconds" t_flowcase = self.duration ds['LDEL'] = ((t_flowcase * (ds.DEL / f)**ds.m).sum( ('time')) / n_eq_lifetime)**(1 / ds.m) * f else: ds['P'] = self.P t_flowcase = ds.P * 3600 * 24 * 365 * lifetime_years ds['LDEL'] = ((t_flowcase * (ds.DEL / f)**ds.m).sum( ('wd', 'ws')) / n_eq_lifetime)**(1 / ds.m) * f ds.LDEL.attrs[ 'description'] = "Lifetime (%d years) equivalent loads, n_eq_L=%d" % ( lifetime_years, n_eq_lifetime) return ds
def __call__(self, x, y, h=None, type=0, wd=None, ws=None, yaw=None, tilt=None, time=False, verbose=False, **kwargs): """Run the wind farm simulation Parameters ---------- x : array_like Wind turbine x positions y : array_like Wind turbine y positions h : array_like, optional Wind turbine hub heights type : int or array_like, optional Wind turbine type, default is 0 wd : int or array_like Wind direction(s) ws : int, float or array_like Wind speed(s) yaw_ilk : array_like or None, optional Yaw misalignement of turbine(i) for wind direction(l) and wind speed (k)\n Positive is counter-clockwise when seen from above Returns ------- SimulationResult """ if time is False and np.ndim(wd): wd = np.sort(wd) assert len(x) == len(y) self.verbose = verbose h, _ = self.windTurbines.get_defaults(len(x), type, h) I, L, K, = len(x), len(np.atleast_1d(wd)), (1, len( np.atleast_1d(ws)))[time is False] if len([ k for k in kwargs if 'yaw' in k.lower() and k != 'yaw' and not k.startswith('yawc_') ]): raise ValueError( 'Custom *yaw*-keyword arguments not allowed to avoid confusion with the default "yaw" keyword' ) yaw_ilk = fix_shape(yaw, (I, L, K), allow_None=True) tilt_ilk = fix_shape(tilt, (I, L, K), allow_None=True) if len(x) == 0: lw = UniformSite([1], 0.1).local_wind(x_i=[], y_i=[], h_i=[], wd=wd, ws=ws) z = xr.DataArray(np.zeros((0, len(lw.wd), len(lw.ws))), coords=[('wt', []), ('wd', lw.wd), ('ws', lw.ws)]) return SimulationResult(self, lw, [], yaw, tilt, z, z, z, z, kwargs) res = self.calc_wt_interaction(x_i=np.asarray(x), y_i=np.asarray(y), h_i=h, type_i=type, yaw_ilk=yaw_ilk, tilt_ilk=tilt_ilk, wd=wd, ws=ws, time=time, **kwargs) WS_eff_ilk, TI_eff_ilk, power_ilk, ct_ilk, localWind, wt_inputs = res return SimulationResult(self, localWind=localWind, type_i=np.zeros(len(x), dtype=int) + type, yaw_ilk=yaw_ilk, tilt_ilk=tilt_ilk, WS_eff_ilk=WS_eff_ilk, TI_eff_ilk=TI_eff_ilk, power_ilk=power_ilk, ct_ilk=ct_ilk, wt_inputs=wt_inputs)
def __call__(self, f, ws, Air_density=None, **kwargs): power_ct_arr = np.asarray(f(ws, **kwargs)) if Air_density is not None: power_ct_arr *= fix_shape(Air_density, ws, True) / self.air_density_ref return power_ct_arr