Skip to content

Commit f3f42ed

Browse files
Bartdoekemeijerpaulf81misi9170
authored
Reduce computation time massively in large het_map objects (#1024)
* Reduce computation time massively in large het_map objects by avoiding the recalculation of the delaunay triangulation for every findex * Import copy library * Enforce appropriate shape for interpolant object * ruff formatting * bugfix * Add a test of applying a het map * Add convert to array * Add convert to array * Add to new tests * Clean up * Add comments and rename variables for clarity. * Change FlowField.het_map to be a numpy array rather than a list. --------- Co-authored-by: Paul <[email protected]> Co-authored-by: misi9170 <[email protected]>
1 parent 33ffc73 commit f3f42ed

File tree

2 files changed

+133
-12
lines changed

2 files changed

+133
-12
lines changed

floris/core/flow_field.py

+32-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
from __future__ import annotations
33

4+
import copy
5+
46
import attrs
57
import matplotlib.path as mpltPath
68
import numpy as np
@@ -16,6 +18,7 @@
1618
from floris.type_dec import (
1719
floris_array_converter,
1820
NDArrayFloat,
21+
NDArrayObject,
1922
)
2023

2124

@@ -41,7 +44,7 @@ class FlowField(BaseClass):
4144
u: NDArrayFloat = field(init=False, factory=lambda: np.array([]))
4245
v: NDArrayFloat = field(init=False, factory=lambda: np.array([]))
4346
w: NDArrayFloat = field(init=False, factory=lambda: np.array([]))
44-
het_map: list = field(init=False, default=None)
47+
het_map: NDArrayObject = field(init=False, default=None)
4548
dudz_initial_sorted: NDArrayFloat = field(init=False, factory=lambda: np.array([]))
4649

4750
turbulence_intensity_field: NDArrayFloat = field(init=False, factory=lambda: np.array([]))
@@ -281,29 +284,46 @@ def generate_heterogeneous_wind_map(self):
281284
- **y**: A list of y locations at which the speed up factors are defined.
282285
- **z** (optional): A list of z locations at which the speed up factors are defined.
283286
"""
284-
speed_multipliers = self.heterogeneous_inflow_config['speed_multipliers']
287+
speed_multipliers = np.array(self.heterogeneous_inflow_config['speed_multipliers'])
285288
x = self.heterogeneous_inflow_config['x']
286289
y = self.heterogeneous_inflow_config['y']
287290
z = self.heterogeneous_inflow_config['z']
288291

292+
# Declare an empty list to store interpolants by findex
293+
interps_f = np.empty(self.n_findex, dtype=object)
289294
if z is not None:
290295
# Compute the 3-dimensional interpolants for each wind direction
291296
# Linear interpolation is used for points within the user-defined area of values,
292-
# while the freestream wind speed is used for points outside that region
293-
in_region = [
294-
self.interpolate_multiplier_xyz(x, y, z, multiplier, fill_value=1.0)
295-
for multiplier in speed_multipliers
296-
]
297+
# while the freestream wind speed is used for points outside that region.
298+
299+
# Because the (x,y,z) points are the same for each findex, we create the triangulation
300+
# once and then overwrite the values for each findex.
301+
302+
# Create triangulation using zeroth findex
303+
interp_3d = self.interpolate_multiplier_xyz(
304+
x, y, z, speed_multipliers[0], fill_value=1.0
305+
)
306+
# Copy the interpolant for each findex and overwrite the values
307+
for findex in range(self.n_findex):
308+
interp_3d.values = speed_multipliers[findex, :].reshape(-1, 1)
309+
interps_f[findex] = copy.deepcopy(interp_3d)
310+
297311
else:
298312
# Compute the 2-dimensional interpolants for each wind direction
299313
# Linear interpolation is used for points within the user-defined area of values,
300314
# while the freestream wind speed is used for points outside that region
301-
in_region = [
302-
self.interpolate_multiplier_xy(x, y, multiplier, fill_value=1.0)
303-
for multiplier in speed_multipliers
304-
]
305315

306-
self.het_map = in_region
316+
# Because the (x,y) points are the same for each findex, we create the triangulation
317+
# once and then overwrite the values for each findex.
318+
319+
# Create triangulation using zeroth findex
320+
interp_2d = self.interpolate_multiplier_xy(x, y, speed_multipliers[0], fill_value=1.0)
321+
# Copy the interpolant for each findex and overwrite the values
322+
for findex in range(self.n_findex):
323+
interp_2d.values = speed_multipliers[findex, :].reshape(-1, 1)
324+
interps_f[findex] = copy.deepcopy(interp_2d)
325+
326+
self.het_map = interps_f
307327

308328
@staticmethod
309329
def interpolate_multiplier_xy(x: NDArrayFloat,

tests/heterogeneous_map_integration_test.py

+101
Original file line numberDiff line numberDiff line change
@@ -394,3 +394,104 @@ def test_3d_het_and_shear():
394394
wind_directions=[270.0], wind_speeds=[wind_speed]
395395
),
396396
)
397+
398+
399+
def test_run_2d_het_map():
400+
# Define a 2D het map and confirm the results are as expected
401+
# when applied to FLORIS
402+
403+
# The side of the flow which is accelerated reverses for east versus west
404+
het_map = HeterogeneousMap(
405+
x=np.array([0.0, 0.0, 500.0, 500.0]),
406+
y=np.array([0.0, 500.0, 0.0, 500.0]),
407+
speed_multipliers=np.array(
408+
[
409+
[1.0, 2.0, 1.0, 2.0], # Top accelerated
410+
[2.0, 1.0, 2.0, 1.0], # Bottom accelerated
411+
]
412+
),
413+
wind_directions=np.array([270.0, 90.0]),
414+
wind_speeds=np.array([8.0, 8.0]),
415+
)
416+
417+
# Get the FLORIS model
418+
fmodel = FlorisModel(configuration=YAML_INPUT)
419+
420+
from floris import TimeSeries
421+
422+
time_series = TimeSeries(
423+
wind_directions=np.array([270.0, 90.0]),
424+
wind_speeds=8.0,
425+
turbulence_intensities=0.06,
426+
heterogeneous_map=het_map,
427+
)
428+
429+
# Set the model to a turbines perpinducluar to
430+
# east/west flow with 0 turbine closer to bottom and
431+
# turbine 1 closer to top
432+
fmodel.set(
433+
wind_data=time_series,
434+
layout_x=[250.0, 250.0],
435+
layout_y=[100.0, 400.0],
436+
)
437+
438+
# Run the model
439+
fmodel.run()
440+
441+
# Get the turbine powers
442+
powers = fmodel.get_turbine_powers()
443+
444+
# Assert that in the first condition, turbine 1 has higher power
445+
assert powers[0, 1] > powers[0, 0]
446+
447+
# Assert that in the second condition, turbine 0 has higher power
448+
assert powers[1, 0] > powers[1, 1]
449+
450+
# Assert that the power of turbine 1 equals in the first condition
451+
# equals the power of turbine 0 in the second condition
452+
assert powers[0, 1] == powers[1, 0]
453+
454+
455+
def test_het_config():
456+
457+
# Test that setting FLORIS with a heterogeneous inflow configuration
458+
# works as expected and consistent with previous results
459+
460+
# Get the FLORIS model
461+
fmodel = FlorisModel(configuration=YAML_INPUT)
462+
463+
# Change the layout to a 4 turbine layout in a box
464+
fmodel.set(layout_x=[0, 0, 500.0, 500.0], layout_y=[0, 500.0, 0, 500.0])
465+
466+
# Set FLORIS to run for a single condition
467+
fmodel.set(wind_speeds=[8.0], wind_directions=[270.0], turbulence_intensities=[0.06])
468+
469+
# Define the speed-ups of the heterogeneous inflow, and their locations.
470+
# Note that heterogeneity is only applied within the bounds of the points defined in the
471+
# heterogeneous_inflow_config dictionary. In this case, set the inflow to be 1.25x the ambient
472+
# wind speed for the upper turbines at y = 500m.
473+
speed_ups = [[1.0, 1.25, 1.0, 1.25]] # Note speed-ups has dimensions of n_findex X n_points
474+
x_locs = [-500.0, -500.0, 1000.0, 1000.0]
475+
y_locs = [-500.0, 1000.0, -500.0, 1000.0]
476+
477+
# Create the configuration dictionary to be used for the heterogeneous inflow.
478+
heterogeneous_inflow_config = {
479+
"speed_multipliers": speed_ups,
480+
"x": x_locs,
481+
"y": y_locs,
482+
}
483+
484+
# Set the heterogeneous inflow configuration
485+
fmodel.set(heterogeneous_inflow_config=heterogeneous_inflow_config)
486+
487+
# Run the FLORIS simulation
488+
fmodel.run()
489+
490+
turbine_powers = fmodel.get_turbine_powers() / 1000.0
491+
492+
# Test that the turbine powers are consistent with previous implementation
493+
# 2248.2, 2800.1, 466.2, 601.5 before the change
494+
# Using almost equal assert
495+
assert np.allclose(
496+
turbine_powers, np.array([[2248.2, 2800.0, 466.2, 601.4]]), atol=1.0,
497+
)

0 commit comments

Comments
 (0)