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.13
to
2.6.14
+101
py_wake/site/streamline_distance.py
import warnings
import matplotlib.pyplot as plt
from numpy import newaxis as na
from py_wake import np
from py_wake.deficit_models.noj import NOJ
from py_wake.examples.data.hornsrev1 import V80
from py_wake.examples.data.ParqueFicticio._parque_ficticio import ParqueFicticioSite
from py_wake.flow_map import XYGrid
from py_wake.site.distance import StraightDistance
from py_wake.utils.model_utils import DeprecatedModel
from py_wake.utils.streamline import VectorField3D
class StreamlineDistance(StraightDistance):
"""Just-In-Time Streamline Distance
Calculates downwind crosswind and vertical distance along streamlines.
Streamlines calculated in each call
"""
def __init__(self, vectorField, step_size=20):
"""Parameters
----------
vectorField : VectorField3d
step_size : int for float
Size of linear streamline steps
"""
StraightDistance.__init__(self, wind_direction='wd')
self.vectorField = vectorField
self.step_size = step_size
def __call__(self, src_x_ilk, src_y_ilk, src_h_ilk, wd_l=None, WD_ilk=None, time=None, dst_xyh_jlk=None):
(src_x_ilk, src_y_ilk, src_h_ilk), (dst_x_jlk, dst_y_jlk, dst_h_jlk) = self.get_pos(
src_x_ilk, src_y_ilk, src_h_ilk, wd_l, WD_ilk, dst_xyh_jlk)
assert src_x_ilk.shape[2] == 1, 'StreamlineDistance does not support flowcase dependent positions'
start_points_m = np.moveaxis([v[:, :, 0].flatten() for v in [src_x_ilk, src_y_ilk, src_h_ilk]], 0, -1)
dw_ijlk, hcw_ijlk, dh_ijlk = StraightDistance.__call__(self, src_x_ilk, src_y_ilk, src_h_ilk, wd_l=wd_l,
dst_xyh_jlk=dst_xyh_jlk)
src_z_ilk = self.site.elevation(src_x_ilk, src_y_ilk)
dst_z_jlk = self.site.elevation(dst_x_jlk, dst_y_jlk)
dz_ijlk = dst_z_jlk[na, :] - src_z_ilk[:, na]
# +0 ~ autograd safe copy (broadcast_to returns readonly array)
dh_ijlk = np.broadcast_to(dh_ijlk, dw_ijlk.shape) + 0.
dz_ijlk = np.broadcast_to(dz_ijlk, dw_ijlk.shape) + 0.
I, J, L, K = dw_ijlk.shape
dw_mj, hcw_mj, dh_mj, dz_mj = [np.moveaxis(v, 1, 2).reshape(I * L, J)
for v in [dw_ijlk, hcw_ijlk, dh_ijlk, dz_ijlk]]
wd_m = np.tile(wd_l, I)
stream_lines = self.vectorField.stream_lines(wd_m, time=time, start_points=start_points_m, dw_stop=dw_mj.max(1),
step_size=self.step_size)
dxyz = np.diff(np.concatenate([stream_lines[:, :1], stream_lines], 1), 1, -2)
length_is = np.cumsum(np.sqrt(np.sum(dxyz**2, -1)), -1)
dist_xyz = stream_lines - start_points_m[:, na]
t = np.deg2rad(270 - wd_m)[:, na]
dw_is = dist_xyz[:, :, 0] * np.cos(t) + dist_xyz[:, :, 1] * np.sin(t)
hcw_is = dist_xyz[:, :, 0] * np.sin(t) - dist_xyz[:, :, 1] * np.cos(t)
for m, (dw_j, dw_s, hcw_s, dh_s, length_s) in enumerate(
zip(dw_mj, dw_is, hcw_is, dist_xyz[:, :, 2], length_is)):
dw = dw_j > 0
hcw_mj[m, dw] += np.interp(dw_j[dw], dw_s, hcw_s)
dh_mj[m, dw] -= np.interp(dw_j[dw], dw_s, dh_s)
dw_mj[m, dw] = np.interp(dw_j[dw], dw_s, length_s)
# streamline dh contains absolute height different, but pywake needs differences relative to ground, so
# we need to subtract elevation differences, dz
dh_mj += dz_mj
return [np.moveaxis(v.reshape((I, L, J, 1)), 2, 1) for v in [dw_mj, hcw_mj, dh_mj]]
def main():
if __name__ == '__main__':
wt = V80()
vf3d = VectorField3D.from_WaspGridSite(ParqueFicticioSite())
site = ParqueFicticioSite(distance=StreamlineDistance(vf3d))
x, y = site.initial_position[:].T
wfm = NOJ(site, wt)
wd = 330
sim_res = wfm(x, y, wd=[wd], ws=10)
fm = sim_res.flow_map(XYGrid(x=np.linspace(site.ds.x[0].item(), site.ds.x[-1].item(), 500),
y=np.linspace(site.ds.y[0].item(), site.ds.y[-1].item(), 500)))
stream_lines = vf3d.stream_lines(wd=np.full(x.shape, wd), start_points=np.array([x, y, np.full(x.shape, 70)]).T,
dw_stop=y - 6504700)
fm.plot_wake_map()
for sl in stream_lines:
plt.plot(sl[:, 0], sl[:, 1])
plt.show()
main()
import os
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import xarray as xr
from numpy import newaxis as na
from py_wake.deficit_models import BastankhahGaussianDeficit, SelfSimilarityDeficit2020
from py_wake.examples.data.hornsrev1 import V80, Hornsrev1Site
from py_wake.flow_map import Points, XYGrid
from py_wake.site.streamline_distance import StreamlineDistance
from py_wake.superposition_models import LinearSum
from py_wake.tests import npt
from py_wake.utils import layouts
from py_wake.utils.plotting import setup_plot
from py_wake.utils.profiling import profileit, timeit
from py_wake.utils.streamline import VectorField3D
from py_wake.wind_farm_models import All2AllIterative, PropagateDownwind
from py_wake.wind_farm_models.external_wind_farm_models import (
ExternalWFMWindFarm,
ExternalWindFarm,
ExternalXRAbsWindFarm,
ExternalXRRelWindFarm,
)
from py_wake.wind_turbines import WindTurbines
def get_wfm(externalWindFarms=[], wfm_cls=PropagateDownwind, site=Hornsrev1Site(), blockage=False):
windTurbines = WindTurbines.from_WindTurbine_lst([V80()] * 6)
windTurbines._names = ["Current WF"] + [f"WF{i + 1}" for i in np.arange(5)]
kwargs = dict(site=site, windTurbines=windTurbines,
wake_deficitModel=BastankhahGaussianDeficit(use_effective_ws=True),
superpositionModel=LinearSum(),
externalWindFarms=externalWindFarms)
if blockage:
wfm_cls = All2AllIterative
kwargs['blockage_deficitModel'] = SelfSimilarityDeficit2020()
return wfm_cls(**kwargs)
def setup_ext_farms(cls, neighbour_x_y_angle, wfm=get_wfm(), include_wd_range=np.arange(-45, 45)):
ext_farms = []
for i, (x, y, angle) in enumerate(neighbour_x_y_angle, 1):
wd_lst = (include_wd_range + angle) % 360 # relevant wind directions
name = f'WF{i}'
if cls is ExternalWFMWindFarm:
ext_wf = ExternalWFMWindFarm(name, wfm, x, y, include_wd=wd_lst)
elif cls is ExternalXRRelWindFarm:
# Coarse grid in relative downwind, crosswind and vertical direction
grid_xyh = (np.linspace(1200, 2500, 12),
np.linspace(-1000, 1000, 20),
np.array([0]))
ext_wf = ExternalXRRelWindFarm.generate(name, grid_xyh, wfm, wt_x=x, wt_y=y, wd=wd_lst)
elif cls is ExternalXRAbsWindFarm:
# coarse grid in East, North and vertical direction covering the current wind farm
e = 500
grid_xyh = (np.linspace(- e, e, 20),
np.linspace(- e, e, 20),
wfm.windTurbines.hub_height())
ext_wf = ExternalXRAbsWindFarm.generate(name, grid_xyh, wfm, wt_x=x, wt_y=y, wd=wd_lst)
ext_farms.append(ext_wf)
return ext_farms
def test_aep():
# setup current, neighbour and all positions
wf_x, wf_y = layouts.circular([1, 5], 400)
wts = WindTurbines.from_WindTurbine_lst([V80()] * 6)
wts._names = ["Current WF"] + [f"WF{i + 1}" for i in np.arange(5)]
No_neighbours = 3
neighbour_x_y_angle = [(wf_x + 2000 * np.cos(d), wf_y + 2000 * np.sin(d), (90 - np.rad2deg(d)) % 360)
for d in np.pi + np.linspace(0, np.pi / 2, No_neighbours)]
neighbour_x, neighbour_y, _ = zip(*neighbour_x_y_angle)
all_x, all_y = np.r_[wf_x, np.array(neighbour_x).flatten()], np.r_[wf_y, np.array(neighbour_y).flatten()]
for wfm_cls in [PropagateDownwind, All2AllIterative]:
def run():
return get_wfm(wfm_cls=wfm_cls)(all_x, all_y, type=0).aep().isel(wt=np.arange(len(wf_x)))
aep_ref, t = timeit(run)()
# print(wfm_cls.__name__, np.mean(t), aep_ref.sum().item())
ext_cls_lst = [ExternalWFMWindFarm, ExternalXRAbsWindFarm, ExternalXRRelWindFarm]
for cls in ext_cls_lst:
def run():
ext_farm = setup_ext_farms(cls, neighbour_x_y_angle)
wfm = get_wfm(ext_farm, wfm_cls=wfm_cls)
if 0:
wfm(wf_x, wf_y, type=0, wd=254, ws=10).flow_map().plot_wake_map()
plt.title(f'{wfm_cls.__name__}, {cls.__name__}')
plt.show()
return wfm(wf_x, wf_y, type=0).aep().isel(wt=np.arange(len(wf_x)))
aep, t = timeit(run, )()
err_msg = f'{wfm_cls.__name__}, {cls.__name__}'
atol = {ExternalXRAbsWindFarm: 0.0006, ExternalXRRelWindFarm: 0.001}.get(cls, 1e-6)
# print(err_msg, np.mean(t), aep.sum().item())
npt.assert_allclose(aep.sum().item(), aep_ref.sum().item(), atol=atol, err_msg=err_msg)
npt.assert_allclose(aep.values, aep_ref.values, atol=atol, err_msg=err_msg)
def test_functionality():
wf_x, wf_y = layouts.circular([1, 5], 400)
wts = WindTurbines.from_WindTurbine_lst([V80()] * 6)
wts._names = ["Current WF"] + [f"WF{i + 1}" for i in np.arange(5)]
No_neighbours = 3
neighbour_x_y_angle = [(wf_x + 2000 * np.cos(d), wf_y + 2000 * np.sin(d), (90 - np.rad2deg(d)) % 360)
for d in np.pi + np.linspace(0, np.pi / 2, No_neighbours)]
neighbour_x, neighbour_y, _ = zip(*neighbour_x_y_angle)
all_x, all_y = np.r_[wf_x, np.array(neighbour_x).flatten()], np.r_[wf_y, np.array(neighbour_y).flatten()]
ext_farm = setup_ext_farms(ExternalWFMWindFarm, neighbour_x_y_angle)
wfm = get_wfm(ext_farm)
wfm(wf_x, wf_y, wd=234, ws=10).flow_map().plot_wake_map()
if 0:
plt.show()
plt.close('all')
npt.assert_array_equal([wd for wd in np.arange(360) if ext_farm[0].include_wd_func(wd)], np.arange(225, 315))
ext_farm[0].set_include_wd(np.arange(225, 300))
npt.assert_array_equal([wd for wd in np.arange(360) if ext_farm[0].include_wd_func(wd)], np.arange(225, 300))
ext_farm[0].set_include_wd(lambda wd: 235 <= wd <= 245)
npt.assert_array_equal([wd for wd in np.arange(360) if ext_farm[0].include_wd_func(wd)], np.arange(235, 246))
npt.assert_array_equal(ext_farm[0].get_relevant_wd((wf_x, wf_y, wts.hub_height())), np.arange(236, 305))
npt.assert_array_equal(ext_farm[0].get_relevant_wd((wf_x, wf_y, wts.hub_height()), ws=18), np.arange(237, 304))
npt.assert_array_equal(ext_farm[0].get_relevant_wd((wf_x, wf_y, wts.hub_height()), tol=1e-3), np.arange(240, 301))
ext_farm = setup_ext_farms(ExternalXRAbsWindFarm, neighbour_x_y_angle[:1])
assert ext_farm[0].to_xarray().deficit.shape == (20, 20, 1, 90, 23)
def test_cluster_interaction():
wf_x, wf_y = layouts.circular([1, 5, 12, 18], 1800)
No_neighbours = 2
neighbour_x_y_angle = [(wf_x - 6000 * i, wf_y, 270) for i in range(1, No_neighbours + 1)]
neighbour_x, neighbour_y, _ = zip(*neighbour_x_y_angle)
all_x, all_y = np.r_[wf_x, np.array(neighbour_x).flatten()], np.r_[wf_y, np.array(neighbour_y).flatten()]
types = [v for i in range(No_neighbours + 1) for v in [i] * len(wf_x)]
wd = 270
wfm_ref = get_wfm()
sim_res_ref = wfm_ref(all_x, all_y, type=types, wd=wd, ws=10)
ext_farms = setup_ext_farms(ExternalWFMWindFarm, neighbour_x_y_angle)
wfm = get_wfm(ext_farms)
sim_res_ext = wfm(wf_x, wf_y, type=0, wd=wd, ws=10)
if 0:
grid = XYGrid(x=np.linspace(-15000, 3000, 150), y=np.linspace(-2500, 2500, 100))
fm_ref = sim_res_ref.flow_map(grid)
fm_ref.plot_wake_map(levels=np.linspace(4, 10, 50))
setup_plot(grid=False, figsize=(12, 3), axis='scaled')
plt.show()
npt.assert_allclose(sim_res_ext.Power[:len(wf_x)].sum().item(),
sim_res_ref.Power[:len(wf_x)].sum().item(), atol=26000)
def test_cluster_blockage():
wf_x, wf_y = layouts.circular([1, 5, 12, 18], 1800)
No_neighbours = 1
neighbour_x_y_angle = [(wf_x - 6000 * i, wf_y, 270) for i in range(1, No_neighbours + 1)]
neighbour_x, neighbour_y, _ = zip(*neighbour_x_y_angle)
all_x, all_y = np.r_[wf_x, np.array(neighbour_x).flatten()], np.r_[wf_y, np.array(neighbour_y).flatten()]
types = [v for i in range(No_neighbours + 1) for v in [i] * len(wf_x)]
wfm_ref = get_wfm(blockage=True)
sim_res_ref = wfm_ref(all_x, all_y, type=types, wd=270, ws=10)
ext_farms = setup_ext_farms(ExternalWFMWindFarm, neighbour_x_y_angle, wfm=wfm_ref)
wfm = get_wfm(ext_farms, blockage=True)
sim_res_ext = wfm(wf_x, wf_y, type=0, wd=270, ws=10)
# all WT without blockage
npt.assert_allclose(sim_res_ref.Power[:len(wf_x)].sum().item(),
get_wfm()(all_x, all_y, wd=270, ws=10).Power[:len(wf_x)].sum().item(), atol=9000)
# Current + external farms with blockage
npt.assert_allclose(sim_res_ref.Power[:len(wf_x)].sum().item(),
sim_res_ext.Power[:len(wf_x)].sum().item(), atol=400)
def test_streamlines():
wf_x, wf_y = layouts.circular([1, 5], 400)
H = 70
class MyVectorField(VectorField3D):
def __init__(self):
pass
def __call__(self, wd, time, x, y, h):
turning = (x + 1000) / 50
theta = np.deg2rad(270 - wd + turning)
return np.array([np.cos(theta), np.sin(theta), theta * 0]).T
vf3d = MyVectorField()
site = Hornsrev1Site()
site_with_streamlines = Hornsrev1Site()
site_with_streamlines.distance = StreamlineDistance(vf3d)
wfm_ref = get_wfm(site=site_with_streamlines)
No_neighbours = 1
neighbour_x_y_angle = (wf_x - 2000, wf_y, 270)
neighbour_x, neighbour_y, _ = neighbour_x_y_angle
all_x, all_y = np.r_[wf_x, np.array(neighbour_x).flatten()], np.r_[wf_y, np.array(neighbour_y).flatten()]
types = [v for i in range(No_neighbours + 1) for v in [i] * len(wf_x)]
grid = XYGrid(x=np.linspace(-4000, 2000, 150), y=np.linspace(-1500, 1500, 200))
sim_res_ref = wfm_ref(all_x, all_y, type=types, wd=270, ws=10)
df = pd.DataFrame({'Power current WF [MW]': []})
P_ref = sim_res_ref.Power[:len(wf_x)].sum().item()
if 0:
fm_ref = sim_res_ref.flow_map(grid)
fm_ref.plot_wake_map()
stream_lines = vf3d.stream_lines(wd=np.full_like(neighbour_x, 270), start_points=np.array([neighbour_x, neighbour_y, np.full_like(neighbour_x, 70)]).T,
dw_stop=np.full_like(neighbour_x, 2000))
for sl in stream_lines:
plt.plot(sl[:, 0], sl[:, 1], 'k', lw=1, alpha=0.1)
setup_plot(axis='scaled', xlabel='', ylabel='', xlim=[-4000, 2000], grid=0, figsize=(12, 4))
plt.show()
for cls in [ExternalWFMWindFarm, ExternalXRAbsWindFarm, ExternalXRRelWindFarm]:
fig, axes = plt.subplots(2, 1, figsize=(8, 6))
for i, (ax, site_setup) in enumerate(zip(axes, [site_with_streamlines, site]), 1):
# setup external wind farm model
# - option 1: with streamlines
# - option 2: without streamlines
ext_farms = setup_ext_farms(cls, [neighbour_x_y_angle], wfm=get_wfm(site=site_setup),
include_wd_range=np.array([0]))
# Setup simulation using site with stream lines
wfm = get_wfm(ext_farms, site=site_with_streamlines)
sim_res_ext = wfm(wf_x, wf_y, type=0, wd=270, ws=10)
s = f'{cls.__name__}, option {i}'
if i == 2:
rtol = 0.05
else:
rtol = {ExternalWFMWindFarm: 1e-7,
ExternalXRAbsWindFarm: 0.0005,
ExternalXRRelWindFarm: 0.009}[cls]
npt.assert_allclose(sim_res_ext.Power[:len(wf_x)].sum().item(), P_ref, rtol=rtol, err_msg=s)
if 0:
fm = sim_res_ext.flow_map(grid)
fm.plot_wake_map(ax=ax)
if site_setup == site_with_streamlines:
start_points, alpha = np.array(
[ext_farms[0].wt_x, ext_farms[0].wt_y, ext_farms[0].wt_x * 0 + H]).T, 0.2
else:
start_points, alpha = np.array([[ext_farms[0].wf_x, ext_farms[0].wf_y, 70]]), .5
stream_lines = vf3d.stream_lines(wd=[270], start_points=start_points,
dw_stop=np.full(start_points[:, 0].shape, 2000))
for sl in stream_lines:
ax.plot(sl[:, 0], sl[:, 1], alpha=alpha)
setup_plot(ax=ax, axis='scaled', xlabel='', ylabel='', title=f'{cls.__name__}, option {i}', grid=0)
# plt.show()
plt.close('all')
from abc import ABC, abstractmethod
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
from numpy import newaxis as na
from scipy.interpolate import RegularGridInterpolator as RGI
from tqdm import tqdm
from py_wake.flow_map import Points
from py_wake.site.distance import StraightDistance
from py_wake.utils.gradients import item_assign
from py_wake.utils.model_utils import Model
from py_wake.utils.xarray_utils import sel_interp_all
class ExternalWindFarm(Model, ABC):
def __init__(self, name, windTurbines, wt_x, wt_y, wt_h=None):
self.name = name
self.wt_x = wt_x
self.wt_y = wt_y
self.wf_x = np.mean([np.min(wt_x), np.max(wt_x)])
self.wf_y = np.mean([np.min(wt_y), np.max(wt_y)])
if wt_h is None:
self.wt_h = windTurbines.hub_height(windTurbines.types())
self.wf_h = np.mean(self.wt_h)
self.windTurbines = windTurbines
def set_include_wd(self, include_wd):
if callable(include_wd):
self.include_wd_func = include_wd
else:
assert isinstance(include_wd, (int, float, list, tuple, np.ndarray))
def include_wd_func(wd):
return np.round(wd) in set(np.asarray(include_wd).tolist())
self.include_wd_func = include_wd_func
def rel2abs(self, dw, hcw, dh, WD):
theta = np.deg2rad(270 - WD)
co, si = np.cos(theta), np.sin(theta)
x = co * dw - hcw * si + self.wf_x
y = si * dw + hcw * co + self.wf_y
h = dh + self.wf_h
return x, y, h
def plot(self, ax=None):
ax = ax or plt.gca()
ax.plot(self.wt_x, self.wt_y, '1', label=self.name)
@abstractmethod
def __call__(self):
""
class ExternalWFMWindFarm(ExternalWindFarm):
def __init__(self, name, windFarmModel, wt_x, wt_y, wt_h=None, type=0, include_wd=np.arange(360), **kwargs):
self.wfm = windFarmModel
self.wfm_kwargs = {**kwargs, 'x': wt_x, 'y': wt_y, 'h': wt_h, 'type': type}
self.set_include_wd(include_wd)
ExternalWindFarm.__init__(self, name, windFarmModel.windTurbines, wt_x, wt_y, wt_h)
def get_relevant_wd(self, target_xyh, ws=10, tol=1e-6):
x, y, h = target_xyh
h = np.zeros_like(x) + h
sim_res = self.get_sim_res(wd=np.arange(360), ws=[ws])
fm = sim_res.flow_map(Points(x, y, h), wd=sim_res.wd)
return fm.wd[((fm.WS - fm.WS_eff).max('i') > tol)].values
def get_sim_res(self, wd, ws):
return self.wfm(**{**self.wfm_kwargs, 'ws': ws, 'wd': wd})
def __call__(self, i, l, deficit_jlk, WS_jlk, WS_eff_ilk,
WD_ilk, dst_xyh_jlk, IJLK, dw_ijlk, hcw_ijlk, dh_ijlk, **_):
I, J, L, K = IJLK
WD_l = np.round(WD_ilk[np.minimum(i, len(WD_ilk) - 1), :, 0])
m_lst = [m for m, wd in enumerate(WD_l) if l[m] and self.include_wd_func(wd)]
if deficit_jlk is None:
deficit_jlk = np.zeros((J, L, K))
if m_lst:
M = len(m_lst)
ws_lst = np.sort(np.unique(np.r_[WS_eff_ilk.min(), np.round(WS_eff_ilk.flatten()), WS_eff_ilk.max()]))
wd_lst = WD_l[m_lst]
sim_res = self.wfm(**{**self.wfm_kwargs, 'ws': ws_lst, 'wd': np.sort(np.unique(wd_lst))})
from py_wake.site.streamline_distance import StreamlineDistance
if isinstance(self.wfm.site.distance, StreamlineDistance):
# Simulation and flow map of external WF is computed with stream line distances
# the WF wake must therefore be calculated for fixed abs positions
dst_xyh_jlk = dst_xyh_jlk
else:
# Simulation and flow map of external WF is computed with straight distance.
# The WF wakes must therefore be calcualted using the relative distances (including streamlines)
_dst_xyh_jlk = self.rel2abs(dw_ijlk[i], hcw_ijlk[i], dh_ijlk[i], WD_l[na, :, na])
if M == 1 or np.abs(dst_xyh_jlk[0][:, [0]] - dst_xyh_jlk[0]).max() > 1e-10:
# destinations differ for differnet wd, use destinations calculated from
# distances to include streamlines
dst_xyh_jlk = _dst_xyh_jlk
if dst_xyh_jlk[0].shape[1:] == (1, 1) and M > J:
# same destination for all wd
x_j, y_j, h_j = [v[:, 0, 0] for v in dst_xyh_jlk]
lw_j, WS_eff_jlk, TI_eff_jlk = self.wfm._flow_map(x_j[:, na], y_j[:, na], h_j[:, na], sim_res.localWind,
wd_lst, ws_lst, sim_res)
_deficit_jlk = lw_j.WS_ilk - WS_eff_jlk
deficit_jmk = np.moveaxis([RGI([ws_lst], np.moveaxis(_deficit_jlk[:, l], 0, -1))(WS_eff_ilk[i, m])
for l, m in enumerate(m_lst)], -1, 0)
else:
assert dst_xyh_jlk[0].shape[2] == 1, "ExternalWFMWindFarm does not support inflow dependent positions"
def get_deficit_l(m):
x_j, y_j, h_j = [np.broadcast_to(v_jlk, (J, L, K))[:, m] for v_jlk in dst_xyh_jlk]
WD = WD_l[m]
WS_eff = WS_eff_ilk[i, m]
sr = sel_interp_all(sim_res)(dict(wd=WD, ws=WS_eff))
lw_j, WS_eff_jlk, TI_eff_jlk = self.wfm._flow_map(x_j, y_j, h_j, sim_res.localWind, WD, WS_eff, sr)
return lw_j.WS_ilk[:, 0] - WS_eff_jlk[:, 0]
deficit_jmk = np.moveaxis([get_deficit_l(m) for m in m_lst], 0, 1)
deficit_jlk = item_assign(deficit_jlk, m_lst, deficit_jmk, axis=1)
return deficit_jlk
class ExternalXRAbsWindFarm(ExternalWindFarm):
def __init__(self, name, ds, windTurbines, wt_x, wt_y, relative_distance=True, include_wd=None):
ExternalWindFarm.__init__(self, name, windTurbines, wt_x, wt_y)
x = [ds.x.values, ds.y.values, ds.h.values, ds.wd.values, ds.ws.values]
self.deficit_interp = RGI(x, ds.deficit.values, bounds_error=False)
if include_wd is None:
include_wd = ds.wd.values
self.set_include_wd(include_wd)
self.relative_distance = relative_distance
def to_xarray(self):
di = self.deficit_interp
return xr.Dataset({'deficit': (['x', 'y', 'h', 'wd', 'ws'], di.values)},
coords=dict(x=di.grid[0], y=di.grid[1], h=di.grid[2], wd=di.grid[3], ws=di.grid[4]))
@classmethod
def generate(cls, name, grid_xyh, windFarmModel, wt_x, wt_y, type=0, **kwargs):
sim_res = windFarmModel(wt_x, wt_y, type=type, **kwargs)
X, Y, H = np.meshgrid(*grid_xyh, indexing='ij')
x_j, y_j, h_j = X.flatten(), Y.flatten(), H.flatten()
lw_j, WS_eff_jlk, TI_eff_jlk = windFarmModel._flow_map(x_j[:, na], y_j[:, na], h_j[:, na], sim_res.localWind,
sim_res.wd, sim_res.ws, sim_res)
deficit = lw_j.WS_ilk - WS_eff_jlk.reshape(X.shape + (WS_eff_jlk.shape[1:]))
ds = xr.Dataset({'deficit': (['x', 'y', 'h', 'wd', 'ws'], deficit)},
coords=dict(x=grid_xyh[0], y=grid_xyh[1], h=np.atleast_1d(grid_xyh[2]), wd=sim_res.wd, ws=sim_res.ws))
from py_wake.site.streamline_distance import StreamlineDistance
return cls(name, ds, windFarmModel.windTurbines, wt_x, wt_y,
relative_distance=not isinstance(windFarmModel.site.distance, StreamlineDistance))
def __call__(self, i, l, deficit_jlk, WS_eff_ilk, WS_ilk,
dw_ijlk, hcw_ijlk, dh_ijlk,
WD_ilk, IJLK, dst_xyh_jlk, **_):
I, J, L, K = IJLK
WD_l = WD_ilk[np.minimum(i, len(WD_ilk) - 1), :, 0]
m_lst = [m for m, wd in enumerate(WD_l) if l[m] and self.include_wd_func(wd)]
if deficit_jlk is None:
deficit_jlk = np.zeros((J, L, K))
if m_lst:
M = len(m_lst)
WS_eff_ilk[i]
if self.relative_distance:
# Simulation and flow map of external WF is computed with straight distance.
# The WF wakes must therefore be calcualted using the relative distances (including streamlines)
_dst_xyh_jlk = self.rel2abs(dw_ijlk[i], hcw_ijlk[i], dh_ijlk[i], WD_l[na, :, na])
if M == 1 or np.abs(dst_xyh_jlk[0][:, [0]] - dst_xyh_jlk[0]).max() > 1e-10:
# destinations differ for different wd, use destinations calculated from
# distances to include streamlines
dst_xyh_jlk = _dst_xyh_jlk
if dst_xyh_jlk[0].shape[1:] == (1, 1) and M > J:
# same destination for all wd
# make interpolator with current destination and interpolate ws for each wd
di = self.deficit_interp
fm_jlk = RGI(di.grid[:3], di.values, bounds_error=False)(np.array([v[:, 0, 0] for v in dst_xyh_jlk]).T)
np.nan_to_num(fm_jlk, copy=False)
rgi_dst = RGI(di.grid[3:], np.moveaxis(fm_jlk, 0, -1), bounds_error=False)
WS_eff = WS_eff_ilk[i, m_lst]
WD = np.broadcast_to(WD_l[m_lst, na], WS_eff.shape)
deficit_jmk = np.moveaxis(
rgi_dst(np.array([WD.flatten(), WS_eff.flatten()]).T), -1, 0).reshape((J, M, K))
else:
x = [np.broadcast_to(v[:, m_lst], (J, M, K)).flatten()
for v in list(dst_xyh_jlk) + [WD_l[na, :, na], WS_eff_ilk[i, :][na]]]
deficit_jmk = self.deficit_interp(np.array(x).T).reshape((J, M, K))
np.nan_to_num(deficit_jmk, copy=False)
deficit_jlk = item_assign(deficit_jlk, m_lst, deficit_jmk, axis=1)
return deficit_jlk
class ExternalXRRelWindFarm(ExternalXRAbsWindFarm):
@classmethod
def generate(cls, name, grid_xyh, windFarmModel, wt_x, wt_y, type=0, **kwargs):
sim_res = windFarmModel(wt_x, wt_y, type=type, **kwargs)
X, Y, H = np.meshgrid(*grid_xyh, indexing='ij')
dw, hcw, dh = X.flatten(), Y.flatten(), H.flatten()
wf_x = np.mean([np.min(wt_x), np.max(wt_x)])
wf_y = np.mean([np.min(wt_y), np.max(wt_y)])
wf_h = windFarmModel.windTurbines.hub_height(type)
deficit = []
for wd in tqdm(sim_res.wd.values, disable=1):
theta = np.deg2rad(270 - wd)
co, si = np.cos(theta), np.sin(theta)
x_j = co * dw - hcw * si + wf_x
y_j = si * dw + hcw * co + wf_y
h_j = dh + wf_h
lw_j, WS_eff_jlk, TI_eff_jlk = windFarmModel._flow_map(x_j[:, na], y_j[:, na], h_j[:, na], sim_res.localWind,
wd, sim_res.ws, sim_res)
deficit.append(lw_j.WS_ilk - WS_eff_jlk)
deficit = np.moveaxis(deficit, 0, 1).reshape(X.shape + (len(sim_res.wd), len(sim_res.ws)))
ds = xr.Dataset({'deficit': (['x', 'y', 'h', 'wd', 'ws'], deficit)},
coords=dict(x=grid_xyh[0], y=grid_xyh[1], h=grid_xyh[2], wd=sim_res.wd, ws=sim_res.ws))
from py_wake.site.streamline_distance import StreamlineDistance
return cls(name, ds, windFarmModel.windTurbines, wt_x, wt_y,
relative_distance=not isinstance(windFarmModel.site.distance, StreamlineDistance))
def __call__(self, i, l, deficit_jlk, WS_eff_ilk, WS_ilk,
dw_ijlk, hcw_ijlk, dh_ijlk,
WD_ilk, IJLK, dst_xyh_jlk, **_):
I, J, L, K = IJLK
WD_l = WD_ilk[np.minimum(i, len(WD_ilk) - 1), :, 0]
m_lst = [m for m, wd in enumerate(WD_l) if l[m] and self.include_wd_func(wd)]
if deficit_jlk is None:
deficit_jlk = np.zeros((J, L, K))
if not self.relative_distance:
xyh_ilk = [np.reshape(v, (1, 1, 1)) for v in [self.wf_x, self.wf_y, self.wf_h]]
dw_jlk, hcw_jlk, dh_jlk = [v[0] for v in StraightDistance()(*xyh_ilk, wd_l=WD_l, dst_xyh_jlk=dst_xyh_jlk)]
else:
dw_jlk, hcw_jlk, dh_jlk = dw_ijlk[i], hcw_ijlk[i], np.broadcast_to(dh_ijlk[i], (J, L, K))
if m_lst:
M = len(m_lst)
WS_eff_ilk[i]
x = [np.broadcast_to(v[:, m_lst], (J, M, K)).flatten()
for v in [dw_jlk, hcw_jlk, dh_jlk,
WD_l[na, :, na], WS_eff_ilk[i][na]]]
deficit_jmk = self.deficit_interp(np.array(x).T).reshape((J, M, K))
np.nan_to_num(deficit_jmk, copy=False)
deficit_jlk = item_assign(deficit_jlk, m_lst, deficit_jmk, axis=1)
return deficit_jlk
+1
-1
Metadata-Version: 2.4
Name: py_wake
Version: 2.6.13
Version: 2.6.14
Summary: Open source static wake modeling framework from DTU

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

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

