Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wind direction heterogeneity #954

Draft
wants to merge 39 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b3d4c8d
Adding preliminary tests and some infrastructure for bulk wd heteroge…
misi9170 May 20, 2024
20edf05
WIP
misi9170 May 21, 2024
d73eb29
Adding test for turbines only.
misi9170 May 21, 2024
3270135
switched to sliding method.
misi9170 May 21, 2024
3912720
Some running visualization examples now.
misi9170 May 21, 2024
3ddb1de
Fix sign issue.
misi9170 May 21, 2024
6469c63
upddated method.
misi9170 May 23, 2024
845d3da
Enable other full_flow solvers.
misi9170 May 23, 2024
89d771d
Update file path to run.
misi9170 Jun 26, 2024
6eaff02
Merge branch 'develop' into feature/bulk-wd-het
misi9170 Jun 26, 2024
63fe52a
Ruff and isort.
misi9170 Jun 26, 2024
46e28aa
Initial infrastucture for turbine-specific layouts. Currently only in…
misi9170 Jul 25, 2024
4bd5e82
Mock up with turbine locations simply repeated.
misi9170 Jul 25, 2024
b067dee
Placeholder for the layout modifier.
misi9170 Jul 25, 2024
34e8b53
Add basic example; wind_direcitons not used, simply hardcoded rearran…
misi9170 Jul 25, 2024
66ef9fe
Infrastructure now in place and running for full_flow_sequential_solver
misi9170 Jul 29, 2024
3d73fe3
Working through a 2D example case.
misi9170 Jul 29, 2024
9746ec8
Viz by sweep working for single findex case.
misi9170 Jul 31, 2024
2fed76f
Works over findices, but by looping (may be slow for many findices)
misi9170 Jul 31, 2024
92b3ae4
Fixes for full flow solve and visualization.
misi9170 Jul 31, 2024
4f74a46
Merge branch 'develop' into feature/wd-het-2
misi9170 Aug 1, 2024
a391e7d
Committing first-pass tests.
misi9170 Aug 1, 2024
d412853
Remove examples and tests relating to earlier prototype.
misi9170 Aug 1, 2024
e34ec85
Remove running code for earlier prototype.
misi9170 Aug 1, 2024
bb38c5c
Consolidate and rename examples.
misi9170 Aug 1, 2024
a428113
Change turbine ordering so that viz by sweep works again.
misi9170 Aug 1, 2024
64916e4
Remove unneeded debugging code.
misi9170 Aug 1, 2024
049b7de
Require that Grid children be instantiated with required keyword args…
misi9170 Aug 1, 2024
7d1c8ed
Formatting.
misi9170 Aug 2, 2024
1fc4c32
Working on both 3d and rotations
misi9170 Aug 5, 2024
d482344
Remove z capabilities; working on rotation.
misi9170 Aug 5, 2024
796e146
Set up rotation tests correctly; now passing.
misi9170 Aug 5, 2024
e4ce7fb
Enable for all wake models/solvers.
misi9170 Aug 6, 2024
86884bc
Rename to unit test.
misi9170 Aug 6, 2024
7f9daa8
Enable specifying wind speed heterogeneity using u and v
misi9170 Aug 6, 2024
e391664
improved integration, handling for moving grid poitns by streamline.
Aug 8, 2024
625c934
Delay sqrt (doesnt seem to help much)
Aug 26, 2024
e42b4f4
Switch viz_by_sweep to False.
misi9170 Aug 15, 2024
607f50a
Revert to shift_points_by_streamline while debugging.
misi9170 Aug 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import matplotlib.pyplot as plt
import numpy as np

import floris.layout_visualization as layoutviz
from floris import FlorisModel
from floris.flow_visualization import visualize_cut_plane


visualize = True

# Get a test fi (single turbine at 0,0)
fmodel = FlorisModel("../inputs/gch.yaml")
fmodel.set(layout_x=[0, 0, 600, 600], layout_y=[0, -300, 0, -300])

fmodel.set(
wind_directions=[270.0],
wind_speeds=[8.0],
turbulence_intensities=[0.06],
wind_shear=0.0
)
fmodel.run()
P_wo_het = fmodel.get_turbine_powers()/1e6

