Giter Site home page Giter Site logo

floris's Introduction

FLORIS Wake Modeling and Wind Farm Controls Software

FLORIS is a controls-focused wind farm simulation software incorporating steady-state engineering wake models into a performance-focused Python framework. It has been in active development at NREL since 2013 and the latest release is FLORIS v4.1.1. Online documentation is available at https://nrel.github.io/floris.

The software is in active development and engagement with the development team is highly encouraged. If you are interested in using FLORIS to conduct studies of a wind farm or extending FLORIS to include your own wake model, please join the conversation in GitHub Discussions!

Installation

If upgrading from a previous version, it is recommended to install FLORIS v4 into a new virtual environment. If you intend to use pyOptSparse with FLORIS, it is recommended to install that package first before installing FLORIS.

FLORIS can be installed by downloading the source code or via the PyPI package manager with pip.

The simplest method is with pip by using this command:

pip install floris

Developers and anyone who intends to inspect the source code can install FLORIS by downloading the git repository from GitHub with git and use pip to locally install it. It is highly recommended to use a Python virtual environment manager such as conda in order to maintain a clean and sandboxed environment. The following commands in a terminal or shell will download and install FLORIS.

    # Download the source code from the `main` branch
    git clone -b main https://github.com/NREL/floris.git

    # If using conda, be sure to activate your environment prior to installing
    # conda activate <env name>

    # If using pyOptSpare, install it first
    conda install -c conda-forge pyoptsparse

    # Install FLORIS
    pip install -e floris

With both methods, the installation can be verified by opening a Python interpreter and importing FLORIS:

>>> import floris
>>> help(floris)

Help on package floris:

NAME
    floris - # Copyright 2024 NREL

PACKAGE CONTENTS
    convert_floris_input_v3_to_v4
    convert_turbine_v3_to_v4
    core (package)
    cut_plane
    floris_model
    flow_visualization
    layout_visualization
    logging_manager
    optimization (package)
    parallel_floris_model
    turbine_library (package)
    type_dec
    uncertain_floris_model
    utilities
    version
    wind_data

VERSION
    4.1.1

FILE
    ~/floris/floris/__init__.py

It is important to regularly check for new updates and releases as new features, improvements, and bug fixes will be issued on an ongoing basis.

Quick Start

FLORIS is a Python package run on the command line typically by providing an input file with an initial configuration. It can be installed with pip install floris (see installation). The typical entry point is FlorisModel which accepts the path to the input file as an argument. From there, changes can be made to the initial configuration through the FlorisModel.set routine, and the simulation is executed with FlorisModel.run.

from floris import FlorisModel
fmodel = FlorisModel("path/to/input.yaml")
fmodel.set(
    wind_directions=[i for i in range(10)],
    wind_speeds=[8.0]*10,
    turbulence_intensities=[0.06]*10
)
fmodel.run()

Finally, results can be analyzed via post-processing functions available within FlorisModel such as

and in two visualization packages: layoutviz and flowviz. A collection of examples describing the creation of simulations as well as analysis and post processing are included in the repository. Examples are also listed in the online documentation.

Engaging on GitHub

FLORIS leverages the following GitHub features to coordinate support and development efforts:

  • Discussions: Collaborate to develop ideas for new use cases, features, and software designs, and get support for usage questions
  • Issues: Report potential bugs and well-developed feature requests
  • Projects: Include current and future work on a timeline and assign a person to "own" it

Generally, the first entry point for the community will be within one of the categories in Discussions. Ideas is a great spot to develop the details for a feature request. Q&A is where to get usage support. Show and tell is a free-form space to show off the things you are doing with FLORIS.

License

BSD 3-Clause License

Copyright (c) 2024, Alliance for Sustainable Energy LLC, All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  • Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

floris's People

Contributors

afarrell2 avatar bartdoekemeijer avatar bayc avatar dependabot[bot] avatar ejsimley avatar ewquon avatar fg320 avatar gogannes avatar jaredthomas68 avatar jfrederik-nrel avatar jrannoni avatar kflemin avatar kilojoules avatar knutss avatar misi9170 avatar nhamilto avatar pablo-benito avatar paulf81 avatar pduff-code avatar petebachant avatar pjireland avatar pjstanle avatar rafmudaf avatar rctredgold avatar rhammond2 avatar scottryn avatar sondreso avatar tonyinme avatar vallbog avatar zerweck avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

floris's Issues

Bug report: Yaw optimization with uncertainty raises a type error

Bug description
Yaw optimization raises a TypeErrror ("loop of ufunc does not support argument 0 of type float which has no callable sin method") when uncertainty is introduced in the simulation.

To Reproduce
The best way to see this bug is running the example file "optimize_yaw_wind_rose_robust.py", which can be found in: "floris/examples/optimization/scipy/controls_optimization/". In this example, yaw optimization is conducted with and without uncertainty. The model works perfectly for the case without uncertainty, but raises the error described below when introducing uncertainty in the model.

Steps to reproduce the behavior:

  1. Run "optimize_yaw_wind_rose_robust.py"
  2. See the error:

AttributeError: 'float' object has no attribute 'sin'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):

File "", line 1, in
df_opt_unc = yaw_opt.optimize()

File "C:\Users.conda\envs\wisdem-env\lib\site-packages\floris\tools\optimization\scipy\yaw_wind_rose.py", line 736, in optimize
unc_options=self.unc_options,

File "C:\Users.conda\envs\wisdem-env\lib\site-packages\floris\tools\floris_interface.py", line 1048, in get_turbine_power
self.reinitialize_flow_field(wind_direction=wd_orig + delta_wd)

File "C:\Users.conda\envs\wisdem-env\lib\site-packages\floris\tools\floris_interface.py", line 256, in reinitialize_flow_field
wind_map=self.floris.farm.wind_map,

File "C:\Users.conda\envs\wisdem-env\lib\site-packages\floris\simulation\flow_field.py", line 524, in reinitialize_flow_field
self.set_bounds(bounds_to_set=bounds_to_set)

File "C:\Users.conda\envs\wisdem-env\lib\site-packages\floris\simulation\flow_field.py", line 418, in set_bounds
/ np.pi

File "C:\Users.conda\envs\wisdem-env\lib\site-packages\scipy\stats\morestats.py", line 3338, in circmean
nan_policy=nan_policy)

File "C:\Users.conda\envs\wisdem-env\lib\site-packages\scipy\stats\morestats.py", line 3285, in _circfuncs_common
sin_samp = sin((samples - low)*2.*pi / (high - low))

TypeError: loop of ufunc does not support argument 0 of type float which has no callable sin method

Expected behavior
The example should resolve the optimization without errors.

Floris Version
Floris: 2.3.

System Information (please complete the following information):

  • OS: Windows 10 64 bits
  • Library versions
    • numpy 1.21.0
    • scipy 1.6.3.
    • pandas 1.2.5.

Feature request: Turbine identifiers

