Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

py-wake

Package Overview
Dependencies
Maintainers
2
Versions
40
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

py-wake - npm Package Compare versions

Comparing version
2.6.16
to
2.6.17
+1
-1
METADATA
Metadata-Version: 2.4
Name: py_wake
Version: 2.6.16
Version: 2.6.17
Summary: Open source static wake modeling framework from DTU

@@ -5,0 +5,0 @@ Author: DTU Wind Energy

@@ -173,3 +173,3 @@ from abc import ABC, abstractmethod

deficit_centre_ijlk : array_like
Wind speed deficit caused by the i'th turbine at j'th downstream location, without accounting for crosswind distance (ie cw = 0)
Wind speed centerline deficit caused by the i'th turbine at j'th downstream location, without accounting for crosswind distance (ie cw = 0)
uc_ijlk : array_like

@@ -187,3 +187,3 @@ Convection velocity of the i'th turbine at locations j

method='linear', bounds='limit',
rotorAvgModel=None, groundModel=None, use_effective_ws=True, use_effective_ti=False):
superpositionModel=None, rotorAvgModel=None, groundModel=None, use_effective_ws=True, use_effective_ti=False):
"""

@@ -215,4 +215,6 @@ Parameters

XRLUTModel.__init__(self, da, get_input, get_output, method=method, bounds=bounds)
BlockageDeficitModel.__init__(self, upstream_only=True, rotorAvgModel=rotorAvgModel, groundModel=groundModel)
WakeDeficitModel.__init__(self, rotorAvgModel=rotorAvgModel, groundModel=groundModel,
BlockageDeficitModel.__init__(self, upstream_only=True, superpositionModel=superpositionModel,
rotorAvgModel=rotorAvgModel, groundModel=groundModel)
WakeDeficitModel.__init__(self, superpositionModel=superpositionModel, rotorAvgModel=rotorAvgModel,
groundModel=groundModel,
use_effective_ws=use_effective_ws, use_effective_ti=use_effective_ti)

@@ -219,0 +221,0 @@

import xarray as xr
from py_wake.deficit_models.deficit_model import XRLUTDeficitModel
from py_wake.deficit_models.deficit_model import XRLUTDeficitModel, ConvectionDeficitModel
from py_wake.utils.model_utils import XRLUTModel
from py_wake import np
from py_wake.superposition_models import LinearSum
from py_wake.superposition_models import LinearSum, WeightedSum
from py_wake.wind_farm_models.engineering_models import All2AllIterative

@@ -10,6 +11,7 @@ from py_wake.turbulence_models.rans_lut_turb import RANSLUTTurbulence

from pathlib import Path
from numpy import newaxis as na
class RANSLUT(All2AllIterative):
def __init__(self, lut, site, windTurbines, rotorAvgModel=None):
def __init__(self, lut, site, windTurbines, convlut=None, rotorAvgModel=None):
"""

@@ -39,10 +41,24 @@ Parameters

lut_lst = [load_lut(lut) for lut in lut_lst]
deficit = RANSLUTDeficit(lut=[lut.deficits for lut in lut_lst], rotorAvgModel=rotorAvgModel)
if convlut is not None:
# Use convection wake deficit model for WeigthedSum superposition
superpositionModel = WeightedSum(weight_limit=1.0)
if not isinstance(convlut, (list, tuple)):
convlut = [convlut]
convlut_lst = convlut
convlut_lst = [load_lut(convlut) for convlut in convlut_lst]
wake_deficit = RANSLUTConvDeficit(lut=[lut.deficits for lut in lut_lst], convlut=convlut_lst, rotorAvgModel=rotorAvgModel, superpositionModel=WeightedSum(weight_limit=1.0))
else:
# Use linear wake superposition
superpositionModel = LinearSum()
wake_deficit = RANSLUTDeficit(lut=[lut.deficits for lut in lut_lst], rotorAvgModel=rotorAvgModel, superpositionModel=LinearSum())
blockage_deficit = RANSLUTDeficit(lut=[lut.deficits for lut in lut_lst], rotorAvgModel=rotorAvgModel, superpositionModel=LinearSum())
turb = RANSLUTTurbulence(lut=[lut.added_ti for lut in lut_lst], rotorAvgModel=rotorAvgModel)
All2AllIterative.__init__(self, site, windTurbines,
wake_deficitModel=deficit,
blockage_deficitModel=deficit,
wake_deficitModel=wake_deficit,
blockage_deficitModel=blockage_deficit,
turbulenceModel=turb,
superpositionModel=LinearSum())
superpositionModel=superpositionModel)

@@ -53,7 +69,7 @@

