|
8 | 8 |
|
9 | 9 | from floris.core import State
|
10 | 10 | from floris.floris_model import FlorisModel
|
| 11 | +from floris.type_dec import ( |
| 12 | + NDArrayFloat, |
| 13 | +) |
11 | 14 |
|
12 | 15 |
|
13 | 16 | class ParFlorisModel(FlorisModel):
|
@@ -123,81 +126,111 @@ def run(self) -> None:
|
123 | 126 | t0 = timerpc()
|
124 | 127 | super().run()
|
125 | 128 | t1 = timerpc()
|
126 |
| - elif self.interface == "multiprocessing": |
| 129 | + self._print_timings(t0, t1, None, None) |
| 130 | + else: |
127 | 131 | t0 = timerpc()
|
128 | 132 | self.core.initialize_domain()
|
129 | 133 | parallel_run_inputs = self._preprocessing()
|
130 | 134 | t1 = timerpc()
|
131 |
| - if self.return_turbine_powers_only: |
132 |
| - with self._PoolExecutor(self.max_workers) as p: |
133 |
| - self._turbine_powers_split = p.starmap( |
134 |
| - _parallel_run_powers_only, |
| 135 | + if self.interface == "multiprocessing": |
| 136 | + if self.return_turbine_powers_only: |
| 137 | + with self._PoolExecutor(self.max_workers) as p: |
| 138 | + self._turbine_powers_split = p.starmap( |
| 139 | + _parallel_run_powers_only, |
| 140 | + parallel_run_inputs |
| 141 | + ) |
| 142 | + else: |
| 143 | + with self._PoolExecutor(self.max_workers) as p: |
| 144 | + self._fmodels_split = p.starmap(_parallel_run, parallel_run_inputs) |
| 145 | + elif self.interface == "pathos": |
| 146 | + if self.return_turbine_powers_only: |
| 147 | + self._turbine_powers_split = self.pathos_pool.map( |
| 148 | + _parallel_run_powers_only_map, |
135 | 149 | parallel_run_inputs
|
136 | 150 | )
|
137 |
| - else: |
138 |
| - with self._PoolExecutor(self.max_workers) as p: |
139 |
| - self._fmodels_split = p.starmap(_parallel_run, parallel_run_inputs) |
| 151 | + else: |
| 152 | + self._fmodels_split = self.pathos_pool.map( |
| 153 | + _parallel_run_map, |
| 154 | + parallel_run_inputs |
| 155 | + ) |
| 156 | + elif self.interface == "concurrent": |
| 157 | + if self.return_turbine_powers_only: |
| 158 | + with self._PoolExecutor(self.max_workers) as p: |
| 159 | + self._turbine_powers_split = p.map( |
| 160 | + _parallel_run_powers_only_map, |
| 161 | + parallel_run_inputs |
| 162 | + ) |
| 163 | + self._turbine_powers_split = list(self._turbine_powers_split) |
| 164 | + else: |
| 165 | + with self._PoolExecutor(self.max_workers) as p: |
| 166 | + self._fmodels_split = p.map( |
| 167 | + _parallel_run_map, |
| 168 | + parallel_run_inputs |
| 169 | + ) |
| 170 | + self._fmodels_split = list(self._fmodels_split) |
140 | 171 | t2 = timerpc()
|
141 | 172 | self._postprocessing()
|
142 | 173 | self.core.farm.finalize(self.core.grid.unsorted_indices)
|
143 | 174 | self.core.state = State.USED
|
144 | 175 | t3 = timerpc()
|
145 |
| - elif self.interface == "pathos": |
| 176 | + self._print_timings(t0, t1, t2, t3) |
| 177 | + |
| 178 | + def sample_flow_at_points(self, x: NDArrayFloat, y: NDArrayFloat, z: NDArrayFloat): |
| 179 | + """ |
| 180 | + Sample the flow field at specified points. |
| 181 | +
|
| 182 | + Args: |
| 183 | + x: The x-coordinates of the points. |
| 184 | + y: The y-coordinates of the points. |
| 185 | + z: The z-coordinates of the points. |
| 186 | +
|
| 187 | + Returns: |
| 188 | + NDArrayFloat: The wind speeds at the specified points. |
| 189 | + """ |
| 190 | + if self.return_turbine_powers_only: |
| 191 | + raise NotImplementedError( |
| 192 | + "Sampling flow at points is not supported when " |
| 193 | + "return_turbine_powers_only is set to True on ParFlorisModel." |
| 194 | + ) |
| 195 | + |
| 196 | + if self.interface is None: |
146 | 197 | t0 = timerpc()
|
147 |
| - self.core.initialize_domain() |
148 |
| - parallel_run_inputs = self._preprocessing() |
| 198 | + sampled_wind_speeds = super().sample_flow_at_points(x, y, z) |
149 | 199 | t1 = timerpc()
|
150 |
| - if self.return_turbine_powers_only: |
151 |
| - self._turbine_powers_split = self.pathos_pool.map( |
152 |
| - _parallel_run_powers_only_map, |
153 |
| - parallel_run_inputs |
154 |
| - ) |
155 |
| - else: |
156 |
| - self._fmodels_split = self.pathos_pool.map( |
157 |
| - _parallel_run_map, |
158 |
| - parallel_run_inputs |
159 |
| - ) |
160 |
| - t2 = timerpc() |
161 |
| - self._postprocessing() |
162 |
| - self.core.farm.finalize(self.core.grid.unsorted_indices) |
163 |
| - self.core.state = State.USED |
164 |
| - t3 = timerpc() |
165 |
| - elif self.interface == "concurrent": |
| 200 | + self._print_timings(t0, t1, None, None) |
| 201 | + else: |
166 | 202 | t0 = timerpc()
|
167 | 203 | self.core.initialize_domain()
|
168 | 204 | parallel_run_inputs = self._preprocessing()
|
| 205 | + parallel_sample_flow_at_points_inputs = [ |
| 206 | + (fmodel_dict, control_setpoints, x, y, z) |
| 207 | + for fmodel_dict, control_setpoints in parallel_run_inputs |
| 208 | + ] |
169 | 209 | t1 = timerpc()
|
170 |
| - if self.return_turbine_powers_only: |
| 210 | + if self.interface == "multiprocessing": |
171 | 211 | with self._PoolExecutor(self.max_workers) as p:
|
172 |
| - self._turbine_powers_split = p.map( |
173 |
| - _parallel_run_powers_only_map, |
174 |
| - parallel_run_inputs |
| 212 | + sampled_wind_speeds_p = p.starmap( |
| 213 | + _parallel_sample_flow_at_points, |
| 214 | + parallel_sample_flow_at_points_inputs |
175 | 215 | )
|
176 |
| - self._turbine_powers_split = list(self._turbine_powers_split) |
177 |
| - else: |
| 216 | + elif self.interface == "pathos": |
| 217 | + sampled_wind_speeds_p = self.pathos_pool.map( |
| 218 | + _parallel_sample_flow_at_points_map, |
| 219 | + parallel_sample_flow_at_points_inputs |
| 220 | + ) |
| 221 | + elif self.interface == "concurrent": |
178 | 222 | with self._PoolExecutor(self.max_workers) as p:
|
179 |
| - self._fmodels_split = p.map( |
180 |
| - _parallel_run_map, |
181 |
| - parallel_run_inputs |
| 223 | + sampled_wind_speeds_p = p.map( |
| 224 | + _parallel_sample_flow_at_points_map, |
| 225 | + parallel_sample_flow_at_points_inputs |
182 | 226 | )
|
183 |
| - self._fmodels_split = list(self._fmodels_split) |
| 227 | + sampled_wind_speeds_p = list(sampled_wind_speeds_p) |
184 | 228 | t2 = timerpc()
|
185 |
| - self._postprocessing() |
186 |
| - self.core.farm.finalize(self.core.grid.unsorted_indices) |
187 |
| - self.core.state = State.USED |
| 229 | + sampled_wind_speeds = np.concatenate(sampled_wind_speeds_p, axis=0) |
188 | 230 | t3 = timerpc()
|
189 |
| - if self.print_timings: |
190 |
| - print("===============================================================================") |
191 |
| - if self.interface is None: |
192 |
| - print(f"Total time spent for serial calculation (interface=None): {t1 - t0:.3f} s") |
193 |
| - else: |
194 |
| - print( |
195 |
| - "Total time spent for parallel calculation " |
196 |
| - f"({self.max_workers} workers): {t3-t0:.3f} s" |
197 |
| - ) |
198 |
| - print(f" Time spent in parallel preprocessing: {t1-t0:.3f} s") |
199 |
| - print(f" Time spent in parallel loop execution: {t2-t1:.3f} s.") |
200 |
| - print(f" Time spent in parallel postprocessing: {t3-t2:.3f} s") |
| 231 | + self._print_timings(t0, t1, t2, t3) |
| 232 | + |
| 233 | + return sampled_wind_speeds |
201 | 234 |
|
202 | 235 | def _preprocessing(self):
|
203 | 236 | """
|
@@ -278,6 +311,23 @@ def _postprocessing(self):
|
278 | 311 | axis=0
|
279 | 312 | )
|
280 | 313 |
|
| 314 | + def _print_timings(self, t0, t1, t2, t3): |
| 315 | + """ |
| 316 | + Print the timings for the parallel execution. |
| 317 | + """ |
| 318 | + if self.print_timings: |
| 319 | + print("===============================================================================") |
| 320 | + if self.interface is None: |
| 321 | + print(f"Total time spent for serial calculation (interface=None): {t1 - t0:.3f} s") |
| 322 | + else: |
| 323 | + print( |
| 324 | + "Total time spent for parallel calculation " |
| 325 | + f"({self.max_workers} workers): {t3-t0:.3f} s" |
| 326 | + ) |
| 327 | + print(f" Time spent in parallel preprocessing: {t1-t0:.3f} s") |
| 328 | + print(f" Time spent in parallel loop execution: {t2-t1:.3f} s.") |
| 329 | + print(f" Time spent in parallel postprocessing: {t3-t2:.3f} s") |
| 330 | + |
281 | 331 | def _get_turbine_powers(self):
|
282 | 332 | """
|
283 | 333 | Calculates the power at each turbine in the wind farm.
|
@@ -364,3 +414,14 @@ def _parallel_run_powers_only_map(x):
|
364 | 414 | Wrapper for unpacking inputs to _parallel_run_powers_only() for use with map().
|
365 | 415 | """
|
366 | 416 | return _parallel_run_powers_only(*x)
|
| 417 | + |
| 418 | +def _parallel_sample_flow_at_points(fmodel_dict, set_kwargs, x, y, z): |
| 419 | + fmodel = FlorisModel(fmodel_dict) |
| 420 | + fmodel.set(**set_kwargs) |
| 421 | + return fmodel.sample_flow_at_points(x, y, z) |
| 422 | + |
| 423 | +def _parallel_sample_flow_at_points_map(x): |
| 424 | + """ |
| 425 | + Wrapper for unpacking inputs to _parallel_sample_flow_at_points() for use with map(). |
| 426 | + """ |
| 427 | + return _parallel_sample_flow_at_points(*x) |
0 commit comments