Skip to content
Snippets Groups Projects
Commit 4232c2cc authored by Mikkel Friis-Møller's avatar Mikkel Friis-Møller
Browse files

call random positions from topfarm

PEP8 compliance
offset-based random positions implemented
parent b1f57a56
No related branches found
No related tags found
1 merge request!94Handle disabled mpi
...@@ -3,6 +3,7 @@ from topfarm.constraint_components.boundary_component import BoundaryComp,\ ...@@ -3,6 +3,7 @@ from topfarm.constraint_components.boundary_component import BoundaryComp,\
from topfarm.constraint_components.spacing_component import SpacingComp from topfarm.constraint_components.spacing_component import SpacingComp
from topfarm.plotting import PlotComp from topfarm.plotting import PlotComp
from topfarm.utils import pos_from_case, latest_id from topfarm.utils import pos_from_case, latest_id
from topfarm.utils import shuffle_positions as spos
import os import os
import time import time
import numpy as np import numpy as np
...@@ -12,6 +13,7 @@ with warnings.catch_warnings(): ...@@ -12,6 +13,7 @@ with warnings.catch_warnings():
from openmdao.api import Problem, ScipyOptimizeDriver, IndepVarComp, \ from openmdao.api import Problem, ScipyOptimizeDriver, IndepVarComp, \
SqliteRecorder SqliteRecorder
class TopFarm(object): class TopFarm(object):
"""Optimize wind farm layout in terms of """Optimize wind farm layout in terms of
- Position of turbines - Position of turbines
...@@ -24,6 +26,7 @@ class TopFarm(object): ...@@ -24,6 +26,7 @@ class TopFarm(object):
boundary_type='convex_hull', plot_comp=None, boundary_type='convex_hull', plot_comp=None,
driver=ScipyOptimizeDriver(), record=False, driver=ScipyOptimizeDriver(), record=False,
case_recorder_dir=os.getcwd(), rerun_case_id=None): case_recorder_dir=os.getcwd(), rerun_case_id=None):
self.min_spacing = min_spacing
if rerun_case_id is None: if rerun_case_id is None:
self.initial_positions = turbines = np.array(turbines) self.initial_positions = turbines = np.array(turbines)
elif rerun_case_id is 'latest': elif rerun_case_id is 'latest':
...@@ -34,6 +37,7 @@ class TopFarm(object): ...@@ -34,6 +37,7 @@ class TopFarm(object):
else: else:
self.initial_positions = turbines = pos_from_case(rerun_case_id) self.initial_positions = turbines = pos_from_case(rerun_case_id)
n_wt = turbines.shape[0] n_wt = turbines.shape[0]
self.n_wt = n_wt
if boundary_type == 'polygon': if boundary_type == 'polygon':
self.boundary_comp = PolygonBoundaryComp(boundary, n_wt) self.boundary_comp = PolygonBoundaryComp(boundary, n_wt)
else: else:
...@@ -44,13 +48,15 @@ class TopFarm(object): ...@@ -44,13 +48,15 @@ class TopFarm(object):
min_x, min_y = self.boundary_comp.vertices.min(0) min_x, min_y = self.boundary_comp.vertices.min(0)
mean_x, mean_y = self.boundary_comp.vertices.mean(0) mean_x, mean_y = self.boundary_comp.vertices.mean(0)
design_var_kwargs = {} design_var_kwargs = {}
do = driver.options
if 'optimizer' in driver.options and driver.options['optimizer'] == 'SLSQP': if 'optimizer' in do and do['optimizer'] == 'SLSQP':
min_x, min_y, mean_x, mean_y = 0, 0, 1, 1 # scaling disturbs SLSQP min_x, min_y, mean_x, mean_y = 0, 0, 1, 1 # scaling disturbs SLSQP
# Default +/- sys.float_info.max does not work for SLSQP # Default +/- sys.float_info.max does not work for SLSQP
design_var_kwargs = {'lower': np.nan, 'upper': np.nan} design_var_kwargs = {'lower': np.nan, 'upper': np.nan}
indeps.add_output('turbineX', turbines[:, 0], units='m', ref0=min_x, ref=mean_x) indeps.add_output('turbineX', turbines[:, 0], units='m', ref0=min_x,
indeps.add_output('turbineY', turbines[:, 1], units='m', ref0=min_y, ref=mean_y) ref=mean_x)
indeps.add_output('turbineY', turbines[:, 1], units='m', ref0=min_y,
ref=mean_y)
indeps.add_output('boundary', self.boundary_comp.vertices, units='m') indeps.add_output('boundary', self.boundary_comp.vertices, units='m')
prob.model.add_subsystem('cost_comp', cost_comp, promotes=['*']) prob.model.add_subsystem('cost_comp', cost_comp, promotes=['*'])
prob.driver = driver prob.driver = driver
...@@ -111,7 +117,8 @@ class TopFarm(object): ...@@ -111,7 +117,8 @@ class TopFarm(object):
def evaluate_gradients(self): def evaluate_gradients(self):
t = time.time() t = time.time()
res = self.problem.compute_totals(['cost'], wrt=['turbineX', 'turbineY'], return_format='dict') res = self.problem.compute_totals(['cost'], wrt=['turbineX',
'turbineY'], return_format='dict')
print("Gradients evaluated in\t%.3fs" % (time.time() - t)) print("Gradients evaluated in\t%.3fs" % (time.time() - t))
return res return res
...@@ -133,33 +140,52 @@ class TopFarm(object): ...@@ -133,33 +140,52 @@ class TopFarm(object):
def turbine_positions(self): def turbine_positions(self):
return np.array([self.problem['turbineX'], self.problem['turbineY']]).T return np.array([self.problem['turbineX'], self.problem['turbineY']]).T
def clean(self):
def post_process(self, anim_time=10, verbose=True):
if self.plot_comp.animate:
self.plot_comp.run_animate(anim_time, verbose)
for file in os.listdir(self.plot_comp.temp): for file in os.listdir(self.plot_comp.temp):
if file.startswith('plot_') and file.endswith('.png'): if file.startswith('plot_') and file.endswith('.png'):
os.remove(os.path.join(self.plot_comp.temp,file)) os.remove(os.path.join(self.plot_comp.temp, file))
def shuffle_positions(self, shuffle_type='rel', n_iter=1000,
step_size=0.1, pad=1.1, offset=5, plot=False,
verbose=False):
if shuffle_type is not None:
turbines = spos(self.boundary, self.n_wt, self.min_spacing,
self.turbine_positions, shuffle_type, n_iter,
step_size, pad, offset, plot, verbose)
self.problem['turbineX'] = turbines.T[0]
self.problem['turbineY'] = turbines.T[1]
def animate(self, anim_time=10, verbose=False):
if self.plot_comp.animate:
self.plot_comp.run_animate(anim_time, verbose)
else:
if verbose:
print('Animation requested but was not enabled for this '
'optimization. Set plot_comp.animate = True to enable')
def try_me(): def try_me():
if __name__ == '__main__': if __name__ == '__main__':
from topfarm.cost_models.dummy import DummyCostPlotComp, DummyCost from topfarm.cost_models.dummy import DummyCostPlotComp, DummyCost
n_wt = 4 n_wt = 20
random_offset = 5 random_offset = 5
optimal = [(3, -3), (7, -7), (4, -3), (3, -7), (-3, -3), (-7, -7), (-4, -3), (-3, -7)][:n_wt] optimal = [(3, -3), (7, -7), (4, -3), (3, -7), (-3, -3), (-7, -7),
(-4, -3), (-3, -7)][:n_wt]
rotorDiameter = 1.0 rotorDiameter = 1.0
minSpacing = 2.0 minSpacing = 2.0
turbines = np.array(optimal) + np.random.randint(-random_offset, random_offset, (n_wt, 2))
plot_comp = DummyCostPlotComp(optimal) plot_comp = DummyCostPlotComp(optimal)
plot_comp.animate = True # plot_comp.animate = True
boundary = [(0, 0), (6, 0), (6, -10), (0, -10)] boundary = [(0, 0), (6, 0), (6, -10), (0, -10)]
tf = TopFarm(turbines, DummyCost(optimal), minSpacing * rotorDiameter, boundary=boundary, plot_comp=plot_comp) tf = TopFarm(optimal, DummyCost(optimal), minSpacing * rotorDiameter,
boundary=boundary, plot_comp=plot_comp)
# tf.check() # tf.check()
tf.shuffle_positions(shuffle_type='abs', offset=random_offset)
tf.optimize() tf.optimize()
tf.post_process() tf.animate()
tf.clean()
try_me() try_me()
...@@ -5,7 +5,7 @@ from topfarm.cost_models.cost_model_wrappers import CostModelComponent ...@@ -5,7 +5,7 @@ from topfarm.cost_models.cost_model_wrappers import CostModelComponent
import pytest import pytest
import os import os
from topfarm.utils import pos_from_case, latest_id, _random_positions from topfarm.utils import pos_from_case, latest_id, _shuffle_positions_abs
thisdir = os.path.dirname(os.path.abspath(__file__)) thisdir = os.path.dirname(os.path.abspath(__file__))
turbines = np.array([[ 2.4999377 , -2.99987763], turbines = np.array([[ 2.4999377 , -2.99987763],
...@@ -58,13 +58,13 @@ def testlatest_id(): ...@@ -58,13 +58,13 @@ def testlatest_id():
ref_path = os.path.join(path,'cases_20180621_111710.sql') ref_path = os.path.join(path,'cases_20180621_111710.sql')
assert latest_id(path) == ref_path assert latest_id(path) == ref_path
def test_random_positions(): def test_shuffle_positions_abs():
turbines2 = _random_positions(x, y, boundary, n_wt, n_iter, step_size, turbines2 = _shuffle_positions_abs(x, y, boundary, n_wt, n_iter, step_size,
min_space, pad, plot, verbose) min_space, pad, plot, verbose)
np.testing.assert_allclose(turbines2, turbines2_ref) np.testing.assert_allclose(turbines2, turbines2_ref)
if __name__ == '__main__': if __name__ == '__main__':
# testpos_from_case() # testpos_from_case()
# testlatest_id() # testlatest_id()
# test_random_positions() # test_shuffle_positions_abs()
pass pass
\ No newline at end of file
...@@ -2,7 +2,6 @@ from topfarm.constraint_components.boundary_component import \ ...@@ -2,7 +2,6 @@ from topfarm.constraint_components.boundary_component import \
PolygonBoundaryComp PolygonBoundaryComp
from topfarm.constraint_components.spacing_component import SpacingComp from topfarm.constraint_components.spacing_component import SpacingComp
import os import os
from copy import copy
import numpy as np import numpy as np
from openmdao.api import CaseReader from openmdao.api import CaseReader
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
...@@ -46,51 +45,76 @@ def latest_id(case_recorder_dir): ...@@ -46,51 +45,76 @@ def latest_id(case_recorder_dir):
return latest return latest
def random_positions(boundary, n_wt, n_iter, step_size, min_space, def shuffle_positions(boundary, n_wt, min_space, init_pos, shuffle_type='abs',
pad=1.1, plot=False, verbose=True): n_iter=1000, step_size=0.1, pad=1.1, offset=0.5,
plot=True, verbose=True):
''' '''
Input: Input:
boundary: list of tuples, e.g.: [(0, 0), (6, 1), (7, -11), (-1, -10)] boundary: list of tuples, e.g.: [(0, 0), (6, 1), (7, -11), (-1, -10)]
n_wt: number of wind turbines n_wt: number of wind turbines
min_space: the minimum spacing between turbines
init_pos: inital positions that suffeling is based off of if the
shuffle_type requires it
shuffle_type:
'abs': absolute random positions that respect the boundary,
and aims to respect the minimum spacing
'rel': moves each turbine with an offset compared to the
initial positions - not implemented yet.
n_iter: number of iterations allowed to try and satisfy the minimum n_iter: number of iterations allowed to try and satisfy the minimum
spacing constraint spacing constraint
step_size: the multiplier on the spacing gradient that the turbines step_size: the multiplier on the spacing gradient that the turbines
are moved in each step are moved in each step
min_space: the minimum spacing between turbines
pad: the multiplier on the boundary gradient pad: the multiplier on the boundary gradient
plot: plot the generated random layout plot: plot the generated random layout
verbose: print helpful text to the console verbose: print helpful text to the console
Returns an array of xy coordinates of the wind turbines Returns an array of xy coordinates of the wind turbines
''' '''
x, y = map(_random, np.array(boundary).T) if shuffle_type == 'abs':
turbines = _random_positions(x, y, boundary, n_wt, n_iter, step_size, def _random(b):
min_space, pad, plot, verbose) return np.random.rand(n_wt) * (max(b) - min(b)) + min(b)
x, y = map(_random, np.array(boundary).T)
turbines = _shuffle_positions_abs(x, y, boundary, n_wt, n_iter,
step_size, min_space, pad, plot,
verbose)
elif shuffle_type == 'rel':
turbines = _shuffle_postions_rel(init_pos, offset, boundary, n_wt,
plot)
return turbines
def _shuffle_postions_rel(init_pos, offset, boundary, n_wt, plot):
turbineX = init_pos[:, 0]
turbineY = init_pos[:, 1]
ba = np.array(boundary).T
turbines = np.array(init_pos) + np.random.rand(n_wt, 2)*2*offset-offset
if plot:
plt.cla()
plt.plot(ba[0], ba[1])
plt.plot(turbineX, turbineY, '.')
plt.plot(turbines.T[0], turbines.T[1], 'o')
return turbines return turbines
def _random_positions(x, y, boundary, n_wt, n_iter, step_size, min_space, def _shuffle_positions_abs(turbineX, turbineY, boundary, n_wt, n_iter,
pad, plot, verbose): step_size, min_space, pad, plot, verbose):
boundary_comp = PolygonBoundaryComp(boundary, n_wt) boundary_comp = PolygonBoundaryComp(boundary, n_wt)
spacing_comp = SpacingComp(nTurbines=n_wt) spacing_comp = SpacingComp(nTurbines=n_wt)
turbineX = copy(x)
turbineY = copy(y)
min_space2 = min_space**2 min_space2 = min_space**2
ba = np.array(boundary).T ba = np.array(boundary).T
bx = np.append(ba[0], ba[0][0])
by = np.append(ba[1], ba[1][0])
if plot: if plot:
plt.cla() plt.cla()
plt.plot(bx, by) plt.plot(ba[0], ba[1])
plt.plot(x, y, '.') plt.plot(turbineX, turbineY, '.')
for j in range(n_iter): for j in range(n_iter):
dist = spacing_comp._compute(turbineX, turbineY) dist = spacing_comp._compute(turbineX, turbineY)
dx, dy = spacing_comp._compute_partials(turbineX, turbineY) dx, dy = spacing_comp._compute_partials(turbineX, turbineY)
index = np.argmin(dist) index = np.argmin(dist)
if dist[index] < min_space2: if dist[index] < min_space2 or j == 0:
turbineX += dx[index]*step_size turbineX += dx[index]*step_size
turbineY += dy[index]*step_size turbineY += dy[index]*step_size
turbineX, turbineY = _contain(n_wt, turbineX, turbineY, turbineX, turbineY = _move_inside_boundary(n_wt, turbineX,
boundary_comp, pad) turbineY, boundary_comp,
pad)
else: else:
if verbose: if verbose:
print('Obtained required spacing after {} iterations'.format( print('Obtained required spacing after {} iterations'.format(
...@@ -107,11 +131,7 @@ def _random_positions(x, y, boundary, n_wt, n_iter, step_size, min_space, ...@@ -107,11 +131,7 @@ def _random_positions(x, y, boundary, n_wt, n_iter, step_size, min_space,
return turbines return turbines
def _random(b): def _move_inside_boundary(n_wt, turbineX, turbineY, boundary_comp, pad):
return np.random.rand(n_wt) * (max(b) - min(b)) + min(b)
def _contain(n_wt, turbineX, turbineY, boundary_comp, pad):
for i in range(0, n_wt): for i in range(0, n_wt):
dng = boundary_comp.calc_distance_and_gradients(turbineX, turbineY) dng = boundary_comp.calc_distance_and_gradients(turbineX, turbineY)
dist = dng[0][i] dist = dng[0][i]
...@@ -135,14 +155,11 @@ if __name__ == '__main__': ...@@ -135,14 +155,11 @@ if __name__ == '__main__':
latest_id = latest_id(case_recorder_dir) latest_id = latest_id(case_recorder_dir)
print(latest_id) print(latest_id)
boundary = [(0, 0), (6, 1), (7, -11), (-1, -10)] boundary = [(0, 0), (6, 1), (7, -11), (-1, -10), (0, 0)]
n_wt = 20 n_wt = 20
n_iter = 1000 init_pos = np.column_stack((np.random.randint(0, 6, (n_wt)),
step_size = 0.1 np.random.randint(-10, 0, (n_wt))))
min_space = 2.1 min_space = 2.1
pad = 1.01 turbines = shuffle_positions(boundary, n_wt, min_space, init_pos,
plot = True shuffle_type='rel')
verbose = True
turbines = random_positions(boundary, n_wt, n_iter, step_size, min_space,
pad, plot, verbose)
print(turbines) print(turbines)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment