Beispiel #1
0
 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)
Beispiel #2
0
 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]]
Beispiel #3
0
 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]
Beispiel #4
0
    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)
Beispiel #5
0
 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
Beispiel #6
0
    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]
        ]
Beispiel #7
0
 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))
Beispiel #8
0
    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
Beispiel #9
0
    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)
Beispiel #10
0
 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