import numpy as np
from iwopy import Objective
from scipy.spatial.distance import cdist
from foxes.config import config
[docs]
class OMaxN(Objective):
"""
Maximal number of turbines objective
for purely geometrical layouts problems.
:group: opt.problems.layout.geom_layouts.objectives
"""
[docs]
def __init__(self, problem, name="maxN"):
"""
Constructor.
Parameters
----------
problem: foxes_opt.FarmOptProblem
The underlying geometrical layout
optimization problem
name: str
The constraint name
"""
super().__init__(
problem,
name,
vnames_int=problem.var_names_int(),
vnames_float=problem.var_names_float(),
)
[docs]
def n_components(self):
"""
Returns the number of components of the
function.
Returns
-------
int:
The number of components.
"""
return 1
[docs]
def maximize(self):
"""
Returns flag for maximization of each component.
Returns
-------
flags: np.array
Bool array for component maximization,
shape: (n_components,)
"""
return [True]
[docs]
def calc_individual(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for a single individual of the
underlying problem.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_vars_int,)
vars_float : np.array
The float variable values, shape: (n_vars_float,)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_sel_components,)
"""
__, valid = problem_results
return np.sum(valid)
[docs]
def calc_population(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for all individuals of a population.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_pop, n_vars_int)
vars_float : np.array
The float variable values, shape: (n_pop, n_vars_float)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_pop, n_sel_components)
"""
__, valid = problem_results
return np.sum(valid, axis=1)[:, None]
[docs]
class OMinN(OMaxN):
"""
Minimal number of turbines objective
for purely geometrical layouts problems.
:group: opt.problems.layout.geom_layouts.objectives
"""
[docs]
def __init__(self, problem, name="ominN"):
"""
Constructor.
Parameters
----------
problem: foxes_opt.FarmOptProblem
The underlying geometrical layout
optimization problem
name: str
The constraint name
"""
super().__init__(problem, name)
[docs]
def maximize(self):
return [False]
[docs]
class OFixN(Objective):
"""
Fixed number of turbines objective
for purely geometrical layouts problems.
:group: opt.problems.layout.geom_layouts.objectives
"""
[docs]
def __init__(self, problem, N, name="ofixN"):
"""
Constructor.
Parameters
----------
problem: foxes_opt.FarmOptProblem
The underlying geometrical layout
optimization problem
N: int
The number of turbines
name: str
The constraint name
"""
super().__init__(
problem,
name,
vnames_int=problem.var_names_int(),
vnames_float=problem.var_names_float(),
)
self.N = N
[docs]
def n_components(self):
"""
Returns the number of components of the
function.
Returns
-------
int:
The number of components.
"""
return 1
[docs]
def maximize(self):
"""
Returns flag for maximization of each component.
Returns
-------
flags: np.array
Bool array for component maximization,
shape: (n_components,)
"""
return [False]
[docs]
def calc_individual(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for a single individual of the
underlying problem.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_vars_int,)
vars_float : np.array
The float variable values, shape: (n_vars_float,)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_sel_components,)
"""
__, valid = problem_results
N = np.sum(valid, dtype=np.float64)
return np.maximum(N - self.N, self.N - N)
[docs]
def calc_population(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for all individuals of a population.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_pop, n_vars_int)
vars_float : np.array
The float variable values, shape: (n_pop, n_vars_float)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_pop, n_sel_components)
"""
__, valid = problem_results
N = np.sum(valid, axis=1, dtype=np.float64)[:, None]
return np.maximum(N - self.N, self.N - N)
[docs]
class MaxGridSpacing(Objective):
"""
Maximal grid spacing objective
for purely geometrical layouts problems.
:group: opt.problems.layout.geom_layouts.objectives
"""
[docs]
def __init__(self, problem, name="max_dxdy"):
"""
Constructor.
Parameters
----------
problem: foxes_opt.FarmOptProblem
The underlying geometrical layout
optimization problem
name: str
The constraint name
"""
super().__init__(
problem,
name,
vnames_int=problem.var_names_int(),
vnames_float=problem.var_names_float(),
)
[docs]
def n_components(self):
"""
Returns the number of components of the
function.
Returns
-------
int:
The number of components.
"""
return 1
[docs]
def maximize(self):
"""
Returns flag for maximization of each component.
Returns
-------
flags: np.array
Bool array for component maximization,
shape: (n_components,)
"""
return [True]
[docs]
def calc_individual(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for a single individual of the
underlying problem.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_vars_int,)
vars_float : np.array
The float variable values, shape: (n_vars_float,)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_sel_components,)
"""
vflt = vars_float.reshape(self.problem.n_grids, 5)
delta = np.minimum(vflt[:, 2], vflt[:, 3])
return np.nanmin(delta)
[docs]
def calc_population(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for all individuals of a population.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_pop, n_vars_int)
vars_float : np.array
The float variable values, shape: (n_pop, n_vars_float)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_pop, n_sel_components)
"""
n_pop = vars_float.shape[0]
vflt = vars_float.reshape(n_pop, self.problem.n_grids, 5)
delta = np.minimum(vflt[:, :, 2], vflt[:, :, 3])
return np.nanmin(delta, axis=1)[:, None]
[docs]
class MaxDensity(Objective):
"""
Maximal turbine density objective
for purely geometrical layouts problems.
:group: opt.problems.layout.geom_layouts.objectives
"""
[docs]
def __init__(self, problem, dfactor=1, min_dist=None, name="max_density"):
"""
Constructor.
Parameters
----------
problem: foxes_opt.FarmOptProblem
The underlying geometrical layout
optimization problem
dfactor: float
Delta factor for grid spacing
min_dist: float, optional
The minimal distance
name: str
The constraint name
"""
super().__init__(
problem,
name,
vnames_int=problem.var_names_int(),
vnames_float=problem.var_names_float(),
)
self.dfactor = dfactor
self.min_dist = problem.min_dist if min_dist is None else min_dist
[docs]
def n_components(self):
"""
Returns the number of components of the
function.
Returns
-------
int:
The number of components.
"""
return 1
[docs]
def maximize(self):
"""
Returns flag for maximization of each component.
Returns
-------
flags: np.array
Bool array for component maximization,
shape: (n_components,)
"""
return [False]
[docs]
def initialize(self, verbosity):
"""
Initialize the object.
Parameters
----------
verbosity: int
The verbosity level, 0 = silent
"""
super().initialize(verbosity)
# define regular grid of probe points:
geom = self.problem.boundary
pmin = geom.p_min()
pmax = geom.p_max()
detlta = self.min_dist / self.dfactor
self._probes = np.stack(
np.meshgrid(
np.arange(pmin[0] - detlta, pmax[0] + 2 * detlta, detlta),
np.arange(pmin[1] - detlta, pmax[1] + 2 * detlta, detlta),
indexing="ij",
),
axis=-1,
)
nx, ny = self._probes.shape[:2]
n = nx * ny
self._probes = self._probes.reshape(n, 2)
# reduce to points within geometry:
valid = geom.points_inside(self._probes)
self._probes = self._probes[valid]
[docs]
def calc_individual(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for a single individual of the
underlying problem.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_vars_int,)
vars_float : np.array
The float variable values, shape: (n_vars_float,)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_sel_components,)
"""
xy, valid = problem_results
xy = xy[valid]
dists = cdist(self._probes, xy)
return np.nanmax(np.nanmin(dists, axis=1))
[docs]
def calc_population(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for all individuals of a population.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_pop, n_vars_int)
vars_float : np.array
The float variable values, shape: (n_pop, n_vars_float)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_pop, n_sel_components)
"""
n_pop = vars_float.shape[0]
xy, valid = problem_results
out = np.full(n_pop, 1e20, dtype=config.dtype_double)
for pi in range(n_pop):
if np.any(valid[pi]):
hxy = xy[pi][valid[pi]]
dists = cdist(self._probes, hxy)
out[pi] = np.nanmax(np.nanmin(dists, axis=1))
return out[:, None]
[docs]
class MeMiMaDist(Objective):
"""
Mean-min-max distance objective
for purely geometrical layouts problems.
:group: opt.problems.layout.geom_layouts.objectives
"""
[docs]
def __init__(self, problem, scale=500.0, c1=1, c2=1, c3=1, name="MiMaMean"):
"""
Constructor.
Parameters
----------
problem: foxes_opt.FarmOptProblem
The underlying geometrical layout
optimization problem
scale: float
The distance scale
c1: float
Parameter for mean weighting
c2: float
Parameter for max diff weighting
c3: float
Parameter for min diff weighting
name: str
The constraint name
"""
super().__init__(
problem,
name,
vnames_int=problem.var_names_int(),
vnames_float=problem.var_names_float(),
)
self.scale = scale
self.c1 = c1
self.c2 = c2
self.c3 = c3
[docs]
def n_components(self):
"""
Returns the number of components of the
function.
Returns
-------
int:
The number of components.
"""
return 1
[docs]
def maximize(self):
"""
Returns flag for maximization of each component.
Returns
-------
flags: np.array
Bool array for component maximization,
shape: (n_components,)
"""
return [True]
[docs]
def calc_individual(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for a single individual of the
underlying problem.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_vars_int,)
vars_float : np.array
The float variable values, shape: (n_vars_float,)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_sel_components,)
"""
xy, valid = problem_results
# xy = xy[valid]
dists = cdist(xy, xy)
np.fill_diagonal(dists, np.inf)
dists = np.min(dists, axis=1) / self.scale / len(xy)
mean = np.average(dists)
mi = np.min(dists)
ma = np.max(dists)
return np.atleast_1d(
self.c1 * mean**2 - self.c2 * (mean - mi) ** 2 - self.c3 * (mean - ma) ** 2
)
[docs]
def calc_population(self, vars_int, vars_float, problem_results, cmpnts=None):
"""
Calculate values for all individuals of a population.
Parameters
----------
vars_int : np.array
The integer variable values, shape: (n_pop, n_vars_int)
vars_float : np.array
The float variable values, shape: (n_pop, n_vars_float)
problem_results : Any
The results of the variable application
to the problem
components : list of int, optional
The selected components or None for all
Returns
-------
values : np.array
The component values, shape: (n_pop, n_sel_components)
"""
xy, valid = problem_results
n_pop, n_xy = xy.shape[:2]
out = np.zeros((n_pop, 1), dtype=config.dtype_double)
for pi in range(n_pop):
hxy = xy[pi] # , valid[pi]]
dists = cdist(hxy, hxy)
np.fill_diagonal(dists, np.inf)
dists = np.min(dists, axis=1) / self.scale / n_xy
mean = np.average(dists)
mi = np.min(dists)
ma = np.max(dists)
out[pi, 0] = (
self.c1 * mean**2
- self.c2 * (mean - mi) ** 2
- self.c3 * (mean - ma) ** 2
)
return out