het_inflow_config = {
"x": np.array([-1000.0, -1000.0, 1000.0, 1000.0]),
"y": np.array([-500.0, 500.0, -500.0, 500.0]),
"speed_multipliers": np.array([[1.0, 1.0, 1.0, 1.0]]),
"u": np.array([[8.0, 8.0, 8.0, 8.0]]),
"v": np.array([[-2.0, 0.0, -2.0, 0.0]]),
#"v": np.array([[0.0, 0.0, 0.0, 0.0]]),
}

fmodel.set(heterogeneous_inflow_config=het_inflow_config)
fmodel.run()
P_w_het = fmodel.get_turbine_powers()/1e6

print("Difference (MW):", P_w_het - P_wo_het)

if visualize:
fig, ax = plt.subplots(2, 1, figsize=(4,8))
fmodel.set(
heterogeneous_inflow_config={
"x": np.array([-1000.0, -1000.0, 1000.0, 1000.0]),
"y": np.array([-1000.0, 1000.0, -1000.0, 1000.0]),
"speed_multipliers":np.array([[1.0, 1.0, 1.0, 1.0]])
}
)
fmodel.run()
horizontal_plane = fmodel.calculate_horizontal_plane(
x_resolution=200,
y_resolution=100,
height=90.0,
x_bounds=(-200, 1000),
y_bounds=(-500, 500),
)
visualize_cut_plane(
horizontal_plane,
ax=ax[0],
label_contours=False,
title="Without WD het"
)

fmodel.set(heterogeneous_inflow_config=het_inflow_config)
horizontal_plane = fmodel.calculate_horizontal_plane(
x_resolution=200,
y_resolution=100,
height=90.0,
x_bounds=(-200, 1000),
y_bounds=(-500, 500),
)
#import ipdb; ipdb.set_trace()
visualize_cut_plane(
horizontal_plane,
ax=ax[1],
label_contours=False,
title="With WD het"
)

plt.show()
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Example: Visualize flow by sweeping turbines

Demonstrate the use calculate_horizontal_plane_with_turbines

"""

import matplotlib.pyplot as plt
import numpy as np

import floris.flow_visualization as flowviz
from floris import FlorisModel


fmodel = FlorisModel("../inputs/gch.yaml")

viz_by_sweep = False

# # Some wake models may not yet have a visualization method included, for these cases can use
# # a slower version which scans a turbine model to produce the horizontal flow
x = np.array([-100, 0, 100, 200, 300, 400, 500, 600])
v = -np.array([[0, 1, 2, 3, 4, 5, 6, 7]])

het_inflow_config = {
"x": np.repeat(x, 2),
"y": np.tile(np.array([-100.0, 100.0]), 8),
#"z": np.array([90.0, 90.0, 90.0, 91.0]),
"speed_multipliers": 1.0*np.ones((1, 16)),
"u": 8.0*np.ones((1, v.shape[1]*2)),
"v": np.repeat(v, 2, axis=1)
#"v": np.array([[0.0, 0.0, 0.0, 0.0]]),
}

# Set a 2 turbine layout
fmodel.set(
layout_x=[0, 300],
layout_y=[0, 0],
wind_directions=[270],
wind_speeds=[8],
turbulence_intensities=[0.06],
heterogeneous_inflow_config=het_inflow_config,
)

if viz_by_sweep:
horizontal_plane = flowviz.calculate_horizontal_plane_with_turbines(
fmodel,
x_resolution=20,
y_resolution=10,
x_bounds=(-100, 500),
y_bounds=(-100, 100),
)
title = "Coarse turbine scan method"
else:
horizontal_plane = fmodel.calculate_horizontal_plane(
x_resolution=200,
y_resolution=100,
height=90.0,
x_bounds=(-100, 500),
y_bounds=(-100, 100),
)
title = "Standard flow visualization calculation"

fig, ax = plt.subplots(figsize=(10, 4))
flowviz.visualize_cut_plane(
horizontal_plane,
ax=ax,
label_contours=True,
title=title
)


plt.show()
5 changes: 5 additions & 0 deletions floris/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,23 @@ def __attrs_post_init__(self) -> None:
turbine_diameters=self.farm.rotor_diameters,
wind_directions=self.flow_field.wind_directions,
grid_resolution=self.solver["turbine_grid_points"],
heterogeneous_inflow_config=self.flow_field.heterogeneous_inflow_config,
)
elif self.solver["type"] == "turbine_cubature_grid":
self.grid = TurbineCubatureGrid(
turbine_coordinates=self.farm.coordinates,
turbine_diameters=self.farm.rotor_diameters,
wind_directions=self.flow_field.wind_directions,
grid_resolution=self.solver["turbine_grid_points"],
heterogeneous_inflow_config=self.flow_field.heterogeneous_inflow_config,
)
elif self.solver["type"] == "flow_field_grid":
self.grid = FlowFieldGrid(
turbine_coordinates=self.farm.coordinates,
turbine_diameters=self.farm.rotor_diameters,
wind_directions=self.flow_field.wind_directions,
grid_resolution=self.solver["flow_field_grid_points"],
heterogeneous_inflow_config=self.flow_field.heterogeneous_inflow_config,
)
elif self.solver["type"] == "flow_field_planar_grid":
self.grid = FlowFieldPlanarGrid(
Expand All @@ -119,6 +122,7 @@ def __attrs_post_init__(self) -> None:
normal_vector=self.solver["normal_vector"],
planar_coordinate=self.solver["planar_coordinate"],
grid_resolution=self.solver["flow_field_grid_points"],
heterogeneous_inflow_config=self.flow_field.heterogeneous_inflow_config,
x1_bounds=self.solver["flow_field_bounds"][0],
x2_bounds=self.solver["flow_field_bounds"][1],
)
Expand Down Expand Up @@ -243,6 +247,7 @@ def solve_for_points(self, x, y, z):
turbine_coordinates=self.farm.coordinates,
turbine_diameters=self.farm.rotor_diameters,
wind_directions=self.flow_field.wind_directions,
heterogeneous_inflow_config=self.flow_field.heterogeneous_inflow_config,
grid_resolution=1,
x_center_of_rotation=self.grid.x_center_of_rotation,
y_center_of_rotation=self.grid.y_center_of_rotation
Expand Down
26 changes: 21 additions & 5 deletions floris/core/flow_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,18 @@ def heterogeneous_config_validator(self, instance: attrs.Attribute, value: dict
return

# Check that the correct keys are supplied for the heterogeneous_inflow_config dict
for k in ["speed_multipliers", "x", "y"]:
for k in ["x", "y"]:
if k not in value.keys():
raise ValueError(
"heterogeneous_inflow_config must contain entries for 'speed_multipliers',"
f"'x', and 'y', with 'z' optional. Missing '{k}'."
"heterogeneous_inflow_config must contain entries for "
f"'x' and 'y', with 'z' optional. Missing '{k}'."
)
if ("speed_multipliers" not in value.keys() and
("u" not in value.keys() or "v" not in value.keys())):
raise ValueError(
"heterogeneous_inflow_config must contain entries for either 'speed_multipliers'"
" or 'u' and 'v'."
)
if "z" not in value:
# If only a 2D case, add "None" for the z locations
value["z"] = None
Expand All @@ -130,7 +136,7 @@ def het_map_validator(self, instance: attrs.Attribute, value: list | None) -> No


def __attrs_post_init__(self) -> None:
if self.heterogeneous_inflow_config is not None:
if self._speed_heterogeneity:
self.generate_heterogeneous_wind_map()


Expand Down Expand Up @@ -281,7 +287,13 @@ def generate_heterogeneous_wind_map(self):
- **y**: A list of y locations at which the speed up factors are defined.
- **z** (optional): A list of z locations at which the speed up factors are defined.
"""
speed_multipliers = self.heterogeneous_inflow_config['speed_multipliers']
if "speed_multipliers" in self.heterogeneous_inflow_config:
speed_multipliers = self.heterogeneous_inflow_config['speed_multipliers']
else:
speed_multipliers = np.sqrt(
np.array(self.heterogeneous_inflow_config['u'])**2
+ np.array(self.heterogeneous_inflow_config['v'])**2
) / self.wind_speeds
x = self.heterogeneous_inflow_config['x']
y = self.heterogeneous_inflow_config['y']
z = self.heterogeneous_inflow_config['z']
Expand All @@ -305,6 +317,10 @@ def generate_heterogeneous_wind_map(self):

self.het_map = in_region

@property
def _speed_heterogeneity(self):
return self.heterogeneous_inflow_config is not None

@staticmethod
def interpolate_multiplier_xy(x: NDArrayFloat,
y: NDArrayFloat,
Expand Down
Loading
Loading