R_ijlk = (D_src_il / 2)[:, na, :, na]
iw = ((dw_ijlk / R_ijlk >= -self.limiter) & (cabs(cw_ijlk) <= wake_radius_ijlk))
iw = ((dw_ijlk / R_ijlk >= -self.limiter) & (cabs(cw_ijlk) <= wake_radius_ijlk) & (wake_radius_ijlk > 0))
deficit_ijlk = np.where(iw, 0., deficit_ijlk)

@@ -118,0 +118,0 @@

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

def plot(self, data, clabel, levels=100, cmap=None, plot_colorbar=True, plot_windturbines=True,
normalize_with=1, ax=None):
normalize_with=1, ax=None, cax=None):
"""Plot data as contouf map

@@ -195,3 +195,3 @@

if plot_colorbar:
plt.colorbar(c, label=clabel, ax=ax)
plt.colorbar(c, label=clabel, ax=ax, cax=cax)
else:

@@ -203,2 +203,4 @@ raise NotImplementedError(

self.plot_windturbines(normalize_with=normalize_with, ax=ax)
for ewf in self.windFarmModel.externalWindFarms:
ewf.plot(ax=ax)
ax.axis('equal')

@@ -209,7 +211,8 @@ return c

fm = self.windFarmModel
n_ewf = len(fm.externalWindFarms)
# mean over wd and ws if present
x_i = self.simulationResult.x.mean(set(self.simulationResult.x.dims) - {'wt'}).values
y_i = self.simulationResult.y.mean(set(self.simulationResult.x.dims) - {'wt'}).values
type_i = self.simulationResult.type.data
n = len(self.simulationResult.wt) - n_ewf
x_i = self.simulationResult.x.mean(set(self.simulationResult.x.dims) - {'wt'}).values[:n]
y_i = self.simulationResult.y.mean(set(self.simulationResult.x.dims) - {'wt'}).values[:n]
type_i = self.simulationResult.type.values[:n]
if self.plane[0] in ['XZ', "YZ"]:

@@ -256,3 +259,3 @@ h_i = self.simulationResult.h.values

def plot_wake_map(self, levels=100, cmap=None, plot_colorbar=True, plot_windturbines=True,
normalize_with=1, ax=None):
normalize_with=1, ax=None, cax=None):
"""Plot effective wind speed contourf map

@@ -280,3 +283,3 @@

levels=levels, cmap=cmap, plot_colorbar=plot_colorbar,
plot_windturbines=plot_windturbines, normalize_with=normalize_with, ax=ax)
plot_windturbines=plot_windturbines, normalize_with=normalize_with, ax=ax, cax=cax)

@@ -512,2 +515,3 @@ def plot_ti_map(self, levels=100, cmap=None, plot_colorbar=True, plot_windturbines=True, ax=None):

def __init__(self, x, y, h):
x, y, h = [np.atleast_1d(v) for v in [x, y, h]]
assert len(x) == len(y) == len(h)

@@ -514,0 +518,0 @@ self.x = x

@@ -56,3 +56,3 @@ import numpy as np

def ground_eff(self, ground_distance_ijlk, distance_ijlk, ground_type):
def ground_eff(self, ground_distance_ijlk, distance_ijlk, rec_h, ground_type):
# Ground effects ISO

@@ -62,5 +62,7 @@

src_h_ijlk = self.distance.src_h_ilk[:, na]
rec_h_ijlk = self.distance.dst_h_j[na, :, na, na]
src_h_ilk = np.expand_dims(self.src_h, tuple(range(len(np.shape(self.src_h)), 3))) + .0
src_h_ijlk = src_h_ilk[:, na]
rec_h_ijlk = rec_h[na, :, na, na]
hei_check_ijlk = 30.0 * (src_h_ijlk + rec_h_ijlk)

@@ -176,9 +178,9 @@

rec_h = np.zeros_like(rec_x) + rec_h
rec_z = self.elevation_function(rec_x, rec_y)
self.distance.setup(self.src_x, self.src_y, self.src_h, self.src_z, [rec_x, rec_y, rec_h, rec_z])
ground_distance_ijlk = np.sqrt(self.distance.dx_ijlk**2 + self.distance.dy_ijlk**2)
distance_ijlk = np.sqrt(self.distance.dx_ijlk**2 + self.distance.dy_ijlk**2 + self.distance.dh_ijlk**2)
dx_ijlk, dy_ijlk, dh_ijlk = self.distance(self.src_x, self.src_y, self.src_h, wd_l=np.array([270]),
dst_xyh_jlk=[rec_x, rec_y, rec_h])
ground_distance_ijlk = np.sqrt(dx_ijlk**2 + dy_ijlk**2)
distance_ijlk = np.sqrt(dx_ijlk**2 + dy_ijlk**2 + dh_ijlk**2)
atm_abs_ijlkf = self.atmab(distance_ijlk, T0=Temp, RH0=RHum, p_s=patm) # The atmospheric absorption term
ground_eff_ijlkf = self.ground_eff(ground_distance_ijlk, distance_ijlk, ground_type)
ground_eff_ijlkf = self.ground_eff(ground_distance_ijlk, distance_ijlk, rec_h, ground_type)

@@ -185,0 +187,0 @@ return ground_eff_ijlkf - atm_abs_ijlkf # Delta_SPL

@@ -10,2 +10,3 @@ import os

from scipy.interpolate import RectBivariateSpline
from py_wake.utils import gradients

@@ -25,3 +26,4 @@

# dat = table.interp(R_sigma=R_sigma, CW_sigma=CW_sigma, method='cubic')
# performs a two-step interpolation which is slower and less accurate than the previous one-step interpolation
# performs a two-step interpolation which is slower and less accurate than
# the previous one-step interpolation
dat = RectBivariateSpline(table.R_sigma.values, table.CW_sigma.values, table.values)(R_sigma, CW_sigma)

@@ -40,4 +42,5 @@ self._overlap_interpolator = GridInterpolator([R_sigma, CW_sigma], dat, bounds='limit')

sigma_ijlk = func.__self__.sigma_ijlk(**kwargs)
overlap_factor_ijlk = self.overlap_interpolator(
np.array([((D_dst_ijl / 2)[..., na] / sigma_ijlk).flatten(), (cw_ijlk / sigma_ijlk).flatten()]).T).reshape(sigma_ijlk.shape)
r_sigma = ((D_dst_ijl / 2)[..., na] / sigma_ijlk).flatten()
cw_sigma = gradients.cabs(cw_ijlk / sigma_ijlk).flatten()
overlap_factor_ijlk = self.overlap_interpolator(np.array([r_sigma, cw_sigma]).T).reshape(sigma_ijlk.shape)
return res_ijlk * overlap_factor_ijlk

@@ -44,0 +47,0 @@

@@ -214,5 +214,2 @@ import matplotlib.pyplot as plt

def wt2wt_distances(self, WD_ilk=None, wd_l=None):
return self.distance(WD_ilk=WD_ilk, wd_l=wd_l)
@abstractmethod

@@ -219,0 +216,0 @@ def elevation(self, x_i, y_i):

@@ -21,2 +21,3 @@ from py_wake import np