Is your feature request related to a problem? Please describe.
Often, a FLORIS model is an abstraction of a real wind farm. Within those wind farms, turbines have typical identifiers such as A1-A9, B1-B5 and C14-C22, rather than integers from 0 to num_turbs-1. I often find myself generating a mapping.csv file that shows what turbine number in FLORIS correspond to what turbine in the actual farm. I believe that being able to keep track of the turbine names and generate visualizations with the right turbine names would be a valuable addition to floris.

Describe the solution you'd like
An (optional) additional input in the floris input .jsons to specify the turbine names. These turbine identifiers are never used in the actual core code, but rather only used in visualizations and as a useful way for the user to keep track of the turbines.

Describe alternatives you've considered
I now have a practice where I create a wrapper class for floris wherein I save a variable called "turbine_names" that can be returned to the user upon request.

Additional context
This is a minor suggestion, and by no means urgent. I'd like to hear whether others think this is useful.

Different model parameters in config file and paper

Hi there,
I'm currently trying to experiment with model presented in (Gebraad et al., 2016), where Jensen wake model and Jiménez wake deflection model were combined. But I find that parameters shown in the table are different from those in example_input.json? Why? Which one is reliable?

"jimenez": { "kd": 0.05, "ad": 0.0, "bd": 0.0 },

image

Millions of thanks in advance.

Gebraad, P.M.O., Teeuwisse, F.W., Wingerden, J.W. van, Fleming, P.A., Ruben, S.D., Marden, J.R., Pao, L.Y., 2016. Wind plant power optimization through yaw control using a parametric model for wake effects—a CFD simulation study. Wind Energy 19, 95–114. https://doi.org/10.1002/we.1822

Feature request: Allowing multiple turbine types

Is your feature request related to a problem? Please describe.
It is not particularly related to a problem, but rather a nuisance. Whenever I want to set up a wind farm with more than one turbine type, I feel like I am "hacking" the FLORIS object. Currently, the process goes something along these lines:

  • I create one FLORIS .json file with the locations and turbine properties for every wind turbine type
  • I load all .json files and combine the floris objects by updating the layout and then the turbine properties:
layout_x = []
layout_y = []
turbine_cpct = []
turbine_hh = []
turbine_D = []

for fi in fi_list:
    # Wind farm layout
    layout_x.extend(fi.layout_x)
    layout_y.extend(fi.layout_y)

    # Turbine properties
    num_turbs = len(fi.layout_x)
    t0 = fi.floris.farm.turbines[0]
    turbine_cpct.extend([t0.power_thrust_table] * num_turbs)
    turbine_hh.extend([t0.hub_height] * num_turbs)
    turbine_D.extend([t0.rotor_diameter] * num_turbs)

fi = deepcopy(fi_list[0])

# Copy default turbines to new layout locations
fi.reinitialize_flow_field(layout_array=[layout_x, layout_y])

# Update turbine properties
for ti in range(len(fi.layout_x)):
    fi.change_turbine(
        [ti],
        {
            "power_thrust_table": turbine_cpct[ti],
            "rotor_diameter": turbine_D[ti],
            "hub_height": turbine_hh[ti]
        }
    )

This also makes it more difficult to track the specified_wind_height. Ideally, FLORIS could simply tell the user to specify an explicit specified_wind_height whenever more than one turbine type is used.

Describe the solution you'd like
Ideally, the .json would simply allow a variable number of turbine types. Then, one would then need another input in the json in either each turbine field or farm specifying which turbines belong to which turbine type.

Describe alternatives you've considered
I now have a practice where I create a class for these types of wind farms, wherein I contain all these operations. That way, I can still simply call a wind farm using a single command and get a floris object in return.

add __version__attributes

Is your feature request related to a problem? Please describe.
with new releases recently, need to check current version installed

Describe the solution you'd like
add the version attribute that can output current version installed through "floris.version"

Describe alternatives you've considered
it seems to be a simple feature, don't think need alternatives

Additional context

certain fixed power

Hello, I am recently going to do research on power-limited operation of offshore wind farms and would like to know if floris can maintain the wind turbine at a certain fixed power by reducing the speed.

Bug on FLORIS init with v2.3.0

Bug description
The basic installation does not install VERSION causing init to crash:

To Reproduce
Steps to reproduce the behavior:

  1. In command prompt (Windows 10) "pip install floris"
  2. In IDLE shell import floris
  3. See the error:
    with open(ROOT.parent / "VERSION") as version_file:
    FileNotFoundError: [Errno 2] No such file or directory

Expected behavior
The Floris package should be imported

Floris Version
2.3.0

System Information (please complete the following information):

  • OS: Windows 10

Additional context

Error in parallel: example_optimize_yaw_wind_rose_parallel.py

Bug description
Hi, I tried to run example_optimize_yaw_wind_rose_parallel.py, and connection error is occurred.
After changing the wd & ws source from local file, the program can run, with following errors:

To Reproduce
In example_optimize_yaw_wind_rose_parallel.py, change download from API to False
Run example_optimize_yaw_wind_rose_parallel.py

Expected behavior
Traceback (most recent call last):
File "//floris/examples/optimization/scipy/example_optimize_yaw_wind_rose_parallel.py", line 127, in
df_base = yaw_opt.calc_baseline_power()
File "/floris/floris/tools/optimization/scipy/optimization.py", line 1196, in calc_baseline_power
for df_base_one in executor.map(self._calc_baseline_power_one_case,self.ws.values,self.wd.values):
File "/mpi4py/futures/pool.py", line 207, in result_iterator
yield futures.pop().result()
File "/python3.8/concurrent/futures/_base.py", line 439, in result
return self.__get_result()
File "/env/lib/python3.8/concurrent/futures/_base.py", line 388, in __get_result
raise self._exception
TypeError: 'numpy.float64' object is not iterable

Floris Version
latest dev

System Information (please complete the following information):

  • OS: Ubuntu 18.06

Horizontal cut plane fails with user-specified resolution and bounds

Bug description
Resulting visualization from calling wfct.visualization.visualize_cut_plane()

To Reproduce

fi =  wfct.floris_interface.FlorisInterface('asdf.json')
fi.calculate_wake()
Nx,Ny = 301,201
hor_plane = fi.get_hor_plane(x_resolution=301, y_resolution=201, 
                             x_bounds=(-5,10), y_bounds=(-5,5))
fig, ax = plt.subplots()
wfct.visualization.visualize_cut_plane(hor_plane, ax=ax)

debug

Expected behavior
Manually sorting and plotting the data:

df = hor_plane.df.set_index(['x1','x2','x3']).xs(1.0,level='x3').sort_index()
x1 = df.index.get_level_values(0).values.reshape((Nx,Ny))
x2 = df.index.get_level_values(1).values.reshape((Nx,Ny))
u = df['u'].values.reshape((Nx,Ny))

fig,ax = plt.subplots()
cm = ax.pcolormesh(x1,x2,u, cmap='magma')
ax.axis('scaled')
fig.colorbar(cm, ax=ax)
fig.savefig('u_{:s}.png'.format(case),bbox_inches='tight')

u_CT095

Floris Version
commit 2833cc7

