foxes-opt 0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. foxes_opt-0.1/LICENSE +21 -0
  2. foxes_opt-0.1/MANIFEST.in +0 -0
  3. foxes_opt-0.1/PKG-INFO +66 -0
  4. foxes_opt-0.1/README.md +14 -0
  5. foxes_opt-0.1/foxes_opt/VERSION +1 -0
  6. foxes_opt-0.1/foxes_opt/__init__.py +9 -0
  7. foxes_opt-0.1/foxes_opt/constraints/__init__.py +6 -0
  8. foxes_opt-0.1/foxes_opt/constraints/area_geometry.py +214 -0
  9. foxes_opt-0.1/foxes_opt/constraints/min_dist.py +239 -0
  10. foxes_opt-0.1/foxes_opt/core/__init__.py +9 -0
  11. foxes_opt-0.1/foxes_opt/core/farm_constraint.py +96 -0
  12. foxes_opt-0.1/foxes_opt/core/farm_objective.py +97 -0
  13. foxes_opt-0.1/foxes_opt/core/farm_opt_problem.py +346 -0
  14. foxes_opt-0.1/foxes_opt/core/farm_vars_problem.py +219 -0
  15. foxes_opt-0.1/foxes_opt/core/pop_states.py +206 -0
  16. foxes_opt-0.1/foxes_opt/objectives/__init__.py +6 -0
  17. foxes_opt-0.1/foxes_opt/objectives/farm_vars.py +323 -0
  18. foxes_opt-0.1/foxes_opt/objectives/max_n_turbines.py +142 -0
  19. foxes_opt-0.1/foxes_opt/problems/__init__.py +7 -0
  20. foxes_opt-0.1/foxes_opt/problems/layout/__init__.py +9 -0
  21. foxes_opt-0.1/foxes_opt/problems/layout/farm_layout.py +137 -0
  22. foxes_opt-0.1/foxes_opt/problems/layout/geom_layouts/__init__.py +10 -0
  23. foxes_opt-0.1/foxes_opt/problems/layout/geom_layouts/constraints.py +802 -0
  24. foxes_opt-0.1/foxes_opt/problems/layout/geom_layouts/geom_layout.py +290 -0
  25. foxes_opt-0.1/foxes_opt/problems/layout/geom_layouts/geom_layout_gridded.py +276 -0
  26. foxes_opt-0.1/foxes_opt/problems/layout/geom_layouts/geom_reggrid.py +351 -0
  27. foxes_opt-0.1/foxes_opt/problems/layout/geom_layouts/geom_reggrids.py +482 -0
  28. foxes_opt-0.1/foxes_opt/problems/layout/geom_layouts/objectives.py +666 -0
  29. foxes_opt-0.1/foxes_opt/problems/layout/reggrids_layout.py +417 -0
  30. foxes_opt-0.1/foxes_opt/problems/layout/regular_layout.py +350 -0
  31. foxes_opt-0.1/foxes_opt/problems/opt_farm_vars.py +586 -0
  32. foxes_opt-0.1/foxes_opt.egg-info/PKG-INFO +66 -0
  33. foxes_opt-0.1/foxes_opt.egg-info/SOURCES.txt +38 -0
  34. foxes_opt-0.1/foxes_opt.egg-info/dependency_links.txt +1 -0
  35. foxes_opt-0.1/foxes_opt.egg-info/requires.txt +29 -0
  36. foxes_opt-0.1/foxes_opt.egg-info/top_level.txt +1 -0
  37. foxes_opt-0.1/foxes_opt.egg-info/zip-safe +1 -0
  38. foxes_opt-0.1/setup.cfg +68 -0
  39. foxes_opt-0.1/setup.py +4 -0