"""
assert wind_direction in ['wd', 'WD_i'], "'StraightDistance.wind_direction must be 'wd' or 'WD_i'"
self.wind_direction = wind_direction

@@ -30,6 +31,73 @@

def plot(self, WD_ilk=None, wd_l=None, src_idx=slice(None), dst_idx=slice(None)):
def __getstate__(self):
return {k: v for k, v in self.__dict__.items()
if k not in {'src_x_ilk', 'src_y_ilk', 'src_h_ilk', 'dst_x_j', 'dst_y_j', 'dst_h_j',
'dx_iilk', 'dy_iilk', 'dh_iilk', 'dx_ijlk', 'dy_ijlk', 'dh_ij', 'src_eq_dst'}}
def get_pos(self, src_x_ilk, src_y_ilk, src_h_ilk, wd_l=None, WD_ilk=None, dst_xyh_jlk=None):
# ensure 3d and
# +.0 ensures float or complex
src_x_ilk, src_y_ilk, src_h_ilk = [np.expand_dims(v, tuple(range(len(np.shape(v)), 3))) + .0
for v in [src_x_ilk, src_y_ilk, src_h_ilk]]
if dst_xyh_jlk is None:
dst_x_jlk, dst_y_jlk, dst_h_jlk = src_x_ilk, src_y_ilk, src_h_ilk
else:
assert len(dst_xyh_jlk) == 3
dst_x_jlk, dst_y_jlk, dst_h_jlk = [np.expand_dims(v, tuple(range(len(np.shape(v)), 3))) + .0
for v in dst_xyh_jlk]
return (src_x_ilk, src_y_ilk, src_h_ilk), (dst_x_jlk, dst_y_jlk, dst_h_jlk)
def __call__(self, src_x_ilk, src_y_ilk, src_h_ilk, wd_l=None, WD_ilk=None, time=None, dst_xyh_jlk=None):
(src_x_ilk, src_y_ilk, src_h_ilk), (dst_x_jlk, dst_y_jlk, dst_h_jlk) = self.get_pos(
src_x_ilk, src_y_ilk, src_h_ilk, wd_l, WD_ilk, dst_xyh_jlk)
wd_l = np.asarray(wd_l)
if self.wind_direction == 'wd':
assert wd_l is not None, "wd_l must be specified when Distance.wind_direction='wd'"
WD_ilk = np.asarray(wd_l)[na, :, na]
else:
assert WD_ilk is not None, "WD_ilk must be specified when Distance.wind_direction='WD_i'"
cos_ilk, sin_ilk = self._cos_sin(WD_ilk)
if self.wind_direction == 'wd':
src_dw_ilk = -cos_ilk * src_x_ilk - sin_ilk * src_y_ilk
src_hcw_ilk = sin_ilk * src_x_ilk - cos_ilk * src_y_ilk
if dst_xyh_jlk is None:
dst_dw_jlk, dst_hcw_jlk, dst_h_jlk = src_dw_ilk, src_hcw_ilk, src_h_ilk
else:
cos_jlk, sin_jlk = self._cos_sin(wd_l[na, :, na])
dst_dw_jlk = (-cos_jlk * dst_x_jlk - sin_jlk * dst_y_jlk)
dst_hcw_jlk = (sin_jlk * dst_x_jlk - cos_jlk * dst_y_jlk)
dw_ijlk = dst_dw_jlk[na] - src_dw_ilk[:, na]
hcw_ijlk = dst_hcw_jlk[na] - src_hcw_ilk[:, na]
else:
dx_ijlk = dst_x_jlk[na] - src_x_ilk[:, na]
dy_ijlk = dst_y_jlk[na] - src_y_ilk[:, na]
cos_ilk, sin_ilk = self._cos_sin(WD_ilk)
dw_ijlk = -cos_ilk[:, na] * dx_ijlk - sin_ilk[:, na] * dy_ijlk
hcw_ijlk = sin_ilk[:, na] * dx_ijlk - cos_ilk[:, na] * dy_ijlk
dh_ijlk = dst_h_jlk[na, :] - src_h_ilk[:, na]
return dw_ijlk, hcw_ijlk, np.broadcast_to(dh_ijlk, hcw_ijlk.shape)
def dw_order_indices(self, src_x_ilk, src_y_ilk, wd_l):
WD_ilk = np.asarray(wd_l)[na, :, na]
src_x_ilk, src_y_ilk = [np.expand_dims(v, tuple(range(len(np.shape(v)), 3))) + .0
for v in [src_x_ilk, src_y_ilk]]
cos_ilk, sin_ilk = self._cos_sin(WD_ilk)
src_dw_ilk = -cos_ilk * src_x_ilk - sin_ilk * src_y_ilk
return np.moveaxis(np.argsort(src_dw_ilk, 0), 0, -1)
def plot(self, src_x_ilk, src_y_ilk, src_h_ilk, wd_l=None, WD_ilk=None, dst_xyh_jlk=None):
import matplotlib.pyplot as plt
(src_x_ilk, src_y_ilk, src_h_ilk), (dst_x_jlk, dst_y_jlk, dst_h_jlk) = self.get_pos(
src_x_ilk, src_y_ilk, src_h_ilk, wd_l, WD_ilk, dst_xyh_jlk)
dw_ijlk, hcw_ijlk, _ = self(WD_ilk=WD_ilk, wd_l=wd_l)
dw_ijlk, hcw_ijlk, _ = self(src_x_ilk, src_y_ilk, src_h_ilk, WD_ilk=WD_ilk, wd_l=wd_l, dst_xyh_jlk=dst_xyh_jlk)
if self.wind_direction == 'wd':

@@ -47,8 +115,7 @@ WD_ilk = np.asarray(wd_l)[na, :, na]

f = 2
for i, x_, y_ in zip(np.arange(len(self.src_x_ilk))[
src_idx], self.src_x_ilk[src_idx, 0, 0], self.src_y_ilk[src_idx, 0, 0]):
for i, x_, y_ in zip(np.arange(len(src_x_ilk)), src_x_ilk[:, 0, 0], src_y_ilk[:, 0, 0]):
c = colors[i % len(colors)]
ax.plot(x_, y_, '2', color=c, ms=10, mew=3)
for j, dst_x, dst_y in zip(np.arange(len(self.dst_x_j))[dst_idx],
self.dst_x_j[dst_idx, 0, 0], self.dst_y_j[dst_idx, 0, 0]):
for j, dst_x, dst_y in zip(np.arange(len(dst_x_jlk)),
dst_x_jlk[:, 0, 0], dst_y_jlk[:, 0, 0]):
ax.arrow(x_ - j / f, y_ - j / f, -np.cos(theta) * dw_ijlk[i, j, l, 0], -

@@ -58,77 +125,7 @@ np.sin(theta) * dw_ijlk[i, j, l, 0], width=.3, color=c)

[dst_y - i / f, dst_y + np.cos(theta) * hcw_ijlk[i, j, l, 0] - i / f], '--', color=c)
plt.plot(self.src_x_ilk[:, 0, 0], self.src_y_ilk[:, 0, 0], 'k2')
plt.plot(src_x_ilk[:, 0, 0], src_y_ilk[:, 0, 0], 'k2')
plt.plot(dst_x_jlk[:, 0, 0], dst_y_jlk[:, 0, 0], 'k2')
ax.axis('equal')
def setup(self, src_x_ilk, src_y_ilk, src_h_ilk, src_z_ilk, dst_xyhz_j=None):
# ensure 3d and
# +.0 ensures float or complex
src_x_ilk, src_y_ilk, src_h_ilk, src_z_ilk = [np.expand_dims(v, tuple(range(len(np.shape(v)), 3))) + .0
for v in [src_x_ilk, src_y_ilk, src_h_ilk, src_z_ilk]]
self.src_x_ilk, self.src_y_ilk, self.src_h_ilk = src_x_ilk, src_y_ilk, src_h_ilk
self.dx_iilk = src_x_ilk - src_x_ilk[:, na]
self.dy_iilk = src_y_ilk - src_y_ilk[:, na]
self.dh_iilk = src_h_ilk - src_h_ilk[:, na]
self.dz_iilk = src_z_ilk - src_z_ilk[:, na]
if dst_xyhz_j is None:
dst_x_j, dst_y_j, dst_h_j, dst_z_j = src_x_ilk, src_y_ilk, src_h_ilk, src_z_ilk
self.dx_ijlk, self.dy_ijlk, self.dh_ijlk, self.dz_ijlk = self.dx_iilk, self.dy_iilk, self.dh_iilk, self.dz_iilk
self.src_eq_dst = True
else:
dst_x_j, dst_y_j, dst_h_j, dst_z_j = map(np.asarray, dst_xyhz_j)
self.dx_ijlk = dst_x_j[na, :, na, na] - src_x_ilk[:, na]
self.dy_ijlk = dst_y_j[na, :, na, na] - src_y_ilk[:, na]
self.dh_ijlk = dst_h_j[na, :, na, na] - src_h_ilk[:, na]
self.dz_ijlk = dst_z_j[na, :, na, na] - src_z_ilk[:, na]
self.src_eq_dst = False
self.dst_x_j, self.dst_y_j, self.dst_h_j, self.dst_z_j = dst_x_j, dst_y_j, dst_h_j, dst_z_j
def __getstate__(self):
return {k: v for k, v in self.__dict__.items()
if k not in {'src_x_ilk', 'src_y_ilk', 'src_h_ilk', 'dst_x_j', 'dst_y_j', 'dst_h_j',
'dx_iilk', 'dy_iilk', 'dh_iilk', 'dx_ijlk', 'dy_ijlk', 'dh_ij', 'src_eq_dst'}}
def __call__(self, WD_ilk=None, wd_l=None, src_idx=slice(None), dst_idx=slice(None)):
assert hasattr(self, 'dx_ijlk'), "wind_direction setup must be called first"
assert self.wind_direction in ['wd', 'WD_i'], "'StraightDistance.wind_direction must be 'wd' or 'WD_i'"
if self.wind_direction == 'wd':
assert wd_l is not None, "wd_l must be specified when Distance.wind_direction='wd'"
WD_ilk = np.asarray(wd_l)[na, :, na]
else:
assert WD_ilk is not None, "WD_ilk must be specified when Distance.wind_direction='WD_i'"
if len(np.shape(dst_idx)) == 2:
# dst_idx depends on wind direction
cos_jlk, sin_jlk = self._cos_sin(WD_ilk[0, na])
i_wd_l = np.arange(np.shape(dst_idx)[1])
dx_jlk = self.dx_iilk[src_idx, dst_idx, np.minimum(i_wd_l, self.dx_iilk.shape[2] - 1).astype(int)]
dy_jlk = self.dy_iilk[src_idx, dst_idx, np.minimum(i_wd_l, self.dy_iilk.shape[2] - 1).astype(int)]
dh_jlk = self.dh_iilk[src_idx, dst_idx, np.minimum(i_wd_l, self.dh_iilk.shape[2] - 1).astype(int)]
dw_jlk = (-cos_jlk * dx_jlk - sin_jlk * dy_jlk)
hcw_jlk = (sin_jlk * dx_jlk - cos_jlk * dy_jlk)
return dw_jlk[na], hcw_jlk[na], dh_jlk[na]
else:
# dst_idx independent of wind direction
cos_ijlk, sin_ijlk = self._cos_sin(WD_ilk[:, na])
dx_ijlk = self.dx_ijlk[src_idx][:, dst_idx]
dy_ijlk = self.dy_ijlk[src_idx][:, dst_idx]
dw_ijlk = -cos_ijlk * dx_ijlk - sin_ijlk * dy_ijlk
hcw_ijlk = sin_ijlk * dx_ijlk - cos_ijlk * dy_ijlk
# +0 ~ autograd safe copy (broadcast_to returns readonly array)
dh_ijlk = np.broadcast_to(self.dh_ijlk[src_idx][:, dst_idx], dw_ijlk.shape) + 0
return dw_ijlk, hcw_ijlk, dh_ijlk
def dw_order_indices(self, wd_l):
assert hasattr(self, 'dx_ijlk'), "method setup must be called first"
I, J, *_ = self.dx_ijlk.shape
assert I == J
cos_l, sin_l = self._cos_sin(np.asarray(wd_l))
# return np.argsort(-cos_l[:, na] * np.asarray(src_x_ilk)[na] - sin_l[:, na] * np.asarray(src_y_ilk)[na], 1)
dw_iil = -cos_l[na, na, :, na] * self.dx_ijlk - sin_l[na, na, :, na] * self.dy_ijlk
dw_order_indices_lkd = np.moveaxis(np.argsort((dw_iil > 0).sum(0), 0), 0, -1)
return dw_order_indices_lkd
class TerrainFollowingDistance(StraightDistance):

@@ -139,34 +136,27 @@ def __init__(self, distance_resolution=1000, wind_direction='wd', **kwargs):

def setup(self, src_x_ilk, src_y_ilk, src_h_ilk, src_z_ilk, dst_xyhz_j=None):
StraightDistance.setup(self, src_x_ilk, src_y_ilk, src_h_ilk, src_z_ilk, dst_xyhz_j=dst_xyhz_j)
if len(src_x_ilk) == 0:
return
def __call__(self, src_x_ilk, src_y_ilk, src_h_ilk,
WD_ilk=None, wd_l=None, time=None, dst_xyh_jlk=None):
# StraightDistance.setup(self, src_x_ilk, src_y_ilk, src_h_ilk, src_z_ilk, dst_xyh_jlk=dst_xyh_jlk)
# if len(src_x_ilk) == 0:
# return
# Calculate distance between src and dst and project to the down wind direction
assert self.src_x_ilk.shape[1:] == (
1, 1), 'TerrainFollowingDistance does not support flowcase dependent positions'
src_x_ilk, src_y_ilk, src_h_ilk = self.src_x_ilk[:, 0, 0], self.src_y_ilk[:, 0, 0], self.src_h_ilk[:, 0, 0]
if len(self.dst_x_j.shape) == 3:
dst_x_j, dst_y_j = self.dst_x_j[:, 0, 0], self.dst_y_j[:, 0, 0]
else:
dst_x_j, dst_y_j = self.dst_x_j, self.dst_y_j
(src_x_ilk, src_y_ilk, src_h_ilk), (dst_x_jlk, dst_y_jlk, dst_h_jlk) = self.get_pos(
src_x_ilk, src_y_ilk, src_h_ilk, wd_l, WD_ilk, dst_xyh_jlk)
dw_ijlk, hcw_ijlk, dh_ijlk = StraightDistance.__call__(self, src_x_ilk, src_y_ilk, src_h_ilk,
WD_ilk=WD_ilk, wd_l=wd_l, dst_xyh_jlk=dst_xyh_jlk)
assert src_x_ilk.shape[2] == 1, 'TerrainFollowingDistance does not support flowcase dependent positions'
src_x_i, src_y_i = src_x_ilk[:, 0, 0], src_y_ilk[:, 0, 0]
dst_x_j, dst_y_j = dst_x_jlk[:, 0, 0], dst_y_jlk[:, 0, 0]
# Generate interpolation lines
if (self.src_eq_dst and self.dx_ijlk.shape[0] > 1):
# calculate upper triangle of d_ij(distance from i to j) only
xy = np.array([(np.linspace(src_x, dst_x, self.distance_resolution),
np.linspace(src_y, dst_y, self.distance_resolution))
for i, (src_x, src_y) in enumerate(zip(src_x_ilk, src_y_ilk))
for dst_x, dst_y in zip(dst_x_j[i + 1:], dst_y_j[i + 1:])])
upper_tri_only = True
self.theta_ij = gradients.arctan2(dst_y_j[na, :] - src_y_ilk[:, na],
dst_x_j[na, :] - src_x_ilk[:, na])
else:
xy = np.array([(np.linspace(src_x, dst_x, self.distance_resolution),
np.linspace(src_y, dst_y, self.distance_resolution))
for src_x, src_y in zip(src_x_ilk, src_y_ilk)
for dst_x, dst_y in zip(dst_x_j, dst_y_j)])
self.theta_ij = gradients.arctan2(dst_y_j[na, :, ] - src_y_ilk[:, na],
dst_x_j[na, :] - src_x_ilk[:, na])
upper_tri_only = False
xy = np.array([(np.linspace(src_x, dst_x, self.distance_resolution),
np.linspace(src_y, dst_y, self.distance_resolution))
for src_x, src_y in zip(src_x_i, src_y_i)
for dst_x, dst_y in zip(dst_x_j, dst_y_j)])
theta_ij = gradients.arctan2(dst_y_j[na, :, ] - src_y_i[:, na],
dst_x_j[na, :] - src_x_i[:, na])
x, y = xy[:, 0], xy[:, 1]

@@ -182,17 +172,4 @@

if upper_tri_only:
# d_ij = np.zeros(self.dx_ijlk.shape)
# d_ij[np.triu(np.eye(len(src_x_ilk)) == 0)] = s # set upper triangle
d_ij = s.reshape(dw_ijlk.shape[:2])
# same as above without item assignment
n = len(src_x_ilk)
d_ij = np.array([np.concatenate([[0] * (i + 1),
s[int(n * i - (i * (i + 1) / 2)):][:n - i - 1]])
for i in range(n)]) # set upper and lower triangle
d_ij += d_ij.T
else:
d_ij = s.reshape(self.dx_ijlk.shape[:2])
self.d_ij = d_ij
def __call__(self, WD_ilk=None, wd_l=None, src_idx=slice(None), dst_idx=slice(None)):
# project terrain following distance between wts onto downwind direction

@@ -202,4 +179,2 @@ # instead of projecting the distances onto first x,y and then onto down wind direction

_, hcw_ijlk, dh_ijlk = StraightDistance.__call__(self, WD_ilk=WD_ilk, wd_l=wd_l,
src_idx=src_idx, dst_idx=dst_idx)
if self.wind_direction == 'wd':

@@ -209,17 +184,7 @@ WD_ilk = np.asarray(wd_l)[na, :, na]

WD_il = mean_deg(WD_ilk, 2)
dir_ij = 90 - rad2deg(theta_ij)
wdir_offset_ijl = np.asarray(WD_il)[:, na] - dir_ij[:, :, na]
theta_ijl = deg2rad(90 - wdir_offset_ijl)
dw_ijlk = (- np.sin(theta_ijl) * d_ij[:, :, na])[..., na]
if len(np.shape(dst_idx)) == 2:
# dst_idx depends on wind direction
WD_l = WD_il[0]
dir_jl = 90 - rad2deg(self.theta_ij[src_idx, dst_idx])
wdir_offset_jl = WD_l[na, :] - dir_jl
theta_jl = deg2rad(90 - wdir_offset_jl)
dw_ijlk = (- np.sin(theta_jl) * self.d_ij[src_idx, dst_idx])[na, :, :, na]
else:
dir_ij = 90 - rad2deg(self.theta_ij[src_idx, ][:, dst_idx])
wdir_offset_ijl = np.asarray(WD_il)[:, na] - dir_ij[:, :, na]
theta_ijl = deg2rad(90 - wdir_offset_ijl)
dw_ijlk = (- np.sin(theta_ijl) * self.d_ij[src_idx][:, dst_idx][:, :, na])[..., na]
return dw_ijlk, hcw_ijlk, dh_ijlk

@@ -1,108 +0,3 @@

from py_wake.site.distance import StraightDistance
from py_wake import np
from numpy import newaxis as na
import matplotlib.pyplot as plt
from py_wake.examples.data.ParqueFicticio._parque_ficticio import ParqueFicticioSite
from py_wake.examples.data.hornsrev1 import V80
from py_wake.deficit_models.noj import NOJ
from py_wake.flow_map import XYGrid
from py_wake.utils.streamline import VectorField3D
import warnings
class JITStreamlineDistance(StraightDistance):
"""Just-In-Time Streamline Distance
Calculates downwind crosswind and vertical distance along streamlines.
Streamlines calculated in each call
"""
def __init__(self, vectorField, step_size=20):
"""Parameters
----------
vectorField : VectorField3d
step_size : int for float
Size of linear streamline steps
"""
StraightDistance.__init__(self, wind_direction='wd')
self.vectorField = vectorField
self.step_size = step_size
def __call__(self, wd_l, WD_ilk, src_idx=slice(None), dst_idx=slice(None)):
assert self.src_x_ilk.shape[1:] == (1, 1), 'JITStreamlineDistance does not support flowcase dependent positions'
start_points_m = np.array([self.src_x_ilk[src_idx], self.src_y_ilk[src_idx],
self.src_h_ilk[src_idx]])[:, :, 0, 0].T
if len(np.shape(dst_idx)) == 2:
# dst_idx depends on wind direction
dw_jl, hcw_jl, dh_jl = [v[0, :, :, 0] for v in StraightDistance.__call__(self, wd_l=wd_l,
src_idx=src_idx, dst_idx=dst_idx)]
dw_mj, hcw_mj, dh_mj = [np.moveaxis(v, 0, 1) for v in [dw_jl, hcw_jl, dh_jl]]
i_wd_l = np.arange(np.shape(dst_idx)[1])
dz_jlk = self.dz_iilk[src_idx, dst_idx, np.minimum(i_wd_l, self.dh_iilk.shape[2] - 1).astype(int)]
dz_mj = np.moveaxis(dz_jlk[:, :, 0], 0, 1)
wd_m = wd_l
else:
# dst_idx independent of wind direction
dw_ijlk, hcw_ijlk, dh_ijlk = StraightDistance.__call__(self, wd_l=wd_l,
src_idx=src_idx, dst_idx=dst_idx)
# +0 ~ autograd safe copy (broadcast_to returns readonly array)
dz_ijlk = np.broadcast_to(self.dz_ijlk[src_idx][:, dst_idx], dw_ijlk.shape) + 0
I, J, L, K = dw_ijlk.shape
dw_mj, hcw_mj, dh_mj, dz_mj = [np.moveaxis(v, 1, 2).reshape(I * L, J)
for v in [dw_ijlk, hcw_ijlk, dh_ijlk, dz_ijlk]]
wd_m = np.tile(wd_l, I)
start_points_m = np.repeat(start_points_m, L, 0)
stream_lines = self.vectorField.stream_lines(wd_m, start_points=start_points_m, dw_stop=dw_mj.max(1),
step_size=self.step_size)
dxyz = np.diff(np.concatenate([stream_lines[:, :1], stream_lines], 1), 1, -2)
length_is = np.cumsum(np.sqrt(np.sum(dxyz**2, -1)), -1)
dist_xyz = stream_lines - start_points_m[:, na]
t = np.deg2rad(270 - wd_m)[:, na]
dw_is = dist_xyz[:, :, 0] * np.cos(t) + dist_xyz[:, :, 1] * np.sin(t)
hcw_is = dist_xyz[:, :, 0] * np.sin(t) - dist_xyz[:, :, 1] * np.cos(t)
for m, (dw_j, dw_s, hcw_s, dh_s, length_s) in enumerate(
zip(dw_mj, dw_is, hcw_is, dist_xyz[:, :, 2], length_is)):
dw = dw_j > 0
hcw_mj[m, dw] += np.interp(dw_j[dw], dw_s, hcw_s)
dh_mj[m, dw] -= np.interp(dw_j[dw], dw_s, dh_s)
dw_mj[m, dw] = np.interp(dw_j[dw], dw_s, length_s)
# streamline dh contains absolute height different, but pywake needs differences relative to ground, so
# we need to subtract elevation differences, dz
dh_mj += dz_mj
if len(np.shape(dst_idx)) == 2:
return np.array([np.moveaxis(v, 0, 1)[na, :, :, na] for v in [dw_mj, hcw_mj, dh_mj]])
else:
return [v.reshape((I, J, L, K)) for v in [dw_mj, hcw_mj, dh_mj]]
def main():
if __name__ == '__main__':
wt = V80()
vf3d = VectorField3D.from_WaspGridSite(ParqueFicticioSite())
site = ParqueFicticioSite(distance=JITStreamlineDistance(vf3d))
x, y = site.initial_position[:].T
wfm = NOJ(site, wt)
wd = 330
sim_res = wfm(x, y, wd=[wd], ws=10)
fm = sim_res.flow_map(XYGrid(x=np.linspace(site.ds.x[0].item(), site.ds.x[-1].item(), 500),
y=np.linspace(site.ds.y[0].item(), site.ds.y[-1].item(), 500)))
stream_lines = vf3d.stream_lines(wd=np.full(x.shape, wd), start_points=np.array([x, y, np.full(x.shape, 70)]).T,
dw_stop=y - 6504700)
fm.plot_wake_map()
for sl in stream_lines:
plt.plot(sl[:, 0], sl[:, 1])
plt.show()
main()
warnings.warn('jit_streamline_distance.JITStreamLineDistance has been renamed to streamline_distance.StreamLineDistance', DeprecationWarning)

@@ -31,3 +31,3 @@ import pytest

from py_wake.deficit_models.utils import ct2a_mom1d
from py_wake.wind_turbines._wind_turbines import WindTurbine, WindTurbines
from py_wake.wind_turbines._wind_turbines import WindTurbines
from py_wake.site._site import UniformSite

@@ -67,6 +67,6 @@ from py_wake.deficit_models.rans_lut import RANSLUTDemoDeficit

smooth2zero_x=250, smooth2zero_y=50),
(404307.913694007, [9962.5736, 9801.78637, 12608.917, 15452.89633, 22577.55462,
27901.06282, 43479.02414, 49825.74736, 25105.68548, 15542.67624,
16918.79822, 35640.98079, 76856.89565, 19752.83273, 13882.09085,
8998.39151])),
(404307.96007812454, [9962.59462, 9801.78637, 12608.917, 15452.89633, 22577.55462,
27901.06281, 43479.02414, 49825.74736, 25105.71085, 15542.67624,
16918.79822, 35640.98079, 76856.89565, 19752.83273, 13882.09085,
8998.39151])),
(GCLDeficit(), (370863.6246093183,

@@ -232,4 +232,4 @@ [9385.75387, 8768.52105, 11450.13309, 14262.42186, 21178.74926,

y_j = np.linspace(-1500, 1500, 100)
flow_map = wf_model(x, y, wd=0, ws=9).flow_map(HorizontalGrid(x_j, y_j))
wf_model(x, y, wd=1e-6, ws=9).flow_map(HorizontalGrid(x_j[100:133:2], y_j[[49]]))
flow_map = wf_model(x, y, wd=1e-6, ws=9).flow_map(HorizontalGrid(x_j, y_j))
X, Y = flow_map.X, flow_map.Y

@@ -349,17 +349,17 @@ Z = flow_map.WS_eff_xylk[:, :, 0, 0]

# test that the result is equal to last run (no evidens that these number are correct)
[(BastankhahGaussianDeficit(ct2a=ct2a_mom1d), (345846.3355259293,
[8835.30563, 7877.90062, 11079.66832, 13565.65235, 18902.99769,
24493.53897, 38205.75284, 40045.9948, 22264.97018, 12662.90784,
14650.96535, 31289.90349, 65276.92307, 17341.39229, 12021.3049,
7331.15717])),
[(BastankhahGaussianDeficit(ct2a=ct2a_mom1d), (345844.56385386083,
[8835.30376, 7877.89335, 11079.53783, 13565.45008, 18902.94686,
24493.17471, 38205.304, 40045.95482, 22264.96545, 12662.88276,
14650.96516, 31289.89954, 65276.44783, 17341.3905, 12021.30506,
7331.14214])),
(ZongGaussianDeficit(ct2a=ct2a_mom1d, eps_coeff=0.35),
(342944.4057168523, [8674.44232, 7806.22033, 11114.78804, 13549.48197, 18895.50866,
24464.34244, 38326.85532, 39681.61999, 21859.59465, 12590.25899,
14530.24656, 31158.81189, 63812.14454, 17268.73912, 11922.25359,
7289.09731])),
(342945.18621135753, [8674.44233, 7806.22057, 11114.78784, 13549.48164, 18895.50866,
24464.34303, 38326.85602, 39681.61877, 21860.37542, 12590.25945,
14530.24631, 31158.81143, 63812.14454, 17268.73937, 11922.2538,
7289.09705])),
(NiayifarGaussianDeficit(ct2a=ct2a_mom1d),
(349044.6891842835, [8888.36909, 8025.38191, 11134.3202, 13643.08009, 19503.25093,
24633.33906, 38394.20758, 40795.69138, 22398.69011, 12972.04355,
14504.45911, 31328.69308, 66049.04782, 17362.89014, 11901.09465,
7510.13048])),
(349044.6889144409, [8888.3691, 8025.38213, 11134.32002, 13643.07981, 19503.25093,
24633.33957, 38394.20821, 40795.69026, 22398.6901, 12972.04395,
14504.45888, 31328.69272, 66049.04782, 17362.89034, 11901.09484,
7510.13025])),

@@ -375,3 +375,3 @@ ])

aep_ilk = wf_model(x, y, wd=np.arange(0, 360, 22.5), ws=[9.8]).aep_ilk(normalize_probabilities=True)
aep_ilk = wf_model(x, y, wd=np.arange(0, 360, 22.5) + 1e-6, ws=[9.8]).aep_ilk(normalize_probabilities=True)
aep_MW_l = aep_ilk.sum((0, 2)) * 1000

@@ -495,4 +495,4 @@

*args, **kwargs),
(404411.56948244764, [9963.95691, 9804.76072, 12613.39798, 15457.51387, 22582.08811,
27909.40005, 43494.4758, 49840.86701, 25109.17142, 15546.80155,
(404411.61587925453, [9963.97791, 9804.76072, 12613.39798, 15457.51387, 22582.08811,
27909.40005, 43494.4758, 49840.86701, 25109.19683, 15546.80155,
16923.11586, 35647.55255, 76875.57937, 19756.4749, 13885.63352,

@@ -499,0 +499,0 @@ 9000.77984])),

@@ -37,10 +37,11 @@ from py_wake import np

y_j = np.linspace(-1500, 1500, 300)
flow_map70 = simres.flow_map(HorizontalGrid(x_j, y_j, h=70))
flow_map73 = simres.flow_map(HorizontalGrid(x_j, y_j, h=73))
X, Y = flow_map70.XY
Z70 = flow_map70.WS_eff_xylk[:, :, 0, 0]
Z73 = flow_map73.WS_eff_xylk[:, :, 0, 0]
if 0:
flow_map70 = simres.flow_map(HorizontalGrid(x_j, y_j, h=70))
flow_map73 = simres.flow_map(HorizontalGrid(x_j, y_j, h=73))
if 0:
X, Y = flow_map70.XY
Z70 = flow_map70.WS_eff_xylk[:, :, 0, 0]
Z73 = flow_map73.WS_eff_xylk[:, :, 0, 0]
flow_map70.plot_wake_map(levels=np.arange(6, 10.5, .1))

@@ -52,8 +53,27 @@ plt.plot(X[0], Y[140])

plt.plot(X[0, 100:400:10], Z70[140, 100:400:10], '.')
print(list(np.round(Z70.data[140, 100:400:10], 4)))
print(list(np.round(Z73.data[140, 100:400:10], 4)))
print(np.round(Z70.data[140, 100:400:10], 4).tolist())
print(np.round(Z73.data[140, 100:400:10], 4).tolist())
plt.legend()
plt.show()
flow_map70 = simres.flow_map(HorizontalGrid(x_j[100:400:10], y_j[[140]], h=70))
flow_map73 = simres.flow_map(HorizontalGrid(x_j[100:400:10], y_j[[140]], h=73))
X, Y = flow_map70.XY
Z70 = flow_map70.WS_eff_xylk[0, :, 0, 0]
Z73 = flow_map73.WS_eff_xylk[0, :, 0, 0]
npt.assert_array_almost_equal(
Z70,
[10.0199, 10.0235, 10.0293, 10.0358, 9.8078, 5.5094, 4.718, 9.4776, 10.034, 10.0113, 10.0116, 10.027, 10.0617,
9.4429, 5.6, 8.8807, 10.0984, 10.0073, 9.9856, 9.96, 9.0589, 7.4311, 3.7803, 6.5824, 10.1399, 10.0271,
9.9978, 9.9919, 9.9907, 9.9906], 4)
npt.assert_array_almost_equal(
Z73,
[10.0198, 10.0234, 10.0291, 10.0351, 9.8118, 5.6347, 4.7181, 9.4902, 10.0332, 10.0113, 10.0116, 10.0267,
10.0607, 9.4732, 5.5569, 8.9562, 10.0969, 10.0073, 9.9858, 9.9601, 9.0821, 7.4662, 3.8426, 6.6404, 10.1375,
10.0268, 9.9979, 9.992, 9.9908, 9.9906], 4)
def test_rans_lut():

@@ -76,23 +96,3 @@ # move turbine 1 600 300

0.80567201, 0.80495685])
x_j = np.linspace(-1500, 1500, 500)
y_j = np.linspace(-1500, 1500, 300)
flow_map70 = simres.flow_map(HorizontalGrid(x_j, y_j, h=70))
flow_map73 = simres.flow_map(HorizontalGrid(x_j, y_j, h=73))
X, Y = flow_map70.XY
Z70 = flow_map70.WS_eff_xylk[:, :, 0, 0]
Z73 = flow_map73.WS_eff_xylk[:, :, 0, 0]
if 0:
flow_map70.plot_wake_map(levels=np.arange(6, 10.5, .1))
plt.plot(X[0], Y[140])
plt.figure()
plt.plot(X[0], Z70[140, :], label="Z=70m")
plt.plot(X[0], Z73[140, :], label="Z=73m")
plt.plot(X[0, 100:400:10], Z70[140, 100:400:10], '.')
print(list(np.round(Z70.data[140, 100:400:10], 4)))
print(list(np.round(Z73.data[140, 100:400:10], 4)))
plt.legend()
plt.show()
# Test Power as RANS post step

@@ -157,3 +157,4 @@ dataset = xr.open_dataset(demo_lut)

# Test MOST shear, stable.
# WS_eff_star does not change since we not change the actual simulation using Site with MOSTShear and LUTs with stability
# WS_eff_star does not change since we not change the actual simulation
# using Site with MOSTShear and LUTs with stability
aDControl2 = ADControl.from_lut([dataset, lut2], wts, ws_cutin=4, ws_cutout=25, dws=1.0, cal_TI=0.06, cal_zeta=0.5)

@@ -215,23 +216,2 @@ ellipsys_power, WS_eff_star, ct_star = get_Ellipsys_equivalent_output(simres, aDControl2)

x_j = np.linspace(-1500, 1500, 500)
y_j = np.linspace(-1500, 1500, 300)
flow_map70 = simres.flow_map(HorizontalGrid(x_j, y_j, h=70), wd=30, ws=10)
flow_map73 = simres.flow_map(HorizontalGrid(x_j, y_j, h=73), wd=30, ws=10)
X, Y = flow_map70.XY
Z70 = flow_map70.WS_eff_xylk[:, :, 0, 0]
Z73 = flow_map73.WS_eff_xylk[:, :, 0, 0]
if 0:
flow_map70.plot_wake_map(levels=np.arange(6, 10.5, .1))
plt.plot(X[0], Y[140])
plt.figure()
plt.plot(X[0], Z70[140, :], label="Z=70m")
plt.plot(X[0], Z73[140, :], label="Z=73m")
plt.plot(X[0, 100:400:10], Z70[140, 100:400:10], '.')
print(list(np.round(Z70.data[140, 100:400:10], 4)))
print(list(np.round(Z73.data[140, 100:400:10], 4)))
plt.legend()
plt.show()
# Test Power as RANS post step

@@ -238,0 +218,0 @@ dataset = xr.open_dataset(demo_lut)

@@ -499,1 +499,55 @@ from py_wake.flow_map import HorizontalGrid, YZGrid, Points, XYGrid, XZGrid

sim_res.flow_map(HorizontalGrid(x=np.linspace(0, 1000, 10), y=np.linspace(0, 1000, 10)), time=[0, 1, 2])
def test_wd_dependent_dst():
wfm = IEA37CaseStudy1(16)
x, y = wfm.site.initial_position.T
sim_res = wfm(x, y, wd=np.arange(360), ws=np.arange(3, 25))
X, Y = np.meshgrid(np.linspace(-1500, 3500, 10), np.linspace(-1500, 1500, 10))
dw, hcw = X.flatten(), Y.flatten()
dh = dw * 0
wf_h = wfm.windTurbines.hub_height()
wf_x = 0
wf_y = 0
from tqdm import tqdm
from numpy import newaxis as na
def run_loop():
wd_lst = sim_res.wd.values
theta = np.deg2rad(270 - wd_lst)
co, si = np.cos(theta), np.sin(theta)
x_jl = co[na] * dw[:, na] - hcw[:, na] * si[na] + wf_x
y_jl = si[na] * dw[:, na] + hcw[:, na] * co[na] + wf_y
h_j = dh + wf_h
for wd, x_j, y_j in zip(wd_lst, x_jl.T, y_jl.T):
lw_j, WS_eff_jlk, TI_eff_jlk = wfm._flow_map(x_j[:, na], y_j[:, na], h_j[:, na], sim_res.localWind,
wd, sim_res.ws, sim_res)
if 0:
plt.contourf(x_j.reshape(X.shape), y_j.reshape(X.shape),
WS_eff_jlk[:, :, 7].reshape(X.shape), levels=50)
wfm.windTurbines.plot(x, y)
plt.axis('scaled')
plt.show()
timeit(run_loop, verbose=1, line_profile=0)()
def run_vec():
wd_lst = sim_res.wd.values
theta = np.deg2rad(270 - wd_lst)
co, si = np.cos(theta), np.sin(theta)
x_jl = co[na] * dw[:, na] - hcw[:, na] * si[na] + wf_x
y_jl = si[na] * dw[:, na] + hcw[:, na] * co[na] + wf_y
h_jl = dh[:, na] + wf_h
lw_j, WS_eff_jlk, TI_eff_jlk = wfm._flow_map(x_jl, y_jl, h_jl, sim_res.localWind,
wd_lst, sim_res.ws, sim_res)
if 0:
for l, wd_lst in enumerate(wd_lst):
plt.contourf(x_jl[:, l].reshape(X.shape), y_jl[:, l].reshape(X.shape),
WS_eff_jlk[:, l, 7].reshape(X.shape), levels=50)
wfm.windTurbines.plot(x, y)
plt.axis('scaled')
plt.show()
timeit(run_vec, verbose=1, line_profile=0)()
import importlib
import os
import pkgutil
import sys
import warnings
from unittest import mock
import pytest
import sys
import py_wake
from unittest import mock
from py_wake.flow_map import Grid

@@ -22,4 +22,7 @@

m = importlib.import_module(modname)
except DeprecationWarning:
pass
except Exception:
print(f"!!!!!! failed to import {modname}")
raise

@@ -26,0 +29,0 @@

@@ -44,3 +44,3 @@ import os

notebook.remove_empty_end_cell()
notebook.check_pip_header()
# notebook.check_pip_header()
except Exception as e:

@@ -47,0 +47,0 @@ raise Exception(notebook.filename + " failed") from e

@@ -1,23 +0,31 @@

from py_wake import np
import warnings
import matplotlib.pyplot as plt
import pytest
from numpy import newaxis as na
from py_wake.tests import npt
from py_wake.site.distance import StraightDistance, TerrainFollowingDistance
from py_wake.site._site import UniformSite
import pytest
from py_wake import NOJ, np
from py_wake.deficit_models.gaussian import (
BastankhahGaussian,
BastankhahGaussianDeficit,
)
from py_wake.deficit_models.utils import ct2a_mom1d
from py_wake.examples.data.hornsrev1 import V80
from py_wake.examples.data.iea37._iea37 import IEA37_WindTurbines
from py_wake import NOJ
from py_wake.examples.data.ParqueFicticio import ParqueFicticioSite
from py_wake.flow_map import HorizontalGrid, XYGrid, XZGrid, Points
import matplotlib.pyplot as plt
from py_wake.flow_map import HorizontalGrid, Points, XYGrid, XZGrid
from py_wake.site._site import UniformSite
from py_wake.site.distance import StraightDistance, TerrainFollowingDistance
from py_wake.site.streamline_distance import StreamlineDistance
from py_wake.tests import npt
from py_wake.tests.test_wind_farm_models.test_enginering_wind_farm_model import (
OperatableV80,
)
from py_wake.utils.streamline import VectorField3D
from py_wake.site.jit_streamline_distance import JITStreamlineDistance
from py_wake.examples.data.hornsrev1 import V80
from py_wake.wind_farm_models.engineering_models import (
All2AllIterative,
PropagateDownwind,
)
from py_wake.tests.test_wind_farm_models.test_enginering_wind_farm_model import OperatableV80
from py_wake.wind_farm_models.engineering_models import PropagateDownwind, All2AllIterative
from py_wake.deficit_models.gaussian import BastankhahGaussianDeficit, BastankhahGaussian
from py_wake.deficit_models.utils import ct2a_mom1d
import warnings
class FlatSite(UniformSite):

@@ -54,13 +62,16 @@ def __init__(self, distance):

def test_flat_distances(distance):
x = [0, 50, 100, 100, 0]
y = [100, 100, 100, 0, 0]
h = [0, 10, 20, 30, 0]
z = [0, 0, 0, 0]
x = np.array([0, 50, 100, 100, 0])
y = np.array([100, 100, 100, 0, 0])
h = np.array([0, 10, 20, 30, 0])
wdirs = [-1e-10, 30, 90 + 1e-10]
site = FlatSite(distance=distance)
site.distance.setup(src_x_ilk=x, src_y_ilk=y, src_h_ilk=h, src_z_ilk=z)
dw_ijlk, hcw_ijlk, dh_ijlk = site.distance(wd_l=np.array(wdirs), WD_ilk=None, src_idx=[0, 1, 2, 3], dst_idx=[4])
dw_indices_lkd = site.distance.dw_order_indices(np.array(wdirs))
src_idx = [0, 1, 2, 3]
dw_ijlk, hcw_ijlk, dh_ijlk = site.distance(src_x_ilk=x[src_idx], src_y_ilk=y[src_idx],
src_h_ilk=h[src_idx],
wd_l=np.array(wdirs),
dst_xyh_jlk=(x[[4]], y[[4]], h[[4]]))
dw_indices_lkd = site.distance.dw_order_indices(x, y, np.array(wdirs))
if 0:

@@ -78,6 +89,6 @@ distance.plot(wd_ilk=np.array(wdirs)[na, :, na], src_i=[0, 1, 2, 3], dst_i=[4])

[[-100, -86.6025404, 0]]])
npt.assert_array_almost_equal(dh_ijlk[..., 0], [[[0, 0, 0]],
[[-10, -10, -10]],
[[-20, -20, -20]],
[[-30, -30, -30]]])
npt.assert_array_almost_equal(dh_ijlk[..., :1, 0], [[[0]],
[[-10]],
[[-20]],
[[-30]]])
npt.assert_array_equal(dw_indices_lkd[:, 0, :], [[0, 1, 2, 4, 3],

@@ -98,5 +109,6 @@ [2, 1, 0, 3, 4],

site = FlatSite(distance=distance)
site.distance.setup(src_x_ilk=x, src_y_ilk=y, src_h_ilk=h, src_z_ilk=z, dst_xyhz_j=(x, y, [1, 2, 3], z))
dw_ijlk, hcw_ijlk, dh_ijlk = site.distance(wd_l=np.array(wdirs), WD_ilk=None)
dw_indices_lkd = distance.dw_order_indices(wdirs)
dw_ijlk, hcw_ijlk, dh_ijlk = site.distance(src_x_ilk=x, src_y_ilk=y, src_h_ilk=h,
dst_xyh_jlk=(x, y, [1, 2, 3]), wd_l=np.array(wdirs))
dw_indices_lkd = distance.dw_order_indices(x, y, wdirs)
if 0:

@@ -126,3 +138,3 @@ distance.plot(wd_ilk=np.array(wdirs)[na, :, na])

# check dw indices
npt.assert_array_equal(dw_indices_lkd[:, 0], [[1, 0, 2],
npt.assert_array_equal(dw_indices_lkd[:, 0], [[0, 1, 2],
[1, 0, 2]])

@@ -140,4 +152,3 @@

ws=site.default_ws)
site.distance.setup(x, y, np.zeros_like(x), np.zeros_like(x))
dw_iilk, hcw_iilk, _ = site.wt2wt_distances(WD_ilk=lw.WD_ilk, wd_l=None)
dw_iilk, hcw_iilk, _ = site.distance(x, y, np.zeros_like(x), WD_ilk=lw.WD_ilk)
# Wind direction.

@@ -176,3 +187,6 @@ wdir = np.rad2deg(np.arctan2(hcw_iilk, dw_iilk))

WD = np.array(np.r_[turning, [0] * len(ghost_y)]) + 270
sim_res = wfm(np.r_[[0, 500], ghost_x], np.r_[[0, 0], ghost_y], operating=operation, wd=[0, 270], WD=WD)
sim_res = wfm(np.r_[[0, 500], ghost_x], np.r_[[0, 0], ghost_y], operating=operation, wd=[270], WD=WD)
if 0:
sim_res.flow_map(wd=270).plot_wake_map()
plt.show()
with warnings.catch_warnings():

@@ -215,5 +229,5 @@ warnings.simplefilter('ignore', UserWarning)

hc.distance.setup(src_x_ilk=src_x, src_y_ilk=src_y, src_h_ilk=src_x * 0, src_z_ilk=src_x * 0,
dst_xyhz_j=(dst_x, dst_y, dst_x * 0, dst_x * 0))
dw_ijlk, hcw_ijlk, _ = hc.distance(wd_l=np.array([0, 90]), WD_ilk=None)
dw_ijlk, hcw_ijlk, _ = hc.distance(src_x_ilk=src_x, src_y_ilk=src_y, src_h_ilk=src_x * 0,
wd_l=np.array([0, 90]), WD_ilk=None,
dst_xyh_jlk=(dst_x, dst_y, dst_x * 0))

@@ -273,4 +287,3 @@ if 0:

y_j = x_j * 0
site.distance.setup([-200], [0], [130], [0], (x_j, y_j, y_j + 130, y_j * 0))
d = site.distance(wd_l=[270])[0][0, :, 0, 0]
d = site.distance([-200], [0], [130], [0], wd_l=[270], dst_xyh_jlk=(x_j, y_j, y_j + 130))[0][0, :, 0, 0]

@@ -294,10 +307,12 @@ ref = x_j - x_j[0]

x = [0, 50, 100, 100, 0]
y = [100, 100, 100, 0, 0]
h = [0, 10, 20, 30, 0]
z = [0, 0, 0, 0, 0]
x = np.array([0, 50, 100, 100, 0])
y = np.array([100, 100, 100, 0, 0])
h = np.array([0, 10, 20, 30, 0])
wdirs = [0, 30, 90]
distance = StraightDistance()
distance.setup(src_x_ilk=x, src_y_ilk=y, src_h_ilk=h, src_z_ilk=z)
distance.plot(wd_l=np.array(wdirs), src_idx=[0], dst_idx=[3])
src_idx = [0]
dst_idx = [3]
distance.plot(src_x_ilk=x[src_idx], src_y_ilk=y[src_idx], src_h_ilk=h[src_idx], wd_l=np.array(wdirs),
dst_xyh_jlk=[x[dst_idx], y[dst_idx], h[dst_idx]])
if 0:

@@ -308,7 +323,7 @@ plt.show()

def test_JITStreamlinesparquefictio():
def test_Streamlinesparquefictio():
site = ParqueFicticioSite()
wt = IEA37_WindTurbines()
vf3d = VectorField3D.from_WaspGridSite(site)
site.distance = JITStreamlineDistance(vf3d)
site.distance = StreamlineDistance(vf3d)

@@ -319,3 +334,3 @@ x, y = site.initial_position[:].T

sim_res = wfm(x, y, wd=wd, ws=10)
dw = site.distance(wd_l=wd, WD_ilk=np.repeat(wd[na, na], len(x), 0))[0][:, :, 0, 0]
dw = site.distance(x, y, x * 0 + wt.hub_height(), wd_l=wd, WD_ilk=np.repeat(wd[na, na], len(x), 0))[0][:, :, 0, 0]
# streamline downwind distance (positive numbers, upper triangle) cannot be shorter than

@@ -338,3 +353,3 @@ # straight line distances in opposite direction (negative numbers, lower triangle)

def test_JITStreamlinesparquefictio_yz():
def test_Streamlinesparquefictio_yz():
site = ParqueFicticioSite()

@@ -345,3 +360,3 @@ site.ds.Turning[:] *= 0

vf3d = VectorField3D.from_WaspGridSite(site)
site.distance = JITStreamlineDistance(vf3d)
site.distance = StreamlineDistance(vf3d)

@@ -356,3 +371,4 @@ x, y = site.initial_position[3].T

sim_res = wfm(wt_x, wt_y, wd=wd, ws=10)
dw = site.distance(wd_l=wd, WD_ilk=np.repeat(wd[na, na], len(wt_x), 0))[0][:, :, 0, 0]
dw = site.distance(wt_x, wt_y, wt_x * 0 + wt.hub_height(), wd_l=wd,
WD_ilk=np.repeat(wd[na, na], len(wt_x), 0))[0][:, :, 0, 0]
# streamline downwind distance (positive numbers, upper triangle) cannot be shorter than

@@ -359,0 +375,0 @@ # straight line distances in opposite direction (negative numbers, lower triangle)

@@ -220,4 +220,3 @@ import os

ws=site.default_ws)
site.distance.setup(x, y, np.zeros_like(x), np.zeros_like(x))
dw_iil, hcw_iil, _ = site.wt2wt_distances(wd_l=lw.wd)
dw_iil, hcw_iil, _ = site.distance(x, y, x * 0, wd_l=lw.wd)
# Wind direction.

@@ -227,3 +226,3 @@ wdir = np.rad2deg(np.arctan2(hcw_iil, dw_iil))

wdir[:, 0, 0, 0],
[180, -90, -18, 54, 126, -162, -90, -54, -18, 18, 54, 90, 126, 162, -162, -126],
[0, -90, -18, 54, 126, -162, -90, -54, -18, 18, 54, 90, 126, 162, -162, -126],
atol=1e-4)

@@ -230,0 +229,0 @@

@@ -203,5 +203,5 @@ from py_wake import np

x, y = site.initial_position.T
site.distance.setup(src_x_ilk=x, src_y_ilk=y, src_h_ilk=np.array([70]), src_z_ilk=x * 0,
dst_xyhz_j=(x, y, np.array([70]), x * 0))
dw_ijlk, cw_ijlk, dh_ijlk = site.distance(wd_l=[0])
dw_ijlk, cw_ijlk, dh_ijlk = site.distance(src_x_ilk=x, src_y_ilk=y, src_h_ilk=np.array([70]), wd_l=[0],
dst_xyh_jlk=(x, y, np.array([70])))
npt.assert_almost_equal(dw_ijlk[0, :, 0, 0], dw_ref)

@@ -208,0 +208,0 @@

@@ -11,3 +11,4 @@ from autograd import numpy as anp

from py_wake.utils import gradients
from py_wake.utils.gradients import autograd, plot_gradients, fd, cs, hypot, cabs, interp, set_gradient_function
from py_wake.utils.gradients import autograd, plot_gradients, fd, cs, hypot, cabs, interp, set_gradient_function, \
item_assign
from py_wake.wind_turbines import WindTurbines

@@ -509,1 +510,22 @@ from py_wake.wind_turbines import _wind_turbines

autograd(f)([2, 3, 4])
@pytest.mark.parametrize('idx', [[0, 3, 4], slice(1, 6, 2)])
@pytest.mark.parametrize('axis', [0, 1])
def test_item_assign(idx, axis):
def f(x):
if axis == 0:
v = x[idx]
else:
v = x[:, idx]
x = item_assign(x + 0., idx=idx, values=v * 2, axis=axis)
return x.sum()
x = np.arange(56).reshape((8, 7))
v = x.take(np.arange(x.shape[axis])[idx], axis) * 2
npt.assert_array_equal(item_assign(x + 0., idx, values=v, axis=axis),
item_assign(x + 0., idx, values=v, axis=axis, test=1))
npt.assert_array_almost_equal(fd(f)(x), cs(f)(x))
npt.assert_array_equal(cs(f)(x), autograd(f)(x))

@@ -1,32 +0,47 @@

from numpy import newaxis as na
import warnings
import matplotlib.pyplot as plt
import pytest
import xarray as xr
from numpy import newaxis as na
import matplotlib.pyplot as plt
from py_wake import np
from py_wake.deficit_models.deficit_model import WakeDeficitModel, BlockageDeficitModel
from py_wake.deficit_models.gaussian import BastankhahGaussianDeficit, NiayifarGaussianDeficit
from py_wake.deficit_models.deficit_model import BlockageDeficitModel, WakeDeficitModel
from py_wake.deficit_models.gaussian import (
BastankhahGaussianDeficit,
NiayifarGaussianDeficit,
)
from py_wake.deficit_models.no_wake import NoWakeDeficit
from py_wake.deficit_models.noj import NOJDeficit
from py_wake.deflection_models.deflection_model import DeflectionModel
from py_wake.examples.data.ParqueFicticio._parque_ficticio import ParqueFicticioSite
from py_wake.examples.data.hornsrev1 import V80, Hornsrev1Site
from py_wake.examples.data.iea34_130rwt._iea34_130rwt import IEA34_130_1WT_Surrogate
from py_wake.examples.data.iea37._iea37 import IEA37_WindTurbines, IEA37Site, IEA37WindTurbines
from py_wake.examples.data.iea37._iea37 import (
IEA37_WindTurbines,
IEA37Site,
IEA37WindTurbines,
)
from py_wake.examples.data.ParqueFicticio._parque_ficticio import ParqueFicticioSite
from py_wake.ground_models.ground_models import GroundModel
from py_wake.rotor_avg_models.area_overlap_model import AreaOverlapAvgModel
from py_wake.rotor_avg_models.rotor_avg_model import RotorAvgModel
from py_wake.site.distance import StraightDistance
from py_wake.site.shear import LogShear, PowerShear, Shear, MOSTShear
from py_wake.superposition_models import SuperpositionModel, AddedTurbulenceSuperpositionModel
from py_wake.site.shear import LogShear, MOSTShear, PowerShear, Shear
from py_wake.site.streamline_distance import StreamlineDistance
from py_wake.site.xrsite import XRSite
from py_wake.superposition_models import (
AddedTurbulenceSuperpositionModel,
SuperpositionModel,
)
from py_wake.tests import npt
from py_wake.turbulence_models.stf import STF2017TurbulenceModel, STF2005TurbulenceModel
from py_wake.turbulence_models.stf import STF2005TurbulenceModel, STF2017TurbulenceModel
from py_wake.turbulence_models.turbulence_model import TurbulenceModel
from py_wake.utils import gradients
from py_wake.utils.gradients import autograd, plot_gradients, fd, cs
from py_wake.utils.gradients import autograd, cs, fd, plot_gradients
from py_wake.utils.model_utils import get_models
from py_wake.wind_farm_models.engineering_models import PropagateDownwind, All2AllIterative, EngineeringWindFarmModel
from py_wake.deficit_models.noj import NOJDeficit
from py_wake.site.jit_streamline_distance import JITStreamlineDistance
from py_wake.site.xrsite import XRSite
from py_wake.rotor_avg_models.area_overlap_model import AreaOverlapAvgModel
import warnings
from py_wake.wind_farm_models.engineering_models import (
All2AllIterative,
EngineeringWindFarmModel,
PropagateDownwind,
)

@@ -239,3 +254,3 @@

def test_distance_models(model):
if model not in [None, JITStreamlineDistance]:
if model not in [None, StreamlineDistance]:
site = ParqueFicticioSite(distance=model())

@@ -242,0 +257,0 @@ x, y = site.initial_position[3]

@@ -20,6 +20,6 @@ import time

try:
# check 3 successive runs (all should take .1 s and 10MB
# check 3 successive runs (all should take 1 s and 10MB
for _ in range(3):
r, t, m = profileit(f)(.1, 100)
npt.assert_allclose(t, 0.1, rtol=.2)
r, t, m = profileit(f)(1, 100)
npt.assert_allclose(t, 1, rtol=.2)
npt.assert_allclose(m, 100, rtol=.2)

@@ -26,0 +26,0 @@ return

@@ -46,2 +46,3 @@ import numpy.testing as npt

wfm = Nygaard_2022(site, V80())
wd = ds.WD.mean(['x', 'y', 'h'])

@@ -60,3 +61,3 @@ test_x, test_y = [

_f = np.zeros_like(T)
sim_res = wfm(x, y, time=T, ws=_f, wd=_f, n_cpu=n_cpu)
sim_res = wfm(x, y, time=T, ws=_f, wd=wd, n_cpu=n_cpu)
return sim_res.aep().sum().values

@@ -72,3 +73,3 @@

ws=_f,
wd=_f,
wd=wd,
time=T,

@@ -75,0 +76,0 @@ n_cpu=n_cpu,

@@ -39,2 +39,28 @@ from py_wake import np

def item_assign(x, idx, values, axis=0, test=0):
if axis:
x = np.moveaxis(x, axis, 0)
values = np.moveaxis(values, axis, 0)
assert x[idx].shape == np.shape(values)
if isinstance(x, ArrayBox) or test:
if isinstance(idx, slice):
idx = np.arange(x.shape[axis])[idx]
res = []
idx_i = 0
for x_i in range(x.shape[0]):
if idx_i < len(idx) and x_i == idx[idx_i]:
res.append(values[idx_i])
idx_i += 1
else:
res.append(x[x_i])
x = np.array(res)
else:
# x = x.copy()
x[idx] = values
if axis:
x = np.moveaxis(x, 0, axis)
return x
# def asanyarray(x, dtype=None, order=None):

@@ -41,0 +67,0 @@ # if isinstance(x, (ArrayBox)):

@@ -176,3 +176,3 @@ import inspect

from py_wake.ground_models.ground_models import NoGround
from py_wake.site.jit_streamline_distance import JITStreamlineDistance
from py_wake.site.streamline_distance import StreamlineDistance
return {

@@ -193,3 +193,3 @@ "WindFarmModel": ([EngineeringWindFarmModel], [], PropagateDownwind),

"Shear": ([], [], None),
"StraightDistance": ([], [JITStreamlineDistance], None),
"StraightDistance": ([], [StreamlineDistance], None),

@@ -287,5 +287,5 @@ }

x, y = map(np.asarray, [x, y])
wfm.site.distance.setup(src_x_ilk=[[[0]]], src_y_ilk=[[[0]]], src_h_ilk=[[[0]]], src_z_ilk=[[[0]]],
dst_xyhz_j=(x, y, x * 0, x * 0))
dw_ijlk, hcw_ijlk, dh_ijlk = wfm.site.distance(wd_l=wd)
dw_ijlk, hcw_ijlk, dh_ijlk = wfm.site.distance(src_x_ilk=[[[0]]], src_y_ilk=[[[0]]], src_h_ilk=[[[0]]],
dst_xyh_jlk=(x, y, x * 0), wd_l=wd)
sim_res = wfm([0], [0], ws=ws, wd=wd, **kwargs)

@@ -292,0 +292,0 @@

@@ -55,14 +55,14 @@ import multiprocessing

def get_map_func(n_cpu, verbose, desc='', unit='it'):
n_cpu = n_cpu or multiprocessing.cpu_count()
if n_cpu > 1:
map_func = get_pool_map(n_cpu)
else:
from tqdm import tqdm
# def get_map_func(n_cpu, verbose, desc='', unit='it'):
# n_cpu = n_cpu or multiprocessing.cpu_count()
# if n_cpu > 1:
# map_func = get_pool_map(n_cpu)
# else:
# from tqdm import tqdm
#
# def map_func(f, iter):
# return tqdm(map(f, iter), desc=desc, unit=unit, total=len(iter), disable=not verbose)
# return map_func
def map_func(f, iter):
return tqdm(map(f, iter), desc=desc, unit=unit, total=len(iter), disable=not verbose)
return map_func
def get_starmap_func(n_cpu, verbose, desc='', unit='it', leave=True):

@@ -69,0 +69,0 @@ n_cpu = n_cpu or multiprocessing.cpu_count()

@@ -216,3 +216,4 @@ import xarray as xr

setattr(sim_res_wd, k, getattr(sim_res, k))
WS_eff_jlk = np.concatenate([sim_res.windFarmModel._flow_map(*pos_j.T, sim_res_wd)[1]
wd, ws = sim_res_wd.wd.values, sim_res_wd.ws.values
WS_eff_jlk = np.concatenate([sim_res.windFarmModel._flow_map(*[p[:, na] for p in pos_j.T], sim_res.localWind, wd, ws, sim_res_wd)[1]
for pos_j in np.array_split(cxyzg_xj.T, nflowmaps)])

@@ -224,2 +225,20 @@

# Vectorized version of get_WS_eff_ik.
# Against expectations, this version is slower with numpy 2.3.4 and scipy 1.16.3.
# The slowdown seems to
# def get_WS_eff_ilk():
# # project rotorAvgModel node positions to plane perpendicular to current wind direction
# o3 = o3_l[:, :]
# o1 = np.cross(o2, o3.T).T
# cxyzg_xijl = nx_in[na, :, :, na] * o3[:, na, na, :, ] + ny_in[na, :, :, na] * \
# o1[:, na, na, :] + nz_in[na, :, :, na] * o2[:, na, na, na] + pos[:, :, na, na]
# cxyzg_xjl = cxyzg_xijl.reshape((3, -1, L))
# wd, ws = sim_res.wd.values, sim_res.ws.values
# WS_eff_jlk = sim_res.windFarmModel._flow_map(*cxyzg_xjl, sim_res.localWind, wd, ws, sim_res)[1]
#
# # Integrate U over AD
# return np.sum(np.reshape(WS_eff_jlk, (I, ntheta * (nr - 1), L, K)) * weights[:, :, na], axis=1)
#
# WS_eff_star_ilk = get_WS_eff_ilk()
U_tab_lst = [U_CT_CP_AD[0, :] for U_CT_CP_AD in aDControl.U_CT_CP_AD]

@@ -226,0 +245,0 @@

@@ -1,7 +0,8 @@

from py_wake.utils.grid_interpolator import GridInterpolator
from py_wake import np
import xarray as xr
from numpy import newaxis as na
import xarray as xr
from py_wake import np
from py_wake.utils.grid_interpolator import GridInterpolator
class VectorField3D():

@@ -20,3 +21,3 @@ def __init__(self, da):

def __call__(self, wd, x, y, h):
def __call__(self, wd, time, x, y, h):
return self.interpolator(np.array([np.atleast_1d(v) for v in [wd, x, y, h]]).T, bounds='limit')

@@ -34,4 +35,13 @@

def stream_lines(self, wd, start_points, dw_stop, step_size=20):
wd = np.full(len(start_points), wd)
def stream_lines(self, wd, start_points, dw_stop, time=None, step_size=20):
# print(np.shape(wd), np.shape(start_points), time)
if time is None:
wd = np.full(len(start_points), wd)
time = wd * 0
# else:
# if len(start_points) == 1:
# # 1 start point, multiple wd/time
# start_points = np.broadcast_to(start_points, (len(wd), 3))
# elif len(time) == 1:
# time = wd * 0 + time
stream_lines = [start_points]

@@ -42,3 +52,3 @@ m = np.arange(len(wd))

p = stream_lines[-1].copy()
v = self(wd[m], p[m, 0], p[m, 1], p[m, 2])
v = self(wd[m], time[m], p[m, 0], p[m, 1], p[m, 2])
v = v / (np.sqrt(np.sum(v**2, -1)) / step_size)[:, na] # normalize vector distance to step_size

@@ -45,0 +55,0 @@ p[m] += v

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

__version__ = version = '2.6.13'
__version_tuple__ = version_tuple = (2, 6, 13)
__version__ = version = '2.6.14'
__version_tuple__ = version_tuple = (2, 6, 14)
__commit_id__ = commit_id = None

@@ -7,3 +7,3 @@ from abc import abstractmethod

from py_wake.deflection_models.deflection_model import DeflectionModel
from py_wake.utils.gradients import cabs
from py_wake.utils.gradients import cabs, item_assign
from py_wake.rotor_avg_models.rotor_avg_model import RotorAvgModel, NodeRotorAvgModel, WSPowerRotorAvg

@@ -19,4 +19,5 @@ from py_wake.turbulence_models.turbulence_model import TurbulenceModel

from py_wake.deficit_models.no_wake import NoWakeDeficit
from py_wake.utils.parallelization import get_map_func, get_starmap_func
from py_wake.utils.parallelization import get_starmap_func
import multiprocessing
from py_wake.wind_farm_models.external_wind_farm_models import ExternalWindFarm

@@ -54,3 +55,4 @@

def __init__(self, site, windTurbines: WindTurbines, wake_deficitModel, superpositionModel, rotorAvgModel=None,
blockage_deficitModel=None, deflectionModel=None, turbulenceModel=None, inputModifierModels=[]):
blockage_deficitModel=None, deflectionModel=None, turbulenceModel=None, inputModifierModels=[],
externalWindFarms=[]):

@@ -65,3 +67,4 @@ WindFarmModel.__init__(self, site, windTurbines)

(turbulenceModel, TurbulenceModel, 'turbulenceModel')] +
[(imm, InputModifierModel, 'inputModificierModels') for imm in inputModifierModels]):
[(imm, InputModifierModel, 'inputModificierModels') for imm in inputModifierModels] +
[(ewf, ExternalWindFarm, 'externalWindFarms') for ewf in externalWindFarms]):
check_model(model, cls, name)

@@ -72,2 +75,3 @@ if model is not None:

self.inputModifierModels = inputModifierModels
self.externalWindFarms = externalWindFarms

@@ -81,2 +85,4 @@ if isinstance(superpositionModel, (WeightedSum, CumulativeWakeSum)):

'WeightedSum and CumulativeWakeSum does not work WSPowerRotorAvg'
assert len(externalWindFarms) == 0, \
'WeightedSum and CumulativeWakeSum does not work with ExternalWindFarms'
# TI_eff requires a turbulence model

@@ -123,2 +129,4 @@ assert 'TI_eff_ilk' not in wake_deficitModel.args4deficit or turbulenceModel

self.args4all |= set(input_modifier.args4model)
for ext_wf in self.externalWindFarms:
self.args4all |= set(ext_wf.args4model)

@@ -238,2 +246,4 @@ def __str__(self):

kwargs['TI_eff_ilk'] = lw.TI_ilk + 0. # autograd-friendly copy
if 'time' in lw:
kwargs['time'] = lw.time

@@ -265,6 +275,2 @@ self._check_input(kwargs)

# Calculate down-wind and cross-wind distances
z_ilk = self.site.elevation(kwargs['x_ilk'], kwargs['y_ilk'])
self.site.distance.setup(
np.asarray(kwargs['x_ilk']), np.asarray(kwargs['y_ilk']), np.asarray(kwargs['h_ilk']), np.asarray(z_ilk))
WS_eff_ilk, TI_eff_ilk, ct_ilk, kwargs = self._calc_wt_interaction(**kwargs)

@@ -280,10 +286,13 @@

def get_map_args(self, x_j, y_j, h_j, sim_res_data, D_dst=0):
def get_map_args(self, x_j, y_j, h_j, lw, wd, ws, sim_res_data, D_dst=0):
type_i = sim_res_data.type.values
wt_d_i = self.windTurbines.diameter(type_i)
wd, ws = [np.atleast_1d(sim_res_data[k].values) for k in ['wd', 'ws']]
if 'time' in sim_res_data:
time = np.atleast_1d(sim_res_data['time'].values)
map_arg_funcs = {'time': lambda l, j: time[l]}
else:
time = False
map_arg_funcs = {}
if 'wd' in sim_res_data.dims:
sim_res_data = sim_res_data.sel(wd=wd)
wt_x_ilk = sim_res_data['x'].ilk()

@@ -300,16 +309,17 @@ WD_il = sim_res_data.WD.ilk()

def wrap(l):
def wrap(l, j):
l_ = [l, slice(0, 1)][v.shape[1] == 1]
return v[:, l_]
return wrap
map_arg_funcs = {k.replace('CT', 'ct') + '_ilk': get_ilk(k)
for k in sim_res_data if k not in ['wd_bin_size', 'ws_l', 'ws_u']}
map_arg_funcs.update({k.replace('CT', 'ct') + '_ilk': get_ilk(k)
for k in sim_res_data if k not in ['wd_bin_size', 'ws_l', 'ws_u']})
map_arg_funcs.update({
'type_il': lambda l: type_i[:, na],
'D_src_il': lambda l: wt_d_i[:, na],
'D_dst_ijl': lambda l: np.zeros((1, 1, 1)) + D_dst,
'IJLK': lambda l=slice(None), I=I, J=J, L=L, K=K: (I, J, len(np.arange(L)[l]), K)})
'type_il': lambda l, j: type_i[:, na],
'D_src_il': lambda l, j: wt_d_i[:, na],
'D_dst_ijl': lambda l, j: np.zeros((1, 1, 1)) + D_dst,
'IJLK': lambda l=slice(None), j=slice(None), I=I, J=J, L=L, K=K: (I, len(np.arange(J)[j]), len(np.arange(L)[l]), K)})
for k in ['WS', 'WD', 'TI']:
if k in sim_res_data:
if k + '_ilk' in sim_res_data.localWind.overwritten:
if k + '_ilk' in lw.overwritten:
if 'wt' not in sim_res_data[k].dims and 'i' not in sim_res_data[k].dims:

@@ -320,9 +330,7 @@ lw_j.add_ilk(k + "_ilk", sim_res_data[k])

f"The WT dependent {k} that was provided for the simulation is not available at the flow map points and therefore ignored")
return map_arg_funcs, lw_j, wd, WD_il
return map_arg_funcs, lw_j, WD_il
def _get_flow_l(self, model_kwargs, wt_x_ilk, wt_y_ilk, wt_h_ilk, x_j, y_j, h_j, wd, WD_ilk, WS_jlk, TI_jlk):
wt_z_ilk = self.site.elevation(wt_x_ilk, wt_y_ilk)
z_j = self.site.elevation(x_j, y_j)
self.site.distance.setup(wt_x_ilk, wt_y_ilk, wt_h_ilk, wt_z_ilk, (x_j, y_j, h_j, z_j))
dw_ijlk, hcw_ijlk, dh_ijlk = self.site.distance(wd_l=wd, WD_ilk=WD_ilk)
def _get_flow_l(self, model_kwargs, wt_x_ilk, wt_y_ilk, wt_h_ilk, x_jl, y_jl, h_jl, wd, WD_ilk, WS_jlk, TI_jlk):
dw_ijlk, hcw_ijlk, dh_ijlk = self.site.distance(wt_x_ilk, wt_y_ilk, wt_h_ilk, wd_l=wd, WD_ilk=WD_ilk,
time=model_kwargs.get('time', None), dst_xyh_jlk=(x_jl, y_jl, h_jl))

@@ -362,2 +370,10 @@ if self.wec != 1:

# ===============================================================================================================
# Replace deficit from external farms
# ===============================================================================================================
for i, ewf in enumerate(self.externalWindFarms, len(wt_x_ilk) - len(self.externalWindFarms)):
deficit_ijlk[i] = ewf(i=i, l=np.ones(len(wd), dtype=bool), deficit_jlk=None,
**model_kwargs,
dst_xyh_jlk=[x_jl[:, :, na], y_jl[:, :, na], h_jl[:, :, na]]
)
# ===============================================================================================================
# Calculate added Turbulence

@@ -397,4 +413,5 @@ # ===============================================================================================================

def _aep_map(self, x_j, y_j, h_j, type_j, sim_res_data, memory_GB=1, n_cpu=1):
lw_j, WS_eff_jlk, _ = self._flow_map(x_j, y_j, h_j, sim_res_data, memory_GB=memory_GB, n_cpu=n_cpu)
def _aep_map(self, x_jl, y_jl, h_jl, type_j, sim_res_data, memory_GB=1, n_cpu=1):
lw_j, WS_eff_jlk, _ = self._flow_map(x_jl, y_jl, h_jl, sim_res_data.localWind, sim_res_data.wd.values,
sim_res_data.ws.values, sim_res_data, memory_GB=memory_GB, n_cpu=n_cpu)
power_kwargs = {}

@@ -409,10 +426,11 @@ if 'type' in (self.windTurbines.powerCtFunction.required_inputs +

def _flow_map(self, x_j, y_j, h_j, sim_res_data, D_dst=0, memory_GB=1, n_cpu=1):
def _flow_map(self, x_jl, y_jl, h_jl, lw, wd, ws, sim_res_data, D_dst=0, memory_GB=1, n_cpu=1):
"""call this function via SimulationResult.flow_map"""
arg_funcs, lw_j, wd, WD_il = self.get_map_args(x_j, y_j, h_j, sim_res_data, D_dst=D_dst)
wd, ws = np.atleast_1d(wd), np.atleast_1d(ws)
arg_funcs, lw_j, WD_il = self.get_map_args(x_jl, y_jl, h_jl, lw, wd, ws, sim_res_data, D_dst=D_dst)
I, J, L, K = arg_funcs['IJLK']()
if I == 0:
return (lw_j, np.broadcast_to(lw_j.WS_ilk, (len(x_j), L, K)).astype(float),
np.broadcast_to(lw_j.TI_ilk, (len(x_j), L, K)).astype(float))
return (lw_j, np.broadcast_to(lw_j.WS_ilk, (len(x_jl), L, K)).astype(float),
np.broadcast_to(lw_j.TI_ilk, (len(x_jl), L, K)).astype(float))
n_cpu = n_cpu or multiprocessing.cpu_count()

@@ -430,13 +448,16 @@ # *6=dx_ijlk, dy_ijlk, dz_ijlk, dh_ijlk, deficit, blockage

xyh_j_lst = list(zip(*[np.array_split(x_j, j_chunks),
np.array_split(y_j, j_chunks),
np.array_split(h_j, j_chunks)]))
def get_jl_args(j, l):
js, ls = slice(j[0], j[-1] + 1), slice(l[0], l[-1] + 1)
if x_jl.shape[1] == 1:
ls = slice(None)
l_iter = [({k: arg_funcs[k](l) for k in arg_funcs},
*[(v, v[:, slice(l[0], l[-1] + 1)])[np.shape(v)[1] == L] for v in [wt_x_ilk, wt_y_ilk, wt_h_ilk]],
x_j_, y_j_, h_j_, wd[l], WD_il[:, l],
lw_j.WS_ilk[:, (l, [0])[lw_j.WS_ilk.shape[1] == 1]],
lw_j.TI_ilk[:, (l, [0])[lw_j.TI_ilk.shape[1] == 1]])
return ({k: arg_funcs[k](l, j) for k in arg_funcs},
*[(v, v[:, slice(l[0], l[-1] + 1)])[np.shape(v)[1] == L] for v in [wt_x_ilk, wt_y_ilk, wt_h_ilk]],
x_jl[js, ls], y_jl[js, ls], np.broadcast_to(h_jl, x_jl.shape)[js, ls], wd[l], WD_il[:, l],
lw_j.WS_ilk[:, (l, [0])[lw_j.WS_ilk.shape[1] == 1]],
lw_j.TI_ilk[:, (l, [0])[lw_j.TI_ilk.shape[1] == 1]])
l_iter = [get_jl_args(j, l)
for l in np.array_split(np.arange(L).astype(int), wd_chunks)
for x_j_, y_j_, h_j_ in xyh_j_lst]
for j in np.array_split(np.arange(J).astype(int), j_chunks)]
WS_eff_jlk, TI_eff_jlk = zip(*map_func(self._get_flow_l, l_iter))

@@ -526,3 +547,3 @@ WS_eff_jlk = np.concatenate([np.concatenate(WS_eff_jlk[i * j_chunks:(i + 1) * j_chunks], 0)

I = kwargs['I']
dw_order_indices_ld = self.site.distance.dw_order_indices(wd)[:, 0]
dw_order_indices_ld = self.site.distance.dw_order_indices(kwargs['x_ilk'], kwargs['y_ilk'], wd)[:, 0]

@@ -732,4 +753,8 @@ if self.blockage_deficitModel:

def get_pos(v_ilk, i_wt_l, i_wd_l):
return v_ilk[i_wt_l, np.minimum(i_wd_l, v_ilk.shape[1] - 1).astype(int)]
dst_xyh_jlk = [get_pos(kwargs[k + '_ilk'], i_dw.T, i_wd_l) for k in 'xyh']
dw_ijlk, hcw_ijlk, dh_ijlk = self.site.distance(
wd_l=wd, WD_ilk=WD_mk[m][na], src_idx=i_wt_l, dst_idx=i_dw.T)
*[get_pos(kwargs[k + '_ilk'], i_wt_l, i_wd_l)[na] for k in 'xyh'], wd_l=wd, WD_ilk=WD_mk[m][na],
dst_xyh_jlk=dst_xyh_jlk)

@@ -787,2 +812,13 @@ for inputModidifierModel in self.inputModifierModels:

deficit, blockage = self._calc_deficit(**model_kwargs)
for i, ewf in enumerate(self.externalWindFarms, I - len(self.externalWindFarms)):
# print(i)
deficit = ewf(i=0, l=i_wt_l == i,
deficit_jlk=deficit[0],
**model_kwargs,
# **{**model_kwargs, 'dw_ijlk': sdw_ijlk, 'hcw_ijlk': shcw_ijlk, 'dh_ijlk': sdh_ijlk}
dst_xyh_jlk=dst_xyh_jlk
)[na]
if self.blockage_deficitModel:

@@ -822,3 +858,4 @@ blockage_nk.append(blockage[0])

deflectionModel=None, turbulenceModel=None, rotorAvgModel=None,
inputModifierModels=[]):
inputModifierModels=[],
externalWindFarms=[]):
"""Initialize flow model

