import numpy as np
from shapely.geometry import Point, Polygon
[docs]
def discontinuous_grid(
nrows,
ncols,
farm_width,
farm_height,
shear,
rotation,
center_x,
center_y,
shrink_boundary,
boundary_x,
boundary_y,
eps=1e-3,
):
"""
Map from grid design variables to turbine x and y locations.
Includes integer design variables and the formulation
results in a discontinous design space.
TODO: shrink_boundary doesn't work well with concave boundaries,
or with boundary angles less than 90 deg
Args:
nrows (Int): number of rows in the grid.
ncols (Int): number of columns in the grid.
farm_width (Float): total grid width (before shear).
farm_height (Float): total grid height.
shear (Float): grid shear (rad).
rotation (Float): rotation about grid center (rad).
center_x (Float): location of grid x center.
center_y (Float): location of grid y center.
shrink_boundary (Float): how much to shrink the boundary that the grid can occupy.
boundary_x (Array(Float)): x boundary points.
boundary_y (Array(Float)): y boundary points.
Returns:
grid_x (Array(Float)): turbine x locations.
grid_y (Array(Float)): turbine y locations.
"""
# create grid
nrows = int(nrows)
ncols = int(ncols)
xlocs = np.linspace(0.0, farm_width, ncols)
ylocs = np.linspace(0.0, farm_height, nrows)
y_spacing = ylocs[1] - ylocs[0]
nturbs = nrows * ncols
grid_x = np.zeros(nturbs)
grid_y = np.zeros(nturbs)
turb = 0
for i in range(nrows):
for j in range(ncols):
grid_x[turb] = xlocs[j] + float(i) * y_spacing * np.tan(shear)
grid_y[turb] = ylocs[i]
turb += 1
# rotate
grid_x, grid_y = (
np.cos(rotation) * grid_x - np.sin(rotation) * grid_y,
np.sin(rotation) * grid_x + np.cos(rotation) * grid_y,
)
# move center of grid
grid_x = (grid_x - np.mean(grid_x)) + center_x
grid_y = (grid_y - np.mean(grid_y)) + center_y
# arrange the boundary
# boundary = np.zeros((len(boundary_x),2))
# boundary[:,0] = boundary_x[:]
# boundary[:,1] = boundary_y[:]
# poly = Polygon(boundary)
# centroid = poly.centroid
# boundary[:,0] = (boundary_x[:]-centroid.x)*boundary_mult + centroid.x
# boundary[:,1] = (boundary_y[:]-centroid.y)*boundary_mult + centroid.y
# poly = Polygon(boundary)
boundary = np.zeros((len(boundary_x), 2))
boundary[:, 0] = boundary_x[:]
boundary[:, 1] = boundary_y[:]
poly = Polygon(boundary)
if shrink_boundary != 0.0:
nBounds = len(boundary_x)
for i in range(nBounds):
point = Point(boundary_x[i] + eps, boundary_y[i])
if poly.contains(point) is True or poly.touches(point) is True:
boundary[i, 0] = boundary_x[i] + shrink_boundary
else:
boundary[i, 0] = boundary_x[i] - shrink_boundary
point = Point(boundary_x[i], boundary_y[i] + eps)
if poly.contains(point) is True or poly.touches(point) is True:
boundary[i, 1] = boundary_y[i] + shrink_boundary
else:
boundary[i, 1] = boundary_y[i] - shrink_boundary
poly = Polygon(boundary)
# get rid of points outside of boundary
index = 0
for i in range(len(grid_x)):
point = Point(grid_x[index], grid_y[index])
if poly.contains(point) is False and poly.touches(point) is False:
grid_x = np.delete(grid_x, index)
grid_y = np.delete(grid_y, index)
else:
index += 1
return grid_x, grid_y
[docs]
def place_boundary_turbines(n_boundary_turbs, start, boundary_x, boundary_y):
"""
Place turbines equally spaced traversing the perimiter if the wind farm along the boundary
Args:
n_boundary_turbs (Int): number of turbines to be placed on the boundary
start (Float): where the first turbine should be placed
boundary_x (Array(Float)): x boundary points
boundary_y (Array(Float)): y boundary points
Returns
layout_x (Array(Float)): turbine x locations
layout_y (Array(Float)): turbine y locations
"""
# check if the boundary is closed, correct if not
if boundary_x[-1] != boundary_x[0] or boundary_y[-1] != boundary_y[0]:
boundary_x = np.append(boundary_x, boundary_x[0])
boundary_y = np.append(boundary_y, boundary_y[0])
# make the boundary
boundary = np.zeros((len(boundary_x), 2))
boundary[:, 0] = boundary_x[:]
boundary[:, 1] = boundary_y[:]
poly = Polygon(boundary)
perimeter = poly.length
# get the flattened turbine locations
spacing = perimeter / float(n_boundary_turbs)
flattened_locs = np.linspace(start, perimeter + start - spacing, n_boundary_turbs)
# set all of the flattened values between 0 and the perimeter
for i in range(n_boundary_turbs):
while flattened_locs[i] < 0.0:
flattened_locs[i] += perimeter
if flattened_locs[i] > perimeter:
flattened_locs[i] = flattened_locs[i] % perimeter
# place the turbines around the perimeter
nBounds = len(boundary_x)
layout_x = np.zeros(n_boundary_turbs)
layout_y = np.zeros(n_boundary_turbs)
lenBound = np.zeros(nBounds - 1)
for i in range(nBounds - 1):
lenBound[i] = Point(boundary[i]).distance(Point(boundary[i + 1]))
for i in range(n_boundary_turbs):
for j in range(nBounds - 1):
if flattened_locs[i] < sum(lenBound[0 : j + 1]):
layout_x[i] = (
boundary_x[j]
+ (boundary_x[j + 1] - boundary_x[j])
* (flattened_locs[i] - sum(lenBound[0:j]))
/ lenBound[j]
)
layout_y[i] = (
boundary_y[j]
+ (boundary_y[j + 1] - boundary_y[j])
* (flattened_locs[i] - sum(lenBound[0:j]))
/ lenBound[j]
)
break
return layout_x, layout_y
[docs]
def boundary_grid(
n_boundary_turbs,
start,
nrows,
ncols,
farm_width,
farm_height,
shear,
rotation,
center_x,
center_y,
shrink_boundary,
boundary_x,
boundary_y,
eps=1e-3,
):
"""
Place turbines equally spaced traversing the perimiter if the wind farm along the boundary
Args:
n_boundary_turbs,start: boundary variables
nrows,ncols,farm_width,farm_height,shear,
rotation,center_x,center_y,shrink_boundary,eps: grid variables
boundary_x,boundary_y: boundary points
Returns
layout_x (Array(Float)): turbine x locations
layout_y (Array(Float)): turbine y locations
"""
boundary_turbines_x, boundary_turbines_y = place_boundary_turbines(
n_boundary_turbs, start, boundary_x, boundary_y
)
grid_turbines_x, grid_turbines_y = discontinuous_grid(
nrows,
ncols,
farm_width,
farm_height,
shear,
rotation,
center_x,
center_y,
shrink_boundary,
boundary_x,
boundary_y,
eps=eps,
)
layout_x = np.append(boundary_turbines_x, grid_turbines_x)
layout_y = np.append(boundary_turbines_y, grid_turbines_y)
return layout_x, layout_y
[docs]
class BoundaryGrid:
"""
Parameterize the wind farm layout with a grid or the boundary grid method
"""
def __init__(self, fi):
"""
Initializes a BoundaryGrid object by assigning a
FlorisModel object.
Args:
fmodel (FlorisModel): A FlorisModel object.
"""
self.fmodel = fi
self.n_boundary_turbs = 0
self.start = 0.0
self.nrows = 0
self.ncols = 0
self.farm_width = 0.0
self.farm_height = 0.0
self.shear = 0.0
self.rotation = 0.0
self.center_x = 0.0
self.center_y = 0.0
self.shrink_boundary = 0.0
self.boundary_x = np.array([])
self.boundary_y = np.array([])
self.eps = 1e-3
[docs]
def reinitialize_bg(
self,
n_boundary_turbs=None,
start=None,
nrows=None,
ncols=None,
farm_width=None,
farm_height=None,
shear=None,
rotation=None,
center_x=None,
center_y=None,
shrink_boundary=None,
boundary_x=None,
boundary_y=None,
eps=None,
):
if n_boundary_turbs is not None:
self.n_boundary_turbs = n_boundary_turbs
if start is not None:
self.start = start
if nrows is not None:
self.nrows = nrows
if ncols is not None:
self.ncols = ncols
if farm_width is not None:
self.farm_width = farm_width
if farm_height is not None:
self.farm_height = farm_height
if shear is not None:
self.shear = shear
if rotation is not None:
self.rotation = rotation
if center_x is not None:
self.center_x = center_x
if center_y is not None:
self.center_y = center_y
if shrink_boundary is not None:
self.shrink_boundary = shrink_boundary
if boundary_x is not None:
self.boundary_x = boundary_x
if boundary_y is not None:
self.boundary_y = boundary_y
if eps is not None:
self.eps = eps
[docs]
def reinitialize_xy(self):
layout_x, layout_y = boundary_grid(
self.n_boundary_turbs,
self.start,
self.nrows,
self.ncols,
self.farm_width,
self.farm_height,
self.shear,
self.rotation,
self.center_x,
self.center_y,
self.shrink_boundary,
self.boundary_x,
self.boundary_y,
eps=self.eps,
)
self.fmodel.reinitialize_flow_field(layout_array=(layout_x, layout_y))
if __name__ == "__main__":
nrows = 10
ncols = 10
farm_width = 600
farm_height = 600
shear = np.deg2rad(10)
rotation = np.deg2rad(30)
center_x = 250
center_y = 300
boundary_mult = 0.6
shrink_boundary = 10.0
# boundary_x = np.array([-300.0,-100.0,100.0,100.0,0.0]) + 2200.0
# boundary_y = np.array([-100.0,100.0,140.0,-100.0,0.0]) + 300.0
boundary_x = np.array(
[0.0, 100.0, 100.0, 200.0, 200.0, 300.0, 300.0, 400.0, 400.0, 500.0, 500.0, 0.0]
)
boundary_y = np.array(
[
500.0,
500.0,
400.0,
400.0,
300.0,
300.0,
200.0,
200.0,
100.0,
100.0,
600.0,
600.0,
]
)
x, y = discontinuous_grid(
nrows,
ncols,
farm_width,
farm_height,
shear,
rotation,
center_x,
center_y,
shrink_boundary,
boundary_x,
boundary_y,
)
n_boundary_turbs = 25
start = 1000.0
layout_x, layout_y = place_boundary_turbines(
n_boundary_turbs, start, boundary_x, boundary_y
)
bx = np.append(boundary_x, boundary_x[0])
by = np.append(boundary_y, boundary_y[0])
boundary = np.zeros((len(bx), 2))
boundary[:, 0] = bx[:]
boundary[:, 1] = by[:]
poly = Polygon(boundary)
# centroid = poly.centroid
# new_bx = (bx[:]-centroid.x)*boundary_mult + centroid.x
# new_by = (by[:]-centroid.y)*boundary_mult + centroid.y
nBounds = len(bx)
new_bx = np.zeros(len(bx))
new_by = np.zeros(len(by))
eps = 1e-3
for i in range(nBounds):
point = Point(bx[i] + eps, by[i])
if poly.contains(point) is True or poly.touches(point) is True:
new_bx[i] = bx[i] + shrink_boundary
else:
new_bx[i] = bx[i] - shrink_boundary
point = Point(bx[i], by[i] + eps)
if poly.contains(point) is True or poly.touches(point) is True:
new_by[i] = by[i] + shrink_boundary
else:
new_by[i] = by[i] - shrink_boundary
import matplotlib.pyplot as plt
nx, ny = boundary_grid(
n_boundary_turbs,
start,
nrows,
ncols,
farm_width,
farm_height,
shear,
rotation,
center_x,
center_y,
shrink_boundary,
boundary_x,
boundary_y,
)
plt.plot(bx, by)
plt.plot(new_bx, new_by)
plt.plot(x, y, "o")
plt.plot(layout_x, layout_y, "o")
plt.axis("equal")
plt.figure(2)
plt.plot(bx, by)
plt.plot(nx, ny, "o")
plt.axis("equal")
plt.show()