json_example - calculate_wake()

I've an issue when implementing the examples with your code by calling calculate_wake() function.

TypeError: u_prime() takes 1 positional argument but 2 were given

Out of date documentation - readthedocs

Bug description
On the current develop branch (pre v2 release), the readthedocs documentation is outdated and out of sync from the repository.

The following sections should be updated:

  • Background and Objectives: include the GCH features and changes to the "gauss" models
  • Theory: include the GCH features and changes to the "gauss" models
  • Code Reference: this no longer correctly maps to the docstrings for the wake models since the directory structure has changed
  • Inputs: add new API requirements
  • Examples: is this still relevant? If so, this should be updated to match the reworked examples
  • Testing: update status of regression tests and details of unit tests (SampleInputs)
  • Change Log: Include v2.0.0 changes

Floris Version
develop branch as of April 17, 2020.

Additional context
Here's the link to the public documentation: https://floris.readthedocs.io. The default branch is develop.

Suggestion to use air density-corrected wind speed to determine power and Ct

Bug description
Currently changing the air density parameter scales the entire power curve up or down. A more realistic approach would be to use an air density-corrected wind speed to determine power. This would shift the wind speed at which rated power is reached, but the turbine would still achieve the same rated power.

To Reproduce
This code produces the following plot, illustrating how the entire power curve is scaled

import floris as fl
import numpy as np
from matplotlib import pyplot as plt

fi = fl.tools.floris_interface.FlorisInterface("example_input.json")
ws = np.arange(0.,21.,1.)
pc = fi.get_power_curve(ws)
fi.reinitialize_flow_field(air_density=1.)
pc1 = fi.get_power_curve(ws)

plt.figure()
plt.plot(ws,pc,label='Air Density = 1.225')
plt.plot(ws,pc,label='Air Density = 1.0')

image

Expected behavior
The change in the power curve expected from the reduction in air density is as follows. Rated power is still achieved, but at a higher wind speed.

image

A proposed solution (in turbine.py) is to scale the wind speed to create an air-density corrected wind speed used to determine power. A similar approach could be used to determine Ct. This implies that the Cp and Ct curves that are defined should be the standard sea-level values.

# Compute the air density-corrected yaw effective velocity
pW = self.pP / 3.0  # Convert from pP to w
yaw_effective_velocity = ((self.air_density/1.225)**(1/3)) * self.average_velocity * cosd(self.yaw_angle) ** pW

# Now compute the power
return (
    1.225
    * self.powInterp(yaw_effective_velocity)
    * self.turbulence_parameter
)

Bug report: Curl regression rotation test requires a fudge factor

Bug description
The test_regression_rotation in curl_regression_test.py requires a fudge factor the the second waked turbine to pass.

To Reproduce

  1. Remove the rel inputs to the approx functions in the test_regression_rotation test in curl_regression_test.py.
  2. Run test with pytest curl_regression_test.py
  3. See this error:
        turbine = floris.farm.turbine_map.turbines[1]
        assert approx(turbine.Cp) == first_waked_baseline[0]
        assert approx(turbine.Ct) == first_waked_baseline[1]
        assert approx(turbine.power) == first_waked_baseline[2]
        assert approx(turbine.aI) == first_waked_baseline[3]
        assert approx(turbine.average_velocity) == first_waked_baseline[4]
    
        turbine = floris.farm.turbine_map.turbines[2]
>       assert approx(turbine.Cp, ) == second_waked_baseline[0]
E       assert 0.44634920611531154 ± 4.5e-07 == 0.4463486980205582
E        +  where 0.44634920611531154 ± 4.5e-07 = approx(0.44634920611531154)
E        +    where 0.44634920611531154 = <floris.simulation.turbine.Turbine object at 0x7fdd7c77ded0>.Cp

curl_regression_test.py:151: AssertionError
=================================================================================== short test summary info ====================================================================================
FAILED curl_regression_test.py::test_regression_rotation - assert 0.44634920611531154 ± 4.5e-07 == 0.4463486980205582
================================================================================= 1 failed, 4 passed in 57.07s =================================================================================

Expected behavior
It is expected for the test to pass without needing the rel inputs.

Screenshots, if applicable
None.

Floris Version
v2.3.0

System Information (please complete the following information):

  • OS: Ubuntu 18.04 on WSL
  • Library versions
    • matplotlib==3.2.2
    • numpy==1.19.5
    • pytest==5.4.3
    • scipy==1.6.1
    • Sphinx==3.5.2

Additional context
None.

Bug report

Bug description
The calculate_wake() function produces

different result even though the input is the same.

To Reproduce
Run the code in the Additional context section. The input file is the one provided in the example directory of the floris package.

Expected behavior
The total output power differ for the same input wind direction (-270) when calc_wake() is called between with another wind direction (-90) .

Wind speed/dir: 8.0 / -270
Power: 4.470527954624492

Wind speed/dir: 8.0 / -90
Power: 5.276564072664706

Wind speed/dir: 8.0 / -270
Power: 5.276564072664707

Wind speed/dir: 8.0 / -90
Power: 5.276564072664706

Screenshots, if applicable

Floris Version
v1.1.2

System Information (please complete the following information):

  • OS: Win10
  • Library versions
    • matplotlib: 3.0.3
    • numpy: 1.16.3
    • pytest: 4.3.1
    • scipy: 1.1.0
    • Sphinx: 1.8.5

Additional context
`import numpy as np
import floris.tools as wfct

fi = wfct.floris_utilities.FlorisInterface('example_input.json')
print()

fi.reinitialize_flow_field(wind_direction = 0)
print('Wind speed/dir: ', fi.floris.farm.wind_speed, '/', fi.floris.farm.wind_direction)
fi.calculate_wake()
print('Power: ', np.sum(fi.get_turbine_power())/1e6)
print()

fi.reinitialize_flow_field(wind_direction = 180)
print('Wind speed/dir: ', fi.floris.farm.wind_speed, '/', fi.floris.farm.wind_direction)
fi.calculate_wake()
print('Power: ', np.sum(fi.get_turbine_power())/1e6)
print()

fi.reinitialize_flow_field(wind_direction = 0)
print('Wind speed/dir: ', fi.floris.farm.wind_speed, '/', fi.floris.farm.wind_direction)
fi.calculate_wake()
print('Power: ', np.sum(fi.get_turbine_power())/1e6)
print()

fi.reinitialize_flow_field(wind_direction = 180)
print('Wind speed/dir: ', fi.floris.farm.wind_speed, '/', fi.floris.farm.wind_direction)
fi.calculate_wake()
print('Power: ', np.sum(fi.get_turbine_power())/1e6)
print()`

Bug report: scipy>=1.5.0 breaks yaw optimization with SLSQP and equality constraints

Bug description
When I use a recent version of Scipy (versions 1.5.0 or later), I cannot set an equality constraint in the SLSQP yaw optimization functions successfully. It seems that the optimization attempts to calculate a gradient for the equality-constrained turbines, thereby dividing some value by 0 and exiting the optimization without success after the first iteration.

