Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b9a1b22
Expand PyWake submodel support with case-insensitive lookup, optional…
bjarketol Mar 13, 2026
f8b7533
WIP
bjarketol Mar 13, 2026
56cff6e
WIP
bjarketol Mar 13, 2026
9865a58
WIP
bjarketol Mar 13, 2026
f053e52
WIP
bjarketol Mar 13, 2026
49b0cd8
Eliminate redundant YAML+netCDF loads in run_api
bjarketol Mar 13, 2026
1a912a0
Use DensityCompensation instead of DensityScale for PyWake air densit…
bjarketol Mar 17, 2026
de6923c
Fix Speedup axis ordering and add proper Weibull flow case sampling
bjarketol Mar 25, 2026
64a87c6
Support PyWake >= 2.6: SimpleYawModel no longer accepts exp parameter
bjarketol Mar 26, 2026
5bb32bd
Fix pre-commit
bjarketol Mar 27, 2026
91be360
Expand PyWake submodel support with case-insensitive lookup, optional…
bjarketol Mar 13, 2026
1c4b2c7
Fix pre-commit
bjarketol Mar 27, 2026
d98ac2c
Align pyWake reader with windIO: free_stream_ti, overlap rotor-avg, g…
bjarketol Jun 4, 2026
af99599
Merge remote-tracking branch 'origin/expand-pywake-submodel-support' …
bjarketol Jun 4, 2026
e3b96a9
Honor free_stream_ti for the NOJLocalDeficit (Jensen) path
bjarketol Jun 4, 2026
b09c006
Guard WeightedSum/CumulativeWakeSum against non-convection deficits
bjarketol Jun 4, 2026
e7c2162
Accept k_b for the NOJDeficit (Jensen_1983) scalar k
bjarketol Jun 4, 2026
eff6f0e
Support mixed turbine types and hub heights in the pyWake API
bjarketol Jun 8, 2026
adbe516
Pin windIO to the flow_model_chain integration branch
bjarketol Jun 8, 2026
e4d781c
Reduce redundant site-data copies in the pyWake reader
bjarketol Jun 8, 2026
c03969f
Load included wind_resource.nc as numpy arrays (opt into windIO nc_data)
bjarketol Jun 8, 2026
1fd2783
Add memory regression guard for the array-backed netCDF loader
bjarketol Jun 8, 2026
a30e513
Honor axial_induction_model and add the TurbOPark literature recipe
bjarketol Jun 8, 2026
3b79466
Apply pinned black/isort to pre-existing test files
bjarketol Jun 8, 2026
b78411e
Merge pull request #3 from bjarketol/pywake-faithful-phase1
bjarketol Jun 8, 2026
bab2105
Phase 2 faithfulness: CrespoHernandez c-coeffs, None rotor for Weight…
bjarketol Jun 8, 2026
b26ea07
Merge pull request #4 from bjarketol/pywake-faithful-phase2
bjarketol Jun 8, 2026
bce0d28
Phase 3 faithfulness: honor use_effective_ws, enable GCL effective TI
bjarketol Jun 8, 2026
044a187
Merge pull request #5 from bjarketol/pywake-faithful-phase3
bjarketol Jun 8, 2026
90353f1
Fix WeightedSum collapse on the distributions path (drop ws=0 bin)
bjarketol Jun 9, 2026
0c59394
Merge pull request #6 from bjarketol/pywake-dist-weightedsum-ws0
bjarketol Jun 9, 2026
df41fe9
Generate Fuga LUTs on the fly with pyfuga (#7)
bjarketol Jun 9, 2026
7988703
Fuga: z0-sweep multi-LUT for per-flow-case TI; multi-turbine geometry…
bjarketol Jun 9, 2026
7d31ba8
Fuga: handle an explicit z0 list in _fuga_atmosphere (#9)
bjarketol Jun 9, 2026
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
2 changes: 1 addition & 1 deletion examples/cases/KUL_LES/wind_energy_system/analysis_US.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ HPC_config:
mesh_node_number: 2
mesh_ntasks_per_node: 48
mesh_wall_time_hours: 1
run_partition: ""
mesh_partition: ""
#
wckey: ""

Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ classifiers = [
]
requires-python = ">=3.10,<3.13"
dependencies = [
"windIO @ git+https://github.com/EUFlow/windIO.git",
"windIO @ git+https://github.com/bjarketol/windIO.git@flow-model-chain",
"xarray",
"scipy",
"pyyaml",
Expand Down Expand Up @@ -70,6 +70,7 @@ dev = [
"ncplot",
"nctoolkit",
"cartopy",
"pre-commit",
]
docs = [
"sphinx>=7.0",
Expand Down
173 changes: 173 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,179 @@ def cleanup_test_outputs():
}


def make_mixed_type_timeseries_system_dict(flow_model_name):
"""Build a system dict with TWO turbine types at TWO hub heights.

3x3 layout (9 turbines), per-turbine timeseries inflow with dims
["time", "wind_turbine"] and NO vertical ``height`` profile (the wind
speeds are already at each turbine's own hub height, as a microscale
terrain-speedup model would produce). This exercises the mixed
hub-height + per-turbine path in the pyWake API.

Type 0 is the DTU 10MW at 119 m; type 1 is a smaller turbine (half the
power, 120 m rotor, 90 m hub) so both the type assignment and the two
hub heights are genuinely distinct. Wind speeds are kept in 8-11 m/s,
away from cut-in/out, so the comparison is unaffected by curve edges.
"""
n_times = 5
n_turbines = 9

# 3x3 grid, ~5D spacing of the larger rotor (5 * 178.3)
spacing = 5 * _TURBINE["rotor_diameter"]
axis = [0.0, spacing, 2 * spacing]
grid_x = [axis[i] for _ in range(3) for i in range(3)]
grid_y = [axis[j] for j in range(3) for _ in range(3)]

# Alternating type assignment -> [0, 1, 0, 1, 0, 1, 0, 1, 0]
turbine_type_idx = [k % 2 for k in range(n_turbines)]

perf = _TURBINE["performance"]
pc_ws = perf["power_curve"]["power_wind_speeds"]
pc_p = perf["power_curve"]["power_values"]
ct_ws = perf["Ct_curve"]["Ct_wind_speeds"]
ct_v = perf["Ct_curve"]["Ct_values"]
type1_power = [0.5 * p for p in pc_p]

# Deterministic per-turbine timeseries (no RNG), wind from ~270 deg so the
# rows along x interact.
base_ws = [8.0, 9.0, 10.0, 8.5, 11.0]
base_wd = [270.0, 268.0, 272.0, 270.5, 269.0]
ws_data, wd_data, ti_data = [], [], []
for t in range(n_times):
ws_data.append([base_ws[t] + 0.1 * i for i in range(n_turbines)])
wd_data.append([base_wd[t] + 0.2 * (i - 4) for i in range(n_turbines)])
ti_data.append([0.06 + 0.002 * i for i in range(n_turbines)])

return {
"name": "Dict test: mixed turbine types and hub heights",
"site": {
"name": "Test site",
"boundaries": {
"polygons": [
{
"x": [-spacing, 3 * spacing, 3 * spacing, -spacing],
"y": [3 * spacing, 3 * spacing, -spacing, -spacing],
}
]
},
"energy_resource": {
"name": "Test resource",
"wind_resource": {
"time": list(range(n_times)),
"wind_turbine": list(range(n_turbines)),
"wind_speed": {"data": ws_data, "dims": ["time", "wind_turbine"]},
"wind_direction": {
"data": wd_data,
"dims": ["time", "wind_turbine"],
},
"turbulence_intensity": {
"data": ti_data,
"dims": ["time", "wind_turbine"],
},
},
},
},
"wind_farm": {
"name": "Test farm",
"layouts": [
{
"coordinates": {"x": grid_x, "y": grid_y},
"turbine_types": turbine_type_idx,
}
],
"turbine_types": {
0: {
"name": "type_0_DTU10MW",
"hub_height": 119.0,
"rotor_diameter": 178.3,
"performance": {
"power_curve": {
"power_wind_speeds": pc_ws,
"power_values": pc_p,
},
"Ct_curve": {"Ct_wind_speeds": ct_ws, "Ct_values": ct_v},
},
},
1: {
"name": "type_1_small",
"hub_height": 90.0,
"rotor_diameter": 120.0,
"performance": {
"power_curve": {
"power_wind_speeds": pc_ws,
"power_values": type1_power,
},
"Ct_curve": {"Ct_wind_speeds": ct_ws, "Ct_values": ct_v},
},
},
},
},
"attributes": {
"flow_model": {"name": flow_model_name},
"analysis": {
"wind_deficit_model": {
"name": "Bastankhah2014",
"wake_expansion_coefficient": {
"k_a": 0.0,
"k_b": 0.04,
"free_stream_ti": False,
},
"ceps": 0.2,
"use_effective_ws": True,
},
"axial_induction_model": "Madsen",
"deflection_model": {"name": "None"},
"turbulence_model": {"name": "CrespoHernandez"},
"superposition_model": {
"ws_superposition": "Linear",
"ti_superposition": "Squared",
},
"rotor_averaging": {"name": "Center"},
"blockage_model": {"name": "None"},
},
"model_outputs_specification": {
"turbine_outputs": {
"turbine_nc_filename": "turbine_data.nc",
"output_variables": ["power"],
},
},
},
}


def make_mixed_type_profile_system_dict(flow_model_name):
"""Mixed types/hub heights with a (time, height) vertical wind profile.

Same farm as :func:`make_mixed_type_timeseries_system_dict` (two types at
119 m and 90 m), but the inflow is given as a vertical profile over a
``height`` dimension that brackets both hub heights, so WIFA must
interpolate the profile to each turbine's hub height. The wind direction
veers around 360 deg to exercise circular interpolation.
"""
system = make_mixed_type_timeseries_system_dict(flow_model_name)

n_times = 5
heights = [60.0, 90.0, 120.0, 150.0]
href, alpha = 119.0, 0.14 # power-law shear reference + exponent
ws_base = [8.0, 9.0, 10.0, 8.5, 11.0]
wd_base = [358.0, 359.0, 357.0, 0.5, 359.5] # straddles 360 deg

ws_data, wd_data, ti_data = [], [], []
for t in range(n_times):
ws_data.append([ws_base[t] * (h / href) ** alpha for h in heights])
wd_data.append([(wd_base[t] + 0.05 * (h - href)) % 360 for h in heights])
ti_data.append([max(0.08 - 0.0001 * (h - 60.0), 0.02) for h in heights])

system["site"]["energy_resource"]["wind_resource"] = {
"time": list(range(n_times)),
"height": heights,
"wind_speed": {"data": ws_data, "dims": ["time", "height"]},
"wind_direction": {"data": wd_data, "dims": ["time", "height"]},
"turbulence_intensity": {"data": ti_data, "dims": ["time", "height"]},
}
return system


def make_timeseries_per_turbine_system_dict(flow_model_name):
"""Build a complete system dict with per-turbine timeseries data including density.

Expand Down
Loading
Loading