Source code for floris.optimization.layout_optimization.layout_optimization_pyoptsparse_spread


import matplotlib.pyplot as plt
import numpy as np
from scipy.spatial.distance import cdist
from shapely.geometry import Point

from .layout_optimization_base import LayoutOptimization


[docs] class LayoutOptimizationPyOptSparse(LayoutOptimization): def __init__( self, fmodel, boundaries, min_dist=None, solver=None, optOptions=None, timeLimit=None, storeHistory='hist.hist', hotStart=None ): super().__init__(fmodel, boundaries, min_dist=min_dist) self._reinitialize(solver=solver, optOptions=optOptions) self.storeHistory = storeHistory self.timeLimit = timeLimit self.hotStart = hotStart def _reinitialize(self, solver=None, optOptions=None): try: import pyoptsparse except ImportError: err_msg = ( "It appears you do not have pyOptSparse installed. " + "Please refer to https://pyoptsparse.readthedocs.io/ for " + "guidance on how to properly install the module." ) self.logger.error(err_msg, stack_info=True) raise ImportError(err_msg) # Insantiate ptOptSparse optimization object with name and objective function self.optProb = pyoptsparse.Optimization('layout', self._obj_func) self.optProb = self.add_var_group(self.optProb) self.optProb = self.add_con_group(self.optProb) self.optProb.addObj("obj") if solver is not None: self.solver = solver print("Setting up optimization with user's choice of solver: ", self.solver) else: self.solver = "SLSQP" print("Setting up optimization with default solver: SLSQP.") if optOptions is not None: self.optOptions = optOptions else: if self.solver == "SNOPT": self.optOptions = {"Major optimality tolerance": 1e-7} else: self.optOptions = {} exec("self.opt = pyoptsparse." + self.solver + "(options=self.optOptions)") def _optimize(self): if hasattr(self, "_sens"): self.sol = self.opt(self.optProb, sens=self._sens) else: if self.timeLimit is not None: self.sol = self.opt( self.optProb, sens="CDR", storeHistory=self.storeHistory, timeLimit=self.timeLimit, hotStart=self.hotStart ) else: self.sol = self.opt( self.optProb, sens="CDR", storeHistory=self.storeHistory, hotStart=self.hotStart ) return self.sol def _obj_func(self, varDict): # Parse the variable dictionary self.parse_opt_vars(varDict) # Update turbine map with turbince locations # self.fmodel.reinitialize(layout=[self.x, self.y]) # self.fmodel.calculate_wake() # Compute the objective function funcs = {} funcs["obj"] = ( -1 * self.mean_distance(self.x, self.y) ) # Compute constraints, if any are defined for the optimization funcs = self.compute_cons(funcs, self.x, self.y) fail = False return funcs, fail # Optionally, the user can supply the optimization with gradients # def _sens(self, varDict, funcs): # funcsSens = {} # fail = False # return funcsSens, fail
[docs] def parse_opt_vars(self, varDict): self.x = self._unnorm(varDict["x"], self.xmin, self.xmax) self.y = self._unnorm(varDict["y"], self.ymin, self.ymax)
[docs] def parse_sol_vars(self, sol): self.x = list(self._unnorm(sol.getDVs()["x"], self.xmin, self.xmax))[0] self.y = list(self._unnorm(sol.getDVs()["y"], self.ymin, self.ymax))[1]
[docs] def add_var_group(self, optProb): optProb.addVarGroup( "x", self.nturbs, type="c", lower=0.0, upper=1.0, value=self.x0 ) optProb.addVarGroup( "y", self.nturbs, type="c", lower=0.0, upper=1.0, value=self.y0 ) return optProb
[docs] def add_con_group(self, optProb): optProb.addConGroup("boundary_con", self.nturbs, upper=0.0) optProb.addConGroup("spacing_con", 1, upper=0.0) return optProb
[docs] def compute_cons(self, funcs, x, y): funcs["boundary_con"] = self.distance_from_boundaries(x, y) funcs["spacing_con"] = self.space_constraint(x, y) return funcs
[docs] def mean_distance(self, x, y): locs = np.vstack((x, y)).T distances = cdist(locs, locs) return np.mean(distances)
[docs] def space_constraint(self, x, y, rho=500): # Calculate distances between turbines locs = np.vstack((x, y)).T distances = cdist(locs, locs) arange = np.arange(distances.shape[0]) distances[arange, arange] = 1e10 dist = np.min(distances, axis=0) g = 1 - np.array(dist) / self.min_dist # Following code copied from OpenMDAO KSComp(). # Constraint is satisfied when KS_constraint <= 0 g_max = np.max(np.atleast_2d(g), axis=-1)[:, np.newaxis] g_diff = g - g_max exponents = np.exp(rho * g_diff) summation = np.sum(exponents, axis=-1)[:, np.newaxis] KS_constraint = g_max + 1.0 / rho * np.log(summation) return KS_constraint[0][0]
[docs] def distance_from_boundaries(self, x, y): boundary_con = np.zeros(self.nturbs) for i in range(self.nturbs): loc = Point(x[i], y[i]) boundary_con[i] = loc.distance(self.boundary_line) if self.boundary_polygon.contains(loc) is True: boundary_con[i] *= -1.0 return boundary_con
[docs] def plot_layout_opt_results(self): """ Method to plot the old and new locations of the layout opitimization. """ locsx = self._unnorm(self.sol.getDVs()["x"], self.xmin, self.xmax) locsy = self._unnorm(self.sol.getDVs()["y"], self.ymin, self.ymax) x0 = self._unnorm(self.x0, self.xmin, self.xmax) y0 = self._unnorm(self.y0, self.ymin, self.ymax) plt.figure(figsize=(9, 6)) fontsize = 16 plt.plot(x0, y0, "ob") plt.plot(locsx, locsy, "or") # plt.title('Layout Optimization Results', fontsize=fontsize) plt.xlabel("x (m)", fontsize=fontsize) plt.ylabel("y (m)", fontsize=fontsize) plt.axis("equal") plt.grid() plt.tick_params(which="both", labelsize=fontsize) plt.legend( ["Old locations", "New locations"], loc="lower center", bbox_to_anchor=(0.5, 1.01), ncol=2, fontsize=fontsize, ) verts = self.boundaries for i in range(len(verts)): if i == len(verts) - 1: plt.plot([verts[i][0], verts[0][0]], [verts[i][1], verts[0][1]], "b") else: plt.plot( [verts[i][0], verts[i + 1][0]], [verts[i][1], verts[i + 1][1]], "b" )