foxes_opt-0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 FraunhoferIWES
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
File without changes
foxes_opt-0.1/PKG-INFO ADDED
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.1
2
+ Name: foxes-opt
3
+ Version: 0.1
4
+ Summary: Wind farm optimization for the FOXES package
5
+ Author: Fraunhofer IWES
6
+ Author-email: jonas.schulte@iwes.fraunhofer.de
7
+ License: MIT
8
+ Project-URL: Source Code, https://github.com/FraunhoferIWES/foxes-opt
9
+ Project-URL: Bug Tracker, https://github.com/FraunhoferIWES/foxes-opt/issues
10
+ Project-URL: Documentation, https://fraunhoferiwes.github.io/foxes.docs/index.html
11
+ Keywords: Wind farm optimization
12
+ Classifier: Topic :: Scientific/Engineering
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: License :: OSI Approved :: MIT License
22
+ Classifier: Operating System :: OS Independent
23
+ Classifier: Development Status :: 4 - Beta
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: foxes
28
+ Requires-Dist: iwopy>=0.2.2
29
+ Requires-Dist: pymoo
30
+ Provides-Extra: test
31
+ Requires-Dist: flake8; extra == "test"
32
+ Requires-Dist: pytest; extra == "test"
33
+ Provides-Extra: doc
34
+ Requires-Dist: sphinx; extra == "doc"
35
+ Requires-Dist: sphinx-immaterial; extra == "doc"
36
+ Requires-Dist: nbsphinx; extra == "doc"
37
+ Requires-Dist: ipykernel; extra == "doc"
38
+ Requires-Dist: ipywidgets; extra == "doc"
39
+ Requires-Dist: m2r2; extra == "doc"
40
+ Requires-Dist: lxml_html_clean; extra == "doc"
41
+ Provides-Extra: all
42
+ Requires-Dist: flake8; extra == "all"
43
+ Requires-Dist: pytest; extra == "all"
44
+ Requires-Dist: sphinx; extra == "all"
45
+ Requires-Dist: sphinx-immaterial; extra == "all"
46
+ Requires-Dist: nbsphinx; extra == "all"
47
+ Requires-Dist: ipykernel; extra == "all"
48
+ Requires-Dist: ipywidgets; extra == "all"
49
+ Requires-Dist: m2r2; extra == "all"
50
+ Requires-Dist: lxml_html_clean; extra == "all"
51
+ Provides-Extra: scripts
52
+
53
+ # foxes-opt
54
+
55
+ _Optimization for the [foxes](https://github.com/FraunhoferIWES/foxes) package_
56
+
57
+ ## Installation
58
+
59
+ Install from pip via
60
+ ```console
61
+ pip install foxes-opt
62
+ ```
63
+ Install from conda-forge via
64
+ ```console
65
+ conda install foxes-opt -c conda-forge
66
+ ```
@@ -0,0 +1,14 @@
1
+ # foxes-opt
2
+
3
+ _Optimization for the [foxes](https://github.com/FraunhoferIWES/foxes) package_
4
+
5
+ ## Installation
6
+
7
+ Install from pip via
8
+ ```console
9
+ pip install foxes-opt
10
+ ```
11
+ Install from conda-forge via
12
+ ```console
13
+ conda install foxes-opt -c conda-forge
14
+ ```
@@ -0,0 +1 @@
1
+ 0.1
@@ -0,0 +1,9 @@
1
+ """
2
+ Wind farm optimization.
3
+ """
4
+
5
+ from .core import FarmOptProblem, FarmObjective, FarmConstraint
6
+
7
+ from . import problems
8
+ from . import constraints
9
+ from . import objectives
@@ -0,0 +1,6 @@
1
+ """
2
+ Wind farm optimization constraints.
3
+ """
4
+
5
+ from .area_geometry import AreaGeometryConstraint, FarmBoundaryConstraint
6
+ from .min_dist import MinDistConstraint
@@ -0,0 +1,214 @@
1
+ import numpy as np
2
+
3
+ from foxes_opt.core.farm_constraint import FarmConstraint
4
+ import foxes.variables as FV
5
+
6
+
7
+ class AreaGeometryConstraint(FarmConstraint):
8
+ """
9
+ Constrains turbine positions to the inside
10
+ of a given area geometry.
11
+
12
+ Attributes
13
+ ----------
14
+ farm: foxes.WindFarm
15
+ The wind farm
16
+ sel_turbines: list
17
+ The selected turbines
18
+ geometry: foxes.utils.geom2d.AreaGeometry
19
+ The area geometry
20
+ disc_inside: bool
21
+ Ensure full rotor disc inside boundary
22
+ D: float
23
+ Use this radius for rotor disc inside condition
24
+
25
+ :group: opt.constraints
26
+
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ problem,
32
+ name,
33
+ geometry,
34
+ sel_turbines=None,
35
+ disc_inside=False,
36
+ D=None,
37
+ **kwargs,
38
+ ):
39
+ """
40
+ Constructor.
41
+
42
+ Parameters
43
+ ----------
44
+ problem : foxes_opt.FarmOptProblem
45
+ The underlying optimization problem
46
+ name : str
47
+ The name of the constraint
48
+ geometry : foxes.utils.geom2d.AreaGeometry
49
+ The area geometry
50
+ sel_turbines : list of int, optional
51
+ The selected turbines
52
+ disc_inside : bool
53
+ Ensure full rotor disc inside boundary
54
+ D : float, optional
55
+ Use this radius for rotor disc inside condition
56
+ kwargs : dict, optional
57
+ Additional parameters for `iwopy.Constraint`
58
+
59
+ """
60
+ self.geometry = geometry
61
+ self.disc_inside = disc_inside
62
+ self.D = D
63
+
64
+ selt = problem.sel_turbines if sel_turbines is None else sel_turbines
65
+ vrs = []
66
+ cns = []
67
+ for ti in selt:
68
+ vrs += [problem.tvar(FV.X, ti), problem.tvar(FV.Y, ti)]
69
+ cns.append(f"{name}_{ti:04d}")
70
+
71
+ super().__init__(
72
+ problem, name, sel_turbines, vnames_float=vrs, cnames=cns, **kwargs
73
+ )
74
+
75
+ def n_components(self):
76
+ """
77
+ Returns the number of components of the
78
+ function.
79
+
80
+ Returns
81
+ -------
82
+ int:
83
+ The number of components.
84
+
85
+ """
86
+ return self.n_sel_turbines
87
+
88
+ def vardeps_float(self):
89
+ """
90
+ Gets the dependencies of all components
91
+ on the function float variables
92
+
93
+ Returns
94
+ -------
95
+ deps: numpy.ndarray of bool
96
+ The dependencies of components on function
97
+ variables, shape: (n_components, n_vars_float)
98
+
99
+ """
100
+ deps = np.zeros((self.n_components(), self.n_components(), 2), dtype=bool)
101
+ np.fill_diagonal(deps[:, :, 0], True)
102
+ np.fill_diagonal(deps[:, :, 1], True)
103
+ return deps.reshape(self.n_components(), self.n_components() * 2)
104
+
105
+ def calc_individual(self, vars_int, vars_float, problem_results, components=None):
106
+ """
107
+ Calculate values for a single individual of the
108
+ underlying problem.
109
+
110
+ Parameters
111
+ ----------
112
+ vars_int: np.array
113
+ The integer variable values, shape: (n_vars_int,)
114
+ vars_float: np.array
115
+ The float variable values, shape: (n_vars_float,)
116
+ problem_results: Any
117
+ The results of the variable application
118
+ to the problem
119
+ components: list of int, optional
120
+ The selected components or None for all
121
+
122
+ Returns
123
+ -------
124
+ values: np.array
125
+ The component values, shape: (n_sel_components,)
126
+
127
+ """
128
+ s = np.s_[:]
129
+ if components is not None and len(components) < self.n_components():
130
+ s = components
131
+ xy = vars_float.reshape(self.n_components(), 2)[s]
132
+
133
+ dists = self.geometry.points_distance(xy)
134
+ dists[self.geometry.points_inside(xy)] *= -1
135
+
136
+ if self.disc_inside:
137
+ if self.D is None:
138
+ dists += problem_results[FV.D].to_numpy()[0, self.sel_turbines][s] / 2
139
+ else:
140
+ dists += self.D / 2
141
+
142
+ return dists
143
+
144
+ def calc_population(self, vars_int, vars_float, problem_results, components=None):
145
+ """
146
+ Calculate values for all individuals of a population.
147
+
148
+ Parameters
149
+ ----------
150
+ vars_int: np.array
151
+ The integer variable values, shape: (n_pop, n_vars_int)
152
+ vars_float: np.array
153
+ The float variable values, shape: (n_pop, n_vars_float)
154
+ problem_results: Any
155
+ The results of the variable application
156
+ to the problem
157
+ components: list of int, optional
158
+ The selected components or None for all
159
+
160
+ Returns
161
+ -------
162
+ values: np.array
163
+ The component values, shape: (n_pop, n_sel_components)
164
+
165
+ """
166
+ n_pop = len(vars_float)
167
+ n_cmpnts = self.n_components()
168
+ s = np.s_[:]
169
+ if components is not None and len(components) < self.n_components():
170
+ n_cmpnts = len(components)
171
+ s = components
172
+ xy = vars_float[:, s].reshape(n_pop * n_cmpnts, 2)
173
+
174
+ dists = self.geometry.points_distance(xy)
175
+ dists[self.geometry.points_inside(xy)] *= -1
176
+ dists = dists.reshape(n_pop, n_cmpnts)
177
+
178
+ if self.disc_inside:
179
+ if self.D is None:
180
+ dists += (
181
+ problem_results[FV.D].to_numpy()[None, 0, self.sel_turbines][s] / 2
182
+ )
183
+ else:
184
+ dists += self.D / 2
185
+
186
+ return dists
187
+
188
+
189
+ class FarmBoundaryConstraint(AreaGeometryConstraint):
190
+ """
191
+ Constrains turbine positions to the inside of
192
+ the wind farm boundary
193
+
194
+ :group: opt.constraints
195
+
196
+ """
197
+
198
+ def __init__(self, problem, name="boundary", **kwargs):
199
+ """
200
+ Constructor.
201
+
202
+ Parameters
203
+ ----------
204
+ problem: foxes_opt.FarmOptProblem
205
+ The underlying optimization problem
206
+ name: str
207
+ The name of the constraint
208
+ kwargs: dict, optional
209
+ Additional parameters for `AreaGeometryConstraint`
210
+
211
+ """
212
+ b = problem.farm.boundary
213
+ assert b is not None, f"Constraint '{name}': Missing wind farm boundary."
214
+ super().__init__(problem, name, geometry=b, **kwargs)
@@ -0,0 +1,239 @@
1
+ import numpy as np
2
+
3
+ from foxes_opt.core.farm_constraint import FarmConstraint
4
+ import foxes.variables as FV
5
+ import foxes.constants as FC
6
+
7
+
8
+ class MinDistConstraint(FarmConstraint):
9
+ """
10
+ Turbines must keep at least a minimal
11
+ spatial distance.
12
+
13
+ Attributes
14
+ ----------
15
+ farm: foxes.WindFarm
16
+ The wind farm
17
+ sel_turbines: list
18
+ The selected turbines
19
+ min_dist: float
20
+ The minimal distance
21
+ min_dist_unit: str
22
+ The minimal distance unit, either m or D
23
+
24
+ :group: opt.constraints
25
+
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ problem,
31
+ min_dist,
32
+ min_dist_unit="m",
33
+ name="dist",
34
+ sel_turbines=None,
35
+ **kwargs,
36
+ ):
37
+ """
38
+ Constructor.
39
+
40
+ Parameters
41
+ ----------
42
+ problem: foxes_opt.FarmOptProblem
43
+ The underlying optimization problem
44
+ min_dist: float
45
+ The minimal distance
46
+ min_dist_unit: str
47
+ The minimal distance unit, either m or D
48
+ name: str
49
+ The name of the constraint
50
+ sel_turbines: list of int, optional
51
+ The selected turbines
52
+ kwargs: dict, optional
53
+ Additional parameters for `iwopy.Constraint`
54
+
55
+ """
56
+ self.min_dist = min_dist
57
+ self.min_dist_unit = min_dist_unit
58
+
59
+ selt = problem.sel_turbines if sel_turbines is None else sel_turbines
60
+ vrs = []
61
+ for ti in selt:
62
+ vrs += [problem.tvar(FV.X, ti), problem.tvar(FV.Y, ti)]
63
+
64
+ super().__init__(problem, name, sel_turbines, vnames_float=vrs, **kwargs)
65
+
66
+ def initialize(self, verbosity=0):
67
+ """
68
+ Initialize the constaint.
69
+
70
+ Parameters
71
+ ----------
72
+ verbosity: int
73
+ The verbosity level, 0 = silent
74
+
75
+ """
76
+ N = self.farm.n_turbines
77
+ self._i2t = [] # i --> (ti, tj)
78
+ self._t2i = np.full([N, N], -1) # (ti, tj) --> i
79
+ i = 0
80
+ for ti in self.sel_turbines:
81
+ for tj in range(N):
82
+ if ti != tj and self._t2i[ti, tj] < 0:
83
+ self._i2t.append([ti, tj])
84
+ self._t2i[ti, tj] = i
85
+ self._t2i[tj, ti] = i
86
+ i += 1
87
+ self._i2t = np.array(self._i2t)
88
+ self._cnames = [f"{self.name}_{ti}_{tj}" for ti, tj in self._i2t]
89
+ super().initialize(verbosity)
90
+
91
+ def n_components(self):
92
+ """
93
+ Returns the number of components of the
94
+ function.
95
+
96
+ Returns
97
+ -------
98
+ int:
99
+ The number of components.
100
+
101
+ """
102
+ return len(self._i2t)
103
+
104
+ def vardeps_float(self):
105
+ """
106
+ Gets the dependencies of all components
107
+ on the function float variables
108
+
109
+ Returns
110
+ -------
111
+ deps: numpy.ndarray of bool
112
+ The dependencies of components on function
113
+ variables, shape: (n_components, n_vars_float)
114
+
115
+ """
116
+ turbs = list(self.problem.sel_turbines)
117
+ deps = np.zeros((self.n_components(), len(turbs), 2), dtype=bool)
118
+ for i, titj in enumerate(self._i2t):
119
+ for t in titj:
120
+ if t in turbs:
121
+ j = turbs.index(t)
122
+ deps[i, j] = True
123
+ return deps.reshape(self.n_components(), 2 * len(turbs))
124
+
125
+ def calc_individual(self, vars_int, vars_float, problem_results, components=None):
126
+ """
127
+ Calculate values for a single individual of the
128
+ underlying problem.
129
+
130
+ Parameters
131
+ ----------
132
+ vars_int: np.array
133
+ The integer variable values, shape: (n_vars_int,)
134
+ vars_float: np.array
135
+ The float variable values, shape: (n_vars_float,)
136
+ problem_results: Any
137
+ The results of the variable application
138
+ to the problem
139
+ components: list of int, optional
140
+ The selected components or None for all
141
+
142
+ Returns
143
+ -------
144
+ values: np.array
145
+ The component values, shape: (n_sel_components,)
146
+
147
+ """
148
+ xy = np.stack(
149
+ [problem_results[FV.X].to_numpy(), problem_results[FV.Y].to_numpy()],
150
+ axis=-1,
151
+ )
152
+ if not np.all(np.abs(np.min(xy, axis=0) - np.max(xy, axis=0)) < 1e-13):
153
+ raise ValueError(f"Constraint '{self.name}': Require state independet XY")
154
+ xy = xy[0]
155
+
156
+ s = np.s_[:]
157
+ if components is not None and len(components) < self.n_components():
158
+ s = components
159
+
160
+ a = np.take_along_axis(xy, self._i2t[s][:, 0, None], axis=0)
161
+ b = np.take_along_axis(xy, self._i2t[s][:, 1, None], axis=0)
162
+ d = np.linalg.norm(a - b, axis=-1)
163
+
164
+ if self.min_dist_unit == "m":
165
+ mind = self.min_dist
166
+
167
+ elif self.min_dist_unit == "D":
168
+ D = problem_results[FV.D].to_numpy()
169
+ if not np.all(np.abs(np.min(D, axis=0) - np.max(D, axis=0)) < 1e-13):
170
+ raise ValueError(
171
+ f"Constraint '{self.name}': Require state independet D"
172
+ )
173
+ D = D[0]
174
+
175
+ Da = np.take_along_axis(D, self._i2t[s][:, 0], axis=0)
176
+ Db = np.take_along_axis(D, self._i2t[s][:, 1], axis=0)
177
+ mind = self.min_dist * np.maximum(Da, Db)
178
+
179
+ return mind - d
180
+
181
+ def calc_population(self, vars_int, vars_float, problem_results, components=None):
182
+ """
183
+ Calculate values for all individuals of a population.
184
+
185
+ Parameters
186
+ ----------
187
+ vars_int: np.array
188
+ The integer variable values, shape: (n_pop, n_vars_int)
189
+ vars_float: np.array
190
+ The float variable values, shape: (n_pop, n_vars_float)
191
+ problem_results: Any
192
+ The results of the variable application
193
+ to the problem
194
+ components: list of int, optional
195
+ The selected components or None for all
196
+
197
+ Returns
198
+ -------
199
+ values: np.array
200
+ The component values, shape: (n_pop, n_sel_components)
201
+
202
+ """
203
+ n_pop = problem_results["n_pop"].values
204
+ n_states = problem_results["n_org_states"].values
205
+ n_turbines = problem_results.sizes[FC.TURBINE]
206
+
207
+ xy = np.stack(
208
+ [problem_results[FV.X].to_numpy(), problem_results[FV.Y].to_numpy()],
209
+ axis=-1,
210
+ )
211
+ xy = xy.reshape(n_pop, n_states, n_turbines, 2)
212
+ if not np.all(np.abs(np.min(xy, axis=1) - np.max(xy, axis=1)) < 1e-13):
213
+ raise ValueError(f"Constraint '{self.name}': Require state independet XY")
214
+ xy = xy[:, 0]
215
+
216
+ s = np.s_[:]
217
+ if components is not None and len(components) < self.n_components():
218
+ s = components
219
+
220
+ a = np.take_along_axis(xy, self._i2t[s][None, :, 0, None], axis=1)
221
+ b = np.take_along_axis(xy, self._i2t[s][None, :, 1, None], axis=1)
222
+ d = np.linalg.norm(a - b, axis=-1)
223
+
224
+ if self.min_dist_unit == "m":
225
+ mind = self.min_dist
226
+
227
+ elif self.min_dist_unit == "D":
228
+ D = problem_results[FV.D].to_numpy().reshape(n_pop, n_states, n_turbines)
229
+ if not np.all(np.abs(np.min(D, axis=1) - np.max(D, axis=1)) < 1e-13):
230
+ raise ValueError(
231
+ f"Constraint '{self.name}': Require state independet D"
232
+ )
233
+ D = D[:, 0]
234
+
235
+ Da = np.take_along_axis(D, self._i2t[s][None, :, 0], axis=1)
236
+ Db = np.take_along_axis(D, self._i2t[s][None, :, 1], axis=1)
237
+ mind = self.min_dist * np.maximum(Da, Db)
238
+
239
+ return mind - d
@@ -0,0 +1,9 @@
1
+ """
2
+ Abstract classes and core functionality for wind farm optimization.
3
+ """
4
+
5
+ from .farm_opt_problem import FarmOptProblem
6
+ from .farm_vars_problem import FarmVarsProblem
7
+ from .farm_objective import FarmObjective
8
+ from .farm_constraint import FarmConstraint
9
+ from .pop_states import PopStates
@@ -0,0 +1,96 @@
1
+ from iwopy import Constraint
2
+
3
+ from foxes.utils import all_subclasses
4
+
5
+
6
+ class FarmConstraint(Constraint):
7
+ """
8
+ Abstract base class for foxes wind farm
9
+ optimization constraints.
10
+
11
+ :group: opt.core
12
+
13
+ """
14
+
15
+ def __init__(self, problem, name, sel_turbines=None, **kwargs):
16
+ """
17
+ Constructor.
18
+
19
+ Parameters
20
+ ----------
21
+ problem: foxes_opt.FarmOptProblem
22
+ The underlying optimization problem
23
+ name: str
24
+ The name of the constraint
25
+ sel_turbines: list of int, optional
26
+ The selected turbines
27
+ kwargs: dict, optional
28
+ Additional parameters for `iwopy.Constraint`
29
+
30
+ """
31
+ super().__init__(problem, name, **kwargs)
32
+ self._sel_turbines = sel_turbines
33
+
34
+ @property
35
+ def farm(self):
36
+ """
37
+ The wind farm
38
+
39
+ Returns
40
+ -------
41
+ foxes.core.WindFarm :
42
+ The wind farm
43
+
44
+ """
45
+ return self.problem.farm
46
+
47
+ @property
48
+ def sel_turbines(self):
49
+ """
50
+ The list of selected turbines
51
+
52
+ Returns
53
+ -------
54
+ list of int :
55
+ The list of selected turbines
56
+
57
+ """
58
+ return (
59
+ self.problem.sel_turbines
60
+ if self._sel_turbines is None
61
+ else self._sel_turbines
62
+ )
63
+
64
+ @property
65
+ def n_sel_turbines(self):
66
+ """
67
+ The numer of selected turbines
68
+
69
+ Returns
70
+ -------
71
+ int :
72
+ The numer of selected turbines
73
+
74
+ """
75
+ return len(self.sel_turbines)
76
+
77
+ def add_to_layout_figure(self, ax, **kwargs):
78
+ """
79
+ Add to a layout figure
80
+
81
+ Parameters
82
+ ----------
83
+ ax: matplotlib.pyplot.Axis
84
+ The figure axis
85
+
86
+ """
87
+ return ax
88
+
89
+ @classmethod
90
+ def print_models(cls):
91
+ """
92
+ Prints all model names.
93
+ """
94
+ names = sorted([scls.__name__ for scls in all_subclasses(cls)])
95
+ for n in names:
96
+ print(n)