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

Bug fix: GCH effective yaw angle in FLORIS v2 #734

Merged
merged 5 commits into from Dec 8, 2023
Merged

Conversation

fg320
Copy link
Contributor

@fg320 fg320 commented Nov 9, 2023

Fix incorrect GCH effective yaw angle in FLORIS v2

This pull request addresses the issue in #684, where a bug in FLORIS v2 is pointed out regarding the GCH calculations for the effective yaw angle. The bug is in the calculate_effective_yaw_angle method in the file floris/simulation/wake_deflection/gauss.py.

After defining the minimum and maximum limits for the effective yaw angles as
L300 min_yaw = -45.0
L301 max_yaw = 45.0,
the actual implementation introduces a second negative sign for the negative yaw angle
L347 if idx_1 == 90 or idx_2 == 90:
L348 yaw_effective = max_yaw
L350 elif idx_1 == 0 or idx_2 == 0:
L351 yaw_effective = -min_yaw.

This causes the application of a +45 deg effective yaw angle instead of a -45 deg one (refer to the example code and plots below). L351 should be replaced by yaw_effective = +min_yaw.

Related issue

Issue #684

Impacted areas of the software

`floris/simulation/wake_deflection/gauss.py`

Example test

Please refer to the simple example code below, where `gauss_legacy_ste.json` is based on the available examples and included in the last section of this report.
# -*- coding: utf-8 --

import os
import numpy as np
import matplotlib.pyplot as plt
import floris
import floris.tools as wfct
from floris.tools.floris_interface import FlorisInterface as FI

# FLORIS BUG yaw effective V2.4 #

# Change working directory to current file directory
cwd = os.path.dirname(os.path.realpath(__file__))
os.chdir(cwd)

# Initialise floris
fi = FI("inputs/gauss_legacy_ste.json")

# Change layout
rows = 5
cols = 1
spacing_x = 7
spacing_y = 5
D = fi.floris.farm.turbines[0].rotor_diameter
x = np.array([[x*spacing_x*D]*cols for x in range(rows)])
x_coordinates = x.flatten()
y_coordinates = np.array([y*spacing_y*D for y in range(cols)]*rows)
fi.reinitialize_flow_field(layout_array=(x_coordinates, y_coordinates))

# Set atmospheric conditions
fi.reinitialize_flow_field(wind_direction=[270.0],
                           wind_speed=[8.0],
                           turbulence_intensity=0.05,
                           wind_shear=0.0,
                           wind_veer=0.0)

#%% Power prediction
yaw_angles = [-25,-25,0,0,0]
fi.calculate_wake(yaw_angles=yaw_angles)
turbine_powers = np.array(fi.get_turbine_power())/1e6
farm_power = fi.get_farm_power()/1e6

#%% Flow field plot
hor_plane = fi.get_hor_plane()
fig, ax = plt.subplots(figsize=(6,6))
plane = wfct.visualization.visualize_cut_plane(hor_plane, ax=ax)
ax.set_title("Bug: farm power = %.4f MW"%(farm_power), fontsize=11)
# ax.set_title("Fixed: farm power = %.10f MW"%(farm_power), fontsize=11)
ax.set_xlabel("x [m]", fontsize=11)
ax.set_ylabel("y [m]", fontsize=11)

Example test output

The above case describes a simple 5x1 layout of NREL-5MW turbines in aligned conditions (270deg wind direction), 5% turbulence intensity, 8m/s uniform inflow. No wind shear and veer. The yaw angles (from the most upstream turbine) are, in degrees, [-25.0, -25.0, 0.0, 0.0, 0.0]. The resulting flow field with the bug is shown below. Clearly, the third turbine should experience a negative deflection (although its yaw angle is 0deg) due to secondary steering effects. Instead, a sudden shift is noticed with a positive deflection applied to the wake.

5x1_bug

After fixing the bug, the flow field is physical, where a negative deflection is applied to the downstream wakes of non-yawed turbines.

5x1_fixed