To Reproduce
The following is a minimal example that reproduces the issue for me:

import os
import numpy as np

import floris.tools as wfct
from floris.tools.optimization.scipy.yaw import YawOptimization


# Instantiate the FLORIS object
file_dir = os.path.dirname(os.path.abspath(__file__))
fi = wfct.floris_interface.FlorisInterface(
    os.path.join(file_dir, "../../../example_input.json")
)

# Set turbine locations to 3 turbines in a row
D = fi.floris.farm.turbines[0].rotor_diameter
layout_x = [0, 7 * D, 14 * D]
layout_y = [0, 0, 0]
fi.reinitialize_flow_field(layout_array=(layout_x, layout_y))

# Optimize yaw under bounds
yaw_opt = YawOptimization(fi, bnds=[(0., 25.), (0., 25.), (0., 0.)])
yaw_angles = yaw_opt.optimize()

Expected behavior
The correct behavior is, when using scipy<1.5.0:

=====================================================
Optimizing wake redirection control...
Number of parameters to optimize =  3
=====================================================
  NIT    FC           OBJFUN            GNORM
    1     5    -1.000000E+00     4.618128E-02
    2    10    -1.001534E+00     6.848057E-02
    3    15    -1.041302E+00     1.736367E-01
    4    20    -1.144908E+00     4.190432E-02
    5    25    -1.147140E+00     3.612001E-02
    6    30    -1.150802E+00     1.643277E-02
    7    37    -1.150821E+00     1.476510E-02
    8    52    -1.150821E+00     1.476231E-02
    9    67    -1.150821E+00     1.475954E-02
   10    82    -1.150821E+00     1.475678E-02
   11    97    -1.150821E+00     1.475404E-02
   12   112    -1.150821E+00     1.475131E-02
   13   127    -1.150821E+00     1.474860E-02
   14   142    -1.150821E+00     1.474590E-02
   15   157    -1.150821E+00     1.474322E-02
   16   172    -1.150821E+00     1.474056E-02
   17   187    -1.150821E+00     1.473791E-02
   18   202    -1.150821E+00     1.473528E-02
   19   217    -1.150821E+00     1.473266E-02
   20   232    -1.150821E+00     1.473006E-02
   21   247    -1.150821E+00     1.472747E-02
   22   262    -1.150821E+00     1.472489E-02
   23   277    -1.150821E+00     1.472233E-02
   24   292    -1.150821E+00     1.471979E-02
   25   307    -1.150821E+00     1.471726E-02
   26   322    -1.150821E+00     1.471474E-02
   27   337    -1.150821E+00     1.471223E-02
   28   352    -1.150821E+00     1.470974E-02
   29   367    -1.150821E+00     1.470727E-02
   30   382    -1.150821E+00     1.470480E-02
   31   397    -1.150821E+00     1.470235E-02
   32   412    -1.150821E+00     1.469991E-02
   33   427    -1.150821E+00     1.469749E-02
   34   442    -1.150821E+00     1.469508E-02
   35   457    -1.150821E+00     1.469268E-02
   36   472    -1.150821E+00     1.469030E-02
   37   487    -1.150821E+00     1.468792E-02
   38   502    -1.150821E+00     1.468556E-02
   39   517    -1.150821E+00     1.468321E-02
   40   532    -1.150821E+00     1.468088E-02
   41   547    -1.150821E+00     1.467855E-02
   42   562    -1.150821E+00     1.467624E-02
   43   577    -1.150821E+00     1.467394E-02
   44   592    -1.150821E+00     1.467165E-02
   45   607    -1.150821E+00     1.466938E-02
   46   622    -1.150821E+00     1.466711E-02
   47   637    -1.150821E+00     1.466486E-02
   48   652    -1.150821E+00     1.466262E-02
   49   667    -1.150821E+00     1.466038E-02
   50   682    -1.150821E+00     1.465817E-02
   51   697    -1.150821E+00     1.465596E-02
Iteration limit exceeded    (Exit mode 9)
            Current function value: -1.1508212676552003
            Iterations: 51
            Function evaluations: 697
            Gradient evaluations: 51

Screenshots, if applicable
This is the error that is reported:

=====================================================
Optimizing wake redirection control...
Number of parameters to optimize =  3
=====================================================
/home/bdoekeme/.python_environments/.floris/lib/python3.8/site-packages/scipy/optimize/_numdiff.py:519: RuntimeWarning: invalid value encountered in true_divide
  J_transposed[i] = df / dx
  NIT    FC           OBJFUN            GNORM
    1     4    -1.000000E+00              NAN
Inequality constraints incompatible    (Exit mode 4)
            Current function value: -1.0
            Iterations: 1
            Function evaluations: 4
            Gradient evaluations: 1
No change in controls suggested for this inflow                    condition...

Floris Version
v2.3 (main branch as of to date)

System Information (please complete the following information):

  • OS: Ubuntu 18.04 LTS through WSL 1 on a Windows 10 (20H2) machine
  • Library versions
    • numpy=1.19.5
    • pytest=6.2.2
    • scipy=1.6.0

Additional context
A potential future-proof solution to this would be to condition what goes into the scipy.optimize.minimize function. Namely, the yaw optimization code could take the user-specified bounds on the floris-side, then determine what turbines have equality constraints, and finally only optimize for the remaining non-equality-constrained turbines. That way, we would present the user with the right value for the number of variables optimized (in this example, it reports 3 but it should be 2), and it provides greater freedom in the optimization methods. I haven't worked out the details really, but it could be something as simple as:

Adding this snippet to reinitialize_opt(...):

self.turbines_opt = np.where(np.diff(bnds) > 0.001)[0]

and then change the cost function to accept a subset of yaw angles, only the ones relevant for optimization:

def _yaw_power_opt(self, yaw_angles_subset):
    yaw_angles = [b[0] for b in self.bnds]
    yaw_angles[self.turbines_opt] = yaw_angles_subset
   
    yaw_angles = self._unnorm(
        np.array(yaw_angles), self.minimum_yaw_angle, self.maximum_yaw_angle
    )
    return (
        -1
        * self.fi.get_farm_power_for_yaw_angle(
            yaw_angles,
            include_unc=self.include_unc,
            unc_pmfs=self.unc_pmfs,
            unc_options=self.unc_options,
        )
        / self.initial_farm_power
    )

eps_gain property requested in Jimenez Parameters, even though it isn't defined there

eps_gain is a property in the parent class VelocityDeflection;

However, the Jimenez class does not define the property and therefore methods such as get_model_parameters
fail:

`fi.get_model_parameters()`
AttributeError: 'Jimenez' object has no attribute '_eps_gain'

Reproducible example:

fi_jensen = wfct.floris_interface.FlorisInterface("../examples/other_jsons/jensen.json")
fi_jensen.get_model_parameters()

Suggested solution:
Remove this definition from the parent class. I would assume that curl deflection will also throw this error, since it does not define (or need) this property.

Missing folder sampling/ with blockMesh for sowfa_example

Hi, I'm trying to run turbine example sowfa_example.