def __init__(self, lut, rotorAvgModel=None, groundModel=None, use_effective_ws=True,
def __init__(self, lut, superpositionModel=None, rotorAvgModel=None, groundModel=None, use_effective_ws=True,
use_effective_ti=False):
assert use_effective_ws, "RANSLUTDeficit only makes sense when scaling with effective wind speed"
XRLUTDeficitModel.__init__(self, self.get_lut(lut, 'deficits'),
bounds='limit', rotorAvgModel=rotorAvgModel, groundModel=groundModel,
bounds='limit', superpositionModel=superpositionModel, rotorAvgModel=rotorAvgModel, groundModel=groundModel,
use_effective_ws=True, use_effective_ti=use_effective_ti)

@@ -67,4 +83,57 @@

class RANSLUTConvDeficit(RANSLUTModel, XRLUTDeficitModel, ConvectionDeficitModel):
"""Expects LUT velocity deficit and convection variables xarray"""
def __init__(self, lut, convlut, superpositionModel=None, rotorAvgModel=None, groundModel=None, use_effective_ws=True,
use_effective_ti=False):
assert use_effective_ws, "RANSLUTDeficit only makes sense when scaling with effective wind speed"
XRLUTDeficitModel.__init__(self, self.get_lut(lut, 'deficits'),
bounds='limit', superpositionModel=superpositionModel, rotorAvgModel=rotorAvgModel, groundModel=groundModel,
use_effective_ws=True, use_effective_ti=use_effective_ti)
self.sigma_lut = XRLUTModel(self.get_lut([lut.sigma for lut in convlut], 'sigma'),
self.get_conv_input, bounds='limit')
self.center_def_lut = XRLUTModel(self.get_lut([lut.centerline_deficit for lut in convlut], 'centerline_deficit'),
self.get_conv_input, bounds='limit')
def get_conv_input(self, dw_ijlk, D_src_il, TI_eff_ilk, ct_ilk, type_il, **kwargs):
kwargs = dict(x=dw_ijlk / D_src_il[:, na, :, na],
ti=TI_eff_ilk[:, na],
ct=ct_ilk[:, na],
type_i=type_il[:, na, :, na])
return [kwargs[k] for k in self.sigma_lut.da_lst[0].dims]
def sigma_ijlk(self, D_src_il, dw_ijlk, ct_ilk, WS_ref_ijlk, **kwargs):
# dimensional wake expansion
sigma_ijlk = self.sigma_lut.__call__(D_src_il=D_src_il, dw_ijlk=dw_ijlk, ct_ilk=ct_ilk, **kwargs)
# Replace undefined sigma values (upstream of WT) with zeros
sigma_ijlk[:, :, :][dw_ijlk[:, :, :, 0] < 0] = 0.0
return sigma_ijlk * D_src_il[:, na, :, na]
def _calc_deficit(self, D_src_il, dw_ijlk, ct_ilk, WS_ref_ijlk, **kwargs):
# dimensional wake expansion
sigma_sqr_ijlk = (self.sigma_ijlk(D_src_il=D_src_il, dw_ijlk=dw_ijlk, ct_ilk=ct_ilk, WS_ref_ijlk=WS_ref_ijlk, **kwargs))**2
deficit_centre_ijlk = WS_ref_ijlk * np.minimum(1.0, self.center_def_lut.__call__(D_src_il=D_src_il, dw_ijlk=dw_ijlk, ct_ilk=ct_ilk, **kwargs))
# Replace undefined center deficit values (upstream of WT) with zeros
deficit_centre_ijlk[:, :, :][dw_ijlk[:, :, :, 0] < 0] = 0.0
return WS_ref_ijlk, sigma_sqr_ijlk, deficit_centre_ijlk
def calc_deficit_convection(self, D_src_il, dw_ijlk, cw_ijlk, ct_ilk, **kwargs):
if self.groundModel: # pragma: no cover
raise NotImplementedError(
"calc_deficit_convection (WeightedSum) cannot be used in combination with GroundModels")
WS_ref_ijlk, sigma_sqr_ijlk, deficit_centre_ijlk = self._calc_deficit(
D_src_il, dw_ijlk, ct_ilk, **kwargs, **self.get_WS_ref_kwargs(kwargs))
# Convection velocity
uc_ijlk = WS_ref_ijlk - 0.5 * deficit_centre_ijlk
sigma_sqr_ijlk = np.broadcast_to(sigma_sqr_ijlk, deficit_centre_ijlk.shape)
return deficit_centre_ijlk, uc_ijlk, sigma_sqr_ijlk
@property
def args4deficit(self):
return XRLUTModel.args4model.fget(self) | ConvectionDeficitModel.args4deficit.fget(self)
class RANSLUTDemoDeficit(RANSLUTDeficit):
def __init__(self, rotorAvgModel=None, groundModel=None, use_effective_ws=True, use_effective_ti=False):
def __init__(self, superpositionModel=None, rotorAvgModel=None, groundModel=None, use_effective_ws=True, use_effective_ti=False):
# Load default RANS LUT file based on a demo V80 LUT

@@ -74,3 +143,3 @@ demo_lut = ptf('ranslut/V80_ranslut_demo.nc',

RANSLUTDeficit.__init__(self, demo_lut, rotorAvgModel=rotorAvgModel, groundModel=groundModel,
RANSLUTDeficit.__init__(self, demo_lut, superpositionModel=superpositionModel, rotorAvgModel=rotorAvgModel, groundModel=groundModel,
use_effective_ws=use_effective_ws, use_effective_ti=use_effective_ti)

@@ -53,3 +53,4 @@ from py_wake import np

L_inv = self.h_zeta / self.h_ref # 1 / Obukhov length
return (np.log(h[:, na, na] / z0) - psi(h[:, na, na] * L_inv, Cm1=self.Cm1, Cm2=self.Cm2)) / \
h = np.reshape(h, (h.shape + (1, 1))[:3])
return (np.log(h / z0) - psi(h * L_inv, Cm1=self.Cm1, Cm2=self.Cm2)) / \
(np.log(self.h_ref / z0) - psi(self.h_zeta, Cm1=self.Cm1, Cm2=self.Cm2)) * WS_ilk

@@ -56,0 +57,0 @@

@@ -82,3 +82,3 @@ from py_wake import np

def __init__(self, delta=0.01, max_err=1e-3, max_iter=5):
def __init__(self, delta=0.01, max_err=1e-3, max_iter=5, weight_limit=10 ** 10):
# minimum deficit (as fraction of free-stream) to invoke weighted summation

@@ -90,10 +90,12 @@ self.delta = delta

self.max_iter = max_iter
# Limit weights to this value. A limit of 1.0 means not exceeding linear superposition
self.weight_limit = weight_limit
def __call__(self, centerline_deficit_jxxx, WS_xxx,
def __call__(self, deficit_jxxx, WS_xxx,
convection_velocity_jxxx,
sigma_sqr_jxxx, cw_jxxx, hcw_jxxx, dh_jxxx):
sigma_sqr_jxxx, cw_jxxx, hcw_jxxx, dh_jxxx, deficit_centre_jxxx):
Ws = WS_xxx + np.zeros(centerline_deficit_jxxx.shape[1:])
Ws = WS_xxx + np.zeros(deficit_centre_jxxx.shape[1:])
usc = centerline_deficit_jxxx
usc = deficit_centre_jxxx
uc = convection_velocity_jxxx

@@ -107,3 +109,4 @@ sigma_sqr = sigma_sqr_jxxx

# Local deficit
us = usc * np.exp(-1 / (2 * sigma_sqr) * cw**2)
us = usc * np.exp(-1 / (2 * (sigma_sqr + 1e-30)) * cw**2)
us[usc == 0] = 0.0

@@ -115,2 +118,3 @@ # Set lower deficit limit below which deficits are linearly added

ucn = np.ones_like(uc)
# Only start weighting computation if at least two deficits need to be combined

@@ -153,3 +157,4 @@ # Computations are performed where velocities exceed the specified limit (us_lim).

Inz = Uc != 0
ucn[:, Inz] = uc[:, Inz] / Uc[Inz]
# Limit weights to self.weight_limit
ucn[:, Inz] = np.minimum(uc[:, Inz] / Uc[Inz], self.weight_limit)

@@ -200,3 +205,7 @@ # Combined local deficit

count += 1
return Us + np.sum(np.where(~Il, us, 0), axis=0)
# Replace nan with 1.0 for cases where convection variable are undefined (i.e. upstream of wt)
ucn = np.nan_to_num(ucn, nan=1.0)
# Reset weights to linear sum where WeightedSum is not applied
ucn[~Il] = 1.0
return np.sum(deficit_jxxx * ucn, axis=0)

@@ -203,0 +212,0 @@

@@ -14,3 +14,3 @@ import matplotlib.pyplot as plt

from py_wake.utils.profiling import timeit
from py_wake.utils.rans_lut_utils import ADControl, get_Ellipsys_equivalent_output
from py_wake.utils.rans_lut_utils import ADControl, get_Ellipsys_equivalent_output, fit_gauss
from py_wake.wind_farm_models.engineering_models import All2AllIterative

@@ -20,2 +20,3 @@ from py_wake.wind_turbines import WindTurbine

from py_wake.wind_turbines.power_ct_functions import PowerCtTabular
from py_wake.rotor_avg_models.rotor_avg_model import GQGridRotorAvg

@@ -108,3 +109,3 @@

npt.assert_array_almost_equal(aDControl.U_CT_CP_AD, aDControl_saved.U_CT_CP_AD, 10)
npt.assert_array_almost_equal(aDControl.U_CT_CP_AD_ws, aDControl_saved.U_CT_CP_AD_ws, 10)

@@ -225,3 +226,3 @@ wfm = RANSLUT(dataset, site, wts)

npt.assert_array_almost_equal(aDControl.U_CT_CP_AD, aDControl_saved.U_CT_CP_AD, 10)
npt.assert_array_almost_equal(aDControl.U_CT_CP_AD_ws, aDControl_saved.U_CT_CP_AD_ws, 10)

@@ -310,1 +311,75 @@ wfm = RANSLUT(dataset, site, wts)

ellipsys_power, WS_eff_star, ct_star = get_Ellipsys_equivalent_output(sim_res, aDControl)
def test_rans_conv_lut():
# move turbine 1 600 300
wt_x = [-250, 600, -500, 0, 500, -250, 250]
wt_y = [433, 300, 0, 0, 0, -433, -433]
wts = HornsrevV80()
site = UniformSite([1, 0, 0, 0], ti=0.075 * 0.8)
Dref = wts.diameter()
demo_lut = ptf('ranslut/V80_ranslut_demo.nc',
'846213eb655255f6e2201a47c2406f9e77f243f369398cb389bf7320b457dea8')
# Fit Gaussian deficits to hub height profiles for WeightedSum superposition
demo_convlut = 'V80_gaussian.nc'
fit_gauss(demo_lut, 'V80_gaussian.nc', ymin=-10, ymax=10)
wfm = RANSLUT(demo_lut, site, wts, convlut=demo_convlut, rotorAvgModel=GQGridRotorAvg(4, 3))
simres, _ = timeit(wfm.__call__, verbose=0, line_profile=0,
profile_funcs=[GridInterpolator.__call__])(x=wt_x, y=wt_y, wd=[30], ws=[10])
npt.assert_array_almost_equal(simres.WS_eff_ilk.flatten(), [9.97150681, 9.93711854, 7.97991956, 9.99272109, 9.20975831,
7.98196409, 7.46986422])
npt.assert_array_almost_equal(simres.ct_ilk.flatten(), [0.7933989, 0.79388034, 0.80597992, 0.7931019, 0.80406338,
0.80598196, 0.80546986])
ds = xr.load_dataset(demo_lut)
aDControl = ADControl.from_lut(ds.deficits, wts, ws_cutin=4, ws_cutout=25, dws=1.0, cal_TI=0.06)
_, WS_eff_star, ct_star = get_Ellipsys_equivalent_output(simres, aDControl, rotorAvgModel=None)
npt.assert_array_almost_equal(WS_eff_star.flatten(), [7.69759095, 7.66977332, 6.1306985, 7.71475178, 7.08139117, 6.13226504, 5.7398877])
npt.assert_array_almost_equal(ct_star.flatten(), [1.33143388, 1.33273949, 1.36552774, 1.33062844, 1.36035502, 1.36553333, 1.36413403])
def test_rans_conv_lut_multiturbine():
# Test with provided control file
demo_lut = ptf('ranslut/V80_ranslut_demo.nc',
'846213eb655255f6e2201a47c2406f9e77f243f369398cb389bf7320b457dea8')
ds = xr.load_dataset(demo_lut)
# Test multi lut (using the same file for now with 50% wake deficit)
lut_V120 = ds.copy(deep=True)
lut_V120.deficits[:] *= .5
# Fit Gaussian deficits to hub height profiles for WeightedSum superposition
demo_convlut = 'V80_gaussian.nc'
fit_gauss(demo_lut, 'V80_gaussian.nc', ymin=-10, ymax=10, xfits=np.linspace(0.0, 100.0, 11))
lut_V120.to_netcdf('V120.nc')
fit_gauss('V120.nc', 'V120_gaussian.nc', ymin=-10, ymax=10, xfits=np.linspace(0.0, 100.0, 11))
v80 = HornsrevV80()
v120 = WindTurbine('V120', 120, 70, powerCtFunction=PowerCtTabular(
hornsrev1.power_curve[:, 0], hornsrev1.power_curve[:, 1], 'w', hornsrev1.ct_curve[:, 1]))
wts = WindTurbine.from_WindTurbines([v80, v120])
wfm = RANSLUT([ds, lut_V120], UniformSite(ti=0.075 * 0.8), wts, convlut=['V80_gaussian.nc', 'V120_gaussian.nc'], rotorAvgModel=GQGridRotorAvg(4, 3))
type_i = np.array([0, 0, 1, 1])
sim_res = wfm([0, 500, 1000, 1500], [0, 0, 0, 0], type=type_i, wd=[90, 270], ws=10.0)
if 0:
axes = plt.subplots(2, 1)[1]
sim_res.flow_map(wd=90).plot_wake_map(ax=axes[0], levels=np.linspace(3, 10.5))
sim_res.flow_map(wd=270).plot_wake_map(ax=axes[1], levels=np.linspace(3, 10.5))
plt.show()
WS_eff_ref = [[7.3973466, 7.90998823, 8.56042535, 9.97638533], [9.97756182, 7.94966889, 7.78868653, 7.50030898]]
ct_ref = [[0.80539735, 0.80590999, 0.80656043, 0.79333061], [0.79331413, 0.80594967, 0.80578869, 0.80550031]]
power_ref = [[0.5537738, 0.67475722, 0.8641276, 1.33285294], [1.33325883, 0.68412186, 0.64613002, 0.57807292]]
TI_eff_ref = [[0.26789829, 0.21534878, 0.14177617, 0.06003113], [0.0600239, 0.13786305, 0.18521198, 0.23600163]]
npt.assert_array_almost_equal(sim_res.WS_eff_ilk.squeeze().T, WS_eff_ref, 6)
npt.assert_array_almost_equal(sim_res.ct_ilk.squeeze().T, ct_ref, 6)
npt.assert_array_almost_equal(sim_res.power_ilk.squeeze().T * 1e-6, power_ref, 6)
npt.assert_array_almost_equal(sim_res.TI_eff.values.squeeze().T, TI_eff_ref, 6)
aDControl = ADControl.from_lut([ds, lut_V120], wts, ws_cutin=[3, 4], ws_cutout=[26, 25], dws=1.0, cal_TI=0.06)
_, WS_eff_star, ct_star = get_Ellipsys_equivalent_output(sim_res, aDControl, rotorAvgModel=None)
WS_eff_ref = [[5.68432379, 6.07711624, 7.50664894, 8.76770315], [7.70248902, 6.10752006, 6.83079781, 6.57818957]]
ct_star_ref = [[1.36393587, 1.36533666, 1.0488932, 1.0271518], [1.33120399, 1.36544508, 1.04761978, 1.04714416]]
npt.assert_array_almost_equal(WS_eff_star.squeeze().T, WS_eff_ref, 6)
npt.assert_array_almost_equal(ct_star.squeeze().T, ct_star_ref, 6)

@@ -181,2 +181,4 @@ import inspect

from py_wake.superposition_models import LinearSum
from py_wake.deficit_models.deficit_model import XRLUTDeficitModel
from py_wake.deficit_models.rans_lut import RANSLUTDeficit, RANSLUTConvDeficit
from py_wake.turbulence_models.rans_lut_turb import RANSLUTTurbulence

@@ -194,6 +196,6 @@ from py_wake.turbulence_models.turbulence_model import XRLUTTurbulenceModel

[RotorAvgModel], NOJDeficit),
"WakeDeficitModel": ([ConvectionDeficitModel, XRLUTDeficitModel, RANSLUTDeficit], [RotorAvgModel], NOJDeficit),
"WakeDeficitModel": ([ConvectionDeficitModel, XRLUTDeficitModel, RANSLUTDeficit, RANSLUTConvDeficit], [RotorAvgModel], NOJDeficit),
"RotorAvgModel": ([NodeRotorAvgModel], [], None),
"SuperpositionModel": ([], [], LinearSum),
"BlockageDeficitModel": ([XRLUTDeficitModel, RANSLUTDeficit], [], None),
"BlockageDeficitModel": ([XRLUTDeficitModel, RANSLUTDeficit], [RANSLUTConvDeficit], None),
"DeflectionModel": ([DeflectionIntegrator], [], None),

@@ -200,0 +202,0 @@ "TurbulenceModel": ([XRLUTTurbulenceModel, RANSLUTTurbulence], [], None),

@@ -9,2 +9,5 @@ import xarray as xr

from py_wake.utils.most import phi, psi, phi_eps
import sys
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

@@ -33,11 +36,13 @@

class ADControl():
def __init__(self, UAD_CTstar_CPstar_lst, generate_args=None):
self.U_CT_CP_AD = UAD_CTstar_CPstar_lst
def __init__(self, UAD_CTstar_CPstar_ws_lst, generate_args=None):
self.U_CT_CP_AD_ws = UAD_CTstar_CPstar_ws_lst
self.generate_args = generate_args
self.fint_CTstar = []
self.fint_CPstar = []
for UAD, CTstar, CPstar in UAD_CTstar_CPstar_lst:
assert len(UAD) == len(CTstar) == len(CPstar)
self.fint_UAD = []
for UAD, CTstar, CPstar, ws in UAD_CTstar_CPstar_ws_lst:
assert len(UAD) == len(CTstar) == len(CPstar) == len(ws)
self.fint_CPstar.append(interp1d(UAD, CPstar, fill_value=0.0, bounds_error=False))
self.fint_CTstar.append(interp1d(UAD, CTstar, fill_value=0.0, bounds_error=False))
self.fint_UAD.append(interp1d(ws, UAD, fill_value=0.0, bounds_error=False))

@@ -48,5 +53,5 @@ @staticmethod

# the disk averaged velocity UAD provided by the user
UAD_CTstar_CPstar_lst = [np.genfromtxt(ADcontrolfile, skip_header=True).T[:3]
for ADcontrolfile in ADcontrolfiles]
return ADControl(UAD_CTstar_CPstar_lst)
UAD_CTstar_CPstar_ws_lst = [np.genfromtxt(ADcontrolfile, skip_header=True).T[:4]
for ADcontrolfile in ADcontrolfiles]
return ADControl(UAD_CTstar_CPstar_ws_lst)

@@ -57,3 +62,3 @@ def save(self, folder='.'):

for type_, U_CT_CP_AD in enumerate(self.U_CT_CP_AD):
for type_, U_CT_CP_AD_ws in enumerate(self.U_CT_CP_AD_ws):
Dref = windTurbines.diameter(type_)

@@ -67,3 +72,3 @@ cal_TI = cal_TI

# (0.95 * U_CT_CP_AD[0, 0], U_CT_CP_AD[0, 1], U_CT_CP_AD[0, 2], 0.95 * ws[0]))
for (U, CT, CP), ws_ in zip(U_CT_CP_AD.T, ws):
for U, CT, CP, ws_ in U_CT_CP_AD_ws.T:
f.write('%16.14f %16.14f %16.14f %16.14f\n' % (U, CT, CP, ws_))

@@ -147,7 +152,8 @@ # f.write('%16.14f %16.14f %16.14f %16.14f\n' % (ws[-1], 0.0, 0.0, ws[-1]))

ratio = ws / UAD
UAD_CTstar_CPstar = np.array([UAD, cts * ratio ** 2, cps * ratio ** 3])
UAD_CTstar_CPstar[0, 0] = 0.95 * UAD[1]
UAD_CTstar_CPstar[:, -1] = ws[-1], 0.0, 0.0
UAD_CTstar_CPstar_ws = np.array([UAD, cts * ratio ** 2, cps * ratio ** 3, ws])
UAD_CTstar_CPstar_ws[0, 0] = 0.95 * UAD[1]
UAD_CTstar_CPstar_ws[3, 0] = 0.95 * ws[1]
UAD_CTstar_CPstar_ws[:, -1] = ws[-1], 0.0, 0.0, ws[-1]
return UAD_CTstar_CPstar
return UAD_CTstar_CPstar_ws
return ADControl([get_UAD_CTstar_CPstar(t) for t in types], (windTurbines, cal_TI, ws_lst))

@@ -158,2 +164,13 @@

rotorAvgModel=EllipSysPolygonRotorAvg(n_r=9, n_theta=32)):
if rotorAvgModel is not None:
# Use flow map to integrate rotor-averaged wind speed
return get_Ellipsys_equivalent_output_flowmap(sim_res, aDControl, flowmap_maxpoints=flowmap_maxpoints,
rotorAvgModel=rotorAvgModel)
else:
# Interpolate rotor-averaged wind speed, applicable when a rotorAvgModel is used inside RANSLUT model
return get_Ellipsys_equivalent_output_simple(sim_res, aDControl)
def get_Ellipsys_equivalent_output_flowmap(sim_res, aDControl, flowmap_maxpoints=None,
rotorAvgModel=EllipSysPolygonRotorAvg(n_r=9, n_theta=32)):
"""

@@ -249,3 +266,3 @@ Recalculate wt power and thrust by the integrating rotor averaged wind speed

U_tab_lst = [U_CT_CP_AD[0, :] for U_CT_CP_AD in aDControl.U_CT_CP_AD]
U_tab_lst = [U_CT_CP_AD[0, :] for U_CT_CP_AD in aDControl.U_CT_CP_AD_ws[:3]]

@@ -278,1 +295,86 @@ Prated = [wts.power(U_tab,

return power_ilk, WS_eff_star_ilk, ct_star_ilk
def get_Ellipsys_equivalent_output_simple(sim_res, aDControl):
"""
Calculate CTstar, the thrust coefficient based rotor averaged wind speed, U_AD, without
flow_map integration, by interpolating a U_AD, CTstar = f(WS_eff) relation from RANS,
as done in EllipSys.
"""
type_i = sim_res.type.values
I = sim_res.WS_eff_ilk.shape[0]
def get_UAD_ct(i, t):
UAD = aDControl.fint_UAD[t](sim_res.WS_eff_ilk[i, ::])
ct_star = aDControl.fint_CTstar[t](UAD)
return UAD, ct_star
if np.all(type_i == 0):
WS_eff_star_ilk, ct_star_ilk = map(np.array, get_UAD_ct(slice(None), 0))
else:
WS_eff_star_ilk, ct_star_ilk = map(np.array, zip(*[get_UAD_ct(i, type_i[i]) for i in range(I)]))
# Set ct to zero for wts that were originally shut down
operating = sim_res.power_ilk > 0
ct_star_ilk *= operating
return sim_res.power_ilk, WS_eff_star_ilk, ct_star_ilk
def gauss(x, a, x0, sigma):
return a * np.exp(-(x - x0) ** 2 / (2 * sigma ** 2))
def fit_gauss(path_LUT, database_name, ymin=-10, ymax=10, xfits=None):
# Fit a Gaussian for each single wake shape and store sigma and magnitude
ds = xr.open_dataset(path_LUT).interp(z=0.0)
ds = ds.where(ds.y >= ymin, drop=True).where(ds.y <= ymax, drop=True)
if xfits is None:
xmin = 2.0
xmax = 100.0
xfits = np.insert(np.append(ds.x.where(ds.x > xmin, drop=True).where(ds.x < xmax, drop=True), xmax), 0, 0.0)
centerline_deficit = np.zeros((len(ds.ct), len(ds.ti), len(xfits) + 1))
sigma = np.zeros((len(ds.ct), len(ds.ti), len(xfits) + 1))
for k in range(len(ds.ct)):
for j in range(len(ds.ti)):
deficit = ds.isel(ct=k, ti=j)
for i in range(len(xfits)):
u = deficit.interp(x=xfits[i])
try:
# Substract outer values to make fit possible when velocity deficit at hub height is completely negative
# in the very far wake due to vertical wake center displacement
popt, pcov = curve_fit(gauss, u.y, u.deficits - u.deficits.isel(y=0), p0=[1.0, 0.0, 1.0], maxfev=800)
except Exception: # pragma: no cover
plt.plot(u.y, u.deficits, 'b+:', label='data')
plt.plot(u.y, gauss(u.y, *popt), 'ro:', label='fit')
plt.savefig('fittest.pdf')
sys.exit()
centerline_deficit[k, j, i] = popt[0]
sigma[k, j, i] = popt[2]
# Limit centerline deficit in near wake to 1D momentum value, 2*a_x, since fitting a gaussian to a non-gaussian near wake
# is prone to overpredicting the deficit
centerline_deficit[k, :, :] = np.minimum(centerline_deficit[k, :, :], (1.0 - np.sqrt(1.0 - np.minimum(ds.ct.isel(ct=k).values, 1.0)))[na, na])
# Likewise, limit deficit at x=0 with 1D momentum value, a_x
centerline_deficit[k, :, 0] = np.minimum(centerline_deficit[k, :, 0], (0.5 * (1.0 - np.sqrt(1.0 - np.minimum(ds.ct.isel(ct=k).values, 1.0))))[na])
centerline_deficit[:, :, -1] = centerline_deficit[:, :, -2]
sigma[:, :, -1] = sigma[:, :, -2]
xfits = np.append(xfits, 10**10)
dsout = xr.Dataset({'centerline_deficit': xr.DataArray(centerline_deficit,
[('ct', ds.ct.values),
('ti', ds.ti.values),
('x', xfits)],
attrs={'long_name': 'Normalized streamwise centerline deficit from fitted Gaussian', 'units': '[-]'}),
'sigma': xr.DataArray(sigma,
[('ct', ds.ct.values),
('ti', ds.ti.values),
('x', xfits)],
attrs={'long_name': 'Standard deviation normalized by rotor diameter from fitted Gaussian', 'units': '[-]'})})
encoding = {}
vars = ['centerline_deficit', 'sigma']
for var in vars:
encoding[var] = {'dtype': 'float32'}
vars = ['x', 'ct', 'ti']
for var in vars:
encoding[var] = {'dtype': 'float64'}
# Write netcdf file
dsout.to_netcdf(database_name, encoding=encoding)

@@ -31,5 +31,5 @@ # file generated by setuptools-scm

__version__ = version = '2.6.16'
__version_tuple__ = version_tuple = (2, 6, 16)
__version__ = version = '2.6.17'
__version_tuple__ = version_tuple = (2, 6, 17)
__commit_id__ = commit_id = None

@@ -8,3 +8,2 @@ from abc import ABC, abstractmethod

from scipy.interpolate import RegularGridInterpolator as RGI
from tqdm import tqdm

@@ -153,3 +152,3 @@ from py_wake.flow_map import Points

from py_wake.site.streamline_distance import StreamlineDistance
return cls(name, ds, windFarmModel.windTurbines, wt_x, wt_y,
return cls(name, ds.sortby('wd'), windFarmModel.windTurbines, wt_x, wt_y,
relative_distance=not isinstance(windFarmModel.site.distance, StreamlineDistance))

@@ -221,3 +220,3 @@

from py_wake.site.streamline_distance import StreamlineDistance
return cls(name, ds, windFarmModel.windTurbines, wt_x, wt_y,
return cls(name, ds.sortby('wd'), windFarmModel.windTurbines, wt_x, wt_y,
relative_distance=not isinstance(windFarmModel.site.distance, StreamlineDistance))

@@ -224,0 +223,0 @@

+15
-15

@@ -8,6 +8,6 @@ docs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

py_wake/flow_map.py,sha256=pZCV-mbPDtVLuvdGejKJhUnR52VorJ7fobP10Oj7ZL4,22441
py_wake/superposition_models.py,sha256=mlZSy-T6YiAhdSgAu-reP1YECRsPpgHh8GDCfPAsZ6w,10124
py_wake/version.py,sha256=w6Wi-Vv2osckU1Br_lqmghSD8v0m4QyQ_pMHdGIEQi4,706
py_wake/superposition_models.py,sha256=wJ-JJElwdWiNOwv1v0NXjbTV1ZbbBSmC-GJziO1YPJs,10669
py_wake/version.py,sha256=NauhVIERykrLkuAo4rI2RyJQXOSgmqEnQesJmE0DWyY,706
py_wake/deficit_models/__init__.py,sha256=w1S4F3FmAgtwqGO90fASYoqxOb6SsgjbasiD3V8_1uk,709
py_wake/deficit_models/deficit_model.py,sha256=iLb9wUO8wDeYAEBAoAlS2W4Q4B16ZSO0L4DhVq3NxYQ,10280
py_wake/deficit_models/deficit_model.py,sha256=ly8A6pZQ6kt39fzsAz9OVw_5ohr7b26I5iHRYni2QrM,10466
py_wake/deficit_models/fuga.py,sha256=pZ9wUI5VBPcJM-2FWGUVYwQIQ6oWKFlOw0qSyu6V4hI,13907

@@ -20,3 +20,3 @@ py_wake/deficit_models/gaussian.py,sha256=_ntryBrucuo9EMSLnyJH3dt9mM9SnB0DxP9wzH_w7Zo,36119

py_wake/deficit_models/rankinehalfbody.py,sha256=idu2_roqZQWDzkcvRMhUdv4-KWuVUlCgSsxb2N2wbm8,5408
py_wake/deficit_models/rans_lut.py,sha256=0zSDrQSgtjjhjzo0-Oiz5UFRN2vecJrT1hVfkq6c_jQ,3451
py_wake/deficit_models/rans_lut.py,sha256=-sFLM53WbH0N8NgF9bdVa9mDxVZC2STLkdVlHK0Yyg8,7918
py_wake/deficit_models/rathmann.py,sha256=CO2zQXCrjiyoh9Q3Pds8ddHKL_G5M1DEOLnGz9SeOgI,15368

@@ -289,3 +289,3 @@ py_wake/deficit_models/selfsimilarity.py,sha256=l4hFh9l4WkWlJxgEhkXMnGSmiSAXCQlg_7_VaYEnmoc,13534

py_wake/site/jit_streamline_distance.py,sha256=GJo2mn_bTjuGX3-gSGkv1dkRfo7hGDVKDmefRWYk5o8,159
py_wake/site/shear.py,sha256=HVrUmwzUiTAEKGP6eVJQKOODv1FCbI30CJaPSMj4H0s,4248
py_wake/site/shear.py,sha256=7Nzg3wBTAHIknX_yLhq5VNZGNIy0oR5rJfk2BWb5wMY,4276
py_wake/site/streamline_distance.py,sha256=01NdwM__ITY0w4kWavUx1arKxCvtc42cEteugQhN7mo,4518

@@ -310,3 +310,3 @@ py_wake/site/wasp_grid_site.py,sha256=d-vonrkXMuVVgbq9OG0ri3XHYekdDfvfjdndkigCOrQ,10227

py_wake/tests/test_deficit_models/test_noj.py,sha256=qJVv3q3Lp_QILFn-6xdslWevvR4Dmnx0AWj39XCIdXw,5495
py_wake/tests/test_deficit_models/test_rans_lut.py,sha256=gpskfpoK6HBpW-8dFB5dHgD5NKtrZle_2q8ftx8fmlM,17135
py_wake/tests/test_deficit_models/test_rans_lut.py,sha256=9u94ZVzzGIRZEdJ0CftcN8LX1htqoXrw_U8y35Hi2eM,21881
py_wake/tests/test_deficit_models/test_selfsimilarity.py,sha256=mI5DUstBbaC36JQ-eCOMrE88jubvZjb3tp_DUQQzU2Y,4446

@@ -407,3 +407,3 @@ py_wake/tests/test_deflection_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

py_wake/utils/maps.py,sha256=cvz1j14Is_EdsXX4wkautCXm_OcDaSwDk4a6JJdhGs4,593
py_wake/utils/model_utils.py,sha256=UP3H4XlAQsE45zl3QFje1zroF7OaqCUDEm6TZ81OrEc,15521
py_wake/utils/model_utils.py,sha256=BdM_RoEFXfxP2Dha2TUPvqJalhKznBPdLouePR1VkTU,15713
py_wake/utils/most.py,sha256=9DEZ5C06qdttfGdfUCyaFCGt-YT8xPnQ3URIuHpbcdE,1208

@@ -415,3 +415,3 @@ py_wake/utils/notebook_extensions.py,sha256=E0UOiQ1AAMaIbnnzsyj-gGv8oRWD8wAvHsj2BN10LXU,704

py_wake/utils/profiling.py,sha256=g6aDNtZg_7lcxUg4WdAawGMysaoZW87iPyTWwiPvWdA,4668
py_wake/utils/rans_lut_utils.py,sha256=-zs25tCdg2heaoCjyEDdk0vSDTImdhjtTpbDH_YEClI,12423
py_wake/utils/rans_lut_utils.py,sha256=BjEurt2nM7ZO2VLQN1bDdaCtig1N3ksqafYcxlOTQxQ,17596
py_wake/utils/streamline.py,sha256=QSfp_kLVxihTlWq3P0AHvpf_OS-QUIj17xoNhhI8QXk,2835

@@ -427,4 +427,4 @@ py_wake/utils/tensorflow_surrogate_utils.py,sha256=QdIPqxoYiuXFvieHQyP-TE6gS8syXbxoUZ1liCBFil0,7198

py_wake/wind_farm_models/__init__.py,sha256=cl0nC9vqhXgpRuaCFkQpDET2Yyzbwnd5Kv2wgZHUI6E,137
py_wake/wind_farm_models/engineering_models.py,sha256=Mq3kh5aFfO4YSrBHzg3ukPN-RBZb8XKBMT9vJESptp4,63776
py_wake/wind_farm_models/external_wind_farm_models.py,sha256=QRSwdq45J4hwmYANBCv87HFGAho04_gkTWY9rUUUazM,12587
py_wake/wind_farm_models/engineering_models.py,sha256=co-te7NzyLkz3PchizBj2_-YGU6wFlWl6ZbnTgKSLdg,64395
py_wake/wind_farm_models/external_wind_farm_models.py,sha256=bFAmIJ7bRHEZEIo_wyAabefw4OYNKZ1-p5OoSAca7Ok,12591
py_wake/wind_farm_models/minimalistic_wind_farm_model.py,sha256=9BepPjAo_WDMcG4feUQo7YDY2WSFJOpNMmzgT0yNBn4,11160

@@ -438,6 +438,6 @@ py_wake/wind_farm_models/wind_farm_model.py,sha256=8rQ0tbfZDMs8_9Ag_c1UQz28O7xF7tEBS43wey_BWfs,42573

py_wake/wind_turbines/wind_turbines_deprecated.py,sha256=HpNmBR8CJL4-8JBaygDI0t086qfw5bR2DOQI8Ox4AZ4,6250
py_wake-2.6.16.dist-info/licenses/LICENSE,sha256=XE2CGPqQgzSXqIajXpAVYJ5SRNmaWOIeMePK6MocsuY,1084
py_wake-2.6.16.dist-info/METADATA,sha256=gVr6wRfNyRfmVfe0Mbzgi9H8-1EDANsVPMZO5O-SI5Q,3760
py_wake-2.6.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
py_wake-2.6.16.dist-info/top_level.txt,sha256=GsaXU4YwyMkZZ6dkb4h0FMc5RaLIT2Qns_YoScKoXdk,20
py_wake-2.6.16.dist-info/RECORD,,
py_wake-2.6.17.dist-info/licenses/LICENSE,sha256=XE2CGPqQgzSXqIajXpAVYJ5SRNmaWOIeMePK6MocsuY,1084
py_wake-2.6.17.dist-info/METADATA,sha256=bA0FK31ceJP7fXVWs2kbF5heDUstSVqr3ZgQmmbYy_E,3760
py_wake-2.6.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
py_wake-2.6.17.dist-info/top_level.txt,sha256=GsaXU4YwyMkZZ6dkb4h0FMc5RaLIT2Qns_YoScKoXdk,20
py_wake-2.6.17.dist-info/RECORD,,

Sorry, the diff of this file is too big to display