In terms of turbine and farm power, the difference is minimal. However, the bug introduces an incorrect description of the flow field and discontinuities in the GCH farm power function, significantly affecting the performance of gradient-based optimisers such as SLSQP.

Input file gauss_legacy_ste.json

{
  "type": "floris_input",
  "name": "floris_input_file_gauss_legacy_ste",
  "description": "Input file gauss_legacy_ste",
  "floris_version": "v2.4",
  "logging": {
    "console": {
      "enable": true,
      "level": "INFO"
    },
    "file": {
      "enable": false,
      "level": "INFO"
    }
  },
  "farm": {
    "type": "farm",
    "name": "farm_4x4",
    "description": "4x4 Wind Farm",
    "__comment__": "specified_wind_height of -1 uses the first WT's hub height; After initialization, specified_wind_height is a free parameter.",
    "properties": {
      "wind_speed": [
        8.0
      ],
      "wind_direction": [
        270.0
      ],
      "turbulence_intensity": [
        0.06
      ],
      "wind_shear": 0.12,
      "specified_wind_height": -1,
      "wind_veer": 0.0,
      "air_density": 1.225,
      "layout_x": [
        0.0
      ],
      "layout_y": [
        0.0
      ],
      "wind_x": [
        0
      ],
      "wind_y": [
        0
      ]
    }
  },
  "turbine": {
    "type": "turbine",
    "name": "nrel_5mw",
    "description": "NREL 5MW",
    "properties": {
      "use_points_on_perimeter": false,
      "rotor_diameter": 126.0,
      "hub_height": 90.0,
      "blade_count": 3,
      "pP": 1.88,
      "pT": 1.88,
      "generator_efficiency": 1.0,
      "ngrid": 5,
      "rloc": 0.5,
      "blade_pitch": 0.0,
      "yaw_angle": 0.0,
      "tilt_angle": 0.0,
      "TSR": 8.0,
      "power_thrust_table": {
        "power": [
          0.0,
          0.0,
          0.1780851,
          0.28907459,
          0.34902166,
          0.3847278,
          0.40605878,
          0.4202279,
          0.42882274,
          0.43387274,
          0.43622267,
          0.43684468,
          0.43657497,
          0.43651053,
          0.4365612,
          0.43651728,
          0.43590309,
          0.43467276,
          0.43322955,
          0.43003137,
          0.37655587,
          0.33328466,
          0.29700574,
          0.26420779,
          0.23839379,
          0.21459275,
          0.19382354,
          0.1756635,
          0.15970926,
          0.14561785,
          0.13287856,
          0.12130194,
          0.11219941,
          0.10311631,
          0.09545392,
          0.08813781,
          0.08186763,
          0.07585005,
          0.07071926,
          0.06557558,
          0.06148104,
          0.05755207,
          0.05413366,
          0.05097969,
          0.04806545,
          0.04536883,
          0.04287006,
          0.04055141
        ],
        "thrust": [
          1.19187945,
          1.17284634,
          1.09860817,
          1.02889592,
          0.97373036,
          0.92826162,
          0.89210543,
          0.86100905,
          0.835423,
          0.81237673,
          0.79225789,
          0.77584769,
          0.7629228,
          0.76156073,
          0.76261984,
          0.76169723,
          0.75232027,
          0.74026851,
          0.72987175,
          0.70701647,
          0.54054532,
          0.45509459,
          0.39343381,
          0.34250785,
          0.30487242,
          0.27164979,
          0.24361964,
          0.21973831,
          0.19918151,
          0.18131868,
          0.16537679,
          0.15103727,
          0.13998636,
          0.1289037,
          0.11970413,
          0.11087113,
          0.10339901,
          0.09617888,
          0.09009926,
          0.08395078,
          0.0791188,
          0.07448356,
          0.07050731,
          0.06684119,
          0.06345518,
          0.06032267,
          0.05741999,
          0.05472609
        ],
        "wind_speed": [
          2.0,
          2.5,
          3.0,
          3.5,
          4.0,
          4.5,
          5.0,
          5.5,
          6.0,
          6.5,
          7.0,
          7.5,
          8.0,
          8.5,
          9.0,
          9.5,
          10.0,
          10.5,
          11.0,
          11.5,
          12.0,
          12.5,
          13.0,
          13.5,
          14.0,
          14.5,
          15.0,
          15.5,
          16.0,
          16.5,
          17.0,
          17.5,
          18.0,
          18.5,
          19.0,
          19.5,
          20.0,
          20.5,
          21.0,
          21.5,
          22.0,
          22.5,
          23.0,
          23.5,
          24.0,
          24.5,
          25.0,
          25.5
        ]
      }
    }
  },
  "wake": {
    "type": "wake",
    "name": "wake_gauss_legacy_ste",
    "description": "wake gauss_legacy_ste, gauss deflection, crespo_hernandez, sosfs",
    "properties": {
      "velocity_model": "gauss_legacy",
      "deflection_model": "gauss",
      "turbulence_model": "crespo_hernandez",
      "combination_model": "sosfs",
      "parameters": {
        "wake_velocity_parameters": {
          "gauss_legacy": {
			"ka": 0.38,
			"kb": 0.004,
			"alpha": 0.58,
			"beta": 0.077,
			"calculate_VW_velocities": true,
			"use_yaw_added_recovery": true,
			"eps_gain": 0.2
          }
        },
        "wake_deflection_parameters": {
          "gauss": {
			"ka": 0.38,
			"kb": 0.004,
			"alpha": 0.58,
			"beta": 0.077,
			"ad": 0.0,
			"bd": 0.0,
			"dm": 1.0,
			"use_secondary_steering": true,
			"eps_gain": 0.2
          }
        },
        "wake_turbulence_parameters": {
          "crespo_hernandez": {
            "ai": 0.8,
            "constant": 0.5,
            "downstream": -0.32,
            "initial": 0.1
          }
        }
      }
    }
  }
}