@@ -847,3 +884,4 @@

blockage_deficitModel=None, deflectionModel=deflectionModel,
turbulenceModel=turbulenceModel, inputModifierModels=inputModifierModels)
turbulenceModel=turbulenceModel, inputModifierModels=inputModifierModels,
externalWindFarms=externalWindFarms)
self.direction = 'down'

@@ -857,3 +895,3 @@

dw_order_indices_ld = self.site.distance.dw_order_indices(wd)[:, 0]
dw_order_indices_ld = self.site.distance.dw_order_indices(kwargs['x_ilk'], kwargs['y_ilk'], wd)[:, 0]
return self._propagate_deficit(wd, dw_order_indices_ld, WS_ilk, **kwargs)

@@ -869,3 +907,3 @@

blockage_deficitModel=None, deflectionModel=None, turbulenceModel=None,
convergence_tolerance=1e-6, rotorAvgModel=None, inputModifierModels=[]):
convergence_tolerance=1e-6, rotorAvgModel=None, inputModifierModels=[], externalWindFarms=[]):
"""Initialize flow model

@@ -901,3 +939,4 @@

blockage_deficitModel=blockage_deficitModel, deflectionModel=deflectionModel,
turbulenceModel=turbulenceModel, inputModifierModels=inputModifierModels)
turbulenceModel=turbulenceModel, inputModifierModels=inputModifierModels,
externalWindFarms=externalWindFarms)
self.convergence_tolerance = convergence_tolerance

@@ -921,3 +960,3 @@

self.blockage_deficitModel = None
dw_order_indices_ld = self.site.distance.dw_order_indices(wd)[:, 0]
dw_order_indices_ld = self.site.distance.dw_order_indices(kwargs['x_ilk'], kwargs['y_ilk'], wd)[:, 0]
self.direction = 'down'

@@ -939,3 +978,5 @@ WS_eff_ilk = PropagateUpDownIterative._propagate_deficit(

a = 1
dw_iilk, hcw_iilk, dh_iilk = self.site.distance(wd_l=wd, WD_ilk=WD_ilk)
dst_xyh_jlk = [kwargs[k + '_ilk'] for k in 'xyh']
dw_iilk, hcw_iilk, dh_iilk = self.site.distance(*[kwargs[k + '_ilk'] for k in 'xyh'], wd_l=wd, WD_ilk=WD_ilk)
kwargs['WD_ilk'] = WD_ilk

@@ -1016,6 +1057,5 @@

if any([k in modified_input_dict for k in ['x_ilk', 'y_ilk']]):
z_ilk = self.site.elevation(model_kwargs['x_ilk'], model_kwargs['y_ilk'])
self.site.distance.setup(model_kwargs['x_ilk'], model_kwargs['y_ilk'], model_kwargs['h_ilk'], z_ilk)
model_kwargs.update({k: v for k, v in zip(['dw_ijlk', 'hcw_ijlk', 'dh_ijlk'],
self.site.distance(wd_l=wd, WD_ilk=WD_ilk))})
model_kwargs.update({k: v for k, v in zip(
['dw_ijlk', 'hcw_ijlk', 'dh_ijlk'],
self.site.distance(*[model_kwargs[k + '_ilk'] for k in 'xyh'], wd_l=wd, WD_ilk=WD_ilk))})
model_kwargs['cw_ijlk'] = gradients.hypot(model_kwargs['dh_ijlk'], model_kwargs['hcw_ijlk'])

@@ -1046,2 +1086,7 @@ if not self.deflectionModel:

for i, ewf in enumerate(self.externalWindFarms, I - len(self.externalWindFarms)):
deficit_jlk = ewf(i=i, l=np.ones(L, dtype=bool), deficit_jlk=None, dst_xyh_jlk=dst_xyh_jlk,
**model_kwargs)
deficit_iilk = item_assign(deficit_iilk, idx=i, values=deficit_jlk)
# set own deficit to 0

@@ -1048,0 +1093,0 @@ deficit_iilk *= i2i_zero

@@ -187,2 +187,3 @@ """