Errors occurred with blockMesh in runscript.preprocess ,(see

#include "sampling/sliceDataInstantaneous"
), which gives file not found error for system/sampling/sliceDataInstantaneous.

It seems that the given sowfa_example does not have the required directory, while other examples in sowfa all have the system/sampling directory. Is there anyone who has some ideas to solve this problem and could kindly help me?
Millions of thanks in advance.

Suggestions for user-friendliness: Add more methods and properties to Floris or FlorisInterface

It would be great if the Floris and/or FlorisInterface classes (what is the difference?) had methods that wrapped up other functionality like running wake steering optimization and plotting.

For example:

import floris

sim = floris.Floris(input_file="my_config.json")
ax1 = sim.plot_horizontal_slice(figsize=(10, 5))
ax1.set_title("Baseline")
sim_ws = sim.copy()  # Could return a deepcopy of itself
sim_ws.run_wake_steering()
power_ratio = sim_ws.total_power / sim.total_power
print("Optimum yaw:", sim_ws.yaw_angles)
ax2 = sim_ws.plot_horizontal_slice(figsize=(10, 5))
ax2.set_title("Optimized")

Jensen wake model

Bug description

Dear all,
thanks again for your efforts in developing an outstanding FLORIS framework!

The current implementation of the Jensen wake deficit generates a quadratic wake deficit (see figure A). If I am not mistaken, the standard Jensen wake model should be circular (see figure B).

Figure A
A

Figure B
B

Is there any specific reason for the current implementation?

I believe not many are using the Jensen model, but it might still be used as reference...
I also think the difference between circular and quadratic in terms of power production of a downstream WT can be small in many situations. However, especially if the downstream turbine hub height is different to the wake sheding turbine, larger differences should occur.

To Reproduce
Code to reproduce figure A:

import matplotlib.pyplot as plt
import floris.tools as wfct
fi = wfct.floris_interface.FlorisInterface("examples/other_jsons/jensen.json")
fi.calculate_wake()
hor_plane = fi.get_hor_plane()
fig, axes = plt.subplots(2, 1)
wfct.visualization.visualize_cut_plane(hor_plane, ax=axes[0])
x_loc = 200
cross_plane = fi.get_cross_plane(x_loc=x_loc, z_bounds=[1, 200])
wfct.visualization.visualize_cut_plane(cross_plane, ax=axes[1])
axes[1].set_title("@ x=%.0f" % x_loc)
fig.tight_layout()
plt.show()

Code to reproduce figure B:
Add the following lines to jensen.py at line 122:

y_center = np.zeros_like(boundary_line) + turbine_coord.x2 + deflection_field
z_center = np.zeros_like(boundary_line) + turbine.hub_height
c[((y_locations - y_center) ** 2 + (z_locations - z_center) ** 2) > (boundary_line ** 2)] = 0

Expected behavior
See figure B.

Floris Version
2.3

Problem in Gaussianmodels

Problem
Learn Floris through the Example you gave in GitHub, but encountered a problem here (the problem is shown in the figure below), how can I solve it?

Floris Version
2.2.0

System Information
win10

problem
Thank you very much!

Layout optimization: Number of turbines and its attributes don't match the number of turbines given as input

Bug description
<While using the module "floris.tools.optimization.scipy.layout", the output "layout_opt" gives a nturbs allways lower than the number of turbines indicated at the input file, therefore the turbines optimized are always less than expected. In my case, I am trying to optimize a wind farm with of 6 turbines and turbines optimized are 2.>

To Reproduce

Steps to reproduce the behavior:

  1. Run:
# Copyright 2020 NREL
 
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at http://www.apache.org/licenses/LICENSE-2.0
 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
 
# See https://floris.readthedocs.io for documentation
 

import matplotlib.pyplot as plt
import floris.tools as wfct
import floris.tools.visualization as vis
from floris.tools.optimization.scipy.layout import LayoutOptimization
import numpy as np
import os

# Instantiate the FLORIS object
file_dir = os.path.dirname(os.path.abspath(__file__))
fi = wfct.floris_interface.FlorisInterface(
    os.path.join(file_dir, '../example_input.json')
)

# Set turbine locations to 3 turbines in a triangle
D = fi.floris.farm.turbines[0].rotor_diameter
layout_x = [0,1661,0,1661,0,1661]
layout_y = [2376,2376,1188,1188,0,0]
fi.reinitialize_flow_field(layout_array=(layout_x, layout_y))

NT1 = fi.floris.farm.turbines

# Define the boundary for the wind farm
boundaries = [[1661.1, 2376.1], [1661.1, -0.1], [-0.1, -0.1], [-0.1, 2376.1]]


# Generate random wind rose data
wd = np.arange(0., 360., 5.)
np.random.seed(1)
ws = 8.0 + np.random.randn(len(wd))*0.5
freq = np.abs(np.sort(np.random.randn(len(wd))))
freq = freq/freq.sum()

# Set optimization options
opt_options = {'maxiter': 50, 'disp': True, 'iprint': 2, 'ftol': 1e-8}

# Compute initial AEP for optimization normalization
AEP_initial = fi.get_farm_AEP(wd, ws, freq)

# Instantiate the layout otpimization object
layout_opt = LayoutOptimization(
    fi=fi,
    boundaries=boundaries,
    wd=wd,
    ws=ws,
    freq=freq,
    AEP_initial=AEP_initial,
    opt_options=opt_options
)

# Perform layout optimization
layout_results = layout_opt.optimize()

print('=====================================================')
print('Layout coordinates: ')
for i in range(len(layout_results[0])):
    print('Turbine', i, ': \tx = ', '{:.1f}'.format(layout_results[0][i]), \
          '\ty = ', '{:.1f}'.format(layout_results[1][i]))

# Calculate new AEP results
fi.reinitialize_flow_field(layout_array=(layout_results[0], layout_results[1]))
AEP_optimized = fi.get_farm_AEP(wd, ws, freq)

print('=====================================================')
print('Total AEP Gain = %.1f%%' %
      (100.*(AEP_optimized - AEP_initial)/AEP_initial))
print('=====================================================')

# Plot the new layout vs the old layout
layout_opt.plot_layout_opt_results()
plt.show()

NT2 = fi.floris.farm.turbines
  1. Open 'layout_opt.nturbs' output
  2. See the difference between the initial number of turbines and the final number of turbines

Expected behavior

Screenshots, if applicable

image

image

Floris Version
Floris v2.0

System Information (please complete the following information):

  • OS: Windows 10
  • Library versions

Precision errors cause bug on Windows

Bug description
AttributeError: module 'numpy' has no attribute 'float128' coming from line 112 in floris\simulation\wake_velocity\gaussianModels\gauss.py - seems to be Windows specific based on:
winpython/winpython#613
https://stackoverflow.com/questions/9062562/what-is-the-internal-precision-of-numpy-float128

Changing 'np.float128' to 'np.longdouble' seems to fix the problem for me.

To Reproduce
Steps to reproduce the behavior:
-Run a flow case or AEP calculation using the gauss wake model on Windows

Expected behavior
Run the flow case/ AEP calculation.

Screenshots, if applicable
ecd4c80c-3ded-4777-8eab-a75cbcf79a8b

Floris Version
FLORIS 1.1.7 on the develop branch

System Information (please complete the following information):
Windows 10
(I was using VS Code)

(My first github issue so let me know what other information is needed if something is missing)

Request to include the file "windtoolkit_geo_center_us.p"

I am new at Floris and currently trying to use the "compute_aep.py" example to calculate the AEP of a 200 turbine wind farm fro each turbine.

I would like to use my own wind field by loading a wind rose file. In the case of this example, between the lines 101 and 103 we have:

df = wind_rose.load(os.path.join(
	file_dir, '../optimization/scipy/windtoolkit_geo_center_us.p'
))

My idea is to modify the file "windtoolkit_geo_center_us.p", but when I search for it in ../floris/floris/tools/optimization/scipy/
I can't find this file. Could you please upload it?

Input for layout is not verified to make sense

Bug description
The user is allowed to enter arrays of different sizes for the layout arrays.

To Reproduce
See #53

Expected behavior
When layouts of different sizes are given, the user should be at least warned and maybe FLORIS should exit.

Floris Version
develop

Points requested in calculate_wake are not used

Bug description
Calling fi.calculate_wake(points=my_points) returns fi.floris.farm.flow_field.* that are not at the requested coordinates--the number of flowfield points differs from my_points. This is unexpected behavior.

Floris Version
commit 2833cc7

System Information (please complete the following information):
Mac High Sierra

Additional context

About the rotate speed setting in Floris

Cloud you please tell me how does Floris set the rotate speed?

I use these equations to find the rotate speed n, and find that the rotate speed changes when wind speed changes, is that right? but I still don't know the certain equations, codes, or laws in Floris.

image
image
image

Make `use_turbulence_correction=True` the default value for get_farm_power()

Is your feature request related to a problem? Please describe.
get_farm_power() is used many times throughout the library when AEP Calculations happen
However, turbulence correction via use_turbulence_correction is never passed as a parameter from the calling method, and therefore all AEP calculations default to ignoring turbulence effects (use_turbulence_correction=False)

Describe the solution you'd like
I wonder what the reason for this default value is, since as a user I want to utilize the maximum simulation accuracy when calculating the AEP - for example for layout optimization.

Describe alternatives you've considered
Set the default value to true, or at least give the user of functions such as get_farm_AEP or _AEP_loop_wd the transparency that turbulence influence is ignored here.

Loading .vtk files generated by flow_data.save_as_vtk

I'm currently going through examples/flow_interaction/flow_data_to_vtk.py and I was able to save the output as a vtk file. However I'm having trouble understanding the best way to load it using the python vtk library. I tried incorporating the result from this SO thread, resulting in this:

import floris.tools as wfct
import vtk
from vtk.util import numpy_support
from dash_vtk.utils import to_volume_state

fi = wfct.floris_interface.FlorisInterface("./data/example_input.json")

fi.calculate_wake()

# Get the flow data and save to vtk
flow_data = fi.get_flow_data()
flow_data.save_as_vtk("./data/flow.vtk")

path = 'path_to_file.vtk'

reader = vtk.vtkDataReader()
reader.SetFileName(path)
reader.Update()

# try to get output
reader.GetOutput()

However I get the following error:

2021-02-11 16:12:03.656 (   0.783s) [        18C36740]       vtkAlgorithm.cxx:867    ERR| vtkDataReader (0x55ea96203050): FillOutputPortInformation is not implemented.
2021-02-11 16:12:03.656 (   0.783s) [        18C36740]vtkDemandDrivenPipeline:633    ERR| vtkReaderExecutive (0x55ea9616ac30): Algorithm vtkDataReader(0x55ea96203050) did not create output for port 0 when asked by REQUEST_DATA_OBJECT and does not specify any DATA_TYPE_NAME.

...

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'vtkmodules.vtkIOLegacy.vtkDataReader' object has no attribute 'GetOutput'

I've also looked into how to load it using vtk.js, but it seems like the format is legacy and support is limited (see this thread).

I'd be happy to look at any example that would show how to load it in Python or JS.

Codecov fails to submit to the dashboard for PR's from forks

Bug description
The Codecov utility in the GitHub Actions workflow fails to find the codecov.io secret keys when a pull request is submitted from a fork of NREL/FLORIS. This causes the CI to fail even when all tests pass.

Evidently, someone is working on it: codecov/codecov-action#29

To Reproduce
Open a pull request from a fork and see that it fails. Like this one:https://github.com/nrel/floris/actions/runs/34813096

Expected behavior
I'd like these to either not try to upload results to the dashboard or successfully upload results.

Floris Version
Current develop branch.

Is the wind speed range 5m/s to 15m/s suitable for NREL 5MW in real world?

Hi, I wonder that is the wind speed range 5m/s to 15m/s that FLORIS usually adopted suitable for NREL 5MW in real world?

Recently, I did some LES simulations in SOWFA with turbine NREL 5MW. It seems the simulation throws an exception when the inflow wind speed is higher than 11m/s.

Also, I read the NREL 5MW paper, and it seems that the rated wind speed and rated rotor speed are 12m/s and 12.1 rpm.

image
image

Reference
Jonkman et al. - 2009 - Definition of a 5-MW Reference Wind Turbine for Of...

Millions of thanks in advance.

FlorisInterface.reinitialize_flow_field wake

Bug description
In FlorisInterface.reinitialize_flow_field there seem to be a possibility to change the wake model by including a wake string argument which is passed on to flow_field.reinitialize_flow_field, but reinitialize_flow_field expects a floris.simulation.wake class and thus gives an error.

To Reproduce
Include e.g. wake='gauss' as an argument to FlorisInterface.reinitialize_flow_field and get error message "AttributeError: 'str' object has no attribute 'velocity_model'".

Use linear interpolation instead of nearest neighbors for swept area velocities

Is your feature request related to a problem? Please describe.

Nearest neighbor interpolation is used in calculate_swept_area_velocities, which can lead to some unexpected inaccuracies.

Describe the solution you'd like

A linear interpolation, though slower, would reduce the error.

Returning a warning if the nearest grid point is more than a portion of the rotor size away could also help the user if a grid too coarse is used.

copy.deepcopy() error in FLORIS object instantiation using Python 3.6

Bug description
Instantiating the FLORIS object using fi = wfct.floris_interface.FlorisInterface("input.json") gives an error using copy.deepcopy() in floris/floris/simulation/farm.py, "TypeError: can't pickle _thread.Rlock objects"

To Reproduce

Steps to reproduce the behavior:

  1. Initialise the floris interface, fi, using, for example, example_00_open_and_vis_floris.py using Python 3.6.8 - or perhaps any Python 3.6 release, I have only tried with 3.6.8
  2. Open output
  3. See the error

Screenshots, if applicable