@rafmudaf
Copy link
Collaborator

rafmudaf commented Dec 8, 2023

Hey @RHammond2 I'm planning to merge this pull request into the v2 branch, create a new tag v2.4.1, and do the release to PyPI. Just as sanity check, I want to make sure this release will not be the "latest" release on PyPI. Is it true that PyPI checks for the largest version number rather than the latest date? On the GitHub Releases, there's a box to tick to set a release as the "latest", but I don't think there's any active control of the same for PyPI.

@rafmudaf rafmudaf self-assigned this Dec 8, 2023
@rafmudaf rafmudaf added the v2.x Related to any version of FLORIS less than v3 label Dec 8, 2023
@rafmudaf rafmudaf self-requested a review December 8, 2023 15:19
@rafmudaf rafmudaf added the bug Something isn't working label Dec 8, 2023
@RHammond2
Copy link
Collaborator

@rafmudaf I really don't know on this, and can't find anything relating to it. I tried poking around the package management settings in another project I work on, and I can't find anything on the management of the latest release. If you find anything, I'd love to know more, but for some reason this is one of the rare things that seemingly no one has written a blog post about.

@rafmudaf rafmudaf merged commit 677ae84 into NREL:v2 Dec 8, 2023
8 checks passed
@rafmudaf
Copy link
Collaborator

rafmudaf commented Dec 8, 2023

Well we'll see. Here it goes!

rafmudaf added a commit that referenced this pull request Dec 8, 2023
@rafmudaf
Copy link
Collaborator

rafmudaf commented Dec 8, 2023

@fg320 After making a small mess of things, this is merged in the v2 branch and released on PyPI as v2.5.1:

pip install floris==2.5.1

@rafmudaf
Copy link
Collaborator

rafmudaf commented Dec 8, 2023

@rafmudaf I really don't know on this, and can't find anything relating to it. I tried poking around the package management settings in another project I work on, and I can't find anything on the management of the latest release. If you find anything, I'd love to know more, but for some reason this is one of the rare things that seemingly no one has written a blog post about.

@rafmudaf I tested installing floris with no version from PyPI and it does choose the highest version not the most recent release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working v2.x Related to any version of FLORIS less than v3
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants