Skip to content

Commit bcfea64

Browse files
misi9170paulf81
andauthored
Allow multidim_conditions to be specified across multiple findices (#1152)
* Working prototype for thrust * Add handling for power, axial_induction * Update examples to (optionally) remove looping * Add multidim_conditions handling to TimeSeries and FlorisModel * Add placeholders to WindRose * Add tests for wind data objects (WindRose currently fails * Check WRG and output WindRose have no multidim_conditions * FlorisModel integration of multiple conditions * Tests and code for ParFlorisModel * support new changes to multdim throuth UncFLORISmodel * Add utility for checking if dict all scalars * Tests for wind_ti_rose (may need work) * wind rose updates to meet tests * Return None if not defined * Pass through scalar multidim_conditions to improve performance * Use is_all_scalar_dict in ParFlorisModel * Explain new usage of multidim_conditions in script docstrings * Update mutlidim docs --------- Co-authored-by: paulf81 <paul.fleming@nrel.gov>
1 parent 8bb03d8 commit bcfea64

16 files changed

Lines changed: 1068 additions & 184 deletions

docs/multidimensional_wind_turbine.ipynb

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,65 @@
9595
"source": [
9696
"Note that this example is not meant to be represntative of a real turbine, but rather to illustrate\n",
9797
"the functionality. At this time, FLORIS only support a single external condition combination at a\n",
98-
"time, but multiple combinations can be run sequentially as is shown above."
98+
"time, but multiple combinations can be run sequentially as is shown above.\n",
99+
"\n",
100+
"As of FLORIS v4.6, users can further specify array-based multidimensional conditions, where a\n",
101+
"different multidimensional condition can be specified for each findex. This allows users to run\n",
102+
"multiple multidimensional condition combinations in a single call to `fmodel.run()`. Further,\n",
103+
"multidimensional conditions can now be passed to `floris.set()` via the `WindData` objects\n",
104+
"(`TimeSeries`, in this case)."
99105
]
100106
},
101107
{
102108
"cell_type": "markdown",
103109
"id": "8c21f432",
104110
"metadata": {},
105111
"source": []
112+
},
113+
{
114+
"cell_type": "code",
115+
"execution_count": null,
116+
"id": "c0b18ef8",
117+
"metadata": {},
118+
"outputs": [],
119+
"source": [
120+
"multidim_conditions = {\n",
121+
" \"Tp\": np.array([2]*n_wind_speeds + [4]*n_wind_speeds),\n",
122+
" \"Hs\": np.array([1]*n_wind_speeds + [5]*n_wind_speeds),\n",
123+
"}\n",
124+
"\n",
125+
"fmodel.set(\n",
126+
" wind_data=TimeSeries(\n",
127+
" wind_directions=np.zeros(n_wind_speeds*2),\n",
128+
" wind_speeds=np.tile(wind_speeds, 2),\n",
129+
" turbulence_intensities=0.06,\n",
130+
" multidim_conditions=multidim_conditions,\n",
131+
" ),\n",
132+
")\n",
133+
"# Call fmodel.run() once and run both conditions in one go\n",
134+
"fmodel.run()\n",
135+
"\n",
136+
"powers = fmodel.get_turbine_powers()\n",
137+
"thrust_coefficients = fmodel.get_turbine_thrust_coefficients()\n",
138+
"\n",
139+
"fig, ax = plt.subplots(2, 1, sharex=True)\n",
140+
"ax[0].plot(wind_speeds, powers[:n_wind_speeds], label=\"First condition\")\n",
141+
"ax[0].grid()\n",
142+
"ax[0].set_ylabel(\"Power [kW]\")\n",
143+
"ax[1].plot(wind_speeds, thrust_coefficients[:n_wind_speeds])\n",
144+
"ax[1].grid()\n",
145+
"ax[1].set_ylabel(\"Thrust coefficient [-]\")\n",
146+
"ax[1].set_xlabel(\"Wind speed [m/s]\")\n",
147+
"ax[1].set_xlim([0, 30])\n",
148+
"ax[0].plot(wind_speeds, powers[n_wind_speeds:], label=\"Second condition\")\n",
149+
"ax[0].legend(loc=\"upper left\")\n",
150+
"ax[1].plot(wind_speeds, thrust_coefficients[n_wind_speeds:])"
151+
]
106152
}
107153
],
108154
"metadata": {
109155
"kernelspec": {
110-
"display_name": "floris",
156+
"display_name": "eni",
111157
"language": "python",
112158
"name": "python3"
113159
},
@@ -121,7 +167,7 @@
121167
"name": "python",
122168
"nbconvert_exporter": "python",
123169
"pygments_lexer": "ipython3",
124-
"version": "3.13.2"
170+
"version": "3.13.7"
125171
}
126172
},
127173
"nbformat": 4,