ws_cutout = ws_cutout or getattr(windTurbines, 'ws_cutout', 25)
self.externalWindFarms = []

@@ -189,0 +190,0 @@ MinimalisticPredictionModel.__init__(self, correction_factor, latitude, CP=max_cp,

@@ -1,17 +0,19 @@

from abc import abstractmethod, ABC
from py_wake.site._site import Site, LocalWind
from py_wake.wind_turbines import WindTurbines
from py_wake import np
from py_wake.flow_map import FlowMap, HorizontalGrid, FlowBox, Grid
import multiprocessing
from abc import ABC, abstractmethod
from collections.abc import Iterable
import xarray as xr
from py_wake.utils import xarray_utils, weibull # register ilk function @UnusedImport
from numpy import atleast_1d
from numpy import newaxis as na
from numpy import atleast_1d
from py_wake.utils.model_utils import check_model, fix_shape
import multiprocessing
from py_wake.utils.parallelization import get_pool_map, get_pool_starmap, get_map_func
from py_wake import np
from py_wake.flow_map import FlowBox, FlowMap, Grid, HorizontalGrid
from py_wake.noise_models.iso import ISONoiseModel
from py_wake.site._site import LocalWind, Site
from py_wake.utils import weibull, xarray_utils # register ilk function @UnusedImport
from py_wake.utils.functions import arg2ilk
from py_wake.utils.gradients import autograd
from py_wake.noise_models.iso import ISONoiseModel
from collections.abc import Iterable
from py_wake.utils.model_utils import check_model, fix_shape
from py_wake.utils.parallelization import get_pool_map, get_pool_starmap
from py_wake.wind_turbines import WindTurbines

@@ -64,2 +66,11 @@

h, _ = self.windTurbines.get_defaults(len(x), type, h)
if len(self.externalWindFarms):
err_msg = 'Inflow dependent positions not supported in combination with external wind farms'
assert len(np.shape(x)) == 1 or np.shape(x)[1:] == (1, 1), err_msg
assert len(np.shape(y)) == 1 or np.shape(y)[1:] == (1, 1), err_msg
assert len(np.shape(h)) == 1 or np.shape(h)[1:] == (1, 1), err_msg
x = np.r_[x, [wf.wf_x for wf in self.externalWindFarms]]
y = np.r_[y, [wf.wf_y for wf in self.externalWindFarms]]
h = np.r_[h, [np.mean(wf.windTurbines.hub_height(wf.windTurbines.types()))
for wf in self.externalWindFarms]]
wd, ws = self.site.get_defaults(wd, ws)

@@ -134,6 +145,5 @@ I, L, K, = len(x), len(np.atleast_1d(wd)), (1, len(np.atleast_1d(ws)))[time is False]

"""
if isinstance(time, Iterable) and (
len(ws if ws is not None else []) != len(time) or
len(wd if wd is not None else []) != len(time)
): # avoid redundant passing wd & ws for time series sites
if isinstance(time, Iterable) and (len(ws if ws is not None else []) != len(time) or
len(wd if wd is not None else []) != len(time)
): # avoid redundant passing wd & ws for time series sites
wd = np.zeros(len(time))

@@ -293,3 +303,10 @@ ws = np.zeros(len(time))

ws_i = np.linspace(0, len(ws) + 1, ws_chunks + 1).astype(int)
if n_cpu > 1:
map_func = get_pool_map(n_cpu)
else:
from tqdm import tqdm
def map_func(f, iter):
return tqdm(map(f, iter), total=len(iter), disable=not self.verbose)
if time is False:

@@ -302,4 +319,2 @@ # (wd x ws) matrix

# (wd, ws) vector
if time is True:
time = np.arange(len(wd))
slice_lst = [(slice(wd_i0, wd_i1), slice(wd_i0, wd_i1))

@@ -333,3 +348,3 @@ for wd_i0, wd_i1 in zip(wd_i[:-1], wd_i[1:])]

return get_map_func(n_cpu, self.verbose), arg_lst, wd_chunks, ws_chunks
return map_func, arg_lst, wd_chunks, ws_chunks

@@ -668,12 +683,11 @@ def _aep_chunk_wrapper(self, aep_function,

def flow_box(self, x, y, h, wd=None, ws=None, time=None):
if wd is None:
wd = self.wd
if ws is None:
ws = self.ws
X, Y, H = np.meshgrid(x, y, h)
x_j, y_j, h_j = X.flatten(), Y.flatten(), H.flatten()
if time is not None:
lw_j, WS_eff_jlk, TI_eff_jlk = self.windFarmModel._flow_map(x_j, y_j, h_j, self.sel(time=time))
else:
wd, ws = self._wd_ws(wd, ws)
lw_j, WS_eff_jlk, TI_eff_jlk = self.windFarmModel._flow_map(x_j, y_j, h_j, self.sel(wd=wd, ws=ws))
wd, ws, sim_res = self._get_flow_map_args(wd, ws, time)
lw_j, WS_eff_jlk, TI_eff_jlk = self.windFarmModel._flow_map(x_j[:, na], y_j[:, na], h_j[:, na],
self.localWind, wd, ws, sim_res)
return FlowBox(self, X, Y, H, lw_j, WS_eff_jlk, TI_eff_jlk)

@@ -704,7 +718,6 @@

elif k in {'dw_ijlk', 'hcw_ijlk', 'cw_ijlk', 'dh_ijlk'}:
z_ilk = self.windFarmModel.site.elevation(self.x.ilk(), self.y.ilk())
self.windFarmModel.site.distance.setup(self.x.ilk(), self.y.ilk(), self.h.ilk(), z_ilk)
dist = {k: v for k, v in zip(['dw_ijlk', 'hcw_ijlk', 'cw_ijlk'],
self.windFarmModel.site.distance(WD_ilk=self.WD.ilk(),
wd_l=self.wd.values))}
dist = {k: v for k, v in zip(
['dw_ijlk', 'hcw_ijlk', 'cw_ijlk'],
self.windFarmModel.site.distance(self.x.ilk(), self.y.ilk(), self.h.ilk(),
WD_ilk=self.WD.ilk(), wd_l=self.wd.values))}
wt_kwargs.update({k: v for k, v in dist.items() if k in lst})

@@ -724,3 +737,3 @@ if k == 'cw_ijlk': # pragma: no cover

setattr(sim_res, k, getattr(self, k))
aep_j = self.windFarmModel._aep_map(x_j, y_j, h_j, type, sim_res, n_cpu, memory_GB)
aep_j = self.windFarmModel._aep_map(x_j[:, na], y_j[:, na], h_j[:, na], type, sim_res, n_cpu, memory_GB)
if normalize_probabilities:

@@ -774,4 +787,12 @@ lw_j = self.windFarmModel.site.local_wind(x=x_j, y=y_j, h=h_j, wd=wd, ws=ws)

X, Y, x_j, y_j, h_j, plane = self._get_grid(grid)
wd, ws, sim_res = self._get_flow_map_args(wd, ws, time)
lw_j, WS_eff_jlk, TI_eff_jlk = self.windFarmModel._flow_map(
x_j[:, na], y_j[:, na], h_j[:, na], self.localWind, wd, ws, sim_res, D_dst=D_dst,
memory_GB=memory_GB, n_cpu=n_cpu)
return FlowMap(sim_res, X, Y, lw_j, WS_eff_jlk, TI_eff_jlk, plane=plane)
def _get_flow_map_args(self, wd, ws, time):
if 'time' in self:
sim_res = self.sel(time=(time, slice(time))[time is None])
wd, ws = sim_res.wd.values, sim_res.ws.values
else:

@@ -786,7 +807,4 @@ if wd is None:

setattr(sim_res, k, getattr(self, k))
return wd, ws, sim_res
lw_j, WS_eff_jlk, TI_eff_jlk = self.windFarmModel._flow_map(
x_j, y_j, h_j, sim_res, D_dst=D_dst, memory_GB=memory_GB, n_cpu=n_cpu)
return FlowMap(sim_res, X, Y, lw_j, WS_eff_jlk, TI_eff_jlk, plane=plane)
def _wd_ws(self, wd, ws):

@@ -836,7 +854,9 @@ if wd is None:

if __name__ == '__main__':
from py_wake.examples.data.iea37 import IEA37Site, IEA37_WindTurbines
from py_wake import IEA37SimpleBastankhahGaussian
import warnings
import matplotlib.pyplot as plt
from py_wake import IEA37SimpleBastankhahGaussian
from py_wake.examples.data.iea37 import IEA37_WindTurbines, IEA37Site
site = IEA37Site(16)

@@ -843,0 +863,0 @@ x, y = site.initial_position.T

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

def plot_xy(self, x, y, types=None, wd=None, yaw=0, tilt=0, normalize_with=1, ax=None):
def plot_xy(self, x, y, types=None, wd=None, yaw=0, tilt=0, normalize_with=1, ax=None, wt_number=1):
"""Plot wind farm layout including type name and diameter

@@ -180,3 +180,2 @@

ax = plt.gca()
markers = np.array(list("213v^<>o48spP*hH+xXDd|_"))

@@ -188,3 +187,2 @@ assert len(x) == len(y)

tilt = np.zeros_like(x) + tilt
x, y, D = [np.asarray(v) / normalize_with for v in [x, y, self.diameter(types)]]

@@ -206,3 +204,3 @@ R = D / 2

for t, m in zip(np.unique(types), markers):
for t in np.unique(types):
color = cmap[t % cmap.shape[0], :]

@@ -212,4 +210,5 @@ # ax.plot(np.asarray(x)[types == t], np.asarray(y)[types == t], '%sk' % m, label=self._names[int(t)])

for i, (x_, y_, r) in enumerate(zip(x, y, R)):
ax.annotate(i, (x_ + r, y_ + r), fontsize=7)
if wt_number:
for i, (x_, y_, r) in enumerate(zip(x, y, R)):
ax.annotate(i, (x_ + r, y_ + r), fontsize=7)
# ax.legend(loc=1)

@@ -246,4 +245,2 @@

ax = plt.gca()
markers = np.array(list("213v^<>o48spP*hH+xXDd|_"))
colors = ['k', 'gray', 'r', 'g', 'k'] * 5

@@ -265,3 +262,3 @@ yaw = np.zeros_like(y) + yaw

circle = Ellipse((y_, h_ + z_), d * np.sin(np.deg2rad(wd - yaw_)),
d, angle=-tilt_, ec=colors[t], fc="None", zorder=32)
d, angle=-tilt_, ec=cmap[t % cmap.shape[0]], fc="None", zorder=32)
ax.add_artist(circle)