Traceback (most recent call last):
  File "model.py", line 44, in <module>
    fi = wfct.floris_interface.FlorisInterface("input.json")
  File "/ddn/home/npcl74/floris/floris/tools/floris_interface.py", line 64, in __init__
    self.floris = Floris(input_file=input_file, input_dict=input_dict)
  File "/ddn/home/npcl74/floris/floris/simulation/floris.py", line 53, in __init__
    self.farm = Farm(farm_dict, turbine, wake)
  File "/ddn/home/npcl74/floris/floris/simulation/farm.py", line 90, in __init__
    [copy.deepcopy(turbine) for ii in range(len(layout_x))]),
  File "/ddn/home/npcl74/floris/floris/simulation/farm.py", line 90, in <listcomp>
    [copy.deepcopy(turbine) for ii in range(len(layout_x))]),
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 180, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 280, in _reconstruct
    state = deepcopy(state, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 150, in deepcopy
    y = copier(x, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 240, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 180, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 280, in _reconstruct
    state = deepcopy(state, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 150, in deepcopy
    y = copier(x, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 240, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 150, in deepcopy
    y = copier(x, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 215, in _deepcopy_list
    append(deepcopy(a, memo))
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 180, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 280, in _reconstruct
    state = deepcopy(state, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 150, in deepcopy
    y = copier(x, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 240, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/usr/local/Cluster-Apps/python/3.6.8/lib/python3.6/copy.py", line 169, in deepcopy
    rv = reductor(4)
TypeError: can't pickle _thread.RLock objects

Floris Version
Using version 2.0.1 from develop branch

System Information (please complete the following information):

  • OS: HPC Linux Cluster
  • Library versions
    • matplotlib 3.0.2
    • numpy 1.16.1
    • pytest 5.3.5
    • scipy 1.2.1

Additional context
Running on HPC service. Also received warning about a call a "fork()" system call to create a child process. This is a warning I have received previously when running this serial script on this HPC service, but it is a warning I have received when running a parallel script. The warning is:

A process has executed an operation involving a call to the
"fork()" system call to create a child process.  Open MPI is currently
operating in a condition that could result in memory corruption or
other system errors; your job may hang, crash, or produce silent
data corruption.  The use of fork() (or system() or other calls that
create child processes) is strongly discouraged.

The process that invoked fork was:

  Local host:          [[24480,1],0] (PID 11849)

If you are *absolutely sure* that your application will successfully
and correctly survive a call to fork(), you may disable this warning
by setting the mpi_warn_on_fork MCA parameter to 0.

I don't believe this is an issue in itself, as I have changed the mpirun flags in the SLURM script before and removed the warning without any of the corruption or errors it suggests. However, it is interesting that this warning is now also on the serial script, not just the parallel.

Apologies for any discrepancies with this bug report, it's my first.

Thrust coefficient in yaw

Bug description
The thrust coefficient is multiplied by the cosine of the yaw angle.
It should be cosine^2.
This is later corrected in some of the models, but not in the curled wake model.
A power of cosine should have a small effect on the results from the code.
The cases without yaw will not be affected by this.

To Reproduce

return self._fCt(self.average_velocity) * cosd(self.yaw_angle) # **self.pP

Expected behavior
should be: return self._fCt(self.average_velocity) * cosd(self.yaw_angle)**2

Additional context
If this change is present, some of the wake wake models need to be modified.
In the current implementation, this leads to a missing cosine in the circulation used to compute the vortices in the curl model.

Example of *args in "make_wind_rose_from_user_data" submodule

I am using the "make_wind_rose_from_user_data" submodule of "WindRose" Tools module and I don't understand which input data I should use for "*args" input data. Could you give me an example of *args please?

*args: Variable length argument list consisting of a sequence of
the following alternating arguments:

            -   string - Name of additional wind parameters to include in
                wind rose.
            -   array-like - Values of the additional wind parameters used
                to calculate the frequencies of occurance
            -   np.array - Bin center values for binning the additional
                wind parameters.

make_wind_rose_from_user_data(self,wd_raw,ws_raw,*args,wd=np.arange(0, 360, 5.),ws=np.arange(0, 26, 1.))

Feature request: Allowing complex topology

Is your feature request related to a problem? Please describe.
Whenever a turbine has a different relative rotor position (vertically) due to a complex topology, it feels like I am hacking the FLORIS object. Namely, what I end up doing is incrementing each turbine's hub height with the ground height w.r.t. sea level or other reference value. Something along these lines:

# Define complex topology
layout_z = np.array(...)

# Define a zero point, e.g., min. of all turbines (z0)
if normalize_terrain:
    z0 = np.min(layout_z)
else:
    z0 = 0.

# Shift all the turbine hub heights accordingly
for ti in range(len(fi.layout_x)):
    hub_height_z0 = fi.floris.farm.turbines[ti].hub_height
    hub_height_z = layout_z[ti] - z0 + hub_height_z0
    fi.change_turbine([ti], {'hub_height': hub_height_z})

The problem is that this is a somewhat roundabout process, that we lose track of what specified_wind_height corresponds to if its value was set to -1, and that layout_z is not included in the .json file and therefore needs to be defined somewhere externally.

Describe the solution you'd like
Similar to in #223, I would like layout_z to be defined in the .json file. If there is a single turbine type and layout_z is an array with all the same value, then the definition of specified_wind_height is not ambiguous. In any other situation, it must force the user to specify its value manually.

Describe alternatives you've considered
I now have a practice where I create a class for these types of wind farms, wherein I contain all these operations. That way, I can still simply call a wind farm using a single command and get a floris object in return.

Additional context
This issue is somewhat related to Issue #223.

Bug report

I had an error when installing in Python 3.6. error: [WinError 126] The specified module could not be found

Bug report: Recent scipy and numpy releases break YawOptimizationWindRose() when include_unc=True

Bug description
numpy 1.20.1 breaks YawOptimizationWindRose() when include_unc=True. The error is related to the scipy.stats.circmean() function in flow_field.py, which may have changed or has become incompatible with a recent version of numpy. Downgrading to numpy=1.19.5 fixes the issue.

To Reproduce
I have tested this with scipy==1.4.0 and scipy==1.6.1. @bayc has reproduced the error on his system too. Here is a minimal example producing the error for numpy=1.20.1 and scipy.1.6.1.

import os
import numpy as np
import pandas as pd
import floris.tools as wfct
from floris.tools.optimization.scipy.yaw_wind_rose import YawOptimizationWindRose

file_dir = os.path.dirname(os.path.abspath(__file__))
fi = wfct.floris_interface.FlorisInterface(
    os.path.join(file_dir, "../../../example_input.json")
)
layout_x = [0, 0]
layout_y = [0, 5 * fi.floris.farm.turbines[0].rotor_diameter]
fi.reinitialize_flow_field(
    layout_array=(layout_x, layout_y), wind_direction=[270.0], wind_speed=[8.0]
)
unc_options = {"std_wd": 4.95, "std_yaw": 0.0, "pmf_res": 1.0, "pdf_cutoff": 0.95}
df = pd.DataFrame({'wd': np.array([180., 180.]),
                   'ws': np.array([9., 10.])})
yaw_opt = YawOptimizationWindRose(
    fi,
    df.wd,
    df.ws,
    minimum_yaw_angle=0.0,
    maximum_yaw_angle=25.0,
    minimum_ws=3.0,
    maximum_ws=15.0,
    include_unc=True,
    unc_options=unc_options,
)
df_opt_unc = yaw_opt.optimize()
print(df_opt_unc)

Expected behavior
The code should finish without errors and return a dataframe with the optimal yaw angles.

Floris Version
This error comes up with the most recent main version of FLORIS (3f769). I also tested this with a commit in main from 3 months ago, and the same issue comes up.

System Information (please complete the following information):

  • OS: Ubuntu 18.04 through WSL.
  • Library versions
    • numpy==1.20.1
    • scipy==1.6.1

Additional context
The issue is resolved when switching back to numpy==1.19.5. The error also appears with the most recent version of numpy==1.20.3 and scipy==1.6.3. This is the line that needs to be updated. Thanks to @bayc for helping me find this issue.

Missing square operation in the Jimenez model?

Bug description
Line 85 in jimenez.py, as shown in the following. Is there a missing square on the cosine?

xi_init = cosd(turbine.yaw_angle) * sind(turbine.yaw_angle) * turbine.Ct / 2.0

PyPI deploy

Dear Floris developers,

I see that you have multiple versions here at GitHub that is not deployed to PyPI yet. Do you have any plans of getting versions later then 1.0.0 on PyPI any time soon?

Reference: https://pypi.org/project/FLORIS/#history

Would you be interested in a pull request that would have Travis automatically deploy new versions of Floris to PyPI whenever you create new git tags? We have done so for multiple of our repositories in Equinor and it works really smoothly.

Best regards,
Markus Dregi

`get_y_plane` is broken because *_bounds and points are not calculated when normal vector == "y"

Both these if cases have no definition when normal_vector is "y".

if normal_vector == "x": # Rules of thumb for cut plane plane

if normal_vector == "x":

such as when calling get_y_plane.
You can call get_y_plane with bounds parameters, so it could work, but points is only defined inside the function. Therefore, get_y_plane does not work currently

High wind speed issue

Bug description
When running FLORIS with 5 or more aligned NREL5MW turbines (spacing 3D and higher) , gives issues when the wind speed is high. The model returns a 'nan' power production instead of (near) rated power for the last downstream turbine(s) for wind speeds 23 m/s and higher (Ct<0.07).

The model does return runtime warnings, and the issue occurs because at high wind speeds the thrust coefficient is low. Therefore a low velocity deficit is present. That combined with shear in the wind speed results in a negative velocity deficit (so velocity increase). At some point the squareroot is taken of this negative velocity deficit, resulting (probably) in complex numbers which the FLORIS model is not able to handle. This happens in floris/simulation/wake_deflection.py, line 242:
ln_deltaNum = (1.6 + np.sqrt(M0)) * ( 1.6 * np.sqrt(sigma_y * sigma_z / (sigma_y0 * sigma_z0)) - np.sqrt(M0))
So in this line 'M0' contains negative values.

To Reproduce
The code is taken from example 5 of FLORIS and adjusted to increase the number of aligned turbines and increase the wind speed. 21 m/s is shown in comparison, because at that wind speed FLORIS returns rated power instead of 'nan'.

import matplotlib.pyplot as plt
import floris.tools as wfct
import floris.tools.visualization as vis
import floris.tools.cut_plane as cp
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size

# Initialize FLORIS model
fi = wfct.floris_utilities.FlorisInterface("example_input.json")

# set turbine locations to 5 turbines in a row - demonstrate how to change coordinates
D = fi.floris.farm.flow_field.turbine_map.turbines[0].rotor_diameter
nD = 3
layout_x = [0, nD*D, 2*nD*D, 3*nD*D, 4*nD*D]
layout_y = [0, 0, 0, 0, 0]
fi.reinitialize_flow_field(layout_array=(layout_x, layout_y),wind_speed=21)

# Calculate wake
fi.calculate_wake()
print(fi.get_turbine_power())

fi.reinitialize_flow_field(layout_array=(layout_x, layout_y),wind_speed=23.5)

# Calculate wake
fi.calculate_wake()
print(fi.get_turbine_power())

Expected behavior
For these high wind speeds I expect the turbines to operate at rated power and hence return rated power instead of a 'nan' value.

Floris Version
Version 1.0.0 - installed through pip about a week ago.

Feature request: error or warn when setting default parameters

Bug description
Not exactly a bug, but an issue I've hit is that if you enter parameters into the JSON and make a mistake somewhere, rather than producing an error, default values will be used. This can be detected by warnings but might go un-noticed, perhaps a new feature could be to check the JSON for defined parameters which aren't used or expected and forcing the user to either delete or correct the JSON?

Bug report: Flowfield with multiple wind speed measures

Bug description
I am not sure if this is a bug report or a need for enhancement declaration. When the flow filed is estimated drawing on a different wind speed measure for every turbine, the resulting calculation is somewhat strange.

To Reproduce
I am running a code like the next one. If we use 10 m/s for every turbine instead of 10, 11, 12 and 13 m/s, the resulting flow field is correct. However, using different speeds provides a strange flowfield calculation, like the one in the attached picture.

turb1_ws, turb2_ws, turb3_ws, turb4_ws = 10.0, 11.0, 12.0, 13.0
turb1_wd, turb2_wd, turb3_wd, turb4_wd = 20.0, 20.0, 20.0, 20.0
wind_speed = [turb1_ws,turb2_ws,turb3_ws,turb4_ws]
wind_direction = [turb1_wd,turb2_wd,turb3_wd,turb4_wd]

fi.reinitialize_flow_field(wind_speed = wind_speed,
wind_direction = wind_direction,
wind_layout = (layout_x,layout_y),
layout_array=(layout_x,layout_y))
fi.calculate_wake()

Flowfield limits

xmin, xmax = -2000.0,2200.0
ymin, ymax = -2000.0,2200.0
ti = 0.102

Initialize the horizontal cut

hor_plane = fi.get_hor_plane(height=fi.floris.farm.turbines[0].hub_height,
x_bounds=(xmin,xmax),
y_bounds=(ymin,ymax))

Plot

fig, ax = plt.subplots()
im = wfct.visualization.visualize_cut_plane(hor_plane, ax=ax)
wfct.visualization.plot_turbines_with_fi(ax, fi)
cbar = fig.colorbar(im,ax=ax,fraction=0.10,pad=0.04)
cbar.set_label('m/s', labelpad=+10)
ax.set_title('Flujo para U = {} m/s, Dirección: {}$^\circ$, IT: {}%'.format(
wind_speed,
wind_direction,round(ti*100,2)))
plt.show()

Expected behavior
The graphical result should show a proper flowfield. However, it results in distorted flow lines and unreasonable colours (see attached picture).

Figure_10

Floris Version
Floris 2.3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.