examples/examples_multidim/002_multi_dimensional_cp_ct_2Hs.py

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
"""Example: Multi-dimensional power/thrust coefficient with 2 Hs values
22
This example follows the previous example but shows the effect of changing the Hs setting.
33
4+
Updated in FLORIS v4.6 to use new array-based multidimensional functionality, where a different
5+
multidimensional condition can be specified for each findex. Prior to v4.6, when only scalar
6+
multidimensional conditions were supported, this example used two separate FLORIS runs to compute
7+
the two Hs cases.
8+
49
NOTE: The multi-dimensional power/thrust coefficient data used in this example is fictional for the
510
purposes of facilitating this example. The power/thrust coefficient values for the different wave
611
conditions are scaled values of the original power/thrust coefficient data for the IEA 15MW turbine.
@@ -15,45 +20,48 @@
1520

1621
# Initialize FLORIS with the given input file.
1722
fmodel = FlorisModel("../inputs/gch_multi_dim_cp_ct.yaml")
18-
19-
# Make a second Floris instance with a different setting for Hs.
20-
# Note the multi-cp-ct file (iea_15MW_multi_dim_Tp_Hs.csv)
21-
# for the turbine model iea_15MW_floating_multi_dim_cp_ct.yaml
22-
# Defines Hs at 1 and 5.
23-
# The value in gch_multi_dim_cp_ct.yaml is 3.01 which will map
24-
# to 5 as the nearer value, so we set the other case to 1
25-
# for contrast.
26-
fmodel_dict_mod = fmodel.core.as_dict()
27-
fmodel_dict_mod["flow_field"]["multidim_conditions"]["Hs"] = 1.0
28-
fmodel_hs_1 = FlorisModel(fmodel_dict_mod)
29-
30-
# Set both cases to 3 turbine layout
3123
fmodel.set(layout_x=[0.0, 500.0, 1000.0], layout_y=[0.0, 0.0, 0.0])
32-
fmodel_hs_1.set(layout_x=[0.0, 500.0, 1000.0], layout_y=[0.0, 0.0, 0.0])
3324

34-
# Use a sweep of wind speeds
35-
wind_speeds = np.arange(5, 20, 1.0)
25+
# Conditions to evaluate
26+
n_wind_speeds = 16
27+
wind_speeds = np.tile(np.linspace(5, 20, n_wind_speeds), 2) # Sweep wind speeds
28+
multidim_conditions = {
29+
"Tp": np.array([2.5]*n_wind_speeds*2),
30+
"Hs": np.array([3.1]*n_wind_speeds + [1.0]*n_wind_speeds),
31+
}
3632
time_series = TimeSeries(
37-
wind_directions=270.0, wind_speeds=wind_speeds, turbulence_intensities=0.06
33+
wind_directions=270.0,
34+
wind_speeds=wind_speeds,
35+
turbulence_intensities=0.06,
36+
multidim_conditions=multidim_conditions
3837
)
38+
39+
# Set wind, multidim conditios onto the FlorisModel
3940
fmodel.set(wind_data=time_series)
40-
fmodel_hs_1.set(wind_data=time_series)
4141

42-
# Calculate wakes with baseline yaw
42+
# Calculate wakes
4343
fmodel.run()
44-
fmodel_hs_1.run()
4544

4645
# Collect the turbine powers in kW
4746
turbine_powers = fmodel.get_turbine_powers() / 1000.0
48-
turbine_powers_hs_1 = fmodel_hs_1.get_turbine_powers() / 1000.0
4947

5048
# Plot the power in each case and the difference in power
5149
fig, axarr = plt.subplots(1, 3, sharex=True, figsize=(12, 4))
5250

5351
for t_idx in range(3):
5452
ax = axarr[t_idx]
55-
ax.plot(wind_speeds, turbine_powers[:, t_idx], color="k", label="Hs=3.1 (5)")
56-
ax.plot(wind_speeds, turbine_powers_hs_1[:, t_idx], color="r", label="Hs=1.0")
53+
ax.plot(
54+
wind_speeds[:n_wind_speeds],
55+
turbine_powers[:n_wind_speeds, t_idx],
56+
color="k",
57+
label="Hs=3.1 (5)"
58+
)
59+
ax.plot(
60+
wind_speeds[n_wind_speeds:],
61+
turbine_powers[n_wind_speeds:, t_idx],
62+
color="r",
63+
label="Hs=1.0"
64+
)
5765
ax.grid(True)
5866
ax.set_xlabel("Wind Speed (m/s)")
5967
ax.set_title(f"Turbine {t_idx}")

examples/examples_multidim/003_multi_dimensional_cp_ct_TI.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
This example follows the previous example, but demonstrating how a multidimensional turbine can be
33
used to model the effect of turbulence intensity on power and thrust coefficient.
44
5+
Updated in FLORIS v4.6 to demonstrate new array-based multidimensional functionality. In the updated
6+
version, setting loop_over_ti to True will run a single scalar multidimensional condition at a time
7+
and call fmodel.run() multiple times, similar to the behavior prior to v4.6. Alternatively, users
8+
may set loop_over_ti to False to use the new (and improved!) functionality and run all TI values in
9+
a single call to fmodel.run().
10+
511
NOTE: The multi-dimensional power/thrust coefficient data used in this example is fictional for the
612
purposes of facilitating this example and the power values shown should not be taken as
713
representative of the actual effect of turbulence intensity on power/thrust coefficient.
@@ -20,25 +26,54 @@
2026
# Set both cases to 3 turbine layout
2127
fmodel.set(layout_x=[0.0, 500.0, 1000.0], layout_y=[0.0, 0.0, 0.0])
2228

29+
loop_over_ti = True # Otherwise, will set all TIs at once.
30+
2331
# Use a sweep of wind speeds
2432
wind_speeds = np.arange(5, 20, 0.1)
25-
time_series = TimeSeries(
26-
wind_directions=270.0, wind_speeds=wind_speeds, turbulence_intensities=0.06
27-
)
28-
fmodel.set(wind_data=time_series)
29-
30-
# Loop over different turbulence intensities using set()
31-
# When running with TI=0.10, the multidimensional data handler will find the nearest defined
32-
# value of 0.08 and use that data.
33-
fig, axarr = plt.subplots(1, 3, sharex=True, figsize=(12, 4))
34-
for ti, col in zip([0.06, 0.10], ["k", "r"]):
35-
fmodel.set(multidim_conditions={"TI": ti})
33+
34+
if loop_over_ti:
35+
# In this case, we will run() the fmodel multiple times, one for each turbulence intensity.
36+
# We set a scalar multidim_conditions, which is broadcast over the wind speeds.
37+
time_series = TimeSeries(
38+
wind_directions=270.0, wind_speeds=wind_speeds, turbulence_intensities=0.06
39+
)
40+
fmodel.set(wind_data=time_series)
41+
42+
# Loop over different turbulence intensities using set()
43+
# When running with TI=0.10, the multidimensional data handler will find the nearest defined
44+
# value of 0.08 and use that data.
45+
fig, axarr = plt.subplots(1, 3, sharex=True, figsize=(12, 4))
46+
for ti, col in zip([0.06, 0.10], ["k", "r"]):
47+
fmodel.set(multidim_conditions={"TI": ti})
48+
fmodel.run()
49+
turbine_powers = fmodel.get_turbine_powers() / 1000.0
50+
51+
for t_idx in range(3):
52+
ax = axarr[t_idx]
53+
ax.plot(wind_speeds, turbine_powers[:, t_idx], color=col, label="TI={0:.2f}".format(ti))
54+
else:
55+
# Set all conditions to evaluate at once, and call fmodel.run() only once.
56+
# We set multidim_conditions to be arrays matching the number of findices of the fmodel.
57+
58+
# Note that turbulence_intensities on the TimeSeries object is _not_ linked to the
59+
# multidim_conditions
60+
time_series = TimeSeries(
61+
wind_directions=270.0,
62+
wind_speeds=np.tile(wind_speeds, 2),
63+
turbulence_intensities=0.06, # This value will be used for wake calculations only
64+
multidim_conditions={"TI": np.array([0.06]*len(wind_speeds) + [0.10]*len(wind_speeds))},
65+
)
66+
fmodel.set(wind_data=time_series)
3667
fmodel.run()
3768
turbine_powers = fmodel.get_turbine_powers() / 1000.0
3869

70+
fig, axarr = plt.subplots(1, 3, sharex=True, figsize=(12, 4))
3971
for t_idx in range(3):
4072
ax = axarr[t_idx]
41-
ax.plot(wind_speeds, turbine_powers[:, t_idx], color=col, label="TI={0:.2f}".format(ti))
73+
ax.plot(wind_speeds, turbine_powers[:len(wind_speeds), t_idx], color="k", label="TI=0.06")
74+
ax.plot(wind_speeds, turbine_powers[len(wind_speeds):, t_idx], color="r", label="TI=0.10")
75+
76+
# Plot aesthetics
4277
for t_idx in range(3):
4378
axarr[t_idx].grid(True)
4479
axarr[t_idx].set_xlabel("Wind Speed (m/s)")

0 commit comments

Comments
 (0)