@@ -272,3 +269,4 @@ else:

for t, m, c in zip(np.unique(types), markers, colors):
for t in np.unique(types):
c = cmap[t % cmap.shape[0]]
ax.plot([], [], '2', color=c, label=self._names[int(t)])

@@ -280,4 +278,4 @@

def plot(self, x, y, type=None, wd=None, yaw=0, tilt=0, normalize_with=1, ax=None):
return self.plot_xy(x, y, type, wd, yaw, tilt, normalize_with, ax)
def plot(self, x, y, type=None, wd=None, yaw=0, tilt=0, normalize_with=1, ax=None, wt_number=1):
return self.plot_xy(x, y, type, wd, yaw, tilt, normalize_with, ax, wt_number)

@@ -284,0 +282,0 @@ def plot_power_ct(self, ax=None, ws=np.linspace(0, 25, 1000), **wt_kwargs):

+37
-34

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

py_wake/__init__.py,sha256=-0bNwPpZWQXG-_zE_7hFRoJeG0SRBuDeGM9n5aFWgS0,1188
py_wake/flow_map.py,sha256=I5InulXHI0dIP-JXuJlnj9jyHWNeOsnpKfVtuVv44IQ,22119
py_wake/flow_map.py,sha256=zUFPSUQZmxW5LwFTOiBdaL9fPRJxt87UGGemc_PGl3o,22411
py_wake/superposition_models.py,sha256=mlZSy-T6YiAhdSgAu-reP1YECRsPpgHh8GDCfPAsZ6w,10124
py_wake/version.py,sha256=Vi-fvuY4jNLmkjfsZxZ_bieoAIk2n0J8Fd96j6hAUrI,706
py_wake/version.py,sha256=7zrBk63M80mRuOeY6dfWyf-ZQjC6DN9bM1NV1oUDz0Q,706
py_wake/deficit_models/__init__.py,sha256=w1S4F3FmAgtwqGO90fASYoqxOb6SsgjbasiD3V8_1uk,709
py_wake/deficit_models/deficit_model.py,sha256=BQ8vJ-Nlw4CDWHdqIkNtbAc52OOwfEi4n6P0Vmg8bLg,10255
py_wake/deficit_models/deficit_model.py,sha256=iLb9wUO8wDeYAEBAoAlS2W4Q4B16ZSO0L4DhVq3NxYQ,10280
py_wake/deficit_models/fuga.py,sha256=Jhi52MpvF7-KTk62dZfdLLUf4piCslwIBJTr7N17Hng,13863

@@ -278,13 +278,14 @@ py_wake/deficit_models/gaussian.py,sha256=_ntryBrucuo9EMSLnyJH3dt9mM9SnB0DxP9wzH_w7Zo,36119

py_wake/noise_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
py_wake/noise_models/iso.py,sha256=-E6X7pEfOpg6QqTGbLEjLY22NAiJg4Ec_VvWvsVUpR8,12438
py_wake/noise_models/iso.py,sha256=GYaTGuCnjwSlt8et8zxDM21XQhXd0_FHckEkEHnqJMk,12482
py_wake/rotor_avg_models/__init__.py,sha256=HK_AO6731z8G3EosHb4bcd0nW7YobeCBtrxhBQQ87aY,357
py_wake/rotor_avg_models/area_overlap_model.py,sha256=NPEpdtZv9QnoXt4DC_1JSdCETFjNzhinFrJZ1D2lk3k,5237
py_wake/rotor_avg_models/gaussian_overlap_.02_.02_128_512.nc,sha256=qcFEq0C0z60N2UErbYm-8BLqPALFPwqs0CI552U6RoI,4031394
py_wake/rotor_avg_models/gaussian_overlap_model.py,sha256=R4bstoVSi6baXaCeivlDs0SCAeQwxrz_yK3FrgJIB0c,5622
py_wake/rotor_avg_models/gaussian_overlap_model.py,sha256=t8UVAKe7aSPNfyvqEZQxb5QwfxyChu8W6zV147GrWnc,5727
py_wake/rotor_avg_models/rotor_avg_model.py,sha256=mTkz5shWbc48edQHSoNRjxhKyfWR-Aq7a37-g-2TX7k,10959
py_wake/site/__init__.py,sha256=YeIRWHuiIo0cRD3NIcaqgoWL6u-NfQkDo3uQsgAxjj4,161
py_wake/site/_site.py,sha256=bwVmGSVrGkxnv8POZJ225enQDcpOoj1SwcnzlAnrcMs,16367
py_wake/site/distance.py,sha256=-H-xZ5emerf_eO-1dTQ-crG9xToeQUb7DFzUnaZIfek,11365
py_wake/site/jit_streamline_distance.py,sha256=a2FVowV8cjUb-U18riZ4k2NskGPP0KSyqL4w621A2X8,4925
py_wake/site/_site.py,sha256=zvIXS6oPbUffMTXAIq1TXXKhYqePqEFNGAejPtPRxyQ,16256
py_wake/site/distance.py,sha256=61kvn9ye6ld_vn7rTdJ0GgLjA41uB4e_esnoXDWumzw,8947
py_wake/site/jit_streamline_distance.py,sha256=GJo2mn_bTjuGX3-gSGkv1dkRfo7hGDVKDmefRWYk5o8,159
py_wake/site/shear.py,sha256=OGU2eQCuZ7H3hAjAbATK9ZXqIWBFjDazp9dmKumDnSw,4213
py_wake/site/streamline_distance.py,sha256=Qbjv2GCrPzxFlAp6ev5UhQGPfq2CVyFDzcK4iAHWkP0,4434
py_wake/site/wasp_grid_site.py,sha256=81zqtUIwCAUHaZdgnNvzazBjEIPYk22Gr6r7IQzmsZk,10190

@@ -295,5 +296,5 @@ py_wake/site/xrsite.py,sha256=-Kr2LMx0dpKI-ke4z3hbRyv-GWulLntjkSU-nY1LWIA,19887

py_wake/tests/notebook.py,sha256=21RVNXcqRCy6b3GoRcKxR5E0IGV2d9U6BaqqOKZ8f8Y,6804
py_wake/tests/test_flow_map.py,sha256=8lQzTxsQp4xUKkdRGkmdS80wYbN5QcwsYpOSJ0z_vhM,20290
py_wake/tests/test_main.py,sha256=aouzNZBMsqrRitG9HfEOCPx8Fe6IH0mM_eIyeQ_TqnM,1853
py_wake/tests/test_notebooks.py,sha256=805s9rBBr1dWEtapvK2BwrfWUPSWWiA48Dz-cwciObI,1956
py_wake/tests/test_flow_map.py,sha256=V_dZu9y1K4QaAM7whhKyvTG2O57LmLk5o6y08mbSauw,22448
py_wake/tests/test_main.py,sha256=pc01ZUXMnFDHZwNh7ulqP5FWZDt9I7-gp0D_TN2-9Bs,1914
py_wake/tests/test_notebooks.py,sha256=j2mlOAzwMwe6PfEFS2sEl4aQJDxfDQHR8DM6gZq9B-Q,1958
py_wake/tests/test_superposition_models.py,sha256=dxcgeB_LXn1USv_4QNRuyjL83Cy0qhV1yPdJN6enRj4,8759

@@ -304,7 +305,7 @@ py_wake/tests/test_deficit_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

py_wake/tests/test_deficit_models/test_blockage_models.py,sha256=guS3PX7YdNfI1Po4JzEhGu6BT6ODSxEBR6fBIMUorko,11856
py_wake/tests/test_deficit_models/test_deficit_models.py,sha256=vwwj3lSs_4kWCl55-qVs0-3bwdmUT7ZXICpVAuZSfRU,33990
py_wake/tests/test_deficit_models/test_deficit_models.py,sha256=vIe5H_cX0rZ-V1bD3I4FWgnaeR1qy6NGc-uCefA9qhI,34083
py_wake/tests/test_deficit_models/test_fuga.py,sha256=zuWx-oAThjYGdubhX9c-dR8-wsnBJMTi7uA6R17_kos,24769
py_wake/tests/test_deficit_models/test_gcl.py,sha256=i2mIAoNUnGswJ8xXVjjHVw9Imgzhs6thfI8zkgLCt04,1189
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=rfvT0178DfxA52wlscSDL56tJapBVI_BJu1CnWGkCQA,17738
py_wake/tests/test_deficit_models/test_rans_lut.py,sha256=jA8h7Fp4euRvjQNrfnT-gqLGofZNwjQElKBuJnbcIpE,17130
py_wake/tests/test_deficit_models/test_selfsimilarity.py,sha256=mI5DUstBbaC36JQ-eCOMrE88jubvZjb3tp_DUQQzU2Y,4446

@@ -338,7 +339,7 @@ py_wake/tests/test_deflection_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

py_wake/tests/test_sites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
py_wake/tests/test_sites/test_distances.py,sha256=AEB6EwwjE3yo5qkwKcbixWa9xtIByalkFsZPl-ITcAE,16023
py_wake/tests/test_sites/test_distances.py,sha256=VKox97PB3HmHNH0MO9IbVLiniolY2ffwqz9pNrGiu6M,16338
py_wake/tests/test_sites/test_iea37_readers.py,sha256=nEoXISd3M7USxJe4PFBI2wUlLTjiL6NEnXMvFWHzwxo,1427
py_wake/tests/test_sites/test_shear_models.py,sha256=g4zg1QCvl2WQY6f10BiRlBAnyJyaaQRVpeiLnkNT4SU,4053
py_wake/tests/test_sites/test_site.py,sha256=NZ_gm8LvHwY0PYVeJbXjtUONjSGaoNCZQ8Q8Z938rrU,11267
py_wake/tests/test_sites/test_wasp_grid_site.py,sha256=8Swf470l8PdI9FRs-yF27S4pP7X08kXRFTZCK1YYNlU,15686
py_wake/tests/test_sites/test_site.py,sha256=NpE0hX1l17fZmzmrBR5x8w12Hc8Bu5w3NzGG9W60URk,11205
py_wake/tests/test_sites/test_wasp_grid_site.py,sha256=miNHoOVFt31YqM0Auo97GkAfQ86eyUvc_xkoVO86xEA,15662
py_wake/tests/test_sites/test_xrsite.py,sha256=3lASkg3Cdv-MQwrXZRuGqQDGONy6pzRdLOurQTneKGI,31239

@@ -353,6 +354,6 @@ py_wake/tests/test_turbulence_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

py_wake/tests/test_utils/test_gpu_utils.py,sha256=vTVMTLrb9KKr7bHr-AbZZwytV9fhqABq0cn9K4-pt1I,1292
py_wake/tests/test_utils/test_gradient_utils.py,sha256=phZtdTCg64KgjTvyK4n_HHssPJCYAlJUUHUc9GRWBlU,15810
py_wake/tests/test_utils/test_gradient_utils.py,sha256=vyacCD9xviDv1YUBmgRoQbG4E1ZiMpSg27anfunw3Kw,16514
py_wake/tests/test_utils/test_grid_interpolator.py,sha256=XWXlIhCpxoRxlSBM6RUeEmEVfYOe3c2wlhjo9hibyWA,7876
py_wake/tests/test_utils/test_layouts.py,sha256=9soiopXExIdV1jxSnoG6te_54qbxfRGEd3AdpoGmpNM,1199
py_wake/tests/test_utils/test_model_gradients.py,sha256=Rni1JhZWB_gOmK-aBNFR7bi8q5VlqKX3ftgA6Nbgmc0,12587
py_wake/tests/test_utils/test_model_gradients.py,sha256=JHZ-uZfNDW-O8sNTssBmE-EJR_XDSAan1OmCtaSROhE,12638
py_wake/tests/test_utils/test_model_utils.py,sha256=219lb_0_74xshlw2lKdEgTyPGSRXe8F-uplifqpRdnM,3847

@@ -363,3 +364,3 @@ py_wake/tests/test_utils/test_most.py,sha256=WG2Ng8T1mcmIAsP23OnQeGCoVQ9D8NEX-2fmhH4tMd8,1311

py_wake/tests/test_utils/test_plotting.py,sha256=tCmEUdvZinzcFMiBGRth8MbFKuVSxQFDuPNh762GBps,286
py_wake/tests/test_utils/test_profiling.py,sha256=g65cCmFOxs2xsQ9PVJTa6TopSpbRGRI-F3e3zpA10Ts,1562
py_wake/tests/test_utils/test_profiling.py,sha256=jwanMam7pqEVzlKdsjKqDzWJN1Y7PqY0RN6FqYggNIk,1558
py_wake/tests/test_utils/test_xarray_utils.py,sha256=abapAtIBQ931ogNqHvi6X2xL_gAKv5DXMdLV1pqiMss,3069

@@ -374,6 +375,7 @@ py_wake/tests/test_utils/test_floris_api/test_floris_turbine.py,sha256=yrLQsNEyV3tiTFVLIxOaNEiAGhYPf3_ClI7Vbsuz9Vc,317

py_wake/tests/test_wind_farm_models/test_enginering_wind_farm_model.py,sha256=IvbRInNWqnC8FVk8A-IwmDiYOZ3oGNR4Z8drlRHHpnk,28773
py_wake/tests/test_wind_farm_models/test_external_windfarms.py,sha256=MszAdqFAhnQyxbIXABMc-qgGmZ6y_vsL-XMDleiiKVU,12489
py_wake/tests/test_wind_farm_models/test_memory_usage.py,sha256=nDGlWHDNVAUrZZD9BOOC-AluRWKuHCLiljp3m8ZofJs,2016
py_wake/tests/test_wind_farm_models/test_minimalistic_model.py,sha256=Nmu7eNldVD_m_G7bmsOf8eUGkYf_bF5kUhNwFWF9fj8,1566
py_wake/tests/test_wind_farm_models/test_parallel_aep.py,sha256=QvBk7bFpaM5IGRBhhZmUN64SI1eRlm7XOUx3JecpHwY,3037
py_wake/tests/test_wind_farm_models/test_parallel_daep.py,sha256=B128GWrFQl2D8a30-Fkn1emqa4gTud2icNo1JVyYtrY,3272
py_wake/tests/test_wind_farm_models/test_parallel_daep.py,sha256=NrbM7uF6Fed89RqTiItsErEDBB_UuWezYzzLqkK1GKg,3309
py_wake/tests/test_wind_farm_models/test_simulation_result.py,sha256=VoffAnO6EwA4J_4-AbgoCgrU5Lbj-3f_HbC-YeBVav8,1496

@@ -400,15 +402,15 @@ py_wake/tests/test_wind_farm_models/test_wind_farm_model.py,sha256=hsv11zdQD4X5p1aB09vOiJKJx1ZxlqqzZ1QnqBDoUB8,14464

py_wake/utils/gpu_utils.py,sha256=qun-tz8HuwLForxFk7H8DlkdPNmjeRiqMew7f6LOc1c,1618
py_wake/utils/gradients.py,sha256=gb_GoWJWx38vQN0z6HKWB62AQPnDo53oHUDEhcLd16s,14498
py_wake/utils/gradients.py,sha256=uuhBCbpb7y9K02qbW0m6Ryr-QI7HCbsQzD3Q2uFwbLw,15207
py_wake/utils/grid_interpolator.py,sha256=Yq6VFkEMkxJkALC74ATvvHYpTlRMsinkG6VTe_c49Pg,8231
py_wake/utils/ieawind37_utils.py,sha256=t32eUKrPP6oH2G7Lc53l03TZOe7cj4XhBt_FlNZB5kQ,305
py_wake/utils/layouts.py,sha256=YHV1FwZJECBjTqqYIi8ipVIOefXYoWS7AarK7ND048M,998
py_wake/utils/model_utils.py,sha256=JYC1xYuJ5U720Tc7_jB_iDFvYaRZ_WlN-Py0JPOho_M,15389
py_wake/utils/model_utils.py,sha256=CkdZ38hwCkmyfTO5x6iwAgrVi3o0hOohDVZ213ti_Uw,15350
py_wake/utils/most.py,sha256=9DEZ5C06qdttfGdfUCyaFCGt-YT8xPnQ3URIuHpbcdE,1208
py_wake/utils/notebook_extensions.py,sha256=E0UOiQ1AAMaIbnnzsyj-gGv8oRWD8wAvHsj2BN10LXU,704
py_wake/utils/numpy_utils.py,sha256=I9URsvF0ALXOFwdlkTK-mQLul0d-5wLjSWuEeAmE5ZU,3192
py_wake/utils/parallelization.py,sha256=7eCsS-2A26N8P8dmdkX2d7eW4jy0jQoFOcPxA24DbcU,2082
py_wake/utils/parallelization.py,sha256=k_w4AQMndE4acS0vwBMlZx4AgT_Oi7N7OK3OqVJzc5I,2101
py_wake/utils/plotting.py,sha256=zXharIIWIwl-E0Xh2JP3qAnYEEAKGJidQcldhRd0dRo,568
py_wake/utils/profiling.py,sha256=GuE0UcQaZ3KsVnklLHSDFpCc0jQ4fixh0VREwA3aHQ0,4618
py_wake/utils/rans_lut_utils.py,sha256=nQut0o1agTcP-vAumQ9DjITtO5oJUD0NB4S0nVhApoQ,11381
py_wake/utils/streamline.py,sha256=NSiA7acgkeh8iUepqalvSt7XhWS7U4POvoZq2LZCaIc,2172
py_wake/utils/rans_lut_utils.py,sha256=-zs25tCdg2heaoCjyEDdk0vSDTImdhjtTpbDH_YEClI,12423
py_wake/utils/streamline.py,sha256=hFIvkHsdUPLzySvQYdpX6DEUMaz5NayQBEg2fuppPh4,2574
py_wake/utils/tensorflow_surrogate_utils.py,sha256=QdIPqxoYiuXFvieHQyP-TE6gS8syXbxoUZ1liCBFil0,7198

@@ -423,7 +425,8 @@ py_wake/utils/weibull.py,sha256=cD1SIyXHEn17IQueZnsy9-upo2oZivVCpYOWka-_DsE,1262

py_wake/wind_farm_models/__init__.py,sha256=cl0nC9vqhXgpRuaCFkQpDET2Yyzbwnd5Kv2wgZHUI6E,137
py_wake/wind_farm_models/engineering_models.py,sha256=V9Ebkm2uMz0mo4iyzu_ELT4pEMn7qs-uRsJi-q_esbI,60738
py_wake/wind_farm_models/minimalistic_wind_farm_model.py,sha256=geyeFlxI71PPs19pDSWDaQgiSyxaJqNTcuXI1he93_0,11124
py_wake/wind_farm_models/wind_farm_model.py,sha256=7_Bcwlca0Q-EmoFcsM8vFtlQhV-dEcByTtY5LLUlsA4,41812
py_wake/wind_farm_models/engineering_models.py,sha256=heyGqJroRPO_BjRaMf1QeEyu-kpiUkxntQTw4r5VfeA,63601
py_wake/wind_farm_models/external_wind_farm_models.py,sha256=8Qjkzf9w9RFSVr7AHEE3wPtgiwlsZue4SGlBhb1w32k,12649
py_wake/wind_farm_models/minimalistic_wind_farm_model.py,sha256=9BepPjAo_WDMcG4feUQo7YDY2WSFJOpNMmzgT0yNBn4,11160
py_wake/wind_farm_models/wind_farm_model.py,sha256=QvYmeqAVz2QyrjH21VEt1T52lJS2NQcTq3cW-W1qUFQ,42814
py_wake/wind_turbines/__init__.py,sha256=w1D9rLfxk7m_UdrqbVVokWwAikxIzeRh6Wb9zVT2Mhs,145
py_wake/wind_turbines/_wind_turbines.py,sha256=IAIaERtPtRR6QwhHweKC1Vqi8IYqjBV2ebmaby2Mqqo,19010
py_wake/wind_turbines/_wind_turbines.py,sha256=ukh8Kw4tUHH5vkXiVC8JaI-5xuIjCRVUROWInnJQJcw,18915
py_wake/wind_turbines/generic_wind_turbines.py,sha256=SiV-hganUn9TO8ItM8ZPVuJ0BqKIuig5-vA6miSGJeY,9456

@@ -433,6 +436,6 @@ py_wake/wind_turbines/power_ct_functions.py,sha256=CjWKehvop9kMPclU54pFp_hRG7_3bqCx16cJlz5QduU,22663

py_wake/wind_turbines/wind_turbines_deprecated.py,sha256=HpNmBR8CJL4-8JBaygDI0t086qfw5bR2DOQI8Ox4AZ4,6250
py_wake-2.6.13.dist-info/licenses/LICENSE,sha256=XE2CGPqQgzSXqIajXpAVYJ5SRNmaWOIeMePK6MocsuY,1084
py_wake-2.6.13.dist-info/METADATA,sha256=fpTWS7ZgV4KrfCqxIDfNfV-qTxPc295iQmBlADMgfpg,3596
py_wake-2.6.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
py_wake-2.6.13.dist-info/top_level.txt,sha256=GsaXU4YwyMkZZ6dkb4h0FMc5RaLIT2Qns_YoScKoXdk,20
py_wake-2.6.13.dist-info/RECORD,,
py_wake-2.6.14.dist-info/licenses/LICENSE,sha256=XE2CGPqQgzSXqIajXpAVYJ5SRNmaWOIeMePK6MocsuY,1084
py_wake-2.6.14.dist-info/METADATA,sha256=GTsCfUqg3V5gNXNjdrnN3JYWPDYHOun_fqN0ZCqDdj0,3596
py_wake-2.6.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
py_wake-2.6.14.dist-info/top_level.txt,sha256=GsaXU4YwyMkZZ6dkb4h0FMc5RaLIT2Qns_YoScKoXdk,20
py_wake-2.6.14.dist-info/RECORD,,